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 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; }
void jabber_auth_handle_failure(JabberStream *js, PurpleXmlNode *packet) { PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; char *msg = NULL; if (js->auth_mech && js->auth_mech->handle_failure) { PurpleXmlNode *stanza = NULL; JabberSaslState state = js->auth_mech->handle_failure(js, packet, &stanza, &msg); if (state != JABBER_SASL_STATE_FAIL) { if (stanza) { jabber_send(js, stanza); purple_xmlnode_free(stanza); } return; } } if (!msg) msg = jabber_parse_error(js, packet, &reason); if (!msg) { purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Invalid response from server")); } else { purple_connection_error(js->gc, reason, msg); g_free(msg); } }
static void auth_old_result_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *packet, gpointer data) { if (type == JABBER_IQ_RESULT) { jabber_stream_set_state(js, JABBER_STREAM_POST_AUTH); jabber_disco_items_server(js); } else { PurpleAccount *account; PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; char *msg = jabber_parse_error(js, packet, &reason); PurpleXmlNode *error; const char *err_code; account = purple_connection_get_account(js->gc); /* FIXME: Why is this not in jabber_parse_error? */ if((error = purple_xmlnode_get_child(packet, "error")) && (err_code = purple_xmlnode_get_attrib(error, "code")) && g_str_equal(err_code, "401")) { reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; /* Clear the pasword if it isn't being saved */ if (!purple_account_get_remember_password(account)) purple_account_set_password(account, NULL, NULL, NULL); } purple_connection_error(js->gc, reason, msg); g_free(msg); } }
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 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 jabber_chat_register_x_data_result_cb(JabberStream *js, xmlnode *packet, gpointer data) { const char *type = xmlnode_get_attrib(packet, "type"); if(type && !strcmp(type, "error")) { char *msg = jabber_parse_error(js, packet); purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg); if(msg) g_free(msg); return; } }
static void jabber_chat_register_x_data_result_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, xmlnode *packet, gpointer data) { 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; } }
void jabber_presence_parse(JabberStream *js, xmlnode *packet) { const char *type; JabberBuddyResource *jbr = NULL; gboolean signal_return, ret; JabberPresence presence; xmlnode *child; memset(&presence, 0, sizeof(presence)); /* defaults */ presence.state = JABBER_BUDDY_STATE_UNKNOWN; presence.sent = time(NULL); /* interesting values */ presence.from = xmlnode_get_attrib(packet, "from"); presence.to = xmlnode_get_attrib(packet, "to"); type = xmlnode_get_attrib(packet, "type"); presence.type = str_to_presence_type(type); presence.jb = jabber_buddy_find(js, presence.from, TRUE); g_return_if_fail(presence.jb != NULL); presence.jid_from = jabber_id_new(presence.from); if (presence.jid_from == NULL) { purple_debug_error("jabber", "Ignoring presence with malformed 'from' " "JID: %s\n", presence.from); return; } signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), "jabber-receiving-presence", js->gc, type, presence.from, packet)); if (signal_return) { goto out; } if (presence.jid_from->node) presence.chat = jabber_chat_find(js, presence.jid_from->node, presence.jid_from->domain); if(presence.jb->error_msg) { g_free(presence.jb->error_msg); presence.jb->error_msg = NULL; } if (presence.type == JABBER_PRESENCE_AVAILABLE) { presence.state = JABBER_BUDDY_STATE_ONLINE; } else if (presence.type == JABBER_PRESENCE_ERROR) { /* TODO: Is this handled properly? Should it be treated as per-jbr? */ char *msg = jabber_parse_error(js, packet, NULL); presence.state = JABBER_BUDDY_STATE_ERROR; presence.jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence")); } else if (presence.type == JABBER_PRESENCE_SUBSCRIBE) { /* TODO: Move to handle_subscribe() (so nick is extracted by the * PresenceHandler */ struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1); gboolean onlist = FALSE; PurpleAccount *account; PurpleBuddy *buddy; xmlnode *nick; account = purple_connection_get_account(js->gc); buddy = purple_find_buddy(account, presence.from); nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick"); if (nick) presence.nickname = xmlnode_get_data(nick); if (buddy) { if ((presence.jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING))) onlist = TRUE; } jap->gc = js->gc; jap->who = g_strdup(presence.from); jap->js = js; purple_account_request_authorization(account, presence.from, NULL, presence.nickname, NULL, onlist, authorize_add_cb, deny_add_cb, jap); goto out; } else if (presence.type == JABBER_PRESENCE_SUBSCRIBED) {
static gboolean handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet) { static int i = 1; PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; JabberChat *chat = presence->chat; if (presence->state == JABBER_BUDDY_STATE_ERROR) { char *title, *msg = jabber_parse_error(js, packet, NULL); if (!chat->conv) { title = g_strdup_printf(_("Error joining chat %s"), presence->from); purple_serv_got_join_chat_failed(js->gc, chat->components); } else { title = g_strdup_printf(_("Error in chat %s"), presence->from); if (g_hash_table_size(chat->members) == 0) serv_got_chat_left(js->gc, chat->id); } purple_notify_error(js->gc, title, title, msg); g_free(title); g_free(msg); if (g_hash_table_size(chat->members) == 0) /* Only destroy the chat if the error happened while joining */ jabber_chat_destroy(chat); return FALSE; } if (presence->type == JABBER_PRESENCE_AVAILABLE) { const char *jid = NULL; const char *affiliation = NULL; const char *role = NULL; gboolean is_our_resource = FALSE; /* Is the presence about us? */ JabberBuddyResource *jbr; /* * XEP-0045 mandates the presence to include a resource (which is * treated as the chat nick). Some non-compliant servers allow * joining without a nick. */ if (!presence->jid_from->resource) return FALSE; if (presence->chat_info.item) { jid = xmlnode_get_attrib(presence->chat_info.item, "jid"); affiliation = xmlnode_get_attrib(presence->chat_info.item, "affiliation"); role = xmlnode_get_attrib(presence->chat_info.item, "role"); } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110)) || g_str_equal(presence->jid_from->resource, chat->handle) || purple_strequal(presence->to, jid)) is_our_resource = TRUE; if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(201))) { chat->config_dialog_type = PURPLE_REQUEST_ACTION; chat->config_dialog_handle = purple_request_action(js->gc, _("Create New Room"), _("Create New Room"), _("You are creating a new room. Would" " you like to configure it, or" " accept the default settings?"), /* Default Action */ 1, purple_connection_get_account(js->gc), NULL, chat->conv, chat, 2, _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(210))) { /* server rewrote room-nick */ g_free(chat->handle); chat->handle = g_strdup(presence->jid_from->resource); } if (purple_strequal(affiliation, "owner")) flags |= PURPLE_CBFLAGS_FOUNDER; if (role) { if (g_str_equal(role, "moderator")) flags |= PURPLE_CBFLAGS_OP; else if (g_str_equal(role, "participant")) flags |= PURPLE_CBFLAGS_VOICE; } if(!chat->conv) { char *room_jid = g_strdup_printf("%s@%s", presence->jid_from->node, presence->jid_from->domain); chat->id = i++; chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid); purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle); jabber_chat_disco_traffic(chat); g_free(room_jid); } jbr = jabber_buddy_track_resource(presence->jb, presence->jid_from->resource, presence->priority, presence->state, presence->status); jbr->commands_fetched = TRUE; jabber_chat_track_handle(chat, presence->jid_from->resource, jid, affiliation, role); if(!jabber_chat_find_buddy(chat->conv, presence->jid_from->resource)) purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, jid, flags, chat->joined > 0 && ((!presence->delayed) || (presence->sent > chat->joined))); else purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, flags); if (is_our_resource && chat->joined == 0) chat->joined = time(NULL); } else if (presence->type == JABBER_PRESENCE_UNAVAILABLE) { gboolean nick_change = FALSE; gboolean kick = FALSE; gboolean is_our_resource = FALSE; /* Is the presence about us? */ const char *jid = NULL; /* If the chat nick is invalid, we haven't yet joined, or we've * already left (it was probably us leaving after we closed the * chat), we don't care. */ if (!presence->jid_from->resource || !chat->conv || chat->left) { if (chat->left && presence->jid_from->resource && chat->handle && !strcmp(presence->jid_from->resource, chat->handle)) jabber_chat_destroy(chat); return FALSE; } is_our_resource = g_str_equal(presence->jid_from->resource, chat->handle); jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); if (presence->chat_info.item) jid = xmlnode_get_attrib(presence->chat_info.item, "jid"); if (chat->muc) { if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110))) { is_our_resource = TRUE; chat->joined = 0; } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(301))) { /* XXX: We got banned. YAY! (No GIR, that's bad) */ } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(303))) { const char *nick = NULL; if (presence->chat_info.item) nick = xmlnode_get_attrib(presence->chat_info.item, "nick"); /* nick change */ if (!nick) { purple_debug_warning("jabber", "Chat presence indicating a nick change, but no new nickname!\n"); } else { nick_change = TRUE; if (g_str_equal(presence->jid_from->resource, chat->handle)) { /* Changing our own nickname */ g_free(chat->handle); /* TODO: This should be resourceprep'd */ chat->handle = g_strdup(nick); } purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, nick); jabber_chat_remove_handle(chat, presence->jid_from->resource); } } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(307))) { /* Someone was kicked from the room */ const char *actor = NULL; char *reason = NULL; char *tmp; kick = TRUE; if (presence->chat_info.item) { xmlnode *node; node = xmlnode_get_child(presence->chat_info.item, "actor"); if (node) actor = xmlnode_get_attrib(node, "jid"); node = xmlnode_get_child(presence->chat_info.item, "reason"); if (node) reason = xmlnode_get_data(node); } if (reason == NULL) reason = g_strdup(_("No reason")); if (is_our_resource) { if (actor) tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"), actor, reason); else tmp = g_strdup_printf(_("You have been kicked: (%s)"), reason); } else { if (actor) tmp = g_strdup_printf(_("Kicked by %s (%s)"), actor, reason); else tmp = g_strdup_printf(_("Kicked (%s)"), reason); } g_free(presence->status); presence->status = tmp; g_free(reason); } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(321))) { /* XXX: removed due to an affiliation change */ } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(322))) { /* XXX: removed because room is now members-only */ } if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(332))) { /* XXX: removed due to system shutdown */ } } /* * Possibly another connected resource of our JID (see XEP-0045 * v1.24 section 7.1.10) being disconnected. Should be * distinguished by the item_jid. * Also possibly works around bits of an Openfire bug. See * #8319. */ if (is_our_resource && jid && !purple_strequal(presence->to, jid)) { /* TODO: When the above is a loop, this needs to still act * sanely for all cases (this code is a little fragile). */ if (!kick && !nick_change) /* Presumably, kicks and nick changes also affect us. */ is_our_resource = FALSE; } if(!nick_change) { if (is_our_resource) { if (kick) purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, presence->status, PURPLE_MESSAGE_SYSTEM, time(NULL)); serv_got_chat_left(js->gc, chat->id); jabber_chat_destroy(chat); } else { purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, presence->status); jabber_chat_remove_handle(chat, presence->jid_from->resource); } } } return TRUE; }
static void auth_old_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *packet, gpointer data) { JabberIq *iq; PurpleXmlNode *query, *x; const char *pw = purple_connection_get_password(js->gc); if (type == JABBER_IQ_ERROR) { PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; char *msg = jabber_parse_error(js, packet, &reason); purple_connection_error(js->gc, reason, msg); g_free(msg); } else if (type == JABBER_IQ_RESULT) { query = purple_xmlnode_get_child(packet, "query"); if (js->stream_id && *js->stream_id && purple_xmlnode_get_child(query, "digest")) { char *s, *hash; iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); x = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(x, js->user->node, -1); x = purple_xmlnode_new_child(query, "resource"); purple_xmlnode_insert_data(x, js->user->resource, -1); x = purple_xmlnode_new_child(query, "digest"); s = g_strdup_printf("%s%s", js->stream_id, pw); hash = jabber_calculate_data_hash(s, strlen(s), "sha1"); purple_xmlnode_insert_data(x, hash, -1); g_free(hash); g_free(s); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } else if ((x = purple_xmlnode_get_child(query, "crammd5"))) { /* For future reference, this appears to be a custom OS X extension * to non-SASL authentication. */ const char *challenge; gchar digest[33]; PurpleCipher *hmac; PurpleHash *md5; gssize diglen; /* Calculate the MHAC-MD5 digest */ md5 = purple_md5_hash_new(); hmac = purple_hmac_cipher_new(md5); challenge = purple_xmlnode_get_attrib(x, "challenge"); purple_cipher_set_key(hmac, (guchar *)pw, strlen(pw)); purple_cipher_append(hmac, (guchar *)challenge, strlen(challenge)); diglen = purple_cipher_digest_to_str(hmac, digest, 33); g_object_unref(hmac); g_object_unref(md5); g_return_if_fail(diglen > 0); /* Create the response query */ iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth"); query = purple_xmlnode_get_child(iq->node, "query"); x = purple_xmlnode_new_child(query, "username"); purple_xmlnode_insert_data(x, js->user->node, -1); x = purple_xmlnode_new_child(query, "resource"); purple_xmlnode_insert_data(x, js->user->resource, -1); x = purple_xmlnode_new_child(query, "crammd5"); purple_xmlnode_insert_data(x, digest, 32); jabber_iq_set_callback(iq, auth_old_result_cb, NULL); jabber_iq_send(iq); } else if(purple_xmlnode_get_child(query, "password")) { PurpleAccount *account = purple_connection_get_account(js->gc); if(!jabber_stream_is_ssl(js) && !purple_account_get_bool(account, "auth_plain_in_clear", FALSE)) { char *msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), purple_account_get_username(account)); purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, 1, purple_request_cpar_from_account(account), account, allow_plaintext_auth, disallow_plaintext_auth); g_free(msg); return; } finish_plaintext_authentication(js); } else { purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, _("Server does not use any supported authentication method")); return; } } }