# HG changeset patch # User Marcus Lundblad # Date 1233256509 0 # Node ID b018c91fb90bb541adf3e7140eec00e6476804ea # Parent 43da17b9a27e72fe36686af00a80c929b5d01806# Parent c434094cf298cf660459c38a30b4e7b9675ba5a1 propagate from branch 'im.pidgin.pidgin' (head 6a51501ec3aff341c8cd763ffc2076a9bbc07d23) to branch 'im.pidgin.cpw.malu.xmpp.idle' (head 0f53d2b4991ccab13787b974415c2bf7bb16b08f) diff -r 43da17b9a27e -r b018c91fb90b AUTHORS --- a/AUTHORS Thu Jan 22 20:39:12 2009 +0000 +++ b/AUTHORS Thu Jan 29 19:15:09 2009 +0000 @@ -36,7 +36,6 @@ Crazy Patch Writers: ------------------- Paul Aurich -Felipe 'shx' Contreras Marcus 'malu' Lundblad Dennis 'EvilDennisR' Ristuccia Peter 'Fmoo' Ruibal @@ -57,6 +56,7 @@ Retired Crazy Patch Writers: --------------------------- +Felipe 'shx' Contreras Decklin Foster Peter 'Bleeter' Lawler Robert 'Robot101' McQueen diff -r 43da17b9a27e -r b018c91fb90b COPYRIGHT --- a/COPYRIGHT Thu Jan 22 20:39:12 2009 +0000 +++ b/COPYRIGHT Thu Jan 29 19:15:09 2009 +0000 @@ -444,6 +444,7 @@ Todd Troxell Brad Turcotte Kyle Turman +Jon Turney Junichi Uekawa Igor Vlasenko István Váradi @@ -480,6 +481,7 @@ Dan Winship Michal Witkowski Scott Wolchok +Rogier Wolff The Written Word, Inc. Kevin Wu Won Pui Lam Wong diff -r 43da17b9a27e -r b018c91fb90b ChangeLog --- a/ChangeLog Thu Jan 22 20:39:12 2009 +0000 +++ b/ChangeLog Thu Jan 29 19:15:09 2009 +0000 @@ -2,9 +2,17 @@ version 2.5.5 (??/??/????): libpurple: - * Fix transfer of buddy icons, custom smileys and files from the - latest WLM 9 official client. (Thomas Gibson-Robinson) + * Fix transfer of buddy icons, custom smileys, and files from the + latest Windows Live Messenger 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. + * Beta support for SSL connections for AIM and ICQ accounts. To + enable, check the "Use SSL" option from the Advanced tab when + editing your AIM or ICQ account. (Paul Aurich) + * Fix retrieval of ICQ status messages from users of ICQ 6.x, Miranda, + and other libpurple clients (fixes with libpurple users only on + statuses other than Available). (Daniel Ljungborg) Finch: * Allow rebinding keys to change the focused widget (details in the diff -r 43da17b9a27e -r b018c91fb90b doc/finch.1.in --- a/doc/finch.1.in Thu Jan 22 20:39:12 2009 +0000 +++ b/doc/finch.1.in Thu Jan 29 19:15:09 2009 +0000 @@ -543,23 +543,26 @@ \fIhttp://developer.pidgin.im/query?status=new&status=assigned&status=reopened&component=finch+%28gnt%2Fncurses%29&order=priority\fR Before sending a bug report, please verify that you have the latest -version of \fBfinch\fR and \fBlibpurple\fR. Many bugs (major and minor) are +version of \fBfinch\fR and libpurple. Many bugs (major and minor) are fixed at each release, and if yours is out of date, the problem may already have been solved. - .SH PATCHES If you fix a bug in \fBfinch\fR (or otherwise enhance it), please submit a -patch (using \fImtn diff > my.diff\fR against the latest version from the -Monotone repository) at -.br -\fIhttp://developer.pidgin.im/newticket\fR +patch (using \fBmtn diff > my.diff\fR against the latest version from the +Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR -You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR to -discuss development. +You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR +to discuss development. .SH SEE ALSO \fIhttp://pidgin.im/\fR +.br +\fIhttp://developer.pidgin.im/\fR +.br +\fBpurple-remote\fR(1) +.br +\fBpidgin\fR(1) .SH LICENSE This program is free software; you can redistribute it and/or modify diff -r 43da17b9a27e -r b018c91fb90b doc/pidgin.1.in --- a/doc/pidgin.1.in Thu Jan 22 20:39:12 2009 +0000 +++ b/doc/pidgin.1.in Thu Jan 29 19:15:09 2009 +0000 @@ -34,6 +34,9 @@ many common features found in other clients, as well as many unique features. Pidgin is not endorsed by or affiliated with America Online, ICQ, Microsoft, or Yahoo. +.PP +Pidgin can be extended by plugins written in multiple programming languages and +controlled through DBus or \fBpurple-remote\fR. .SH OPTIONS The following options are provided by Pidgin using the standard GNU @@ -556,20 +559,28 @@ .SH BUGS The bug tracker can be reached by visiting \fIhttp://developer.pidgin.im/query\fR -.SH PATCHES -If you fix a bug in Pidgin (or otherwise enhance it), please submit a -patch (using \fImtn diff > my.diff\fR against the latest version from the -Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR - Before sending a bug report, please verify that you have the latest version of Pidgin. Many bugs (major and minor) are fixed at each release, and if yours is out of date, the problem may already have been solved. +.SH PATCHES +If you fix a bug in Pidgin (or otherwise enhance it), please submit a +patch (using \fBmtn diff > my.diff\fR against the latest version from the +Monotone repository) at \fIhttp://developer.pidgin.im/simpleticket\fR + +You are also encouraged to drop by at \fB#pidgin\fR on \fIirc.freenode.net\fR +to discuss development. + + .SH SEE ALSO \fIhttp://pidgin.im/\fR .br \fIhttp://developer.pidgin.im/\fR +.br +\fBpurple-remote\fR(1) +.br +\fBfinch\fR(1) .SH LICENSE This program is free software; you can redistribute it and/or modify @@ -643,8 +654,6 @@ .br Paul Aurich .br - Felipe 'shx' Contreras -.br Marcus 'malu' Lundblad .br Dennis 'EvilDennisR' Ristuccia @@ -687,6 +696,8 @@ Our retired crazy patch writers include: .br + Felipe 'shx' Contreras +.br Decklin Foster .br Peter 'Bleeter' Lawler diff -r 43da17b9a27e -r b018c91fb90b finch/libgnt/gntcolors.c --- a/finch/libgnt/gntcolors.c Thu Jan 22 20:39:12 2009 +0000 +++ b/finch/libgnt/gntcolors.c Thu Jan 29 19:15:09 2009 +0000 @@ -208,7 +208,7 @@ key = g_ascii_strdown(key, -1); color = gnt_colors_get_color(key); g_free(key); - if (color == -1) + if (color == -EINVAL) continue; init_color(color, r, g, b); @@ -251,7 +251,7 @@ int bg = gnt_colors_get_color(bgc); g_free(fgc); g_free(bgc); - if (fg == -1 || bg == -1) + if (fg == -EINVAL || bg == -EINVAL) continue; key = g_ascii_strdown(key, -1); diff -r 43da17b9a27e -r b018c91fb90b libpurple/certificate.c --- a/libpurple/certificate.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/certificate.c Thu Jan 29 19:15:09 2009 +0000 @@ -1546,31 +1546,11 @@ void purple_certificate_uninit(void) { - GList *full_list, *l; - - /* Unregister all Schemes */ - full_list = g_list_copy(cert_schemes); /* Make a working copy */ - for (l = full_list; l; l = l->next) { - purple_certificate_unregister_scheme( - (PurpleCertificateScheme *) l->data ); - } - g_list_free(full_list); - /* Unregister all Verifiers */ - full_list = g_list_copy(cert_verifiers); /* Make a working copy */ - for (l = full_list; l; l = l->next) { - purple_certificate_unregister_verifier( - (PurpleCertificateVerifier *) l->data ); - } - g_list_free(full_list); + g_list_foreach(cert_verifiers, (GFunc)purple_certificate_unregister_verifier, NULL); /* Unregister all Pools */ - full_list = g_list_copy(cert_pools); /* Make a working copy */ - for (l = full_list; l; l = l->next) { - purple_certificate_unregister_pool( - (PurpleCertificatePool *) l->data ); - } - g_list_free(full_list); + g_list_foreach(cert_pools, (GFunc)purple_certificate_unregister_pool, NULL); } gpointer diff -r 43da17b9a27e -r b018c91fb90b libpurple/connection.c --- a/libpurple/connection.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/connection.c Thu Jan 29 19:15:09 2009 +0000 @@ -545,7 +545,7 @@ } if (description == NULL) { - purple_debug_error("connection", "purple_connection_error_reason: check `description != NULL' failed\n"); + purple_debug_error("connection", "purple_connection_error_reason called with NULL description\n"); description = _("Unknown error"); } diff -r 43da17b9a27e -r b018c91fb90b libpurple/core.c --- a/libpurple/core.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/core.c Thu Jan 29 19:15:09 2009 +0000 @@ -198,6 +198,14 @@ /* Transmission ends */ purple_connections_disconnect_all(); + /* + * Certificates must be destroyed before the SSL plugins, because + * PurpleCertificates contain pointers to PurpleCertificateSchemes, + * and the PurpleCertificateSchemes will be unregistered when the + * SSL plugin is uninit. + */ + purple_certificate_uninit(); + /* The SSL plugins must be uninit before they're unloaded */ purple_ssl_uninit(); @@ -220,7 +228,6 @@ purple_notify_uninit(); purple_conversations_uninit(); purple_connections_uninit(); - purple_certificate_uninit(); purple_buddy_icons_uninit(); purple_accounts_uninit(); purple_savedstatuses_uninit(); diff -r 43da17b9a27e -r b018c91fb90b libpurple/dbus-analyze-functions.py --- a/libpurple/dbus-analyze-functions.py Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/dbus-analyze-functions.py Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/protocols/msn/cmdproc.c --- a/libpurple/protocols/msn/cmdproc.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/protocols/msn/cmdproc.h --- a/libpurple/protocols/msn/cmdproc.h Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/msn/cmdproc.h Thu Jan 29 19:15:09 2009 +0000 @@ -46,6 +46,8 @@ MsnHistory *history; + GHashTable *multiparts; /**< Multi-part message ID's */ + void *data; /**< Extra data, like the switchboard. */ }; diff -r 43da17b9a27e -r b018c91fb90b libpurple/protocols/msn/contact.c --- a/libpurple/protocols/msn/contact.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/msn/contact.c Thu Jan 29 19:15:09 2009 +0000 @@ -1362,7 +1362,8 @@ xmlnode *changes; purple_debug_info("msn", "Update contact information with new %s: %s\n", - type==MSN_UPDATE_DISPLAY ? "display name" : "alias", value); + type == MSN_UPDATE_DISPLAY ? "display name" : "alias", + value ? value : "(null)"); purple_debug_info("msn", "passport=%s\n", passport); g_return_if_fail(passport != NULL); contact_info = xmlnode_new("contactInfo"); diff -r 43da17b9a27e -r b018c91fb90b libpurple/protocols/msn/msg.h --- a/libpurple/protocols/msn/msg.h Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/msn/msg.h Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/protocols/msn/slpcall.c --- a/libpurple/protocols/msn/slpcall.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/msn/slpcall.c Thu Jan 29 19:15:09 2009 +0000 @@ -239,6 +239,10 @@ msn_slpcall_session_init(slpcall); } #endif + else if (slpmsg->flags == 0x2) + { + /* Acknowledgement of previous message. Don't do anything currently. */ + } else purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%08lx\n", slpmsg->flags); diff -r 43da17b9a27e -r b018c91fb90b libpurple/protocols/msn/switchboard.c --- a/libpurple/protocols/msn/switchboard.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/msn/switchboard.c Thu Jan 29 19:15:09 2009 +0000 @@ -799,7 +799,7 @@ msn_cmdproc_process_msg(cmdproc, msg); - msn_message_destroy(msg); + msn_message_unref(msg); } static void diff -r 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/family_chat.c --- a/libpurple/protocols/oscar/family_chat.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/family_chat.c Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/family_icq.c --- a/libpurple/protocols/oscar/family_icq.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Thu Jan 29 19:15:09 2009 +0000 @@ -456,64 +456,6 @@ return 0; } -/* - * getstatusnote may be a misleading name because the response - * contains a lot of different information but currently it's only - * used to get that. - */ -int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len) -{ - FlapConnection *conn; - ByteStream bs; - aim_snacid_t snacid; - int bslen; - - purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin); - - if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) - { - purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n"); - return -EINVAL; - } - - bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin); - byte_stream_new(&bs, 4 + bslen); - - snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0); - - byte_stream_put16(&bs, 0x0001); - byte_stream_put16(&bs, bslen); - - byte_stream_putle16(&bs, bslen - 2); - byte_stream_putuid(&bs, od); - byte_stream_putle16(&bs, 0x07d0); /* I command thee. */ - byte_stream_putle16(&bs, snacid); /* eh. */ - byte_stream_putle16(&bs, 0x0fa0); /* shrug. */ - byte_stream_putle16(&bs, 58 + strlen(uin)); - - byte_stream_put32(&bs, 0x05b90002); /* don't ask */ - byte_stream_put32(&bs, 0x80000000); - byte_stream_put32(&bs, 0x00000006); - byte_stream_put32(&bs, 0x00010002); - byte_stream_put32(&bs, 0x00020000); - byte_stream_put32(&bs, 0x04e30000); - byte_stream_put32(&bs, 0x00020002); - byte_stream_put32(&bs, 0x00000001); - - byte_stream_put16(&bs, 24 + strlen(uin)); - byte_stream_put32(&bs, 0x003c0010); - byte_stream_putraw(&bs, note_hash, 16); /* status note hash */ - byte_stream_put16(&bs, 0x0032); /* buddy uin */ - byte_stream_put16(&bs, strlen(uin)); - byte_stream_putstr(&bs, uin); - - flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x000, snacid, &bs, FALSE); - - byte_stream_destroy(&bs); - - return 0; -} - static void aim_icq_freeinfo(struct aim_icq_info *info) { int i; diff -r 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/family_oservice.c --- a/libpurple/protocols/oscar/family_oservice.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/family_oservice.c Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/flap_connection.c --- a/libpurple/protocols/oscar/flap_connection.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Jan 29 19:15:09 2009 +0000 @@ -1088,59 +1088,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 @@ -1171,6 +1176,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; @@ -1430,17 +1514,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; @@ -1565,8 +1688,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) { @@ -1871,8 +2009,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); @@ -1890,9 +2042,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, @@ -1905,37 +2074,6 @@ return 1; } -static gboolean purple_requesticqstatusnote(gpointer data) -{ - PurpleConnection *gc = data; - OscarData *od = gc->proto_data; - - while (od->statusnotes_queue != NULL) - { - char *sn; - struct aim_ssi_item *ssi_item; - aim_tlv_t *note_hash; - - sn = od->statusnotes_queue->data; - - ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, - NULL, sn, AIM_SSI_TYPE_BUDDY); - if (ssi_item != NULL) - { - note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); - if (note_hash != NULL) { - aim_icq_getstatusnote(od, sn, note_hash->value, note_hash->length); - } - } - - od->statusnotes_queue = g_slist_remove(od->statusnotes_queue, sn); - g_free(sn); - } - - od->statusnotes_queue_timer = 0; - return FALSE; -} - static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { @@ -1948,6 +2086,10 @@ const char *status_id; va_list ap; aim_userinfo_t *info; + char *message = NULL; + char *itmsurl = NULL; + char *tmp; + const char *tmp2; gc = od->gc; account = purple_connection_get_account(gc); @@ -2002,49 +2144,36 @@ purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE); } - if (strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) == 0) - { - char *message = NULL; - char *itmsurl = NULL; - char *tmp; - const char *tmp2; - - if (info->status != NULL && info->status[0] != '\0') - /* Grab the available message */ - message = oscar_encoding_to_utf8(account, info->status_encoding, - info->status, info->status_len); - + if (info->status != NULL && info->status[0] != '\0') + /* Grab the available message */ + message = oscar_encoding_to_utf8(account, info->status_encoding, + info->status, info->status_len); + + tmp2 = tmp = (message ? g_markup_escape_text(message, -1) : NULL); + + if (strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) == 0) { if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len) /* Grab the iTunes Music Store URL */ itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding, - info->itmsurl, info->itmsurl_len); - - tmp2 = tmp = (message ? g_markup_escape_text(message, -1) : NULL); + info->itmsurl, info->itmsurl_len); if (tmp2 == NULL && itmsurl != NULL) + /* + * The message can't be NULL because NULL means it was the + * last attribute, so the itmsurl would get ignored below. + */ tmp2 = ""; purple_prpl_got_user_status(account, info->sn, status_id, - "message", tmp2, "itmsurl", itmsurl, NULL); - g_free(tmp); - - g_free(message); - g_free(itmsurl); + "message", tmp2, "itmsurl", itmsurl, NULL); } else - { - PurpleBuddy *b = purple_find_buddy(account, info->sn); - PurplePresence *presence = purple_buddy_get_presence(b); - PurpleStatus *old_status = purple_presence_get_active_status(presence); - PurpleStatus *new_status = purple_presence_get_status(presence, status_id); - - /* If our status_id would change with this update, pass it to the core. - * However, if our status_id would not change, do nothing, as we would clear out any existing - * attributes on the status prematurely. purple_got_infoblock() will update the message as needed. - */ - if (old_status != new_status) - purple_prpl_got_user_status(account, info->sn, status_id, NULL); - } + purple_prpl_got_user_status(account, info->sn, status_id, "message", tmp2, NULL); + + g_free(tmp); + + g_free(message); + g_free(itmsurl); /* Login time stuff */ if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) @@ -2099,51 +2228,6 @@ g_free(b16); } - /* - * If we didn't receive a status message with the status change, - * or if the message is empty, and we have a note hash, then - * query the ICQ6 status note. - * - * TODO: We should probably always query the status note regardless - * of whether they have a status message set, and we should - * figure out a way to display both the status note and the - * status message at the same time. - */ - if (info->status == NULL || info->status[0] == '\0') - { - struct aim_ssi_item *ssi_item; - aim_tlv_t *note_hash; - - ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, - NULL, info->sn, AIM_SSI_TYPE_BUDDY); - if (ssi_item != NULL) - { - note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); - if (note_hash != NULL) { - /* We do automatic rate limiting at the FLAP level, so - * a flood of requests won't disconnect us. However, - * it WOULD mean that we would have to wait a - * potentially long time to be able to message in real - * time again. Also, since we're requesting with every - * purple_parse_oncoming() call, which often come in - * groups, we should coalesce to do only one lookup per - * buddy. - */ - if (od->statusnotes_queue == NULL || - g_slist_find_custom(od->statusnotes_queue, info->sn, (GCompareFunc)strcmp) == NULL) - { - od->statusnotes_queue = g_slist_prepend(od->statusnotes_queue, - g_strdup(info->sn)); - - if (od->statusnotes_queue_timer > 0) - purple_timeout_remove(od->statusnotes_queue_timer); - od->statusnotes_queue_timer = purple_timeout_add_seconds(3, - purple_requesticqstatusnote, gc); - } - } - } - } - return 1; } @@ -5309,7 +5393,6 @@ PurpleBuddy *b; PurpleGroup *g; struct aim_ssi_item *ssi_item; - aim_tlv_t *note_hash; va_list ap; guint16 snac_subtype, type; const char *name; @@ -5379,13 +5462,7 @@ ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, gname, name, AIM_SSI_TYPE_BUDDY); - if (ssi_item != NULL) - { - note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); - if (note_hash != NULL) - aim_icq_getstatusnote(od, name, note_hash->value, note_hash->length); - } - else + if (ssi_item == NULL) { purple_debug_error("oscar", "purple_ssi_parseaddmod: " "Could not find ssi item for oncoming buddy %s, " @@ -5823,19 +5900,8 @@ else ret = g_strdup(_("Offline")); } - else if (purple_status_is_available(status) && !strcmp(id, OSCAR_STATUS_ID_AVAILABLE)) + else { - /* Available */ - message = purple_status_get_attr_string(status, "message"); - if (message != NULL) - { - ret = g_strdup(message); - purple_util_chrreplace(ret, '\n', ' '); - } - } - else if (!purple_status_is_available(status) && !strcmp(id, OSCAR_STATUS_ID_AWAY)) - { - /* Away */ message = purple_status_get_attr_string(status, "message"); if (message != NULL) { @@ -5847,13 +5913,15 @@ g_free(tmp1); g_free(tmp2); } + else if (purple_status_is_available(status)) + { + /* Don't show "Available" as status message in case buddy doesn't have a status message */ + } else { - ret = g_strdup(_("Away")); + ret = g_strdup(purple_status_get_name(status)); } } - else - ret = g_strdup(purple_status_get_name(status)); return ret; } @@ -6859,6 +6927,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 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.h Thu Jan 29 19:15:09 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; @@ -537,10 +541,6 @@ /** A linked list containing PeerConnections. */ GSList *peer_connections; - - /** Queue of ICQ Status Notes to request. */ - GSList *statusnotes_queue; - guint statusnotes_queue_timer; }; /* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */ @@ -593,6 +593,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 +618,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); @@ -1352,7 +1356,6 @@ int aim_icq_getalias(OscarData *od, const char *uin); int aim_icq_getallinfo(OscarData *od, const char *uin); int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char *alias); -int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, guint16 note_hash_len); /* 0x0017 - family_auth.c */ diff -r 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/oscar_data.c --- a/libpurple/protocols/oscar/oscar_data.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/oscar_data.c Thu Jan 29 19:15:09 2009 +0000 @@ -91,14 +91,6 @@ g_free(od->requesticon->data); od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon); } - while (od->statusnotes_queue) - { - g_free(od->statusnotes_queue->data); - od->statusnotes_queue = g_slist_delete_link(od->statusnotes_queue, - od->statusnotes_queue); - } - if (od->statusnotes_queue_timer > 0) - purple_timeout_remove(od->statusnotes_queue_timer); g_free(od->email); g_free(od->newp); g_free(od->oldp); diff -r 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/oscarcommon.h --- a/libpurple/protocols/oscar/oscarcommon.h Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/oscarcommon.h Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/protocols/oscar/peer.c --- a/libpurple/protocols/oscar/peer.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/protocols/oscar/peer.c Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b libpurple/purple-url-handler --- a/libpurple/purple-url-handler Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/purple-url-handler Thu Jan 29 19:15:09 2009 +0000 @@ -207,7 +207,11 @@ def correct_server(account): username = cpurple.PurpleAccountGetUsername(account) - return (server == (username.split("@"))[1]) + user_split = (username.split("@")) + # Not all accounts have a split, so append an empty string so the + # [1] doesn't throw an IndexError. + user_split.append("") + return (server == user_split[1]) account = findaccount(protocol, matcher=correct_server) diff -r 43da17b9a27e -r b018c91fb90b libpurple/status.c --- a/libpurple/status.c Thu Jan 22 20:39:12 2009 +0000 +++ b/libpurple/status.c Thu Jan 29 19:15:09 2009 +0000 @@ -816,28 +816,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 43da17b9a27e -r b018c91fb90b pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkaccount.c Thu Jan 29 19:15:09 2009 +0000 @@ -555,8 +555,10 @@ /* Password */ dialog->password_entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(dialog->password_entry), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->password_entry)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(dialog->password_entry), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"), dialog->password_entry); @@ -860,8 +862,10 @@ if (purple_account_option_get_masked(option)) { gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ } if (str_value != NULL) @@ -1089,8 +1093,10 @@ /* Password */ dialog->proxy_pass_entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(dialog->proxy_pass_entry), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(dialog->proxy_pass_entry), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ add_pref_box(dialog, vbox2, _("Pa_ssword:"), dialog->proxy_pass_entry); if (dialog->account != NULL && @@ -1984,9 +1990,13 @@ if (purple_account_get_bool(account, "use-global-buddyicon", TRUE)) { if (global_buddyicon != NULL) buddyicon = g_object_ref(G_OBJECT(global_buddyicon)); - /* This is for when set_account() is called for a single account */ - else - img = purple_buddy_icons_find_account_icon(account); + else { + /* This is for when set_account() is called for a single account */ + const char *path; + path = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon"); + if (path != NULL) + img = purple_imgstore_new_from_file(path); + } } else { img = purple_buddy_icons_find_account_icon(account); } diff -r 43da17b9a27e -r b018c91fb90b pidgin/gtkblist.c --- a/pidgin/gtkblist.c Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkblist.c Thu Jan 29 19:15:09 2009 +0000 @@ -955,8 +955,10 @@ if (pce->secret) { gtk_entry_set_visibility(GTK_ENTRY(input), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ } pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL); g_signal_connect(G_OBJECT(input), "changed", @@ -6826,8 +6828,10 @@ if (pce->secret) { gtk_entry_set_visibility(GTK_ENTRY(input), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(input)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(input), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ } pidgin_add_widget_to_vbox(GTK_BOX(data->entries_box), pce->label, data->sg, input, TRUE, NULL); g_signal_connect(G_OBJECT(input), "changed", diff -r 43da17b9a27e -r b018c91fb90b pidgin/gtkgaim-compat.h --- a/pidgin/gtkgaim-compat.h Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkgaim-compat.h Thu Jan 29 19:15:09 2009 +0000 @@ -297,7 +297,9 @@ #define GAIM_HIG_BORDER PIDGIN_HIG_BORDER #define GAIM_HIG_BOX_SPACE PIDGIN_HIG_BOX_SPACE #define GAIM_HIG_CAT_SPACE PIDGIN_HIG_CAT_SPACE +#if !GTK_CHECK_VERSION(2,16,0) #define GAIM_INVISIBLE_CHAR PIDGIN_INVISIBLE_CHAR +#endif /* Less than GTK+ 2.16 */ #define GAIM_IS_GTK_CONVERSATION PIDGIN_IS_PIDGIN_CONVERSATION #define GAIM_IS_GTK_PLUGIN PIDGIN_IS_PIDGIN_PLUGIN #define gaim_new_check_item pidgin_new_check_item diff -r 43da17b9a27e -r b018c91fb90b pidgin/gtkimhtml.c --- a/pidgin/gtkimhtml.c Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkimhtml.c Thu Jan 29 19:15:09 2009 +0000 @@ -5378,12 +5378,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 43da17b9a27e -r b018c91fb90b pidgin/gtkmain.c --- a/pidgin/gtkmain.c Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkmain.c Thu Jan 29 19:15:09 2009 +0000 @@ -784,8 +784,8 @@ dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); #endif - purple_debug_info("main", "exiting because another libpurple client is already running\n"); purple_core_quit(); + g_printerr(_("Exiting because another libpurple client is already running.\n")); #ifdef HAVE_SIGNAL_H g_free(segfault_message); #endif diff -r 43da17b9a27e -r b018c91fb90b pidgin/gtkpluginpref.c --- a/pidgin/gtkpluginpref.c Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkpluginpref.c Thu Jan 29 19:15:09 2009 +0000 @@ -101,8 +101,10 @@ if (purple_plugin_pref_get_masked(pref)) { gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ } g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(entry_cb), diff -r 43da17b9a27e -r b018c91fb90b pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkprefs.c Thu Jan 29 19:15:09 2009 +0000 @@ -1412,8 +1412,10 @@ gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry); gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 1, 2, GTK_FILL , 0, 0, 0); gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(proxy_print_option), (void *)PROXYPASS); diff -r 43da17b9a27e -r b018c91fb90b pidgin/gtkrequest.c --- a/pidgin/gtkrequest.c Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/gtkrequest.c Thu Jan 29 19:15:09 2009 +0000 @@ -430,8 +430,10 @@ if (masked) { gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(entry)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(entry), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ } } gtk_widget_show_all(vbox); @@ -791,8 +793,10 @@ if (purple_request_field_string_is_masked(field)) { gtk_entry_set_visibility(GTK_ENTRY(widget), FALSE); +#if !GTK_CHECK_VERSION(2,16,0) if (gtk_entry_get_invisible_char(GTK_ENTRY(widget)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(widget), PIDGIN_INVISIBLE_CHAR); +#endif /* Less than GTK+ 2.16 */ } gtk_editable_set_editable(GTK_EDITABLE(widget), diff -r 43da17b9a27e -r b018c91fb90b pidgin/pidgin.h --- a/pidgin/pidgin.h Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/pidgin.h Thu Jan 29 19:15:09 2009 +0000 @@ -91,11 +91,22 @@ #define PIDGIN_HIG_BORDER 12 #define PIDGIN_HIG_BOX_SPACE 6 +#if !GTK_CHECK_VERSION(2,16,0) || !defined(PIDGIN_DISABLE_DEPRECATED) /* - * See GNOME bug #307304 for some discussion about the invisible - * character. 0x25cf is a good choice, too. + * Older versions of GNOME defaulted to using an asterisk as the invisible + * character. But this is ugly and we want to use something nicer. + * + * The default invisible character was changed in GNOME revision 21446 + * (GTK+ 2.16) from an asterisk to the first available character out of + * 0x25cf, 0x2022, 0x2731, 0x273a. See GNOME bugs 83935 and 307304 for + * discussion leading up to the change. + * + * Here's the change: + * http://svn.gnome.org/viewvc/gtk%2B?view=revision&revision=21446 + * */ -#define PIDGIN_INVISIBLE_CHAR (gunichar)0x2022 +#define PIDGIN_INVISIBLE_CHAR (gunichar)0x25cf +#endif /* Less than GTK+ 2.16 */ #endif /* _PIDGIN_H_ */ diff -r 43da17b9a27e -r b018c91fb90b pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Thu Jan 22 20:39:12 2009 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b po/fi.po --- a/po/fi.po Thu Jan 22 20:39:12 2009 +0000 +++ b/po/fi.po Thu Jan 29 19:15:09 2009 +0000 @@ -11,7 +11,7 @@ "Project-Id-Version: Pidgin\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-01-07 13:30+0200\n" -"PO-Revision-Date: 2009-01-07 13:30+0200\n" +"PO-Revision-Date: 2009-01-23 19:58+0200\n" "Last-Translator: Timo Jyrinki \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -1169,7 +1169,7 @@ msgstr "Keskustelut" msgid "Logging" -msgstr "Kirjataan lokiin" +msgstr "Lokiinkirjaus" msgid "You must fill all the required fields." msgstr "Täytä kaikki vaaditut kentät." @@ -15466,7 +15466,7 @@ #~ "keskustelun uuden keskustelun aluksi.\n" #~ "\n" #~ "Historia-liitännäinen vaatii lokiinkirjauksen käyttöä. Loki voidaan ottaa " -#~ "käyttöön menemällä Työkalut -> Asetukset -> Kirjataan lokiin. Lokien " +#~ "käyttöön menemällä Työkalut -> Asetukset -> Lokiinkirjaus. Lokien " #~ "käyttöönotto pikaviesteille ja/tai ryhmäkeskusteluille ottaa käyttöön " #~ "historiatoiminnon vastaaville keskustelutyypeille." diff -r 43da17b9a27e -r b018c91fb90b share/ca-certs/Makefile.am --- a/share/ca-certs/Makefile.am Thu Jan 22 20:39:12 2009 +0000 +++ b/share/ca-certs/Makefile.am Thu Jan 29 19:15:09 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 43da17b9a27e -r b018c91fb90b 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 Thu Jan 29 19:15:09 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-----