# HG changeset patch # User Mark Doliner # Date 1233138217 0 # Node ID 8c8948b9f6025e09839a38e4b48a120c7f479228 # Parent 9bff90dfb2e5ee76a4df1aa2531fd8d2f1188602# Parent 29f953732186db6b506b88315fa6a624e844c0b0 propagate from branch 'im.pidgin.pidgin' (head 8ae2fca06a3be4b99cc677c8f880d9521a35ec8b) to branch 'im.pidgin.pidgin.next.minor' (head 3725318168b46a11998066b36f9311e26d51885b) diff -r 9bff90dfb2e5 -r 8c8948b9f602 COPYRIGHT --- a/COPYRIGHT Wed Jan 28 10:16:41 2009 +0000 +++ b/COPYRIGHT Wed Jan 28 10:23:37 2009 +0000 @@ -445,6 +445,7 @@ Todd Troxell Brad Turcotte Kyle Turman +Jon Turney Junichi Uekawa Igor Vlasenko István Váradi diff -r 9bff90dfb2e5 -r 8c8948b9f602 ChangeLog --- a/ChangeLog Wed Jan 28 10:16:41 2009 +0000 +++ b/ChangeLog Wed Jan 28 10:23:37 2009 +0000 @@ -20,6 +20,7 @@ * Fix transfer of buddy icons, custom smileys and files from the latest WLM 9 official client. (Thomas Gibson-Robinson) * Fix a crash when removing an account with an unknown protocol id. + * Large (multi-part) messages on MSN are now correctly re-combined. Finch: * Allow rebinding keys to change the focused widget (details in the diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/certificate.c diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/core.c diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/dbus-analyze-functions.py --- a/libpurple/dbus-analyze-functions.py Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/dbus-analyze-functions.py Wed Jan 28 10:23:37 2009 +0000 @@ -117,7 +117,7 @@ self.params.append(Parameter.fromtokens(paramtexts[i].split(), i)) self.call = "%s(%s)" % (self.function.name, - ", ".join([param.name for param in self.params])) + ", ".join(param.name for param in self.params)) def process(self): @@ -160,6 +160,10 @@ elif type[0].startswith("Purple") or type[0] == "xmlnode": return self.inputpurplestructure(type, name) + # special case for *_get_data functions, be careful here... + elif (type[0] == "size_t") and (name == "len"): + return self.inputgetdata(type, name) + # unknown pointers are always replaced with NULL else: return self.inputpointer(type, name) @@ -196,6 +200,10 @@ if type[0] in ["GList", "GSList"]: return self.outputlist(type, name) + # Special case for *_get_data functions + if type[0] == "gconstpointer": + return self.outputgetdata(type, name) + raise myexception @@ -309,7 +317,13 @@ self.returncode.append("return garray_int_to_%s(%s);" % (type[0].lower(), name)); - + # Special case for *_get_data functions, don't need client bindings, + # but do need the name so it doesn't crash + def inputgetdata(self, type, name): + raise myexception + def outputgetdata(self, type, name): + raise myexception + class ServerBinding (Binding): def __init__(self, functiontext, paramtexts): Binding.__init__(self, functiontext, paramtexts) @@ -475,6 +489,21 @@ % (name, name)) self.addouttype("ai", name) + # Special case for *_get_data functions + def inputgetdata(self, type, name): + self.cdecls.append("\tsize_t %s = 0;" % name) + return True + def outputgetdata(self, type, name): + # This is a total hack, but self.call is set up before the parameters + # are processed, so we can't tell it to pass a parameter by reference. + self.call = "%s(%s)" % (self.function.name, + ", ".join(param.name if param.name != "len" else "&len" for param in self.params)) + + self.cdecls.append("\tgconstpointer %s;" % name) + self.ccode.append("\t%s = %s;" % (name, self.call)) + self.cparamsout.append("DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &%s, %s" \ + % (name, "len")) + self.addouttype("ay", name) class BindingSet: regexp = r"^(\w[^()]*)\(([^()]*)\)\s*;\s*$"; diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/msn/cmdproc.c --- a/libpurple/protocols/msn/cmdproc.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Wed Jan 28 10:23:37 2009 +0000 @@ -35,6 +35,9 @@ cmdproc->txqueue = g_queue_new(); cmdproc->history = msn_history_new(); + cmdproc->multiparts = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, (GDestroyNotify)msn_message_unref); + return cmdproc; } @@ -53,6 +56,8 @@ if (cmdproc->last_cmd != NULL) msn_command_destroy(cmdproc->last_cmd); + g_hash_table_destroy(cmdproc->multiparts); + g_free(cmdproc); } @@ -235,6 +240,61 @@ msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { MsnMsgTypeCb cb; + const char *messageId = NULL; + + /* Multi-part messages */ + if ((messageId = msn_message_get_attr(msg, "Message-ID")) != NULL) { + const char *chunk_text = msn_message_get_attr(msg, "Chunks"); + guint chunk; + if (chunk_text != NULL) { + chunk = strtol(chunk_text, NULL, 10); + /* 1024 chunks of ~1300 bytes is ~1MB, which seems OK to prevent + some random client causing pidgin to hog a ton of memory. + Probably should figure out the maximum that the official client + actually supports, though. */ + if (chunk > 0 && chunk < 1024) { + msg->total_chunks = chunk; + msg->received_chunks = 1; + g_hash_table_insert(cmdproc->multiparts, (gpointer)messageId, msn_message_ref(msg)); + purple_debug_info("msn", "Received chunked message, messageId: '%s', total chunks: %d\n", + messageId, chunk); + } else { + purple_debug_error("msn", "MessageId '%s' has too many chunks: %d\n", messageId, chunk); + } + return; + } else { + chunk_text = msn_message_get_attr(msg, "Chunk"); + if (chunk_text != NULL) { + MsnMessage *first = g_hash_table_lookup(cmdproc->multiparts, messageId); + chunk = strtol(chunk_text, NULL, 10); + if (first == NULL) { + purple_debug_error("msn", + "Unable to find first chunk of messageId '%s' to correspond with chunk %d.\n", + messageId, chunk+1); + } else if (first->received_chunks == chunk) { + /* Chunk is from 1 to total-1 (doesn't count first one) */ + purple_debug_info("msn", "Received chunk %d of %d, messageId: '%s'\n", + chunk+1, first->total_chunks, messageId); + first->body = g_realloc(first->body, first->body_len + msg->body_len); + memcpy(first->body + first->body_len, msg->body, msg->body_len); + first->body_len += msg->body_len; + first->received_chunks++; + if (first->received_chunks != first->total_chunks) + return; + else + /* We're done! Send it along... The caller takes care of + freeing the old one. */ + msg = first; + } else { + /* TODO: Can you legitimately receive chunks out of order? */ + g_hash_table_remove(cmdproc->multiparts, messageId); + return; + } + } else { + purple_debug_error("msn", "Received MessageId '%s' with no chunk number!\n", messageId); + } + } + } if (msn_message_get_content_type(msg) == NULL) { @@ -245,15 +305,14 @@ cb = g_hash_table_lookup(cmdproc->cbs_table->msgs, msn_message_get_content_type(msg)); - if (cb == NULL) - { + if (cb != NULL) + cb(cmdproc, msg); + else purple_debug_warning("msn", "Unhandled content-type '%s'\n", msn_message_get_content_type(msg)); - return; - } - - cb(cmdproc, msg); + if (messageId != NULL) + g_hash_table_remove(cmdproc->multiparts, messageId); } void diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/msn/cmdproc.h --- a/libpurple/protocols/msn/cmdproc.h Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/msn/cmdproc.h Wed Jan 28 10:23:37 2009 +0000 @@ -46,6 +46,8 @@ MsnHistory *history; + GHashTable *multiparts; /**< Multi-part message ID's */ + void *data; /**< Extra data, like the switchboard. */ }; diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/msn/msg.h --- a/libpurple/protocols/msn/msg.h Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/msn/msg.h Wed Jan 28 10:23:37 2009 +0000 @@ -109,6 +109,8 @@ char *charset; char *body; gsize body_len; + guint total_chunks; /**< How many chunks in this multi-part message */ + guint received_chunks; /**< How many chunks we've received so far */ MsnSlpHeader msnslp_header; MsnSlpFooter msnslp_footer; diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/msn/switchboard.c --- a/libpurple/protocols/msn/switchboard.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/msn/switchboard.c Wed Jan 28 10:23:37 2009 +0000 @@ -799,7 +799,7 @@ msn_cmdproc_process_msg(cmdproc, msg); - msn_message_destroy(msg); + msn_message_unref(msg); } static void diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/oscar/family_chat.c --- a/libpurple/protocols/oscar/family_chat.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/oscar/family_chat.c Wed Jan 28 10:23:37 2009 +0000 @@ -79,13 +79,15 @@ if (conn->type != SNAC_FAMILY_CHAT) continue; - if (!conn->internal) { - purple_debug_misc("oscar", "faim: chat: chat connection with no name! (fd = %d)\n", conn->fd); + if (!conn->internal) + { + purple_debug_misc("oscar", "%sfaim: chat: chat connection with no name! (fd = %d)\n", + conn->gsc ? "(ssl) " : "", conn->gsc ? conn->gsc->fd : conn->fd); continue; } if (strcmp(ccp->name, name) == 0) - return conn;; + return conn; } return NULL; diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/oscar/family_oservice.c --- a/libpurple/protocols/oscar/family_oservice.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/oscar/family_oservice.c Wed Jan 28 10:23:37 2009 +0000 @@ -103,12 +103,29 @@ aim_srv_requestnew(OscarData *od, guint16 serviceid) { FlapConnection *conn; + ByteStream bs; + aim_snacid_t snacid; + GSList *tlvlist = NULL; conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS); if(!conn) return; - aim_genericreq_s(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, &serviceid); + byte_stream_new(&bs, 6); + + byte_stream_put16(&bs, serviceid); + + if (od->use_ssl) + /* Request SSL Connection */ + aim_tlvlist_add_noval(&tlvlist, 0x008c); + + aim_tlvlist_write(&bs, &tlvlist); + aim_tlvlist_free(tlvlist); + + snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, NULL, 0); + flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, snacid, &bs); + + byte_stream_destroy(&bs); } /* @@ -127,10 +144,10 @@ struct chatsnacinfo csi; conn = flap_connection_findbygroup(od, SNAC_FAMILY_BOS); - if (!conn || !roomname || !strlen(roomname)) + if (!conn || !roomname || roomname[0] == '\0') return -EINVAL; - byte_stream_new(&bs, 502); + byte_stream_new(&bs, 506); memset(&csi, 0, sizeof(csi)); csi.exchange = exchange; @@ -143,6 +160,11 @@ byte_stream_put16(&bs, 0x000e); aim_tlvlist_add_chatroom(&tlvlist, 0x0001, exchange, roomname, instance); + + if (od->use_ssl) + /* Request SSL Connection */ + aim_tlvlist_add_noval(&tlvlist, 0x008c); + aim_tlvlist_write(&bs, &tlvlist); aim_tlvlist_free(tlvlist); @@ -179,6 +201,8 @@ redir.ip = aim_tlv_getstr(tlvlist, 0x0005, 1); redir.cookielen = aim_tlv_gettlv(tlvlist, 0x0006, 1)->length; redir.cookie = (guchar *)aim_tlv_getstr(tlvlist, 0x0006, 1); + redir.ssl_cert_cn = aim_tlv_getstr(tlvlist, 0x008d, 1); + redir.use_ssl = aim_tlv_get8(tlvlist, 0x008e, 1); /* Fetch original SNAC so we can get csi if needed */ origsnac = aim_remsnac(od, snac->id); @@ -196,6 +220,7 @@ g_free((void *)redir.ip); g_free((void *)redir.cookie); + g_free((void *)redir.ssl_cert_cn); if (origsnac) g_free(origsnac->data); diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/oscar/flap_connection.c --- a/libpurple/protocols/oscar/flap_connection.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Wed Jan 28 10:23:37 2009 +0000 @@ -364,6 +364,15 @@ conn->fd = -1; } + if (conn->gsc != NULL) + { + if (conn->type == SNAC_FAMILY_LOCATE) + flap_connection_send_close(od, conn); + + purple_ssl_close(conn->gsc); + conn->gsc = NULL; + } + if (conn->watcher_incoming != 0) { purple_input_remove(conn->watcher_incoming); @@ -467,6 +476,7 @@ g_free(conn->error_message); g_free(conn->cookie); + g_free(conn->ssl_cert_cn); /* * Free conn->internal, if necessary @@ -844,24 +854,31 @@ * All complete FLAPs handled immedate after they're received. * Incomplete FLAP data is stored locally and appended to the next * time this callback is triggered. + * + * This is called by flap_connection_recv_cb and + * flap_connection_recv_cb_ssl for unencrypted/encrypted connections. */ -void -flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) +static void +flap_connection_recv(FlapConnection *conn) { - FlapConnection *conn; + gpointer buf; + gsize buflen; gssize read; - conn = data; - /* Read data until we run out of data and break out of the loop */ while (TRUE) { /* Start reading a new FLAP */ if (conn->buffer_incoming.data.data == NULL) { + buf = conn->header + conn->header_received; + buflen = 6 - conn->header_received; + /* Read the first 6 bytes (the FLAP header) */ - read = recv(conn->fd, conn->header + conn->header_received, - 6 - conn->header_received, 0); + if (conn->gsc) + read = purple_ssl_read(conn->gsc, buf, buflen); + else + read = recv(conn->fd, buf, buflen, 0); /* Check if the FLAP server closed the connection */ if (read == 0) @@ -918,13 +935,15 @@ conn->buffer_incoming.data.offset = 0; } - if (conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset) + buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset; + if (buflen) { + buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset]; /* Read data into the temporary FlapFrame until it is complete */ - read = recv(conn->fd, - &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset], - conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset, - 0); + if (conn->gsc) + read = purple_ssl_read(conn->gsc, buf, buflen); + else + read = recv(conn->fd, buf, buflen, 0); /* Check if the FLAP server closed the connection */ if (read == 0) @@ -964,6 +983,29 @@ } } +void +flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + FlapConnection *conn = data; + + flap_connection_recv(conn); +} + +void +flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond) +{ + FlapConnection *conn = data; + + flap_connection_recv(conn); +} + +/** + * @param source When this function is called as a callback source is + * set to the fd that triggered the callback. But this function + * is also called directly from flap_connection_send_byte_stream(), + * in which case source will be -1. So don't use source--use + * conn->gsc or conn->fd instead. + */ static void send_cb(gpointer data, gint source, PurpleInputCondition cond) { @@ -980,7 +1022,11 @@ return; } - ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0); + if (conn->gsc) + ret = purple_ssl_write(conn->gsc, conn->buffer_outgoing->outptr, + writelen); + else + ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0); if (ret <= 0) { if (ret < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) @@ -990,8 +1036,13 @@ /* Error! */ purple_input_remove(conn->watcher_outgoing); conn->watcher_outgoing = 0; - close(conn->fd); - conn->fd = -1; + if (conn->gsc) { + purple_ssl_close(conn->gsc); + conn->gsc = NULL; + } else { + close(conn->fd); + conn->fd = -1; + } flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); return; @@ -1017,11 +1068,17 @@ purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count); /* If we haven't already started writing stuff, then start the cycle */ - if ((conn->watcher_outgoing == 0) && (conn->fd >= 0)) + if (conn->watcher_outgoing == 0) { - conn->watcher_outgoing = purple_input_add(conn->fd, - PURPLE_INPUT_WRITE, send_cb, conn); - send_cb(conn, conn->fd, 0); + if (conn->gsc) { + conn->watcher_outgoing = purple_input_add(conn->gsc->fd, + PURPLE_INPUT_WRITE, send_cb, conn); + send_cb(conn, -1, 0); + } else if (conn->fd >= 0) { + conn->watcher_outgoing = purple_input_add(conn->fd, + PURPLE_INPUT_WRITE, send_cb, conn); + send_cb(conn, -1, 0); + } } } diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Wed Jan 28 10:23:37 2009 +0000 @@ -1090,59 +1090,64 @@ } /** - * This is the callback function anytime purple_proxy_connect() - * establishes a new TCP connection with an oscar host. Depending - * on the type of host, we do a few different things here. + * This is called from the callback functions for establishing + * a TCP connection with an oscar host if an error occurred. */ static void -connection_established_cb(gpointer data, gint source, const gchar *error_message) +connection_common_error_cb(FlapConnection *conn, const gchar *error_message) { - PurpleConnection *gc; OscarData *od; + PurpleConnection *gc; + + od = conn->od; + gc = od->gc; + + purple_debug_error("oscar", "unable to connect to FLAP " + "server of type 0x%04hx\n", conn->type); + + if (conn->type == SNAC_FAMILY_AUTH) + { + gchar *msg; + msg = g_strdup_printf(_("Could not connect to authentication server:\n%s"), + error_message); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); + g_free(msg); + } + else if (conn->type == SNAC_FAMILY_LOCATE) + { + gchar *msg; + msg = g_strdup_printf(_("Could not connect to BOS server:\n%s"), + error_message); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); + g_free(msg); + } + else + { + /* Maybe we should call this for BOS connections, too? */ + flap_connection_schedule_destroy(conn, + OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message); + } +} + +/** + * This is called from the callback functions for establishing + * a TCP connection with an oscar host. Depending on the type + * of host, we do a few different things here. + */ +static void +connection_common_established_cb(FlapConnection *conn) +{ + 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; - conn->fd = source; - - if (source < 0) - { - purple_debug_error("oscar", "unable to connect to FLAP " - "server of type 0x%04hx\n", conn->type); - if (conn->type == SNAC_FAMILY_AUTH) - { - gchar *msg; - msg = g_strdup_printf(_("Could not connect to authentication server:\n%s"), - error_message); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - } - else if (conn->type == SNAC_FAMILY_LOCATE) - { - gchar *msg; - msg = g_strdup_printf(_("Could not connect to BOS server:\n%s"), - error_message); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); - g_free(msg); - } - else - { - /* Maybe we should call this for BOS connections, too? */ - flap_connection_schedule_destroy(conn, - OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message); - } - return; - } - purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n", conn->type); - conn->watcher_incoming = purple_input_add(conn->fd, - PURPLE_INPUT_READ, flap_connection_recv_cb, conn); + if (conn->cookie == NULL) flap_connection_send_version(od, conn); else @@ -1173,6 +1178,85 @@ } static void +connection_established_cb(gpointer data, gint source, const gchar *error_message) +{ + FlapConnection *conn; + + conn = data; + + conn->connect_data = NULL; + conn->fd = source; + + if (source < 0) + { + connection_common_error_cb(conn, error_message); + return; + } + + conn->watcher_incoming = purple_input_add(conn->fd, + PURPLE_INPUT_READ, flap_connection_recv_cb, conn); + connection_common_established_cb(conn); +} + +static void +ssl_connection_established_cb(gpointer data, PurpleSslConnection *gsc, + PurpleInputCondition cond) +{ + FlapConnection *conn; + + conn = data; + + purple_ssl_input_add(gsc, flap_connection_recv_cb_ssl, conn); + connection_common_established_cb(conn); +} + +static void +ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, + gpointer data) +{ + FlapConnection *conn; + + conn = data; + + if (conn->watcher_outgoing) + { + purple_input_remove(conn->watcher_outgoing); + conn->watcher_outgoing = 0; + } + + /* sslconn frees the connection on error */ + conn->gsc = NULL; + + connection_common_error_cb(conn, purple_ssl_strerror(error)); +} + +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; @@ -1433,17 +1517,56 @@ gc->flags |= PURPLE_CONNECTION_AUTO_RESP; } + od->use_ssl = purple_account_get_bool(account, "use_ssl", OSCAR_DEFAULT_USE_SSL); + /* Connect to core Purple signals */ purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc); purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc); newconn = flap_connection_new(od, SNAC_FAMILY_AUTH); - newconn->connect_data = purple_proxy_connect(NULL, account, - purple_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER), - purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), - connection_established_cb, newconn); - if (newconn->connect_data == NULL) - { + if (od->use_ssl) { + if (purple_ssl_is_supported()) { + const char *server = purple_account_get_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER); + /* + * If the account's server is what the oscar prpl has offered as + * the default login server through the vast eons (all two of + * said default options, AFAIK) and the user wants SSL, we'll + * do what we know is best for them and change the setting out + * from under them to the SSL login server. + */ + if (!strcmp(server, OSCAR_DEFAULT_LOGIN_SERVER) || !strcmp(server, OSCAR_OLD_LOGIN_SERVER)) { + purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n"); + purple_account_set_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER); + server = OSCAR_DEFAULT_SSL_LOGIN_SERVER; + } + + newconn->gsc = purple_ssl_connect(account, server, + purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), + ssl_connection_established_cb, ssl_connection_error_cb, newconn); + } else { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, + _("SSL support unavailable")); + } + } else { + const char *server = purple_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); + + /* + * See the comment above. We do the reverse here. If they don't want + * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER, + * set it back to the default. + */ + if (!strcmp(server, OSCAR_DEFAULT_SSL_LOGIN_SERVER)) { + purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n"); + purple_account_set_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); + server = OSCAR_DEFAULT_LOGIN_SERVER; + } + + newconn->connect_data = purple_proxy_connect(NULL, account, server, + purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), + connection_established_cb, newconn); + } + + if (newconn->gsc == NULL && newconn->connect_data == NULL) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Couldn't connect to host")); return; @@ -1568,8 +1691,23 @@ newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE); newconn->cookielen = info->cookielen; newconn->cookie = g_memdup(info->cookie, info->cookielen); - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - connection_established_cb, newconn); + + 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. + */ + 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); + } + else + { + newconn->connect_data = purple_proxy_connect(NULL, account, host, port, + connection_established_cb, newconn); + } + g_free(host); if (newconn->connect_data == NULL) { @@ -1874,8 +2012,22 @@ else host = g_strdup(redir->ip); - purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n", - host, port, redir->group); + /* + * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts + * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext. + */ + if (redir->use_ssl && (redir->group == SNAC_FAMILY_ADMIN || + redir->group == SNAC_FAMILY_BART)) + { + purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n", + redir->group); + redir->use_ssl = 0; + } + + purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx%s\n", + host, port, redir->group, + od->use_ssl && !redir->use_ssl ? " without SSL, despite main stream encryption" : ""); + newconn = flap_connection_new(od, redir->group); newconn->cookielen = redir->cookielen; newconn->cookie = g_memdup(redir->cookie, redir->cookielen); @@ -1893,9 +2045,26 @@ purple_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc->name, cc->exchange); } - newconn->connect_data = purple_proxy_connect(NULL, account, host, port, - connection_established_cb, newconn); - if (newconn->connect_data == NULL) + + 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); + } + else + { + newconn->connect_data = purple_proxy_connect(NULL, account, host, port, + connection_established_cb, newconn); + } + + if (newconn->gsc == NULL && newconn->connect_data == NULL) { flap_connection_schedule_destroy(newconn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, @@ -6927,6 +7096,10 @@ option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT); prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + option = purple_account_option_bool_new(_("Use SSL"), "use_ssl", + OSCAR_DEFAULT_USE_SSL); + prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + option = purple_account_option_bool_new( _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy", OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY); diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.h Wed Jan 28 10:23:37 2009 +0000 @@ -34,6 +34,7 @@ #include "eventloop.h" #include "internal.h" #include "proxy.h" +#include "sslconn.h" #include #include @@ -417,8 +418,10 @@ guint16 cookielen; guint8 *cookie; gpointer new_conn_data; + gchar *ssl_cert_cn; int fd; + PurpleSslConnection *gsc; guint8 header[6]; gssize header_received; FlapFrame buffer_incoming; @@ -476,6 +479,7 @@ GHashTable *buddyinfo; GSList *requesticon; + gboolean use_ssl; gboolean icq; guint getblisttimer; @@ -593,6 +597,8 @@ const char *ip; guint16 cookielen; const guint8 *cookie; + const char *ssl_cert_cn; + guint8 use_ssl; struct { /* group == SNAC_FAMILY_CHAT */ guint16 exchange; const char *room; @@ -616,6 +622,8 @@ FlapConnection *flap_connection_getbytype(OscarData *, int type); FlapConnection *flap_connection_getbytype_all(OscarData *, int type); void flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond); +void flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); + void flap_connection_send(FlapConnection *conn, FlapFrame *frame); void flap_connection_send_version(OscarData *od, FlapConnection *conn); void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy); diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/oscar/oscarcommon.h --- a/libpurple/protocols/oscar/oscarcommon.h Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/oscar/oscarcommon.h Wed Jan 28 10:23:37 2009 +0000 @@ -32,6 +32,8 @@ #define OSCAR_DEFAULT_LOGIN_SERVER "login.messaging.aol.com" #define OSCAR_DEFAULT_LOGIN_PORT 5190 +#define OSCAR_DEFAULT_SSL_LOGIN_SERVER "slogin.oscar.aol.com" +#define OSCAR_OLD_LOGIN_SERVER "login.oscar.aol.com" #ifndef _WIN32 #define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1" #else @@ -42,6 +44,7 @@ #define OSCAR_DEFAULT_WEB_AWARE FALSE #define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE #define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE +#define OSCAR_DEFAULT_USE_SSL FALSE #ifdef _WIN32 const char *oscar_get_locale_charset(void); diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/protocols/oscar/peer.c --- a/libpurple/protocols/oscar/peer.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/protocols/oscar/peer.c Wed Jan 28 10:23:37 2009 +0000 @@ -690,7 +690,10 @@ return; } - listener_ip = purple_network_get_my_ip(bos_conn->fd); + if (bos_conn->gsc) + listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd); + else + listener_ip = purple_network_get_my_ip(bos_conn->fd); listener_port = purple_network_get_port_from_fd(conn->listenerfd); if (conn->type == OSCAR_CAPABILITY_DIRECTIM) { diff -r 9bff90dfb2e5 -r 8c8948b9f602 libpurple/status.c --- a/libpurple/status.c Wed Jan 28 10:16:41 2009 +0000 +++ b/libpurple/status.c Wed Jan 28 10:23:37 2009 +0000 @@ -817,28 +817,42 @@ /* Reset any unspecified attributes to their default value */ status_type = purple_status_get_type(status); l = purple_status_type_get_attrs(status_type); - while (l != NULL) - { + while (l != NULL) { PurpleStatusAttr *attr; attr = l->data; - if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp)) - { + l = l->next; + + if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp)) { PurpleValue *default_value; default_value = purple_status_attr_get_value(attr); - if (default_value->type == PURPLE_TYPE_STRING) - purple_status_set_attr_string(status, attr->id, - purple_value_get_string(default_value)); - else if (default_value->type == PURPLE_TYPE_INT) - purple_status_set_attr_int(status, attr->id, - purple_value_get_int(default_value)); - else if (default_value->type == PURPLE_TYPE_BOOLEAN) - purple_status_set_attr_boolean(status, attr->id, - purple_value_get_boolean(default_value)); + if (default_value->type == PURPLE_TYPE_STRING) { + const char *cur = purple_status_get_attr_string(status, attr->id); + const char *def = purple_value_get_string(default_value); + if ((cur == NULL && def == NULL) + || (cur != NULL && def != NULL + && !strcmp(cur, def))) { + continue; + } + + purple_status_set_attr_string(status, attr->id, def); + } else if (default_value->type == PURPLE_TYPE_INT) { + int cur = purple_status_get_attr_int(status, attr->id); + int def = purple_value_get_int(default_value); + if (cur == def) + continue; + + purple_status_set_attr_int(status, attr->id, def); + } else if (default_value->type == PURPLE_TYPE_BOOLEAN) { + gboolean cur = purple_status_get_attr_boolean(status, attr->id); + gboolean def = purple_value_get_boolean(default_value); + if (cur == def) + continue; + + purple_status_set_attr_boolean(status, attr->id, def); + } changed = TRUE; } - - l = l->next; } g_list_free(specified_attr_ids); diff -r 9bff90dfb2e5 -r 8c8948b9f602 pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Wed Jan 28 10:16:41 2009 +0000 +++ b/pidgin/gtkimhtml.c Wed Jan 28 10:23:37 2009 +0000 @@ -5395,12 +5395,14 @@ text_tag_data_destroy(tmp); } - if (tmp == NULL) - purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n"); - else { + if (tmp != NULL) { g_string_append(str, tmp->end); text_tag_data_destroy(tmp); } +#if 0 /* This can't be allowed to happen because it causes the iters to be invalidated in the debug window imhtml during text copying */ + else + purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n"); +#endif while ((tmp = g_queue_pop_head(r))) { g_string_append(str, tmp->start); diff -r 9bff90dfb2e5 -r 8c8948b9f602 pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Wed Jan 28 10:16:41 2009 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Jan 28 10:23:37 2009 +0000 @@ -718,6 +718,7 @@ Delete "$INSTDIR\ca-certs\StartCom_Free_SSL_CA.pem" Delete "$INSTDIR\ca-certs\Verisign_Class3_Primary_CA.pem" Delete "$INSTDIR\ca-certs\VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem" + Delete "$INSTDIR\ca-certs\VeriSign_International_Server_Class_3_CA.pem" Delete "$INSTDIR\ca-certs\Verisign_RSA_Secure_Server_CA.pem" RMDir "$INSTDIR\ca-certs" RMDir /r "$INSTDIR\locale" diff -r 9bff90dfb2e5 -r 8c8948b9f602 share/ca-certs/Makefile.am --- a/share/ca-certs/Makefile.am Wed Jan 28 10:16:41 2009 +0000 +++ b/share/ca-certs/Makefile.am Wed Jan 28 10:23:37 2009 +0000 @@ -10,7 +10,8 @@ EXTRA_CERTS = \ Microsoft_Internet_Authority.pem \ - Microsoft_Secure_Server_Authority.pem + Microsoft_Secure_Server_Authority.pem \ + VeriSign_International_Server_Class_3_CA.pem cacertsdir = $(datadir)/purple/ca-certs diff -r 9bff90dfb2e5 -r 8c8948b9f602 share/ca-certs/VeriSign_International_Server_Class_3_CA.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/VeriSign_International_Server_Class_3_CA.pem Wed Jan 28 10:23:37 2009 +0000 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDgzCCAuygAwIBAgIQRvzrurTQLw+SYJgjP5MHjzANBgkqhkiG9w0BAQUFADBf +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT +LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw +HhcNOTcwNDE3MDAwMDAwWhcNMTYxMDI0MjM1OTU5WjCBujEfMB0GA1UEChMWVmVy +aVNpZ24gVHJ1c3QgTmV0d29yazEXMBUGA1UECxMOVmVyaVNpZ24sIEluYy4xMzAx +BgNVBAsTKlZlcmlTaWduIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gQ2xhc3Mg +MzFJMEcGA1UECxNAd3d3LnZlcmlzaWduLmNvbS9DUFMgSW5jb3JwLmJ5IFJlZi4g +TElBQklMSVRZIExURC4oYyk5NyBWZXJpU2lnbjCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA2IKA6NYZAn0fhRg5JaJlK+G/1AXTvOY2O6rwTGxbtueqPHNFVbLx +veqXQu2aNAoV1Klc9UAl3dkHwTKydWzEyruj/lYncUOqY/UwPpMo5frxCTvzt01O +OfdcSVq4wR3Tsor+cDCVQsv+K1GLWjw6+SJPkLICp1OcTzTnqwSye28CAwEAAaOB +4zCB4DAPBgNVHRMECDAGAQH/AgEAMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw +KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL0NQUzA0BgNV +HSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIGCWCGSAGG+EIEAQYKYIZIAYb4RQEI +ATALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEGMDEGA1UdHwQqMCgwJqAk +oCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA0GCSqGSIb3DQEB +BQUAA4GBAECOSZeWinPdjk3vPmG3yqBirfQOCrt1PeJu2CzHv/S5jDabyqLQnHJG +OfamggNlEcS8vy2m9dk7CrWY+rN4uR7yK0xi1f2yeh3fM/1z+aXYLYwq6tH8sCi2 +6UlIE0uDihtIeyT3ON5vQVS4q1drBt/HotSp9vE2YoCI8ot11oBx +-----END CERTIFICATE-----