# HG changeset patch # User Paul Aurich # Date 1247710840 0 # Node ID d677981cf97ed45a34f07151f179e86ebbfd04e3 # Parent 7ca43b2cd1327ad669545f7a04a423dbad979eba# Parent a0748a638eb0672cd46a9ad51d558c217daf2ea6 merge of '16b1eeda9a01e74e4b8906f46cd976bf0935a1c2' and 'dde113d3a3376b87e0c8ed0ede8c73e7ac207135' diff -r a0748a638eb0 -r d677981cf97e ChangeLog.API --- a/ChangeLog.API Wed Jul 15 16:37:46 2009 +0000 +++ b/ChangeLog.API Thu Jul 16 02:20:40 2009 +0000 @@ -56,6 +56,7 @@ * purple_request_field_get_group * purple_request_field_get_ui_data * purple_request_field_set_ui_data + * purple_ssl_connect_with_ssl_cn * purple_strequal * purple_utf8_strip_unprintables * purple_util_fetch_url_request_len_with_account diff -r a0748a638eb0 -r d677981cf97e libpurple/protocols/jabber/bosh.c --- a/libpurple/protocols/jabber/bosh.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/protocols/jabber/bosh.c Thu Jul 16 02:20:40 2009 +0000 @@ -51,8 +51,13 @@ PurpleHTTPConnection *connections[MAX_HTTP_CONNECTIONS]; unsigned short failed_connections; - gboolean ready; + enum { + BOSH_CONN_OFFLINE, + BOSH_CONN_BOOTING, + BOSH_CONN_ONLINE + } state; gboolean ssl; + gboolean needs_restart; /* decoded URL */ char *host; @@ -84,7 +89,11 @@ PurpleCircBuffer *write_buffer; - gboolean ready; + enum { + HTTP_CONN_OFFLINE, + HTTP_CONN_CONNECTING, + HTTP_CONN_CONNECTED + } state; int requests; /* number of outstanding HTTP requests */ GString *buf; @@ -129,7 +138,7 @@ PurpleHTTPConnection *conn = g_new0(PurpleHTTPConnection, 1); conn->bosh = bosh; conn->fd = -1; - conn->ready = FALSE; + conn->state = HTTP_CONN_OFFLINE; conn->write_buffer = purple_circ_buffer_new(0 /* default grow size */); @@ -176,6 +185,7 @@ conn->path = g_strdup_printf("/%s", path); g_free(path); conn->pipelining = TRUE; + conn->needs_restart = FALSE; if ((user && user[0] != '\0') || (passwd && passwd[0] != '\0')) { purple_debug_info("jabber", "Ignoring unexpected username and password " @@ -198,7 +208,7 @@ conn->pending = purple_circ_buffer_new(0 /* default grow size */); - conn->ready = FALSE; + conn->state = BOSH_CONN_OFFLINE; if (purple_strcasestr(url, "https://") != NULL) conn->ssl = TRUE; else @@ -243,18 +253,28 @@ /* Easy solution: Does everyone involved support pipelining? Hooray! Just use * one TCP connection! */ if (conn->pipelining) - return conn->connections[0]->ready ? conn->connections[0] : NULL; + return conn->connections[0]->state == HTTP_CONN_CONNECTED ? + conn->connections[0] : NULL; /* First loop, look for a connection that's ready */ for (i = 0; i < MAX_HTTP_CONNECTIONS; ++i) { - if (conn->connections[i] && conn->connections[i]->ready && - conn->connections[i]->requests == 0) + if (conn->connections[i] && + conn->connections[i]->state == HTTP_CONN_CONNECTED && + conn->connections[i]->requests == 0) return conn->connections[i]; } - /* Second loop, look for one that's NULL and create a new connection */ + /* Second loop, is something currently connecting? If so, just queue up. */ + for (i = 0; i < MAX_HTTP_CONNECTIONS; ++i) { + if (conn->connections[i] && + conn->connections[i]->state == HTTP_CONN_CONNECTING) + return NULL; + } + + /* Third loop, look for one that's NULL and create a new connection */ for (i = 0; i < MAX_HTTP_CONNECTIONS; ++i) { if (!conn->connections[i]) { + purple_debug_info("jabber", "bosh: Creating and connecting new httpconn\n"); conn->connections[i] = jabber_bosh_http_connection_init(conn); http_connection_connect(conn->connections[i]); @@ -268,7 +288,7 @@ static void jabber_bosh_connection_send(PurpleBOSHConnection *conn, - PurpleBOSHPacketType type, const char *data) + const PurpleBOSHPacketType type, const char *data) { PurpleHTTPConnection *chosen; GString *packet = NULL; @@ -277,12 +297,12 @@ if (type != PACKET_NORMAL && !chosen) { /* - * For non-ordinary traffic, we don't want to 'buffer' it, so use the + * For non-ordinary traffic, we can't 'buffer' it, so use the * first connection. */ chosen = conn->connections[0]; - if (!chosen->ready) { + if (chosen->state != HTTP_CONN_CONNECTED) { purple_debug_info("jabber", "Unable to find a ready BOSH " "connection. Ignoring send of type 0x%02x.\n", type); return; @@ -300,6 +320,7 @@ int len = data ? strlen(data) : 0; purple_circ_buffer_append(conn->pending, data, len); } + return; } @@ -316,9 +337,11 @@ conn->sid, conn->js->user->domain); - if (type == PACKET_STREAM_RESTART) + if (type == PACKET_STREAM_RESTART) { packet = g_string_append(packet, " xmpp:restart='true'/>"); - else { + /* TODO: Do we need to wait for a response? */ + conn->needs_restart = FALSE; + } else { gsize read_amt; if (type == PACKET_TERMINATE) packet = g_string_append(packet, " type='terminate'"); @@ -343,7 +366,9 @@ jabber_bosh_connection_send(conn, PACKET_TERMINATE, NULL); } -static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) { +static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) +{ + conn->needs_restart = TRUE; jabber_bosh_connection_send(conn, PACKET_STREAM_RESTART, NULL); } @@ -353,7 +378,7 @@ type = xmlnode_get_attrib(node, "type"); if (type != NULL && !strcmp(type, "terminate")) { - conn->ready = FALSE; + conn->state = BOSH_CONN_OFFLINE; purple_connection_error_reason(conn->js->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("The BOSH connection manager terminated your session.")); @@ -384,11 +409,6 @@ /* jabber_process_packet might free child */ xmlnode *next = child->next; if (child->type == XMLNODE_TYPE_TAG) { - if (!strcmp(child->name, "iq")) { - if (xmlnode_get_child(child, "session")) - conn->ready = TRUE; - } - jabber_process_packet(js, &child); } @@ -476,9 +496,13 @@ conn->max_inactivity = 0; } else { /* TODO: Integrate this with jabber.c keepalive checks... */ - conn->inactivity_timer = purple_timeout_add_seconds( - conn->max_inactivity - 2 /* rounding */, bosh_inactivity_cb, - conn); + if (conn->inactivity_timer == 0) { + purple_debug_misc("jabber", "Starting BOSH inactivity timer for %d secs (compensating for rounding)\n", + conn->max_inactivity - 5); + conn->inactivity_timer = purple_timeout_add_seconds( + conn->max_inactivity - 5 /* rounding */, + bosh_inactivity_cb, conn); + } } } @@ -487,6 +511,7 @@ /* FIXME: Depending on receiving features might break with some hosts */ packet = xmlnode_get_child(node, "features"); + conn->state = BOSH_CONN_ONLINE; conn->js->use_bosh = TRUE; conn->receive_cb = auth_response_cb; jabber_stream_features_parse(conn->js, packet); @@ -511,6 +536,8 @@ conn->js->user->domain, ++conn->rid); + purple_debug_misc("jabber", "SendBOSH Boot %s(%" G_GSIZE_FORMAT "): %s\n", + conn->ssl ? "(ssl)" : "", buf->len, buf->str); conn->receive_cb = boot_response_cb; http_connection_send_request(conn->connections[0], buf); g_string_free(buf, TRUE); @@ -550,7 +577,7 @@ connection_common_established_cb(PurpleHTTPConnection *conn) { /* Indicate we're ready and reset some variables */ - conn->ready = TRUE; + conn->state = HTTP_CONN_CONNECTED; conn->requests = 0; if (conn->buf) { g_string_free(conn->buf, TRUE); @@ -559,9 +586,11 @@ conn->headers_done = FALSE; conn->handled_len = conn->body_len = 0; - if (conn->bosh->ready) { + if (conn->bosh->needs_restart) + jabber_bosh_connection_stream_restart(conn->bosh); + else if (conn->bosh->state == BOSH_CONN_ONLINE) { purple_debug_info("jabber", "BOSH session already exists. Trying to reuse it.\n"); - if (conn->bosh->pending->bufused > 0) { + if (conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0) { /* Send the pending data */ jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL); } @@ -585,7 +614,7 @@ * Well, then. Fine! I never liked you anyway, server! I was cheating on you * with AIM! */ - conn->ready = FALSE; + conn->state = HTTP_CONN_OFFLINE; if (conn->psc) { purple_ssl_close(conn->psc); conn->psc = NULL; @@ -620,6 +649,10 @@ void jabber_bosh_connection_connect(PurpleBOSHConnection *bosh) { PurpleHTTPConnection *conn = bosh->connections[0]; + + g_return_if_fail(bosh->state == BOSH_CONN_OFFLINE); + bosh->state = BOSH_CONN_BOOTING; + http_connection_connect(conn); } @@ -679,10 +712,10 @@ http_received_cb(conn->buf->str + conn->handled_len, conn->body_len, conn->bosh); - if (conn->bosh->ready && + if (conn->bosh->state == BOSH_CONN_ONLINE && (conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0)) { + purple_debug_misc("jabber", "BOSH: Sending an empty request\n"); jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL); - purple_debug_misc("jabber", "BOSH: Sending an empty request\n"); } g_string_free(conn->buf, TRUE); @@ -802,6 +835,8 @@ PurpleConnection *gc = bosh->js->gc; PurpleAccount *account = purple_connection_get_account(gc); + conn->state = HTTP_CONN_CONNECTING; + if (bosh->ssl) { if (purple_ssl_is_supported()) { conn->psc = purple_ssl_connect(account, bosh->host, bosh->port, diff -r a0748a638eb0 -r d677981cf97e libpurple/protocols/jabber/jutil.c --- a/libpurple/protocols/jabber/jutil.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/protocols/jabber/jutil.c Thu Jul 16 02:20:40 2009 +0000 @@ -257,29 +257,29 @@ /* normalization */ if(at) { - node = g_utf8_normalize(str, at-str, G_NORMALIZE_NFKC); + node = g_utf8_strdown(str, at-str); if(slash) { - domain = g_utf8_normalize(at+1, slash-(at+1), G_NORMALIZE_NFKC); + domain = g_utf8_strdown(at+1, slash-(at+1)); jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); } else { - domain = g_utf8_normalize(at+1, -1, G_NORMALIZE_NFKC); + domain = g_utf8_strdown(at+1, -1); } } else { if(slash) { - domain = g_utf8_normalize(str, slash-str, G_NORMALIZE_NFKC); + domain = g_utf8_strdown(str, slash-str); jid->resource = g_utf8_normalize(slash+1, -1, G_NORMALIZE_NFKC); } else { - domain = g_utf8_normalize(str, -1, G_NORMALIZE_NFKC); + domain = g_utf8_strdown(str, -1); } } if (node) { - jid->node = g_utf8_strdown(node, -1); + jid->node = g_utf8_normalize(node, -1, G_NORMALIZE_NFKC); g_free(node); } if (domain) { - jid->domain = g_utf8_strdown(domain, -1); + jid->domain = g_utf8_normalize(domain, -1, G_NORMALIZE_NFKC); g_free(domain); } diff -r a0748a638eb0 -r d677981cf97e libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/protocols/jabber/presence.c Thu Jul 16 02:20:40 2009 +0000 @@ -1065,7 +1065,7 @@ /* if the message is blank, then there really isn't a message */ if(formatted_msg && *formatted_msg) - *msg = g_strdup(formatted_msg); + *msg = purple_markup_strip_html(formatted_msg); } if(priority) diff -r a0748a638eb0 -r d677981cf97e libpurple/protocols/jabber/si.c --- a/libpurple/protocols/jabber/si.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/protocols/jabber/si.c Thu Jul 16 02:20:40 2009 +0000 @@ -51,9 +51,9 @@ char *iq_id; enum { - STREAM_METHOD_UNKNOWN = 0, + STREAM_METHOD_UNKNOWN = 0, STREAM_METHOD_BYTESTREAMS = 2 << 1, - STREAM_METHOD_IBB = 2 << 2, + STREAM_METHOD_IBB = 2 << 2, STREAM_METHOD_UNSUPPORTED = 2 << 31 } stream_method; @@ -1379,6 +1379,31 @@ static void jabber_si_xfer_request_denied(PurpleXfer *xfer) { + JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; + JabberStream *js = jsx->js; + + /* + * TODO: It's probably an error if jsx->iq_id == NULL. g_return_if_fail + * might be warranted. + */ + if (jsx->iq_id && !jsx->accepted) { + JabberIq *iq; + xmlnode *error, *child; + iq = jabber_iq_new(js, JABBER_IQ_ERROR); + xmlnode_set_attrib(iq->node, "to", xfer->who); + jabber_iq_set_id(iq, jsx->iq_id); + + error = xmlnode_new_child(iq->node, "error"); + xmlnode_set_attrib(error, "type", "cancel"); + child = xmlnode_new_child(error, "forbidden"); + xmlnode_set_namespace(child, "urn:ietf:params:xml:ns:xmpp-stanzas"); + child = xmlnode_new_child(error, "text"); + xmlnode_set_namespace(child, "urn:ietf:params:xml:ns:xmpp-stanzas"); + xmlnode_insert_data(child, "Offer Declined", -1); + + jabber_iq_send(iq); + } + jabber_si_xfer_free(xfer); purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_request_denied\n"); } @@ -1421,6 +1446,7 @@ purple_notify_error(js->gc, _("File Send Failed"), _("File Send Failed"), msg); g_free(msg); + purple_xfer_cancel_local(xfer); } } @@ -1434,13 +1460,38 @@ JabberSIXfer *jsx = xfer->data; char **who_v = g_strsplit(xfer->who, "/", 2); char *who; + JabberBuddy *jb; + JabberBuddyResource *jbr = NULL; + + jb = jabber_buddy_find(jsx->js, who_v[0], FALSE); + if (jb) { + jbr = jabber_buddy_find_resource(jb, resource); + } who = g_strdup_printf("%s/%s", who_v[0], resource); 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); + + if (jbr) { + char *msg; + + if (jabber_resource_has_capability(jbr, XEP_0047_NAMESPACE)) + jsx->stream_method |= STREAM_METHOD_IBB; + if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/si/profile/file-transfer")) { + jabber_si_xfer_send_request(xfer); + return; + } + + msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who); + purple_notify_error(jsx->js->gc, _("File Send Failed"), + _("File Send Failed"), msg); + g_free(msg); + purple_xfer_cancel_local(xfer); + } else { + jabber_disco_info_do(jsx->js, who, + jabber_si_xfer_send_disco_cb, xfer); + } } static void resource_select_ok_cb(PurpleXfer *xfer, PurpleRequestFields *fields) @@ -1529,6 +1580,8 @@ xmlnode_set_attrib(iq->node, "to", xfer->who); if(jsx->iq_id) jabber_iq_set_id(iq, jsx->iq_id); + else + purple_debug_error("jabber", "Sending SI result with new IQ id.\n"); jsx->accepted = TRUE; diff -r a0748a638eb0 -r d677981cf97e libpurple/protocols/oscar/flap_connection.c --- a/libpurple/protocols/oscar/flap_connection.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Thu Jul 16 02:20:40 2009 +0000 @@ -505,7 +505,6 @@ g_free(conn->error_message); g_free(conn->cookie); - g_free(conn->ssl_cert_cn); /* * Free conn->internal, if necessary diff -r a0748a638eb0 -r d677981cf97e libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Jul 16 02:20:40 2009 +0000 @@ -1246,32 +1246,6 @@ } static void -ssl_proxy_conn_established_cb(gpointer data, gint source, const gchar *error_message) -{ - OscarData *od; - PurpleConnection *gc; - PurpleAccount *account; - FlapConnection *conn; - - conn = data; - od = conn->od; - gc = od->gc; - account = purple_connection_get_account(gc); - - conn->connect_data = NULL; - - if (source < 0) - { - connection_common_error_cb(conn, error_message); - return; - } - - conn->gsc = purple_ssl_connect_with_host_fd(account, source, - ssl_connection_established_cb, ssl_connection_error_cb, - conn->ssl_cert_cn, conn); -} - -static void flap_connection_established_bos(OscarData *od, FlapConnection *conn) { PurpleConnection *gc = od->gc; @@ -1943,12 +1917,13 @@ if (od->use_ssl) { /* - * This shouldn't be hardcoded except that the server isn't sending - * us a name to use for comparing the certificate common name. + * This shouldn't be hardcoded to "bos.oscar.aol.com" except that + * the server isn't sending us a name to use for comparing the + * certificate common name. */ - newconn->ssl_cert_cn = g_strdup("bos.oscar.aol.com"); - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - ssl_proxy_conn_established_cb, newconn); + newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port, + ssl_connection_established_cb, ssl_connection_error_cb, + "bos.oscar.aol.com", newconn); } else { @@ -1957,7 +1932,7 @@ } g_free(host); - if (newconn->connect_data == NULL) + if (newconn->gsc == NULL && newconn->connect_data == NULL) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect")); return 0; @@ -2114,15 +2089,9 @@ if (redir->use_ssl) { - /* - * TODO: It should be possible to specify a certificate common name - * distinct from the host we're passing to purple_ssl_connect. The - * way to work around that is to use purple_proxy_connect + - * purple_ssl_connect_with_host_fd - */ - newconn->ssl_cert_cn = g_strdup(redir->ssl_cert_cn); - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - ssl_proxy_conn_established_cb, newconn); + newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port, + ssl_connection_established_cb, ssl_connection_error_cb, + redir->ssl_cert_cn, newconn); } else { diff -r a0748a638eb0 -r d677981cf97e libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.h Thu Jul 16 02:20:40 2009 +0000 @@ -429,7 +429,6 @@ guint16 cookielen; guint8 *cookie; gpointer new_conn_data; - gchar *ssl_cert_cn; int fd; PurpleSslConnection *gsc; diff -r a0748a638eb0 -r d677981cf97e libpurple/sslconn.c --- a/libpurple/sslconn.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/sslconn.c Thu Jul 16 02:20:40 2009 +0000 @@ -100,6 +100,15 @@ PurpleSslInputFunction func, PurpleSslErrorFunction error_func, void *data) { + return purple_ssl_connect_with_ssl_cn(account, host, port, func, error_func, + NULL, data); +} + +PurpleSslConnection * +purple_ssl_connect_with_ssl_cn(PurpleAccount *account, const char *host, int port, + PurpleSslInputFunction func, PurpleSslErrorFunction error_func, + const char *ssl_cn, void *data) +{ PurpleSslConnection *gsc; g_return_val_if_fail(host != NULL, NULL); @@ -116,7 +125,7 @@ gsc = g_new0(PurpleSslConnection, 1); gsc->fd = -1; - gsc->host = g_strdup(host); + gsc->host = ssl_cn ? g_strdup(ssl_cn) : g_strdup(host); gsc->port = port; gsc->connect_cb_data = data; gsc->connect_cb = func; diff -r a0748a638eb0 -r d677981cf97e libpurple/sslconn.h --- a/libpurple/sslconn.h Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/sslconn.h Thu Jul 16 02:20:40 2009 +0000 @@ -186,6 +186,31 @@ PurpleSslErrorFunction error_func, void *data); +/** + * Makes a SSL connection to the specified host and port, using the separate + * name to verify with the certificate. The caller should keep track of the + * returned value and use it to cancel the connection, if needed. + * + * @param account The account making the connection. + * @param host The destination host. + * @param port The destination port. + * @param func The SSL input handler function. + * @param error_func The SSL error handler function. This function + * should NOT call purple_ssl_close(). In + * the event of an error the #PurpleSslConnection will be + * destroyed for you. + * @param ssl_host The hostname of the other peer (to verify the CN) + * @param data User-defined data. + * + * @return The SSL connection handle. + * @since 2.6.0 + */ +PurpleSslConnection *purple_ssl_connect_with_ssl_cn(PurpleAccount *account, const char *host, + int port, PurpleSslInputFunction func, + PurpleSslErrorFunction error_func, + const char *ssl_host, + void *data); + #if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_SSLCONN_C_) /** * Makes a SSL connection using an already open file descriptor. diff -r a0748a638eb0 -r d677981cf97e libpurple/tests/test_jabber_jutil.c --- a/libpurple/tests/test_jabber_jutil.c Wed Jul 15 16:37:46 2009 +0000 +++ b/libpurple/tests/test_jabber_jutil.c Thu Jul 16 02:20:40 2009 +0000 @@ -80,6 +80,17 @@ jabber_id_free(jid); \ } +#define assert_jid_parts(expect_node, expect_domain, str) { \ + JabberID *jid = jabber_id_new(str); \ + fail_if(jid == NULL, "JID '%s' is valid but jabber_id_new() rejected it", str); \ + fail_if(jid->node == NULL, "JID '%s' is valid but jabber_id_new() didn't return a node", str); \ + fail_if(jid->domain == NULL, "JID '%s' is valid but jabber_id_new() didn't return a domain", str); \ + fail_if(jid->resource != NULL, "JID '%s' doesn't contain a resource", str); \ + assert_string_equal(expect_node, jid->node); \ + assert_string_equal(expect_domain, jid->domain); \ + jabber_id_free(jid); \ +} + START_TEST(test_jabber_id_new) { assert_valid_jid("gmail.com"); @@ -117,6 +128,12 @@ assert_invalid_jid("mark.doliner@gmail\\stuff.org"); assert_invalid_jid("paul@[::1]124"); assert_invalid_jid("paul@2[::1]124/as"); + + /* Ensure that jabber_id_new is properly lowercasing node and domains */ + assert_jid_parts("paul", "darkrain42.org", "PaUL@darkrain42.org"); + assert_jid_parts("paul", "darkrain42.org", "paul@DaRkRaIn42.org"); + assert_jid_parts("ꙥ", "darkrain42.org", "Ꙥ@darkrain42.org"); + assert_jid_parts("paul", "өarkrain42.org", "paul@Өarkrain42.org"); } END_TEST diff -r a0748a638eb0 -r d677981cf97e pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Wed Jul 15 16:37:46 2009 +0000 +++ b/pidgin/gtkdialogs.c Thu Jul 16 02:20:40 2009 +0000 @@ -76,7 +76,7 @@ {"Paul 'darkrain42' Aurich", NULL, NULL }, {"John 'rekkanoryo' Bailey", N_("bug master"), NULL}, {"Ethan 'Paco-Paco' Blanton", NULL, NULL}, - {"Hylke Bons", N_("artist"), "h.bons@student.rug.nl"}, + {"Hylke Bons", N_("artist"), "hylkebons@gmail.com"}, {"Thomas Butter", NULL, NULL}, /* feel free to not translate this */ {N_("Ka-Hing Cheung"), NULL, NULL}, diff -r a0748a638eb0 -r d677981cf97e pidgin/gtkutils.c --- a/pidgin/gtkutils.c Wed Jul 15 16:37:46 2009 +0000 +++ b/pidgin/gtkutils.c Thu Jul 16 02:20:40 2009 +0000 @@ -3736,10 +3736,9 @@ return; } - if (!g_file_set_contents(file, contents, length, &error)) { - purple_debug_error("gtkutils", "Unable to write contents to %s: %s\n", - file, error->message); - g_error_free(error); + if (!purple_util_write_data_to_file_absolute(file, contents, length)) { + purple_debug_error("gtkutils", "Unable to write contents to %s\n", + file); } }