GHashTable *jabber_chat_info_defaults(PurpleConnection *gc, const char *chat_name) { GHashTable *defaults; JabberStream *js = gc->proto_data; defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); g_hash_table_insert(defaults, "handle", g_strdup(js->user->node)); if (js->chat_servers) g_hash_table_insert(defaults, "server", g_strdup(js->chat_servers->data)); if (chat_name != NULL) { JabberID *jid = jabber_id_new(chat_name); if(jid) { g_hash_table_insert(defaults, "room", g_strdup(jid->node)); if(jid->domain) g_hash_table_replace(defaults, "server", g_strdup(jid->domain)); if(jid->resource) g_hash_table_replace(defaults, "handle", g_strdup(jid->resource)); jabber_id_free(jid); } } return defaults; }
const char *jabber_normalize(const GaimAccount *account, const char *in) { GaimConnection *gc = account ? account->gc : NULL; JabberStream *js = gc ? gc->proto_data : NULL; static char buf[3072]; /* maximum legal length of a jabber jid */ JabberID *jid; char *node, *domain; jid = jabber_id_new(in); if(!jid) return NULL; node = jid->node ? g_utf8_strdown(jid->node, -1) : NULL; domain = g_utf8_strdown(jid->domain, -1); if(js && node && jid->resource && jabber_chat_find(js, node, domain)) g_snprintf(buf, sizeof(buf), "%s@%s/%s", node, domain, jid->resource); else g_snprintf(buf, sizeof(buf), "%s%s%s", node ? node : "", node ? "@" : "", domain); jabber_id_free(jid); g_free(node); g_free(domain); return buf; }
void jabber_chat_join(PurpleConnection *gc, GHashTable *data) { char *room, *server, *handle, *passwd; JabberID *jid; JabberStream *js = gc->proto_data; char *tmp; room = g_hash_table_lookup(data, "room"); server = g_hash_table_lookup(data, "server"); handle = g_hash_table_lookup(data, "handle"); passwd = g_hash_table_lookup(data, "password"); if(!room || !server) return; if(!handle) handle = js->user->node; if(!jabber_nodeprep_validate(room)) { char *buf = g_strdup_printf(_("%s is not a valid room name"), room); purple_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"), buf); purple_serv_got_join_chat_failed(gc, data); g_free(buf); return; } else if(!jabber_domain_validate(server)) { char *buf = g_strdup_printf(_("%s is not a valid server name"), server); purple_notify_error(gc, _("Invalid Server Name"), _("Invalid Server Name"), buf); purple_serv_got_join_chat_failed(gc, data); g_free(buf); return; } else if(!jabber_resourceprep_validate(handle)) { char *buf = g_strdup_printf(_("%s is not a valid room handle"), handle); purple_notify_error(gc, _("Invalid Room Handle"), _("Invalid Room Handle"), buf); purple_serv_got_join_chat_failed(gc, data); g_free(buf); return; } /* Normalize the room and server parameters */ tmp = g_strdup_printf("%s@%s", room, server); jid = jabber_id_new(tmp); g_free(tmp); if (jid == NULL) { /* TODO: Error message */ g_return_if_reached(); } /* * Now that we've done all that nice core-interface stuff, let's join * this room! */ jabber_join_chat(js, jid->node, jid->domain, handle, passwd, data); jabber_id_free(jid); }
static void jabber_chat_room_configure_cb(JabberStream *js, xmlnode *packet, gpointer data) { xmlnode *query, *x; const char *type = xmlnode_get_attrib(packet, "type"); const char *from = xmlnode_get_attrib(packet, "from"); char *msg; JabberChat *chat; JabberID *jid; if(!type || !from) return; if(!strcmp(type, "result")) { jid = jabber_id_new(from); if(!jid) return; chat = jabber_chat_find(js, jid->node, jid->domain); jabber_id_free(jid); if(!chat) return; if(!(query = xmlnode_get_child(packet, "query"))) return; for(x = xmlnode_get_child(query, "x"); x; x = xmlnode_get_next_twin(x)) { const char *xmlns; if(!(xmlns = xmlnode_get_namespace(x))) continue; if(!strcmp(xmlns, "jabber:x:data")) { chat->config_dialog_type = PURPLE_REQUEST_FIELDS; chat->config_dialog_handle = jabber_x_data_request(js, x, jabber_chat_room_configure_x_data_cb, chat); return; } } } else if(!strcmp(type, "error")) { char *msg = jabber_parse_error(js, packet); purple_notify_error(js->gc, _("Configuration error"), _("Configuration error"), msg); if(msg) g_free(msg); return; } msg = g_strdup_printf("Unable to configure room %s", from); purple_notify_info(js->gc, _("Unable to configure"), _("Unable to configure"), msg); g_free(msg); }
static void roomlist_disco_result_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, xmlnode *packet, gpointer data) { xmlnode *query; xmlnode *item; if(!js->roomlist) return; if (type == JABBER_IQ_ERROR) { char *err = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Error"), _("Error retrieving room list"), err); purple_roomlist_set_in_progress(js->roomlist, FALSE); purple_roomlist_unref(js->roomlist); js->roomlist = NULL; g_free(err); return; } if(!(query = xmlnode_get_child(packet, "query"))) { char *err = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Error"), _("Error retrieving room list"), err); purple_roomlist_set_in_progress(js->roomlist, FALSE); purple_roomlist_unref(js->roomlist); js->roomlist = NULL; g_free(err); return; } for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) { const char *name; PurpleRoomlistRoom *room; JabberID *jid; if(!(jid = jabber_id_new(xmlnode_get_attrib(item, "jid")))) continue; name = xmlnode_get_attrib(item, "name"); room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, jid->node, NULL); purple_roomlist_room_add_field(js->roomlist, room, jid->node); purple_roomlist_room_add_field(js->roomlist, room, jid->domain); purple_roomlist_room_add_field(js->roomlist, room, name ? name : ""); purple_roomlist_room_add(js->roomlist, room); jabber_id_free(jid); } purple_roomlist_set_in_progress(js->roomlist, FALSE); purple_roomlist_unref(js->roomlist); js->roomlist = NULL; }
static void jabber_chat_register_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, xmlnode *packet, gpointer data) { xmlnode *query, *x; char *msg; JabberChat *chat; JabberID *jid; if (!from) return; if (type == JABBER_IQ_RESULT) { jid = jabber_id_new(from); if(!jid) return; chat = jabber_chat_find(js, jid->node, jid->domain); jabber_id_free(jid); if(!chat) return; if(!(query = xmlnode_get_child(packet, "query"))) return; for(x = xmlnode_get_child(query, "x"); x; x = xmlnode_get_next_twin(x)) { const char *xmlns; if(!(xmlns = xmlnode_get_namespace(x))) continue; if(!strcmp(xmlns, "jabber:x:data")) { jabber_x_data_request(js, x, jabber_chat_register_x_data_cb, chat); return; } } } else if (type == JABBER_IQ_ERROR) { char *msg = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg); if(msg) g_free(msg); return; } msg = g_strdup_printf("Unable to configure room %s", from); purple_notify_info(js->gc, _("Unable to configure"), _("Unable to configure"), msg); g_free(msg); }
static void roomlist_disco_result_cb(JabberStream *js, xmlnode *packet, gpointer data) { xmlnode *query; xmlnode *item; const char *type; if(!js->roomlist) return; if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) { char *err = jabber_parse_error(js,packet); gaim_notify_error(js->gc, _("Error"), _("Error retrieving room list"), err); gaim_roomlist_set_in_progress(js->roomlist, FALSE); gaim_roomlist_unref(js->roomlist); js->roomlist = NULL; g_free(err); return; } if(!(query = xmlnode_get_child(packet, "query"))) { char *err = jabber_parse_error(js, packet); gaim_notify_error(js->gc, _("Error"), _("Error retrieving room list"), err); gaim_roomlist_set_in_progress(js->roomlist, FALSE); gaim_roomlist_unref(js->roomlist); js->roomlist = NULL; g_free(err); return; } for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) { const char *name; GaimRoomlistRoom *room; JabberID *jid; if(!(jid = jabber_id_new(xmlnode_get_attrib(item, "jid")))) continue; name = xmlnode_get_attrib(item, "name"); room = gaim_roomlist_room_new(GAIM_ROOMLIST_ROOMTYPE_ROOM, jid->node, NULL); gaim_roomlist_room_add_field(js->roomlist, room, jid->node); gaim_roomlist_room_add_field(js->roomlist, room, jid->domain); gaim_roomlist_room_add_field(js->roomlist, room, name ? name : ""); gaim_roomlist_room_add(js->roomlist, room); jabber_id_free(jid); } gaim_roomlist_set_in_progress(js->roomlist, FALSE); gaim_roomlist_unref(js->roomlist); js->roomlist = NULL; }
char *jabber_get_resource(const char *in) { JabberID *jid = jabber_id_new(in); char *out; if(!jid) return NULL; out = g_strdup(jid->resource); jabber_id_free(jid); return out; }
char * jabber_get_bare_jid(const char *in) { JabberID *jid = jabber_id_new(in); char *out; if (!jid) return NULL; out = jabber_id_get_bare_jid(jid); jabber_id_free(jid); return out; }
char *jabber_get_bare_jid(const char *in) { JabberID *jid = jabber_id_new(in); char *out; if(!jid) return NULL; out = g_strdup_printf("%s%s%s", jid->node ? jid->node : "", jid->node ? "@" : "", jid->domain); jabber_id_free(jid); return out; }
JabberID* jabber_id_new(const char *str) { char *at; char *slash; JabberID *jid; if(!str || !g_utf8_validate(str, -1, NULL)) return NULL; jid = g_new0(JabberID, 1); at = g_utf8_strchr(str, -1, '@'); slash = g_utf8_strchr(str, -1, '/'); if(at) { jid->node = g_utf8_normalize(str, at-str, G_NORMALIZE_NFKC); if(slash) { jid->domain = g_utf8_normalize(at+1, slash-(at+1), G_NORMALIZE_NFKC); jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); } else { jid->domain = g_utf8_normalize(at+1, -1, G_NORMALIZE_NFKC); } } else { if(slash) { jid->domain = g_utf8_normalize(str, slash-str, G_NORMALIZE_NFKC); jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); } else { jid->domain = g_utf8_normalize(str, -1, G_NORMALIZE_NFKC); } } if(!jabber_nodeprep_validate(jid->node) || !jabber_nameprep_validate(jid->domain) || !jabber_resourceprep_validate(jid->resource)) { jabber_id_free(jid); return NULL; } return jid; }
gboolean jabber_is_own_server(JabberStream *js, const char *str) { JabberID *jid; gboolean equal; if (str == NULL) return FALSE; g_return_val_if_fail(*str != '\0', FALSE); jid = jabber_id_new(str); if (!jid) return FALSE; equal = (jid->node == NULL && g_str_equal(jid->domain, js->user->domain) && jid->resource == NULL); jabber_id_free(jid); return equal; }
gboolean jabber_is_own_account(JabberStream *js, const char *str) { JabberID *jid; gboolean equal; if (str == NULL) return TRUE; g_return_val_if_fail(*str != '\0', FALSE); jid = jabber_id_new(str); if (!jid) return FALSE; equal = (purple_strequal(jid->node, js->user->node) && g_str_equal(jid->domain, js->user->domain) && (jid->resource == NULL || g_str_equal(jid->resource, js->user->resource))); jabber_id_free(jid); return equal; }
const char *jabber_normalize(const PurpleAccount *account, const char *in) { PurpleConnection *gc = account ? account->gc : NULL; JabberStream *js = gc ? gc->proto_data : NULL; static char buf[3072]; /* maximum legal length of a jabber jid */ JabberID *jid; jid = jabber_id_new_internal(in, TRUE); if(!jid) return NULL; if(js && jid->node && jid->resource && jabber_chat_find(js, jid->node, jid->domain)) g_snprintf(buf, sizeof(buf), "%s@%s/%s", jid->node, jid->domain, jid->resource); else g_snprintf(buf, sizeof(buf), "%s%s%s", jid->node ? jid->node : "", jid->node ? "@" : "", jid->domain); jabber_id_free(jid); return buf; }
static JabberID* jabber_id_new_internal(const char *str, gboolean allow_terminating_slash) { const char *at = NULL; const char *slash = NULL; const char *c; gboolean needs_validation = FALSE; #if 0 gboolean node_is_required = FALSE; #endif #ifndef USE_IDN char *node = NULL; char *domain; #endif JabberID *jid; if (!str) return NULL; for (c = str; *c != '\0'; c++) { switch (*c) { case '@': if (!slash) { if (at) { /* Multiple @'s in the node/domain portion, not a valid JID! */ return NULL; } if (c == str) { /* JIDs cannot start with @ */ return NULL; } if (c[1] == '\0') { /* JIDs cannot end with @ */ return NULL; } at = c; } break; case '/': if (!slash) { if (c == str) { /* JIDs cannot start with / */ return NULL; } if (c[1] == '\0' && !allow_terminating_slash) { /* JIDs cannot end with / */ return NULL; } slash = c; } break; default: /* characters allowed everywhere */ if ((*c >= 'a' && *c <= 'z') || (*c >= '0' && *c <= '9') || (*c >= 'A' && *c <= 'Z') || *c == '.' || *c == '-') /* We're good */ break; #if 0 if (slash != NULL) { /* characters allowed only in the resource */ if (implement_me) /* We're good */ break; } /* characters allowed only in the node */ if (implement_me) { /* * Ok, this character is valid, but only if it's a part * of the node and not the domain. But we don't know * if "c" is a part of the node or the domain until after * we've found the @. So set a flag for now and check * that we found an @ later. */ node_is_required = TRUE; break; } #endif /* * Hmm, this character is a bit more exotic. Better fall * back to using the more expensive UTF-8 compliant * stringprep functions. */ needs_validation = TRUE; break; } } #if 0 if (node_is_required && at == NULL) /* Found invalid characters in the domain */ return NULL; #endif if (!needs_validation) { /* JID is made of only ASCII characters--just lowercase and return */ jid = g_new0(JabberID, 1); if (at) { jid->node = g_ascii_strdown(str, at - str); if (slash) { jid->domain = g_ascii_strdown(at + 1, slash - (at + 1)); if (*(slash + 1)) jid->resource = g_strdup(slash + 1); } else { jid->domain = g_ascii_strdown(at + 1, -1); } } else { if (slash) { jid->domain = g_ascii_strdown(str, slash - str); if (*(slash + 1)) jid->resource = g_strdup(slash + 1); } else { jid->domain = g_ascii_strdown(str, -1); } } return jid; } /* * If we get here, there are some non-ASCII chars in the string, so * we'll need to validate it, normalize, and finally do a full jabber * nodeprep on the jid. */ if (!g_utf8_validate(str, -1, NULL)) return NULL; #ifdef USE_IDN return jabber_idn_validate(str, at, slash, c /* points to the null */); #else /* USE_IDN */ jid = g_new0(JabberID, 1); /* normalization */ if(at) { node = g_utf8_casefold(str, at-str); if(slash) { domain = g_utf8_casefold(at+1, slash-(at+1)); if (*(slash + 1)) jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); } else { domain = g_utf8_casefold(at+1, -1); } } else { if(slash) { domain = g_utf8_casefold(str, slash-str); if (*(slash + 1)) jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); } else { domain = g_utf8_casefold(str, -1); } } if (node) { jid->node = g_utf8_normalize(node, -1, G_NORMALIZE_NFKC); g_free(node); } if (domain) { jid->domain = g_utf8_normalize(domain, -1, G_NORMALIZE_NFKC); g_free(domain); } /* and finally the jabber nodeprep */ if(!jabber_nodeprep_validate(jid->node) || !jabber_domain_validate(jid->domain) || !jabber_resourceprep_validate(jid->resource)) { jabber_id_free(jid); return NULL; } return jid; #endif /* USE_IDN */ }
static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer) { JabberSIXfer *jsx = xfer->data; struct bytestreams_streamhost *streamhost; char *dstaddr, *p; int i; unsigned char hashval[20]; JabberID *dstjid; if(!jsx->streamhosts) { JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR); xmlnode *error, *inf; if(jsx->iq_id) jabber_iq_set_id(iq, jsx->iq_id); xmlnode_set_attrib(iq->node, "to", xfer->who); error = xmlnode_new_child(iq->node, "error"); xmlnode_set_attrib(error, "code", "404"); xmlnode_set_attrib(error, "type", "cancel"); inf = xmlnode_new_child(error, "item-not-found"); xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas"); jabber_iq_send(iq); gaim_xfer_cancel_local(xfer); return; } streamhost = jsx->streamhosts->data; dstjid = jabber_id_new(xfer->who); if(dstjid != NULL) { jsx->gpi = gaim_proxy_info_new(); gaim_proxy_info_set_type(jsx->gpi, GAIM_PROXY_SOCKS5); gaim_proxy_info_set_host(jsx->gpi, streamhost->host); gaim_proxy_info_set_port(jsx->gpi, streamhost->port); dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource, jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource); gaim_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr), sizeof(hashval), hashval, NULL); g_free(dstaddr); dstaddr = g_malloc(41); p = dstaddr; for(i=0; i<20; i++, p+=2) snprintf(p, 3, "%02x", hashval[i]); jsx->connect_data = gaim_proxy_connect_socks5(NULL, jsx->gpi, dstaddr, 0, jabber_si_bytestreams_connect_cb, xfer); g_free(dstaddr); jabber_id_free(dstjid); } if (jsx->connect_data == NULL) { jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); g_free(streamhost->jid); g_free(streamhost->host); g_free(streamhost); jabber_si_bytestreams_attempt_connect(xfer); } }
void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd) { jabber_id_free(jcd->to); g_free(jcd); }
static JabberID* jabber_idn_validate(const char *str, const char *at, const char *slash, const char *null) { const char *node = NULL; const char *domain = NULL; const char *resource = NULL; int node_len = 0; int domain_len = 0; int resource_len = 0; char *out; JabberID *jid; /* Ensure no parts are > 1023 bytes */ if (at) { node = str; node_len = at - str; domain = at + 1; if (slash) { domain_len = slash - (at + 1); resource = slash + 1; resource_len = null - (slash + 1); } else { domain_len = null - (at + 1); } } else { domain = str; if (slash) { domain_len = slash - str; resource = slash; resource_len = null - (slash + 1); } else { domain_len = null - (str + 1); } } if (node && node_len > 1023) return NULL; if (domain_len > 1023) return NULL; if (resource && resource_len > 1023) return NULL; jid = g_new0(JabberID, 1); if (node) { strncpy(idn_buffer, node, node_len); idn_buffer[node_len] = '\0'; if (!jabber_nodeprep(idn_buffer, sizeof(idn_buffer))) { jabber_id_free(jid); jid = NULL; goto out; } jid->node = g_strdup(idn_buffer); } /* domain *must* be here */ strncpy(idn_buffer, domain, domain_len); idn_buffer[domain_len] = '\0'; if (domain[0] == '[') { /* IPv6 address */ gboolean valid = FALSE; if (idn_buffer[domain_len - 1] == ']') { idn_buffer[domain_len - 1] = '\0'; valid = purple_ipv6_address_is_valid(idn_buffer + 1); } if (!valid) { jabber_id_free(jid); jid = NULL; goto out; } } else { /* Apply nameprep */ if (stringprep_nameprep(idn_buffer, sizeof(idn_buffer)) != STRINGPREP_OK) { jabber_id_free(jid); jid = NULL; goto out; } /* And now ToASCII */ if (idna_to_ascii_8z(idn_buffer, &out, IDNA_USE_STD3_ASCII_RULES) != IDNA_SUCCESS) { jabber_id_free(jid); jid = NULL; goto out; } /* This *MUST* be freed using 'free', not 'g_free' */ free(out); jid->domain = g_strdup(idn_buffer); } if (resource) { strncpy(idn_buffer, resource, resource_len); idn_buffer[resource_len] = '\0'; if (!jabber_resourceprep(idn_buffer, sizeof(idn_buffer))) { jabber_id_free(jid); jid = NULL; goto out; } else jid->resource = g_strdup(idn_buffer); } out: return jid; }
void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { JabberStream *js = gc->proto_data; char *who; JabberID *jid; JabberBuddy *jb; JabberBuddyResource *jbr; const char *name; /* If we haven't received the roster yet, ignore any adds */ if (js->state != JABBER_STREAM_CONNECTED) return; name = purple_buddy_get_name(buddy); jid = jabber_id_new(name); if (jid == NULL) { /* TODO: Remove the buddy from the list? */ return; } /* Adding a chat room or a chat buddy to the roster is *not* supported. */ if (jid->node && jabber_chat_find(js, jid->node, jid->domain) != NULL) { /* * This is the same thing Bonjour does. If it causes problems, move * it to an idle callback. */ purple_debug_warning("jabber", "Cowardly refusing to add a MUC user " "to your buddy list and removing the buddy. " "Buddies can only be added by real (non-MUC) " "JID\n"); purple_blist_remove_buddy(buddy); jabber_id_free(jid); return; } who = jabber_id_get_bare_jid(jid); if (jid->resource != NULL) { /* * If the buddy name added contains a resource, strip that off and * rename the buddy. */ purple_blist_rename_buddy(buddy, who); } jb = jabber_buddy_find(js, who, FALSE); purple_debug_info("jabber", "jabber_roster_add_buddy(): Adding %s\n", who); jabber_roster_update(js, who, NULL); if (jb == js->user_jb) { jabber_presence_fake_to_self(js, NULL); } else if(!jb || !(jb->subscription & JABBER_SUB_TO)) { jabber_presence_subscription_set(js, who, "subscribe"); } else if((jbr =jabber_buddy_find_resource(jb, NULL))) { purple_prpl_got_user_status(gc->account, who, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL); } g_free(who); }
void jabber_iq_parse(JabberStream *js, PurpleXmlNode *packet) { JabberIqCallbackData *jcd; PurpleXmlNode *child, *error, *x; const char *xmlns; const char *iq_type, *id, *from; JabberIqType type = JABBER_IQ_NONE; gboolean signal_return; JabberID *from_id; from = purple_xmlnode_get_attrib(packet, "from"); id = purple_xmlnode_get_attrib(packet, "id"); iq_type = purple_xmlnode_get_attrib(packet, "type"); /* * Ensure the 'from' attribute is valid. No point in handling a stanza * of which we don't understand where it came from. */ from_id = jabber_id_new(from); if (from && !from_id) { purple_debug_error("jabber", "Received an iq with an invalid from: %s\n", from); return; } /* * child will be either the first tag child or NULL if there is no child. * Historically, we used just the 'query' subchild, but newer XEPs use * differently named children. Grabbing the first child is (for the time * being) sufficient. */ for (child = packet->child; child; child = child->next) { if (child->type == PURPLE_XMLNODE_TYPE_TAG) break; } if (iq_type) { if (!strcmp(iq_type, "get")) type = JABBER_IQ_GET; else if (!strcmp(iq_type, "set")) type = JABBER_IQ_SET; else if (!strcmp(iq_type, "result")) type = JABBER_IQ_RESULT; else if (!strcmp(iq_type, "error")) type = JABBER_IQ_ERROR; } if (type == JABBER_IQ_NONE) { purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n", iq_type ? iq_type : "(null)"); jabber_id_free(from_id); return; } /* All IQs must have an ID, so send an error for a set/get that doesn't */ if(!id || !*id) { if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); purple_xmlnode_free(iq->node); iq->node = purple_xmlnode_copy(packet); if (from) { purple_xmlnode_set_attrib(iq->node, "to", from); purple_xmlnode_remove_attrib(iq->node, "from"); } purple_xmlnode_set_attrib(iq->node, "type", "error"); /* This id is clearly not useful, but we must put something there for a valid stanza */ iq->id = jabber_get_next_id(js); purple_xmlnode_set_attrib(iq->node, "id", iq->id); error = purple_xmlnode_new_child(iq->node, "error"); purple_xmlnode_set_attrib(error, "type", "modify"); x = purple_xmlnode_new_child(error, "bad-request"); purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS); jabber_iq_send(iq); } else purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", iq_type); jabber_id_free(from_id); return; } signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), "jabber-receiving-iq", js->gc, iq_type, id, from, packet)); if (signal_return) { jabber_id_free(from_id); return; } /* First, lets see if a special callback got registered */ if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) { jcd = g_hash_table_lookup(js->iq_callbacks, id); if (jcd) { if (does_reply_from_match_request_to(js, jcd->to, from_id)) { jcd->callback(js, from, type, id, packet, jcd->data); jabber_iq_remove_callback_by_id(js, id); jabber_id_free(from_id); return; } else { char *expected_to; if (jcd->to) { expected_to = jabber_id_get_full_jid(jcd->to); } else { expected_to = jabber_id_get_bare_jid(js->user); } purple_debug_error("jabber", "Got a result iq with id %s from %s instead of expected %s!\n", id, from ? from : "(null)", expected_to); g_free(expected_to); } } } /* * Apparently not, so let's see if we have a pre-defined handler * or if an outside plugin is interested. */ if(child && (xmlns = purple_xmlnode_get_namespace(child))) { char *key = g_strdup_printf("%s %s", child->name, xmlns); JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key); int signal_ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key)); g_free(key); if (signal_ref > 0) { signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), "jabber-watched-iq", js->gc, iq_type, id, from, child)); if (signal_return) { jabber_id_free(from_id); return; } } if(jih) { jih(js, from, type, id, child); jabber_id_free(from_id); return; } } purple_debug_misc("jabber", "Unhandled IQ with id %s\n", id); /* If we get here, send the default error reply mandated by XMPP-CORE */ if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); purple_xmlnode_free(iq->node); iq->node = purple_xmlnode_copy(packet); if (from) { purple_xmlnode_set_attrib(iq->node, "to", from); purple_xmlnode_remove_attrib(iq->node, "from"); } purple_xmlnode_set_attrib(iq->node, "type", "error"); error = purple_xmlnode_new_child(iq->node, "error"); purple_xmlnode_set_attrib(error, "type", "cancel"); purple_xmlnode_set_attrib(error, "code", "501"); x = purple_xmlnode_new_child(error, "feature-not-implemented"); purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS); jabber_iq_send(iq); } jabber_id_free(from_id); }