Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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;
}
Esempio n. 6
0
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;
    }
}
Esempio n. 7
0
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) */
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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)++;
	}
}
Esempio n. 11
0
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;
	}
}
Esempio n. 12
0
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;
}
Esempio n. 13
0
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;
}
Esempio n. 14
0
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);
	}
}
Esempio n. 15
0
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;
}
Esempio n. 16
0
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;
}
Esempio n. 17
0
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);
}