void jabber_gmail_init(JabberStream *js) { JabberIq *iq; PurpleXmlNode *usersetting, *mailnotifications; if (!purple_account_get_check_mail(purple_connection_get_account(js->gc))) return; /* * Quoting https://developers.google.com/talk/jep_extensions/usersettings: * To ensure better compatibility with other clients, rather than * setting this value to "false" to turn off notifications, it is * recommended that a client set this to "true" and filter incoming * email notifications itself. */ iq = jabber_iq_new(js, JABBER_IQ_SET); usersetting = purple_xmlnode_new_child(iq->node, "usersetting"); purple_xmlnode_set_namespace(usersetting, "google:setting"); mailnotifications = purple_xmlnode_new_child(usersetting, "mailnotifications"); purple_xmlnode_set_attrib(mailnotifications, "value", "true"); jabber_iq_send(iq); iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_GOOGLE_MAIL_NOTIFY); jabber_iq_set_callback(iq, jabber_gmail_parse, NULL); jabber_iq_send(iq); }
static PurpleXmlNode * jingle_add_jingle_packet(JingleSession *session, JabberIq *iq, JingleActionType action) { PurpleXmlNode *jingle = iq ? purple_xmlnode_new_child(iq->node, "jingle") : purple_xmlnode_new("jingle"); gchar *local_jid = jingle_session_get_local_jid(session); gchar *remote_jid = jingle_session_get_remote_jid(session); gchar *sid = jingle_session_get_sid(session); purple_xmlnode_set_namespace(jingle, JINGLE); purple_xmlnode_set_attrib(jingle, "action", jingle_get_action_name(action)); if (jingle_session_is_initiator(session)) { purple_xmlnode_set_attrib(jingle, "initiator", local_jid); purple_xmlnode_set_attrib(jingle, "responder", remote_jid); } else { purple_xmlnode_set_attrib(jingle, "initiator", remote_jid); purple_xmlnode_set_attrib(jingle, "responder", local_jid); } purple_xmlnode_set_attrib(jingle, "sid", sid); g_free(local_jid); g_free(remote_jid); g_free(sid); return jingle; }
static PurpleXmlNode * google_session_create_xmlnode(GoogleSession *session, const char *type) { PurpleXmlNode *node = purple_xmlnode_new("session"); purple_xmlnode_set_namespace(node, NS_GOOGLE_SESSION); purple_xmlnode_set_attrib(node, "id", session->id.id); purple_xmlnode_set_attrib(node, "initiator", session->id.initiator); purple_xmlnode_set_attrib(node, "type", type); return node; }
static JabberSaslState digest_md5_start(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **response, char **error) { PurpleXmlNode *auth = purple_xmlnode_new("auth"); purple_xmlnode_set_namespace(auth, NS_XMPP_SASL); purple_xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5"); *response = auth; return JABBER_SASL_STATE_CONTINUE; }
JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type, const char *xmlns) { JabberIq *iq = jabber_iq_new(js, type); PurpleXmlNode *query; query = purple_xmlnode_new_child(iq->node, "query"); purple_xmlnode_set_namespace(query, xmlns); return iq; }
void purple_xmlnode_strip_prefixes(PurpleXmlNode *node) { PurpleXmlNode *child; const char *prefix; g_return_if_fail(node != NULL); for (child = node->child; child; child = child->next) { if (child->type == PURPLE_XMLNODE_TYPE_TAG) purple_xmlnode_strip_prefixes(child); } prefix = purple_xmlnode_get_prefix(node); if (prefix) { const char *ns = purple_xmlnode_get_prefix_namespace(node, prefix); purple_xmlnode_set_namespace(node, ns); purple_xmlnode_set_prefix(node, NULL); } else { purple_xmlnode_set_namespace(node, purple_xmlnode_get_default_namespace(node)); } }
static void xep_ft_si_result(PurpleXfer *xfer, const char *to) { PurpleXmlNode *si_node, *feature, *field, *value, *x; XepIq *iq; XepXfer *xf; BonjourData *bd; if(!to || !xfer) return; xf = purple_xfer_get_protocol_data(xfer); if(!xf) return; bd = xf->data; purple_debug_info("bonjour", "xep file transfer stream initialization result.\n"); iq = xep_iq_new(bd, XEP_IQ_RESULT, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id); if(iq == NULL) return; si_node = purple_xmlnode_new_child(iq->node, "si"); purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); /*purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/ feature = purple_xmlnode_new_child(si_node, "feature"); purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = purple_xmlnode_new_child(feature, "x"); purple_xmlnode_set_namespace(x, "jabber:x:data"); purple_xmlnode_set_attrib(x, "type", "submit"); field = purple_xmlnode_new_child(x, "field"); purple_xmlnode_set_attrib(field, "var", "stream-method"); value = purple_xmlnode_new_child(field, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); xep_iq_send_and_free(iq); }
static void xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type) { PurpleXmlNode *error_node; XepIq *iq; g_return_if_fail(error_code != NULL); g_return_if_fail(error_type != NULL); if(!to || !id) { purple_debug_info("bonjour", "xep file transfer stream initialization error.\n"); return; } iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->jabber_data->account), id); if(iq == NULL) return; error_node = purple_xmlnode_new_child(iq->node, "error"); purple_xmlnode_set_attrib(error_node, "code", error_code); purple_xmlnode_set_attrib(error_node, "type", error_type); /* TODO: Make this better */ if (!strcmp(error_code, "403")) { PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "forbidden"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); tmp_node = purple_xmlnode_new_child(error_node, "text"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); purple_xmlnode_insert_data(tmp_node, "Offer Declined", -1); } else if (!strcmp(error_code, "404")) { PurpleXmlNode *tmp_node = purple_xmlnode_new_child(error_node, "item-not-found"); purple_xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); } xep_iq_send_and_free(iq); }
static void purple_xmlnode_parser_element_start_libxml(void *user_data, const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) { struct _xmlnode_parser_data *xpd = user_data; PurpleXmlNode *node; int i, j; if(!element_name || xpd->error) { return; } else { if(xpd->current) node = purple_xmlnode_new_child(xpd->current, (const char*) element_name); else node = purple_xmlnode_new((const char *) element_name); purple_xmlnode_set_namespace(node, (const char *) xmlns); purple_xmlnode_set_prefix(node, (const char *)prefix); if (nb_namespaces != 0) { node->namespace_map = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free); for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) { const char *key = (const char *)namespaces[j]; const char *val = (const char *)namespaces[j + 1]; g_hash_table_insert(node->namespace_map, g_strdup(key ? key : ""), g_strdup(val ? val : "")); } } for(i=0; i < nb_attributes * 5; i+=5) { const char *name = (const char *)attributes[i]; const char *prefix = (const char *)attributes[i+1]; char *txt; int attrib_len = attributes[i+4] - attributes[i+3]; char *attrib = g_strndup((const char *)attributes[i+3], attrib_len); txt = attrib; attrib = purple_unescape_text(txt); g_free(txt); purple_xmlnode_set_attrib_full(node, name, NULL, prefix, attrib); g_free(attrib); } xpd->current = node; } }
static void do_nick_set(JabberStream *js, const char *nick) { PurpleXmlNode *publish, *nicknode; publish = purple_xmlnode_new("publish"); purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/nick"); nicknode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "nick"); purple_xmlnode_set_namespace(nicknode, "http://jabber.org/protocol/nick"); if(nick && nick[0] != '\0') purple_xmlnode_insert_data(nicknode, nick, -1); jabber_pep_publish(js, publish); /* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free (yay for well-defined memory management rules) */ }
static void bonjour_bytestreams_listen(int sock, gpointer data) { PurpleXfer *xfer = data; XepXfer *xf; XepIq *iq; PurpleXmlNode *query, *streamhost; gchar *port; GSList *local_ips; BonjourData *bd; purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock); if (sock < 0 || xfer == NULL) { /*purple_xfer_cancel_local(xfer);*/ return; } purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ, bonjour_sock5_request_cb, xfer)); xf = purple_xfer_get_protocol_data(xfer); xf->listen_data = NULL; bd = xf->data; iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->sid); query = purple_xmlnode_new_child(iq->node, "query"); purple_xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams"); purple_xmlnode_set_attrib(query, "sid", xf->sid); purple_xmlnode_set_attrib(query, "mode", "tcp"); purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock)); local_ips = bonjour_jabber_get_local_ips(sock); port = g_strdup_printf("%hu", purple_xfer_get_local_port(xfer)); while(local_ips) { streamhost = purple_xmlnode_new_child(query, "streamhost"); purple_xmlnode_set_attrib(streamhost, "jid", xf->sid); purple_xmlnode_set_attrib(streamhost, "host", local_ips->data); purple_xmlnode_set_attrib(streamhost, "port", port); g_free(local_ips->data); local_ips = g_slist_delete_link(local_ips, local_ips); } g_free(port); xep_iq_send_and_free(iq); }
static void jabber_ibb_send_error_response(JabberStream *js, const char *to, const char *id) { JabberIq *result = jabber_iq_new(js, JABBER_IQ_ERROR); PurpleXmlNode *error = purple_xmlnode_new("error"); PurpleXmlNode *item_not_found = purple_xmlnode_new("item-not-found"); purple_xmlnode_set_namespace(item_not_found, NS_XMPP_STANZAS); purple_xmlnode_set_attrib(error, "code", "440"); purple_xmlnode_set_attrib(error, "type", "cancel"); jabber_iq_set_id(result, id); purple_xmlnode_set_attrib(result->node, "to", to); purple_xmlnode_insert_child(error, item_not_found); purple_xmlnode_insert_child(result->node, error); jabber_iq_send(result); }
static void jabber_bosh_connection_recv(PurpleHttpConnection *http_conn, PurpleHttpResponse *response, gpointer _bosh_conn) { PurpleJabberBOSHConnection *bosh_conn = _bosh_conn; PurpleXmlNode *node, *child; if (purple_debug_is_verbose() && purple_debug_is_unsafe()) { purple_debug_misc("jabber-bosh", "received: %s\n", purple_http_response_get_data(response, NULL)); } node = jabber_bosh_connection_parse(bosh_conn, response); if (node == NULL) return; child = node->child; while (child != NULL) { /* jabber_process_packet might free child */ PurpleXmlNode *next = child->next; const gchar *xmlns; if (child->type != PURPLE_XMLNODE_TYPE_TAG) { child = next; continue; } /* Workaround for non-compliant servers that don't stamp * the right xmlns on these packets. See #11315. */ xmlns = purple_xmlnode_get_namespace(child); if ((xmlns == NULL || g_strcmp0(xmlns, NS_BOSH) == 0) && (g_strcmp0(child->name, "iq") == 0 || g_strcmp0(child->name, "message") == 0 || g_strcmp0(child->name, "presence") == 0)) { purple_xmlnode_set_namespace(child, NS_XMPP_CLIENT); } jabber_process_packet(bosh_conn->js, &child); child = next; } jabber_bosh_connection_send(bosh_conn, NULL); }
void jabber_ibb_session_send_data(JabberIBBSession *sess, gconstpointer data, gsize size) { JabberIBBSessionState state = jabber_ibb_session_get_state(sess); purple_debug_info("jabber", "sending data block of %" G_GSIZE_FORMAT " bytes on IBB stream\n", size); if (state != JABBER_IBB_SESSION_OPENED) { purple_debug_error("jabber", "trying to send data on a non-open IBB session\n"); } else if (size > jabber_ibb_session_get_max_data_size(sess)) { purple_debug_error("jabber", "trying to send a too large packet in the IBB session\n"); } else { JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess), JABBER_IQ_SET); PurpleXmlNode *data_element = purple_xmlnode_new("data"); char *base64 = purple_base64_encode(data, size); char seq[10]; g_snprintf(seq, sizeof(seq), "%u", jabber_ibb_session_get_send_seq(sess)); purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess)); purple_xmlnode_set_namespace(data_element, NS_IBB); purple_xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess)); purple_xmlnode_set_attrib(data_element, "seq", seq); purple_xmlnode_insert_data(data_element, base64, -1); purple_xmlnode_insert_child(set->node, data_element); purple_debug_info("jabber", "IBB: setting send <iq/> callback for session %p %s\n", sess, sess->sid); jabber_iq_set_callback(set, jabber_ibb_session_send_acknowledge_cb, sess); sess->last_iq_id = g_strdup(purple_xmlnode_get_attrib(set->node, "id")); purple_debug_info("jabber", "IBB: set sess->last_iq_id: %s\n", sess->last_iq_id); jabber_iq_send(set); g_free(base64); (sess->send_seq)++; } }
static void bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message) { PurpleXfer *xfer = data; XepXfer *xf = purple_xfer_get_protocol_data(xfer); XepIq *iq; PurpleXmlNode *q_node, *tmp_node; BonjourData *bd; gboolean ret = FALSE; xf->proxy_connection = NULL; if(source < 0) { purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s\n", xf->proxy_host, error_message ? error_message : "(null)"); tmp_node = purple_xmlnode_get_next_twin(xf->streamhost); ret = __xep_bytestreams_parse(xf->pb, xfer, tmp_node, xf->iq_id); if (!ret) { xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel"); /* Cancel the connection */ purple_xfer_cancel_local(xfer); } return; } purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n"); bd = xf->data; /* Here, start the file transfer.*/ /* Notify Initiator of Connection */ iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->iq_id); q_node = purple_xmlnode_new_child(iq->node, "query"); purple_xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams"); tmp_node = purple_xmlnode_new_child(q_node, "streamhost-used"); purple_xmlnode_set_attrib(tmp_node, "jid", xf->jid); xep_iq_send_and_free(iq); purple_xfer_start(xfer, source, NULL, -1); }
void jabber_ibb_session_close(JabberIBBSession *sess) { JabberIBBSessionState state = jabber_ibb_session_get_state(sess); if (state != JABBER_IBB_SESSION_OPENED && state != JABBER_IBB_SESSION_ERROR) { purple_debug_error("jabber", "jabber_ibb_session_close called on a session that has not been" "opened\n"); } else { JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess), JABBER_IQ_SET); PurpleXmlNode *close = purple_xmlnode_new("close"); purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess)); purple_xmlnode_set_namespace(close, NS_IBB); purple_xmlnode_set_attrib(close, "sid", jabber_ibb_session_get_sid(sess)); purple_xmlnode_insert_child(set->node, close); jabber_iq_send(set); sess->state = JABBER_IBB_SESSION_CLOSED; } }
static void jabber_time_parse(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *child) { JabberIq *iq; time_t now_t; struct tm *tm; time(&now_t); if(type == JABBER_IQ_GET) { PurpleXmlNode *tzo, *utc; const char *date, *tz; iq = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(iq, id); if (from) purple_xmlnode_set_attrib(iq->node, "to", from); child = purple_xmlnode_new_child(iq->node, child->name); purple_xmlnode_set_namespace(child, NS_ENTITY_TIME); /* <tzo>-06:00</tzo> */ tm = localtime(&now_t); tz = purple_get_tzoff_str(tm, TRUE); tzo = purple_xmlnode_new_child(child, "tzo"); purple_xmlnode_insert_data(tzo, tz, -1); /* <utc>2006-12-19T17:58:35Z</utc> */ tm = gmtime(&now_t); date = purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm); utc = purple_xmlnode_new_child(child, "utc"); purple_xmlnode_insert_data(utc, date, -1); jabber_iq_send(iq); } else { /* TODO: Errors */ } }
void jabber_ibb_session_open(JabberIBBSession *sess) { if (jabber_ibb_session_get_state(sess) != JABBER_IBB_SESSION_NOT_OPENED) { purple_debug_error("jabber", "jabber_ibb_session called on an already open stream\n"); } else { JabberIq *set = jabber_iq_new(sess->js, JABBER_IQ_SET); PurpleXmlNode *open = purple_xmlnode_new("open"); gchar block_size[10]; purple_xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess)); purple_xmlnode_set_namespace(open, NS_IBB); purple_xmlnode_set_attrib(open, "sid", jabber_ibb_session_get_sid(sess)); g_snprintf(block_size, sizeof(block_size), "%" G_GSIZE_FORMAT, jabber_ibb_session_get_block_size(sess)); purple_xmlnode_set_attrib(open, "block-size", block_size); purple_xmlnode_insert_child(set->node, open); jabber_iq_set_callback(set, jabber_ibb_session_opened_cb, sess); jabber_iq_send(set); } }
gboolean jabber_mood_set(JabberStream *js, const char *mood, const char *text) { const PurpleMood *target_mood = NULL; PurpleXmlNode *publish, *moodnode; if (mood && *mood) { target_mood = find_mood_by_name(mood); /* Mood specified, but is invalid -- * fail so that the command can handle this. */ if (!target_mood) return FALSE; } publish = purple_xmlnode_new("publish"); purple_xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood"); moodnode = purple_xmlnode_new_child(purple_xmlnode_new_child(publish, "item"), "mood"); purple_xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood"); if (target_mood) { /* If target_mood is not NULL, then * target_mood->mood == mood, and is a valid element name. */ purple_xmlnode_new_child(moodnode, mood); /* Only set text when setting a mood */ if (text && *text) { PurpleXmlNode *textnode = purple_xmlnode_new_child(moodnode, "text"); purple_xmlnode_insert_data(textnode, text, -1); } } jabber_pep_publish(js, publish); return TRUE; }
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); }
static JabberSaslState scram_start(JabberStream *js, PurpleXmlNode *mechanisms, PurpleXmlNode **out, char **error) { PurpleXmlNode *reply; JabberScramData *data; guint64 cnonce; #ifdef CHANNEL_BINDING gboolean binding_supported = TRUE; #endif gchar *dec_out, *enc_out; gchar *prepped_node, *tmp; gchar *prepped_pass; prepped_node = jabber_saslprep(js->user->node); if (!prepped_node) { *error = g_strdup(_("Unable to canonicalize username")); return JABBER_SASL_STATE_FAIL; } tmp = escape_username(prepped_node); g_free(prepped_node); prepped_node = tmp; prepped_pass = jabber_saslprep(purple_connection_get_password(js->gc)); if (!prepped_pass) { g_free(prepped_node); *error = g_strdup(_("Unable to canonicalize password")); return JABBER_SASL_STATE_FAIL; } data = js->auth_mech_data = g_new0(JabberScramData, 1); data->hash = mech_to_hash(js->auth_mech->name); data->password = prepped_pass; #ifdef CHANNEL_BINDING if (strstr(js->auth_mech_name, "-PLUS")) data->channel_binding = TRUE; #endif cnonce = ((guint64)g_random_int() << 32) | g_random_int(); data->cnonce = purple_base64_encode((guchar *)&cnonce, sizeof(cnonce)); data->auth_message = g_string_new(NULL); g_string_printf(data->auth_message, "n=%s,r=%s", prepped_node, data->cnonce); g_free(prepped_node); data->step = 1; reply = purple_xmlnode_new("auth"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); purple_xmlnode_set_attrib(reply, "mechanism", js->auth_mech->name); /* TODO: Channel binding */ dec_out = g_strdup_printf("%c,,%s", 'n', data->auth_message->str); enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out)); purple_debug_misc("jabber", "initial SCRAM message '%s'\n", dec_out); purple_xmlnode_insert_data(reply, enc_out, -1); g_free(enc_out); g_free(dec_out); *out = reply; return JABBER_SASL_STATE_CONTINUE; }
static void xep_ft_si_offer(PurpleXfer *xfer, const gchar *to) { PurpleXmlNode *si_node, *feature, *field, *file, *x; XepIq *iq; XepXfer *xf = purple_xfer_get_protocol_data(xfer); BonjourData *bd = NULL; char buf[32]; if(!xf) return; bd = xf->data; if(!bd) return; purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id); /* Assign stream id. */ g_free(xf->iq_id); xf->iq_id = g_strdup_printf("%u", next_id++); iq = xep_iq_new(xf->data, XEP_IQ_SET, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id); if(iq == NULL) return; /*Construct Stream initialization offer message.*/ si_node = purple_xmlnode_new_child(iq->node, "si"); purple_xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si"); purple_xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer"); g_free(xf->sid); xf->sid = g_strdup(xf->iq_id); purple_xmlnode_set_attrib(si_node, "id", xf->sid); file = purple_xmlnode_new_child(si_node, "file"); purple_xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer"); purple_xmlnode_set_attrib(file, "name", purple_xfer_get_filename(xfer)); g_snprintf(buf, sizeof(buf), "%" G_GOFFSET_FORMAT, purple_xfer_get_size(xfer)); purple_xmlnode_set_attrib(file, "size", buf); feature = purple_xmlnode_new_child(si_node, "feature"); purple_xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = purple_xmlnode_new_child(feature, "x"); purple_xmlnode_set_namespace(x, "jabber:x:data"); purple_xmlnode_set_attrib(x, "type", "form"); field = purple_xmlnode_new_child(x, "field"); purple_xmlnode_set_attrib(field, "var", "stream-method"); purple_xmlnode_set_attrib(field, "type", "list-single"); if (xf->mode & XEP_BYTESTREAMS) { PurpleXmlNode *option = purple_xmlnode_new_child(field, "option"); PurpleXmlNode *value = purple_xmlnode_new_child(option, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); } if (xf->mode & XEP_IBB) { PurpleXmlNode *option = purple_xmlnode_new_child(field, "option"); PurpleXmlNode *value = purple_xmlnode_new_child(option, "value"); purple_xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); } xep_iq_send_and_free(iq); }
static JabberSaslState scram_handle_challenge(JabberStream *js, PurpleXmlNode *challenge, PurpleXmlNode **out, char **error) { JabberScramData *data = js->auth_mech_data; PurpleXmlNode *reply; gchar *enc_in, *dec_in = NULL; gchar *enc_out = NULL, *dec_out = NULL; gsize len; JabberSaslState state = JABBER_SASL_STATE_FAIL; enc_in = purple_xmlnode_get_data(challenge); if (!enc_in || *enc_in == '\0') { reply = purple_xmlnode_new("abort"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; *error = g_strdup(_("Invalid challenge from server")); goto out; } dec_in = (gchar *)purple_base64_decode(enc_in, &len); if (!dec_in || len != strlen(dec_in)) { /* Danger afoot; SCRAM shouldn't contain NUL bytes */ reply = purple_xmlnode_new("abort"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; *error = g_strdup(_("Malicious challenge from server")); goto out; } purple_debug_misc("jabber", "decoded challenge: %s\n", dec_in); if (!jabber_scram_feed_parser(data, dec_in, &dec_out)) { reply = purple_xmlnode_new("abort"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); data->step = -1; *error = g_strdup(_("Invalid challenge from server")); goto out; } data->step += 1; reply = purple_xmlnode_new("response"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); purple_debug_misc("jabber", "decoded response: %s\n", dec_out ? dec_out : "(null)"); if (dec_out) { enc_out = purple_base64_encode((guchar *)dec_out, strlen(dec_out)); purple_xmlnode_insert_data(reply, enc_out, -1); } state = JABBER_SASL_STATE_CONTINUE; out: g_free(enc_in); g_free(dec_in); g_free(enc_out); g_free(dec_out); *out = reply; return state; }
static JabberSaslState digest_md5_handle_challenge(JabberStream *js, PurpleXmlNode *packet, PurpleXmlNode **response, char **msg) { PurpleXmlNode *reply = NULL; char *enc_in = purple_xmlnode_get_data(packet); char *dec_in; char *enc_out; GHashTable *parts; JabberSaslState state = JABBER_SASL_STATE_CONTINUE; if (!enc_in) { *msg = g_strdup(_("Invalid response from server")); return JABBER_SASL_STATE_FAIL; } dec_in = (char *)purple_base64_decode(enc_in, NULL); purple_debug_misc("jabber", "decoded challenge (%" G_GSIZE_FORMAT "): %s\n", strlen(dec_in), dec_in); parts = jabber_auth_digest_md5_parse(dec_in); if (g_hash_table_lookup(parts, "rspauth")) { char *rspauth = g_hash_table_lookup(parts, "rspauth"); char *expected_rspauth = js->auth_mech_data; if (rspauth && purple_strequal(rspauth, expected_rspauth)) { reply = purple_xmlnode_new("response"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); } else { *msg = g_strdup(_("Invalid challenge from server")); state = JABBER_SASL_STATE_FAIL; } g_free(js->auth_mech_data); js->auth_mech_data = NULL; } else { /* assemble a response, and send it */ /* see RFC 2831 */ char *realm; char *nonce; /* Make sure the auth string contains everything that should be there. This isn't everything in RFC2831, but it is what we need. */ nonce = g_hash_table_lookup(parts, "nonce"); /* we're actually supposed to prompt the user for a realm if * the server doesn't send one, but that really complicates things, * so i'm not gonna worry about it until is poses a problem to * someone, or I get really bored */ realm = g_hash_table_lookup(parts, "realm"); if(!realm) realm = js->user->domain; if (nonce == NULL || realm == NULL) { *msg = g_strdup(_("Invalid challenge from server")); state = JABBER_SASL_STATE_FAIL; } else { GString *response = g_string_new(""); char *a2; char *auth_resp; char *cnonce; cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), g_random_int()); a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); auth_resp = generate_response_value(js->user, purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); g_free(a2); a2 = g_strdup_printf(":xmpp/%s", realm); js->auth_mech_data = generate_response_value(js->user, purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); g_free(a2); g_string_append_printf(response, "username=\"%s\"", js->user->node); g_string_append_printf(response, ",realm=\"%s\"", realm); g_string_append_printf(response, ",nonce=\"%s\"", nonce); g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); g_string_append_printf(response, ",nc=00000001"); g_string_append_printf(response, ",qop=auth"); g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); g_string_append_printf(response, ",response=%s", auth_resp); g_string_append_printf(response, ",charset=utf-8"); g_free(auth_resp); g_free(cnonce); enc_out = purple_base64_encode((guchar *)response->str, response->len); purple_debug_misc("jabber", "decoded response (%" G_GSIZE_FORMAT "): %s\n", response->len, response->str); reply = purple_xmlnode_new("response"); purple_xmlnode_set_namespace(reply, NS_XMPP_SASL); purple_xmlnode_insert_data(reply, enc_out, -1); g_free(enc_out); g_string_free(response, TRUE); } } g_free(enc_in); g_free(dec_in); g_hash_table_destroy(parts); *response = reply; return state; }
static void google_session_ready(GoogleSession *session) { PurpleMedia *media = ((GoogleAVSessionData *)session->session_data)->media; gboolean video = ((GoogleAVSessionData *)session->session_data)->video; if (purple_media_codecs_ready(media, NULL) && purple_media_candidates_prepared(media, NULL, NULL)) { gchar *me = g_strdup_printf("%s@%s/%s", session->js->user->node, session->js->user->domain, session->js->user->resource); JabberIq *iq; PurpleXmlNode *sess, *desc, *payload; GList *codecs, *iter; gboolean is_initiator = !strcmp(session->id.initiator, me); if (!is_initiator && !purple_media_accepted(media, NULL, NULL)) { g_free(me); return; } iq = jabber_iq_new(session->js, JABBER_IQ_SET); if (is_initiator) { purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid); purple_xmlnode_set_attrib(iq->node, "from", session->id.initiator); sess = google_session_create_xmlnode(session, "initiate"); } else { google_session_send_candidates(media, "google-voice", session->remote_jid, session); google_session_send_candidates(media, "google-video", session->remote_jid, session); purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid); purple_xmlnode_set_attrib(iq->node, "from", me); sess = google_session_create_xmlnode(session, "accept"); } purple_xmlnode_insert_child(iq->node, sess); desc = purple_xmlnode_new_child(sess, "description"); if (video) purple_xmlnode_set_namespace(desc, NS_GOOGLE_SESSION_VIDEO); else purple_xmlnode_set_namespace(desc, NS_GOOGLE_SESSION_PHONE); codecs = purple_media_get_codecs(media, "google-video"); for (iter = codecs; iter; iter = g_list_next(iter)) { PurpleMediaCodec *codec = (PurpleMediaCodec*)iter->data; gchar *id = g_strdup_printf("%d", purple_media_codec_get_id(codec)); gchar *encoding_name = purple_media_codec_get_encoding_name(codec); payload = purple_xmlnode_new_child(desc, "payload-type"); purple_xmlnode_set_attrib(payload, "id", id); purple_xmlnode_set_attrib(payload, "name", encoding_name); purple_xmlnode_set_attrib(payload, "width", "320"); purple_xmlnode_set_attrib(payload, "height", "200"); purple_xmlnode_set_attrib(payload, "framerate", "30"); g_free(encoding_name); g_free(id); } purple_media_codec_list_free(codecs); codecs = purple_media_get_codecs(media, "google-voice"); for (iter = codecs; iter; iter = g_list_next(iter)) { PurpleMediaCodec *codec = (PurpleMediaCodec*)iter->data; gchar *id = g_strdup_printf("%d", purple_media_codec_get_id(codec)); gchar *encoding_name = purple_media_codec_get_encoding_name(codec); gchar *clock_rate = g_strdup_printf("%d", purple_media_codec_get_clock_rate(codec)); payload = purple_xmlnode_new_child(desc, "payload-type"); if (video) purple_xmlnode_set_namespace(payload, NS_GOOGLE_SESSION_PHONE); purple_xmlnode_set_attrib(payload, "id", id); /* * Hack to make Gmail accept speex as the codec. * It shouldn't have to be case sensitive. */ if (purple_strequal(encoding_name, "SPEEX")) purple_xmlnode_set_attrib(payload, "name", "speex"); else purple_xmlnode_set_attrib(payload, "name", encoding_name); purple_xmlnode_set_attrib(payload, "clockrate", clock_rate); g_free(clock_rate); g_free(encoding_name); g_free(id); } purple_media_codec_list_free(codecs); jabber_iq_send(iq); if (is_initiator) { google_session_send_candidates(media, "google-voice", session->remote_jid, session); google_session_send_candidates(media, "google-video", session->remote_jid, session); } g_signal_handlers_disconnect_by_func(G_OBJECT(media), G_CALLBACK(google_session_ready), session); } }