static PurpleXmlNode * pounce_to_xmlnode(PurplePounce *pounce) { PurpleXmlNode *node, *child; PurpleAccount *pouncer; PurplePounceEvent events; PurplePounceOption options; pouncer = purple_pounce_get_pouncer(pounce); events = purple_pounce_get_events(pounce); options = purple_pounce_get_options(pounce); node = purple_xmlnode_new("pounce"); purple_xmlnode_set_attrib(node, "ui", pounce->ui_type); child = purple_xmlnode_new_child(node, "account"); purple_xmlnode_set_attrib(child, "protocol", purple_account_get_protocol_id(pouncer)); purple_xmlnode_insert_data(child, purple_normalize(pouncer, purple_account_get_username(pouncer)), -1); child = purple_xmlnode_new_child(node, "pouncee"); purple_xmlnode_insert_data(child, purple_pounce_get_pouncee(pounce), -1); /* Write pounce options */ child = purple_xmlnode_new_child(node, "options"); if (options & PURPLE_POUNCE_OPTION_AWAY) add_option_to_xmlnode(child, "on-away"); /* Write pounce events */ child = purple_xmlnode_new_child(node, "events"); if (events & PURPLE_POUNCE_SIGNON) add_event_to_xmlnode(child, "sign-on"); if (events & PURPLE_POUNCE_SIGNOFF) add_event_to_xmlnode(child, "sign-off"); if (events & PURPLE_POUNCE_AWAY) add_event_to_xmlnode(child, "away"); if (events & PURPLE_POUNCE_AWAY_RETURN) add_event_to_xmlnode(child, "return-from-away"); if (events & PURPLE_POUNCE_IDLE) add_event_to_xmlnode(child, "idle"); if (events & PURPLE_POUNCE_IDLE_RETURN) add_event_to_xmlnode(child, "return-from-idle"); if (events & PURPLE_POUNCE_TYPING) add_event_to_xmlnode(child, "start-typing"); if (events & PURPLE_POUNCE_TYPED) add_event_to_xmlnode(child, "typed"); if (events & PURPLE_POUNCE_TYPING_STOPPED) add_event_to_xmlnode(child, "stop-typing"); if (events & PURPLE_POUNCE_MESSAGE_RECEIVED) add_event_to_xmlnode(child, "message-received"); /* Write pounce actions */ child = purple_xmlnode_new_child(node, "actions"); g_hash_table_foreach(pounce->actions, action_parameter_list_to_xmlnode, child); if (purple_pounce_get_save(pounce)) purple_xmlnode_new_child(node, "save"); return node; }
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 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 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; }
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 PurpleXmlNode * smileys_to_xmlnode(void) { PurpleXmlNode *root_node, *profile_node, *smileyset_node; root_node = purple_xmlnode_new(XML_ROOT_TAG); purple_xmlnode_set_attrib(root_node, "version", "1.0"); /* See the top comments above to understand why initial tag elements * are not being considered by now. */ profile_node = purple_xmlnode_new(XML_PROFILE_TAG); if (profile_node) { purple_xmlnode_set_attrib(profile_node, XML_PROFILE_NAME_ATTRIB_TAG, "Default"); purple_xmlnode_insert_child(root_node, profile_node); smileyset_node = purple_xmlnode_new(XML_SMILEY_SET_TAG); if (smileyset_node) { purple_xmlnode_insert_child(profile_node, smileyset_node); g_hash_table_foreach(smiley_shortcut_index, add_smiley_to_main_node, smileyset_node); } } return root_node; }
static PurpleXmlNode * pounces_to_xmlnode(void) { PurpleXmlNode *node, *child; GList *cur; node = purple_xmlnode_new("pounces"); purple_xmlnode_set_attrib(node, "version", "1.0"); for (cur = purple_pounces_get_all(); cur != NULL; cur = cur->next) { child = pounce_to_xmlnode(cur->data); purple_xmlnode_insert_child(node, child); } return node; }
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_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; } }
JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type) { JabberIq *iq; iq = g_new0(JabberIq, 1); iq->type = type; iq->node = purple_xmlnode_new("iq"); switch(iq->type) { case JABBER_IQ_SET: purple_xmlnode_set_attrib(iq->node, "type", "set"); break; case JABBER_IQ_GET: purple_xmlnode_set_attrib(iq->node, "type", "get"); break; case JABBER_IQ_ERROR: purple_xmlnode_set_attrib(iq->node, "type", "error"); break; case JABBER_IQ_RESULT: purple_xmlnode_set_attrib(iq->node, "type", "result"); break; case JABBER_IQ_NONE: /* this shouldn't ever happen */ break; } iq->js = js; if(type == JABBER_IQ_GET || type == JABBER_IQ_SET) { iq->id = jabber_get_next_id(js); purple_xmlnode_set_attrib(iq->node, "id", iq->id); } return iq; }
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_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 PurpleXmlNode * smiley_to_xmlnode(PurpleSmiley *smiley) { PurpleSmileyPrivate *priv = NULL; PurpleXmlNode *smiley_node = NULL; smiley_node = purple_xmlnode_new(XML_SMILEY_TAG); if (!smiley_node) return NULL; priv = PURPLE_SMILEY_GET_PRIVATE(smiley); purple_xmlnode_set_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG, priv->shortcut); purple_xmlnode_set_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG, priv->checksum); purple_xmlnode_set_attrib(smiley_node, XML_FILENAME_ATRIB_TAG, purple_imgstore_get_filename(priv->img)); return smiley_node; }
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 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_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; PurpleXmlNode *sess; PurpleXmlNode *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"); purple_xmlnode_insert_child(iq->node, sess); purple_xmlnode_set_attrib(iq->node, "to", session->remote_jid); candidate = purple_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); purple_xmlnode_set_attrib(candidate, "address", ip); purple_xmlnode_set_attrib(candidate, "port", port); purple_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"); purple_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. */ purple_xmlnode_set_attrib(candidate, "password", password != NULL ? password : ""); purple_xmlnode_set_attrib(candidate, "preference", pref); purple_xmlnode_set_attrib(candidate, "protocol", purple_media_candidate_get_protocol(transport) == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ? "udp" : "tcp"); purple_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); purple_xmlnode_set_attrib(candidate, "generation", "0"); purple_xmlnode_set_attrib(candidate, "network", "0"); purple_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); }