# HG changeset patch # User Yoshiki Yazawa # Date 1188576618 0 # Node ID ee5551542eee95f1ecb5dbc373755f9c35d40de3 # Parent 7f8cf35fc99bc23e237f6c5ea464501a3230c4a8# Parent c07a9c6f0263ea48c7bf999b0d3505b5c8a293d2 propagate from branch 'im.pidgin.pidgin' (head 6f5c855ee2435b317e7ddb8c9551fd17cd0328fe) to branch 'im.pidgin.pidgin.yaz' (head 017ee71808fe6a5473018d15f37ae1de046eb3e2) diff -r 7f8cf35fc99b -r ee5551542eee ChangeLog.API --- a/ChangeLog.API Tue Aug 28 09:20:27 2007 +0000 +++ b/ChangeLog.API Fri Aug 31 16:10:18 2007 +0000 @@ -1,6 +1,15 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul Version 2.2.0 (??/??/????): + libpurple: + Added: + * PURPLE_MESSAGE_INVISIBLE flag, which can be used by + purple_conv_im_send_with_flags to send a message, but not display it + in the conversation + Changed: + * purple_prefs_load is now called within purple_prefs_init. + The UI no longer needs to call it. + Pidgin: Added: * pidgin_set_accessible_relations, sets up label-for and labelled-by diff -r 7f8cf35fc99b -r ee5551542eee ChangeLog.win32 --- a/ChangeLog.win32 Tue Aug 28 09:20:27 2007 +0000 +++ b/ChangeLog.win32 Fri Aug 31 16:10:18 2007 +0000 @@ -1,5 +1,9 @@ +version 2.2.0 (??/??/2007): + * Updated gtkspell to 2.0.11 + * Upgrade SILC to use the 1.1.2 toolkit + version 2.1.1 (08/20/2007): - * No changes + * No changes version 2.1.0 (7/28/2007): * Updated launcher application (pidgin.exe) to support portable mode diff -r 7f8cf35fc99b -r ee5551542eee finch/Makefile.am --- a/finch/Makefile.am Tue Aug 28 09:20:27 2007 +0000 +++ b/finch/Makefile.am Fri Aug 31 16:10:18 2007 +0000 @@ -14,6 +14,7 @@ finch_SOURCES = \ gntaccount.c \ gntblist.c \ + gntcertmgr.c \ gntconn.c \ gntconv.c \ gntdebug.c \ @@ -32,6 +33,7 @@ finch_headers = \ gntaccount.h \ gntblist.h \ + gntcertmgr.h \ gntconn.h \ gntconv.h \ gntdebug.h \ diff -r 7f8cf35fc99b -r ee5551542eee finch/finch.c --- a/finch/finch.c Tue Aug 28 09:20:27 2007 +0000 +++ b/finch/finch.c Fri Aug 31 16:10:18 2007 +0000 @@ -360,9 +360,7 @@ purple_set_blist(purple_blist_new()); purple_blist_load(); - /* TODO: Move prefs loading into purple_prefs_init() */ - purple_prefs_load(); - purple_prefs_update_old(); + /* TODO: should this be moved into finch_prefs_init() ? */ finch_prefs_update_old(); /* load plugins we had when we quit */ diff -r 7f8cf35fc99b -r ee5551542eee finch/gntcertmgr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/gntcertmgr.c Fri Aug 31 16:10:18 2007 +0000 @@ -0,0 +1,340 @@ +/** + * @file gntcertmgr.c GNT Certificate Manager API + * @ingroup finch + * + * finch + * + * Finch is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" + +#include "certificate.h" +#include "debug.h" +#include "notify.h" +#include "request.h" + +#include "finch.h" +#include "gntcertmgr.h" + +#include "gntbutton.h" +#include "gntlabel.h" +#include "gnttree.h" +#include "gntutils.h" +#include "gntwindow.h" + +struct { + GntWidget *window; + GntWidget *tree; + PurpleCertificatePool *pool; +} certmgr; + +/* Pretty much Xerox of gtkcertmgr */ + +/* Add certificate */ +static void +tls_peers_mgmt_import_ok2_cb(gpointer data, const char *result) +{ + PurpleCertificate *crt = (PurpleCertificate *) data; + const char *id = result; + + /* TODO: Perhaps prompt if you're overwriting a cert? */ + + purple_certificate_pool_store(purple_certificate_find_pool("x509", "tls_peers"), id, crt); + purple_certificate_destroy(crt); +} + +static void +tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result) +{ + PurpleCertificate *crt = (PurpleCertificate *) data; + purple_certificate_destroy(crt); +} + +static void +tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename) +{ + PurpleCertificateScheme *x509; + PurpleCertificate *crt; + + x509 = purple_certificate_pool_get_scheme(purple_certificate_find_pool("x509", "tls_peers")); + + crt = purple_certificate_import(x509, filename); + + if (crt != NULL) { + gchar *default_hostname; + default_hostname = purple_certificate_get_subject_name(crt); + purple_request_input(NULL, + _("Certificate Import"), + _("Specify a hostname"), + _("Type the host name this certificate is for."), + default_hostname, FALSE, FALSE, NULL, + _("OK"), G_CALLBACK(tls_peers_mgmt_import_ok2_cb), + _("Cancel"), G_CALLBACK(tls_peers_mgmt_import_cancel2_cb), + NULL, NULL, NULL, + crt); + g_free(default_hostname); + } else { + gchar * secondary; + secondary = g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable and in PEM format.\n"), filename); + purple_notify_error(NULL, + _("Certificate Import Error"), + _("X.509 certificate import failed"), + secondary); + g_free(secondary); + } +} + +static void +add_cert_cb(GntWidget *button, gpointer null) +{ + purple_request_file(NULL, + _("Select a PEM certificate"), + "certificate.pem", + FALSE, + G_CALLBACK(tls_peers_mgmt_import_ok_cb), + NULL, + NULL, NULL, NULL, NULL ); +} + +/* Save certs in some file */ +static void +tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename) +{ + PurpleCertificate *crt = (PurpleCertificate *) data; + + if (!purple_certificate_export(filename, crt)) { + gchar * secondary; + + secondary = g_strdup_printf(_("Export to file %s failed.\nCheck that you have write permission to the target path\n"), filename); + purple_notify_error(NULL, + _("Certificate Export Error"), + _("X.509 certificate export failed"), + secondary); + g_free(secondary); + } + + purple_certificate_destroy(crt); +} + +static void +save_cert_cb(GntWidget *button, gpointer null) +{ + PurpleCertificate *crt; + const char *key; + + if (!certmgr.window) + return; + + key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree)); + if (!key) + return; + + crt = purple_certificate_pool_retrieve(certmgr.pool, key); + if (!crt) { + purple_debug_error("gntcertmgr/tls_peers_mgmt", + "Id %s was not in the peers cache?!\n", key); + return; + } + + purple_request_file((void*)key, + _("PEM X.509 Certificate Export"), + "certificate.pem", TRUE, + G_CALLBACK(tls_peers_mgmt_export_ok_cb), + G_CALLBACK(purple_certificate_destroy), + NULL, NULL, NULL, + crt); +} + +/* Show information about a cert */ +static void +info_cert_cb(GntWidget *button, gpointer null) +{ + const char *key; + PurpleCertificate *crt; + gchar *subject; + GByteArray *fpr_sha1; + gchar *fpr_sha1_asc; + gchar *primary, *secondary; + + if (!certmgr.window) + return; + + key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree)); + if (!key) + return; + + crt = purple_certificate_pool_retrieve(certmgr.pool, key); + g_return_if_fail(crt); + + primary = g_strdup_printf(_("Certificate for %s"), key); + + fpr_sha1 = purple_certificate_get_fingerprint_sha1(crt); + fpr_sha1_asc = purple_base16_encode_chunked(fpr_sha1->data, + fpr_sha1->len); + subject = purple_certificate_get_subject_name(crt); + + secondary = g_strdup_printf(_("Common name: %s\n\nSHA1 fingerprint:\n%s"), subject, fpr_sha1_asc); + + purple_notify_info(NULL, + _("SSL Host Certificate"), primary, secondary); + + g_free(primary); + g_free(secondary); + g_byte_array_free(fpr_sha1, TRUE); + g_free(fpr_sha1_asc); + g_free(subject); + purple_certificate_destroy(crt); +} + +/* Delete a cert */ +static void +tls_peers_mgmt_delete_confirm_cb(gchar *id, gint dontcare) +{ + if (!purple_certificate_pool_delete(certmgr.pool, id)) { + purple_debug_warning("gntcertmgr/tls_peers_mgmt", + "Deletion failed on id %s\n", id); + }; + + g_free(id); +} + +static void +delete_cert_cb(GntWidget *button, gpointer null) +{ + gchar *primary; + const char *key; + + if (!certmgr.window) + return; + + key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree)); + if (!key) + return; + + primary = g_strdup_printf(_("Really delete certificate for %s?"), key); + + purple_request_close_with_handle((void *)key); + purple_request_yes_no((void *)key, _("Confirm certificate delete"), + primary, NULL, + 2, + NULL, NULL, NULL, + g_strdup(key), + tls_peers_mgmt_delete_confirm_cb, + g_free); + + g_free(primary); +} + +/* populate the list */ +static void +populate_cert_list() +{ + GList *idlist, *l; + + if (!certmgr.window) + return; + + gnt_tree_remove_all(GNT_TREE(certmgr.tree)); + + idlist = purple_certificate_pool_get_idlist(purple_certificate_find_pool("x509", "tls_peers")); + for (l = idlist; l; l = l->next) { + gnt_tree_add_row_last(GNT_TREE(certmgr.tree), g_strdup(l->data), + gnt_tree_create_row(GNT_TREE(certmgr.tree), l->data), NULL); + } + purple_certificate_pool_destroy_idlist(idlist); +} + +static void +cert_list_added(PurpleCertificatePool *pool, const char *id, gpointer null) +{ + g_return_if_fail(certmgr.window); + gnt_tree_add_row_last(GNT_TREE(certmgr.tree), g_strdup(id), + gnt_tree_create_row(GNT_TREE(certmgr.tree), id), NULL); +} + +static void +cert_list_removed(PurpleCertificatePool *pool, const char *id, gpointer null) +{ + g_return_if_fail(certmgr.window); + purple_request_close_with_handle((void*)id); + gnt_tree_remove(GNT_TREE(certmgr.tree), (void*)id); +} + +void finch_certmgr_show(void) +{ + GntWidget *win, *tree, *box, *button; + PurpleCertificatePool *pool; + + if (certmgr.window) { + gnt_window_present(certmgr.window); + return; + } + + certmgr.window = win = gnt_vwindow_new(FALSE); + gnt_box_set_title(GNT_BOX(win), _("Certificate Manager")); + gnt_box_set_pad(GNT_BOX(win), 0); + + certmgr.tree = tree = gnt_tree_new(); + gnt_tree_set_hash_fns(GNT_TREE(tree), g_str_hash, g_str_equal, g_free); + gnt_tree_set_column_title(GNT_TREE(tree), 0, _("Hostname")); + gnt_tree_set_show_title(GNT_TREE(tree), TRUE); + + gnt_box_add_widget(GNT_BOX(win), tree); + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(win), box); + + button = gnt_button_new(_("Add")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(add_cert_cb), NULL); + gnt_util_set_trigger_widget(GNT_WIDGET(tree), GNT_KEY_INS, button); + + button = gnt_button_new(_("Save")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_cert_cb), NULL); + + button = gnt_button_new(_("Info")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(info_cert_cb), NULL); + + button = gnt_button_new(_("Delete")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(delete_cert_cb), NULL); + gnt_util_set_trigger_widget(GNT_WIDGET(tree), GNT_KEY_DEL, button); + + button = gnt_button_new(_("Close")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), win); + + g_signal_connect_swapped(G_OBJECT(win), "destroy", G_CALLBACK(g_nullify_pointer), &certmgr.window); + + populate_cert_list(); + + pool = certmgr.pool = purple_certificate_find_pool("x509", "tls_peers"); + purple_signal_connect(pool, "certificate-stored", + win, PURPLE_CALLBACK(cert_list_added), NULL); + purple_signal_connect(pool, "certificate-deleted", + win, PURPLE_CALLBACK(cert_list_removed), NULL); + g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(purple_signals_disconnect_by_handle), NULL); + + gnt_widget_show(certmgr.window); +} + diff -r 7f8cf35fc99b -r ee5551542eee finch/gntcertmgr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/gntcertmgr.h Fri Aug 31 16:10:18 2007 +0000 @@ -0,0 +1,31 @@ +/** + * @file gntcertmgr.h GNT Certificate Manager API + * @ingroup finch + * + * finch + * + * Finch is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _GNT_CERTMGR_H +#define _GNT_CERTMGR_H + +void finch_certmgr_show(void); + +#endif diff -r 7f8cf35fc99b -r ee5551542eee finch/gntconv.c --- a/finch/gntconv.c Tue Aug 28 09:20:27 2007 +0000 +++ b/finch/gntconv.c Fri Aug 31 16:10:18 2007 +0000 @@ -1006,6 +1006,7 @@ { FinchConv *ggconv = conv->ui_data; gnt_text_view_clear(GNT_TEXT_VIEW(ggconv->tv)); + purple_conversation_clear_message_history(conv); return PURPLE_CMD_STATUS_OK; } diff -r 7f8cf35fc99b -r ee5551542eee finch/gntrequest.c --- a/finch/gntrequest.c Tue Aug 28 09:20:27 2007 +0000 +++ b/finch/gntrequest.c Fri Aug 31 16:10:18 2007 +0000 @@ -198,7 +198,7 @@ static void * finch_request_choice(const char *title, const char *primary, - const char *secondary, unsigned int default_value, + const char *secondary, int default_value, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, PurpleAccount *account, const char *who, PurpleConversation *conv, @@ -244,7 +244,7 @@ static void* finch_request_action(const char *title, const char *primary, - const char *secondary, unsigned int default_value, + const char *secondary, int default_value, PurpleAccount *account, const char *who, PurpleConversation *conv, void *user_data, size_t actioncount, va_list actions) diff -r 7f8cf35fc99b -r ee5551542eee finch/gntui.c --- a/finch/gntui.c Tue Aug 28 09:20:27 2007 +0000 +++ b/finch/gntui.c Fri Aug 31 16:10:18 2007 +0000 @@ -25,6 +25,7 @@ #include "gntaccount.h" #include "gntblist.h" +#include "gntcertmgr.h" #include "gntconn.h" #include "gntconv.h" #include "gntdebug.h" @@ -81,6 +82,7 @@ gnt_register_action(_("Accounts"), finch_accounts_show_all); gnt_register_action(_("Buddy List"), finch_blist_show); gnt_register_action(_("Buddy Pounces"), finch_pounces_manager_show); + gnt_register_action(_("Certificates"), finch_certmgr_show); gnt_register_action(_("Debug Window"), finch_debug_window_show); gnt_register_action(_("File Transfers"), finch_xfer_dialog_show); gnt_register_action(_("Plugins"), finch_plugins_show_all); diff -r 7f8cf35fc99b -r ee5551542eee finch/libgnt/pygnt/dbus-gnt --- a/finch/libgnt/pygnt/dbus-gnt Tue Aug 28 09:20:27 2007 +0000 +++ b/finch/libgnt/pygnt/dbus-gnt Fri Aug 31 16:10:18 2007 +0000 @@ -13,7 +13,7 @@ import gnt import sys -from time import strftime +import time convwins = {} @@ -27,19 +27,26 @@ # if a conv window is closed, then reopened, this thing crashes convwins[key] = None -def wrote_msg(account, who, msg, conv, flags): - stuff = show_conversation(conv) +def add_message(conv, who, msg, flags, timestamp): + stuff = show_conversation(conv, False) tv = stuff[1] tv.append_text_with_flags("\n", 0) - tv.append_text_with_flags(strftime("(%X) "), 8) + if timestamp: + tv.append_text_with_flags(time.strftime("(%X) ", time.localtime(timestamp)), 8) + else: + tv.append_text_with_flags(time.strftime("(%X) "), 8) if flags & 3: tv.append_text_with_flags(who + ": ", 1) + msg = purple.PurpleMarkupStripHtml(msg) tv.append_text_with_flags(msg, 0) stuff[0].set_urgent() else: tv.append_text_with_flags(msg, 8) tv.scroll(0) +def wrote_msg(account, who, msg, conv, flags): + add_message(conv, who, msg, flags, None) + bus = dbus.SessionBus() obj = bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject") purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface") @@ -79,7 +86,7 @@ def conv_window_destroyed(win, key): del convwins[key] -def show_conversation(conv): +def show_conversation(conv, history): key = get_dict_key(conv) if key in convwins: return convwins[key] @@ -96,9 +103,21 @@ vbox.add_widget(entry) entry.connect("key_pressed", send_im_cb, conv) tv.clear() + tv.attach_scroll_widget(entry) win.show() convwins[key] = [win, tv, entry] win.connect("destroy", conv_window_destroyed, key) + + if history: + msgs = purple.PurpleConversationGetMessageHistory(conv) + msgs.reverse() + for msg in msgs: + who = purple.PurpleConversationMessageGetSender(msg) + what = purple.PurpleConversationMessageGetMessage(msg) + flags = purple.PurpleConversationMessageGetFlags(msg) + when = purple.PurpleConversationMessageGetTimestamp(msg) + add_message(conv, who, what, flags, when) + return convwins[key] def show_buddylist(): @@ -127,7 +146,7 @@ convs = purple.PurpleGetConversations() for conv in convs: - show_conversation(conv) + show_conversation(conv, True) gnt.gnt_main() diff -r 7f8cf35fc99b -r ee5551542eee finch/libgnt/pygnt/gendef.sh --- a/finch/libgnt/pygnt/gendef.sh Tue Aug 28 09:20:27 2007 +0000 +++ b/finch/libgnt/pygnt/gendef.sh Fri Aug 31 16:10:18 2007 +0000 @@ -31,7 +31,9 @@ rm -f gnt.def for file in $FILES do + echo -n "Generating definitions for ${file} ... " python /usr/share/pygtk/2.0/codegen/h2def.py ../$file >> gnt.def + echo "Done" done # Remove the definitions about the enums diff -r 7f8cf35fc99b -r ee5551542eee libpurple/Makefile.am --- a/libpurple/Makefile.am Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/Makefile.am Fri Aug 31 16:10:18 2007 +0000 @@ -22,9 +22,9 @@ win32/giowin32.c \ win32/win32dep.h -# if USE_GCONFTOOL -# GCONF_DIR=gconf -# endif +if USE_GCONFTOOL +GCONF_DIR=gconf +endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = purple.pc diff -r 7f8cf35fc99b -r ee5551542eee libpurple/certificate.c --- a/libpurple/certificate.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/certificate.c Fri Aug 31 16:10:18 2007 +0000 @@ -30,6 +30,7 @@ #include "internal.h" #include "certificate.h" +#include "dbus-maybe.h" #include "debug.h" #include "request.h" #include "signals.h" @@ -1124,6 +1125,18 @@ } } +static void +x509_tls_cached_user_auth_accept_cb(x509_tls_cached_ua_ctx *c, gint ignore) +{ + x509_tls_cached_user_auth_cb(c, 2); +} + +static void +x509_tls_cached_user_auth_reject_cb(x509_tls_cached_ua_ctx *c, gint ignore) +{ + x509_tls_cached_user_auth_cb(c, 1); +} + /** Validates a certificate by asking the user * @param reason String to explain why the user needs to accept/refuse the * certificate. @@ -1151,8 +1164,8 @@ NULL, /* No associated conversation */ x509_tls_cached_ua_ctx_new(vrq, reason), 3, /* Number of actions */ - _("Yes"), x509_tls_cached_user_auth_cb, - _("No"), x509_tls_cached_user_auth_cb, + _("Accept"), x509_tls_cached_user_auth_accept_cb, + _("Reject"), x509_tls_cached_user_auth_reject_cb, _("_View Certificate..."), x509_tls_cached_show_cert); /* Cleanup */ @@ -1192,7 +1205,14 @@ /* Load up the cached certificate */ cached_crt = purple_certificate_pool_retrieve( tls_peers, vrq->subject_name); - g_assert(cached_crt); + if ( !cached_crt ) { + purple_debug_error("certificate/x509/tls_cached", + "Lookup failed on cached certificate!\n" + "It was here just a second ago. Forwarding " + "to cert_changed.\n"); + /* vrq now becomes the problem of cert_changed */ + x509_tls_cached_peer_cert_changed(vrq); + } /* Now get SHA1 sums for both and compare them */ /* TODO: This is not an elegant way to compare certs */ @@ -1325,7 +1345,14 @@ ca_crt = purple_certificate_pool_retrieve(ca, ca_id); g_free(ca_id); - g_assert(ca_crt); + if (!ca_crt) { + purple_debug_error("certificate/x509/tls_cached", + "Certificate authority disappeared out " + "underneath me!\n"); + purple_certificate_verify_complete(vrq, + PURPLE_CERTIFICATE_INVALID); + return; + } /* Check the signature */ if ( !purple_certificate_signed_by(end_crt, ca_crt) ) { @@ -1362,9 +1389,11 @@ "tls_peers"); if (tls_peers) { - g_assert(purple_certificate_pool_store(tls_peers, - vrq->subject_name, - peer_crt) ); + if (!purple_certificate_pool_store(tls_peers,vrq->subject_name, + peer_crt) ) { + purple_debug_error("certificate/x509/tls_cached", + "FAILED to cache peer certificate\n"); + } } else { purple_debug_error("certificate/x509/tls_cached", "Unable to locate tls_peers certificate " @@ -1700,6 +1729,7 @@ /* TODO: Emit a signal that the pool got registered */ + PURPLE_DBUS_REGISTER_POINTER(pool, PurpleCertificatePool); purple_signal_register(pool, /* Signals emitted from pool */ "certificate-stored", purple_marshal_VOID__POINTER_POINTER, @@ -1748,6 +1778,7 @@ } /* Uninit the pool if needed */ + PURPLE_DBUS_UNREGISTER_POINTER(pool); if (pool->uninit) { pool->uninit(); } @@ -1775,7 +1806,6 @@ GByteArray *sha_bin; gchar *cn; time_t activation, expiration; - /* Length of these buffers is dictated by 'man ctime_r' */ gchar *activ_str, *expir_str; gchar *secondary; @@ -1792,7 +1822,11 @@ /* Get the certificate times */ /* TODO: Check the times against localtime */ /* TODO: errorcheck? */ - g_assert(purple_certificate_get_times(crt, &activation, &expiration)); + if (!purple_certificate_get_times(crt, &activation, &expiration)) { + purple_debug_error("certificate", + "Failed to get certificate times!\n"); + activation = expiration = 0; + } activ_str = g_strdup(ctime(&activation)); expir_str = g_strdup(ctime(&expiration)); @@ -1819,6 +1853,3 @@ g_byte_array_free(sha_bin, TRUE); } - - - diff -r 7f8cf35fc99b -r ee5551542eee libpurple/conversation.c --- a/libpurple/conversation.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/conversation.c Fri Aug 31 16:10:18 2007 +0000 @@ -39,6 +39,7 @@ static GList *ims = NULL; static GList *chats = NULL; static PurpleConversationUiOps *default_ops = NULL; +static GHashTable *histories = NULL; void purple_conversations_set_ui_ops(PurpleConversationUiOps *ops) @@ -111,17 +112,17 @@ /* Always linkfy the text for display, unless we're * explicitly asked to do otheriwse*/ - if(msgflags & PURPLE_MESSAGE_NO_LINKIFY) - displayed = g_strdup(message); - else - displayed = purple_markup_linkify(message); - - if ((conv->features & PURPLE_CONNECTION_HTML) && - !(msgflags & PURPLE_MESSAGE_RAW)) - { + if (!(msgflags & PURPLE_MESSAGE_INVISIBLE)) { + if(msgflags & PURPLE_MESSAGE_NO_LINKIFY) + displayed = g_strdup(message); + else + displayed = purple_markup_linkify(message); + } + + if (displayed && (conv->features & PURPLE_CONNECTION_HTML) && + !(msgflags & PURPLE_MESSAGE_RAW)) { sent = g_strdup(displayed); - } - else + } else sent = g_strdup(message); msgflags |= PURPLE_MESSAGE_SEND; @@ -202,6 +203,51 @@ conv, time(NULL), NULL)); } +/* Functions that deal with PurpleConvMessage */ + +static void +add_message_to_history(PurpleConversation *conv, const char *who, const char *message, + PurpleMessageFlags flags, time_t when) +{ + GList *list; + PurpleConvMessage *msg; + + if (flags & PURPLE_MESSAGE_SEND) { + const char *me = NULL; + if (conv->account->gc) + me = conv->account->gc->display_name; + if (!me) + me = conv->account->username; + who = me; + } + + msg = g_new0(PurpleConvMessage, 1); + PURPLE_DBUS_REGISTER_POINTER(msg, PurpleConvMessage); + msg->who = g_strdup(who); + msg->flags = flags; + msg->what = g_strdup(message); + msg->when = when; + + list = g_hash_table_lookup(histories, conv); + list = g_list_prepend(list, msg); + g_hash_table_insert(histories, conv, list); +} + +static void +free_conv_message(PurpleConvMessage *msg) +{ + g_free(msg->who); + g_free(msg->what); + PURPLE_DBUS_UNREGISTER_POINTER(msg); + g_free(msg); +} + +static void +message_history_free(GList *list) +{ + g_list_foreach(list, (GFunc)free_conv_message, NULL); + g_list_free(list); +} /************************************************************************** * Conversation API @@ -473,6 +519,8 @@ purple_conversation_close_logs(conv); + purple_conversation_clear_message_history(conv); + PURPLE_DBUS_UNREGISTER_POINTER(conv); g_free(conv); conv = NULL; @@ -805,9 +853,6 @@ ops = purple_conversation_get_ui_ops(conv); - if (ops == NULL || ops->write_conv == NULL) - return; - account = purple_conversation_get_account(conv); type = purple_conversation_get_type(conv); @@ -891,7 +936,9 @@ } } - ops->write_conv(conv, who, alias, displayed, flags, mtime); + if (ops && ops->write_conv) + ops->write_conv(conv, who, alias, displayed, flags, mtime); + add_message_to_history(conv, who, message, flags, mtime); purple_signal_emit(purple_conversations_get_handle(), (type == PURPLE_CONV_TYPE_IM ? "wrote-im-msg" : "wrote-chat-msg"), @@ -2030,6 +2077,42 @@ return menu; } +void purple_conversation_clear_message_history(PurpleConversation *conv) +{ + GList *list = g_hash_table_lookup(histories, conv); + message_history_free(list); + g_hash_table_remove(histories, conv); +} + +GList *purple_conversation_get_message_history(PurpleConversation *conv) +{ + return g_hash_table_lookup(histories, conv); +} + +const char *purple_conversation_message_get_sender(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, NULL); + return msg->who; +} + +const char *purple_conversation_message_get_message(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, NULL); + return msg->what; +} + +PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, 0); + return msg->flags; +} + +time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg) +{ + g_return_val_if_fail(msg, 0); + return msg->when; +} + gboolean purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **error) @@ -2310,6 +2393,9 @@ purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONVERSATION), purple_value_new(PURPLE_TYPE_BOXED, "GList **")); + + /* Initialize the history */ + histories = g_hash_table_new(g_direct_hash, g_direct_equal); } void @@ -2318,4 +2404,6 @@ while (conversations) purple_conversation_destroy((PurpleConversation*)conversations->data); purple_signals_unregister_by_instance(purple_conversations_get_handle()); + g_hash_table_destroy(histories); + histories = NULL; } diff -r 7f8cf35fc99b -r ee5551542eee libpurple/conversation.h --- a/libpurple/conversation.h Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/conversation.h Fri Aug 31 16:10:18 2007 +0000 @@ -37,6 +37,7 @@ typedef struct _PurpleConvIm PurpleConvIm; typedef struct _PurpleConvChat PurpleConvChat; typedef struct _PurpleConvChatBuddy PurpleConvChatBuddy; +typedef struct _PurpleConvMessage PurpleConvMessage; /** * A type of conversation. @@ -117,9 +118,9 @@ apply formatting */ PURPLE_MESSAGE_IMAGES = 0x1000, /**< Message contains images */ PURPLE_MESSAGE_NOTIFY = 0x2000, /**< Message is a notification */ - PURPLE_MESSAGE_NO_LINKIFY = 0x4000 /**< Message should not be auto- + PURPLE_MESSAGE_NO_LINKIFY = 0x4000, /**< Message should not be auto- linkified */ - + PURPLE_MESSAGE_INVISIBLE = 0x8000, /**< Message should not be displayed */ } PurpleMessageFlags; /** @@ -283,6 +284,17 @@ }; /** + * Description of a conversation message + */ +struct _PurpleConvMessage +{ + char *who; + char *what; + PurpleMessageFlags flags; + time_t when; +}; + +/** * A core representation of a conversation between two or more people. * * The conversation can be an IM or a chat. @@ -650,6 +662,60 @@ */ void purple_conversation_foreach(void (*func)(PurpleConversation *conv)); +/** + * Retrieve the message history of a conversation. + * + * @param conv The conversation + * + * @return A GList of PurpleConvMessage's. The must not modify the list or the data within. + * The list contains the newest message at the beginning, and the oldest message at + * the end. + */ +GList *purple_conversation_get_message_history(PurpleConversation *conv); + +/** + * Clear the message history of a conversation. + * + * @param conv The conversation + */ +void purple_conversation_clear_message_history(PurpleConversation *conv); + +/** + * Get the sender from a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +const char *purple_conversation_message_get_sender(PurpleConvMessage *msg); + +/** + * Get the message from a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +const char *purple_conversation_message_get_message(PurpleConvMessage *msg); + +/** + * Get the message-flags of a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +PurpleMessageFlags purple_conversation_message_get_flags(PurpleConvMessage *msg); + +/** + * Get the timestamp of a PurpleConvMessage + * + * @param msg A PurpleConvMessage + * + * @return The name of the sender of the message + */ +time_t purple_conversation_message_get_timestamp(PurpleConvMessage *msg); + /*@}*/ diff -r 7f8cf35fc99b -r ee5551542eee libpurple/dbus-analyze-functions.py --- a/libpurple/dbus-analyze-functions.py Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/dbus-analyze-functions.py Fri Aug 31 16:10:18 2007 +0000 @@ -66,6 +66,7 @@ "purple_savedstatuses_get_all", "purple_status_type_get_attrs", "purple_presence_get_statuses", + "purple_conversation_get_message_history", ] pointer = "#pointer#" diff -r 7f8cf35fc99b -r ee5551542eee libpurple/example/nullclient.c --- a/libpurple/example/nullclient.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/example/nullclient.c Fri Aug 31 16:10:18 2007 +0000 @@ -197,7 +197,7 @@ purple_util_set_user_dir(CUSTOM_USER_DIRECTORY); /* We do not want any debugging for now to keep the noise to a minimum. */ - purple_debug_set_enabled(TRUE); + purple_debug_set_enabled(FALSE); /* Set the core-uiops, which is used to * - initialize the ui specific preferences. @@ -257,24 +257,6 @@ PURPLE_CALLBACK(signed_on), NULL); } - - - -void signedOn( PurpleConnection *gc, gpointer dummy ) { - - - if( gc ) { - - PurpleAccount* a = purple_connection_get_account( gc ); - - if( a ) { - - purple_presence_set_idle( purple_account_get_presence( a ), TRUE, time( NULL ) ); - } - } -} - - int main() { GList *iter; @@ -300,26 +282,30 @@ names = g_list_append(names, info->id); } } + printf("Select the protocol [0-%d]: ", i-1); + fgets(name, sizeof(name), stdin); + sscanf(name, "%d", &num); + prpl = g_list_nth_data(names, num); + + printf("Username: "); + fgets(name, sizeof(name), stdin); + name[strlen(name) - 1] = 0; /* strip the \n at the end */ /* Create the account */ - account = purple_account_new("msimprpl@xyzzy.cjb.net", "prpl-myspace" ); - purple_account_set_password(account, "4224jc" ); + account = purple_account_new(name, prpl); + + /* Get the password for the account */ + password = getpass("Password: "); + purple_account_set_password(account, password); /* It's necessary to enable the account first. */ purple_account_set_enabled(account, UI_ID, TRUE); -#if 0 - static int handle; - purple_signal_connect( purple_connections_get_handle(), - "signed-on", &handle, - PURPLE_CALLBACK( signedOn ), - NULL ); - /* Now, to connect the account(s), create a status and activate it. */ - purple_savedstatus_activate( purple_savedstatus_get_default() ); + status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); + purple_savedstatus_activate(status); connect_to_signals_for_demonstration_purposes_only(); -#endif g_main_loop_run(loop); diff -r 7f8cf35fc99b -r ee5551542eee libpurple/plugins/ssl/ssl-gnutls.c --- a/libpurple/plugins/ssl/ssl-gnutls.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/plugins/ssl/ssl-gnutls.c Fri Aug 31 16:10:18 2007 +0000 @@ -60,7 +60,7 @@ (gnutls_realloc_function) g_realloc, /* realloc */ (gnutls_free_function) g_free /* free */ ); - + gnutls_global_init(); gnutls_certificate_allocate_credentials(&xcred); @@ -73,7 +73,7 @@ static gboolean ssl_gnutls_init(void) { - return TRUE; + return TRUE; } static void @@ -130,17 +130,18 @@ purple_ssl_close(gsc); } else { - purple_debug_info("gnutls", "Handshake complete\n"); - - /* TODO: Remove all this debugging babble */ /* Now we are cooking with gas! */ PurpleSslOps *ops = purple_ssl_get_ops(); GList * peers = ops->get_peer_certificates(gsc); - + PurpleCertificateScheme *x509 = purple_certificate_find_scheme("x509"); GList * l; + + /* TODO: Remove all this debugging babble */ + purple_debug_info("gnutls", "Handshake complete\n"); + for (l=peers; l; l = l->next) { PurpleCertificate *crt = l->data; GByteArray *z = @@ -155,72 +156,71 @@ /* Kill the cert! */ x509->destroy_certificate(crt); - + g_free(fpr); g_byte_array_free(z, TRUE); } g_list_free(peers); - + { - const gnutls_datum_t *cert_list; - unsigned int cert_list_size = 0; - gnutls_session_t session=gnutls_data->session; - - cert_list = - gnutls_certificate_get_peers(session, &cert_list_size); - - purple_debug_info("gnutls", - "Peer provided %d certs\n", - cert_list_size); - int i; - for (i=0; isession; + int i; + + cert_list = + gnutls_certificate_get_peers(session, &cert_list_size); + + purple_debug_info("gnutls", + "Peer provided %d certs\n", + cert_list_size); + for (i=0; isession) == GNUTLS_CRT_X509, NULL); @@ -426,12 +426,14 @@ static void x509_crtdata_delref(x509_crtdata_t *cd) { - g_assert(cd->refcount > 0); - (cd->refcount)--; + if (cd->refcount < 0) + g_critical("Refcount of x509_crtdata_t is %d, which is less " + "than zero!\n", cd->refcount); + /* If the refcount reaches zero, kill the structure */ - if (cd->refcount == 0) { + if (cd->refcount <= 0) { purple_debug_info("gnutls/x509", "Freeing unused cert data at %p\n", cd); @@ -466,11 +468,11 @@ certdat = g_new0(x509_crtdata_t, 1); gnutls_x509_crt_init(&(certdat->crt)); certdat->refcount = 0; - + /* Perform the actual certificate parse */ /* Yes, certdat->crt should be passed as-is */ gnutls_x509_crt_import(certdat->crt, &dt, mode); - + /* Allocate the certificate and load it with data */ crt = g_new0(PurpleCertificate, 1); crt->scheme = &x509_gnutls; @@ -495,7 +497,7 @@ purple_debug_info("gnutls", "Attempting to load X.509 certificate from %s\n", filename); - + /* Next, we'll simply yank the entire contents of the file into memory */ /* TODO: Should I worry about very large files here? */ @@ -506,7 +508,7 @@ NULL /* No error checking for now */ ), NULL); - + /* Load the datum struct */ dt.data = (unsigned char *) buf; dt.size = buf_sz; @@ -514,7 +516,7 @@ /* Perform the conversion */ crt = x509_import_from_datum(dt, GNUTLS_X509_FMT_PEM); // files should be in PEM format - + /* Cleanup */ g_free(buf); @@ -571,7 +573,6 @@ success = purple_util_write_data_to_file_absolute(filename, out_buf, out_size); - g_free(out_buf); g_return_val_if_fail(success, FALSE); return success; @@ -596,10 +597,10 @@ } /** Frees a Certificate * - * Destroys a Certificate's internal data structures and frees the pointer - * given. - * @param crt Certificate instance to be destroyed. It WILL NOT be destroyed - * if it is not of the correct CertificateScheme. Can be NULL + * Destroys a Certificate's internal data structures and frees the pointer + * given. + * @param crt Certificate instance to be destroyed. It WILL NOT be destroyed + * if it is not of the correct CertificateScheme. Can be NULL * */ static void @@ -622,7 +623,7 @@ /* Use the reference counting system to free (or not) the underlying data */ x509_crtdata_delref((x509_crtdata_t *)crt->data); - + /* Kill the structure itself */ g_free(crt); } @@ -643,7 +644,7 @@ gnutls_x509_crt_t issuer_dat; unsigned int verify; /* used to store result from GnuTLS verifier */ int ret; - + g_return_val_if_fail(crt, FALSE); g_return_val_if_fail(issuer, FALSE); @@ -685,7 +686,7 @@ /* The issuer is not correct, or there were errors */ return FALSE; } - + /* Now, check the signature */ /* The second argument is a ptr to an array of "trusted" issuer certs, but we're only using one trusted one */ @@ -696,7 +697,7 @@ current standard) */ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, &verify); - + if (ret != 0) { purple_debug_error("gnutls/x509", "Attempted certificate verification caused a GnuTLS error code %d. I will just say the signature is bad, but you should look into this.\n", ret); @@ -713,7 +714,7 @@ issuer_id, crt_id); g_free(crt_id); g_free(issuer_id); - + return FALSE; } /* if (ret, etc.) */ @@ -742,7 +743,7 @@ /* This shouldn't happen */ g_return_val_if_fail(tmpsz == hashlen, NULL); - + /* Okay, now create and fill hash array */ hash = g_byte_array_new(); g_byte_array_append(hash, hashbuf, hashlen); @@ -776,7 +777,7 @@ g_free(dn); return NULL; } - + return dn; } @@ -807,7 +808,7 @@ g_free(dn); return NULL; } - + return dn; } @@ -848,7 +849,6 @@ return NULL; } - return cn; } @@ -893,7 +893,7 @@ if (*activation == errval || *expiration == errval) { return FALSE; } - + return TRUE; } diff -r 7f8cf35fc99b -r ee5551542eee libpurple/protocols/Makefile.mingw --- a/libpurple/protocols/Makefile.mingw Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/protocols/Makefile.mingw Fri Aug 31 16:10:18 2007 +0000 @@ -8,7 +8,7 @@ PIDGIN_TREE_TOP := ../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak -SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc10 simple yahoo bonjour myspace +SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo bonjour myspace .PHONY: all install clean diff -r 7f8cf35fc99b -r ee5551542eee libpurple/protocols/bonjour/issues.txt --- a/libpurple/protocols/bonjour/issues.txt Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/protocols/bonjour/issues.txt Fri Aug 31 16:10:18 2007 +0000 @@ -2,6 +2,5 @@ ============= Known issues =============== ========================================== -* Status changes don't work * File transfers * Typing notifications diff -r 7f8cf35fc99b -r ee5551542eee libpurple/protocols/silc/Makefile.mingw --- a/libpurple/protocols/silc/Makefile.mingw Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/protocols/silc/Makefile.mingw Fri Aug 31 16:10:18 2007 +0000 @@ -8,8 +8,8 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = libsilc -NEEDED_DLLS = $(SILC_TOOLKIT)/lib/silc.dll \ - $(SILC_TOOLKIT)/lib/silcclient.dll +NEEDED_DLLS = $(SILC_TOOLKIT)/bin/libsilc-1-1-2.dll \ + $(SILC_TOOLKIT)/bin/libsilcclient-1-1-2.dll TYPE = PLUGIN # Static or Plugin... diff -r 7f8cf35fc99b -r ee5551542eee libpurple/protocols/silc/silc.c --- a/libpurple/protocols/silc/silc.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/protocols/silc/silc.c Fri Aug 31 16:10:18 2007 +0000 @@ -1975,9 +1975,6 @@ silc_log_set_debug_string("*client*"); #endif -#ifdef _WIN32 - silc_net_win32_init(); -#endif } PURPLE_INIT_PLUGIN(silc, init_plugin, info); diff -r 7f8cf35fc99b -r ee5551542eee libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Fri Aug 31 16:10:18 2007 +0000 @@ -969,7 +969,6 @@ PurpleConnection *gc; char *id; char *who; - char *msg; int protocol; }; @@ -987,7 +986,6 @@ g_free(add_req->id); g_free(add_req->who); - g_free(add_req->msg); g_free(add_req); } @@ -1018,7 +1016,6 @@ g_free(add_req->id); g_free(add_req->who); - g_free(add_req->msg); g_free(add_req); } @@ -1132,10 +1129,18 @@ l = l->next; } - if (add_req->id) { - char *alias = NULL; + if (add_req->id && add_req->who) { + char *alias = NULL, *dec_msg = NULL; + + if (!yahoo_privacy_check(gc, add_req->who)) { + purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n", + add_req->who); + yahoo_buddy_add_deny_cb(add_req, NULL); + return; + } + if (msg) - add_req->msg = yahoo_string_decode(gc, msg, FALSE); + dec_msg = yahoo_string_decode(gc, msg, FALSE); if (firstname && lastname) alias = g_strdup_printf("%s %s", firstname, lastname); @@ -1144,20 +1149,19 @@ else if (lastname) alias = g_strdup(lastname); - /* DONE! this is almost exactly the same as what MSN does, * this should probably be moved to the core. */ purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id, - alias, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL, + alias, dec_msg, purple_find_buddy(purple_connection_get_account(gc), add_req->who) != NULL, yahoo_buddy_add_authorize_cb, yahoo_buddy_add_deny_reason_cb, add_req); g_free(alias); + g_free(dec_msg); } else { g_free(add_req->id); g_free(add_req->who); - /*g_free(add_req->msg);*/ g_free(add_req); } } else { @@ -1165,6 +1169,7 @@ } } +/* I don't think this happens anymore in Version 15 */ static void yahoo_buddy_added_us(PurpleConnection *gc, struct yahoo_packet *pkt) { struct yahoo_add_request *add_req; char *msg = NULL; @@ -1192,22 +1197,31 @@ l = l->next; } - if (add_req->id) { + if (add_req->id && add_req->who) { + char *dec_msg = NULL; + + if (!yahoo_privacy_check(gc, add_req->who)) { + purple_debug_misc("yahoo", "Auth. request from %s dropped and automatically denied due to privacy settings!\n", + add_req->who); + yahoo_buddy_add_deny_cb(add_req, NULL); + return; + } + if (msg) - add_req->msg = yahoo_string_decode(gc, msg, FALSE); + dec_msg = yahoo_string_decode(gc, msg, FALSE); /* DONE! this is almost exactly the same as what MSN does, * this should probably be moved to the core. */ purple_account_request_authorization(purple_connection_get_account(gc), add_req->who, add_req->id, - NULL, add_req->msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL, + NULL, dec_msg, purple_find_buddy(purple_connection_get_account(gc),add_req->who) != NULL, yahoo_buddy_add_authorize_cb, yahoo_buddy_add_deny_reason_cb, add_req); + g_free(dec_msg); } else { g_free(add_req->id); g_free(add_req->who); - /*g_free(add_req->msg);*/ g_free(add_req); } } @@ -3016,6 +3030,11 @@ if (yd->ycht) ycht_connection_close(yd->ycht); + g_free(yd->pending_chat_room); + g_free(yd->pending_chat_id); + g_free(yd->pending_chat_topic); + g_free(yd->pending_chat_goto); + g_free(yd); gc->proto_data = NULL; } @@ -4104,9 +4123,7 @@ purple_debug(PURPLE_DEBUG_INFO, "yahoo", "Sending on account %s to buddy %s.\n", username, c->name); - /* TODO: find out how to send a without showing up as a blank line on - * the conversation window. */ - purple_conv_im_send(PURPLE_CONV_IM(c), ""); + purple_conv_im_send_with_flags(PURPLE_CONV_IM(c), "", PURPLE_MESSAGE_INVISIBLE); return TRUE; } diff -r 7f8cf35fc99b -r ee5551542eee libpurple/protocols/yahoo/yahoo.h --- a/libpurple/protocols/yahoo/yahoo.h Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Fri Aug 31 16:10:18 2007 +0000 @@ -130,6 +130,10 @@ gboolean chat_online; gboolean in_chat; char *chat_name; + char *pending_chat_room; + char *pending_chat_id; + char *pending_chat_topic; + char *pending_chat_goto; char *auth; gsize auth_written; char *cookie_y; diff -r 7f8cf35fc99b -r ee5551542eee libpurple/protocols/yahoo/yahoochat.c --- a/libpurple/protocols/yahoo/yahoochat.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoochat.c Fri Aug 31 16:10:18 2007 +0000 @@ -55,16 +55,24 @@ { struct yahoo_data *yd = gc->proto_data; struct yahoo_packet *pkt; + const char *rll; if (yd->wm) { ycht_connection_open(gc); return; } + rll = purple_account_get_string(purple_connection_get_account(gc), + "room_list_locale", YAHOO_ROOMLIST_LOCALE); + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE,0); - yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc), - 109, purple_connection_get_display_name(gc), 6, "abcde", - 135, "ym8.1.0.415"); + yahoo_packet_hash(pkt, "sssss", + 109, purple_connection_get_display_name(gc), + 1, purple_connection_get_display_name(gc), + 6, "abcde", + /* I'm not sure this is the correct way to set this. */ + 98, rll, + 135, "ym8.1.0.415"); yahoo_packet_send_and_free(pkt, yd); } @@ -125,6 +133,7 @@ case 1: /* us, but we already know who we are */ break; case 57: + g_free(room); room = yahoo_string_decode(gc, pair->value, FALSE); break; case 50: /* inviter */ @@ -136,6 +145,7 @@ g_string_append_printf(members, "%s\n", pair->value); break; case 58: + g_free(msg); msg = yahoo_string_decode(gc, pair->value, FALSE); break; case 13: /* ? */ @@ -145,6 +155,17 @@ if (!room) { g_string_free(members, TRUE); + g_free(msg); + return; + } + + if (!yahoo_privacy_check(gc, who) || + (purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) { + purple_debug_info("yahoo", + "Invite to conference %s from %s has been dropped.\n", room, who); + g_free(room); + g_free(msg); + g_string_free(members, TRUE); return; } @@ -153,19 +174,9 @@ if (msg) g_hash_table_replace(components, g_strdup("topic"), msg); g_hash_table_replace(components, g_strdup("type"), g_strdup("Conference")); - if (members) { - g_hash_table_replace(components, g_strdup("members"), g_strdup(members->str)); - } - if (!yahoo_privacy_check(gc, who) || - (purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) { - purple_debug_info("yahoo", - "Invite to conference %s from %s has been dropped.\n", room, who); - g_string_free(members, TRUE); - return; - } + g_hash_table_replace(components, g_strdup("members"), g_string_free(members, FALSE)); serv_got_chat_invite(gc, room, who, msg, components); - g_string_free(members, TRUE); } void yahoo_process_conference_decline(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -180,20 +191,21 @@ switch (pair->key) { case 57: + g_free(room); room = yahoo_string_decode(gc, pair->value, FALSE); break; case 54: who = pair->value; break; case 14: + g_free(msg); msg = yahoo_string_decode(gc, pair->value, FALSE); break; } } if (!yahoo_privacy_check(gc, who)) { g_free(room); - if (msg != NULL) - g_free(msg); + g_free(msg); return; } @@ -209,8 +221,7 @@ } g_free(room); - if (msg) - g_free(msg); + g_free(msg); } } @@ -226,6 +237,7 @@ switch (pair->key) { case 57: + g_free(room); room = yahoo_string_decode(gc, pair->value, FALSE); break; case 53: @@ -254,6 +266,7 @@ switch (pair->key) { case 57: + g_free(room); room = yahoo_string_decode(gc, pair->value, FALSE); break; case 56: @@ -276,7 +289,6 @@ char *room = NULL; char *who = NULL; char *msg = NULL; - char *msg2; int utf8 = 0; PurpleConversation *c; @@ -285,6 +297,7 @@ switch (pair->key) { case 57: + g_free(room); room = yahoo_string_decode(gc, pair->value, FALSE); break; case 3: @@ -299,28 +312,82 @@ } } - if (room && who && msg) { - msg2 = yahoo_string_decode(gc, msg, utf8); - c = yahoo_find_conference(gc, room); - if (!c) - return; - msg = yahoo_codes_to_html(msg2); - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL)); - g_free(msg); - g_free(msg2); + if (room && who && msg) { + char *msg2; + + c = yahoo_find_conference(gc, room); + if (!c) { + g_free(room); + return; } - if (room) - g_free(room); + + msg2 = yahoo_string_decode(gc, msg, utf8); + msg = yahoo_codes_to_html(msg2); + serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), who, 0, msg, time(NULL)); + g_free(msg); + g_free(msg2); + } + + g_free(room); } +static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic, const char *id) +{ + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt; + char *room2; + gboolean utf8 = TRUE; + + if (yd->wm) { + g_return_if_fail(yd->ycht != NULL); + ycht_chat_join(yd->ycht, room); + return; + } + + /* apparently room names are always utf8, or else always not utf8, + * so we don't have to actually pass the flag in the packet. Or something. */ + room2 = yahoo_string_encode(gc, room, &utf8); + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, "ssss", + 1, purple_connection_get_display_name(gc), + 104, room2, + 62, "2", + 129, id ? id : "0"); + yahoo_packet_send_and_free(pkt, yd); + g_free(room2); +} /* this is a confirmation of yahoo_chat_online(); */ void yahoo_process_chat_online(PurpleConnection *gc, struct yahoo_packet *pkt) { struct yahoo_data *yd = (struct yahoo_data *) gc->proto_data; - if (pkt->status == 1) + if (pkt->status == 1) { yd->chat_online = 1; + + /* We need to goto a user in chat */ + if (yd->pending_chat_goto) { + struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_CHATGOTO, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, "sss", + 109, yd->pending_chat_goto, + 1, purple_connection_get_display_name(gc), + 62, "2"); + yahoo_packet_send_and_free(pkt, yd); + } else if (yd->pending_chat_room) { + yahoo_chat_join(gc, purple_connection_get_display_name(gc), yd->pending_chat_room, + yd->pending_chat_topic, yd->pending_chat_id); + } + + g_free(yd->pending_chat_room); + yd->pending_chat_room = NULL; + g_free(yd->pending_chat_id); + yd->pending_chat_id = NULL; + g_free(yd->pending_chat_topic); + yd->pending_chat_topic = NULL; + g_free(yd->pending_chat_goto); + yd->pending_chat_goto = NULL; + } } /* this is basicly the opposite of chat_online */ @@ -340,6 +407,14 @@ if (pkt->status == 1) { yd->chat_online = 0; + g_free(yd->pending_chat_room); + yd->pending_chat_room = NULL; + g_free(yd->pending_chat_id); + yd->pending_chat_id = NULL; + g_free(yd->pending_chat_topic); + yd->pending_chat_topic = NULL; + g_free(yd->pending_chat_goto); + yd->pending_chat_goto = NULL; if (yd->in_chat) yahoo_c_leave(gc, YAHOO_CHAT_ID); } @@ -384,9 +459,11 @@ switch (pair->key) { case 104: + g_free(room); room = yahoo_string_decode(gc, pair->value, TRUE); break; case 105: + g_free(topic); topic = yahoo_string_decode(gc, pair->value, TRUE); break; case 128: @@ -445,8 +522,11 @@ purple_conversation_set_name(c, room); c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room); - if (topic) + if (topic) { purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic); + /* Also print the topic to the backlog so that the captcha link is clickable */ + purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL)); + } yd->in_chat = 1; yd->chat_name = g_strdup(room); purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE); @@ -456,14 +536,22 @@ g_free(tmpmsg); } else { c = serv_got_joined_chat(gc, YAHOO_CHAT_ID, room); - if (topic) + if (topic) { purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic); + /* Also print the topic to the backlog so that the captcha link is clickable */ + purple_conv_chat_write(PURPLE_CONV_CHAT(c), "", topic, PURPLE_MESSAGE_SYSTEM, time(NULL)); + } yd->in_chat = 1; yd->chat_name = g_strdup(room); purple_conv_chat_add_users(PURPLE_CONV_CHAT(c), members, NULL, flags, FALSE); } g_list_free(flags); } else if (c) { + if (topic) { + const char *cur_topic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(c)); + if (cur_topic == NULL || strcmp(cur_topic, topic) != 0) + purple_conv_chat_set_topic(PURPLE_CONV_CHAT(c), NULL, topic); + } yahoo_chat_add_users(PURPLE_CONV_CHAT(c), members); } @@ -497,8 +585,10 @@ for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; - if (pair->key == 104) + if (pair->key == 104) { + g_free(room); room = yahoo_string_decode(gc, pair->value, TRUE); + } if (pair->key == 109) who = pair->value; } @@ -529,6 +619,7 @@ utf8 = strtol(pair->value, NULL, 10); break; case 104: + g_free(room); room = yahoo_string_decode(gc, pair->value, TRUE); break; case 109: @@ -583,6 +674,7 @@ switch (pair->key) { case 104: + g_free(room); room = yahoo_string_decode(gc, pair->value, TRUE); break; case 129: /* room id? */ @@ -590,6 +682,7 @@ case 126: /* ??? */ break; case 117: + g_free(msg); msg = yahoo_string_decode(gc, pair->value, FALSE); break; case 119: @@ -603,24 +696,21 @@ if (room && who) { GHashTable *components; + if (!yahoo_privacy_check(gc, who) || + (purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) { + purple_debug_info("yahoo", "Invite to room %s from %s has been dropped.\n", room, who); + g_free(room); + g_free(msg); + return; + } + components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_hash_table_replace(components, g_strdup("room"), g_strdup(room)); - if (!yahoo_privacy_check(gc, who) || - (purple_account_get_bool(purple_connection_get_account(gc), "ignore_invites", FALSE))) { - purple_debug_info("yahoo", - "Invite to room %s from %s has been dropped.\n", room, who); - if (room != NULL) - g_free(room); - if (msg != NULL) - g_free(msg); - return; - } serv_got_chat_invite(gc, room, who, msg, components); } - if (room) - g_free(room); - if (msg) - g_free(msg); + + g_free(room); + g_free(msg); } void yahoo_process_chat_goto(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -783,6 +873,14 @@ yahoo_packet_send_and_free(pkt, yd); yd->chat_online = 0; + g_free(yd->pending_chat_room); + yd->pending_chat_room = NULL; + g_free(yd->pending_chat_id); + yd->pending_chat_id = NULL; + g_free(yd->pending_chat_topic); + yd->pending_chat_topic = NULL; + g_free(yd->pending_chat_goto); + yd->pending_chat_goto = NULL; g_free(eroom); } @@ -829,29 +927,6 @@ return 0; } -static void yahoo_chat_join(PurpleConnection *gc, const char *dn, const char *room, const char *topic) -{ - struct yahoo_data *yd = gc->proto_data; - struct yahoo_packet *pkt; - char *room2; - gboolean utf8 = TRUE; - - if (yd->wm) { - g_return_if_fail(yd->ycht != NULL); - ycht_chat_join(yd->ycht, room); - return; - } - - /* apparently room names are always utf8, or else always not utf8, - * so we don't have to actually pass the flag in the packet. Or something. */ - room2 = yahoo_string_encode(gc, room, &utf8); - - pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pkt, "ssss", 1, purple_connection_get_display_name(gc), - 62, "2", 104, room2, 129, "0"); - yahoo_packet_send_and_free(pkt, yd); - g_free(room2); -} static void yahoo_chat_invite(PurpleConnection *gc, const char *dn, const char *buddy, const char *room, const char *msg) @@ -892,8 +967,18 @@ return; } - if (!yd->chat_online) + if (!yd->chat_online) { yahoo_chat_online(gc); + g_free(yd->pending_chat_room); + yd->pending_chat_room = NULL; + g_free(yd->pending_chat_id); + yd->pending_chat_id = NULL; + g_free(yd->pending_chat_topic); + yd->pending_chat_topic = NULL; + g_free(yd->pending_chat_goto); + yd->pending_chat_goto = g_strdup(name); + return; + } pkt = yahoo_packet_new(YAHOO_SERVICE_CHATGOTO, YAHOO_STATUS_AVAILABLE, 0); yahoo_packet_hash(pkt, "sss", 109, name, 1, purple_connection_get_display_name(gc), 62, "2"); @@ -988,8 +1073,7 @@ void yahoo_c_join(PurpleConnection *gc, GHashTable *data) { struct yahoo_data *yd; - char *room, *topic, *members, *type; - int id; + char *room, *topic, *type; PurpleConversation *c; yd = (struct yahoo_data *) gc->proto_data; @@ -1004,9 +1088,9 @@ if (!topic) topic = ""; - members = g_hash_table_lookup(data, "members"); - if ((type = g_hash_table_lookup(data, "type")) && !strcmp(type, "Conference")) { + int id; + const char *members = g_hash_table_lookup(data, "members"); id = yd->conf_id++; c = serv_got_joined_chat(gc, id, room); yd->confs = g_slist_prepend(yd->confs, c); @@ -1014,13 +1098,27 @@ yahoo_conf_join(yd, c, purple_connection_get_display_name(gc), room, topic, members); return; } else { - if (yd->in_chat) + const char *id; + /*if (yd->in_chat) yahoo_chat_leave(gc, room, purple_connection_get_display_name(gc), - FALSE); - if (!yd->chat_online) + FALSE);*/ + + id = g_hash_table_lookup(data, "id"); + + if (!yd->chat_online) { yahoo_chat_online(gc); - yahoo_chat_join(gc, purple_connection_get_display_name(gc), room, topic); + g_free(yd->pending_chat_room); + yd->pending_chat_room = g_strdup(room); + g_free(yd->pending_chat_id); + yd->pending_chat_id = g_strdup(id); + g_free(yd->pending_chat_topic); + yd->pending_chat_topic = g_strdup(topic); + g_free(yd->pending_chat_goto); + yd->pending_chat_goto = NULL; + } else { + yahoo_chat_join(gc, purple_connection_get_display_name(gc), room, topic, id); + } return; } } @@ -1148,16 +1246,13 @@ for (i = 0; anames[i]; i++) { if (!strcmp(anames[i], "id")) { - if (s->room.id) - g_free(s->room.id); + g_free(s->room.id); s->room.id = g_strdup(avalues[i]); } else if (!strcmp(anames[i], "name")) { - if (s->room.name) - g_free(s->room.name); + g_free(s->room.name); s->room.name = g_strdup(avalues[i]); } else if (!strcmp(anames[i], "topic")) { - if (s->room.topic) - g_free(s->room.topic); + g_free(s->room.topic); s->room.topic = g_strdup(avalues[i]); } else if (!strcmp(anames[i], "type")) { if (!strcmp("yahoo", avalues[i])) diff -r 7f8cf35fc99b -r ee5551542eee libpurple/prpl.c --- a/libpurple/prpl.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/prpl.c Fri Aug 31 16:10:18 2007 +0000 @@ -199,8 +199,10 @@ if(NULL == status) continue; - purple_status_set_active(status, FALSE); - purple_blist_update_buddy_status(buddy, status); + if (purple_status_is_active(status)) { + purple_status_set_active(status, FALSE); + purple_blist_update_buddy_status(buddy, status); + } } g_slist_free(list); diff -r 7f8cf35fc99b -r ee5551542eee libpurple/server.c --- a/libpurple/server.c Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/server.c Fri Aug 31 16:10:18 2007 +0000 @@ -272,6 +272,7 @@ PurpleAttentionType *attn; PurpleMessageFlags flags; PurplePlugin *prpl; + PurpleConversation *conv; gboolean (*send_attention)(PurpleConnection *, const char *, guint); gchar *description; @@ -302,8 +303,8 @@ if (!send_attention(gc, who, type_code)) return; - /* TODO: icons, sound, shaking... same as serv_got_attention(). */ - serv_got_im(gc, who, description, flags, mtime); + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who); + purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime); g_free(description); } diff -r 7f8cf35fc99b -r ee5551542eee libpurple/win32/global.mak --- a/libpurple/win32/global.mak Tue Aug 28 09:20:27 2007 +0000 +++ b/libpurple/win32/global.mak Fri Aug 31 16:10:18 2007 +0000 @@ -20,7 +20,7 @@ NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4 NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl58 -SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.0.2 +SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.2 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5 # Where we installing this stuff to? diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtkaccount.c --- a/pidgin/gtkaccount.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtkaccount.c Fri Aug 31 16:10:18 2007 +0000 @@ -2405,13 +2405,6 @@ g_free(accounts_window); accounts_window = NULL; - - /* See if we're the main window here. */ - if (PIDGIN_BLIST(purple_get_blist())->window == NULL && - purple_connections_get_all() == NULL) { - - purple_core_quit(); - } } static void diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtkblist.c --- a/pidgin/gtkblist.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtkblist.c Fri Aug 31 16:10:18 2007 +0000 @@ -125,7 +125,7 @@ static PidginBuddyList *gtkblist = NULL; static gboolean pidgin_blist_refresh_timer(PurpleBuddyList *list); -static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean statusChange); +static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean status_change); static void pidgin_blist_selection_changed(GtkTreeSelection *selection, gpointer data); static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node); static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node); @@ -332,8 +332,10 @@ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account); - if (conv != NULL) + if (conv != NULL) { + pidgin_conv_attach_to_conversation(conv); purple_conversation_present(conv); + } serv_join_chat(chat->account->gc, chat->components); g_free(chat_name); @@ -612,6 +614,8 @@ static void pidgin_blist_update_privacy_cb(PurpleBuddy *buddy) { + if (buddy->node.ui_data == NULL || ((struct _pidgin_blist_node*)buddy->node.ui_data)->row == NULL) + return; pidgin_blist_update_buddy(purple_get_blist(), (PurpleBlistNode*)(buddy), TRUE); } @@ -5156,7 +5160,7 @@ -static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean statusChange) +static void pidgin_blist_update_buddy(PurpleBuddyList *list, PurpleBlistNode *node, gboolean status_change) { PurpleBuddy *buddy; struct _pidgin_blist_node *gtkparentnode; diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtkcertmgr.c --- a/pidgin/gtkcertmgr.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtkcertmgr.c Fri Aug 31 16:10:18 2007 +0000 @@ -597,7 +597,7 @@ for (l=idlist; l; l = l->next) { purple_debug_info("gtkcertmgr", "- %s\n", - (gchar *) l->data); + l->data ? (gchar *) l->data : "(null)"); } /* idlist */ purple_certificate_pool_destroy_idlist(idlist); } /* poollist */ @@ -676,11 +676,4 @@ gtk_widget_destroy(certmgr_dialog->window); g_free(certmgr_dialog); certmgr_dialog = NULL; - - /* If this was the only window left, quit */ - if (PIDGIN_BLIST(purple_get_blist())->window == NULL && - purple_connections_get_all() == NULL) { - - purple_core_quit(); - } } diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtkconv.c --- a/pidgin/gtkconv.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtkconv.c Fri Aug 31 16:10:18 2007 +0000 @@ -330,6 +330,7 @@ const char *cmd, char **args, char **error, void *data) { clear_conversation_scrollback(conv); + purple_conversation_clear_message_history(conv); return PURPLE_CMD_STATUS_OK; } @@ -1303,6 +1304,14 @@ } static void +menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget) +{ + PidginWindow *win = data; + PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win); + purple_conversation_set_ui_ops(conv, NULL); +} + +static void menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget) { PidginWindow *win = data; @@ -2701,6 +2710,7 @@ pidgin_conv_present_conversation(PurpleConversation *conv) { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + GdkModifierType state; if(gtkconv->win==hidden_convwin) { pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); @@ -2708,7 +2718,10 @@ } pidgin_conv_switch_active_conversation(conv); - pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv); + /* Switch the tab only if the user initiated the event by pressing + * a button or hitting a key. */ + if (gtk_get_current_event_state(&state)) + pidgin_conv_window_switch_gtkconv(gtkconv->win, gtkconv); gtk_window_present(GTK_WINDOW(gtkconv->win->window)); } @@ -2734,7 +2747,7 @@ PurpleConversation *conv = (PurpleConversation*)l->data; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if(gtkconv->active_conv != conv) + if(gtkconv == NULL || gtkconv->active_conv != conv) continue; if (gtkconv->unseen_state >= min_state @@ -2861,6 +2874,8 @@ { "/Conversation/sep4", NULL, NULL, 0, "", NULL }, + { N_("/Conversation/_Hide"), NULL, menu_hide_conv_cb, 0, + "", NULL}, { N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0, "", GTK_STOCK_CLOSE }, @@ -5040,6 +5055,10 @@ g_list_foreach(gtkconv->send_history, (GFunc)g_free, NULL); g_list_free(gtkconv->send_history); + if (gtkconv->attach.timer) { + g_source_remove(gtkconv->attach.timer); + } + if (tooltip.gtkconv == gtkconv) reset_tooltip(); @@ -5256,6 +5275,15 @@ gtkconv = PIDGIN_CONVERSATION(conv); g_return_if_fail(gtkconv != NULL); + if (gtkconv->attach.timer) { + /* We are currently in the process of filling up the buffer with the message + * history of the conversation. So we do not need to add the message here. + * Instead, this message will be added to the message-list, which in turn will + * be processed and displayed by the attach-callback. + */ + return; + } + if (conv != gtkconv->active_conv) { if (flags & PURPLE_MESSAGE_ACTIVE_ONLY) @@ -7222,6 +7250,55 @@ pidgin_conv_update_fields(conv, PIDGIN_CONV_TOPIC); } +static gboolean +add_message_history_to_gtkconv(gpointer data) +{ + PidginConversation *gtkconv = data; + int count = 0; + int timer = gtkconv->attach.timer; + gtkconv->attach.timer = 0; + while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ + PurpleConvMessage *msg = gtkconv->attach.current->data; + pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when); + gtkconv->attach.current = gtkconv->attach.current->prev; + count++; + } + gtkconv->attach.timer = timer; + if (gtkconv->attach.current) + return TRUE; + + g_source_remove(gtkconv->attach.timer); + gtkconv->attach.timer = 0; + return FALSE; +} + +gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv) +{ + GList *list; + PidginConversation *gtkconv; + + if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) + return FALSE; + + purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops()); + private_gtkconv_new(conv, FALSE); + gtkconv = PIDGIN_CONVERSATION(conv); + + list = purple_conversation_get_message_history(conv); + if (list) { + list = g_list_last(list); + gtkconv->attach.current = list; + gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv); + } + + /* XXX: If this is a chat: + * - populate the userlist + * - set the topic + */ + + return TRUE; +} + void * pidgin_conversations_get_handle(void) { @@ -8867,9 +8944,8 @@ if (pidgin_conv_window_get_gtkconv_count(win) == 1) gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/tabs") && - (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") || - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_LEFT || - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") == GTK_POS_RIGHT)); + (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons") || + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side") != GTK_POS_TOP)); /* show the widgets */ /* gtk_widget_show(gtkconv->icon); */ diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtkconv.h --- a/pidgin/gtkconv.h Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtkconv.h Fri Aug 31 16:10:18 2007 +0000 @@ -162,6 +162,13 @@ GtkWidget *infopane; GtkListStore *infopane_model; GtkTreeIter infopane_iter; + + /* Used when attaching a PidginConversation to a PurpleConversation + * with message history */ + struct { + int timer; + GList *current; + } attach; }; /*@}*/ @@ -238,6 +245,8 @@ */ void pidgin_conv_present_conversation(PurpleConversation *conv); +gboolean pidgin_conv_attach_to_conversation(PurpleConversation *conv); + PidginWindow *pidgin_conv_get_window(PidginConversation *gtkconv); GdkPixbuf *pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon); void pidgin_conv_new(PurpleConversation *conv); diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtkdialogs.c Fri Aug 31 16:10:18 2007 +0000 @@ -99,6 +99,7 @@ {"Dennis 'EvilDennisR' Ristuccia", N_("Senior Contributor/QA"), NULL}, {"Peter 'Fmoo' Ruibal", NULL, NULL}, {"Gabriel 'Nix' Schulhof", NULL, NULL}, + {"Will 'resiak' Thompson", NULL, NULL}, {NULL, NULL, NULL} }; @@ -768,6 +769,7 @@ if (conv == NULL) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username); + pidgin_conv_attach_to_conversation(conv); purple_conversation_present(conv); } diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtksound.c --- a/pidgin/gtksound.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtksound.c Fri Aug 31 16:10:18 2007 +0000 @@ -114,7 +114,7 @@ play_conv_event(PurpleConversation *conv, PurpleSoundEventID event) { /* If we should not play the sound for some reason, then exit early */ - if (conv != NULL) + if (conv != NULL && PIDGIN_IS_PIDGIN_CONVERSATION(conv)) { PidginConversation *gtkconv; PidginWindow *win; diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtksourceundomanager.c --- a/pidgin/gtksourceundomanager.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtksourceundomanager.c Fri Aug 31 16:10:18 2007 +0000 @@ -677,8 +677,6 @@ if (um->priv->running_not_undoable_actions > 0) return; - g_return_if_fail (strlen (text) >= (guint)length); - undo_action.action_type = GTK_SOURCE_UNDO_ACTION_INSERT; undo_action.action.insert.pos = gtk_text_iter_get_offset (pos); @@ -774,7 +772,7 @@ *action = *undo_action; if (action->action_type == GTK_SOURCE_UNDO_ACTION_INSERT) - action->action.insert.text = g_strdup (undo_action->action.insert.text); + action->action.insert.text = g_strndup (undo_action->action.insert.text, undo_action->action.insert.length); else if (action->action_type == GTK_SOURCE_UNDO_ACTION_DELETE) action->action.delete.text = g_strdup (undo_action->action.delete.text); else diff -r 7f8cf35fc99b -r ee5551542eee pidgin/gtkstatusbox.c --- a/pidgin/gtkstatusbox.c Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/gtkstatusbox.c Fri Aug 31 16:10:18 2007 +0000 @@ -1394,7 +1394,7 @@ return; } gtk_grab_add (box->popup_window); - box->popup_in_progress = TRUE; +// box->popup_in_progress = TRUE; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (box->toggle_button), TRUE); @@ -1590,14 +1590,15 @@ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (status_box->toggle_button))) { pidgin_status_box_popdown (status_box); return TRUE; + } else if (ewidget == status_box->toggle_button) { + status_box->popup_in_progress = TRUE; } /* released outside treeview */ - if (ewidget != status_box->toggle_button) - { + if (ewidget != status_box->toggle_button) { pidgin_status_box_popdown (status_box); return TRUE; - } + } return FALSE; } diff -r 7f8cf35fc99b -r ee5551542eee pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Tue Aug 28 09:20:27 2007 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Fri Aug 31 16:10:18 2007 +0000 @@ -503,8 +503,8 @@ ${If} ${IsNT} ${AndIf} ${IsWinNT4} Delete "$INSTDIR\plugins\libsilc.dll" - Delete "$INSTDIR\silcclient.dll" - Delete "$INSTDIR\silc.dll" + Delete "$INSTDIR\libsilcclient-1-1-2.dll" + Delete "$INSTDIR\libsilc-1-1-2.dll" ${EndIf} SetOutPath "$INSTDIR" @@ -693,6 +693,8 @@ ; Remove Language preference info (TODO: check if NSIS removes this) Delete "$INSTDIR\ca-certs\Equifax_Secure_CA.pem" + Delete "$INSTDIR\ca-certs\GTE_CyberTrust_Global_Root.pem" + Delete "$INSTDIR\ca-certs\Verisign_Class3_Primary_CA.pem" Delete "$INSTDIR\ca-certs\Verisign_RSA_Secure_Server_CA.pem" RMDir "$INSTDIR\ca-certs" RMDir /r "$INSTDIR\locale" @@ -764,8 +766,8 @@ Delete "$INSTDIR\pidgin.dll" Delete "$INSTDIR\plc4.dll" Delete "$INSTDIR\plds4.dll" - Delete "$INSTDIR\silc.dll" - Delete "$INSTDIR\silcclient.dll" + Delete "$INSTDIR\libsilc-1-1-2.dll" + Delete "$INSTDIR\libsilcclient-1-1-2.dll" Delete "$INSTDIR\smime3.dll" Delete "$INSTDIR\softokn3.dll" Delete "$INSTDIR\ssl3.dll" diff -r 7f8cf35fc99b -r ee5551542eee share/ca-certs/GTE_CyberTrust_Global_Root.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/GTE_CyberTrust_Global_Root.pem Fri Aug 31 16:10:18 2007 +0000 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgw +FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRy +dXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg +R2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1 +MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYD +VQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMT +GkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4 +ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALn +ZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8F +LztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh3 +46B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq +81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+d +XIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- diff -r 7f8cf35fc99b -r ee5551542eee share/ca-certs/Makefile.am --- a/share/ca-certs/Makefile.am Tue Aug 28 09:20:27 2007 +0000 +++ b/share/ca-certs/Makefile.am Fri Aug 31 16:10:18 2007 +0000 @@ -1,8 +1,11 @@ cacertsdir = $(datadir)/purple/ca-certs cacerts_DATA = \ Equifax_Secure_CA.pem \ - Verisign_RSA_Secure_Server_CA.pem + GTE_CyberTrust_Global_Root.pem \ + Verisign_RSA_Secure_Server_CA.pem \ + Verisign_Class3_Primary_CA.pem -EXTRA_DIST = \ - $(cacerts_DATA) +EXTRA_DIST = \ + Makefile.mingw \ + $(cacerts_DATA) diff -r 7f8cf35fc99b -r ee5551542eee share/ca-certs/Verisign_Class3_Primary_CA.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/Verisign_Class3_Primary_CA.pem Fri Aug 31 16:10:18 2007 +0000 @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE-----