static void google_session_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, gchar *sid, gchar *name, gboolean local, GoogleSession *session) { if (sid != NULL || name != NULL) return; if (type == PURPLE_MEDIA_INFO_HANGUP) { xmlnode *sess; JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); xmlnode_set_attrib(iq->node, "to", session->remote_jid); sess = google_session_create_xmlnode(session, "terminate"); xmlnode_insert_child(iq->node, sess); jabber_iq_send(iq); } else if (type == PURPLE_MEDIA_INFO_REJECT) { xmlnode *sess; JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET); xmlnode_set_attrib(iq->node, "to", session->remote_jid); sess = google_session_create_xmlnode(session, "reject"); xmlnode_insert_child(iq->node, sess); jabber_iq_send(iq); } else if (type == PURPLE_MEDIA_INFO_ACCEPT && local == TRUE) { google_session_ready(session); } }
void jabber_data_parse(JabberStream *js, const char *who, JabberIqType type, const char *id, xmlnode *data_node) { JabberIq *result = NULL; const char *cid = xmlnode_get_attrib(data_node, "cid"); const JabberData *data = cid ? jabber_data_find_local_by_cid(cid) : NULL; if (!data) { xmlnode *item_not_found = xmlnode_new("item-not-found"); result = jabber_iq_new(js, JABBER_IQ_ERROR); if (who) xmlnode_set_attrib(result->node, "to", who); xmlnode_set_attrib(result->node, "id", id); xmlnode_insert_child(result->node, item_not_found); } else { result = jabber_iq_new(js, JABBER_IQ_RESULT); if (who) xmlnode_set_attrib(result->node, "to", who); xmlnode_set_attrib(result->node, "id", id); xmlnode_insert_child(result->node, jabber_data_get_xml_definition(data)); } jabber_iq_send(result); }
void jabber_data_parse(JabberStream *js, xmlnode *packet) { JabberIq *result = NULL; const char *who = xmlnode_get_attrib(packet, "from"); xmlnode *data_node = xmlnode_get_child(packet, "data"); const JabberData *data = jabber_data_find_local_by_cid(xmlnode_get_attrib(data_node, "cid")); if (!data) { xmlnode *item_not_found = xmlnode_new("item-not-found"); result = jabber_iq_new(js, JABBER_IQ_ERROR); xmlnode_set_attrib(result->node, "to", who); xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); xmlnode_insert_child(result->node, item_not_found); } else { result = jabber_iq_new(js, JABBER_IQ_RESULT); xmlnode_set_attrib(result->node, "to", who); xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); xmlnode_insert_child(result->node, jabber_data_get_xml_definition(data)); } jabber_iq_send(result); }
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); }
JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type, const char *xmlns) { JabberIq *iq = jabber_iq_new(js, type); xmlnode *query; query = xmlnode_new_child(iq->node, "query"); xmlnode_set_namespace(query, xmlns); return iq; }
void jabber_ibb_session_accept(JabberIBBSession *sess) { JabberIq *result = jabber_iq_new(jabber_ibb_session_get_js(sess), JABBER_IQ_RESULT); purple_xmlnode_set_attrib(result->node, "to", jabber_ibb_session_get_who(sess)); jabber_iq_set_id(result, sess->id); jabber_iq_send(result); sess->state = JABBER_IBB_SESSION_OPENED; }
JabberIq * jingle_session_create_ack(JingleSession *session, const PurpleXmlNode *jingle) { JabberIq *result = jabber_iq_new( jingle_session_get_js(session), JABBER_IQ_RESULT); PurpleXmlNode *packet = purple_xmlnode_get_parent(jingle); jabber_iq_set_id(result, purple_xmlnode_get_attrib(packet, "id")); purple_xmlnode_set_attrib(result->node, "from", purple_xmlnode_get_attrib(packet, "to")); purple_xmlnode_set_attrib(result->node, "to", purple_xmlnode_get_attrib(packet, "from")); return result; }
static void jabber_oob_xfer_end(PurpleXfer *xfer) { JabberOOBXfer *jox = xfer->data; JabberIq *iq; iq = jabber_iq_new(jox->js, JABBER_IQ_RESULT); xmlnode_set_attrib(iq->node, "to", xfer->who); jabber_iq_set_id(iq, jox->iq_id); jabber_iq_send(iq); jabber_oob_xfer_free(xfer); }
static void jabber_si_xfer_send_request(GaimXfer *xfer) { JabberSIXfer *jsx = xfer->data; JabberIq *iq; xmlnode *si, *file, *feature, *x, *field, *option, *value; char buf[32]; xfer->filename = g_path_get_basename(xfer->local_filename); iq = jabber_iq_new(jsx->js, JABBER_IQ_SET); xmlnode_set_attrib(iq->node, "to", xfer->who); si = xmlnode_new_child(iq->node, "si"); xmlnode_set_namespace(si, "http://jabber.org/protocol/si"); jsx->stream_id = jabber_get_next_id(jsx->js); xmlnode_set_attrib(si, "id", jsx->stream_id); xmlnode_set_attrib(si, "profile", "http://jabber.org/protocol/si/profile/file-transfer"); file = xmlnode_new_child(si, "file"); xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer"); xmlnode_set_attrib(file, "name", xfer->filename); g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size); xmlnode_set_attrib(file, "size", buf); /* maybe later we'll do hash and date attribs */ feature = xmlnode_new_child(si, "feature"); xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = xmlnode_new_child(feature, "x"); xmlnode_set_namespace(x, "jabber:x:data"); xmlnode_set_attrib(x, "type", "form"); field = xmlnode_new_child(x, "field"); xmlnode_set_attrib(field, "var", "stream-method"); xmlnode_set_attrib(field, "type", "list-single"); option = xmlnode_new_child(field, "option"); value = xmlnode_new_child(option, "value"); xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); /* option = xmlnode_new_child(field, "option"); value = xmlnode_new_child(option, "value"); xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); */ jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); jabber_iq_send(iq); }
static JabberIq * jingle_create_iq(JingleSession *session) { JabberStream *js = jingle_session_get_js(session); JabberIq *result = jabber_iq_new(js, JABBER_IQ_SET); gchar *from = jingle_session_get_local_jid(session); gchar *to = jingle_session_get_remote_jid(session); purple_xmlnode_set_attrib(result->node, "from", from); purple_xmlnode_set_attrib(result->node, "to", to); g_free(from); g_free(to); return result; }
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); }
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)++; } }
void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); xmlnode *pubsub, *items, *item; xmlnode_set_attrib(iq->node,"to",to); pubsub = xmlnode_new_child(iq->node,"pubsub"); xmlnode_set_namespace(pubsub,"http://jabber.org/protocol/pubsub"); items = xmlnode_new_child(pubsub, "items"); xmlnode_set_attrib(items,"node",node); item = xmlnode_new_child(items, "item"); if(id) xmlnode_set_attrib(item, "id", id); jabber_iq_set_callback(iq,do_pep_iq_request_item_callback,(gpointer)cb); jabber_iq_send(iq); }
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 */ } }
static void jabber_oob_xfer_recv_error(PurpleXfer *xfer, const char *code) { JabberOOBXfer *jox = xfer->data; JabberIq *iq; xmlnode *y, *z; iq = jabber_iq_new(jox->js, JABBER_IQ_ERROR); xmlnode_set_attrib(iq->node, "to", xfer->who); jabber_iq_set_id(iq, jox->iq_id); y = xmlnode_new_child(iq->node, "error"); xmlnode_set_attrib(y, "code", code); if(purple_strequal(code, "406")) { z = xmlnode_new_child(y, "not-acceptable"); xmlnode_set_attrib(y, "type", "modify"); xmlnode_set_namespace(z, NS_XMPP_STANZAS); } else if(purple_strequal(code, "404")) { z = xmlnode_new_child(y, "not-found"); xmlnode_set_attrib(y, "type", "cancel"); xmlnode_set_namespace(z, NS_XMPP_STANZAS); } jabber_iq_send(iq); jabber_oob_xfer_free(xfer); }
void jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type, const char *id, PurpleXmlNode *new_mail) { PurpleXmlNode *query; JabberIq *iq; /* bail if the user isn't interested */ if (!purple_account_get_check_mail(purple_connection_get_account(js->gc))) return; /* Is this an initial incoming mail notification? If so, send a request for more info */ if (type != JABBER_IQ_SET) return; /* Acknowledge the notification */ iq = jabber_iq_new(js, JABBER_IQ_RESULT); if (from) purple_xmlnode_set_attrib(iq->node, "to", from); purple_xmlnode_set_attrib(iq->node, "id", id); jabber_iq_send(iq); purple_debug_misc("jabber", "Got new mail notification. Sending request for more info\n"); iq = jabber_iq_new_query(js, JABBER_IQ_GET, NS_GOOGLE_MAIL_NOTIFY); jabber_iq_set_callback(iq, jabber_gmail_parse, NULL); query = purple_xmlnode_get_child(iq->node, "query"); if (js->gmail_last_time) purple_xmlnode_set_attrib(query, "newer-than-time", js->gmail_last_time); if (js->gmail_last_tid) purple_xmlnode_set_attrib(query, "newer-than-tid", js->gmail_last_tid); jabber_iq_send(iq); return; }
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); } }
static gboolean handle_presence_contact(JabberStream *js, JabberPresence *presence) { JabberBuddyResource *jbr; PurpleAccount *account; PurpleBuddy *b; char *buddy_name; PurpleConversation *conv; buddy_name = jabber_id_get_bare_jid(presence->jid_from); account = purple_connection_get_account(js->gc); b = purple_find_buddy(account, buddy_name); /* * Unbind/unlock from sending messages to a specific resource on * presence changes. This is locked to a specific resource when * receiving a message (in message.c). */ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddy_name, account); if (conv) { purple_debug_info("jabber", "Changed conversation binding from %s to %s\n", purple_conversation_get_name(conv), buddy_name); purple_conversation_set_name(conv, buddy_name); } if (b == NULL) { if (presence->jb != js->user_jb) { purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n", buddy_name, purple_account_get_username(account), account); g_free(buddy_name); return FALSE; } else { /* this is a different resource of our own account. Resume even when this account isn't on our blist */ } } if (b && presence->vcard_avatar_hash) { const char *ah = presence->vcard_avatar_hash[0] != '\0' ? presence->vcard_avatar_hash : NULL; const char *ah2 = purple_buddy_icons_get_checksum_for_user(b); if (!purple_strequal(ah, ah2)) { /* XXX this is a crappy way of trying to prevent * someone from spamming us with presence packets * and causing us to DoS ourselves...what we really * need is a queue system that can throttle itself, * but i'm too tired to write that right now */ if(!g_slist_find(js->pending_avatar_requests, presence->jb)) { JabberIq *iq; xmlnode *vcard; js->pending_avatar_requests = g_slist_prepend(js->pending_avatar_requests, presence->jb); iq = jabber_iq_new(js, JABBER_IQ_GET); xmlnode_set_attrib(iq->node, "to", buddy_name); vcard = xmlnode_new_child(iq->node, "vCard"); xmlnode_set_namespace(vcard, "vcard-temp"); jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL); jabber_iq_send(iq); } } } if (presence->state == JABBER_BUDDY_STATE_ERROR || presence->type == JABBER_PRESENCE_UNAVAILABLE || presence->type == JABBER_PRESENCE_UNSUBSCRIBED) { jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); } else { jbr = jabber_buddy_track_resource(presence->jb, presence->jid_from->resource, presence->priority, presence->state, presence->status); jbr->idle = presence->idle ? time(NULL) - presence->idle : 0; } jbr = jabber_buddy_find_resource(presence->jb, NULL); if (jbr) { jabber_google_presence_incoming(js, buddy_name, jbr); purple_prpl_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, "message", jbr->status, NULL); purple_prpl_got_user_idle(account, buddy_name, jbr->idle, jbr->idle); if (presence->nickname) serv_got_alias(js->gc, buddy_name, presence->nickname); } else { purple_prpl_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), presence->status ? "message" : NULL, presence->status, NULL); } g_free(buddy_name); 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); }
void jabber_roster_parse(JabberStream *js, const char *from, JabberIqType type, const char *id, xmlnode *query) { xmlnode *item, *group; #if 0 const char *ver; #endif if (!jabber_is_own_account(js, from)) { purple_debug_warning("jabber", "Received bogon roster push from %s\n", from); return; } js->currently_parsing_roster_push = TRUE; for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) { const char *jid, *name, *subscription, *ask; JabberBuddy *jb; subscription = xmlnode_get_attrib(item, "subscription"); jid = xmlnode_get_attrib(item, "jid"); name = xmlnode_get_attrib(item, "name"); ask = xmlnode_get_attrib(item, "ask"); if(!jid) continue; if(!(jb = jabber_buddy_find(js, jid, TRUE))) continue; if(subscription) { if (g_str_equal(subscription, "remove")) jb->subscription = JABBER_SUB_REMOVE; else if (jb == js->user_jb) jb->subscription = JABBER_SUB_BOTH; else if (g_str_equal(subscription, "none")) jb->subscription = JABBER_SUB_NONE; else if (g_str_equal(subscription, "to")) jb->subscription = JABBER_SUB_TO; else if (g_str_equal(subscription, "from")) jb->subscription = JABBER_SUB_FROM; else if (g_str_equal(subscription, "both")) jb->subscription = JABBER_SUB_BOTH; } if(purple_strequal(ask, "subscribe")) jb->subscription |= JABBER_SUB_PENDING; else jb->subscription &= ~JABBER_SUB_PENDING; if(jb->subscription & JABBER_SUB_REMOVE) { remove_purple_buddies(js, jid); } else { GSList *groups = NULL; gboolean seen_empty = FALSE; if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER) if (!jabber_google_roster_incoming(js, item)) continue; for(group = xmlnode_get_child(item, "group"); group; group = xmlnode_get_next_twin(group)) { char *group_name = xmlnode_get_data(group); if (!group_name && !seen_empty) { group_name = g_strdup(""); seen_empty = TRUE; } /* * See the note in add_purple_buddy_to_groups; the core handles * names case-insensitively and this is required to not * end up with duplicates if a buddy is in, e.g., * 'XMPP' and 'xmpp' */ if (g_slist_find_custom(groups, group_name, (GCompareFunc)purple_utf8_strcasecmp)) g_free(group_name); else groups = g_slist_prepend(groups, group_name); } add_purple_buddy_to_groups(js, jid, name, groups); if (jb == js->user_jb) jabber_presence_fake_to_self(js, NULL); } } #if 0 ver = xmlnode_get_attrib(query, "ver"); if (ver) { PurpleAccount *account = purple_connection_get_account(js->gc); purple_account_set_string(account, "roster_ver", ver); } #endif if (type == JABBER_IQ_SET) { JabberIq *ack = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(ack, id); jabber_iq_send(ack); } js->currently_parsing_roster_push = FALSE; }
void jabber_iq_parse(JabberStream *js, xmlnode *packet) { JabberCallbackData *jcd; xmlnode *query, *error, *x; const char *xmlns; const char *type, *id, *from; JabberIqHandler *jih; query = xmlnode_get_child(packet, "query"); type = xmlnode_get_attrib(packet, "type"); from = xmlnode_get_attrib(packet, "from"); id = xmlnode_get_attrib(packet, "id"); if(type == NULL || !(!strcmp(type, "get") || !strcmp(type, "set") || !strcmp(type, "result") || !strcmp(type, "error"))) { purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n", type ? type : "(null)"); return; } /* All IQs must have an ID, so send an error for a set/get that doesn't */ if(!id || !*id) { if(!strcmp(type, "set") || !strcmp(type, "get")) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); xmlnode_free(iq->node); iq->node = xmlnode_copy(packet); xmlnode_set_attrib(iq->node, "to", from); xmlnode_remove_attrib(iq->node, "from"); 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); xmlnode_set_attrib(iq->node, "id", iq->id); error = xmlnode_new_child(iq->node, "error"); xmlnode_set_attrib(error, "type", "modify"); x = xmlnode_new_child(error, "bad-request"); xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas"); jabber_iq_send(iq); } else purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", type); return; } /* First, lets see if a special callback got registered */ if(!strcmp(type, "result") || !strcmp(type, "error")) { if(id && *id && (jcd = g_hash_table_lookup(js->iq_callbacks, id))) { jcd->callback(js, packet, jcd->data); jabber_iq_remove_callback_by_id(js, id); return; } } /* Apparently not, so lets see if we have a pre-defined handler */ if(query && (xmlns = xmlnode_get_namespace(query))) { if((jih = g_hash_table_lookup(iq_handlers, xmlns))) { jih(js, packet); return; } } if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) { jabber_si_parse(js, packet); return; } if(xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) { jabber_gmail_poke(js, packet); return; } purple_debug_info("jabber", "jabber_iq_parse\n"); if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) { jabber_ping_parse(js, packet); return; } if (xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE)) { jabber_data_parse(js, packet); return; } /* If we get here, send the default error reply mandated by XMPP-CORE */ if(!strcmp(type, "set") || !strcmp(type, "get")) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); xmlnode_free(iq->node); iq->node = xmlnode_copy(packet); xmlnode_set_attrib(iq->node, "to", from); xmlnode_remove_attrib(iq->node, "from"); xmlnode_set_attrib(iq->node, "type", "error"); error = xmlnode_new_child(iq->node, "error"); xmlnode_set_attrib(error, "type", "cancel"); xmlnode_set_attrib(error, "code", "501"); x = xmlnode_new_child(error, "feature-not-implemented"); xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas"); jabber_iq_send(iq); } }
static void google_session_send_candidates(PurpleMedia *media, gchar *session_id, gchar *participant, GoogleSession *session) { PurpleMedia *session_media = ((GoogleAVSessionData *) session->session_data)->media; GList *candidates = purple_media_get_local_candidates(session_media, session_id, session->remote_jid); GList *iter; PurpleMediaCandidate *transport; gboolean video = FALSE; if (!strcmp(session_id, "google-video")) video = TRUE; for (iter = candidates; iter; iter = iter->next) { JabberIq *iq; gchar *ip, *port, *username, *password; gchar pref[16]; PurpleMediaCandidateType type; xmlnode *sess; xmlnode *candidate; guint component_id; transport = PURPLE_MEDIA_CANDIDATE(iter->data); component_id = purple_media_candidate_get_component_id( transport); iq = jabber_iq_new(session->js, JABBER_IQ_SET); sess = google_session_create_xmlnode(session, "candidates"); xmlnode_insert_child(iq->node, sess); xmlnode_set_attrib(iq->node, "to", session->remote_jid); candidate = xmlnode_new("candidate"); ip = purple_media_candidate_get_ip(transport); port = g_strdup_printf("%d", purple_media_candidate_get_port(transport)); g_ascii_dtostr(pref, 16, purple_media_candidate_get_priority(transport) / 1000.0); username = purple_media_candidate_get_username(transport); password = purple_media_candidate_get_password(transport); type = purple_media_candidate_get_candidate_type(transport); xmlnode_set_attrib(candidate, "address", ip); xmlnode_set_attrib(candidate, "port", port); xmlnode_set_attrib(candidate, "name", component_id == PURPLE_MEDIA_COMPONENT_RTP ? video ? "video_rtp" : "rtp" : component_id == PURPLE_MEDIA_COMPONENT_RTCP ? video ? "video_rtcp" : "rtcp" : "none"); xmlnode_set_attrib(candidate, "username", username); /* * As of this writing, Farsight 2 in Google compatibility * mode doesn't provide a password. The Gmail client * requires this to be set. */ xmlnode_set_attrib(candidate, "password", password != NULL ? password : ""); xmlnode_set_attrib(candidate, "preference", pref); xmlnode_set_attrib(candidate, "protocol", purple_media_candidate_get_protocol(transport) == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ? "udp" : "tcp"); xmlnode_set_attrib(candidate, "type", type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "local" : type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "stun" : type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : NULL); xmlnode_set_attrib(candidate, "generation", "0"); xmlnode_set_attrib(candidate, "network", "0"); xmlnode_insert_child(sess, candidate); g_free(ip); g_free(port); g_free(username); g_free(password); jabber_iq_send(iq); } purple_media_candidate_list_free(candidates); }
static void google_session_handle_accept(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { xmlnode *desc_element = xmlnode_get_child(sess, "description"); xmlnode *codec_element = xmlnode_get_child( desc_element, "payload-type"); GList *codecs = NULL, *video_codecs = NULL; JabberIq *result = NULL; const gchar *xmlns = xmlnode_get_namespace(desc_element); gboolean video = (xmlns && !strcmp(xmlns, NS_GOOGLE_SESSION_VIDEO)); GoogleAVSessionData *session_data = (GoogleAVSessionData *) session->session_data; for (; codec_element; codec_element = codec_element->next) { const gchar *xmlns, *encoding_name, *id, *clock_rate, *width, *height, *framerate; gboolean video_codec = FALSE; if (!purple_strequal(codec_element->name, "payload-type")) continue; xmlns = xmlnode_get_namespace(codec_element); encoding_name = xmlnode_get_attrib(codec_element, "name"); id = xmlnode_get_attrib(codec_element, "id"); if (!video || purple_strequal(xmlns, NS_GOOGLE_SESSION_PHONE)) clock_rate = xmlnode_get_attrib( codec_element, "clockrate"); else { clock_rate = "90000"; width = xmlnode_get_attrib(codec_element, "width"); height = xmlnode_get_attrib(codec_element, "height"); framerate = xmlnode_get_attrib( codec_element, "framerate"); video_codec = TRUE; } if (id && encoding_name) { PurpleMediaCodec *codec = purple_media_codec_new( atoi(id), encoding_name, video_codec ? PURPLE_MEDIA_VIDEO : PURPLE_MEDIA_AUDIO, clock_rate ? atoi(clock_rate) : 0); if (video_codec) video_codecs = g_list_append( video_codecs, codec); else codecs = g_list_append(codecs, codec); } } if (codecs) purple_media_set_remote_codecs(session_data->media, "google-voice", session->remote_jid, codecs); if (video_codecs) purple_media_set_remote_codecs(session_data->media, "google-video", session->remote_jid, video_codecs); purple_media_stream_info(session_data->media, PURPLE_MEDIA_INFO_ACCEPT, NULL, NULL, FALSE); result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); jabber_iq_send(result); }
static void google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { JabberIq *result; GList *list = NULL, *video_list = NULL; xmlnode *cand; static int name = 0; char n[4]; GoogleAVSessionData *session_data = (GoogleAVSessionData *) session->session_data; for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) { PurpleMediaCandidate *info; const gchar *cname = xmlnode_get_attrib(cand, "name"); const gchar *type = xmlnode_get_attrib(cand, "type"); const gchar *protocol = xmlnode_get_attrib(cand, "protocol"); const gchar *address = xmlnode_get_attrib(cand, "address"); const gchar *port = xmlnode_get_attrib(cand, "port"); const gchar *preference = xmlnode_get_attrib(cand, "preference"); guint component_id; if (cname && type && address && port) { PurpleMediaCandidateType candidate_type; guint prio = preference ? g_ascii_strtod(preference, NULL) * 1000 : 0; g_snprintf(n, sizeof(n), "S%d", name++); if (g_str_equal(type, "local")) candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST; else if (g_str_equal(type, "stun")) candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX; else if (g_str_equal(type, "relay")) candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_RELAY; else candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST; if (purple_strequal(cname, "rtcp") || purple_strequal(cname, "video_rtcp")) component_id = PURPLE_MEDIA_COMPONENT_RTCP; else component_id = PURPLE_MEDIA_COMPONENT_RTP; info = purple_media_candidate_new(n, component_id, candidate_type, purple_strequal(protocol, "udp") ? PURPLE_MEDIA_NETWORK_PROTOCOL_UDP : PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, address, atoi(port)); g_object_set(info, "username", xmlnode_get_attrib(cand, "username"), "password", xmlnode_get_attrib(cand, "password"), "priority", prio, NULL); if (!strncmp(cname, "video_", 6)) { if (session_data->added_streams) { video_list = g_list_append(video_list, info); } else { session_data->remote_video_candidates = g_list_append(session_data->remote_video_candidates, info); } } else { if (session_data->added_streams) { list = g_list_append(list, info); } else { session_data->remote_audio_candidates = g_list_append(session_data->remote_audio_candidates, info); } } } } if (list) { purple_media_add_remote_candidates(session_data->media, "google-voice", session->remote_jid, list); purple_media_candidate_list_free(list); } if (video_list) { purple_media_add_remote_candidates(session_data->media, "google-video", session->remote_jid, video_list); purple_media_candidate_list_free(video_list); } result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); jabber_iq_send(result); }
static void jabber_google_relay_response_session_handle_initiate_cb(GoogleSession *session, const gchar *relay_ip, guint relay_udp, guint relay_tcp, guint relay_ssltcp, const gchar *relay_username, const gchar *relay_password) { GParameter *params; guint num_params; JabberStream *js = session->js; xmlnode *codec_element; const gchar *xmlns; PurpleMediaCodec *codec; GList *video_codecs = NULL; GList *codecs = NULL; JabberIq *result; GoogleAVSessionData *session_data = (GoogleAVSessionData *) session->session_data; params = jabber_google_session_get_params(js, relay_ip, relay_udp, relay_tcp, relay_ssltcp, relay_username, relay_password, &num_params); if (purple_media_add_stream(session_data->media, "google-voice", session->remote_jid, PURPLE_MEDIA_AUDIO, FALSE, "nice", num_params, params) == FALSE || (session_data->video && purple_media_add_stream( session_data->media, "google-video", session->remote_jid, PURPLE_MEDIA_VIDEO, FALSE, "nice", num_params, params) == FALSE)) { purple_media_error(session_data->media, "Error adding stream."); purple_media_stream_info(session_data->media, PURPLE_MEDIA_INFO_REJECT, NULL, NULL, TRUE); } else { /* successfully added stream(s) */ session_data->added_streams = TRUE; if (session_data->remote_audio_candidates) { purple_media_add_remote_candidates(session_data->media, "google-voice", session->remote_jid, session_data->remote_audio_candidates); purple_media_candidate_list_free(session_data->remote_audio_candidates); session_data->remote_audio_candidates = NULL; } if (session_data->remote_video_candidates) { purple_media_add_remote_candidates(session_data->media, "google-video", session->remote_jid, session_data->remote_video_candidates); purple_media_candidate_list_free(session_data->remote_video_candidates); session_data->remote_video_candidates = NULL; } } g_free(params); for (codec_element = xmlnode_get_child(session->description, "payload-type"); codec_element; codec_element = codec_element->next) { const char *id, *encoding_name, *clock_rate, *width, *height, *framerate; gboolean video; if (codec_element->name && strcmp(codec_element->name, "payload-type")) continue; xmlns = xmlnode_get_namespace(codec_element); encoding_name = xmlnode_get_attrib(codec_element, "name"); id = xmlnode_get_attrib(codec_element, "id"); if (!session_data->video || (xmlns && !strcmp(xmlns, NS_GOOGLE_SESSION_PHONE))) { clock_rate = xmlnode_get_attrib( codec_element, "clockrate"); video = FALSE; } else { width = xmlnode_get_attrib(codec_element, "width"); height = xmlnode_get_attrib(codec_element, "height"); framerate = xmlnode_get_attrib( codec_element, "framerate"); clock_rate = "90000"; video = TRUE; } if (id) { codec = purple_media_codec_new(atoi(id), encoding_name, video ? PURPLE_MEDIA_VIDEO : PURPLE_MEDIA_AUDIO, clock_rate ? atoi(clock_rate) : 0); if (video) video_codecs = g_list_append( video_codecs, codec); else codecs = g_list_append(codecs, codec); } } if (codecs) purple_media_set_remote_codecs(session_data->media, "google-voice", session->remote_jid, codecs); if (video_codecs) purple_media_set_remote_codecs(session_data->media, "google-video", session->remote_jid, video_codecs); purple_media_codec_list_free(codecs); purple_media_codec_list_free(video_codecs); result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, session->iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); jabber_iq_send(result); }
static void jabber_si_xfer_init(GaimXfer *xfer) { JabberSIXfer *jsx = xfer->data; JabberIq *iq; if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { JabberBuddy *jb; JabberBuddyResource *jbr = NULL; jb = jabber_buddy_find(jsx->js, xfer->who, TRUE); /* XXX */ if(!jb) return; /* XXX: for now, send to the first resource available */ if(g_list_length(jb->resources) >= 1) { char **who_v = g_strsplit(xfer->who, "/", 2); char *who; jbr = jabber_buddy_find_resource(jb, NULL); who = g_strdup_printf("%s/%s", who_v[0], jbr->name); g_strfreev(who_v); g_free(xfer->who); xfer->who = who; jabber_disco_info_do(jsx->js, who, jabber_si_xfer_send_disco_cb, xfer); } else { return; /* XXX: ick */ } } else { xmlnode *si, *feature, *x, *field, *value; iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT); xmlnode_set_attrib(iq->node, "to", xfer->who); if(jsx->iq_id) jabber_iq_set_id(iq, jsx->iq_id); jsx->accepted = TRUE; si = xmlnode_new_child(iq->node, "si"); xmlnode_set_namespace(si, "http://jabber.org/protocol/si"); feature = xmlnode_new_child(si, "feature"); xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg"); x = xmlnode_new_child(feature, "x"); xmlnode_set_namespace(x, "jabber:x:data"); xmlnode_set_attrib(x, "type", "submit"); field = xmlnode_new_child(x, "field"); xmlnode_set_attrib(field, "var", "stream-method"); value = xmlnode_new_child(field, "value"); if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); /* else if(jsx->stream_method & STREAM_METHOD_IBB) xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); */ jabber_iq_send(iq); } }
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_ibb_parse(JabberStream *js, const char *who, JabberIqType type, const char *id, PurpleXmlNode *child) { const char *name = child->name; gboolean data = g_str_equal(name, "data"); gboolean close = g_str_equal(name, "close"); gboolean open = g_str_equal(name, "open"); const gchar *sid = (data || close) ? purple_xmlnode_get_attrib(child, "sid") : NULL; JabberIBBSession *sess = sid ? g_hash_table_lookup(jabber_ibb_sessions, sid) : NULL; if (sess) { if (strcmp(who, jabber_ibb_session_get_who(sess)) != 0) { /* the iq comes from a different JID than the remote JID of the session, ignore it */ purple_debug_error("jabber", "Got IBB iq from wrong JID, ignoring\n"); } else if (data) { const gchar *seq_attr = purple_xmlnode_get_attrib(child, "seq"); guint16 seq = (seq_attr ? atoi(seq_attr) : 0); /* reject the data, and set the session in error if we get an out-of-order packet */ if (seq_attr && seq == jabber_ibb_session_get_recv_seq(sess)) { /* sequence # is the expected... */ JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, id); purple_xmlnode_set_attrib(result->node, "to", who); if (sess->data_received_cb) { gchar *base64 = purple_xmlnode_get_data(child); gsize size; gpointer rawdata = purple_base64_decode(base64, &size); g_free(base64); if (rawdata) { purple_debug_info("jabber", "got %" G_GSIZE_FORMAT " bytes of data on IBB stream\n", size); /* we accept other clients to send up to block-size of _unencoded_ data, since there's been some confusions regarding the interpretation of this attribute (including previous versions of libpurple) */ if (size > jabber_ibb_session_get_block_size(sess)) { purple_debug_error("jabber", "IBB: received a too large packet\n"); if (sess->error_cb) sess->error_cb(sess); g_free(rawdata); return; } else { purple_debug_info("jabber", "calling IBB callback for received data\n"); sess->data_received_cb(sess, rawdata, size); } g_free(rawdata); } else { purple_debug_error("jabber", "IBB: invalid BASE64 data received\n"); if (sess->error_cb) sess->error_cb(sess); return; } } (sess->recv_seq)++; jabber_iq_send(result); } else { purple_debug_error("jabber", "Received an out-of-order/invalid IBB packet\n"); sess->state = JABBER_IBB_SESSION_ERROR; if (sess->error_cb) { sess->error_cb(sess); } } } else if (close) { sess->state = JABBER_IBB_SESSION_CLOSED; purple_debug_info("jabber", "IBB: received close\n"); if (sess->closed_cb) { purple_debug_info("jabber", "IBB: calling closed handler\n"); sess->closed_cb(sess); } } } else if (open) { JabberIq *result; const GList *iterator; /* run all open handlers registered until one returns true */ for (iterator = open_handlers ; iterator ; iterator = g_list_next(iterator)) { JabberIBBOpenHandler *handler = iterator->data; if (handler(js, who, id, child)) { result = jabber_iq_new(js, JABBER_IQ_RESULT); purple_xmlnode_set_attrib(result->node, "to", who); jabber_iq_set_id(result, id); jabber_iq_send(result); return; } } /* no open callback returned success, reject */ jabber_ibb_send_error_response(js, who, id); } else { /* send error reply */ jabber_ibb_send_error_response(js, who, id); } }
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; xmlnode *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) { xmlnode_set_attrib(iq->node, "to", session->remote_jid); 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); xmlnode_set_attrib(iq->node, "to", session->remote_jid); xmlnode_set_attrib(iq->node, "from", me); sess = google_session_create_xmlnode(session, "accept"); } xmlnode_insert_child(iq->node, sess); desc = xmlnode_new_child(sess, "description"); if (video) xmlnode_set_namespace(desc, NS_GOOGLE_SESSION_VIDEO); else 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 = xmlnode_new_child(desc, "payload-type"); xmlnode_set_attrib(payload, "id", id); xmlnode_set_attrib(payload, "name", encoding_name); xmlnode_set_attrib(payload, "width", "320"); xmlnode_set_attrib(payload, "height", "200"); 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 = xmlnode_new_child(desc, "payload-type"); if (video) xmlnode_set_namespace(payload, NS_GOOGLE_SESSION_PHONE); 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")) xmlnode_set_attrib(payload, "name", "speex"); else xmlnode_set_attrib(payload, "name", encoding_name); 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); } }