Mercurial > pidgin
changeset 29272:4561aa5b368c
propagate from branch 'im.pidgin.pidgin' (head 3da4a61feea56ff40dc5fdba355a1057c675a32d)
to branch 'im.pidgin.cpw.attention_ui' (head 47f585845529ec2f856e92f34a2ef86470c8e967)
author | Marcus Lundblad <ml@update.uu.se> |
---|---|
date | Fri, 06 Feb 2009 23:27:49 +0000 |
parents | 8f5a63aebdb6 (diff) 385473abc18b (current diff) |
children | 4460eab56456 |
files | libpurple/protocols/jabber/jabber.c libpurple/protocols/msn/msn.c |
diffstat | 83 files changed, 1366 insertions(+), 882 deletions(-) [+] |
line wrap: on
line diff
--- a/AUTHORS Fri Feb 06 23:27:45 2009 +0000 +++ b/AUTHORS Fri Feb 06 23:27:49 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
--- a/COPYRIGHT Fri Feb 06 23:27:45 2009 +0000 +++ b/COPYRIGHT Fri Feb 06 23:27:49 2009 +0000 @@ -63,6 +63,7 @@ Damien Carbery Michael Carlson Keegan Carruthers-Smith +Ludovico Cavedon Steve Cavilia Julien Cegarra Cerulean Studios, LLC @@ -210,6 +211,7 @@ Jaromír Karmazín John Kelm Jochen Kemnade +Yann Kerherve Akuke Kok Kir Kolyshkin Konstantin Korikov @@ -229,6 +231,7 @@ Steve Láposi Daniel Larsson Peter Lawler +Vadim Lebedev Ho-seok Lee Jean-Yves Lefort Moses Lei @@ -311,6 +314,7 @@ Ted Percival Eduardo Pérez Matt Perry +Luke Petre Diego Petten Nathan Peterson Sebastián E. Peyrott @@ -441,6 +445,7 @@ Todd Troxell Brad Turcotte Kyle Turman +Jon Turney Junichi Uekawa Igor Vlasenko István Váradi @@ -477,6 +482,7 @@ Dan Winship Michal Witkowski Scott Wolchok +Rogier Wolff The Written Word, Inc. Kevin Wu Won Pui Lam Wong
--- a/ChangeLog Fri Feb 06 23:27:45 2009 +0000 +++ b/ChangeLog Fri Feb 06 23:27:49 2009 +0000 @@ -1,5 +1,35 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.5.5 (??/??/????): + libpurple: + * Fix a crash when removing an account with an unknown protocol id. + * 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 a memory leak in SILC. (Luke Petre) + + ICQ: + * Fix retrieval of status messages from users of ICQ 6.x, Miranda, and + other libpurple clients. (Daniel Ljungborg) + * Change client ID to match ICQ Basic 14.34.3096. This fixes publishing + of buddy icons and available messages. + * Properly publish status messages for statuses other than Available. + ICQ 6.x users can now see these status messages. (Daniel Ljungborg) + + MSN: + * Fix transfer of buddy icons, custom smileys, and files from the + latest Windows Live Messenger 9 official client. (Thomas + Gibson-Robinson) + * Large (multi-part) messages are now correctly re-combined. + * Federated/Yahoo! buddies should now stop creating sync issues at + every signin. You may need to remove duplicates in the Address + Book. See the FAQ for more information. + * Messages from Yahoo! buddies are no longer silently dropped. + + Finch: + * Allow rebinding keys to change the focused widget (details in the + man-page, look for GntBox::binding) + version 2.5.4 (01/12/2009): libpurple: * Fix a connection timeout with empty Gadu-Gady buddy lists. (Martin
--- a/ChangeLog.API Fri Feb 06 23:27:45 2009 +0000 +++ b/ChangeLog.API Fri Feb 06 23:27:49 2009 +0000 @@ -1,5 +1,14 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.5.5 (??/??/2009): + libpurple: + Changed: + * purple_status_type_new now defaults "saveable" to TRUE. + This was necessary in order to maintain the current behavior + while fixing non-saveable statuses not to be saved. + * xmlnode_get_prefix, xmlnode_to_str and xmlnode_to_formatted_str + now all take a const xmlnode* instead of an xmlnode* + version 2.5.4 (01/12/2009): perl: Changed:
--- a/ChangeLog.win32 Fri Feb 06 23:27:45 2009 +0000 +++ b/ChangeLog.win32 Fri Feb 06 23:27:49 2009 +0000 @@ -1,3 +1,8 @@ +version 2.5.5 (??/??/2009): + * Remove the "Flash window when chat messages are received" pref from + the Windows Pidgin Options plugin - the Message Notification plugin + does this (and much more). + version 2.5.4 (01/12/2009): * Fix the "Hang on Exit" issue that a number of users encountered. * Updated GTK+ to 2.14.6
--- a/doc/finch.1.in Fri Feb 06 23:27:45 2009 +0000 +++ b/doc/finch.1.in Fri Feb 06 23:27:49 2009 +0000 @@ -296,6 +296,15 @@ \fI~/.gntrc\fR correspond to the default keybindings for the actions: .br +[GntBox::binding] +.br +tab = focus-next +.br +right = focus-next +.br +left = focus-prev + +.br [GntEntry::binding] .br c-a = cursor-home @@ -534,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
--- a/doc/pidgin.1.in Fri Feb 06 23:27:45 2009 +0000 +++ b/doc/pidgin.1.in Fri Feb 06 23:27:49 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
--- a/finch/finch.c Fri Feb 06 23:27:45 2009 +0000 +++ b/finch/finch.c Fri Feb 06 23:27:49 2009 +0000 @@ -210,7 +210,7 @@ text = g_strdup_printf(_("%s\n" "Usage: %s [OPTION]...\n\n" " -c, --config=DIR use DIR for config files\n" - " -d, --debug print debugging messages to stdout\n" + " -d, --debug print debugging messages to stderr\n" " -h, --help display this help and exit\n" " -n, --nologin don't automatically login\n" " -v, --version display the current version and exit\n"), DISPLAY_VERSION, name);
--- a/finch/libgnt/gntbox.c Fri Feb 06 23:27:45 2009 +0000 +++ b/finch/libgnt/gntbox.c Fri Feb 06 23:27:49 2009 +0000 @@ -21,6 +21,7 @@ */ #include "gntbox.h" +#include "gntstyle.h" #include "gntutils.h" #include <string.h> @@ -304,38 +305,38 @@ gnt_box_key_pressed(GntWidget *widget, const char *text) { GntBox *box = GNT_BOX(widget); - GntWidget *now; + gboolean ret; + + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_DISABLE_ACTIONS)) + return FALSE; if (box->active == NULL && !find_focusable_widget(box)) return FALSE; if (gnt_widget_key_pressed(box->active, text)) return TRUE; - + + /* This dance is necessary to make sure that the child widgets get a chance + to trigger their bindings first */ + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_DISABLE_ACTIONS); + ret = gnt_widget_key_pressed(widget, text); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_DISABLE_ACTIONS); + return ret; +} + +static gboolean +box_focus_change(GntBox *box, gboolean next) +{ + GntWidget *now; now = box->active; - if (text[0] == 27) - { - if (strcmp(text, GNT_KEY_LEFT) == 0) - { - find_prev_focus(box); - } - else if (strcmp(text, GNT_KEY_RIGHT) == 0) - { - find_next_focus(box); - } - else if (strcmp(text, GNT_KEY_BACK_TAB) == 0) - { - find_prev_focus(box); - } - } - else if (text[0] == '\t') - { + if (next) { find_next_focus(box); + } else { + find_prev_focus(box); } - if (now && now != box->active) - { + if (now && now != box->active) { gnt_widget_set_focus(now, FALSE); gnt_widget_set_focus(box->active, TRUE); return TRUE; @@ -344,6 +345,18 @@ return FALSE; } +static gboolean +action_focus_next(GntBindable *bindable, GList *null) +{ + return box_focus_change(GNT_BOX(bindable), TRUE); +} + +static gboolean +action_focus_prev(GntBindable *bindable, GList *null) +{ + return box_focus_change(GNT_BOX(bindable), FALSE); +} + static void gnt_box_lost_focus(GntWidget *widget) { @@ -556,6 +569,7 @@ static void gnt_box_class_init(GntBoxClass *klass) { + GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); GObjectClass *gclass = G_OBJECT_CLASS(klass); parent_class = GNT_WIDGET_CLASS(klass); parent_class->destroy = gnt_box_destroy; @@ -589,6 +603,15 @@ G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB ) ); + + gnt_bindable_class_register_action(bindable, "focus-next", action_focus_next, + "\t", NULL); + gnt_bindable_register_binding(bindable, "focus-next", GNT_KEY_RIGHT, NULL); + gnt_bindable_class_register_action(bindable, "focus-prev", action_focus_prev, + GNT_KEY_BACK_TAB, NULL); + gnt_bindable_register_binding(bindable, "focus-prev", GNT_KEY_LEFT, NULL); + + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable); } static void @@ -599,7 +622,7 @@ /* Initially make both the height and width resizable. * Update the flags as necessary when widgets are added to it. */ GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y); - GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_DISABLE_ACTIONS); GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); box->pad = 1; box->fill = TRUE;
--- a/finch/libgnt/gntcolors.c Fri Feb 06 23:27:45 2009 +0000 +++ b/finch/libgnt/gntcolors.c Fri Feb 06 23:27:49 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);
--- a/finch/libgnt/gntkeys.c Fri Feb 06 23:27:45 2009 +0000 +++ b/finch/libgnt/gntkeys.c Fri Feb 06 23:27:49 2009 +0000 @@ -163,7 +163,8 @@ (*(text + 2) >= 'A' && *(text + 2) <= 'D')) { /* Apparently this is necessary for urxvt and screen and xterm */ if (strstr(term, "screen") == term || strcmp(term, "rxvt-unicode") == 0 || - strstr(term, "xterm") == term) + strstr(term, "xterm") == term || + strstr(term, "vt100") == term) *(text + 1) = 'O'; } else if (*(unsigned char*)text == 195) { if (*(text + 2) == 0 && strstr(term, "xterm") == term) {
--- a/libpurple/account.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/account.c Fri Feb 06 23:27:49 2009 +0000 @@ -257,15 +257,20 @@ statuses_to_xmlnode(const PurplePresence *presence) { xmlnode *node, *child; - GList *statuses, *status; + GList *statuses; + PurpleStatus *status; node = xmlnode_new("statuses"); statuses = purple_presence_get_statuses(presence); - for (status = statuses; status != NULL; status = status->next) + for (; statuses != NULL; statuses = statuses->next) { - child = status_to_xmlnode((PurpleStatus *)status->data); - xmlnode_insert_child(node, child); + status = statuses->data; + if (purple_status_type_is_saveable(purple_status_get_type(status))) + { + child = status_to_xmlnode(status); + xmlnode_insert_child(node, child); + } } return node;
--- a/libpurple/blist.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/blist.c Fri Feb 06 23:27:49 2009 +0000 @@ -1811,7 +1811,7 @@ PurpleGroup *group; struct _purple_hbuddy hb; PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info; + PurplePluginProtocolInfo *prpl_info = NULL; g_return_if_fail(buddy != NULL); @@ -1872,7 +1872,8 @@ * can free proto_data */ prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account)); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + if (prpl) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); if (prpl_info && prpl_info->buddy_free) prpl_info->buddy_free(buddy);
--- a/libpurple/certificate.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/certificate.c Fri Feb 06 23:27:49 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
--- a/libpurple/connection.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/connection.c Fri Feb 06 23:27:49 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"); }
--- a/libpurple/core.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/core.c Fri Feb 06 23:27:49 2009 +0000 @@ -137,7 +137,7 @@ * subsystem right away too. */ purple_plugins_init(); - + /* Initialize all static protocols. */ static_proto_init(); @@ -198,22 +198,40 @@ /* 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(); + + /* Unload all plugins before the UI because UI plugins might call + * UI-specific functions */ + purple_debug_info("main", "Unloading all plugins\n"); + purple_plugins_destroy_all(); + + /* Shut down the UI before all the subsystems */ + ops = purple_core_get_ui_ops(); + if (ops != NULL && ops->quit != NULL) + ops->quit(); + /* Save .xml files, remove signals, etc. */ purple_smileys_uninit(); purple_idle_uninit(); - purple_ssl_uninit(); purple_pounces_uninit(); purple_blist_uninit(); purple_ciphers_uninit(); purple_notify_uninit(); purple_conversations_uninit(); purple_connections_uninit(); - purple_certificate_uninit(); purple_buddy_icons_uninit(); purple_accounts_uninit(); purple_savedstatuses_uninit(); purple_status_uninit(); - purple_prefs_uninit(); purple_sound_uninit(); purple_xfers_uninit(); purple_proxy_uninit(); @@ -221,19 +239,15 @@ purple_imgstore_uninit(); purple_network_uninit(); - purple_debug_info("main", "Unloading all plugins\n"); - purple_plugins_destroy_all(); - - ops = purple_core_get_ui_ops(); - if (ops != NULL && ops->quit != NULL) - ops->quit(); - + /* Everything after this must not try to read any prefs */ + purple_prefs_uninit(); purple_plugins_uninit(); #ifdef HAVE_DBUS purple_dbus_uninit(); #endif purple_cmds_uninit(); + /* Everything after this cannot try to write things to the confdir */ purple_util_uninit(); purple_signals_uninit();
--- a/libpurple/dbus-analyze-functions.py Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/dbus-analyze-functions.py Fri Feb 06 23:27:49 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, "&len")[param.name == "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*$";
--- a/libpurple/network.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/network.c Fri Feb 06 23:27:49 2009 +0000 @@ -74,8 +74,12 @@ /* Mutex for the other global vars */ static GStaticMutex mutex = G_STATIC_MUTEX_INIT; -static gboolean network_initialized; -static HANDLE network_change_handle; +static gboolean network_initialized = FALSE; +static HANDLE network_change_handle = NULL; +static int (WSAAPI *MyWSANSPIoctl) ( + HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, + DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, + LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL; #endif struct _PurpleNetworkListenData { @@ -538,27 +542,28 @@ return FALSE; } +static gboolean _print_debug_msg(gpointer data) { + gchar *msg = data; + purple_debug_warning("network", msg); + g_free(msg); + return FALSE; +} + static gpointer wpurple_network_change_thread(gpointer data) { WSAQUERYSET qs; WSAEVENT *nla_event; - time_t last_trigger = time(NULL); - - int (WSAAPI *MyWSANSPIoctl) ( - HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer, - DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer, - LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL; - - if (!(MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) { - g_thread_exit(NULL); - return NULL; - } + time_t last_trigger = time(NULL) - 31; + char buf[4096]; + WSAQUERYSET *res = (LPWSAQUERYSET) buf; + DWORD size; if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) { int errorid = WSAGetLastError(); gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Couldn't create WSA event. " - "Message: %s (%d).\n", msg, errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Couldn't create WSA event. " + "Message: %s (%d).\n", msg, errorid)); g_free(msg); g_thread_exit(NULL); return NULL; @@ -579,30 +584,26 @@ return NULL; } - memset(&qs, 0, sizeof(WSAQUERYSET)); - qs.dwSize = sizeof(WSAQUERYSET); - qs.dwNameSpace = NS_NLA; - if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) { - int errorid = WSAGetLastError(); - gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. " - "NLA service is probably not running. Message: %s (%d).\n", - msg, errorid); - g_free(msg); - WSACloseEvent(nla_event); - g_static_mutex_unlock(&mutex); - g_thread_exit(NULL); - return NULL; + if (network_change_handle == NULL) { + memset(&qs, 0, sizeof(WSAQUERYSET)); + qs.dwSize = sizeof(WSAQUERYSET); + qs.dwNameSpace = NS_NLA; + if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) { + int errorid = WSAGetLastError(); + gchar *msg = g_win32_error_message(errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Couldn't retrieve NLA SP lookup handle. " + "NLA service is probably not running. Message: %s (%d).\n", + msg, errorid)); + g_free(msg); + WSACloseEvent(nla_event); + g_static_mutex_unlock(&mutex); + g_thread_exit(NULL); + return NULL; + } } g_static_mutex_unlock(&mutex); - /* Make sure at least 30 seconds have elapsed since the last - * notification so we don't peg the cpu if this keeps changing. */ - if ((time(NULL) - last_trigger) < 30) - Sleep(30000); - - last_trigger = time(NULL); - memset(&completion, 0, sizeof(WSACOMPLETION)); completion.Type = NSP_NOTIFY_EVENT; overlapped.hEvent = nla_event; @@ -610,18 +611,34 @@ if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) { int errorid = WSAGetLastError(); + if (errorid == WSA_INVALID_HANDLE) { + purple_timeout_add(0, _print_debug_msg, + g_strdup("Invalid NLA handle; resetting.\n")); + g_static_mutex_lock(&mutex); + retval = WSALookupServiceEnd(network_change_handle); + network_change_handle = NULL; + g_static_mutex_unlock(&mutex); + continue; /* WSA_IO_PENDING indicates successful async notification will happen */ - if (errorid != WSA_IO_PENDING) { + } else if (errorid != WSA_IO_PENDING) { gchar *msg = g_win32_error_message(errorid); - purple_debug_warning("network", "Unable to wait for changes. Message: %s (%d).\n", - msg, errorid); + purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("Unable to wait for changes. Message: %s (%d).\n", + msg, errorid)); g_free(msg); } } + /* Make sure at least 30 seconds have elapsed since the last + * notification so we don't peg the cpu if this keeps changing. */ + if ((time(NULL) - last_trigger) < 30) + Sleep(30000); + /* This will block until NLA notifies us */ retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE); + last_trigger = time(NULL); + g_static_mutex_lock(&mutex); if (network_initialized == FALSE) { /* Time to die */ @@ -631,8 +648,14 @@ return NULL; } - retval = WSALookupServiceEnd(network_change_handle); - network_change_handle = NULL; + size = sizeof(buf); + while ((retval = WSALookupServiceNext(network_change_handle, 0, &size, res)) == ERROR_SUCCESS) { + /*purple_timeout_add(0, _print_debug_msg, + g_strdup_printf("thread found network '%s'\n", + res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/ + size = sizeof(buf); + } + WSAResetEvent(nla_event); g_static_mutex_unlock(&mutex); @@ -768,11 +791,12 @@ if (cnt < 0) /* Assume there is a network */ current_network_count = 1; /* Don't listen for network changes if we can't tell anyway */ - else - { + else { current_network_count = cnt; - if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err)) - purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : ""); + if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) { + if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err)) + purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : ""); + } } #endif @@ -848,10 +872,12 @@ msg, errorid); g_free(msg); } + network_change_handle = NULL; + } g_static_mutex_unlock(&mutex); #endif purple_signal_unregister(purple_network_get_handle(), - "network-configuration-changed"); + "network-configuration-changed"); }
--- a/libpurple/prefs.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/prefs.c Fri Feb 06 23:27:49 2009 +0000 @@ -693,12 +693,15 @@ char *name; GSList *l; - if(!pref || pref == &prefs) + if(!pref) return; while(pref->first_child) remove_pref(pref->first_child); + if(pref == &prefs) + return; + if(pref->parent->first_child == pref) { pref->parent->first_child = pref->sibling; } else { @@ -711,7 +714,8 @@ name = pref_full_name(pref); - purple_debug_info("prefs", "removing pref %s\n", name); + if (prefs_loaded) + purple_debug_info("prefs", "removing pref %s\n", name); g_hash_table_remove(prefs_hash, name); g_free(name); @@ -1451,6 +1455,9 @@ sync_prefs(); } + prefs_loaded = FALSE; + purple_prefs_destroy(); + g_hash_table_destroy(prefs_hash); + prefs_hash = NULL; - prefs_loaded = FALSE; }
--- a/libpurple/protocols/gg/gg.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/gg/gg.c Fri Feb 06 23:27:49 2009 +0000 @@ -755,18 +755,29 @@ /* ----- CONFERENCES ---------------------------------------------------- */ -static void ggp_callback_add_to_chat_ok(PurpleConnection *gc, PurpleRequestFields *fields) +static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields) { - GGPInfo *info = gc->proto_data; + GGPInfo *info; + PurpleConnection *conn; PurpleRequestField *field; - /* TODO: sel may be null. */ GList *sel; + conn = purple_account_get_connection(purple_buddy_get_account(buddy)); + + g_return_if_fail(conn != NULL); + + info = conn->proto_data; + field = purple_request_fields_get_field(fields, "name"); sel = purple_request_field_list_get_selected(field); - ggp_confer_participants_add_uin(gc, sel->data, info->tmp_buddy); - info->tmp_buddy = 0; + if (sel == NULL) { + purple_debug_error("gg", "No chat selected\n"); + return; + } + + ggp_confer_participants_add_uin(conn, sel->data, + ggp_str_to_uin(purple_buddy_get_name(buddy))); } static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored) @@ -786,9 +797,6 @@ gc = purple_account_get_connection(purple_buddy_get_account(buddy)); info = gc->proto_data; - /* TODO: It tmp_buddy != 0 then stop! */ - info->tmp_buddy = ggp_str_to_uin(purple_buddy_get_name(buddy)); - fields = purple_request_fields_new(); group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); @@ -796,8 +804,7 @@ field = purple_request_field_list_new("name", "Chat name"); for (l = info->chats; l != NULL; l = l->next) { GGPChat *chat = l->data; - purple_request_field_list_add(field, g_strdup(chat->name), - g_strdup(chat->name)); + purple_request_field_list_add(field, chat->name, chat->name); } purple_request_field_group_add_field(group, field); @@ -810,8 +817,8 @@ fields, _("Add"), G_CALLBACK(ggp_callback_add_to_chat_ok), _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); + purple_connection_get_account(gc), NULL, NULL, + buddy); g_free(msg); } @@ -1699,14 +1706,20 @@ { PurpleMenuAction *act; GList *m = NULL; + PurpleAccount *account; + GGPInfo *info; if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) return NULL; - act = purple_menu_action_new(_("Add to chat"), - PURPLE_CALLBACK(ggp_bmenu_add_to_chat), - NULL, NULL); - m = g_list_append(m, act); + account = purple_buddy_get_account((PurpleBuddy *) node); + info = purple_account_get_connection(account)->proto_data; + if (info->chats) { + act = purple_menu_action_new(_("Add to chat"), + PURPLE_CALLBACK(ggp_bmenu_add_to_chat), + NULL, NULL); + m = g_list_append(m, act); + } /* Using a blist node boolean here is also wrong. * Once the Block and Unblock actions are added to the core,
--- a/libpurple/protocols/gg/gg.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/gg/gg.h Fri Feb 06 23:27:49 2009 +0000 @@ -61,10 +61,7 @@ GGPToken *token; GList *chats; GGPSearches *searches; - - uin_t tmp_buddy; int chats_count; - GList *pending_richtext_messages; GHashTable *pending_images; } GGPInfo;
--- a/libpurple/protocols/jabber/auth.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/auth.c Fri Feb 06 23:27:49 2009 +0000 @@ -749,8 +749,8 @@ val_end = cur; while (val_end != val_start && (*val_end == ' ' || *val_end == ',' || *val_end == '\t' - || *val_end == '\r' || *val_start == '\n' - || *val_end == '"')) + || *val_end == '\r' || *val_end == '\n' + || *val_end == '"' || *val_end == '\0')) val_end--; if (val_start != val_end)
--- a/libpurple/protocols/jabber/buddy.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/buddy.c Fri Feb 06 23:27:49 2009 +0000 @@ -2518,7 +2518,7 @@ JabberBuddyResource *jbr = jabber_buddy_find_resource((JabberBuddy*)jb, NULL); if (!jbr) { - purple_debug_error("jabber", + purple_debug_info("jabber", "Unable to find caps: buddy might be offline\n"); return FALSE; }
--- a/libpurple/protocols/jabber/data.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/data.c Fri Feb 06 23:27:49 2009 +0000 @@ -200,11 +200,9 @@ } void -jabber_data_parse(JabberStream *js, xmlnode *packet) +jabber_data_parse(JabberStream *js, xmlnode *data_node, const char *who, const char *id) { JabberIq *result = NULL; - const char *who = xmlnode_get_attrib(packet, "from"); - xmlnode *data_node = xmlnode_get_child(packet, "data"); const JabberData *data = jabber_data_find_local_by_cid(xmlnode_get_attrib(data_node, "cid")); @@ -213,12 +211,12 @@ result = jabber_iq_new(js, JABBER_IQ_ERROR); xmlnode_set_attrib(result->node, "to", who); - xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "id", id); xmlnode_insert_child(result->node, item_not_found); } else { result = jabber_iq_new(js, JABBER_IQ_RESULT); xmlnode_set_attrib(result->node, "to", who); - xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "id", id); xmlnode_insert_child(result->node, jabber_data_get_xml_definition(data)); }
--- a/libpurple/protocols/jabber/data.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/data.h Fri Feb 06 23:27:49 2009 +0000 @@ -65,7 +65,8 @@ void jabber_data_associate_remote(JabberData *data); /* handles iq requests */ -void jabber_data_parse(JabberStream *js, xmlnode *packet); +void jabber_data_parse(JabberStream *js, xmlnode *data_node, + const char *who, const char *id); void jabber_data_init(void); void jabber_data_uninit(void);
--- a/libpurple/protocols/jabber/google.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/google.c Fri Feb 06 23:27:49 2009 +0000 @@ -144,9 +144,8 @@ } void -jabber_gmail_poke(JabberStream *js, xmlnode *packet) +jabber_gmail_poke(JabberStream *js, const char *type) { - const char *type; xmlnode *query; JabberIq *iq; @@ -154,11 +153,8 @@ if (!purple_account_get_check_mail(js->gc->account)) return; - type = xmlnode_get_attrib(packet, "type"); - - /* Is this an initial incoming mail notification? If so, send a request for more info */ - if (strcmp(type, "set") || !xmlnode_get_child(packet, "new-mail")) + if (strcmp(type, "set")) return; purple_debug(PURPLE_DEBUG_MISC, "jabber",
--- a/libpurple/protocols/jabber/google.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/google.h Fri Feb 06 23:27:49 2009 +0000 @@ -27,7 +27,7 @@ #include "jabber.h" void jabber_gmail_init(JabberStream *js); -void jabber_gmail_poke(JabberStream *js, xmlnode *node); +void jabber_gmail_poke(JabberStream *js, const char *type); void jabber_google_roster_init(JabberStream *js); void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item);
--- a/libpurple/protocols/jabber/iq.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/iq.c Fri Feb 06 23:27:49 2009 +0000 @@ -309,7 +309,7 @@ void jabber_iq_parse(JabberStream *js, xmlnode *packet) { JabberCallbackData *jcd; - xmlnode *query, *error, *x; + xmlnode *child, *query, *error, *x; const char *xmlns; const char *type, *id, *from; JabberIqHandler *jih; @@ -371,25 +371,25 @@ } } - if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) { - jabber_si_parse(js, packet); + if ((child = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { + jabber_si_parse(js, child, from, id); return; } - if(xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) { - jabber_gmail_poke(js, packet); + if (xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) { + jabber_gmail_poke(js, type); return; } purple_debug_info("jabber", "jabber_iq_parse\n"); - if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) { - jabber_ping_parse(js, packet); + if (xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) { + jabber_ping_parse(js, from, id); return; } - if (xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE)) { - jabber_data_parse(js, packet); + if ((child = xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE))) { + jabber_data_parse(js, child, from, id); return; }
--- a/libpurple/protocols/jabber/jabber.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Fri Feb 06 23:27:49 2009 +0000 @@ -1866,7 +1866,7 @@ type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), - NULL, FALSE, TRUE, FALSE, + NULL, TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, type);
--- a/libpurple/protocols/jabber/ping.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/ping.c Fri Feb 06 23:27:49 2009 +0000 @@ -30,7 +30,7 @@ #include "iq.h" void -jabber_ping_parse(JabberStream *js, xmlnode *packet) +jabber_ping_parse(JabberStream *js, const char *from, const char *id) { JabberIq *iq; @@ -38,9 +38,9 @@ iq = jabber_iq_new(js, JABBER_IQ_RESULT); - xmlnode_set_attrib(iq->node, "to", xmlnode_get_attrib(packet, "from") ); + xmlnode_set_attrib(iq->node, "to", from); - jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id")); + jabber_iq_set_id(iq, id); jabber_iq_send(iq); }
--- a/libpurple/protocols/jabber/ping.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/ping.h Fri Feb 06 23:27:49 2009 +0000 @@ -26,7 +26,8 @@ #include "conversation.h" void jabber_ping_parse(JabberStream *js, - xmlnode *packet); + const char *from, + const char *id); gboolean jabber_ping_jid(PurpleConversation *conv, const char *jid);
--- a/libpurple/protocols/jabber/si.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/si.c Fri Feb 06 23:27:49 2009 +0000 @@ -1182,17 +1182,14 @@ purple_xfer_request(xfer); } -void jabber_si_parse(JabberStream *js, xmlnode *packet) +void jabber_si_parse(JabberStream *js, xmlnode *si, const char *from, const char *id) { JabberSIXfer *jsx; PurpleXfer *xfer; - xmlnode *si, *file, *feature, *x, *field, *option, *value; - const char *stream_id, *filename, *filesize_c, *profile, *from; + xmlnode *file, *feature, *x, *field, *option, *value; + const char *stream_id, *filename, *filesize_c, *profile; size_t filesize = 0; - if(!(si = xmlnode_get_child(packet, "si"))) - return; - if(!(profile = xmlnode_get_attrib(si, "profile")) || strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) return; @@ -1215,7 +1212,7 @@ if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) return; - if(!(from = xmlnode_get_attrib(packet, "from"))) + if(!from) return; /* if they've already sent us this file transfer with the same damn id @@ -1256,7 +1253,7 @@ jsx->js = js; jsx->stream_id = g_strdup(stream_id); - jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); + jsx->iq_id = g_strdup(id); xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from); if (xfer)
--- a/libpurple/protocols/jabber/si.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/jabber/si.h Fri Feb 06 23:27:49 2009 +0000 @@ -27,7 +27,7 @@ #include "jabber.h" void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet); -void jabber_si_parse(JabberStream *js, xmlnode *packet); +void jabber_si_parse(JabberStream *js, xmlnode *si, const char *from, const char *id); PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who); void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);
--- a/libpurple/protocols/msn/cmdproc.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Fri Feb 06 23:27:49 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
--- a/libpurple/protocols/msn/cmdproc.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/cmdproc.h Fri Feb 06 23:27:49 2009 +0000 @@ -46,6 +46,8 @@ MsnHistory *history; + GHashTable *multiparts; /**< Multi-part message ID's */ + void *data; /**< Extra data, like the switchboard. */ };
--- a/libpurple/protocols/msn/contact.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/contact.c Fri Feb 06 23:27:49 2009 +0000 @@ -158,6 +158,113 @@ state->action |= action; } +/*************************************************************** + * General SOAP handling + ***************************************************************/ + +static const char * +msn_contact_operation_str(MsnCallbackAction action) +{ + /* Make sure this is large enough when adding more */ + static char buf[BUF_LEN]; + buf[0] = '\0'; + + if (action & MSN_ADD_BUDDY) + strcat(buf, "Adding Buddy,"); + if (action & MSN_MOVE_BUDDY) + strcat(buf, "Moving Buddy,"); + if (action & MSN_ACCEPTED_BUDDY) + strcat(buf, "Accepted Buddy,"); + if (action & MSN_DENIED_BUDDY) + strcat(buf, "Denied Buddy,"); + if (action & MSN_ADD_GROUP) + strcat(buf, "Adding Group,"); + if (action & MSN_DEL_GROUP) + strcat(buf, "Deleting Group,"); + if (action & MSN_RENAME_GROUP) + strcat(buf, "Renaming Group,"); + if (action & MSN_UPDATE_INFO) + strcat(buf, "Updating Contact Info,"); + + return buf; +} + +static gboolean msn_contact_request(MsnCallbackState *state); + +static void +msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp, + gpointer data) +{ + MsnCallbackState *state = data; + xmlnode *fault; + char *faultcode_str; + + if (resp == NULL) { + purple_debug_error("msn", + "Operation {%s} failed. No response received from server.\n", + msn_contact_operation_str(state->action)); + return; + } + + fault = xmlnode_get_child(resp->xml, "Body/Fault"); + + if (fault == NULL) { + /* No errors */ + if (state->cb) + ((MsnSoapCallback)state->cb)(req, resp, data); + msn_callback_state_free(state); + return; + } + + faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode")); + + if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { + purple_debug_info("msn", + "Contact Operation {%s} failed because of bad token." + " Updating token now and retrying operation.\n", + msn_contact_operation_str(state->action)); + /* Token has expired, so renew it, and try again later */ + msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS, + (GSourceFunc)msn_contact_request, data); + } + else + { + if (state->cb) { + ((MsnSoapCallback)state->cb)(req, resp, data); + } else { + /* We don't know how to respond to this faultcode, so log it */ + char *str = xmlnode_to_str(fault, NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), str); + g_free(str); + } + msn_callback_state_free(state); + } + + g_free(faultcode_str); +} + +static gboolean +msn_contact_request(MsnCallbackState *state) +{ + if (state->token == NULL) + state->token = xmlnode_get_child(state->body, + "Header/ABAuthHeader/TicketToken"); + /* delete old & replace with new token */ + xmlnode_free(state->token->child); + xmlnode_insert_data(state->token, + msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1); + msn_soap_message_send(state->session, + msn_soap_message_new(state->post_action, xmlnode_copy(state->body)), + MSN_CONTACT_SERVER, state->post_url, FALSE, + msn_contact_request_cb, state); + return FALSE; +} + +/*************************************************************** + * Address Book and Membership List Operations + ***************************************************************/ + /*get MSN member role utility*/ static MsnListId msn_get_memberrole(const char *role) @@ -180,9 +287,10 @@ static void msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { + MsnCallbackState *state = data; if (resp && xmlnode_get_child(resp->xml, "Body/Fault") == NULL) { purple_debug_info("msn", "Address Book successfully created!\n"); - msn_get_address_book((MsnSession *)data, MSN_PS_INITIAL, NULL, NULL); + msn_get_address_book(state->session, MSN_PS_INITIAL, NULL, NULL); } else { purple_debug_info("msn", "Address Book creation failed!\n"); } @@ -192,7 +300,7 @@ msn_create_address_book(MsnSession *session) { gchar *body; - gchar *token_str; + MsnCallbackState *state; g_return_if_fail(session != NULL); g_return_if_fail(session->user != NULL); @@ -200,17 +308,15 @@ purple_debug_info("msn", "Creating an Address Book.\n"); - token_str = g_markup_escape_text( - msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, - token_str, session->user->passport); - g_free(token_str); + session->user->passport); - msn_soap_message_send(session, - msn_soap_message_new(MSN_ADD_ADDRESSBOOK_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, - msn_create_address_cb, session); + state = msn_callback_state_new(session); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_ADD_ADDRESSBOOK_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_create_address_cb; + msn_contact_request(state); g_free(body); } @@ -243,7 +349,7 @@ g_free(name); } } - + purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s, NetworkID: %u\n", node, passport, type, member_id == NULL ? "(null)" : member_id, nid); @@ -362,8 +468,8 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - GetContactListCbData *cb_data = data; - MsnSession *session = cb_data->session; + MsnCallbackState *state = data; + MsnSession *session = state->session; g_return_if_fail(session != NULL); @@ -379,7 +485,7 @@ dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL); - if (cb_data->which == MSN_PS_INITIAL) { + if (state->partner_scenario == MSN_PS_INITIAL) { #ifdef MSN_PARTIAL_LISTS /* XXX: this should be enabled when we can correctly do partial syncs with the server. Currently we need to retrieve the whole @@ -390,8 +496,6 @@ #endif } } - - g_free(cb_data); } /*SOAP get contact list*/ @@ -401,8 +505,7 @@ { gchar *body = NULL; gchar *update_str = NULL; - gchar *token_str; - GetContactListCbData cb_data = { session, partner_scenario }; + MsnCallbackState *state; const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario]; purple_debug_misc("msn", "Getting Contact List.\n"); @@ -412,17 +515,16 @@ update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML, update_time); } - token_str = g_markup_escape_text( - msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, - token_str, update_str ? update_str : ""); - g_free(token_str); + update_str ? update_str : ""); - msn_soap_message_send(session, - msn_soap_message_new(MSN_GET_CONTACT_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL, FALSE, - msn_get_contact_list_cb, g_memdup(&cb_data, sizeof(cb_data))); + state = msn_callback_state_new(session); + state->partner_scenario = partner_scenario; + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_GET_CONTACT_SOAP_ACTION; + state->post_url = MSN_GET_CONTACT_POST_URL; + state->cb = msn_get_contact_list_cb; + msn_contact_request(state); g_free(update_str); g_free(body); @@ -525,7 +627,7 @@ for(contactNode = xmlnode_get_child(node, "Contact"); contactNode; contactNode = xmlnode_get_next_twin(contactNode)) { - xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user; + xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds; xmlnode *annotation; MsnUser *user; @@ -556,58 +658,52 @@ continue; /* Not adding own account as buddy to buddylist */ } - /* ignore non-messenger contacts */ - if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) { - char *is_messenger_user = xmlnode_get_data(messenger_user); - - if(is_messenger_user && !strcmp(is_messenger_user, "false")) { - g_free(is_messenger_user); - continue; - } - - g_free(is_messenger_user); - } - passportName = xmlnode_get_child(contactInfo, "passportName"); if (passportName == NULL) { xmlnode *emailsNode, *contactEmailNode, *emailNode; xmlnode *messengerEnabledNode; char *msnEnabled; - /*TODO: add it to the none-instant Messenger group and recognize as email Membership*/ - /*Yahoo User?*/ + /*TODO: add it to the non-instant Messenger group and recognize as email Membership*/ + /* Yahoo/Federated User? */ emailsNode = xmlnode_get_child(contactInfo, "emails"); if (emailsNode == NULL) { /*TODO: need to support the Mobile type*/ continue; } for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode; - contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){ - if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) { - /* XXX: Should this be a continue instead of a break? It seems like it'd cause unpredictable results otherwise. */ - break; - } + contactEmailNode = xmlnode_get_next_twin(contactEmailNode)) { + if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) + continue; msnEnabled = xmlnode_get_data(messengerEnabledNode); - if ((emailNode = xmlnode_get_child(contactEmailNode, "email"))) { - g_free(passport); - passport = xmlnode_get_data(emailNode); - } + if (msnEnabled && !strcmp(msnEnabled, "true")) { + if ((emailNode = xmlnode_get_child(contactEmailNode, "email"))) + passport = xmlnode_get_data(emailNode); - if (msnEnabled && !strcmp(msnEnabled, "true")) { /*Messenger enabled, Get the Passport*/ - purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)"); + purple_debug_info("msn", "AB Yahoo/Federated User %s\n", passport ? passport : "(null)"); g_free(msnEnabled); break; - } else { - /*TODO maybe we can just ignore it in Purple?*/ - purple_debug_info("msn", "AB Other type user\n"); } g_free(msnEnabled); } } else { + xmlnode *messenger_user; + /* ignore non-messenger contacts */ + if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) { + char *is_messenger_user = xmlnode_get_data(messenger_user); + + if (is_messenger_user && !strcmp(is_messenger_user, "false")) { + g_free(is_messenger_user); + continue; + } + + g_free(is_messenger_user); + } + passport = xmlnode_get_data(passportName); } @@ -777,10 +873,8 @@ static void msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - MsnSession *session = data; - - if (resp == NULL) - return; + MsnCallbackState *state = data; + MsnSession *session = state->session; g_return_if_fail(session != NULL); @@ -809,7 +903,7 @@ const char *dynamicItemLastChange) { char *body, *update_str = NULL; - gchar *token_str; + MsnCallbackState *state; purple_debug_misc("msn", "Getting Address Book\n"); @@ -819,19 +913,16 @@ else if (LastChanged != NULL) update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, LastChanged); - token_str = g_markup_escape_text( - msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], - token_str, update_str ? update_str : ""); - g_free(token_str); - msn_soap_message_send(session, - msn_soap_message_new(MSN_GET_ADDRESS_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, - msn_get_address_cb, session); + state = msn_callback_state_new(session); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_GET_ADDRESS_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_get_address_cb; + msn_contact_request(state); g_free(update_str); g_free(body); @@ -841,105 +932,6 @@ * Contact Operations ***************************************************************/ -static const char * -msn_contact_operation_str(MsnCallbackAction action) -{ - /* Make sure this is large enough when adding more */ - static char buf[BUF_LEN]; - buf[0] = '\0'; - - if (action & MSN_ADD_BUDDY) - strcat(buf, "Adding Buddy,"); - if (action & MSN_MOVE_BUDDY) - strcat(buf, "Moving Buddy,"); - if (action & MSN_ACCEPTED_BUDDY) - strcat(buf, "Accepted Buddy,"); - if (action & MSN_DENIED_BUDDY) - strcat(buf, "Denied Buddy,"); - if (action & MSN_ADD_GROUP) - strcat(buf, "Adding Group,"); - if (action & MSN_DEL_GROUP) - strcat(buf, "Deleting Group,"); - if (action & MSN_RENAME_GROUP) - strcat(buf, "Renaming Group,"); - if (action & MSN_UPDATE_INFO) - strcat(buf, "Updating Contact Info,"); - - return buf; -} - -static gboolean msn_contact_request(MsnCallbackState *state); - -static void -msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp, - gpointer data) -{ - MsnCallbackState *state = data; - xmlnode *fault; - char *faultcode_str; - - if (resp == NULL) { - purple_debug_error("msn", - "Operation {%s} failed. No response received from server.\n", - msn_contact_operation_str(state->action)); - return; - } - - fault = xmlnode_get_child(resp->xml, "Body/Fault"); - - if (fault == NULL) { - /* No errors */ - if (state->cb) - ((MsnSoapCallback)state->cb)(req, resp, data); - msn_callback_state_free(state); - return; - } - - faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode")); - - if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { - purple_debug_info("msn", - "Contact Operation {%s} failed because of bad token." - " Updating token now and retrying operation.\n", - msn_contact_operation_str(state->action)); - /* Token has expired, so renew it, and try again later */ - msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS, - (GSourceFunc)msn_contact_request, data); - } - else - { - if (state->cb) { - ((MsnSoapCallback)state->cb)(req, resp, data); - } else { - /* We don't know how to respond to this faultcode, so log it */ - char *str = xmlnode_to_str(fault, NULL); - purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", - msn_contact_operation_str(state->action), str); - g_free(str); - } - msn_callback_state_free(state); - } - - g_free(faultcode_str); -} - -static gboolean -msn_contact_request(MsnCallbackState *state) -{ - if (state->token == NULL) - state->token = xmlnode_get_child(state->body, - "Header/ABAuthHeader/TicketToken"); - /* delete old & replace with new token */ - xmlnode_free(state->token->child); - xmlnode_insert_data(state->token, - msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1); - msn_soap_message_send(state->session, - msn_soap_message_new(state->post_action, xmlnode_copy(state->body)), - MSN_CONTACT_SERVER, state->post_url, FALSE, - msn_contact_request_cb, state); - return FALSE; -} - static void msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) @@ -1362,7 +1354,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");
--- a/libpurple/protocols/msn/contact.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/contact.h Fri Feb 06 23:27:49 2009 +0000 @@ -52,7 +52,7 @@ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>"\ - "<TicketToken>%s</TicketToken>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ @@ -94,7 +94,7 @@ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ - "<TicketToken>%s</TicketToken>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -135,7 +135,7 @@ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ - "<TicketToken>%s</TicketToken>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -619,6 +619,15 @@ MSN_UPDATE_INFO = 0x80 } MsnCallbackAction; +typedef enum +{ + MSN_PS_INITIAL, + MSN_PS_SAVE_CONTACT, + MSN_PS_PENDING_LIST, + MSN_PS_CONTACT_API, + MSN_PS_BLOCK_UNBLOCK +} MsnSoapPartnerScenario; + typedef struct _MsnCallbackState MsnCallbackState; struct _MsnCallbackState @@ -636,19 +645,12 @@ const gchar *post_action; const gchar *post_url; MsnSoapCallback cb; + /* For msn_get_contact_list only */ + MsnSoapPartnerScenario partner_scenario; }; typedef enum { - MSN_PS_INITIAL, - MSN_PS_SAVE_CONTACT, - MSN_PS_PENDING_LIST, - MSN_PS_CONTACT_API, - MSN_PS_BLOCK_UNBLOCK -} MsnSoapPartnerScenario; - -typedef enum -{ MSN_UPDATE_DISPLAY, /* Real display name */ MSN_UPDATE_ALIAS, /* Aliased display name */ MSN_UPDATE_COMMENT
--- a/libpurple/protocols/msn/msn.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/msn.c Fri Feb 06 23:27:49 2009 +0000 @@ -855,11 +855,11 @@ types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, - NULL, NULL, FALSE, TRUE, FALSE); + NULL, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - NULL, NULL, FALSE, TRUE, FALSE); + NULL, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, @@ -1178,6 +1178,7 @@ MsnMessage *msg; char *msgformat; char *msgtext; + size_t msglen; const char *username; purple_debug_info("msn", "send IM {%s} to %s\n", message, who); @@ -1205,13 +1206,23 @@ } msn_import_html(message, &msgformat, &msgtext); + msglen = strlen(msgtext); + if (msglen == 0) { + /* Stuff like <hr> will be ignored. Don't send an empty message + if that's all there is. */ + g_free(msgtext); + g_free(msgformat); + + return 0; + } + if (msn_user_is_online(account, who) || msn_user_is_yahoo(account, who) || swboard != NULL) { /*User online or have a swboard open because it's invisible * and sent us a message,then send Online Instant Message*/ - if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564) + if (msglen + strlen(msgformat) + strlen(VERSION) > 1564) { g_free(msgformat); g_free(msgtext);
--- a/libpurple/protocols/msn/notification.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/notification.c Fri Feb 06 23:27:49 2009 +0000 @@ -1094,8 +1094,10 @@ return; } - serv_got_alias(gc, passport, friendly); - msn_user_set_friendly_name(user, friendly); + if (msn_user_set_friendly_name(user, friendly)) { + serv_got_alias(gc, passport, friendly); + msn_update_contact(session, passport, MSN_UPDATE_DISPLAY, friendly); + } g_free(friendly); msn_user_set_object(user, msnobj); @@ -1220,7 +1222,7 @@ MsnObject *msnobj; unsigned long clientid; int networkid; - const char *state, *passport, *friendly, *old_friendly; + const char *state, *passport, *friendly; session = cmdproc->session; account = session->account; @@ -1234,11 +1236,10 @@ user = msn_userlist_find_user(session->userlist, passport); if (user == NULL) return; - old_friendly = msn_user_get_friendly_name(user); - if (!old_friendly || (old_friendly && (!friendly || strcmp(old_friendly, friendly)))) + if (msn_user_set_friendly_name(user, friendly)) { serv_got_alias(gc, passport, friendly); - msn_user_set_friendly_name(user, friendly); + msn_update_contact(session, passport, MSN_UPDATE_DISPLAY, friendly); } if (cmd->param_count == 6) @@ -2095,6 +2096,13 @@ msn_table_add_msg_type(cbs_table, "application/x-msmsgssystemmessage", system_msg); + /* generic message handlers */ + msn_table_add_msg_type(cbs_table, "text/plain", + msn_plain_msg); + msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol", + msn_control_msg); + msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast", + msn_datacast_msg); } void
--- a/libpurple/protocols/msn/session.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/session.h Fri Feb 06 23:27:49 2009 +0000 @@ -123,7 +123,7 @@ } passport_info; GHashTable *soap_table; - int soap_cleanup_handle; + guint soap_cleanup_handle; }; /**
--- a/libpurple/protocols/msn/slpcall.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/slpcall.c Fri Feb 06 23:27:49 2009 +0000 @@ -206,7 +206,7 @@ body = slpmsg->buffer; body_len = slpmsg->size; - if (slpmsg->flags == 0x0) + if (slpmsg->flags == 0x0 || slpmsg->flags == 0x1000000) { char *body_str; @@ -214,7 +214,9 @@ slpcall = msn_slp_sip_recv(slplink, body_str); g_free(body_str); } - else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) + else if (slpmsg->flags == 0x20 || + slpmsg->flags == 0x1000020 || + slpmsg->flags == 0x1000030) { slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); @@ -237,6 +239,13 @@ 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); return slpcall; }
--- a/libpurple/protocols/msn/slplink.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/slplink.c Fri Feb 06 23:27:49 2009 +0000 @@ -281,7 +281,8 @@ g_list_append(slpmsg->msgs, msg); msn_slplink_send_msg(slplink, msg); - if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && + if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 || + slpmsg->flags == 0x1000030) && (slpmsg->slpcall != NULL)) { slpmsg->slpcall->progress = TRUE; @@ -316,7 +317,8 @@ else { /* The whole message has been sent */ - if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) + if (slpmsg->flags == 0x20 || + slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) { if (slpmsg->slpcall != NULL) { @@ -362,7 +364,8 @@ msg->msnslp_header.ack_size = slpmsg->ack_size; msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id; } - else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) + else if (slpmsg->flags == 0x20 || + slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) { MsnSlpCall *slpcall; slpcall = slpmsg->slpcall; @@ -398,6 +401,8 @@ void msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { + g_return_if_fail(slpmsg != NULL); + slpmsg->id = slplink->slp_seq_id++; g_queue_push_tail(slplink->slp_msg_queue, slpmsg); @@ -530,7 +535,8 @@ if (slpmsg->slpcall != NULL) { - if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) + if (slpmsg->flags == 0x20 || + slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) { PurpleXfer *xfer; @@ -593,7 +599,8 @@ memcpy(slpmsg->buffer + offset, data, len); } - if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && + if ((slpmsg->flags == 0x20 || + slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) && (slpmsg->slpcall != NULL)) { slpmsg->slpcall->progress = TRUE; @@ -628,8 +635,9 @@ msn_directconn_send_handshake(directconn); #endif } - else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 || - slpmsg->flags == 0x1000030) + else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 || + slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 || + slpmsg->flags == 0x1000030) { /* Release all the messages and send the ACK */
--- a/libpurple/protocols/msn/soap.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/soap.c Fri Feb 06 23:27:49 2009 +0000 @@ -342,12 +342,14 @@ } if (fault || body) { - MsnSoapRequest *request = conn->current_request; - conn->current_request = NULL; - request->cb(request->message, response, - request->cb_data); + if (conn->current_request) { + MsnSoapRequest *request = conn->current_request; + conn->current_request = NULL; + request->cb(request->message, response, + request->cb_data); + msn_soap_request_destroy(request, FALSE); + } msn_soap_message_destroy(response); - msn_soap_request_destroy(request, FALSE); } return TRUE;
--- a/libpurple/protocols/msn/user.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/user.c Fri Feb 06 23:27:49 2009 +0000 @@ -177,13 +177,18 @@ user->passport = g_strdup(passport); } -void +gboolean msn_user_set_friendly_name(MsnUser *user, const char *name) { - g_return_if_fail(user != NULL); + g_return_val_if_fail(user != NULL, FALSE); + + if (user->friendly_name && name && !strcmp(user->friendly_name, name)) + return FALSE; g_free(user->friendly_name); user->friendly_name = g_strdup(name); + + return TRUE; } void
--- a/libpurple/protocols/msn/user.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/msn/user.h Fri Feb 06 23:27:49 2009 +0000 @@ -178,8 +178,10 @@ * * @param user The user. * @param name The friendly name. + * + * @returns TRUE is name actually changed, FALSE otherwise. */ -void msn_user_set_friendly_name(MsnUser *user, const char *name); +gboolean msn_user_set_friendly_name(MsnUser *user, const char *name); /** * Sets the buddy icon for a local user.
--- a/libpurple/protocols/myspace/myspace.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/myspace/myspace.c Fri Feb 06 23:27:49 2009 +0000 @@ -1146,7 +1146,11 @@ break; case MSIM_CONTACT_LIST_INITIAL_FRIENDS: - /* Nothing */ + /* The session is now set up, ready to be connected. This emits the + * signedOn signal, so clients can now do anything with msimprpl, and + * we're ready for it (session key, userid, username all setup). */ + purple_connection_update_progress(session->gc, _("Connected"), 3, 4); + purple_connection_set_state(session->gc, PURPLE_CONNECTED); break; } @@ -1185,12 +1189,6 @@ /* Set display name to username (otherwise will show email address) */ purple_connection_set_display_name(session->gc, session->username); - /* The session is now set up, ready to be connected. This emits the - * signedOn signal, so clients can now do anything with msimprpl, and - * we're ready for it (session key, userid, username all setup). */ - purple_connection_update_progress(session->gc, _("Connected"), 3, 4); - purple_connection_set_state(session->gc, PURPLE_CONNECTED); - body = msim_msg_new( "UserID", MSIM_TYPE_INTEGER, session->userid, NULL);
--- a/libpurple/protocols/novell/novell.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/novell/novell.c Fri Feb 06 23:27:49 2009 +0000 @@ -2976,7 +2976,7 @@ NULL, TRUE, TRUE, FALSE); status_types = g_list_append(status_types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE); status_types = g_list_append(status_types, type); return status_types;
--- a/libpurple/protocols/oscar/family_chat.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/family_chat.c Fri Feb 06 23:27:49 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;
--- a/libpurple/protocols/oscar/family_icq.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Fri Feb 06 23:27:49 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;
--- a/libpurple/protocols/oscar/family_oservice.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/family_oservice.c Fri Feb 06 23:27:49 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);
--- a/libpurple/protocols/oscar/flap_connection.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Fri Feb 06 23:27:49 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); + } } }
--- a/libpurple/protocols/oscar/odc.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/odc.c Fri Feb 06 23:27:49 2009 +0000 @@ -394,7 +394,7 @@ } } - /* Send the message */ + /* Display the message we received */ imflags = 0; if (images != NULL) imflags |= PURPLE_MESSAGE_IMAGES;
--- a/libpurple/protocols/oscar/oscar.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Fri Feb 06 23:27:49 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; } @@ -4337,8 +4421,7 @@ } g_string_free(data, TRUE); - peer_odc_send_im(conn, msg->str, msg->len, charset, - imflags & PURPLE_MESSAGE_AUTO_RESP); + peer_odc_send_im(conn, msg->str, msg->len, charset, imflags); g_string_free(msg, TRUE); } @@ -4699,7 +4782,7 @@ if (status_html != NULL) { status_text = purple_markup_strip_html(status_html); - /* If the status_text is longer than 60 character then truncate it */ + /* If the status_text is longer than 251 characters then truncate it */ if (strlen(status_text) > MAXAVAILMSGLEN) { char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]); @@ -4716,9 +4799,28 @@ } else { + char *status_text = NULL; + htmlaway = purple_status_get_attr_string(status, "message"); if ((htmlaway == NULL) || (*htmlaway == '\0')) htmlaway = purple_status_type_get_name(status_type); + + /* ICQ 6.x seems to use an available message for all statuses so set one */ + if (od->icq) + { + status_text = purple_markup_strip_html(htmlaway); + /* If the status_text is longer than 251 characters then truncate it */ + if (strlen(status_text) > MAXAVAILMSGLEN) + { + char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]); + strcpy(tmp, "..."); + } + aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, NULL); + } + g_free(status_text); + + /* Set a proper away message for icq too so that they work for old third party clients */ + away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding); if (awaylen > od->rights.maxawaymsglen) @@ -5310,7 +5412,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; @@ -5380,13 +5481,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, " @@ -5824,19 +5919,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) { @@ -5848,13 +5932,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; } @@ -6860,6 +6946,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);
--- a/libpurple/protocols/oscar/oscar.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.h Fri Feb 06 23:27:49 2009 +0000 @@ -34,6 +34,7 @@ #include "eventloop.h" #include "internal.h" #include "proxy.h" +#include "sslconn.h" #include <stdio.h> #include <string.h> @@ -285,6 +286,15 @@ "us", "en", \ } +#define CLIENTINFO_ICQBASIC_14_34_3096 { \ + "ICQBasic", \ + 0x010a, \ + 0x0014, 0x0034, \ + 0x0000, 0x0c18, \ + 0x0000043d, \ + "us", "en", \ +} + #define CLIENTINFO_NETSCAPE_7_0_1 { \ "Netscape 2000 an approved user of AOL Instant Messenger (SM)", \ 0x1d0d, \ @@ -311,14 +321,14 @@ #define CLIENTINFO_PURPLE_ICQ { \ "Purple/" VERSION, \ 0x010a, \ - 0x0006, 0x0000, \ - 0x0000, 0x17ab, \ - 0x00007535, \ + 0x0014, 0x0034, \ + 0x0000, 0x0c18, \ + 0x0000043d, \ "us", "en", \ } #define CLIENTINFO_AIM_KNOWNGOOD CLIENTINFO_AIM_5_1_3036 -#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3000 +#define CLIENTINFO_ICQ_KNOWNGOOD CLIENTINFO_ICQBASIC_14_34_3096 typedef enum { @@ -417,8 +427,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 +488,7 @@ GHashTable *buddyinfo; GSList *requesticon; + gboolean use_ssl; gboolean icq; guint getblisttimer; @@ -537,10 +550,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 +602,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 +627,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 +1365,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 */
--- a/libpurple/protocols/oscar/oscar_data.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/oscar_data.c Fri Feb 06 23:27:49 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);
--- a/libpurple/protocols/oscar/oscarcommon.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/oscarcommon.h Fri Feb 06 23:27:49 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);
--- a/libpurple/protocols/oscar/peer.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/oscar/peer.c Fri Feb 06 23:27:49 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) {
--- a/libpurple/protocols/qq/qq.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/qq/qq.c Fri Feb 06 23:27:49 2009 +0000 @@ -406,15 +406,15 @@ GList *types = NULL; status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, - "available", _("Available"), FALSE, TRUE, FALSE); + "available", _("Available"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "away", _("Away"), FALSE, TRUE, FALSE); + "away", _("Away"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, - "invisible", _("Invisible"), FALSE, TRUE, FALSE); + "invisible", _("Invisible"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, @@ -422,7 +422,7 @@ types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - "offline", _("Offline"), FALSE, TRUE, FALSE); + "offline", _("Offline"), TRUE, TRUE, FALSE); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
--- a/libpurple/protocols/silc/silc.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/silc/silc.c Fri Feb 06 23:27:49 2009 +0000 @@ -48,19 +48,19 @@ PurpleStatusType *type; GList *types = NULL; - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); return types; @@ -1357,6 +1357,7 @@ char tmp[256]; SilcClientEntry client_entry; SilcDList list; + gboolean free_list = FALSE; convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick, sg->account); @@ -1373,6 +1374,8 @@ im->nick, FALSE); if (!clients) goto err; + + free_list = TRUE; } silc_dlist_start(clients); @@ -1413,6 +1416,9 @@ purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); out: + if (free_list) { + silc_client_list_free(client, conn, clients); + } g_free(im->nick); g_free(im->message); silc_free(im);
--- a/libpurple/protocols/silc10/silc.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/protocols/silc10/silc.c Fri Feb 06 23:27:49 2009 +0000 @@ -39,19 +39,19 @@ PurpleStatusType *type; GList *types = NULL; - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE); types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, FALSE, TRUE, FALSE); + type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); return types;
--- a/libpurple/prpl.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/prpl.c Fri Feb 06 23:27:49 2009 +0000 @@ -190,7 +190,7 @@ g_return_if_fail(account != NULL); g_return_if_fail(name != NULL); - g_return_if_fail(purple_account_is_connected(account)); + g_return_if_fail(purple_account_is_connected(account) || purple_account_is_connecting(account)); if ((list = purple_find_buddies(account, name)) == NULL) return;
--- a/libpurple/purple-url-handler Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/purple-url-handler Fri Feb 06 23:27:49 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)
--- a/libpurple/status.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/status.c Fri Feb 06 23:27:49 2009 +0000 @@ -250,7 +250,7 @@ { g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, NULL); - return purple_status_type_new_full(primitive, id, name, FALSE, + return purple_status_type_new_full(primitive, id, name, TRUE, user_settable, FALSE); } @@ -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);
--- a/libpurple/status.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/status.h Fri Feb 06 23:27:49 2009 +0000 @@ -199,8 +199,8 @@ gboolean independent); /** - * Creates a new status type with some default values (not - * savable and not independent). + * Creates a new status type with some default values ( + * saveable and not independent). * * @param primitive The primitive status type. * @param id The ID of the status type, or @c NULL to use the id of
--- a/libpurple/xmlnode.c Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/xmlnode.c Fri Feb 06 23:27:49 2009 +0000 @@ -303,7 +303,7 @@ node->prefix = g_strdup(prefix); } -const char *xmlnode_get_prefix(xmlnode *node) +const char *xmlnode_get_prefix(const xmlnode *node) { g_return_val_if_fail(node != NULL, NULL); return node->prefix; @@ -443,11 +443,11 @@ } static char * -xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) +xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int depth) { GString *text = g_string_new(""); const char *prefix; - xmlnode *c; + const xmlnode *c; char *node_name, *esc, *esc2, *tab = NULL; gboolean need_end = FALSE, pretty = formatting; @@ -537,13 +537,13 @@ } char * -xmlnode_to_str(xmlnode *node, int *len) +xmlnode_to_str(const xmlnode *node, int *len) { return xmlnode_to_str_helper(node, len, FALSE, 0); } char * -xmlnode_to_formatted_str(xmlnode *node, int *len) +xmlnode_to_formatted_str(const xmlnode *node, int *len) { char *xml, *xml_with_declaration;
--- a/libpurple/xmlnode.h Fri Feb 06 23:27:45 2009 +0000 +++ b/libpurple/xmlnode.h Fri Feb 06 23:27:49 2009 +0000 @@ -243,7 +243,7 @@ * @param node The node to get the prefix from * @return The prefix of this node */ -const char *xmlnode_get_prefix(xmlnode *node); +const char *xmlnode_get_prefix(const xmlnode *node); /** * Returns the node in a string of xml. @@ -254,7 +254,7 @@ * @return The node represented as a string. You must * g_free this string when finished using it. */ -char *xmlnode_to_str(xmlnode *node, int *len); +char *xmlnode_to_str(const xmlnode *node, int *len); /** * Returns the node in a string of human readable xml. @@ -266,7 +266,7 @@ * tab and new line characters. You must * g_free this string when finished using it. */ -char *xmlnode_to_formatted_str(xmlnode *node, int *len); +char *xmlnode_to_formatted_str(const xmlnode *node, int *len); /** * Creates a node from a string of XML. Calling this on the
--- a/pidgin/gtkaccount.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkaccount.c Fri Feb 06 23:27:49 2009 +0000 @@ -84,6 +84,13 @@ typedef struct { + GtkWidget *widget; + gchar *setting; + PurplePrefType type; +} ProtocolOptEntry; + +typedef struct +{ PidginAccountDialogType type; PurpleAccount *account; @@ -548,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); @@ -740,21 +749,22 @@ char *title, *tmp; const char *str_value; gboolean bool_value; + ProtocolOptEntry *opt_entry; if (dialog->protocol_frame != NULL) { gtk_widget_destroy(dialog->protocol_frame); dialog->protocol_frame = NULL; } - if (dialog->protocol_opt_entries != NULL) { - g_list_free(dialog->protocol_opt_entries); - dialog->protocol_opt_entries = NULL; - } - if (dialog->prpl_info == NULL || - dialog->prpl_info->protocol_options == NULL) { - + dialog->prpl_info->protocol_options == NULL) return; + + while (dialog->protocol_opt_entries != NULL) { + ProtocolOptEntry *opt_entry = dialog->protocol_opt_entries->data; + g_free(opt_entry->setting); + g_free(opt_entry); + dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries); } account = dialog->account; @@ -778,7 +788,11 @@ { option = (PurpleAccountOption *)l->data; - switch (purple_account_option_get_type(option)) + opt_entry = g_new0(ProtocolOptEntry, 1); + opt_entry->type = purple_account_option_get_type(option); + opt_entry->setting = g_strdup(purple_account_option_get_setting(option)); + + switch (opt_entry->type) { case PURPLE_PREF_BOOLEAN: if (account == NULL || @@ -795,7 +809,7 @@ } tmp = g_strconcat("_", purple_account_option_get_text(option), NULL); - check = gtk_check_button_new_with_mnemonic(tmp); + opt_entry->widget = check = gtk_check_button_new_with_mnemonic(tmp); g_free(tmp); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), @@ -803,10 +817,6 @@ gtk_box_pack_start(GTK_BOX(vbox), check, FALSE, FALSE, 0); gtk_widget_show(check); - - dialog->protocol_opt_entries = - g_list_append(dialog->protocol_opt_entries, check); - break; case PURPLE_PREF_INT: @@ -825,19 +835,13 @@ g_snprintf(buf, sizeof(buf), "%d", int_value); - entry = gtk_entry_new(); + opt_entry->widget = entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry), buf); title = g_strdup_printf("_%s:", purple_account_option_get_text(option)); - add_pref_box(dialog, vbox, title, entry); - g_free(title); - - dialog->protocol_opt_entries = - g_list_append(dialog->protocol_opt_entries, entry); - break; case PURPLE_PREF_STRING: @@ -854,12 +858,14 @@ purple_account_option_get_default_string(option)); } - entry = gtk_entry_new(); + opt_entry->widget = entry = gtk_entry_new(); 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) @@ -867,14 +873,8 @@ title = g_strdup_printf("_%s:", purple_account_option_get_text(option)); - add_pref_box(dialog, vbox, title, entry); - g_free(title); - - dialog->protocol_opt_entries = - g_list_append(dialog->protocol_opt_entries, entry); - break; case PURPLE_PREF_STRING_LIST: @@ -896,7 +896,7 @@ list = purple_account_option_get_list(option); model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); - combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); + opt_entry->widget = combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(model)); /* Loop through list of PurpleKeyValuePair items */ for (node = list; node != NULL; node = node->next) { @@ -928,20 +928,21 @@ title = g_strdup_printf("_%s:", purple_account_option_get_text(option)); - add_pref_box(dialog, vbox, title, combo); - g_free(title); - - dialog->protocol_opt_entries = - g_list_append(dialog->protocol_opt_entries, combo); - break; - default: - break; + purple_debug_error("gtkaccount", "Invalid Account Option pref type (%d)\n", + opt_entry->type); + g_free(opt_entry->setting); + g_free(opt_entry); + continue; } + + dialog->protocol_opt_entries = + g_list_append(dialog->protocol_opt_entries, opt_entry); + } } @@ -1092,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 && @@ -1152,7 +1155,12 @@ gtk_widget_destroy(dialog->window); g_list_free(dialog->user_split_entries); - g_list_free(dialog->protocol_opt_entries); + while (dialog->protocol_opt_entries != NULL) { + ProtocolOptEntry *opt_entry = dialog->protocol_opt_entries->data; + g_free(opt_entry->setting); + g_free(opt_entry); + dialog->protocol_opt_entries = g_list_delete_link(dialog->protocol_opt_entries, dialog->protocol_opt_entries); + } g_free(dialog->protocol_id); g_object_unref(dialog->sg); @@ -1317,46 +1325,38 @@ /* Add the protocol settings */ if (dialog->prpl_info) { - for (l = dialog->prpl_info->protocol_options, - l2 = dialog->protocol_opt_entries; - l != NULL && l2 != NULL; - l = l->next, l2 = l2->next) { - - PurplePrefType type; - PurpleAccountOption *option = l->data; - GtkWidget *widget = l2->data; - GtkTreeIter iter; - const char *setting; - char *value2; - int int_value; - gboolean bool_value; - - type = purple_account_option_get_type(option); - - setting = purple_account_option_get_setting(option); - - switch (type) { + ProtocolOptEntry *opt_entry; + GtkTreeIter iter; + char *value2; + int int_value; + gboolean bool_value; + + for (l2 = dialog->protocol_opt_entries; l2; l2 = l2->next) { + + opt_entry = l2->data; + + switch (opt_entry->type) { case PURPLE_PREF_STRING: - value = gtk_entry_get_text(GTK_ENTRY(widget)); - purple_account_set_string(account, setting, value); + value = gtk_entry_get_text(GTK_ENTRY(opt_entry->widget)); + purple_account_set_string(account, opt_entry->setting, value); break; case PURPLE_PREF_INT: - int_value = atoi(gtk_entry_get_text(GTK_ENTRY(widget))); - purple_account_set_int(account, setting, int_value); + int_value = atoi(gtk_entry_get_text(GTK_ENTRY(opt_entry->widget))); + purple_account_set_int(account, opt_entry->setting, int_value); break; case PURPLE_PREF_BOOLEAN: bool_value = - gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); - purple_account_set_bool(account, setting, bool_value); + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(opt_entry->widget)); + purple_account_set_bool(account, opt_entry->setting, bool_value); break; case PURPLE_PREF_STRING_LIST: value2 = NULL; - if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter)) - gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(widget)), &iter, 1, &value2, -1); - purple_account_set_string(account, setting, value2); + if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(opt_entry->widget), &iter)) + gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(opt_entry->widget)), &iter, 1, &value2, -1); + purple_account_set_string(account, opt_entry->setting, value2); break; default: @@ -1990,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); }
--- a/pidgin/gtkblist.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkblist.c Fri Feb 06 23:27:49 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",
--- a/pidgin/gtkdialogs.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkdialogs.c Fri Feb 06 23:27:49 2009 +0000 @@ -198,7 +198,7 @@ {N_("Macedonian"), "mk", "Arangel Angov ", "arangel@linux.net.mk"}, {N_("Macedonian"), "mk", "Ivana Kirkovska", "ivana.kirkovska@gmail.com"}, {N_("Macedonian"), "mk", "Jovan Naumovski", "jovan@lugola.net"}, - {"Mongolian", "mn", "gooyo", NULL}, + {N_("Mongolian"), "mn", "gooyo", NULL}, {N_("Bokmål Norwegian"), "nb", "Hans Fredrik Nordhaug", "hans@nordhaug.priv.no"}, {N_("Nepali"), "ne", "Shyam Krishna Bal", "shyamkrishna_bal@yahoo.com"}, {N_("Dutch, Flemish"), "nl", "Vincent van Adrighem", "V.vanAdrighem@dirck.mine.nu"},
--- a/pidgin/gtkgaim-compat.h Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkgaim-compat.h Fri Feb 06 23:27:49 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
--- a/pidgin/gtkimhtml.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkimhtml.c Fri Feb 06 23:27:49 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);
--- a/pidgin/gtkmain.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkmain.c Fri Feb 06 23:27:49 2009 +0000 @@ -322,9 +322,6 @@ pidgin_session_end(); #endif - /* Save the plugins we have loaded for next time. */ - pidgin_plugins_save(); - /* Uninit */ pidgin_smileys_uninit(); pidgin_conversations_uninit(); @@ -787,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
--- a/pidgin/gtkpluginpref.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkpluginpref.c Fri Feb 06 23:27:49 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),
--- a/pidgin/gtkprefs.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkprefs.c Fri Feb 06 23:27:49 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);
--- a/pidgin/gtkrequest.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/gtkrequest.c Fri Feb 06 23:27:49 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),
--- a/pidgin/pidgin.h Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/pidgin.h Fri Feb 06 23:27:49 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_ */
--- a/pidgin/pidgintooltip.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/pidgintooltip.c Fri Feb 06 23:27:49 2009 +0000 @@ -82,7 +82,8 @@ static void destroy_tooltip_data(PidginTooltipData *data) { - gtk_tree_path_free(data->common.treeview.path); + if (data->common.treeview.path) + gtk_tree_path_free(data->common.treeview.path); pidgin_tooltip_destroy(); g_free(data); } @@ -380,7 +381,7 @@ g_signal_connect(G_OBJECT(widget), "motion-notify-event", G_CALLBACK(widget_motion_cb), wdata); g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(widget_leave_cb), NULL); - g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(g_free), wdata); + g_signal_connect_swapped(G_OBJECT(widget), "destroy", G_CALLBACK(destroy_tooltip_data), wdata); return TRUE; }
--- a/pidgin/plugins/win32/winprefs/winprefs.c Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/plugins/win32/winprefs/winprefs.c Fri Feb 06 23:27:49 2009 +0000 @@ -55,9 +55,8 @@ static const char *PREF_DBLIST_HEIGHT = "/plugins/gtk/win32/winprefs/dblist_height"; static const char *PREF_DBLIST_SIDE = "/plugins/gtk/win32/winprefs/dblist_side"; static const char *PREF_BLIST_ON_TOP = "/plugins/gtk/win32/winprefs/blist_on_top"; +/* Deprecated */ static const char *PREF_CHAT_BLINK = "/plugins/gtk/win32/winprefs/chat_blink"; - -/* Deprecated */ static const char *PREF_DBLIST_ON_TOP = "/plugins/gtk/win32/winprefs/dblist_on_top"; static PurplePlugin *handle = NULL; @@ -229,17 +228,6 @@ blist_set_ontop(FALSE); } -static gboolean -winpidgin_conv_chat_blink(PurpleAccount *account, const char *who, char **message, - PurpleConversation *conv, PurpleMessageFlags flags, void *data) -{ - if(purple_prefs_get_bool(PREF_CHAT_BLINK)) - winpidgin_conv_blink(conv, flags); - - return FALSE; -} - - /* * EXPORTED FUNCTIONS */ @@ -258,10 +246,6 @@ purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-created", plugin, PURPLE_CALLBACK(blist_create_cb), NULL); - purple_signal_connect(pidgin_conversations_get_handle(), - "displaying-chat-msg", plugin, PURPLE_CALLBACK(winpidgin_conv_chat_blink), - NULL); - purple_signal_connect((void*)purple_get_core(), "quitting", plugin, PURPLE_CALLBACK(purple_quit_cb), NULL); @@ -336,11 +320,6 @@ _("Only when docked"), BLIST_TOP_DOCKED, NULL); - /* Conversations */ - vbox = pidgin_make_frame(ret, _("Conversations")); - pidgin_prefs_checkbox(_("_Flash window when chat messages are received"), - PREF_CHAT_BLINK, vbox); - gtk_widget_show_all(ret); return ret; } @@ -399,7 +378,6 @@ purple_prefs_add_bool(PREF_DBLIST_DOCKED, FALSE); purple_prefs_add_int(PREF_DBLIST_HEIGHT, 0); purple_prefs_add_int(PREF_DBLIST_SIDE, 0); - purple_prefs_add_bool(PREF_CHAT_BLINK, FALSE); /* Convert old preferences */ if(purple_prefs_exists(PREF_DBLIST_ON_TOP)) { @@ -413,6 +391,7 @@ purple_prefs_add_int(PREF_BLIST_ON_TOP, blist_top); } else purple_prefs_add_int(PREF_BLIST_ON_TOP, BLIST_TOP_NEVER); + purple_prefs_remove(PREF_CHAT_BLINK); } PURPLE_INIT_PLUGIN(winprefs, init_plugin, info)
--- a/pidgin/win32/nsis/pidgin-installer.nsi Fri Feb 06 23:27:45 2009 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Fri Feb 06 23:27:49 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" @@ -1246,6 +1247,9 @@ Push $R0 Push $R1 Push $R2 + Push $R3 ; This is only used for the Parameters throughout the function + + ${GetParameters} $R3 IntOp $R1 0 + 0 retry_runcheck: @@ -1257,7 +1261,14 @@ IntCmp $R0 0 +3 ;This could check for ERROR_ALREADY_EXISTS(183), but lets just assume MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(INSTALLER_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck Abort + + ; Allow installer to run even if pidgin is running via "/NOPIDGINRUNCHECK=1" + ; This is useful for testing + ClearErrors + ${GetOptions} "$R3" "/NOPIDGINRUNCHECK=" $R1 + IfErrors 0 +2 Call RunCheck + StrCpy $name "Pidgin ${PIDGIN_VERSION}" StrCpy $SPELLCHECK_SEL "" @@ -1311,16 +1322,13 @@ SetShellVarContext "current" StrCpy $ISSILENT "/S" - - ; GTK installer has two silent states.. one with Message boxes, one without + ; GTK installer has two silent states - one with Message boxes, one without ; If pidgin installer was run silently, we want to supress gtk installer msg boxes. - IfSilent 0 set_gtk_normal - StrCpy $ISSILENT "/NOUI" - set_gtk_normal: + IfSilent 0 +2 + StrCpy $ISSILENT "/NOUI" - ${GetParameters} $R0 ClearErrors - ${GetOptions} "$R0" "/L=" $R1 + ${GetOptions} "$R3" "/L=" $R1 IfErrors +3 StrCpy $LANGUAGE $R1 Goto skip_lang @@ -1331,7 +1339,7 @@ skip_lang: ClearErrors - ${GetOptions} "$R0" "/DS=" $R1 + ${GetOptions} "$R3" "/DS=" $R1 IfErrors +8 SectionGetFlags ${SecDesktopShortcut} $R2 StrCmp "1" $R1 0 +2 @@ -1342,7 +1350,7 @@ SectionSetFlags ${SecDesktopShortcut} $R2 ClearErrors - ${GetOptions} "$R0" "/SMS=" $R1 + ${GetOptions} "$R3" "/SMS=" $R1 IfErrors +8 SectionGetFlags ${SecStartMenuShortcut} $R2 StrCmp "1" $R1 0 +2 @@ -1379,6 +1387,7 @@ instdir_done: ;LogSet on + Pop $R3 Pop $R2 Pop $R1 Pop $R0 @@ -1693,6 +1702,7 @@ Push $R1 Push $R2 Push $R3 + Push $R4 check: ClearErrors @@ -1713,7 +1723,12 @@ StrCmp $R3 "success" +3 StrCpy $R0 $R3 Goto done + ; Use a specific temporary $OUTDIR for each dictionary because the installer doesn't clean up after itself + StrCpy $R4 "$OUTDIR" + SetOutPath "$TEMP\aspell_dict-$R0" ExecWait '"$R1"' + SetOutPath "$R4" + RMDir /r "$TEMP\aspell_dict-$R0" Delete $R1 Goto check ; Check that it is now installed correctly @@ -1722,6 +1737,7 @@ StrCpy $R0 '' done: + Pop $R4 Pop $R3 Pop $R2 Pop $R1
--- a/po/fi.po Fri Feb 06 23:27:45 2009 +0000 +++ b/po/fi.po Fri Feb 06 23:27:49 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 <timo.jyrinki@iki.fi>\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."
--- a/po/l10n.xsl Fri Feb 06 23:27:45 2009 +0000 +++ b/po/l10n.xsl Fri Feb 06 23:27:49 2009 +0000 @@ -1,22 +1,47 @@ <?xml version='1.0' ?> <xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> + <xsl:output + method="html" + omit-xml-declaration="yes" + doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" + doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" + indent="yes" + /> <xsl:template match='/project'> - <html> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title><xsl:value-of select='@name'/> translation statistics</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <!-- <link rel="Stylesheet" href="/gaim.css" type="text/css" media="screen" /> --> - <style> + <style type="text/css"> .bargraph { width: 200px; height: 20px; - background: black; + background-color: red; border-collapse: collapse; border-spacing: 0px; margin: 0px; border: 0px; padding: 0px; } - + .translated { + background-color: green; + padding: 0px; + } + .fuzzy { + background-color: blue; + padding: 0px; + } + .untranslated { + background-color: red; + padding: 0px; + } + td.sep { + padding-right: 10px; + } + th { + text-align: left; + } </style> </head> <body> @@ -28,21 +53,23 @@ <xsl:sort select='@code' /> <tr> <td><a><xsl:attribute name='href'><xsl:value-of select='@code'/>.po</xsl:attribute><xsl:value-of select='@name'/> (<xsl:value-of select='@code'/>)</a></td> - <td><xsl:value-of select='@translated'/></td><td><xsl:value-of select="format-number(@translated div ../@strings * 100,'#.##')"/> %</td> - <td><xsl:value-of select='@fuzzy'/></td><td><xsl:value-of select="format-number(@fuzzy div ../@strings * 100,'#.##')"/> %</td> - <td><xsl:value-of select='../@strings - (@translated + @fuzzy)'/></td><td><xsl:value-of select="format-number((../@strings - (@translated + @fuzzy)) div ../@strings * 100,'#.##')"/> %</td> + <td><xsl:value-of select='@translated'/></td> + <td class='sep'><xsl:value-of select="format-number(@translated div ../@strings * 100,'#.##')"/> %</td> + <td><xsl:value-of select='@fuzzy'/></td> + <td class='sep'><xsl:value-of select="format-number(@fuzzy div ../@strings * 100,'#.##')"/> %</td> + <td><xsl:value-of select='../@strings - (@translated + @fuzzy)'/></td> + <td><xsl:value-of select="format-number((../@strings - (@translated + @fuzzy)) div ../@strings * 100,'#.##')"/> %</td> <td> <table class='bargraph'><tr> - <td bgcolor='green'><xsl:attribute name='width'><xsl:value-of select='round(@translated div ../@strings * 200)'/>px;</xsl:attribute></td> - <td bgcolor='blue'><xsl:attribute name='width'><xsl:value-of select='round(@fuzzy div ../@strings * 200)'/>px;</xsl:attribute></td> - <!-- <td bgcolor='red'><xsl:attribute name='width'><xsl:value-of select='200 - round((@translated + @fuzzy) div ../@strings * 200)'/>px;</xsl:attribute></td> --> - <td bgcolor='red'></td> + <td class="translated"><xsl:attribute name='style'>width:<xsl:value-of select='round(@translated div ../@strings * 200)'/>px;</xsl:attribute></td> + <td class="fuzzy"><xsl:attribute name='style'>width:<xsl:value-of select='round(@fuzzy div ../@strings * 200)'/>px;</xsl:attribute></td> + <td class="untranslated"><xsl:attribute name='style'>width:<xsl:value-of select='round((../@strings - @translated - @fuzzy) div ../@strings * 200)'/>px;</xsl:attribute></td> </tr></table> </td> </tr> </xsl:for-each> </table> - <a><xsl:attribute name='href'><xsl:value-of select='@pofile'/></xsl:attribute><xsl:value-of select='@pofile'/></a> generated on <xsl:value-of select='@generated'/> + <p><a><xsl:attribute name='href'><xsl:value-of select='@pofile'/></xsl:attribute><xsl:value-of select='@pofile'/></a> generated on <xsl:value-of select='@generated'/></p> <!-- </div> --> </body> </html>
--- a/share/ca-certs/Makefile.am Fri Feb 06 23:27:45 2009 +0000 +++ b/share/ca-certs/Makefile.am Fri Feb 06 23:27:49 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/VeriSign_International_Server_Class_3_CA.pem Fri Feb 06 23:27:49 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-----