Mercurial > pidgin.yaz
changeset 27842:acef4202e147
propagate from branch 'im.pidgin.pidgin' (head 217103c3e954ff2d295a6590ad3477d357894f9c)
to branch 'im.pidgin.pidgin.yaz' (head 0a5a324e3974fba2b40dcd1bb82fb1cc8b8fcabf)
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Sun, 25 May 2008 04:30:41 +0000 |
parents | 5fc417883a5d (current diff) e7f345a980d6 (diff) |
children | 30eaeb7cc076 |
files | configure.ac libpurple/conversation.c libpurple/protocols/irc/irc.c libpurple/protocols/jabber/message.c libpurple/protocols/msn/msg.c libpurple/protocols/msn/msn.c libpurple/protocols/msnp9/msg.c libpurple/protocols/msnp9/msn.c libpurple/protocols/yahoo/util.c libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo_profile.c libpurple/protocols/yahoo/yahoochat.c libpurple/protocols/zephyr/zephyr.h libpurple/server.c libpurple/util.c libpurple/util.h pidgin/gtkblist.c pidgin/gtkconv.c pidgin/gtkimhtml.c pidgin/gtkimhtmltoolbar.c pidgin/gtkprefs.c pidgin/gtkutils.c pidgin/gtkutils.h |
diffstat | 114 files changed, 4942 insertions(+), 1413 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Mon May 19 16:18:32 2008 +0000 +++ b/COPYRIGHT Sun May 25 04:30:41 2008 +0000 @@ -233,6 +233,7 @@ Shlomi Loubaton Uli Luckas Matthew Luckie +Marcus Lundblad Mike Lundy Jason Lynch Iain MacDonnell @@ -289,6 +290,7 @@ John Oyler Matt Pandina Laszlo Pandy +Giulio 'Twain28' Pascali Ricardo Fernandez Pascual Riley Patterson Havoc Pennington
--- a/ChangeLog Mon May 19 16:18:32 2008 +0000 +++ b/ChangeLog Sun May 25 04:30:41 2008 +0000 @@ -1,6 +1,22 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.4.3 (??/??/2008): +version 2.5.0 (??/??/2008): + libpurple: + * Ability to create custom smileys (currently only the MSN protocol + utilizes the feature). (Thanks to Mauro Sérgio Ferreira Brasil, + Marcus Lundblad, Jorge Villaseñor and other contributors) + * Yahoo! Japan now uses UTF-8, matching the behavior of official clients + and restoring compatibility with the web messenger (Yusuke Odate) + + Pidgin: + * Custom buddy icons can now be added and removed to buddy list + entries via the buddy list entry right-click menu. + * Resize large incoming custom smileys to a maximum of 96px on either + side. + + General: + * Group and Chat buddy list entries can now be given custom buddy + icons. version 2.4.2 (05/17/2008): libpurple:
--- a/ChangeLog.API Mon May 19 16:18:32 2008 +0000 +++ b/ChangeLog.API Sun May 25 04:30:41 2008 +0000 @@ -1,6 +1,40 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.4.2 (05/17/2008): +version 2.5.0 (??/??/2008): + libpurple: + Added: + * Connection flag PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY to indicate + that the connection supports sending and receiving custom smileys. + * PurpleSmiley and the Smiley API. + * purple_serv_got_join_chat_failed + * chat-join-failed signal (see conversation-signals.dox) + * chat-invite-blocked and blocked-im-msg signals (see + converation-signals.dox) (Thanks to Stefan Ott) + * purple_blist_update_node_icon + * purple_buddy_icons_node_has_custom_icon + * purple_buddy_icons_node_find_custom_icon + * purple_buddy_icons_node_set_custom_icon + * purple_buddy_icons_node_set_custom_icon_from_file + + Deprecated: + * purple_blist_update_buddy_icon + * purple_buddy_icons_has_custom_icon + * purple_buddy_icons_find_custom_icon + * purple_buddy_icons_set_custom_icon + * pidgin_set_custom_buddy_icon + + pidgin: + Added: + * gtk_imhtml_smiley_create, gtk_imhtml_smiley_reload and + gtk_imhtml_smiley_destroy to deal with GtkIMHtmlSmiley's. + * pidgin_pixbuf_from_imgstore to create a GdkPixbuf from a + PurpleStoredImage. + * pidgin_themes_smiley_themeize_custom to associate custom smileys to + a GtkIMHtml widget. + * GTK_IMHTML_CUSTOM_SMILEY flag for GtkIMHtml. + * GTK+ Custom Smiley API. + +version 2.4.2: perl: Added: * Purple::Prefs::get_children_names.
--- a/Doxyfile.in Mon May 19 16:18:32 2008 +0000 +++ b/Doxyfile.in Sun May 25 04:30:41 2008 +0000 @@ -485,11 +485,11 @@ # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = libpurple \ - finch \ - finch/libgnt \ - pidgin \ - doc +INPUT = @top_srcdir@/libpurple \ + @top_srcdir@/finch \ + @top_srcdir@/finch/libgnt \ + @top_srcdir@/pidgin \ + @top_srcdir@/doc # This tag can be used to specify the character encoding of the source files that # doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
--- a/configure.ac Mon May 19 16:18:32 2008 +0000 +++ b/configure.ac Sun May 25 04:30:41 2008 +0000 @@ -43,19 +43,19 @@ # # Make sure to update finch/libgnt/configure.ac with libgnt version changes. # -m4_define([purple_lt_current], [4]) +m4_define([purple_lt_current], [5]) m4_define([purple_major_version], [2]) -m4_define([purple_minor_version], [4]) -m4_define([purple_micro_version], [3]) +m4_define([purple_minor_version], [5]) +m4_define([purple_micro_version], [0]) m4_define([purple_version_suffix], [devel]) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) m4_define([purple_display_version], purple_version[]m4_ifdef([purple_version_suffix],[purple_version_suffix])) -m4_define([gnt_lt_current], [4]) +m4_define([gnt_lt_current], [5]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [4]) -m4_define([gnt_micro_version], [3]) +m4_define([gnt_minor_version], [5]) +m4_define([gnt_micro_version], [0]) m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/doc/conversation-signals.dox Mon May 19 16:18:32 2008 +0000 +++ b/doc/conversation-signals.dox Sun May 25 04:30:41 2008 +0000 @@ -7,6 +7,7 @@ @signal sent-im-msg @signal receiving-im-msg @signal received-im-msg + @signal blocked-im-msg @signal writing-chat-msg @signal wrote-chat-msg @signal sending-chat-msg @@ -26,7 +27,9 @@ @signal chat-inviting-user @signal chat-invited-user @signal chat-invited + @signal chat-invite-blocked @signal chat-joined + @signal chat-join-failed @signal chat-left @signal chat-topic-changed @signal conversation-extended-menu @@ -131,6 +134,21 @@ @param flags The IM message flags. @endsignaldef + @signaldef blocked-im-msg + @signalproto +void (*blocked_im_msg)(PurpleAccount *account, const char *sender, + const char *message, PurpleMessageFlags flags, time_t when); + @endsignalproto + @signaldesc + Emitted after an IM is blocked due to privacy settings. + @param account The account the message was received on. + @param sender The username of the sender. + @param message The message that was blocked. + @param flags The IM message flags. + @param when The time the message was sent. + @since 2.5.0 + @endsignaldef + @signaldef writing-chat-msg @signalproto gboolean (*writing_chat_msg)(PurpleAccount *account, const char *who, @@ -302,6 +320,18 @@ @param new_arrival If the buddy is a new arrival. @endsignaldef + @signaldef chat-join-failed + @signalproto +void (*chat_join_failed)(PurpleConnection *gc, GHashTable *components); + @endsignalproto + @signaldesc + Emitted when an account fails to join a chat room + @param gc The PurpleConnection of the account which failed to join the chat. + @param data The components passed to serv_join_chat() originally. + The hash function should be g_str_hash() and the equal + function should be g_str_equal(). + @endsignaldef + @signaldef chat-buddy-flags @signalproto void (*chat_buddy_flags)(PurpleConversation *conv, const char *name, @@ -391,6 +421,21 @@ default behavior will be maintained: the user will be prompted. @endsignaldef + @signaldef chat-invite-blocked + @signalproto +void (*chat_invite_blocked)(PurpleAccount *account, const char *inviter, + const char *name, const char *message, GHashTable *data); + @endsignalproto + @signaldesc + Emitted when an invitation to join a chat is blocked. + @param account The account the invitation was sent to. + @param inviter The name of the person sending the invitation. + @param name The name of the chat invited to. + @param message The invitation message sent. + @param data Hashtable containing data about the invited chat. + @since 2.5.0 + @endsignaldef + @signaldef chat-joined @signalproto void (*chat_joined)(PurpleConversation *conv);
--- a/doc/finch.1.in Mon May 19 16:18:32 2008 +0000 +++ b/doc/finch.1.in Sun May 25 04:30:41 2008 +0000 @@ -381,6 +381,12 @@ one of \fBa-\fR, \fBalt-\fR, \fBm-\fR or \fBmeta-\fR. You can also use \fBhome\fR, \fBend\fR, \fBleft\fR, \fBright\fR etc. keys. +To unbind a key which has a default binding, you simply bind it to the empty string. For example, to unbind \fBAlt + q\fR from the Quit function, you would use: + +[GntWM::binding] +.br +a-q = + .SH Menus You can also specify key-bindings to trigger specific menuitems in windows. For example, the following entry in \fI~/.gntrc\fR will bind \fBCtrl + t\fR to the 'Send IM...' item in the buddylist:
--- a/doc/pidgin.1.in Mon May 19 16:18:32 2008 +0000 +++ b/doc/pidgin.1.in Sun May 25 04:30:41 2008 +0000 @@ -19,9 +19,9 @@ .\" License along with this manual; if not, write to the Free .\" Software Foundation, Inc., 51 Franklin Street, Fifth Floor, .\" Boston, MA 02111-1301 USA. -.TH pidgin 1 +.TH pidgin 1 "" "Pidgin v@VERSION@" .SH NAME -Pidgin v@VERSION@ \- Instant Messaging client +pidgin \- Instant Messaging client .SH SYNOPSIS .TP 5 \fBpidgin \fI[options]\fR
--- a/finch/gntaccount.c Mon May 19 16:18:32 2008 +0000 +++ b/finch/gntaccount.c Sun May 25 04:30:41 2008 +0000 @@ -43,6 +43,7 @@ #include <notify.h> #include <plugin.h> #include <request.h> +#include <savedstatuses.h> #include "gntaccount.h" #include "gntblist.h" @@ -67,7 +68,7 @@ GntWidget *screenname; GntWidget *password; GntWidget *alias; - + GntWidget *splits; GList *split_entries; @@ -76,6 +77,7 @@ GntWidget *newmail; GntWidget *remember; + GntWidget *regserver; } AccountEditDialog; /* This is necessary to close an edit-dialog when an account is deleted */ @@ -125,7 +127,7 @@ _("Username of an account must be non-empty.")); return; } - + username = g_string_new(value); if (prplinfo != NULL) @@ -183,7 +185,7 @@ if (prplinfo) { GList *iter, *entries; - + for (iter = prplinfo->protocol_options, entries = dialog->prpl_entries; iter && entries; iter = iter->next, entries = entries->next) { @@ -228,6 +230,20 @@ gnt_box_give_focus_to_child(GNT_BOX(accounts.window), accounts.tree); } + if (prplinfo && prplinfo->register_user && + gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->regserver))) { + purple_account_register(account); + } else if (dialog->account == NULL) { + /* This is a new account. Set it to the current status. */ + /* Xerox from gtkaccount.c :D */ + const PurpleSavedStatus *saved_status; + saved_status = purple_savedstatus_get_current(); + if (saved_status != NULL) { + purple_savedstatus_activate_for_account(saved_status, account); + purple_account_set_enabled(account, FINCH_UI, TRUE); + } + } + gnt_widget_destroy(dialog->window); } @@ -419,6 +435,11 @@ } } } + + /* Show the registration checkbox only in a new account dialog, + * and when the selected prpl has the support for it. */ + gnt_widget_set_visible(dialog->regserver, account == NULL && + prplinfo->register_user != NULL); } static void @@ -559,6 +580,10 @@ gnt_box_add_widget(GNT_BOX(window), dialog->remember); gnt_box_add_widget(GNT_BOX(window), dialog->newmail); + /* Register checkbox */ + dialog->regserver = gnt_check_box_new(_("Create this account on the server")); + gnt_box_add_widget(GNT_BOX(window), dialog->regserver); + gnt_box_add_widget(GNT_BOX(window), gnt_line_new(FALSE)); /* The advanced box */
--- a/finch/gntnotify.c Mon May 19 16:18:32 2008 +0000 +++ b/finch/gntnotify.c Sun May 25 04:30:41 2008 +0000 @@ -208,8 +208,10 @@ else { char *to; + gboolean newwin = (emaildialog.window == NULL); - setup_email_dialog(); + if (newwin) + setup_email_dialog(); to = g_strdup_printf("%s (%s)", tos ? *tos : purple_account_get_username(account), purple_account_get_protocol_name(account)); @@ -219,7 +221,10 @@ *subjects), NULL, NULL); g_free(to); - gnt_widget_show(emaildialog.window); + if (newwin) + gnt_widget_show(emaildialog.window); + else + gnt_window_present(emaildialog.window); return NULL; }
--- a/finch/libgnt/configure.ac Mon May 19 16:18:32 2008 +0000 +++ b/finch/libgnt/configure.ac Sun May 25 04:30:41 2008 +0000 @@ -24,10 +24,10 @@ # Make sure to update ../../configure.ac with libgnt version changes. # -m4_define([gnt_lt_current], [4]) +m4_define([gnt_lt_current], [5]) m4_define([gnt_major_version], [2]) -m4_define([gnt_minor_version], [4]) -m4_define([gnt_micro_version], [2]) +m4_define([gnt_minor_version], [5]) +m4_define([gnt_micro_version], [0]) m4_define([gnt_version_suffix], []) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/finch/libgnt/gntentry.c Mon May 19 16:18:32 2008 +0000 +++ b/finch/libgnt/gntentry.c Sun May 25 04:30:41 2008 +0000 @@ -551,10 +551,10 @@ return TRUE; } -#define SAME(a,b) ((g_unichar_isalpha(a) && g_unichar_isalpha(b)) || \ - (g_unichar_isdigit(a) && g_unichar_isdigit(b)) || \ +#define SAME(a,b) ((g_unichar_isalnum(a) && g_unichar_isalnum(b)) || \ (g_unichar_isspace(a) && g_unichar_isspace(b)) || \ - (g_unichar_iswide(a) && g_unichar_iswide(b))) + (g_unichar_iswide(a) && g_unichar_iswide(b)) || \ + (g_unichar_ispunct(a) && g_unichar_ispunct(b))) static const char * begin_word(const char *text, const char *begin)
--- a/libpurple/Makefile.am Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/Makefile.am Sun May 25 04:30:41 2008 +0000 @@ -68,6 +68,7 @@ savedstatuses.c \ server.c \ signals.c \ + smiley.c \ dnsquery.c \ dnssrv.c\ status.c \ @@ -120,6 +121,7 @@ savedstatuses.h \ server.h \ signals.h \ + smiley.h \ dnsquery.h \ dnssrv.h \ status.h \ @@ -154,7 +156,7 @@ dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \ connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h \ - savedstatuses.h status.h server.h util.h xmlnode.h prpl.h + savedstatuses.h smiley.h status.h server.h util.h xmlnode.h prpl.h purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \ $(purple_builtheaders)
--- a/libpurple/Makefile.mingw Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/Makefile.mingw Sun May 25 04:30:41 2008 +0000 @@ -65,6 +65,7 @@ savedstatuses.c \ server.c \ signals.c \ + smiley.c \ sound.c \ sslconn.c \ status.c \
--- a/libpurple/blist.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/blist.c Sun May 25 04:30:41 2008 +0000 @@ -824,16 +824,25 @@ ops->update(purplebuddylist, (PurpleBlistNode *)buddy); } -void purple_blist_update_buddy_icon(PurpleBuddy *buddy) +void +purple_blist_update_node_icon(PurpleBlistNode *node) { PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); - g_return_if_fail(buddy != NULL); + g_return_if_fail(node != NULL); if (ops && ops->update) - ops->update(purplebuddylist, (PurpleBlistNode *)buddy); + ops->update(purplebuddylist, node); } +#ifndef PURPLE_DISABLE_DEPRECATED +void +purple_blist_update_buddy_icon(PurpleBuddy *buddy) +{ + purple_blist_update_node_icon((PurpleBlistNode *)buddy); +} +#endif + /* * TODO: Maybe remove the call to this from server.c and call it * from oscar.c and toc.c instead? @@ -1197,7 +1206,7 @@ purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy); - purple_blist_update_buddy_icon(buddy); + purple_blist_update_node_icon((PurpleBlistNode*)buddy); } PurpleAccount * @@ -2189,7 +2198,7 @@ g_return_val_if_fail((name != NULL) && (*name != '\0'), NULL); for (node = purplebuddylist->root; node != NULL; node = node->next) { - if (!strcmp(((PurpleGroup *)node)->name, name)) + if (!purple_utf8_strcasecmp(((PurpleGroup *)node)->name, name)) return (PurpleGroup *)node; }
--- a/libpurple/blist.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/blist.h Sun May 25 04:30:41 2008 +0000 @@ -317,11 +317,22 @@ void purple_blist_update_buddy_status(PurpleBuddy *buddy, PurpleStatus *old_status); /** + * Updates a node's custom icon. + * + * @param node The PurpleBlistNode whose custom icon has changed. + * @since 2.5.0 + */ +void purple_blist_update_node_icon(PurpleBlistNode *node); + +#ifndef PURPLE_DISABLE_DEPRECATED +/** * Updates a buddy's icon. * * @param buddy The buddy whose buddy icon has changed + * @deprecated Use purple_blist_update_node_icon() instead. */ void purple_blist_update_buddy_icon(PurpleBuddy *buddy); +#endif /** * Renames a buddy in the buddy list.
--- a/libpurple/buddyicon.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/buddyicon.c Sun May 25 04:30:41 2008 +0000 @@ -92,8 +92,10 @@ */ static GHashTable *icon_file_cache = NULL; -/* This one is used for both custom buddy icons - * on PurpleContacts and account icons. */ +/** + * This hash table is used for both custom buddy icons on PurpleBlistNodes and + * account icons. + */ static GHashTable *pointer_icon_cache = NULL; static char *cache_dir = NULL; @@ -684,14 +686,6 @@ return (icon ? purple_buddy_icon_ref(icon) : NULL); } -gboolean -purple_buddy_icons_has_custom_icon(PurpleContact *contact) -{ - g_return_val_if_fail(contact != NULL, FALSE); - - return (purple_blist_node_get_string((PurpleBlistNode*)contact, "custom_buddy_icon") != NULL); -} - PurpleStoredImage * purple_buddy_icons_find_account_icon(PurpleAccount *account) { @@ -807,24 +801,32 @@ return ret; } -PurpleStoredImage * -purple_buddy_icons_find_custom_icon(PurpleContact *contact) +gboolean +purple_buddy_icons_node_has_custom_icon(PurpleBlistNode *node) { - PurpleStoredImage *img; - const char *custom_icon_file; - const char *dirname; + g_return_val_if_fail(node != NULL, FALSE); + + return (purple_blist_node_get_string(node, "custom_buddy_icon") != NULL); +} + +PurpleStoredImage * +purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node) +{ char *path; + size_t len; guchar *data; - size_t len; + PurpleStoredImage *img; + const char *custom_icon_file, *dirname; - g_return_val_if_fail(contact != NULL, NULL); + g_return_val_if_fail(node != NULL, NULL); - if ((img = g_hash_table_lookup(pointer_icon_cache, contact))) + if ((img = g_hash_table_lookup(pointer_icon_cache, node))) { return purple_imgstore_ref(img); } - custom_icon_file = purple_blist_node_get_string((PurpleBlistNode*)contact, "custom_buddy_icon"); + custom_icon_file = purple_blist_node_get_string(node, + "custom_buddy_icon"); if (custom_icon_file == NULL) return NULL; @@ -836,7 +838,7 @@ { g_free(path); img = purple_buddy_icon_data_new(data, len, custom_icon_file); - g_hash_table_insert(pointer_icon_cache, contact, img); + g_hash_table_insert(pointer_icon_cache, node, img); return img; } g_free(path); @@ -845,66 +847,79 @@ } PurpleStoredImage * -purple_buddy_icons_set_custom_icon(PurpleContact *contact, - guchar *icon_data, size_t icon_len) +purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node, + guchar *icon_data, size_t icon_len) { + char *old_icon; PurpleStoredImage *old_img; PurpleStoredImage *img = NULL; - char *old_icon; - PurpleBlistNode *child; + + g_return_val_if_fail(node != NULL, NULL); - old_img = g_hash_table_lookup(pointer_icon_cache, contact); + if (!PURPLE_BLIST_NODE_IS_CONTACT(node) && + !PURPLE_BLIST_NODE_IS_CHAT(node) && + !PURPLE_BLIST_NODE_IS_GROUP(node)) { + return NULL; + } - if (icon_data != NULL && icon_len > 0) - { + old_img = g_hash_table_lookup(pointer_icon_cache, node); + + if (icon_data != NULL && icon_len > 0) { img = purple_buddy_icon_data_new(icon_data, icon_len, NULL); } - old_icon = g_strdup(purple_blist_node_get_string((PurpleBlistNode *)contact, + old_icon = g_strdup(purple_blist_node_get_string(node, "custom_buddy_icon")); - if (img && purple_buddy_icons_is_caching()) - { + if (img && purple_buddy_icons_is_caching()) { const char *filename = purple_imgstore_get_filename(img); - purple_blist_node_set_string((PurpleBlistNode *)contact, - "custom_buddy_icon", + purple_blist_node_set_string(node, "custom_buddy_icon", filename); ref_filename(filename); - } - else - { - purple_blist_node_remove_setting((PurpleBlistNode *)contact, - "custom_buddy_icon"); + } else { + purple_blist_node_remove_setting(node, "custom_buddy_icon"); } unref_filename(old_icon); if (img) - g_hash_table_insert(pointer_icon_cache, contact, img); + g_hash_table_insert(pointer_icon_cache, node, img); else - g_hash_table_remove(pointer_icon_cache, contact); + g_hash_table_remove(pointer_icon_cache, node); - for (child = contact->node.child ; child ; child = child->next) - { - PurpleBuddy *buddy; - PurpleConversation *conv; + if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleBlistNode *child; + for (child = node->child ; child ; child = child->next) + { + PurpleBuddy *buddy; + PurpleConversation *conv; + + if (!PURPLE_BLIST_NODE_IS_BUDDY(child)) + continue; + + buddy = (PurpleBuddy *)child; - if (!PURPLE_BLIST_NODE_IS_BUDDY(child)) - continue; - - buddy = (PurpleBuddy *)child; + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, purple_buddy_get_name(buddy), purple_buddy_get_account(buddy)); + if (conv) + purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - purple_buddy_get_name(buddy), - purple_buddy_get_account(buddy)); - if (conv) + /* Is this call necessary anymore? Can the buddies + * themselves need updating when the custom buddy + * icon changes? */ + purple_blist_update_node_icon((PurpleBlistNode*)buddy); + } + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { + PurpleConversation *conv = NULL; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, purple_chat_get_name((PurpleChat*)node), purple_chat_get_account((PurpleChat*)node)); + if (conv) { purple_conversation_update(conv, PURPLE_CONV_UPDATE_ICON); - - purple_blist_update_buddy_icon(buddy); + } } - if (old_img) + purple_blist_update_node_icon(node); + + if (old_img) { purple_imgstore_unref(old_img); - else if (old_icon) - { + } else if (old_icon) { /* The old icon may not have been loaded into memory. In that * case, we'll need to uncache the filename. The filenames * are ref-counted, so this is safe. */ @@ -915,6 +930,49 @@ return img; } +PurpleStoredImage * +purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node, + const gchar *filename) +{ + size_t len; + guchar *data; + + g_return_val_if_fail(node != NULL, NULL); + + if (!PURPLE_BLIST_NODE_IS_CONTACT(node) && + !PURPLE_BLIST_NODE_IS_CHAT(node) && + !PURPLE_BLIST_NODE_IS_GROUP(node)) { + return NULL; + } + + if (!read_icon_file(filename, &data, &len)) { + return NULL; + } + + return purple_buddy_icons_node_set_custom_icon(node, data, len); +} + +#ifndef PURPLE_DISABLE_DEPRECATED +gboolean +purple_buddy_icons_has_custom_icon(PurpleContact *contact) +{ + return purple_buddy_icons_node_has_custom_icon((PurpleBlistNode*)contact); +} + +PurpleStoredImage * +purple_buddy_icons_find_custom_icon(PurpleContact *contact) +{ + return purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact); +} + +PurpleStoredImage * +purple_buddy_icons_set_custom_icon(PurpleContact *contact, guchar *icon_data, + size_t icon_len) +{ + return purple_buddy_icons_node_set_custom_icon((PurpleBlistNode*)contact, icon_data, icon_len); +} +#endif + void _purple_buddy_icon_set_old_icons_dir(const char *dirname) { @@ -1138,7 +1196,9 @@ } } } - else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) + else if (PURPLE_BLIST_NODE_IS_CONTACT(node) || + PURPLE_BLIST_NODE_IS_CHAT(node) || + PURPLE_BLIST_NODE_IS_GROUP(node)) { const char *filename;
--- a/libpurple/buddyicon.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/buddyicon.h Sun May 25 04:30:41 2008 +0000 @@ -218,16 +218,6 @@ purple_buddy_icons_find(PurpleAccount *account, const char *username); /** - * Returns a boolean indicating if a given contact has a custom buddy icon. - * - * @param contact The contact - * - * @return A boolean indicating if @a contact has a custom buddy icon. - */ -gboolean -purple_buddy_icons_has_custom_icon(PurpleContact *contact); - -/** * Returns the buddy icon image for an account. * * The caller owns a reference to the image in the store, and must dereference @@ -277,7 +267,18 @@ purple_buddy_icons_get_account_icon_timestamp(PurpleAccount *account); /** - * Returns the custom buddy icon image for a contact. + * Returns a boolean indicating if a given blist node has a custom buddy icon. + * + * @param node The blist node. + * + * @return A boolean indicating if @a node has a custom buddy icon. + * @since 2.5.0 + */ +gboolean +purple_buddy_icons_node_has_custom_icon(PurpleBlistNode *node); + +/** + * Returns the custom buddy icon image for a blist node. * * The caller owns a reference to the image in the store, and must dereference * the image with purple_imgstore_unref() for it to be freed. @@ -286,31 +287,82 @@ * needed, so it should be called in any case where you want the * appropriate icon. * - * @param contact The contact + * @param node The node. + * + * @return The custom buddy icon. + * @since 2.5.0 + */ +PurpleStoredImage * +purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node); + +/** + * Sets a custom buddy icon for a blist node. + * + * This function will deal with saving a record of the icon, caching the data, + * etc. + * + * @param node The blist node for which to set a custom icon. + * @param icon_data The image data of the icon, which the buddy icon code will + * free. + * @param icon_len The length of the data in @a icon_data. + * + * @return The icon that was set. The caller does NOT own a reference to this, + * and must call purple_imgstore_ref() if it wants one. + * @since 2.5.0 + */ +PurpleStoredImage * +purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node, + guchar *icon_data, size_t icon_len); + +/** + * Sets a custom buddy icon for a blist node. * - * @return The custom buddy icon image. + * Convenience wrapper around purple_buddy_icons_node_set_custom_icon. + * @see purple_buddy_icons_node_set_custom_icon() + * + * @param node The blist node for which to set a custom icon. + * @param filename The path to the icon to set for the blist node. + * + * @return The icon that was set. The caller does NOT own a reference to this, + * and must call purple_imgstore_ref() if it wants one. + * @since 2.5.0 + */ +PurpleStoredImage * +purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node, + const gchar *filename); + +#ifndef PURPLE_DISABLE_DEPRECATED +/** + * PurpleContact version of purple_buddy_icons_node_has_custom_icon. + * + * @copydoc purple_buddy_icons_node_has_custom_icon() + * + * @deprecated Use purple_buddy_icons_node_has_custom_icon instead. + */ +gboolean +purple_buddy_icons_has_custom_icon(PurpleContact *contact); + +/** + * PurpleContact version of purple_buddy_icons_node_find_custom_icon. + * + * @copydoc purple_buddy_icons_node_find_custom_icon() + * + * @deprecated Use purple_buddy_icons_node_find_custom_icon instead. */ PurpleStoredImage * purple_buddy_icons_find_custom_icon(PurpleContact *contact); /** - * Sets a custom buddy icon for a user. - * - * This function will deal with saving a record of the icon, - * caching the data, etc. + * PurpleContact version of purple_buddy_icons_node_set_custom_icon. * - * @param contact The contact for which to set a custom icon. - * @param icon_data The image data of the icon, which the - * buddy icon code will free. - * @param icon_len The length of the data in @a icon_data. + * @copydoc purple_buddy_icons_node_set_custom_icon() * - * @return The icon that was set. The caller does NOT own - * a reference to this, and must call purple_imgstore_ref() - * if it wants one. + * @deprecated Use purple_buddy_icons_node_set_custom_icon instead. */ PurpleStoredImage * purple_buddy_icons_set_custom_icon(PurpleContact *contact, guchar *icon_data, size_t icon_len); +#endif /** * Sets whether or not buddy icon caching is enabled.
--- a/libpurple/connection.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/connection.h Sun May 25 04:30:41 2008 +0000 @@ -43,6 +43,7 @@ PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */ PURPLE_CONNECTION_NO_URLDESC = 0x0040, /**< Connection does not support descriptions with links */ PURPLE_CONNECTION_NO_IMAGES = 0x0080, /**< Connection does not support sending of images */ + PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */ } PurpleConnectionFlags;
--- a/libpurple/conversation.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/conversation.c Sun May 25 04:30:41 2008 +0000 @@ -2235,6 +2235,16 @@ PURPLE_SUBTYPE_CONVERSATION), purple_value_new(PURPLE_TYPE_UINT)); + purple_signal_register(handle, "blocked-im-msg", + purple_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT, + NULL, 5, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_ACCOUNT), + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_UINT), + purple_value_new(PURPLE_TYPE_UINT)); + purple_signal_register(handle, "writing-chat-msg", purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_UINT, purple_value_new(PURPLE_TYPE_BOOLEAN), 5, @@ -2390,11 +2400,27 @@ purple_value_new(PURPLE_TYPE_STRING), purple_value_new(PURPLE_TYPE_POINTER)); + purple_signal_register(handle, "chat-invite-blocked", + purple_marshal_VOID__POINTER_POINTER_POINTER_POINTER_POINTER, + NULL, 5, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_ACCOUNT), + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_BOXED, "GHashTable *")); + purple_signal_register(handle, "chat-joined", purple_marshal_VOID__POINTER, NULL, 1, purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONVERSATION)); + purple_signal_register(handle, "chat-join-failed", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, + PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_POINTER)); + purple_signal_register(handle, "chat-left", purple_marshal_VOID__POINTER, NULL, 1, purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/core.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/core.c Sun May 25 04:30:41 2008 +0000 @@ -43,6 +43,7 @@ #include "proxy.h" #include "savedstatuses.h" #include "signals.h" +#include "smiley.h" #include "sound.h" #include "sslconn.h" #include "status.h" @@ -166,6 +167,7 @@ purple_stun_init(); purple_xfers_init(); purple_idle_init(); + purple_smileys_init(); /* * Call this early on to try to auto-detect our IP address and @@ -194,6 +196,7 @@ purple_connections_disconnect_all(); /* Save .xml files, remove signals, etc. */ + purple_smileys_uninit(); purple_idle_uninit(); purple_ssl_uninit(); purple_pounces_uninit();
--- a/libpurple/dbus-analyze-functions.py Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/dbus-analyze-functions.py Sun May 25 04:30:41 2008 +0000 @@ -483,6 +483,7 @@ self.inputiter = iter(inputfile) self.functionregexp = \ re.compile("^%s(\w[^()]*)\(([^()]*)\)\s*;\s*$" % fprefix) + self.typeregexp = re.compile("^\w+\s*\*?\s*$") @@ -501,7 +502,7 @@ # accumulate lines until the parentheses are balance or an # empty line has been encountered myline = line.strip() - while myline.count("(") > myline.count(")"): + while (myline.count("(") > myline.count(")")) or self.typeregexp.match(myline): newline = self.inputiter.next().strip() if len(newline) == 0: break
--- a/libpurple/dbus-server.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/dbus-server.c Sun May 25 04:30:41 2008 +0000 @@ -40,6 +40,7 @@ #include "core.h" #include "internal.h" #include "savedstatuses.h" +#include "smiley.h" #include "util.h" #include "value.h" #include "xmlnode.h"
--- a/libpurple/imgstore.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/imgstore.c Sun May 25 04:30:41 2008 +0000 @@ -68,6 +68,22 @@ return img; } +PurpleStoredImage * +purple_imgstore_new_from_file(const char *path) +{ + gchar *data = NULL; + size_t len; + GError *err = NULL; + + if (!g_file_get_contents(path, &data, &len, &err)) { + purple_debug_error("imgstore", "Error reading %s: %s\n", + path, err->message); + g_error_free(err); + return NULL; + } + return purple_imgstore_add(data, len, path); +} + int purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename) {
--- a/libpurple/imgstore.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/imgstore.h Sun May 25 04:30:41 2008 +0000 @@ -63,6 +63,17 @@ purple_imgstore_add(gpointer data, size_t size, const char *filename); /** + * Create an image and add it to the store. + * + * @param path The path to the image. + * + * @return The stored image. + * @since 2.X.X + */ +PurpleStoredImage * +purple_imgstore_new_from_file(const char *path); + +/** * Add an image to the store, allocating an ID. * * The caller owns a reference to the image in the store, and must dereference
--- a/libpurple/network.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/network.c Sun May 25 04:30:41 2008 +0000 @@ -663,6 +663,7 @@ if (!dbus_g_proxy_call(nm_proxy, "state", &err, G_TYPE_INVALID, G_TYPE_UINT, &state, G_TYPE_INVALID)) { /* XXX: Print an error? */ + g_error_free(err); return NM_STATE_UNKNOWN; }
--- a/libpurple/plugins/perl/Makefile.am Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/plugins/perl/Makefile.am Sun May 25 04:30:41 2008 +0000 @@ -67,6 +67,7 @@ common/SavedStatuses.xs \ common/Server.xs \ common/Signal.xs \ + common/Smiley.xs \ common/Sound.xs \ common/Status.xs \ common/Stringref.xs \
--- a/libpurple/plugins/perl/common/MANIFEST Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/plugins/perl/common/MANIFEST Sun May 25 04:30:41 2008 +0000 @@ -28,6 +28,7 @@ SavedStatuses.xs Server.xs Signal.xs +Smiley.xs Sound.xs Status.xs Stringref.xs
--- a/libpurple/plugins/perl/common/Makefile.mingw Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/plugins/perl/common/Makefile.mingw Sun May 25 04:30:41 2008 +0000 @@ -61,8 +61,9 @@ Roomlist.xs \ SSLConn.xs \ SavedStatuses.xs \ + Server.xs \ Signal.xs \ - Server.xs \ + Smiley.xs \ Sound.xs \ Status.xs \ Stringref.xs \
--- a/libpurple/plugins/perl/common/Purple.xs Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/plugins/perl/common/Purple.xs Sun May 25 04:30:41 2008 +0000 @@ -30,6 +30,7 @@ PURPLE_PERL_BOOT_PROTO(SavedStatus); PURPLE_PERL_BOOT_PROTO(Serv); PURPLE_PERL_BOOT_PROTO(Signal); +PURPLE_PERL_BOOT_PROTO(Smiley); PURPLE_PERL_BOOT_PROTO(Sound); PURPLE_PERL_BOOT_PROTO(Status); PURPLE_PERL_BOOT_PROTO(Stringref); @@ -68,6 +69,7 @@ PURPLE_PERL_BOOT(SavedStatus); PURPLE_PERL_BOOT(Serv); PURPLE_PERL_BOOT(Signal); + PURPLE_PERL_BOOT(Smiley); PURPLE_PERL_BOOT(Sound); PURPLE_PERL_BOOT(Status); PURPLE_PERL_BOOT(Stringref);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/perl/common/Smiley.xs Sun May 25 04:30:41 2008 +0000 @@ -0,0 +1,81 @@ +#include "module.h" + +MODULE = Purple::Smiley PACKAGE = Purple::Smiley PREFIX = purple_smiley_ +PROTOTYPES: ENABLE + +Purple::Smiley +purple_smiley_new(img, shortcut) + Purple::StoredImage img + const char * shortcut + +Purple::Smiley +purple_smiley_new_from_file(shortcut, filepath) + const char * shortcut + const char * filepath + +void +purple_smiley_delete(smiley) + Purple::Smiley smiley + +gboolean +purple_smiley_set_shortcut(smiley, shortcut) + Purple::Smiley smiley + const char * shortcut + +void +purple_smiley_set_data(smiley, data, data_len, keepfilename) + Purple::Smiley smiley + guchar * data + size_t data_len + gboolean keepfilename + +const char * +purple_smiley_get_shortcut(smiley) + Purple::Smiley smiley + +const char * +purple_smiley_get_checksum(smiley) + Purple::Smiley smiley + +Purple::StoredImage +purple_smiley_get_stored_image(smiley) + Purple::Smiley smiley + +gconstpointer +purple_smiley_get_data(smiley, len) + Purple::Smiley smiley + size_t * len + +const char * +purple_smiley_get_extension(smiley) + Purple::Smiley smiley + + +gchar_own * +purple_smiley_get_full_path(smiley) + Purple::Smiley smiley + + +MODULE = Purple::Smiley PACKAGE = Purple::Smileys PREFIX = purple_smileys_ +PROTOTYPES: ENABLE + +void +purple_smileys_get_all() +PREINIT: + GList *l; +PPCODE: + for (l = purple_smileys_get_all(); l != NULL; l = g_list_delete_link(l, l)) { + XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::Smiley"))); + } + +Purple::Smiley +purple_smileys_find_by_shortcut(shortcut) + const char * shortcut + +Purple::Smiley +purple_smileys_find_by_checksum(checksum) + const char * checksum + +const char * +purple_smileys_get_storing_dir() +
--- a/libpurple/plugins/perl/common/module.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/plugins/perl/common/module.h Sun May 25 04:30:41 2008 +0000 @@ -51,6 +51,7 @@ #include "savedstatuses.h" #include "server.h" #include "signals.h" +#include "smiley.h" #include "sound.h" #include "sslconn.h" #include "status.h" @@ -240,6 +241,9 @@ typedef PurpleSavedStatus * Purple__SavedStatus; typedef PurpleSavedStatusSub * Purple__SavedStatus__Sub; +/* smiley.h */ +typedef PurpleSmiley * Purple__Smiley; + /* sound.h */ typedef PurpleSoundEventID Purple__SoundEventID; typedef PurpleSoundUiOps * Purple__Sound__UiOps;
--- a/libpurple/plugins/perl/common/typemap Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/plugins/perl/common/typemap Sun May 25 04:30:41 2008 +0000 @@ -151,6 +151,7 @@ Purple::Presence T_PurpleObj Purple::PresenceContext T_IV +Purple::Smiley T_PurpleObj Purple::Status T_PurpleObj Purple::StatusAttr T_PurpleObj Purple::StatusPrimitive T_IV
--- a/libpurple/protocols/bonjour/bonjour.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Sun May 25 04:30:41 2008 +0000 @@ -91,7 +91,7 @@ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: " - "http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging" + "http://d.pidgin.im/BonjourWindows" " for more information.")); return; } @@ -413,6 +413,15 @@ } static gboolean +bonjour_can_receive_file(PurpleConnection *connection, const char *who) +{ + PurpleBuddy *buddy = purple_find_buddy(connection->account, who); + + return (buddy != NULL && buddy->proto_data != NULL); + +} + +static gboolean plugin_unload(PurplePlugin *plugin) { /* These shouldn't happen here because they are allocated in _init() */ @@ -483,7 +492,7 @@ NULL, /* roomlist_get_list */ NULL, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ + bonjour_can_receive_file, /* can_receive_file */ bonjour_send_file, /* send_file */ bonjour_new_xfer, /* new_xfer */ NULL, /* offline_message */ @@ -495,6 +504,7 @@ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/bonjour/bonjour_ft.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Sun May 25 04:30:41 2008 +0000 @@ -47,9 +47,8 @@ static void xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type) { - xmlnode *error_node = NULL; - xmlnode *tmp_node = NULL; - XepIq *iq = NULL; + xmlnode *error_node; + XepIq *iq; g_return_if_fail(error_code != NULL); g_return_if_fail(error_type != NULL); @@ -68,14 +67,14 @@ /* TODO: Make this better */ if (!strcmp(error_code, "403")) { - tmp_node = xmlnode_new_child(error_node, "forbidden"); + xmlnode *tmp_node = xmlnode_new_child(error_node, "forbidden"); xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); tmp_node = xmlnode_new_child(error_node, "text"); xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); xmlnode_insert_data(tmp_node, "Offer Declined", -1); } else if (!strcmp(error_code, "404")) { - tmp_node = xmlnode_new_child(error_node, "item-not-found"); + xmlnode *tmp_node = xmlnode_new_child(error_node, "item-not-found"); xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas"); } @@ -90,11 +89,10 @@ static void bonjour_xfer_request_denied(PurpleXfer *xfer) { - XepXfer *xf = NULL; + XepXfer *xf = xfer->data; purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n"); - xf = xfer->data; if(xf) xep_ft_si_reject(xf->data, xf->sid, xfer->who, "403", "cancel"); @@ -149,9 +147,9 @@ static PurpleXfer* bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from) { - GSList *xfers = NULL; - PurpleXfer *xfer = NULL; - XepXfer *xf = NULL; + GSList *xfers; + PurpleXfer *xfer; + XepXfer *xf; if(!sid || !from || !bd) return NULL; @@ -179,19 +177,12 @@ static void xep_ft_si_offer(PurpleXfer *xfer, const gchar *to) { - xmlnode *si_node = NULL; - xmlnode *feature = NULL; - xmlnode *field = NULL; - xmlnode *option = NULL; - xmlnode *value = NULL; - xmlnode *file = NULL; - xmlnode *x = NULL; - XepIq *iq = NULL; - XepXfer *xf = NULL; + xmlnode *si_node, *feature, *field, *file, *x; + XepIq *iq; + XepXfer *xf = xfer->data; BonjourData *bd = NULL; char buf[32]; - xf = xfer->data; if(!xf) return; @@ -234,13 +225,13 @@ xmlnode_set_attrib(field, "type", "list-single"); if (xf->mode & XEP_BYTESTREAMS) { - option = xmlnode_new_child(field, "option"); - value = xmlnode_new_child(option, "value"); + xmlnode *option = xmlnode_new_child(field, "option"); + xmlnode *value = xmlnode_new_child(option, "value"); xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); } if (xf->mode & XEP_IBB) { - option = xmlnode_new_child(field, "option"); - value = xmlnode_new_child(option, "value"); + xmlnode *option = xmlnode_new_child(field, "option"); + xmlnode *value = xmlnode_new_child(option, "value"); xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); } @@ -250,13 +241,9 @@ static void xep_ft_si_result(PurpleXfer *xfer, char *to) { - xmlnode *si_node = NULL; - xmlnode *feature = NULL; - xmlnode *field = NULL; - xmlnode *value = NULL; - xmlnode *x = NULL; - XepIq *iq = NULL; - XepXfer *xf = NULL; + xmlnode *si_node, *feature, *field, *value, *x; + XepIq *iq; + XepXfer *xf; BonjourData *bd; if(!to || !xfer) @@ -295,8 +282,7 @@ static void bonjour_free_xfer(PurpleXfer *xfer) { - XepXfer *xf = NULL; - BonjourData *bd = NULL; + XepXfer *xf; if(xfer == NULL) { purple_debug_info("bonjour", "bonjour-free-xfer-null.\n"); @@ -307,7 +293,7 @@ xf = (XepXfer*)xfer->data; if(xf != NULL) { - bd = (BonjourData*)xf->data; + BonjourData *bd = (BonjourData*)xf->data; if(bd != NULL) { bd->xfer_lists = g_slist_remove(bd->xfer_lists, xfer); purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists); @@ -332,8 +318,8 @@ bonjour_new_xfer(PurpleConnection *gc, const char *who) { PurpleXfer *xfer; - XepXfer *xep_xfer = NULL; - BonjourData *bd = NULL; + XepXfer *xep_xfer; + BonjourData *bd; if(who == NULL || gc == NULL) return NULL; @@ -367,7 +353,7 @@ void bonjour_send_file(PurpleConnection *gc, const char *who, const char *file) { - PurpleXfer *xfer = NULL; + PurpleXfer *xfer; g_return_if_fail(gc != NULL); g_return_if_fail(who != NULL); @@ -386,9 +372,9 @@ static void bonjour_xfer_init(PurpleXfer *xfer) { - PurpleBuddy *buddy = NULL; - BonjourBuddy *bb = NULL; - XepXfer *xf = NULL; + PurpleBuddy *buddy; + BonjourBuddy *bb; + XepXfer *xf; xf = (XepXfer*)xfer->data; if(xf == NULL) @@ -398,7 +384,7 @@ buddy = purple_find_buddy(xfer->account, xfer->who); /* this buddy is offline. */ - if (buddy == NULL) + if (buddy == NULL || buddy->proto_data == NULL) return; bb = (BonjourBuddy *)buddy->proto_data; @@ -420,8 +406,8 @@ xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb) { const char *type, *id; - BonjourData *bd = NULL; - PurpleXfer *xfer = NULL; + BonjourData *bd; + PurpleXfer *xfer; if(pc == NULL || packet == NULL || pb == NULL) return; @@ -496,12 +482,9 @@ void xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb) { - const char *type = NULL, *from = NULL; - xmlnode *query = NULL, *streamhost = NULL; - BonjourData *bd = NULL; - PurpleXfer *xfer = NULL; - XepXfer *xf = NULL; - int portnum; + const char *type, *from; + xmlnode *query; + BonjourData *bd; if(pc == NULL || packet == NULL || pb == NULL) return; @@ -519,6 +502,7 @@ if(!strcmp(type, "set")) { const char *iq_id, *sid; gboolean found = FALSE; + PurpleXfer *xfer; purple_debug_info("bonjour", "bytestream offer Message type - SET.\n"); @@ -529,6 +513,9 @@ if(xfer) { const char *jid, *host, *port; + xmlnode *streamhost; + int portnum; + XepXfer *xf = NULL; xf = (XepXfer*)xfer->data; for(streamhost = xmlnode_get_child(query, "streamhost"); @@ -577,9 +564,9 @@ bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from, const int filesize, const char *filename, int option) { - PurpleXfer *xfer = NULL; - XepXfer *xf = NULL; - BonjourData *bd = NULL; + PurpleXfer *xfer; + XepXfer *xf; + BonjourData *bd; if(pc == NULL || id == NULL || from == NULL) return; @@ -614,11 +601,10 @@ bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond) { PurpleXfer *xfer = data; - XepXfer *xf = NULL; + XepXfer *xf = xfer->data; int acceptfd; int len = 0; - xf = xfer->data; if(xf == NULL) return; @@ -745,10 +731,9 @@ XepXfer *xf; XepIq *iq; xmlnode *query, *streamhost; - char *port; - const char *next_ip; - const char *local_ip = NULL; - char token [] = ";"; + gchar *port; + const char *next_ip, *local_ip; + const char token [] = ";"; BonjourData *bd; purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock); @@ -793,7 +778,7 @@ static void bonjour_bytestreams_init(PurpleXfer *xfer) { - XepXfer *xf = NULL; + XepXfer *xf; if(xfer == NULL) return; purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n"); @@ -813,7 +798,7 @@ { PurpleXfer *xfer = data; XepXfer *xf = xfer->data; - XepIq *iq = NULL; + XepIq *iq; xmlnode *q_node, *tmp_node; BonjourData *bd; @@ -849,10 +834,10 @@ static void bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb) { - XepXfer *xf = NULL; + XepXfer *xf; char dstaddr[41]; unsigned char hashval[20]; - char *p = NULL; + char *p; int i; if(xfer == NULL)
--- a/libpurple/protocols/bonjour/buddy.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/bonjour/buddy.c Sun May 25 04:30:41 2008 +0000 @@ -156,9 +156,9 @@ purple_blist_node_set_flags((PurpleBlistNode *)buddy, PURPLE_BLIST_NODE_FLAG_NO_SAVE); purple_blist_add_buddy(buddy, NULL, group, NULL); } - + buddy->proto_data = bonjour_buddy; - + /* Create the alias for the buddy using the first and the last name */ if (bonjour_buddy->nick) serv_got_alias(purple_account_get_connection(account), buddy->name, bonjour_buddy->nick); @@ -206,10 +206,12 @@ * If the buddy is being saved, mark as offline, otherwise delete */ void bonjour_buddy_signed_off(PurpleBuddy *pb) { - if (PURPLE_BLIST_NODE_SHOULD_SAVE(pb)) + if (PURPLE_BLIST_NODE_SHOULD_SAVE(pb)) { purple_prpl_got_user_status(purple_buddy_get_account(pb), purple_buddy_get_name(pb), "offline", NULL); - else { + bonjour_buddy_delete(pb->proto_data); + pb->proto_data = NULL; + } else { purple_account_remove_buddy(purple_buddy_get_account(pb), pb, NULL); purple_blist_remove_buddy(pb); }
--- a/libpurple/protocols/bonjour/jabber.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Sun May 25 04:30:41 2008 +0000 @@ -385,7 +385,7 @@ purple_debug_warning("bonjour", "receive error: %s\n", err ? err : "(null)"); bonjour_jabber_close_conversation(bconv); - if (bconv->pb != NULL) { + if (bconv->pb != NULL && bconv->pb->proto_data != NULL) { BonjourBuddy *bb = bconv->pb->proto_data; bb->conversation = NULL; } @@ -957,7 +957,7 @@ int ret; pb = _find_or_start_conversation(jdata, to); - if (pb == NULL) { + if (pb == NULL || pb->proto_data == NULL) { purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to); /* You can not send a message to an offline buddy */ return -10000; @@ -1103,8 +1103,10 @@ buddies = purple_find_buddies(jdata->account, NULL); for (l = buddies; l; l = l->next) { BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data; - bonjour_jabber_close_conversation(bb->conversation); - bb->conversation = NULL; + if (bb != NULL) { + bonjour_jabber_close_conversation(bb->conversation); + bb->conversation = NULL; + } } g_slist_free(buddies);
--- a/libpurple/protocols/gg/gg.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/gg/gg.c Sun May 25 04:30:41 2008 +0000 @@ -1998,7 +1998,7 @@ serv_got_chat_in(gc, id, purple_account_get_username(purple_connection_get_account(gc)), - 0, message, time(NULL)); + flags, message, time(NULL)); return 0; } @@ -2152,6 +2152,7 @@ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL }; /* }}} */
--- a/libpurple/protocols/irc/irc.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/irc/irc.c Sun May 25 04:30:41 2008 +0000 @@ -737,7 +737,7 @@ irc_cmd_privmsg(irc, "msg", NULL, args); - serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, what, time(NULL)); + serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), flags, what, time(NULL)); g_free(tmp); return 0; } @@ -909,6 +909,7 @@ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/jabber/auth.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/jabber/auth.c Sun May 25 04:30:41 2008 +0000 @@ -499,6 +499,15 @@ { char *mech_name = xmlnode_get_data(mechnode); #ifdef HAVE_CYRUS_SASL + /* Don't include Google Talk's X-GOOGLE-TOKEN mechanism, as we will not + * support it and including it gives a false fall-back to other mechs offerred, + * leading to incorrect error handling. + */ + if (mech_name && !strcmp(mech_name, "X-GOOGLE-TOKEN")) { + g_free(mech_name); + continue; + } + g_string_append(js->sasl_mechs, mech_name); g_string_append_c(js->sasl_mechs, ' '); #else @@ -936,6 +945,7 @@ _("Invalid challenge from server")); } g_free(js->expected_rspauth); + js->expected_rspauth = NULL; } else { /* assemble a response, and send it */ /* see RFC 2831 */
--- a/libpurple/protocols/jabber/chat.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/jabber/chat.c Sun May 25 04:30:41 2008 +0000 @@ -197,6 +197,12 @@ return chat_name; } +static void insert_in_hash_table(gpointer key, gpointer value, gpointer user_data) +{ + GHashTable *hash_table = (GHashTable *)user_data; + g_hash_table_insert(hash_table, g_strdup(key), g_strdup(value)); +} + void jabber_chat_join(PurpleConnection *gc, GHashTable *data) { JabberChat *chat; @@ -225,18 +231,21 @@ char *buf = g_strdup_printf(_("%s is not a valid room name"), room); purple_notify_error(gc, _("Invalid Room Name"), _("Invalid Room Name"), buf); + purple_serv_got_join_chat_failed(gc, data); g_free(buf); return; } else if(!jabber_nameprep_validate(server)) { char *buf = g_strdup_printf(_("%s is not a valid server name"), server); purple_notify_error(gc, _("Invalid Server Name"), _("Invalid Server Name"), buf); + purple_serv_got_join_chat_failed(gc, data); g_free(buf); return; } else if(!jabber_resourceprep_validate(handle)) { char *buf = g_strdup_printf(_("%s is not a valid room handle"), handle); purple_notify_error(gc, _("Invalid Room Handle"), _("Invalid Room Handle"), buf); + purple_serv_got_join_chat_failed(gc, data); g_free(buf); return; } @@ -255,6 +264,11 @@ chat->server = g_strdup(server); chat->handle = g_strdup(handle); + /* Copy the data hash table to chat->components */ + chat->components = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + g_hash_table_foreach(data, insert_in_hash_table, chat->components); + chat->members = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)jabber_chat_member_free); @@ -315,6 +329,7 @@ g_free(chat->server); g_free(chat->handle); g_hash_table_destroy(chat->members); + g_hash_table_destroy(chat->components); g_free(chat); }
--- a/libpurple/protocols/jabber/chat.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/jabber/chat.h Sun May 25 04:30:41 2008 +0000 @@ -41,6 +41,7 @@ char *room; char *server; char *handle; + GHashTable *components; int id; PurpleConversation *conv; gboolean muc;
--- a/libpurple/protocols/jabber/libxmpp.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Sun May 25 04:30:41 2008 +0000 @@ -116,7 +116,7 @@ jabber_send_attention, /* send_attention */ jabber_attention_types, /* attention_types */ - /* padding */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/jabber/message.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/jabber/message.c Sun May 25 04:30:41 2008 +0000 @@ -409,12 +409,20 @@ const char *code = xmlnode_get_attrib(child, "code"); char *code_txt = NULL; char *text = xmlnode_get_data(child); + if (!text) { + xmlnode *enclosed_text_node; + + if ((enclosed_text_node = xmlnode_get_child(child, "text"))) + text = xmlnode_get_data(enclosed_text_node); + } if(code) - code_txt = g_strdup_printf(_(" (Code %s)"), code); + code_txt = g_strdup_printf(_("(Code %s)"), code); if(!jm->error) - jm->error = g_strdup_printf("%s%s", text ? text : "", + jm->error = g_strdup_printf("%s%s%s", + text ? text : "", + text && code_txt ? " " : "", code_txt ? code_txt : ""); g_free(code_txt);
--- a/libpurple/protocols/jabber/presence.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/jabber/presence.c Sun May 25 04:30:41 2008 +0000 @@ -587,6 +587,7 @@ serv_got_chat_left(js->gc, chat->id); } else { title = g_strdup_printf(_("Error joining chat %s"), from); + purple_serv_got_join_chat_failed(js->gc, chat->components); } purple_notify_error(js->gc, title, title, msg); g_free(title);
--- a/libpurple/protocols/msn/cmdproc.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Sun May 25 04:30:41 2008 +0000 @@ -132,6 +132,14 @@ data = g_realloc(data, len + trans->payload_len); memcpy(data + len, trans->payload, trans->payload_len); len += trans->payload_len; + + /* + * We're done with trans->payload. Free it so that the memory + * doesn't sit around in cmdproc->history. + */ + g_free(trans->payload); + trans->payload = NULL; + trans->payload_len = 0; } msn_servconn_write(servconn, data, len);
--- a/libpurple/protocols/msn/msg.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msn/msg.c Sun May 25 04:30:41 2008 +0000 @@ -151,9 +151,9 @@ MsnMessage *msg; msg = msn_message_new(MSN_MSG_NUDGE); - msn_message_set_content_type(msg, "text/x-msnmsgr-datacast\r\n"); + msn_message_set_content_type(msg, "text/x-msnmsgr-datacast"); msn_message_set_flag(msg, 'N'); - msn_message_set_attr(msg,"ID","1\r\n"); + msn_message_set_bin_data(msg, "ID: 1\r\n", 7); return msg; }
--- a/libpurple/protocols/msn/msn.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msn/msn.c Sun May 25 04:30:41 2008 +0000 @@ -32,6 +32,7 @@ #include "pluginpref.h" #include "prefs.h" #include "session.h" +#include "smiley.h" #include "state.h" #include "util.h" #include "cmds.h" @@ -82,6 +83,12 @@ time_t when; } MsnIMData; +typedef struct +{ + char *smile; + MsnObject *obj; +} MsnEmoticon; + static const char * msn_normalize(const PurpleAccount *account, const char *str) { @@ -115,6 +122,7 @@ return FALSE; msn_switchboard_send_msg(swboard, msg, TRUE); + msn_message_destroy(msg); return TRUE; } @@ -132,6 +140,17 @@ return list; } +static GHashTable * +msn_get_account_text_table(PurpleAccount *unused) +{ + GHashTable *table; + + table = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_insert(table, "login_label", (gpointer)_("E-mail Address...")); + + return table; +} static PurpleCmdRet msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) @@ -908,7 +927,8 @@ session = msn_session_new(account); gc->proto_data = session; - gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC; + gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR | + PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY; msn_session_set_login_step(session, MSN_LOGIN_STEP_START); @@ -949,6 +969,97 @@ return FALSE; } +static GString* +msn_msg_emoticon_add(GString *current, MsnEmoticon *emoticon) +{ + MsnObject *obj; + char *strobj; + + if (emoticon == NULL) + return current; + + obj = emoticon->obj; + + if (!obj) + return current; + + strobj = msn_object_to_string(obj); + + if (current) + g_string_append_printf(current, "\t%s\t%s", + emoticon->smile, strobj); + else { + current = g_string_new(""); + g_string_printf(current,"%s\t%s", + emoticon->smile, strobj); + } + + g_free(strobj); + + return current; +} + +static void +msn_send_emoticons(MsnSwitchBoard *swboard, GString *body) +{ + MsnMessage *msg; + + g_return_if_fail(body != NULL); + + msg = msn_message_new(MSN_MSG_SLP); + msn_message_set_content_type(msg, "text/x-mms-emoticon"); + msn_message_set_flag(msg, 'N'); + msn_message_set_bin_data(msg, body->str, body->len); + + msn_switchboard_send_msg(swboard, msg, TRUE); + msn_message_destroy(msg); +} + +static void msn_emoticon_destroy(MsnEmoticon *emoticon) +{ + if (emoticon->obj) + msn_object_destroy(emoticon->obj); + g_free(emoticon->smile); + g_free(emoticon); +} + +static GSList* msn_msg_grab_emoticons(const char *msg, const char *username) +{ + GSList *list; + GList *smileys; + PurpleSmiley *smiley; + PurpleStoredImage *img; + char *ptr; + MsnEmoticon *emoticon; + int length; + + list = NULL; + smileys = purple_smileys_get_all(); + length = strlen(msg); + + for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { + smiley = smileys->data; + + ptr = g_strstr_len(msg, length, purple_smiley_get_shortcut(smiley)); + + if (!ptr) + continue; + + img = purple_smiley_get_stored_image(smiley); + + emoticon = g_new0(MsnEmoticon, 1); + emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley)); + emoticon->obj = msn_object_new_from_image(img, + purple_imgstore_get_filename(img), + username, MSN_OBJECT_EMOTICON); + + purple_imgstore_unref(img); + list = g_slist_prepend(list, emoticon); + } + + return list; +} + static int msn_send_im(PurpleConnection *gc, const char *who, const char *message, PurpleMessageFlags flags) @@ -958,9 +1069,11 @@ MsnMessage *msg; char *msgformat; char *msgtext; + const char *username; purple_debug_info("MSNP14","send IM {%s} to %s\n",message,who); account = purple_connection_get_account(gc); + username = purple_account_get_username(account); if (buddy) { PurplePresence *p = purple_buddy_get_presence(buddy); @@ -993,10 +1106,13 @@ g_free(msgtext); purple_debug_info("MSNP14","prepare to send online Message\n"); - if (g_ascii_strcasecmp(who, purple_account_get_username(account))) + if (g_ascii_strcasecmp(who, username)) { MsnSession *session; MsnSwitchBoard *swboard; + MsnEmoticon *smile; + GSList *smileys; + GString *emoticons = NULL; session = gc->proto_data; if(msn_user_is_yahoo(account,who)){ @@ -1006,6 +1122,19 @@ }else{ purple_debug_info("MSNP14","send via switchboard\n"); swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM); + smileys = msn_msg_grab_emoticons(message, username); + while (smileys) { + smile = (MsnEmoticon*)smileys->data; + emoticons = msn_msg_emoticon_add(emoticons, smile); + msn_emoticon_destroy(smile); + smileys = g_slist_delete_link(smileys, smileys); + } + + if (emoticons) { + msn_send_emoticons(swboard, emoticons); + g_string_free(emoticons, TRUE); + } + msn_switchboard_send_msg(swboard, msg, TRUE); } } @@ -1446,7 +1575,7 @@ g_free(msgformat); g_free(msgtext); - serv_got_chat_in(gc, id, purple_account_get_username(account), 0, + serv_got_chat_in(gc, id, purple_account_get_username(account), flags, message, time(NULL)); return 0; @@ -2337,8 +2466,8 @@ msn_send_attention, /* send_attention */ msn_attention_types, /* attention_types */ - /* padding */ - NULL + sizeof(PurplePluginProtocolInfo), /* struct_size */ + msn_get_account_text_table, /* get_account_text_table */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/msn/object.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msn/object.c Sun May 25 04:30:41 2008 +0000 @@ -23,6 +23,10 @@ */ #include "object.h" #include "debug.h" +/* Sha1 stuff */ +#include "cipher.h" +/* Base64 stuff */ +#include "util.h" #define GET_STRING_TAG(field, id) \ if ((tag = strstr(str, id "=\"")) != NULL) \ @@ -104,6 +108,74 @@ return obj; } +MsnObject* +msn_object_new_from_image(PurpleStoredImage *img, const char *location, + const char *creator, MsnObjectType type) +{ + MsnObject *msnobj; + + PurpleCipherContext *ctx; + char *buf; + gconstpointer data; + size_t size; + char *base64; + unsigned char digest[20]; + + msnobj = NULL; + + if (img == NULL) + return msnobj; + + size = purple_imgstore_get_size(img); + data = purple_imgstore_get_data(img); + + /* New object */ + msnobj = msn_object_new(); + msn_object_set_local(msnobj); + msn_object_set_type(msnobj, type); + msn_object_set_location(msnobj, location); + msn_object_set_creator(msnobj, creator); + + msn_object_set_image(msnobj, img); + + /* Compute the SHA1D field. */ + memset(digest, 0, sizeof(digest)); + + ctx = purple_cipher_context_new_by_name("sha1", NULL); + purple_cipher_context_append(ctx, data, size); + purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); + + base64 = purple_base64_encode(digest, sizeof(digest)); + msn_object_set_sha1d(msnobj, base64); + g_free(base64); + + msn_object_set_size(msnobj, size); + + /* Compute the SHA1C field. */ + buf = g_strdup_printf( + "Creator%sSize%dType%dLocation%sFriendly%sSHA1D%s", + msn_object_get_creator(msnobj), + msn_object_get_size(msnobj), + msn_object_get_type(msnobj), + msn_object_get_location(msnobj), + msn_object_get_friendly(msnobj), + msn_object_get_sha1d(msnobj)); + + memset(digest, 0, sizeof(digest)); + + purple_cipher_context_reset(ctx, NULL); + purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf)); + purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); + purple_cipher_context_destroy(ctx); + g_free(buf); + + base64 = purple_base64_encode(digest, sizeof(digest)); + msn_object_set_sha1c(msnobj, base64); + g_free(base64); + + return msnobj; +} + void msn_object_destroy(MsnObject *obj) {
--- a/libpurple/protocols/msn/object.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msn/object.h Sun May 25 04:30:41 2008 +0000 @@ -71,6 +71,19 @@ MsnObject *msn_object_new_from_string(const char *str); /** + * Creates a MsnObject structure from a stored image + * + * @param img The image associated to object + * @param location The object location as stored in MsnObject + * @param creator The creator of the object + * @param type The type of the object + * + * @return A new MsnObject structure + */ +MsnObject *msn_object_new_from_image(PurpleStoredImage *img, + const char *location, const char *creator, MsnObjectType type); + +/** * Destroys an MsnObject structure. * * @param obj The object structure.
--- a/libpurple/protocols/msn/slp.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msn/slp.c Sun May 25 04:30:41 2008 +0000 @@ -31,6 +31,8 @@ #include "user.h" #include "switchboard.h" +#include "smiley.h" + /* ms to delay between sending buddy icon requests to the server. */ #define BUDDY_ICON_DELAY 20000 /*debug SLP*/ @@ -278,23 +280,32 @@ type = msn_object_get_type(obj); g_free(msnobj_data); - if (!(type == MSN_OBJECT_USERTILE)) + if ((type != MSN_OBJECT_USERTILE) && (type != MSN_OBJECT_EMOTICON)) { purple_debug_error("msn", "Wrong object?\n"); msn_object_destroy(obj); g_return_if_reached(); } - img = msn_object_get_image(obj); + if (type == MSN_OBJECT_EMOTICON) { + char *path; + path = g_build_filename(purple_smileys_get_storing_dir(), + obj->location, NULL); + img = purple_imgstore_new_from_file(path); + g_free(path); + } else { + img = msn_object_get_image(obj); + if (img) + purple_imgstore_ref(img); + } + msn_object_destroy(obj); + if (img == NULL) { purple_debug_error("msn", "Wrong object.\n"); - msn_object_destroy(obj); g_return_if_reached(); } - msn_object_destroy(obj); - slpsession = msn_slplink_find_slp_session(slplink, slpcall->session_id); @@ -319,6 +330,7 @@ #endif msn_slpmsg_set_image(slpmsg, img); msn_slplink_queue_slpmsg(slplink, slpmsg); + purple_imgstore_unref(img); } else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683")) {
--- a/libpurple/protocols/msn/user.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msn/user.c Sun May 25 04:30:41 2008 +0000 @@ -246,69 +246,17 @@ void msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img) { - MsnObject *msnobj = msn_user_get_object(user); + MsnObject *msnobj; g_return_if_fail(user != NULL); - if (img == NULL) - msn_user_set_object(user, NULL); - else - { - PurpleCipherContext *ctx; - char *buf; - gconstpointer data = purple_imgstore_get_data(img); - size_t size = purple_imgstore_get_size(img); - char *base64; - unsigned char digest[20]; - - if (msnobj == NULL) - { - msnobj = msn_object_new(); - msn_object_set_local(msnobj); - msn_object_set_type(msnobj, MSN_OBJECT_USERTILE); - msn_object_set_location(msnobj, "TFR2C2.tmp"); - msn_object_set_creator(msnobj, msn_user_get_passport(user)); - - msn_user_set_object(user, msnobj); - } - - msn_object_set_image(msnobj, img); - - /* Compute the SHA1D field. */ - memset(digest, 0, sizeof(digest)); + msnobj = msn_object_new_from_image(img, "TFR2C2.tmp", + user->passport, MSN_OBJECT_USERTILE); - ctx = purple_cipher_context_new_by_name("sha1", NULL); - purple_cipher_context_append(ctx, data, size); - purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); - - base64 = purple_base64_encode(digest, sizeof(digest)); - msn_object_set_sha1d(msnobj, base64); - g_free(base64); - - msn_object_set_size(msnobj, size); + if (!msnobj) + purple_debug_error("msn", "Unable to open buddy icon from %s!\n", user->passport); - /* Compute the SHA1C field. */ - buf = g_strdup_printf( - "Creator%sSize%dType%dLocation%sFriendly%sSHA1D%s", - msn_object_get_creator(msnobj), - msn_object_get_size(msnobj), - msn_object_get_type(msnobj), - msn_object_get_location(msnobj), - msn_object_get_friendly(msnobj), - msn_object_get_sha1d(msnobj)); - - memset(digest, 0, sizeof(digest)); - - purple_cipher_context_reset(ctx, NULL); - purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf)); - purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(ctx); - g_free(buf); - - base64 = purple_base64_encode(digest, sizeof(digest)); - msn_object_set_sha1c(msnobj, base64); - g_free(base64); - } + msn_user_set_object(user, msnobj); } /*add group id to User object*/
--- a/libpurple/protocols/msnp9/cmdproc.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msnp9/cmdproc.c Sun May 25 04:30:41 2008 +0000 @@ -132,6 +132,14 @@ data = g_realloc(data, len + trans->payload_len); memcpy(data + len, trans->payload, trans->payload_len); len += trans->payload_len; + + /* + * We're done with trans->payload. Free it so that the memory + * doesn't sit around in cmdproc->history. + */ + g_free(trans->payload); + trans->payload = NULL; + trans->payload_len = 0; } msn_servconn_write(servconn, data, len);
--- a/libpurple/protocols/msnp9/msg.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msnp9/msg.c Sun May 25 04:30:41 2008 +0000 @@ -158,9 +158,9 @@ MsnMessage *msg; msg = msn_message_new(MSN_MSG_NUDGE); - msn_message_set_content_type(msg, "text/x-msnmsgr-datacast\r\n"); + msn_message_set_content_type(msg, "text/x-msnmsgr-datacast"); msn_message_set_flag(msg, 'N'); - msn_message_set_attr(msg,"ID","1\r\n"); + msn_message_set_bin_data(msg, "ID: 1\r\n", 7); return msg; }
--- a/libpurple/protocols/msnp9/msn.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msnp9/msn.c Sun May 25 04:30:41 2008 +0000 @@ -33,6 +33,7 @@ #include "pluginpref.h" #include "prefs.h" #include "session.h" +#include "smiley.h" #include "state.h" #include "util.h" #include "cmds.h" @@ -83,6 +84,12 @@ time_t when; } MsnIMData; +typedef struct +{ + char *smile; + MsnObject *obj; +} MsnEmoticon; + static const char * msn_normalize(const PurpleAccount *account, const char *str) { @@ -116,6 +123,7 @@ return FALSE; msn_switchboard_send_msg(swboard, msg, TRUE); + msn_message_destroy(msg); return TRUE; } @@ -133,6 +141,17 @@ return list; } +static GHashTable * +msn_get_account_text_table(PurpleAccount *unused) +{ + GHashTable *table; + + table = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_insert(table, "login_label", (gpointer)_("E-mail Address...")); + + return table; +} static PurpleCmdRet msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) @@ -760,7 +779,8 @@ session = msn_session_new(account); gc->proto_data = session; - gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC; + gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR | + PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY; msn_session_set_login_step(session, MSN_LOGIN_STEP_START); @@ -801,6 +821,97 @@ return FALSE; } +static GString* +msn_msg_emoticon_add(GString *current, MsnEmoticon *emoticon) +{ + MsnObject *obj; + char *strobj; + + if (emoticon == NULL) + return current; + + obj = emoticon->obj; + + if (!obj) + return current; + + strobj = msn_object_to_string(obj); + + if (current) + g_string_append_printf(current, "\t%s\t%s", + emoticon->smile, strobj); + else { + current = g_string_new(""); + g_string_printf(current,"%s\t%s", + emoticon->smile, strobj); + } + + g_free(strobj); + + return current; +} + +static void +msn_send_emoticons(MsnSwitchBoard *swboard, GString *body) +{ + MsnMessage *msg; + + g_return_if_fail(body != NULL); + + msg = msn_message_new(MSN_MSG_SLP); + msn_message_set_content_type(msg, "text/x-mms-emoticon"); + msn_message_set_flag(msg, 'N'); + msn_message_set_bin_data(msg, body->str, body->len); + + msn_switchboard_send_msg(swboard, msg, TRUE); + msn_message_destroy(msg); +} + +static void msn_emoticon_destroy(MsnEmoticon *emoticon) +{ + if (emoticon->obj) + msn_object_destroy(emoticon->obj); + g_free(emoticon->smile); + g_free(emoticon); +} + +static GSList* msn_msg_grab_emoticons(const char *msg, const char *username) +{ + GSList *list; + GList *smileys; + PurpleSmiley *smiley; + PurpleStoredImage *img; + char *ptr; + MsnEmoticon *emoticon; + int length; + + list = NULL; + smileys = purple_smileys_get_all(); + length = strlen(msg); + + for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { + smiley = (PurpleSmiley*)smileys->data; + + ptr = g_strstr_len(msg, length, purple_smiley_get_shortcut(smiley)); + + if (!ptr) + continue; + + img = purple_smiley_get_stored_image(smiley); + + emoticon = g_new0(MsnEmoticon, 1); + emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley)); + emoticon->obj = msn_object_new_from_image(img, + purple_imgstore_get_filename(img), + username, MSN_OBJECT_EMOTICON); + + purple_imgstore_unref(img); + list = g_slist_prepend(list, emoticon); + } + + return list; +} + static int msn_send_im(PurpleConnection *gc, const char *who, const char *message, PurpleMessageFlags flags) @@ -810,8 +921,10 @@ MsnMessage *msg; char *msgformat; char *msgtext; + const char *username; account = purple_connection_get_account(gc); + username = purple_account_get_username(account); if (buddy) { PurplePresence *p = purple_buddy_get_presence(buddy); @@ -839,13 +952,29 @@ g_free(msgformat); g_free(msgtext); - if (g_ascii_strcasecmp(who, purple_account_get_username(account))) + if (g_ascii_strcasecmp(who, username)) { MsnSession *session; MsnSwitchBoard *swboard; + MsnEmoticon *smile; + GSList *smileys; + GString *emoticons = NULL; session = gc->proto_data; swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM); + smileys = msn_msg_grab_emoticons(message, username); + + while (smileys) { + smile = (MsnEmoticon*)smileys->data; + emoticons = msn_msg_emoticon_add(emoticons,smile); + msn_emoticon_destroy(smile); + smileys = g_slist_delete_link(smileys, smileys); + } + + if (emoticons) { + msn_send_emoticons(swboard, emoticons); + g_string_free(emoticons, TRUE); + } msn_switchboard_send_msg(swboard, msg, TRUE); } @@ -1268,7 +1397,7 @@ g_free(msgformat); g_free(msgtext); - serv_got_chat_in(gc, id, purple_account_get_username(account), 0, + serv_got_chat_in(gc, id, purple_account_get_username(account), flags, message, time(NULL)); return 0; @@ -2153,8 +2282,8 @@ msn_send_attention, /* send_attention */ msn_attention_types, /* attention_types */ - /* padding */ - NULL + sizeof(PurplePluginProtocolInfo), /* struct_size */ + msn_get_account_text_table, /* get_account_text_table */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/msnp9/object.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msnp9/object.c Sun May 25 04:30:41 2008 +0000 @@ -23,6 +23,10 @@ */ #include "object.h" #include "debug.h" +/* Sha1 stuff */ +#include "cipher.h" +/* Base64 stuff */ +#include "util.h" #define GET_STRING_TAG(field, id) \ if ((tag = strstr(str, id "=\"")) != NULL) \ @@ -104,6 +108,74 @@ return obj; } +MsnObject* +msn_object_new_from_image(PurpleStoredImage *img, const char *location, + const char *creator, MsnObjectType type) +{ + MsnObject *msnobj; + + PurpleCipherContext *ctx; + char *buf; + gconstpointer data; + size_t size; + char *base64; + unsigned char digest[20]; + + msnobj = NULL; + + if (img == NULL) + return msnobj; + + size = purple_imgstore_get_size(img); + data = purple_imgstore_get_data(img); + + /* New object */ + msnobj = msn_object_new(); + msn_object_set_local(msnobj); + msn_object_set_type(msnobj, type); + msn_object_set_location(msnobj, location); + msn_object_set_creator(msnobj, creator); + + msn_object_set_image(msnobj, img); + + /* Compute the SHA1D field. */ + memset(digest, 0, sizeof(digest)); + + ctx = purple_cipher_context_new_by_name("sha1", NULL); + purple_cipher_context_append(ctx, data, size); + purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); + + base64 = purple_base64_encode(digest, sizeof(digest)); + msn_object_set_sha1d(msnobj, base64); + g_free(base64); + + msn_object_set_size(msnobj, size); + + /* Compute the SHA1C field. */ + buf = g_strdup_printf( + "Creator%sSize%dType%dLocation%sFriendly%sSHA1D%s", + msn_object_get_creator(msnobj), + msn_object_get_size(msnobj), + msn_object_get_type(msnobj), + msn_object_get_location(msnobj), + msn_object_get_friendly(msnobj), + msn_object_get_sha1d(msnobj)); + + memset(digest, 0, sizeof(digest)); + + purple_cipher_context_reset(ctx, NULL); + purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf)); + purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); + purple_cipher_context_destroy(ctx); + g_free(buf); + + base64 = purple_base64_encode(digest, sizeof(digest)); + msn_object_set_sha1c(msnobj, base64); + g_free(base64); + + return msnobj; +} + void msn_object_destroy(MsnObject *obj) {
--- a/libpurple/protocols/msnp9/object.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msnp9/object.h Sun May 25 04:30:41 2008 +0000 @@ -71,6 +71,19 @@ MsnObject *msn_object_new_from_string(const char *str); /** + * Creates a MsnObject structure from a stored image + * + * @param img The image associated to object + * @param location The object location as stored in MsnObject + * @param creator The creator of the object + * @param type The type of the object + * + * @return A new MsnObject structure + */ +MsnObject *msn_object_new_from_image(PurpleStoredImage *img, + const char *location, const char *creator, MsnObjectType type); + +/** * Destroys an MsnObject structure. * * @param obj The object structure.
--- a/libpurple/protocols/msnp9/slp.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msnp9/slp.c Sun May 25 04:30:41 2008 +0000 @@ -31,6 +31,8 @@ #include "user.h" #include "switchboard.h" +#include "smiley.h" + /* ms to delay between sending buddy icon requests to the server. */ #define BUDDY_ICON_DELAY 20000 @@ -276,23 +278,32 @@ type = msn_object_get_type(obj); g_free(msnobj_data); - if (!(type == MSN_OBJECT_USERTILE)) + if ((type != MSN_OBJECT_USERTILE) && (type != MSN_OBJECT_EMOTICON)) { purple_debug_error("msn", "Wrong object?\n"); msn_object_destroy(obj); g_return_if_reached(); } - img = msn_object_get_image(obj); + if (type == MSN_OBJECT_EMOTICON) { + char *path; + path = g_build_filename(purple_smileys_get_storing_dir(), + obj->location, NULL); + img = purple_imgstore_new_from_file(path); + g_free(path); + } else { + img = msn_object_get_image(obj); + if (img) + purple_imgstore_ref(img); + } + msn_object_destroy(obj); + if (img == NULL) { purple_debug_error("msn", "Wrong object.\n"); - msn_object_destroy(obj); g_return_if_reached(); } - msn_object_destroy(obj); - slpsession = msn_slplink_find_slp_session(slplink, slpcall->session_id); @@ -317,6 +328,7 @@ #endif msn_slpmsg_set_image(slpmsg, img); msn_slplink_queue_slpmsg(slplink, slpmsg); + purple_imgstore_unref(img); } else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683")) {
--- a/libpurple/protocols/msnp9/user.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/msnp9/user.c Sun May 25 04:30:41 2008 +0000 @@ -153,69 +153,17 @@ void msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img) { - MsnObject *msnobj = msn_user_get_object(user); + MsnObject *msnobj = NULL; g_return_if_fail(user != NULL); - if (img == NULL) - msn_user_set_object(user, NULL); - else - { - PurpleCipherContext *ctx; - char *buf; - gconstpointer data = purple_imgstore_get_data(img); - size_t size = purple_imgstore_get_size(img); - char *base64; - unsigned char digest[20]; - - if (msnobj == NULL) - { - msnobj = msn_object_new(); - msn_object_set_local(msnobj); - msn_object_set_type(msnobj, MSN_OBJECT_USERTILE); - msn_object_set_location(msnobj, "TFR2C2.tmp"); - msn_object_set_creator(msnobj, msn_user_get_passport(user)); - - msn_user_set_object(user, msnobj); - } - - msn_object_set_image(msnobj, img); - - /* Compute the SHA1D field. */ - memset(digest, 0, sizeof(digest)); + msnobj = msn_object_new_from_image(img, "TFR2C2.tmp", + user->passport, MSN_OBJECT_USERTILE); - ctx = purple_cipher_context_new_by_name("sha1", NULL); - purple_cipher_context_append(ctx, data, size); - purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); - - base64 = purple_base64_encode(digest, sizeof(digest)); - msn_object_set_sha1d(msnobj, base64); - g_free(base64); - - msn_object_set_size(msnobj, size); + if(!msnobj) + purple_debug_error("msn", "Unable to open buddy icon from %s!\n", user->passport); - /* Compute the SHA1C field. */ - buf = g_strdup_printf( - "Creator%sSize%dType%dLocation%sFriendly%sSHA1D%s", - msn_object_get_creator(msnobj), - msn_object_get_size(msnobj), - msn_object_get_type(msnobj), - msn_object_get_location(msnobj), - msn_object_get_friendly(msnobj), - msn_object_get_sha1d(msnobj)); - - memset(digest, 0, sizeof(digest)); - - purple_cipher_context_reset(ctx, NULL); - purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf)); - purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(ctx); - g_free(buf); - - base64 = purple_base64_encode(digest, sizeof(digest)); - msn_object_set_sha1c(msnobj, base64); - g_free(base64); - } + msn_user_set_object(user, msnobj); } void
--- a/libpurple/protocols/myspace/myspace.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/myspace/myspace.c Sun May 25 04:30:41 2008 +0000 @@ -2449,6 +2449,18 @@ return normalized; } +static GHashTable * +msim_get_account_text_table(PurpleAccount *unused) +{ + GHashTable *table; + + table = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_insert(table, "login_label", (gpointer)_("E-mail Address...")); + + return table; +} + /** Return whether the buddy can be messaged while offline. * * The protocol supports offline messages in just the same way as online @@ -3128,14 +3140,16 @@ NULL, /* unregister_user */ msim_send_attention, /* send_attention */ msim_attention_types, /* attention_types */ - NULL /* _purple_reserved4 */ + + sizeof(PurplePluginProtocolInfo), /* struct_size */ + msim_get_account_text_table, /* get_account_text_table */ }; /** Based on MSN's plugin info comments. */ static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, + PURPLE_PLUGIN_MAGIC, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_PLUGIN_PROTOCOL, /**< type */
--- a/libpurple/protocols/novell/novell.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/novell/novell.c Sun May 25 04:30:41 2008 +0000 @@ -2506,7 +2506,7 @@ } } - serv_got_chat_in(gc, id, name, 0, text, time(NULL)); + serv_got_chat_in(gc, id, name, flags, text, time(NULL)); return 0; } else return -1; @@ -3517,6 +3517,7 @@ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/oscar/libaim.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/oscar/libaim.c Sun May 25 04:30:41 2008 +0000 @@ -96,7 +96,7 @@ NULL, /* send_attention */ NULL, /* get_attention_types */ - /* padding */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/oscar/libicq.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/oscar/libicq.c Sun May 25 04:30:41 2008 +0000 @@ -96,7 +96,7 @@ NULL, /* send_attention */ NULL, /* get_attention_types */ - /* padding */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/qq/qq.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Sun May 25 04:30:41 2008 +0000 @@ -706,6 +706,7 @@ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/silc/chat.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/silc/chat.c Sun May 25 04:30:41 2008 +0000 @@ -1315,7 +1315,7 @@ g_free(tmp); if (ret) - serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg, time(NULL)); + serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL)); return ret; } } @@ -1326,7 +1326,7 @@ (unsigned char *)msg2, strlen(msg2)); if (ret) { - serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg, + serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL)); } g_free(tmp);
--- a/libpurple/protocols/silc/silc.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/silc/silc.c Sun May 25 04:30:41 2008 +0000 @@ -2071,6 +2071,7 @@ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/silc10/chat.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/silc10/chat.c Sun May 25 04:30:41 2008 +0000 @@ -1351,7 +1351,7 @@ flags, (unsigned char *)msg2, strlen(msg2), TRUE); if (ret) { - serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, msg, + serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL)); } g_free(tmp);
--- a/libpurple/protocols/silc10/silc.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/silc10/silc.c Sun May 25 04:30:41 2008 +0000 @@ -1800,10 +1800,10 @@ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - /* padding */ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/simple/simple.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/simple/simple.c Sun May 25 04:30:41 2008 +0000 @@ -2068,6 +2068,7 @@ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/yahoo/util.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/yahoo/util.c Sun May 25 04:30:41 2008 +0000 @@ -120,13 +120,10 @@ return botch_utf((gchar *)str, strlen((gchar *)str), &newlen); } - if (yd->jp) - to_codeset = "SHIFT_JIS"; - else - to_codeset = purple_account_get_string(purple_connection_get_account(gc), "local_charset", "ISO-8859-1"); + to_codeset = purple_account_get_string(purple_connection_get_account(gc), "local_charset", "ISO-8859-1"); + ret = g_convert_with_fallback(str, -1, to_codeset, "UTF-8", "?", NULL, NULL, NULL); - ret = g_convert_with_fallback(str, strlen(str), to_codeset, "UTF-8", "?", NULL, NULL, NULL); - + ret = g_convert_with_fallback(str, -1, to_codeset, "UTF-8", "?", NULL, NULL, NULL); if (ret) return ret; else
--- a/libpurple/protocols/yahoo/yahoo.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sun May 25 04:30:41 2008 +0000 @@ -4403,7 +4403,7 @@ yahoo_send_attention, yahoo_attention_types, - /* padding */ + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/yahoo/yahoo_aliases.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_aliases.c Sun May 25 04:30:41 2008 +0000 @@ -48,7 +48,8 @@ */ struct callback_data { PurpleConnection *gc; - char *id; + gchar *id; + gchar *who; }; @@ -57,7 +58,7 @@ **************************************************************************/ static void -yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,const gchar *url_text, size_t len, const gchar *error_message) +yahoo_fetch_aliases_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message) { struct callback_data *cb = user_data; PurpleConnection *gc = cb->gc; @@ -69,8 +70,8 @@ purple_debug_info("yahoo", "No Aliases to process.%s%s\n", error_message ? " Error:" : "", error_message ? error_message : ""); } else { - gchar *full_name, *nick_name, *alias; - const char *yid, *id, *fn, *ln, *nn; + gchar *full_name, *nick_name; + const char *yid, *id, *fn, *ln, *nn, *alias; YahooFriend *f; PurpleBuddy *b; xmlnode *item, *contacts; @@ -79,7 +80,10 @@ contacts = xmlnode_from_str(url_text, -1); if (contacts == NULL) { - purple_debug_error("yahoo_aliases","Badly formed XML\n"); + purple_debug_error("yahoo", "Badly formed Alias XML\n"); + g_free(cb->who); + g_free(cb->id); + g_free(cb); return; } purple_debug_info("yahoo", "Fetched %" G_GSIZE_FORMAT @@ -89,20 +93,21 @@ for(item = xmlnode_get_child(contacts, "ct"); item; item = xmlnode_get_next_twin(item)) { /* Yahoo replies with two types of contact (ct) record, we are only interested in the alias ones */ if ((yid = xmlnode_get_attrib(item, "yi"))) { - /* Grab all the bits of information we can */ - fn = xmlnode_get_attrib(item,"fn"); - ln = xmlnode_get_attrib(item,"ln"); - nn = xmlnode_get_attrib(item,"nn"); - id = xmlnode_get_attrib(item,"id"); + /* Grab all the bits of information we can */ + fn = xmlnode_get_attrib(item, "fn"); + ln = xmlnode_get_attrib(item, "ln"); + nn = xmlnode_get_attrib(item, "nn"); + id = xmlnode_get_attrib(item, "id"); - full_name = nick_name = alias = NULL; + full_name = nick_name = NULL; + alias = NULL; /* Yahoo stores first and last names separately, lets put them together into a full name */ if (yd->jp) full_name = g_strstrip(g_strdup_printf("%s %s", (ln != NULL ? ln : "") , (fn != NULL ? fn : ""))); else full_name = g_strstrip(g_strdup_printf("%s %s", (fn != NULL ? fn : "") , (ln != NULL ? ln : ""))); - nick_name = (nn != NULL ? g_strstrip(g_strdup_printf("%s", nn)) : NULL); + nick_name = (nn != NULL ? g_strstrip(g_strdup(nn)) : NULL); if (nick_name != NULL) alias = nick_name; /* If we have a nickname from Yahoo, let's use it */ @@ -115,19 +120,18 @@ /* If we don't find a matching buddy, ignore the alias !! */ if (f != NULL && b != NULL) { + const char *buddy_alias = purple_buddy_get_alias(b); yahoo_friend_set_alias_id(f, id); /* Finally, if we received an alias, we better update the buddy list */ if (alias != NULL) { serv_got_alias(cb->gc, yid, alias); - purple_debug_info("yahoo","Fetched alias '%s' (%s)\n",alias,id); - } else if (b->alias != alias && strcmp(b->alias, "") != 0) { + purple_debug_info("yahoo", "Fetched alias '%s' (%s)\n", alias, id); + } else if (buddy_alias != NULL && strcmp(buddy_alias, "") != 0) { /* Or if we have an alias that Yahoo doesn't, send it up */ - yahoo_update_alias(cb->gc, yid, b->alias); - purple_debug_info("yahoo","Sent alias '%s'\n", b->alias); + yahoo_update_alias(cb->gc, yid, buddy_alias); + purple_debug_info("yahoo", "Sent updated alias '%s'\n", buddy_alias); } - } else { - purple_debug_info("yahoo", "Bizarre, received alias for %s, but they are not on your list...\n", yid); } g_free(full_name); @@ -136,6 +140,8 @@ } xmlnode_free(contacts); } + + g_free(cb->who); g_free(cb->id); g_free(cb); } @@ -146,14 +152,15 @@ struct yahoo_data *yd = gc->proto_data; struct callback_data *cb; const char *url; - char *request, *webpage, *webaddress; + gchar *request, *webpage, *webaddress; PurpleUtilFetchUrlData *url_data; gboolean use_whole_url = FALSE; /* use whole URL if using HTTP Proxy */ - if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP)) - use_whole_url = TRUE; + if ((gc->account->proxy_info) + && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP)) + use_whole_url = TRUE; /* Using callback_data so I have access to gc in the callback function */ cb = g_new0(struct callback_data, 1); @@ -167,13 +174,16 @@ "Cookie: T=%s; Y=%s\r\n" "Host: %s\r\n" "Cache-Control: no-cache\r\n\r\n", - use_whole_url ? "http://" : "", use_whole_url ? webaddress : "", webpage, yd->cookie_t,yd->cookie_y, webaddress); + use_whole_url ? "http://" : "", use_whole_url ? webaddress : "", webpage, + yd->cookie_t, yd->cookie_y, + webaddress); /* We have a URL and some header information, let's connect and get some aliases */ - url_data = purple_util_fetch_url_request(url, use_whole_url, NULL, TRUE, request, FALSE, yahoo_fetch_aliases_cb, cb); - if (url_data != NULL) { + url_data = purple_util_fetch_url_request(url, use_whole_url, NULL, TRUE, + request, FALSE, + yahoo_fetch_aliases_cb, cb); + if (url_data != NULL) yd->url_datas = g_slist_prepend(yd->url_datas, url_data); - } g_free(webaddress); g_free(webpage); @@ -185,7 +195,7 @@ **************************************************************************/ static void -yahoo_update_alias_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data,const gchar *url_text, size_t len, const gchar *error_message) +yahoo_update_alias_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, size_t len, const gchar *error_message) { xmlnode *node, *result; struct callback_data *cb = user_data; @@ -196,8 +206,10 @@ yd->url_datas = g_slist_remove(yd->url_datas, url_data); if (len == 0 || error_message != NULL) { - purple_debug_info("yahoo", "Error updating alias: %s\n", - error_message ? error_message : ""); + purple_debug_info("yahoo", "Error updating alias for %s: %s\n", + cb->who, + error_message ? error_message : ""); + g_free(cb->who); g_free(cb->id); g_free(cb); return; @@ -205,24 +217,42 @@ result = xmlnode_from_str(url_text, -1); - purple_debug_info("yahoo", "ID: %s, Return data: %s\n",cb->id, url_text); - if (result == NULL) { - purple_debug_error("yahoo","Alias update failed: Badly formed response\n"); + purple_debug_error("yahoo", "Alias update for %s failed: Badly formed response\n", + cb->who); + g_free(cb->who); g_free(cb->id); g_free(cb); return; } if ((node = xmlnode_get_child(result, "ct"))) { - if (g_ascii_strncasecmp(xmlnode_get_attrib(node, "id"), cb->id, strlen(cb->id))==0) - purple_debug_info("yahoo", "Alias update succeeded\n"); - else - purple_debug_error("yahoo", "Alias update failed (Contact record return mismatch)\n"); - } else { - purple_debug_info("yahoo", "Alias update failed (No contact record returned)\n"); - } + if (cb->id == NULL) { + const char *new_id = xmlnode_get_attrib(node, "id"); + if (new_id != NULL) { + /* We now have an addressbook id for the friend; we should save it */ + YahooFriend *f = yahoo_friend_find(cb->gc, cb->who); + + purple_debug_info("yahoo", "Alias creation for %s succeeded\n", cb->who); + if (f) + yahoo_friend_set_alias_id(f, new_id); + else + purple_debug_error("yahoo", "Missing YahooFriend. Unable to store new addressbook id.\n"); + } else + purple_debug_error("yahoo", "Missing new addressbook id in add response for %s (weird).\n", + cb->who); + } else { + if (g_ascii_strncasecmp(xmlnode_get_attrib(node, "id"), cb->id, strlen(cb->id))==0) + purple_debug_info("yahoo", "Alias update for %s succeeded\n", cb->who); + else + purple_debug_error("yahoo", "Alias update for %s failed (Contact record return mismatch)\n", + cb->who); + } + } else + purple_debug_info("yahoo", "Alias update for %s failed (No contact record returned)\n", cb->who); + + g_free(cb->who); g_free(cb->id); g_free(cb); xmlnode_free(result); @@ -232,27 +262,27 @@ yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias) { struct yahoo_data *yd; - char *content, *url, *request, *webpage, *webaddress, *strtmp; - char *escaped_alias, *alias_jp, *converted_alias_jp; - int inttmp; + const char *url; + gchar *content, *request, *webpage, *webaddress; struct callback_data *cb; PurpleUtilFetchUrlData *url_data; gboolean use_whole_url = FALSE; YahooFriend *f; /* use whole URL if using HTTP Proxy */ - if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP)) - use_whole_url = TRUE; + if ((gc->account->proxy_info) + && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP)) + use_whole_url = TRUE; - g_return_if_fail(alias != NULL); g_return_if_fail(who != NULL); g_return_if_fail(gc != NULL); - purple_debug_info("yahoo", "Sending '%s' as new alias for user '%s'.\n", alias, who); + if (alias == NULL) + alias = ""; f = yahoo_friend_find(gc, who); if (f == NULL) { - purple_debug_info("yahoo", "Missing proto_data (get_yahoo_aliases must have failed), bailing out\n"); + purple_debug_error("yahoo", "Missing YahooFriend. Unable to set server alias.\n"); return; } @@ -260,28 +290,55 @@ /* Using callback_data so I have access to gc in the callback function */ cb = g_new0(struct callback_data, 1); + cb->who = g_strdup(who); cb->id = g_strdup(yahoo_friend_get_alias_id(f)); cb->gc = gc; /* Build all the info to make the web request */ - url = yd->jp? YAHOOJP_ALIAS_UPDATE_URL: YAHOO_ALIAS_UPDATE_URL; - purple_url_parse(url, &webaddress, &inttmp, &webpage, &strtmp, &strtmp); + url = yd->jp ? YAHOOJP_ALIAS_UPDATE_URL: YAHOO_ALIAS_UPDATE_URL; + purple_url_parse(url, &webaddress, NULL, &webpage, NULL, NULL); + + if (cb->id == NULL) { + /* No id for this buddy, so create an address book entry */ + purple_debug_info("yahoo", "Creating '%s' as new alias for user '%s'\n", alias, who); - if (yd->jp) { - alias_jp = g_convert(alias, strlen(alias), "EUC-JP", "UTF-8", NULL, NULL, NULL); - converted_alias_jp = yahoo_convert_to_numeric(alias_jp); - content = g_strdup_printf("<ab k=\"%s\" cc=\"1\">\n" - "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", - gc->account->username, who, yahoo_friend_get_alias_id(f), converted_alias_jp); - free(converted_alias_jp); - g_free(alias_jp); - } - else { - escaped_alias = g_markup_escape_text(alias, strlen(alias)); - content = g_strdup_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?><ab k=\"%s\" cc=\"1\">\n" - "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", - gc->account->username, who, yahoo_friend_get_alias_id(f), escaped_alias); - g_free(escaped_alias); + if (yd->jp) { + gchar *alias_jp = g_convert(alias, -1, "EUC-JP", "UTF-8", NULL, NULL, NULL); + gchar *converted_alias_jp = yahoo_convert_to_numeric(alias_jp); + content = g_strdup_printf("<ab k=\"%s\" cc=\"9\">\n" + "<ct a=\"1\" yi='%s' nn='%s' />\n</ab>\r\n", + purple_account_get_username(gc->account), + who, converted_alias_jp); + free(converted_alias_jp); + g_free(alias_jp); + } else { + gchar *escaped_alias = g_markup_escape_text(alias, -1); + content = g_strdup_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?><ab k=\"%s\" cc=\"9\">\n" + "<ct a=\"1\" yi='%s' nn='%s' />\n</ab>\r\n", + purple_account_get_username(gc->account), + who, escaped_alias); + g_free(escaped_alias); + } + } else { + purple_debug_info("yahoo", "Updating '%s' as new alias for user '%s'\n", alias, who); + + if (yd->jp) { + gchar *alias_jp = g_convert(alias, -1, "EUC-JP", "UTF-8", NULL, NULL, NULL); + gchar *converted_alias_jp = yahoo_convert_to_numeric(alias_jp); + content = g_strdup_printf("<ab k=\"%s\" cc=\"1\">\n" + "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", + purple_account_get_username(gc->account), + who, cb->id, converted_alias_jp); + free(converted_alias_jp); + g_free(alias_jp); + } else { + gchar *escaped_alias = g_markup_escape_text(alias, -1); + content = g_strdup_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?><ab k=\"%s\" cc=\"1\">\n" + "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", + purple_account_get_username(gc->account), + who, cb->id, escaped_alias); + g_free(escaped_alias); + } } request = g_strdup_printf("POST %s%s/%s HTTP/1.1\r\n" @@ -291,15 +348,19 @@ "Content-Length: %" G_GSIZE_FORMAT "\r\n" "Cache-Control: no-cache\r\n\r\n" "%s", - use_whole_url ? "http://" : "", use_whole_url ? webaddress : "", webpage, yd->cookie_t,yd->cookie_y, webaddress, - strlen(content), content); + use_whole_url ? "http://" : "", use_whole_url ? webaddress : "", webpage, + yd->cookie_t, yd->cookie_y, + webaddress, + strlen(content), + content); /* We have a URL and some header information, let's connect and update the alias */ url_data = purple_util_fetch_url_request(url, use_whole_url, NULL, TRUE, request, FALSE, yahoo_update_alias_cb, cb); - if (url_data != NULL) { + if (url_data != NULL) yd->url_datas = g_slist_prepend(yd->url_datas, url_data); - } + g_free(webpage); + g_free(webaddress); g_free(content); g_free(request); }
--- a/libpurple/protocols/yahoo/yahoo_picture.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_picture.c Sun May 25 04:30:41 2008 +0000 @@ -137,9 +137,6 @@ if (url_data != NULL) { yd = gc->proto_data; yd->url_datas = g_slist_prepend(yd->url_datas, url_data); - } else { - g_free(data->who); - g_free(data); } } else if (who && send_icon_info) { yahoo_send_picture_info(gc, who);
--- a/libpurple/protocols/yahoo/yahoo_profile.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_profile.c Sun May 25 04:30:41 2008 +0000 @@ -950,11 +950,6 @@ FALSE, yahoo_got_photo, info2_data); if (url_data != NULL) yd->url_datas = g_slist_prepend(yd->url_datas, url_data); - else { - g_free(info2_data->info_data->name); - g_free(info2_data->info_data); - g_free(info2_data); - } } else { /* Emulate a callback */ yahoo_got_photo(NULL, info2_data, NULL, 0, NULL); @@ -1295,10 +1290,6 @@ url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE, yahoo_got_info, data); if (url_data != NULL) yd->url_datas = g_slist_prepend(yd->url_datas, url_data); - else { - g_free(data->name); - g_free(data); - } g_free(url); }
--- a/libpurple/protocols/yahoo/yahoochat.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoochat.c Sun May 25 04:30:41 2008 +0000 @@ -1043,7 +1043,7 @@ purple_conversation_get_name(c), what, flags); if (!ret) serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(c)), - purple_connection_get_display_name(gc), 0, what, time(NULL)); + purple_connection_get_display_name(gc), flags, what, time(NULL)); } return ret; }
--- a/libpurple/protocols/zephyr/Makefile.am Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/zephyr/Makefile.am Sun May 25 04:30:41 2008 +0000 @@ -59,9 +59,9 @@ mit-copyright.h \ mit-sipb-copyright.h \ sysdep.h \ - zephyr.h \ zephyr_err.c \ zephyr_err.h \ + zephyr_internal.h \ \ zephyr.c
--- a/libpurple/protocols/zephyr/internal.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/zephyr/internal.h Sun May 25 04:30:41 2008 +0000 @@ -1,10 +1,14 @@ - #ifndef __INTERNAL_H__ #define __INTERNAL_H__ #include <sysdep.h> -#include <zephyr.h> +#ifdef LIBZEPHYR_EXT +#include <zephyr/zephyr.h> +#else +#include <zephyr_internal.h> +#endif + #ifndef WIN32 #include <netdb.h> #endif
--- a/libpurple/protocols/zephyr/zephyr.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Sun May 25 04:30:41 2008 +0000 @@ -38,7 +38,6 @@ #include "privacy.h" #include "version.h" -#include "zephyr.h" #include "internal.h" #include <strings.h> @@ -2907,10 +2906,10 @@ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ - /* padding */ NULL, NULL, NULL, + sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL };
--- a/libpurple/protocols/zephyr/zephyr.h Mon May 19 16:18:32 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains global definitions - * - * Created by: Robert French - * - * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of - * Technology. For copying and distribution information, see the - * file "mit-copyright.h". - */ - -#ifndef __ZEPHYR_H__ -#define __ZEPHYR_H__ - -#include <config.h> - -#include <glib.h> - -#include <sys/types.h> -#include <sys/time.h> - -#include <zephyr_err.h> - -#ifndef IPPROTO_MAX /* Make sure not already included */ -#ifndef WIN32 -#include <netinet/in.h> -#endif -#endif - -/* Use __STDC__ to guess whether we can use stdarg, prototypes, and const. - * This is a public header file, so autoconf can't help us here. */ -#ifdef __STDC__ -# include <stdarg.h> -# define ZP(x) x -# define ZCONST const -#else -# define ZP(x) () -# define ZCONST -#endif - -#ifdef WIN32 -/* this really should be uint32_t */ -/*typedef unsigned int in_addr_t; -struct in_addr -{ - in_addr_t s_addr; -}; */ -#include <winsock2.h> -#endif - -/* Service names */ -#define HM_SVCNAME "zephyr-hm" -#define HM_SRV_SVCNAME "zephyr-hm-srv" -#define SERVER_SVCNAME "zephyr-clt" -#define SERVER_SERVICE "zephyr" -#define SERVER_INSTANCE "zephyr" - -#define ZVERSIONHDR "ZEPH" -#define ZVERSIONMAJOR 0 -#define ZVERSIONMINOR 2 - -#define Z_MAXPKTLEN 1024 -#define Z_MAXHEADERLEN 800 -#define Z_MAXOTHERFIELDS 10 /* Max unknown fields in ZNotice_t */ -#define Z_NUMFIELDS 17 - -/* Authentication levels returned by ZCheckAuthentication */ -#define ZAUTH_FAILED (-1) -#define ZAUTH_YES 1 -#define ZAUTH_NO 0 - -typedef char ZPacket_t[Z_MAXPKTLEN]; - -/* Packet type */ -typedef enum { - UNSAFE, UNACKED, ACKED, HMACK, HMCTL, SERVACK, SERVNAK, CLIENTACK, STAT -} ZNotice_Kind_t; -extern ZCONST char *ZNoticeKinds[9]; - -/* Unique ID format */ -typedef struct _ZUnique_Id_t { - struct in_addr zuid_addr; - struct timeval tv; -} ZUnique_Id_t; - -/* Checksum */ -typedef unsigned long ZChecksum_t; - -/* Notice definition */ -typedef struct _ZNotice_t { - char *z_packet; - char *z_version; - ZNotice_Kind_t z_kind; - ZUnique_Id_t z_uid; -#define z_sender_addr z_uid.zuid_addr - struct timeval z_time; - unsigned short z_port; - int z_auth; - int z_checked_auth; - int z_authent_len; - char *z_ascii_authent; - char *z_class; - const char *z_class_inst; - char *z_opcode; - char *z_sender; - const char *z_recipient; - char *z_default_format; - char *z_multinotice; - ZUnique_Id_t z_multiuid; - ZChecksum_t z_checksum; - int z_num_other_fields; - char *z_other_fields[Z_MAXOTHERFIELDS]; - caddr_t z_message; - int z_message_len; -} ZNotice_t; - -/* Subscription structure */ -typedef struct _ZSubscriptions_t { - char *zsub_recipient; - char *zsub_class; - char *zsub_classinst; -} ZSubscription_t; - -/* Function return code */ -typedef int Code_t; - -/* Locations structure */ -typedef struct _ZLocations_t { - char *host; - char *time; - char *tty; -} ZLocations_t; - -typedef struct _ZAsyncLocateData_t { - char *user; - ZUnique_Id_t uid; - char *version; -} ZAsyncLocateData_t; - -/* for ZSetDebug */ -#ifdef Z_DEBUG -void (*__Z_debug_print) ZP((ZCONST char *fmt, va_list args, void *closure)); -void *__Z_debug_print_closure; -#endif - -int ZCompareUIDPred ZP((ZNotice_t *, void *)); -int ZCompareMultiUIDPred ZP((ZNotice_t *, void *)); - -/* Defines for ZFormatNotice, et al. */ -typedef Code_t (*Z_AuthProc) ZP((ZNotice_t*, char *, int, int *)); -Code_t ZMakeAuthentication ZP((ZNotice_t*, char *,int, int*)); - -char *ZGetSender ZP((void)); -char *ZGetVariable ZP((char *)); -Code_t ZSetVariable ZP((char *var, char *value)); -Code_t ZUnsetVariable ZP((char *var)); -int ZGetWGPort ZP((void)); -Code_t ZSetDestAddr ZP((struct sockaddr_in *)); -Code_t ZFormatNoticeList ZP((ZNotice_t*, char**, int, - char **, int*, Z_AuthProc)); -Code_t ZParseNotice ZP((char*, int, ZNotice_t *)); -Code_t ZReadAscii ZP((char*, int, unsigned char*, int)); -Code_t ZReadAscii32 ZP((char *, int, unsigned long *)); -Code_t ZReadAscii16 ZP((char *, int, unsigned short *)); -Code_t ZSendPacket ZP((char*, int, int)); -Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc)); -Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)())); -Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc)); -Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)())); -Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc)); -Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc)); -Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems, - char **buffer, int *ret_len)); -Code_t ZLocateUser ZP((char *, int *, Z_AuthProc)); -Code_t ZRequestLocations ZP((const char *, ZAsyncLocateData_t *, - ZNotice_Kind_t, Z_AuthProc)); -Code_t ZhmStat ZP((struct in_addr *, ZNotice_t *)); -Code_t ZInitialize ZP((void)); -Code_t ZSetServerState ZP((int)); -Code_t ZSetFD ZP((int)); -Code_t ZFormatSmallRawNotice ZP((ZNotice_t*, ZPacket_t, int*)); -int ZCompareUID ZP((ZUnique_Id_t*, ZUnique_Id_t*)); -Code_t ZMakeAscii ZP((char*, int, unsigned char*, int)); -Code_t ZMakeAscii32 ZP((char *, int, unsigned long)); -Code_t ZMakeAscii16 ZP((char *, int, unsigned int)); -Code_t ZReceivePacket ZP((ZPacket_t, int*, struct sockaddr_in*)); -Code_t ZCheckAuthentication ZP((ZNotice_t*, struct sockaddr_in*)); -Code_t ZSetLocation ZP((char *exposure)); -Code_t ZUnsetLocation ZP((void)); -Code_t ZFlushMyLocations ZP((void)); -Code_t ZFormatRawNotice ZP((ZNotice_t *, char**, int *)); -Code_t ZRetrieveSubscriptions ZP((unsigned short, int*)); -Code_t ZOpenPort ZP((unsigned short *port)); -Code_t ZClosePort ZP((void)); -Code_t ZFlushLocations ZP((void)); -Code_t ZFlushSubscriptions ZP((void)); -Code_t ZFreeNotice ZP((ZNotice_t *notice)); -Code_t ZParseLocations ZP((register ZNotice_t *notice, - register ZAsyncLocateData_t *zald, int *nlocs, - char **user)); -int ZCompareALDPred ZP((ZNotice_t *notice, void *zald)); -void ZFreeALD ZP((register ZAsyncLocateData_t *zald)); -Code_t ZCheckIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from, - register int (*predicate) ZP((ZNotice_t *,void *)), - void *args)); -Code_t ZPeekPacket ZP((char **buffer, int *ret_len, - struct sockaddr_in *from)); -Code_t ZPeekNotice ZP((ZNotice_t *notice, struct sockaddr_in *from)); -Code_t ZIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from, - int (*predicate) ZP((ZNotice_t *, void *)), void *args)); -Code_t ZSubscribeTo ZP((ZSubscription_t *sublist, int nitems, - unsigned int port)); -Code_t ZSubscribeToSansDefaults ZP((ZSubscription_t *sublist, int nitems, - unsigned int port)); -Code_t ZUnsubscribeTo ZP((ZSubscription_t *sublist, int nitems, - unsigned int port)); -Code_t ZCancelSubscriptions ZP((unsigned int port)); -int ZPending ZP((void)); -Code_t ZReceiveNotice ZP((ZNotice_t *notice, struct sockaddr_in *from)); -#ifdef Z_DEBUG -void Z_debug ZP((ZCONST char *, ...)); -#endif - -#undef ZP - -/* Compatibility */ -#define ZNewLocateUser ZLocateUser - -/* Macros to retrieve Zephyr library values. */ -extern int __Zephyr_fd; -extern int __Q_CompleteLength; -extern struct sockaddr_in __HM_addr; -extern char __Zephyr_realm[]; -#define ZGetFD() __Zephyr_fd -#define ZQLength() __Q_CompleteLength -#define ZGetDestAddr() __HM_addr -#define ZGetRealm() __Zephyr_realm - -#ifdef Z_DEBUG -void ZSetDebug ZP((void (*)(ZCONST char *, va_list, void *), void *)); -#define ZSetDebug(proc,closure) (__Z_debug_print=(proc), \ - __Z_debug_print_closure=(closure), \ - (void) 0) -#else -#define ZSetDebug(proc,closure) -#endif - -/* Maximum queue length */ -#define Z_MAXQLEN 30 - -/* Successful function return */ -#define ZERR_NONE 0 - -/* Hostmanager wait time (in secs) */ -#define HM_TIMEOUT 1 - -/* Server wait time (in secs) */ -#define SRV_TIMEOUT 30 - -#define ZAUTH (ZMakeAuthentication) -#define ZNOAUTH ((Z_AuthProc)0) - -/* Packet strings */ -#define ZSRVACK_SENT "SENT" /* SERVACK codes */ -#define ZSRVACK_NOTSENT "LOST" -#define ZSRVACK_FAIL "FAIL" - -/* Server internal class */ -#define ZEPHYR_ADMIN_CLASS "ZEPHYR_ADMIN" /* Class */ - -/* Control codes sent to a server */ -#define ZEPHYR_CTL_CLASS "ZEPHYR_CTL" /* Class */ - -#define ZEPHYR_CTL_CLIENT "CLIENT" /* Inst: From client */ -#define CLIENT_SUBSCRIBE "SUBSCRIBE" /* Opcode: Subscribe */ -#define CLIENT_SUBSCRIBE_NODEFS "SUBSCRIBE_NODEFS" /* Opcode: Subscribe */ -#define CLIENT_UNSUBSCRIBE "UNSUBSCRIBE" /* Opcode: Unsubsubscribe */ -#define CLIENT_CANCELSUB "CLEARSUB" /* Opcode: Clear all subs */ -#define CLIENT_GIMMESUBS "GIMME" /* Opcode: Give me subs */ -#define CLIENT_GIMMEDEFS "GIMMEDEFS" /* Opcode: Give me default - * subscriptions */ - -#define ZEPHYR_CTL_HM "HM" /* Inst: From HM */ -#define HM_BOOT "BOOT" /* Opcode: Boot msg */ -#define HM_FLUSH "FLUSH" /* Opcode: Flush me */ -#define HM_DETACH "DETACH" /* Opcode: Detach me */ -#define HM_ATTACH "ATTACH" /* Opcode: Attach me */ - -/* Control codes send to a HostManager */ -#define HM_CTL_CLASS "HM_CTL" /* Class */ - -#define HM_CTL_SERVER "SERVER" /* Inst: From server */ -#define SERVER_SHUTDOWN "SHUTDOWN" /* Opcode: Server shutdown */ -#define SERVER_PING "PING" /* Opcode: PING */ - -#define HM_CTL_CLIENT "CLIENT" /* Inst: From client */ -#define CLIENT_FLUSH "FLUSH" /* Opcode: Send flush to srv */ -#define CLIENT_NEW_SERVER "NEWSERV" /* Opcode: Find new server */ - -/* HM Statistics */ -#define HM_STAT_CLASS "HM_STAT" /* Class */ - -#define HM_STAT_CLIENT "HMST_CLIENT" /* Inst: From client */ -#define HM_GIMMESTATS "GIMMESTATS" /* Opcode: get stats */ - -/* Login class messages */ -#define LOGIN_CLASS "LOGIN" /* Class */ - -/* Class Instance is principal of user who is logging in or logging out */ - -#define EXPOSE_NONE "NONE" /* Opcode: Not visible */ -#define EXPOSE_OPSTAFF "OPSTAFF" /* Opcode: Opstaff visible */ -#define EXPOSE_REALMVIS "REALM-VISIBLE" /* Opcode: Realm visible */ -#define EXPOSE_REALMANN "REALM-ANNOUNCED"/* Opcode: Realm announced */ -#define EXPOSE_NETVIS "NET-VISIBLE" /* Opcode: Net visible */ -#define EXPOSE_NETANN "NET-ANNOUNCED" /* Opcode: Net announced */ -#define LOGIN_USER_LOGIN "USER_LOGIN" /* Opcode: user login - (from server) */ -#define LOGIN_USER_LOGOUT "USER_LOGOUT" /* Opcode: User logout */ -#define LOGIN_USER_FLUSH "USER_FLUSH" /* Opcode: flush all locs */ - -/* Locate class messages */ -#define LOCATE_CLASS "USER_LOCATE" /* Class */ - -#define LOCATE_HIDE "USER_HIDE" /* Opcode: Hide me */ -#define LOCATE_UNHIDE "USER_UNHIDE" /* Opcode: Unhide me */ - -/* Class Instance is principal of user to locate */ -#define LOCATE_LOCATE "LOCATE" /* Opcode: Locate user */ - -/* WG_CTL class messages */ -#define WG_CTL_CLASS "WG_CTL" /* Class */ - -#define WG_CTL_USER "USER" /* Inst: User request */ -#define USER_REREAD "REREAD" /* Opcode: Reread desc file */ -#define USER_SHUTDOWN "SHUTDOWN" /* Opcode: Go catatonic */ -#define USER_STARTUP "STARTUP" /* Opcode: Come out of it */ -#define USER_EXIT "EXIT" /* Opcode: Exit the client */ - -#endif /* __ZEPHYR_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/zephyr/zephyr_internal.h Sun May 25 04:30:41 2008 +0000 @@ -0,0 +1,339 @@ +/* This file is part of the Project Athena Zephyr Notification System. + * It contains global definitions + * + * Created by: Robert French + * + * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of + * Technology. For copying and distribution information, see the + * file "mit-copyright.h". + */ + +#ifndef __ZEPHYR_H__ +#define __ZEPHYR_H__ + +#include <config.h> + +#include <glib.h> + +#include <sys/types.h> +#include <sys/time.h> + +#include <zephyr_err.h> + +#ifndef IPPROTO_MAX /* Make sure not already included */ +#ifndef WIN32 +#include <netinet/in.h> +#endif +#endif + +/* Use __STDC__ to guess whether we can use stdarg, prototypes, and const. + * This is a public header file, so autoconf can't help us here. */ +#ifdef __STDC__ +# include <stdarg.h> +# define ZP(x) x +# define ZCONST const +#else +# define ZP(x) () +# define ZCONST +#endif + +#ifdef WIN32 +/* this really should be uint32_t */ +/*typedef unsigned int in_addr_t; +struct in_addr +{ + in_addr_t s_addr; +}; */ +#include <winsock2.h> +#endif + +/* Service names */ +#define HM_SVCNAME "zephyr-hm" +#define HM_SRV_SVCNAME "zephyr-hm-srv" +#define SERVER_SVCNAME "zephyr-clt" +#define SERVER_SERVICE "zephyr" +#define SERVER_INSTANCE "zephyr" + +#define ZVERSIONHDR "ZEPH" +#define ZVERSIONMAJOR 0 +#define ZVERSIONMINOR 2 + +#define Z_MAXPKTLEN 1024 +#define Z_MAXHEADERLEN 800 +#define Z_MAXOTHERFIELDS 10 /* Max unknown fields in ZNotice_t */ +#define Z_NUMFIELDS 17 + +/* Authentication levels returned by ZCheckAuthentication */ +#define ZAUTH_FAILED (-1) +#define ZAUTH_YES 1 +#define ZAUTH_NO 0 + +typedef char ZPacket_t[Z_MAXPKTLEN]; + +/* Packet type */ +typedef enum { + UNSAFE, UNACKED, ACKED, HMACK, HMCTL, SERVACK, SERVNAK, CLIENTACK, STAT +} ZNotice_Kind_t; +extern ZCONST char *ZNoticeKinds[9]; + +/* Unique ID format */ +typedef struct _ZUnique_Id_t { + struct in_addr zuid_addr; + struct timeval tv; +} ZUnique_Id_t; + +/* Checksum */ +typedef unsigned long ZChecksum_t; + +/* Notice definition */ +typedef struct _ZNotice_t { + char *z_packet; + char *z_version; + ZNotice_Kind_t z_kind; + ZUnique_Id_t z_uid; +#define z_sender_addr z_uid.zuid_addr + struct timeval z_time; + unsigned short z_port; + int z_auth; + int z_checked_auth; + int z_authent_len; + char *z_ascii_authent; + char *z_class; + const char *z_class_inst; + char *z_opcode; + char *z_sender; + const char *z_recipient; + char *z_default_format; + char *z_multinotice; + ZUnique_Id_t z_multiuid; + ZChecksum_t z_checksum; + int z_num_other_fields; + char *z_other_fields[Z_MAXOTHERFIELDS]; + caddr_t z_message; + int z_message_len; +} ZNotice_t; + +/* Subscription structure */ +typedef struct _ZSubscriptions_t { + char *zsub_recipient; + char *zsub_class; + char *zsub_classinst; +} ZSubscription_t; + +/* Function return code */ +typedef int Code_t; + +/* Locations structure */ +typedef struct _ZLocations_t { + char *host; + char *time; + char *tty; +} ZLocations_t; + +typedef struct _ZAsyncLocateData_t { + char *user; + ZUnique_Id_t uid; + char *version; +} ZAsyncLocateData_t; + +/* for ZSetDebug */ +#ifdef Z_DEBUG +void (*__Z_debug_print) ZP((ZCONST char *fmt, va_list args, void *closure)); +void *__Z_debug_print_closure; +#endif + +int ZCompareUIDPred ZP((ZNotice_t *, void *)); +int ZCompareMultiUIDPred ZP((ZNotice_t *, void *)); + +/* Defines for ZFormatNotice, et al. */ +typedef Code_t (*Z_AuthProc) ZP((ZNotice_t*, char *, int, int *)); +Code_t ZMakeAuthentication ZP((ZNotice_t*, char *,int, int*)); + +char *ZGetSender ZP((void)); +char *ZGetVariable ZP((char *)); +Code_t ZSetVariable ZP((char *var, char *value)); +Code_t ZUnsetVariable ZP((char *var)); +int ZGetWGPort ZP((void)); +Code_t ZSetDestAddr ZP((struct sockaddr_in *)); +Code_t ZFormatNoticeList ZP((ZNotice_t*, char**, int, + char **, int*, Z_AuthProc)); +Code_t ZParseNotice ZP((char*, int, ZNotice_t *)); +Code_t ZReadAscii ZP((char*, int, unsigned char*, int)); +Code_t ZReadAscii32 ZP((char *, int, unsigned long *)); +Code_t ZReadAscii16 ZP((char *, int, unsigned short *)); +Code_t ZSendPacket ZP((char*, int, int)); +Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc)); +Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)())); +Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc)); +Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)())); +Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc)); +Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc)); +Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems, + char **buffer, int *ret_len)); +Code_t ZLocateUser ZP((char *, int *, Z_AuthProc)); +Code_t ZRequestLocations ZP((const char *, ZAsyncLocateData_t *, + ZNotice_Kind_t, Z_AuthProc)); +Code_t ZhmStat ZP((struct in_addr *, ZNotice_t *)); +Code_t ZInitialize ZP((void)); +Code_t ZSetServerState ZP((int)); +Code_t ZSetFD ZP((int)); +Code_t ZFormatSmallRawNotice ZP((ZNotice_t*, ZPacket_t, int*)); +int ZCompareUID ZP((ZUnique_Id_t*, ZUnique_Id_t*)); +Code_t ZMakeAscii ZP((char*, int, unsigned char*, int)); +Code_t ZMakeAscii32 ZP((char *, int, unsigned long)); +Code_t ZMakeAscii16 ZP((char *, int, unsigned int)); +Code_t ZReceivePacket ZP((ZPacket_t, int*, struct sockaddr_in*)); +Code_t ZCheckAuthentication ZP((ZNotice_t*, struct sockaddr_in*)); +Code_t ZSetLocation ZP((char *exposure)); +Code_t ZUnsetLocation ZP((void)); +Code_t ZFlushMyLocations ZP((void)); +Code_t ZFormatRawNotice ZP((ZNotice_t *, char**, int *)); +Code_t ZRetrieveSubscriptions ZP((unsigned short, int*)); +Code_t ZOpenPort ZP((unsigned short *port)); +Code_t ZClosePort ZP((void)); +Code_t ZFlushLocations ZP((void)); +Code_t ZFlushSubscriptions ZP((void)); +Code_t ZFreeNotice ZP((ZNotice_t *notice)); +Code_t ZParseLocations ZP((register ZNotice_t *notice, + register ZAsyncLocateData_t *zald, int *nlocs, + char **user)); +int ZCompareALDPred ZP((ZNotice_t *notice, void *zald)); +void ZFreeALD ZP((register ZAsyncLocateData_t *zald)); +Code_t ZCheckIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from, + register int (*predicate) ZP((ZNotice_t *,void *)), + void *args)); +Code_t ZPeekPacket ZP((char **buffer, int *ret_len, + struct sockaddr_in *from)); +Code_t ZPeekNotice ZP((ZNotice_t *notice, struct sockaddr_in *from)); +Code_t ZIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from, + int (*predicate) ZP((ZNotice_t *, void *)), void *args)); +Code_t ZSubscribeTo ZP((ZSubscription_t *sublist, int nitems, + unsigned int port)); +Code_t ZSubscribeToSansDefaults ZP((ZSubscription_t *sublist, int nitems, + unsigned int port)); +Code_t ZUnsubscribeTo ZP((ZSubscription_t *sublist, int nitems, + unsigned int port)); +Code_t ZCancelSubscriptions ZP((unsigned int port)); +int ZPending ZP((void)); +Code_t ZReceiveNotice ZP((ZNotice_t *notice, struct sockaddr_in *from)); +#ifdef Z_DEBUG +void Z_debug ZP((ZCONST char *, ...)); +#endif + +#undef ZP + +/* Compatibility */ +#define ZNewLocateUser ZLocateUser + +/* Macros to retrieve Zephyr library values. */ +extern int __Zephyr_fd; +extern int __Q_CompleteLength; +extern struct sockaddr_in __HM_addr; +extern char __Zephyr_realm[]; +#define ZGetFD() __Zephyr_fd +#define ZQLength() __Q_CompleteLength +#define ZGetDestAddr() __HM_addr +#define ZGetRealm() __Zephyr_realm + +#ifdef Z_DEBUG +void ZSetDebug ZP((void (*)(ZCONST char *, va_list, void *), void *)); +#define ZSetDebug(proc,closure) (__Z_debug_print=(proc), \ + __Z_debug_print_closure=(closure), \ + (void) 0) +#else +#define ZSetDebug(proc,closure) +#endif + +/* Maximum queue length */ +#define Z_MAXQLEN 30 + +/* Successful function return */ +#define ZERR_NONE 0 + +/* Hostmanager wait time (in secs) */ +#define HM_TIMEOUT 1 + +/* Server wait time (in secs) */ +#define SRV_TIMEOUT 30 + +#define ZAUTH (ZMakeAuthentication) +#define ZNOAUTH ((Z_AuthProc)0) + +/* Packet strings */ +#define ZSRVACK_SENT "SENT" /* SERVACK codes */ +#define ZSRVACK_NOTSENT "LOST" +#define ZSRVACK_FAIL "FAIL" + +/* Server internal class */ +#define ZEPHYR_ADMIN_CLASS "ZEPHYR_ADMIN" /* Class */ + +/* Control codes sent to a server */ +#define ZEPHYR_CTL_CLASS "ZEPHYR_CTL" /* Class */ + +#define ZEPHYR_CTL_CLIENT "CLIENT" /* Inst: From client */ +#define CLIENT_SUBSCRIBE "SUBSCRIBE" /* Opcode: Subscribe */ +#define CLIENT_SUBSCRIBE_NODEFS "SUBSCRIBE_NODEFS" /* Opcode: Subscribe */ +#define CLIENT_UNSUBSCRIBE "UNSUBSCRIBE" /* Opcode: Unsubsubscribe */ +#define CLIENT_CANCELSUB "CLEARSUB" /* Opcode: Clear all subs */ +#define CLIENT_GIMMESUBS "GIMME" /* Opcode: Give me subs */ +#define CLIENT_GIMMEDEFS "GIMMEDEFS" /* Opcode: Give me default + * subscriptions */ + +#define ZEPHYR_CTL_HM "HM" /* Inst: From HM */ +#define HM_BOOT "BOOT" /* Opcode: Boot msg */ +#define HM_FLUSH "FLUSH" /* Opcode: Flush me */ +#define HM_DETACH "DETACH" /* Opcode: Detach me */ +#define HM_ATTACH "ATTACH" /* Opcode: Attach me */ + +/* Control codes send to a HostManager */ +#define HM_CTL_CLASS "HM_CTL" /* Class */ + +#define HM_CTL_SERVER "SERVER" /* Inst: From server */ +#define SERVER_SHUTDOWN "SHUTDOWN" /* Opcode: Server shutdown */ +#define SERVER_PING "PING" /* Opcode: PING */ + +#define HM_CTL_CLIENT "CLIENT" /* Inst: From client */ +#define CLIENT_FLUSH "FLUSH" /* Opcode: Send flush to srv */ +#define CLIENT_NEW_SERVER "NEWSERV" /* Opcode: Find new server */ + +/* HM Statistics */ +#define HM_STAT_CLASS "HM_STAT" /* Class */ + +#define HM_STAT_CLIENT "HMST_CLIENT" /* Inst: From client */ +#define HM_GIMMESTATS "GIMMESTATS" /* Opcode: get stats */ + +/* Login class messages */ +#define LOGIN_CLASS "LOGIN" /* Class */ + +/* Class Instance is principal of user who is logging in or logging out */ + +#define EXPOSE_NONE "NONE" /* Opcode: Not visible */ +#define EXPOSE_OPSTAFF "OPSTAFF" /* Opcode: Opstaff visible */ +#define EXPOSE_REALMVIS "REALM-VISIBLE" /* Opcode: Realm visible */ +#define EXPOSE_REALMANN "REALM-ANNOUNCED"/* Opcode: Realm announced */ +#define EXPOSE_NETVIS "NET-VISIBLE" /* Opcode: Net visible */ +#define EXPOSE_NETANN "NET-ANNOUNCED" /* Opcode: Net announced */ +#define LOGIN_USER_LOGIN "USER_LOGIN" /* Opcode: user login + (from server) */ +#define LOGIN_USER_LOGOUT "USER_LOGOUT" /* Opcode: User logout */ +#define LOGIN_USER_FLUSH "USER_FLUSH" /* Opcode: flush all locs */ + +/* Locate class messages */ +#define LOCATE_CLASS "USER_LOCATE" /* Class */ + +#define LOCATE_HIDE "USER_HIDE" /* Opcode: Hide me */ +#define LOCATE_UNHIDE "USER_UNHIDE" /* Opcode: Unhide me */ + +/* Class Instance is principal of user to locate */ +#define LOCATE_LOCATE "LOCATE" /* Opcode: Locate user */ + +/* WG_CTL class messages */ +#define WG_CTL_CLASS "WG_CTL" /* Class */ + +#define WG_CTL_USER "USER" /* Inst: User request */ +#define USER_REREAD "REREAD" /* Opcode: Reread desc file */ +#define USER_SHUTDOWN "SHUTDOWN" /* Opcode: Go catatonic */ +#define USER_STARTUP "STARTUP" /* Opcode: Come out of it */ +#define USER_EXIT "EXIT" /* Opcode: Exit the client */ + +#endif /* __ZEPHYR_H__ */
--- a/libpurple/proxy.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/proxy.c Sun May 25 04:30:41 2008 +0000 @@ -837,33 +837,10 @@ } static void -http_canwrite(gpointer data, gint source, PurpleInputCondition cond) -{ +http_start_connect_tunneling(PurpleProxyConnectData *connect_data) { GString *request; - PurpleProxyConnectData *connect_data; - int error = ETIMEDOUT; int ret; - connect_data = data; - - purple_debug_info("proxy", "Connected to %s:%d.\n", - connect_data->host, connect_data->port); - - if (connect_data->inpa > 0) - { - purple_input_remove(connect_data->inpa); - connect_data->inpa = 0; - } - - ret = purple_input_get_error(connect_data->fd, &error); - if ((ret != 0) || (error != 0)) - { - if (ret != 0) - error = errno; - purple_proxy_connect_data_disconnect(connect_data, g_strerror(error)); - return; - } - purple_debug_info("proxy", "Using CONNECT tunneling for %s:%d\n", connect_data->host, connect_data->port); @@ -912,7 +889,45 @@ connect_data->inpa = purple_input_add(connect_data->fd, PURPLE_INPUT_WRITE, proxy_do_write, connect_data); - proxy_do_write(connect_data, connect_data->fd, cond); + proxy_do_write(connect_data, connect_data->fd, PURPLE_INPUT_WRITE); +} + +static void +http_canwrite(gpointer data, gint source, PurpleInputCondition cond) { + PurpleProxyConnectData *connect_data = data; + int ret, error = ETIMEDOUT; + + purple_debug_info("proxy", "Connected to %s:%d.\n", + connect_data->host, connect_data->port); + + if (connect_data->inpa > 0) { + purple_input_remove(connect_data->inpa); + connect_data->inpa = 0; + } + + ret = purple_input_get_error(connect_data->fd, &error); + if (ret != 0 || error != 0) { + if (ret != 0) + error = errno; + purple_proxy_connect_data_disconnect(connect_data, g_strerror(error)); + return; + } + + if (connect_data->port == 80) { + /* + * If we're trying to connect to something running on + * port 80 then we assume the traffic using this + * connection is going to be HTTP traffic. If it's + * not then this will fail (uglily). But it's good + * to avoid using the CONNECT method because it's + * not always allowed. + */ + purple_debug_info("proxy", "HTTP proxy connection established\n"); + purple_proxy_connect_data_connected(connect_data); + } else { + http_start_connect_tunneling(connect_data); + } + } static void @@ -940,39 +955,15 @@ fcntl(connect_data->fd, F_SETFD, FD_CLOEXEC); #endif - if (connect(connect_data->fd, addr, addrlen) != 0) - { - if ((errno == EINPROGRESS) || (errno == EINTR)) - { + if (connect(connect_data->fd, addr, addrlen) != 0) { + if (errno == EINPROGRESS || errno == EINTR) { purple_debug_info("proxy", "Connection in progress\n"); - if (connect_data->port != 80) - { - /* we need to do CONNECT first */ - connect_data->inpa = purple_input_add(connect_data->fd, - PURPLE_INPUT_WRITE, http_canwrite, connect_data); - } - else - { - /* - * If we're trying to connect to something running on - * port 80 then we assume the traffic using this - * connection is going to be HTTP traffic. If it's - * not then this will fail (uglily). But it's good - * to avoid using the CONNECT method because it's - * not always allowed. - */ - purple_debug_info("proxy", "HTTP proxy connection established\n"); - purple_proxy_connect_data_connected(connect_data); - } - } - else - { + connect_data->inpa = purple_input_add(connect_data->fd, + PURPLE_INPUT_WRITE, http_canwrite, connect_data); + } else purple_proxy_connect_data_disconnect(connect_data, g_strerror(errno)); - } - } - else - { + } else { purple_debug_info("proxy", "Connected immediately.\n"); http_canwrite(connect_data, connect_data->fd, PURPLE_INPUT_WRITE); @@ -1145,7 +1136,7 @@ int len; if (connect_data->read_buffer == NULL) { - connect_data->read_buf_len = 4; + connect_data->read_buf_len = 5; connect_data->read_buffer = g_malloc(connect_data->read_buf_len); connect_data->read_len = 0; } @@ -1214,6 +1205,11 @@ return; buf += 4 + 16; break; + default: + purple_debug_error("socks5 proxy", "Invalid ATYP received (0x%X)\n", buf[3]); + purple_proxy_connect_data_disconnect(connect_data, + _("Received invalid data on connection with server.")); + return; } /* Skip past BND.PORT */
--- a/libpurple/prpl.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/prpl.h Sun May 25 04:30:41 2008 +0000 @@ -399,9 +399,45 @@ gboolean (*send_attention)(PurpleConnection *gc, const char *username, guint type); GList *(*get_attention_types)(PurpleAccount *acct); - void (*_purple_reserved4)(void); + /** + * The size of the PurplePluginProtocolInfo. This should always be sizeof(PurplePluginProtocolInfo). + * This allows adding more functions to this struct without requiring a major version bump. + */ + unsigned long struct_size; + + /* NOTE: + * If more functions are added, they should accessed using the following syntax: + * + * if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, new_function)) + * prpl->new_function(...); + * + * instead of + * + * if (prpl->new_function != NULL) + * prpl->new_function(...); + * + * The PURPLE_PROTOCOL_PLUGIN_HAS_FUNC macro can be used for the older member + * functions (e.g. login, send_im etc.) too. + */ + + /** This allows protocols to specify additional strings to be used for + * various purposes. The idea is to stuff a bunch of strings in this hash + * table instead of expanding the struct for every addition. This hash + * table is allocated every call and MUST be unrefed by the caller. + * + * @param account The account to specify. This can be NULL. + * @return The protocol's string hash table. The hash table should be + * destroyed by the caller when it's no longer needed. + */ + GHashTable *(*get_account_text_table)(PurpleAccount *account); }; +#define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \ + (((G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < G_STRUCT_OFFSET(PurplePluginProtocolInfo, struct_size)) \ + || (G_STRUCT_OFFSET(PurplePluginProtocolInfo, member) < prpl->struct_size)) && \ + prpl->member != NULL) + + #define PURPLE_IS_PROTOCOL_PLUGIN(plugin) \ ((plugin)->info->type == PURPLE_PLUGIN_PROTOCOL)
--- a/libpurple/purple-remote Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/purple-remote Sun May 25 04:30:41 2008 +0000 @@ -35,7 +35,7 @@ raise "Error: " + self.attr + " " + str(args) + " returned " + str(result) return result -def show_help(): +def show_help(requested=False): print """This program uses D-Bus to communicate with purple. Usage: @@ -66,6 +66,10 @@ PurpleAccountsFindConnected?name=&protocol=prpl-jabber PurpleAccountsFindConnected(,prpl-jabber) """ % sys.argv[0] + if (requested): + sys.exit(0) + else: + sys.exit(1) cpurple = CheckedObject(purple) @@ -213,10 +217,11 @@ raise "Don't know how to handle type \"%s\"" % type return purple.__getattr__(command)(*methodparams) show_help() - raise "Unknown command: %s" % command if len(sys.argv) == 1: show_help() +elif (sys.argv[1] == "--help" or sys.argv[1] == "-h"): + show_help(True) elif (obj == None): print "No existing libpurple instance detected." sys.exit(1);
--- a/libpurple/purple-url-handler Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/purple-url-handler Sun May 25 04:30:41 2008 +0000 @@ -295,10 +295,14 @@ def main(argv=sys.argv): - if len(argv) != 2: + if len(argv) != 2 or argv[1] == "--help" or argv[1] == "-h": print "Usage: %s URI" % argv[0] print "Example: %s \"xmpp:romeo@montague.net?message\"" % argv[0] - return + + if len(argv) != 2: + sys.exit(1) + else: + return 0 uri = argv[1] type = uri.split(":")[0]
--- a/libpurple/server.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/server.c Sun May 25 04:30:41 2008 +0000 @@ -671,8 +671,11 @@ if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->set_permit_deny == NULL) { /* protocol does not support privacy, handle it ourselves */ - if (!purple_privacy_check(account, who)) + if (!purple_privacy_check(account, who)) { + purple_signal_emit(purple_conversations_get_handle(), "blocked-im-msg", + account, who, msg, flags, (unsigned int)mtime); return; + } } /* @@ -891,8 +894,11 @@ account = purple_connection_get_account(gc); if (PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->set_permit_deny == NULL) { /* protocol does not support privacy, handle it ourselves */ - if (!purple_privacy_check(account, who)) + if (!purple_privacy_check(account, who)) { + purple_signal_emit(purple_conversations_get_handle(), "chat-invite-blocked", + account, who, name, message, data); return; + } } plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1( @@ -979,6 +985,12 @@ purple_signal_emit(purple_conversations_get_handle(), "chat-left", conv); } +void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data) +{ + purple_signal_emit(purple_conversations_get_handle(), "chat-join-failed", + gc, data); +} + void serv_got_chat_in(PurpleConnection *g, int id, const char *who, PurpleMessageFlags flags, const char *message, time_t mtime) {
--- a/libpurple/server.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/server.h Sun May 25 04:30:41 2008 +0000 @@ -166,6 +166,17 @@ PurpleConversation *serv_got_joined_chat(PurpleConnection *gc, int id, const char *name); +/** + * Called by a prpl when an attempt to join a chat via serv_join_chat() + * fails. + * + * @param gc The connection on which chat joining failed + * @param data The components passed to serv_join_chat() originally. + * The hash function should be g_str_hash() and the equal + * function should be g_str_equal(). + */ +void purple_serv_got_join_chat_failed(PurpleConnection *gc, GHashTable *data); + void serv_got_chat_left(PurpleConnection *g, int id); void serv_got_chat_in(PurpleConnection *g, int id, const char *who, PurpleMessageFlags flags, const char *message, time_t mtime);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/smiley.c Sun May 25 04:30:41 2008 +0000 @@ -0,0 +1,915 @@ +/** + * @file smiley.c Simley API + * @ingroup core + */ + +/* purple + * + * Purple 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" +#include "dbus-maybe.h" +#include "debug.h" +#include "imgstore.h" +#include "smiley.h" +#include "util.h" +#include "xmlnode.h" + +/**************************************************************************/ +/* Main structures, members and constants */ +/**************************************************************************/ + +struct _PurpleSmiley +{ + GObject parent; + PurpleStoredImage *img; /**< The id of the stored image with the + the smiley data. */ + char *shortcut; /**< Shortcut associated with the custom + smiley. This field will work as a + unique key by this API. */ + char *checksum; /**< The smiley checksum. */ +}; + +struct _PurpleSmileyClass +{ + GObjectClass parent_class; +}; + +static GHashTable *smiley_shortcut_index = NULL; /* shortcut (char *) => smiley (PurpleSmiley*) */ +static GHashTable *smiley_checksum_index = NULL; /* checksum (char *) => smiley (PurpleSmiley*) */ + +static guint save_timer = 0; +static gboolean smileys_loaded = FALSE; +static char *smileys_dir = NULL; + +#define SMILEYS_DEFAULT_FOLDER "custom_smiley" +#define SMILEYS_LOG_ID "smileys" + +#define XML_FILE_NAME "smileys.xml" + +#define XML_ROOT_TAG "smileys" +#define XML_PROFILE_TAG "profile" +#define XML_PROFILE_NAME_ATTRIB_TAG "name" +#define XML_ACCOUNT_TAG "account" +#define XML_ACCOUNT_USERID_ATTRIB_TAG "userid" +#define XML_SMILEY_SET_TAG "smiley_set" +#define XML_SMILEY_TAG "smiley" +#define XML_SHORTCUT_ATTRIB_TAG "shortcut" +#define XML_CHECKSUM_ATRIB_TAG "checksum" +#define XML_FILENAME_ATRIB_TAG "filename" + + +/****************************************************************************** + * XML descriptor file layout * + ****************************************************************************** + * + * Althought we are creating the profile XML structure here, now we + * won't handle it. + * So, we just add one profile named "default" that has no associated + * account elements, and have only the smiley_set that will contain + * all existent custom smiley. + * + * It's our "Highlander Profile" :-) + * + ****************************************************************************** + * + * <smileys> + * <profile name="john.doe"> + * <account userid="john.doe@jabber.org"> + * <account userid="john.doe@gmail.com"> + * <smiley_set> + * <smiley shortcut="aaa" checksum="xxxxxxxx" filename="file_name1.gif"/> + * <smiley shortcut="bbb" checksum="yyyyyyy" filename="file_name2.gif"/> + * </smiley_set> + * </profile> + * </smiley> + * + *****************************************************************************/ + + +/********************************************************************* + * Forward declarations * + *********************************************************************/ + +static gboolean read_smiley_file(const char *path, guchar **data, size_t *len); + +static char *get_file_full_path(const char *filename); + +static PurpleSmiley *purple_smiley_create(const char *shortcut); + +static PurpleSmiley *purple_smiley_load_file(const char *shortcut, const char *checksum, + const char *filename); + +static void +purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data, + size_t smiley_data_len, const char *filename); + +static void +purple_smiley_data_store(PurpleStoredImage *stored_img); + +static void +purple_smiley_data_unstore(const char *filename); + +/********************************************************************* + * Writing to disk * + *********************************************************************/ + +static xmlnode * +smiley_to_xmlnode(PurpleSmiley *smiley) +{ + xmlnode *smiley_node = NULL; + + smiley_node = xmlnode_new(XML_SMILEY_TAG); + + if (!smiley_node) + return NULL; + + xmlnode_set_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG, + smiley->shortcut); + + xmlnode_set_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG, + smiley->checksum); + + xmlnode_set_attrib(smiley_node, XML_FILENAME_ATRIB_TAG, + purple_imgstore_get_filename(smiley->img)); + + return smiley_node; +} + +static void +add_smiley_to_main_node(gpointer key, gpointer value, gpointer user_data) +{ + xmlnode *child_node; + + child_node = smiley_to_xmlnode(value); + xmlnode_insert_child((xmlnode*)user_data, child_node); +} + +static xmlnode * +smileys_to_xmlnode() +{ + xmlnode *root_node, *profile_node, *smileyset_node; + + root_node = xmlnode_new(XML_ROOT_TAG); + xmlnode_set_attrib(root_node, "version", "1.0"); + + /* See the top comment's above to understand why initial tag elements + * are not being considered by now. */ + profile_node = xmlnode_new(XML_PROFILE_TAG); + if (profile_node) { + xmlnode_set_attrib(profile_node, XML_PROFILE_NAME_ATTRIB_TAG, "Default"); + xmlnode_insert_child(root_node, profile_node); + + smileyset_node = xmlnode_new(XML_SMILEY_SET_TAG); + if (smileyset_node) { + xmlnode_insert_child(profile_node, smileyset_node); + g_hash_table_foreach(smiley_shortcut_index, add_smiley_to_main_node, smileyset_node); + } + } + + return root_node; +} + +static void +sync_smileys() +{ + xmlnode *root_node; + char *data; + + if (!smileys_loaded) { + purple_debug_error(SMILEYS_LOG_ID, "Attempted to save smileys before it " + "was read!\n"); + return; + } + + root_node = smileys_to_xmlnode(); + data = xmlnode_to_formatted_str(root_node, NULL); + purple_util_write_data_to_file(XML_FILE_NAME, data, -1); + + g_free(data); + xmlnode_free(root_node); +} + +static gboolean +save_smileys_cb(gpointer data) +{ + sync_smileys(); + save_timer = 0; + return FALSE; +} + +static void +purple_smileys_save() +{ + if (save_timer == 0) + save_timer = purple_timeout_add_seconds(5, save_smileys_cb, NULL); +} + + +/********************************************************************* + * Reading from disk * + *********************************************************************/ + +static PurpleSmiley * +parse_smiley(xmlnode *smiley_node) +{ + PurpleSmiley *smiley; + const char *shortcut = NULL; + const char *checksum = NULL; + const char *filename = NULL; + + shortcut = xmlnode_get_attrib(smiley_node, XML_SHORTCUT_ATTRIB_TAG); + checksum = xmlnode_get_attrib(smiley_node, XML_CHECKSUM_ATRIB_TAG); + filename = xmlnode_get_attrib(smiley_node, XML_FILENAME_ATRIB_TAG); + + if ((shortcut == NULL) || (checksum == NULL) || (filename == NULL)) + return NULL; + + smiley = purple_smiley_load_file(shortcut, checksum, filename); + + return smiley; +} + +static void +purple_smileys_load() +{ + xmlnode *root_node, *profile_node; + xmlnode *smileyset_node = NULL; + xmlnode *smiley_node; + + smileys_loaded = TRUE; + + root_node = purple_util_read_xml_from_file(XML_FILE_NAME, + _(SMILEYS_LOG_ID)); + + if (root_node == NULL) + return; + + /* See the top comment's above to understand why initial tag elements + * are not being considered by now. */ + profile_node = xmlnode_get_child(root_node, XML_PROFILE_TAG); + if (profile_node) + smileyset_node = xmlnode_get_child(profile_node, XML_SMILEY_SET_TAG); + + if (smileyset_node) { + smiley_node = xmlnode_get_child(smileyset_node, XML_SMILEY_TAG); + for (; smiley_node != NULL; + smiley_node = xmlnode_get_next_twin(smiley_node)) { + PurpleSmiley *smiley; + + smiley = parse_smiley(smiley_node); + } + } + + xmlnode_free(root_node); +} + +/********************************************************************* + * GObject Stuff * + *********************************************************************/ +enum +{ + PROP_0, + PROP_SHORTCUT, + PROP_IMGSTORE, +}; + +#define PROP_SHORTCUT_S "shortcut" +#define PROP_IMGSTORE_S "image" + +enum +{ + SIG_DESTROY, + SIG_LAST +}; + +static guint signals[SIG_LAST]; +static GObjectClass *parent_class; + +static void +purple_smiley_init(GTypeInstance *instance, gpointer klass) +{ + PurpleSmiley *smiley = PURPLE_SMILEY(instance); + PURPLE_DBUS_REGISTER_POINTER(smiley, PurpleSmiley); +} + +static void +purple_smiley_get_property(GObject *object, guint param_id, GValue *value, + GParamSpec *spec) +{ + PurpleSmiley *smiley = PURPLE_SMILEY(object); + switch (param_id) { + case PROP_SHORTCUT: + g_value_set_string(value, smiley->shortcut); + break; + case PROP_IMGSTORE: + g_value_set_pointer(value, smiley->img); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec); + break; + } +} + +static void +purple_smiley_set_property(GObject *object, guint param_id, const GValue *value, + GParamSpec *spec) +{ + PurpleSmiley *smiley = PURPLE_SMILEY(object); + switch (param_id) { + case PROP_SHORTCUT: + { + const char *shortcut = g_value_get_string(value); + purple_smiley_set_shortcut(smiley, shortcut); + } + break; + case PROP_IMGSTORE: + { + PurpleStoredImage *img = g_value_get_pointer(value); + + purple_imgstore_unref(smiley->img); + g_free(smiley->checksum); + + smiley->img = img; + if (img) { + smiley->checksum = purple_util_get_image_checksum( + purple_imgstore_get_data(img), + purple_imgstore_get_size(img)); + purple_smiley_data_store(img); + } else { + smiley->checksum = NULL; + } + + g_object_notify(object, PROP_IMGSTORE_S); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, spec); + break; + } +} + +static void +purple_smiley_finalize(GObject *obj) +{ + PurpleSmiley *smiley = PURPLE_SMILEY(obj); + + if (g_hash_table_lookup(smiley_shortcut_index, smiley->shortcut)) { + g_hash_table_remove(smiley_shortcut_index, smiley->shortcut); + g_hash_table_remove(smiley_checksum_index, smiley->checksum); + } + + g_free(smiley->shortcut); + g_free(smiley->checksum); + if (smiley->img) + purple_smiley_data_unstore(purple_imgstore_get_filename(smiley->img)); + purple_imgstore_unref(smiley->img); + + PURPLE_DBUS_UNREGISTER_POINTER(smiley); + + purple_smileys_save(); +} + +static void +purple_smiley_dispose(GObject *gobj) +{ + g_signal_emit(gobj, signals[SIG_DESTROY], 0); + parent_class->dispose(gobj); +} + +static void +purple_smiley_class_init(PurpleSmileyClass *klass) +{ + GObjectClass *gobj_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + + parent_class = g_type_class_peek_parent(klass); + + gobj_class->get_property = purple_smiley_get_property; + gobj_class->set_property = purple_smiley_set_property; + gobj_class->finalize = purple_smiley_finalize; + gobj_class->dispose = purple_smiley_dispose; + + /* Shortcut */ + pspec = g_param_spec_string(PROP_SHORTCUT_S, _("Shortcut"), + _("The text-shortcut for the smiley"), + NULL, + G_PARAM_READWRITE); + g_object_class_install_property(gobj_class, PROP_SHORTCUT, pspec); + + /* Stored Image */ + pspec = g_param_spec_pointer(PROP_IMGSTORE_S, _("Stored Image"), + _("Stored Image. (that'll have to do for now)"), + G_PARAM_READWRITE); + g_object_class_install_property(gobj_class, PROP_IMGSTORE, pspec); + + signals[SIG_DESTROY] = g_signal_new("destroy", + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +GType +purple_smiley_get_type(void) +{ + static GType type = 0; + + if(type == 0) { + static const GTypeInfo info = { + sizeof(PurpleSmileyClass), + NULL, + NULL, + (GClassInitFunc)purple_smiley_class_init, + NULL, + NULL, + sizeof(PurpleSmiley), + 0, + purple_smiley_init, + NULL, + }; + + type = g_type_register_static(G_TYPE_OBJECT, + "PurpleSmiley", + &info, 0); + } + + return type; +} + +/********************************************************************* + * Other Stuff * + *********************************************************************/ + +static char *get_file_full_path(const char *filename) +{ + char *path; + + path = g_build_filename(purple_smileys_get_storing_dir(), filename, NULL); + + if (!g_file_test(path, G_FILE_TEST_EXISTS)) { + g_free(path); + return NULL; + } + + return path; +} + +static PurpleSmiley * +purple_smiley_load_file(const char *shortcut, const char *checksum, const char *filename) +{ + PurpleSmiley *smiley = NULL; + guchar *smiley_data; + size_t smiley_data_len; + char *fullpath = NULL; + + g_return_val_if_fail(shortcut != NULL, NULL); + g_return_val_if_fail(checksum != NULL, NULL); + g_return_val_if_fail(filename != NULL, NULL); + + fullpath = get_file_full_path(filename); + if (!fullpath) + return NULL; + + smiley = purple_smiley_create(shortcut); + if (!smiley) { + g_free(fullpath); + return NULL; + } + + smiley->checksum = g_strdup(checksum); + + if (read_smiley_file(fullpath, &smiley_data, &smiley_data_len)) + purple_smiley_set_data_impl(smiley, smiley_data, + smiley_data_len, filename); + else + purple_smiley_delete(smiley); + + g_free(fullpath); + + return smiley; +} + +static void +purple_smiley_data_store(PurpleStoredImage *stored_img) +{ + const char *dirname; + char *path; + FILE *file = NULL; + + g_return_if_fail(stored_img != NULL); + + if (!smileys_loaded) + return; + + dirname = purple_smileys_get_storing_dir(); + path = g_build_filename(dirname, purple_imgstore_get_filename(stored_img), NULL); + + if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) { + purple_debug_info(SMILEYS_LOG_ID, "Creating smileys directory.\n"); + + if (g_mkdir(dirname, S_IRUSR | S_IWUSR | S_IXUSR) < 0) { + purple_debug_error(SMILEYS_LOG_ID, + "Unable to create directory %s: %s\n", + dirname, g_strerror(errno)); + } + } + + if ((file = g_fopen(path, "wb")) != NULL) { + if (!fwrite(purple_imgstore_get_data(stored_img), + purple_imgstore_get_size(stored_img), 1, file)) { + purple_debug_error(SMILEYS_LOG_ID, "Error writing %s: %s\n", + path, g_strerror(errno)); + } else { + purple_debug_info(SMILEYS_LOG_ID, "Wrote cache file: %s\n", path); + } + + fclose(file); + } else { + purple_debug_error(SMILEYS_LOG_ID, "Unable to create file %s: %s\n", + path, g_strerror(errno)); + g_free(path); + + return; + } + + g_free(path); +} + +static void +purple_smiley_data_unstore(const char *filename) +{ + const char *dirname; + char *path; + + g_return_if_fail(filename != NULL); + + dirname = purple_smileys_get_storing_dir(); + path = g_build_filename(dirname, filename, NULL); + + if (g_file_test(path, G_FILE_TEST_EXISTS)) { + if (g_unlink(path)) + purple_debug_error(SMILEYS_LOG_ID, "Failed to delete %s: %s\n", + path, g_strerror(errno)); + else + purple_debug_info(SMILEYS_LOG_ID, "Deleted cache file: %s\n", path); + } + + g_free(path); +} + +static gboolean +read_smiley_file(const char *path, guchar **data, size_t *len) +{ + GError *err = NULL; + + if (!g_file_get_contents(path, (gchar **)data, len, &err)) { + purple_debug_error(SMILEYS_LOG_ID, "Error reading %s: %s\n", + path, err->message); + g_error_free(err); + + return FALSE; + } + + return TRUE; +} + +static PurpleStoredImage * +purple_smiley_data_new(guchar *smiley_data, size_t smiley_data_len) +{ + char *filename; + PurpleStoredImage *stored_img; + + g_return_val_if_fail(smiley_data != NULL, NULL); + g_return_val_if_fail(smiley_data_len > 0, NULL); + + filename = purple_util_get_image_filename(smiley_data, smiley_data_len); + + if (filename == NULL) { + g_free(smiley_data); + return NULL; + } + + stored_img = purple_imgstore_add(smiley_data, smiley_data_len, filename); + + g_free(filename); + + return stored_img; +} + +static void +purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data, + size_t smiley_data_len, const char *filename) +{ + PurpleStoredImage *old_img, *new_img; + const char *old_filename = NULL; + const char *new_filename = NULL; + + g_return_if_fail(smiley != NULL); + g_return_if_fail(smiley_data != NULL); + g_return_if_fail(smiley_data_len > 0); + + old_img = smiley->img; + + if (filename) + new_img = purple_imgstore_add(smiley_data, smiley_data_len, filename); + else + new_img = purple_smiley_data_new(smiley_data, smiley_data_len); + + g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, new_img, NULL); + + /* If the old and new image files have different names we need + * to unstore old image file. */ + if (!old_img) + return; + + old_filename = purple_imgstore_get_filename(old_img); + new_filename = purple_imgstore_get_filename(smiley->img); + + if (g_ascii_strcasecmp(old_filename, new_filename)) { + purple_smiley_data_unstore(old_filename); + purple_imgstore_unref(old_img); + } +} + + +/***************************************************************************** + * Public API functions * + *****************************************************************************/ + +static PurpleSmiley * +purple_smiley_create(const char *shortcut) +{ + PurpleSmiley *smiley; + + smiley = PURPLE_SMILEY(g_object_new(PURPLE_TYPE_SMILEY, PROP_SHORTCUT_S, shortcut, NULL)); + + return smiley; +} + +PurpleSmiley * +purple_smiley_new(PurpleStoredImage *img, const char *shortcut) +{ + PurpleSmiley *smiley = NULL; + + g_return_val_if_fail(shortcut != NULL, NULL); + g_return_val_if_fail(img != NULL, NULL); + + smiley = purple_smileys_find_by_shortcut(shortcut); + if (smiley) + return smiley; + + smiley = purple_smiley_create(shortcut); + if (!smiley) + return NULL; + + g_object_set(G_OBJECT(smiley), PROP_IMGSTORE_S, img, NULL); + + return smiley; +} + +static PurpleSmiley * +purple_smiley_new_from_stream(const char *shortcut, guchar *smiley_data, + size_t smiley_data_len, const char *filename) +{ + PurpleSmiley *smiley; + + g_return_val_if_fail(shortcut != NULL, NULL); + g_return_val_if_fail(smiley_data != NULL, NULL); + g_return_val_if_fail(smiley_data_len > 0, NULL); + + smiley = purple_smileys_find_by_shortcut(shortcut); + if (smiley) + return smiley; + + /* purple_smiley_create() sets shortcut */ + smiley = purple_smiley_create(shortcut); + if (!smiley) + return NULL; + + purple_smiley_set_data_impl(smiley, smiley_data, smiley_data_len, filename); + + purple_smiley_data_store(smiley->img); + + return smiley; +} + +PurpleSmiley * +purple_smiley_new_from_file(const char *shortcut, const char *filepath) +{ + PurpleSmiley *smiley = NULL; + guchar *smiley_data; + size_t smiley_data_len; + char *filename; + + g_return_val_if_fail(shortcut != NULL, NULL); + g_return_val_if_fail(filepath != NULL, NULL); + + filename = g_path_get_basename(filepath); + if (read_smiley_file(filepath, &smiley_data, &smiley_data_len)) + smiley = purple_smiley_new_from_stream(shortcut, smiley_data, + smiley_data_len, filename); + g_free(filename); + + return smiley; +} + +void +purple_smiley_delete(PurpleSmiley *smiley) +{ + g_return_if_fail(smiley != NULL); + + g_object_unref(smiley); +} + +gboolean +purple_smiley_set_shortcut(PurpleSmiley *smiley, const char *shortcut) +{ + g_return_val_if_fail(smiley != NULL, FALSE); + g_return_val_if_fail(shortcut != NULL, FALSE); + + /* Check out whether the new shortcut is already being used. */ + if (g_hash_table_lookup(smiley_shortcut_index, shortcut)) + return FALSE; + + /* Remove the old shortcut. */ + if (smiley->shortcut) + g_hash_table_remove(smiley_shortcut_index, smiley->shortcut); + + /* Insert the new shortcut. */ + g_hash_table_insert(smiley_shortcut_index, g_strdup(shortcut), smiley); + + g_free(smiley->shortcut); + smiley->shortcut = g_strdup(shortcut); + + g_object_notify(G_OBJECT(smiley), PROP_SHORTCUT_S); + + purple_smileys_save(); + + return TRUE; +} + +void +purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data, + size_t smiley_data_len, gboolean keepfilename) +{ + g_return_if_fail(smiley != NULL); + g_return_if_fail(smiley_data != NULL); + g_return_if_fail(smiley_data_len > 0); + + /* Remove the previous entry */ + g_hash_table_remove(smiley_checksum_index, smiley->checksum); + + /* Update the file data. This also updates the checksum. */ + if ((keepfilename) && (smiley->img) && + (purple_imgstore_get_filename(smiley->img))) + purple_smiley_set_data_impl(smiley, smiley_data, + smiley_data_len, + purple_imgstore_get_filename(smiley->img)); + else + purple_smiley_set_data_impl(smiley, smiley_data, + smiley_data_len, NULL); + + /* Reinsert the index item. */ + g_hash_table_insert(smiley_checksum_index, g_strdup(smiley->checksum), smiley); + + purple_smileys_save(); +} + +PurpleStoredImage * +purple_smiley_get_stored_image(const PurpleSmiley *smiley) +{ + return purple_imgstore_ref(smiley->img); +} + +const char *purple_smiley_get_shortcut(const PurpleSmiley *smiley) +{ + g_return_val_if_fail(smiley != NULL, NULL); + + return smiley->shortcut; +} + +const char * +purple_smiley_get_checksum(const PurpleSmiley *smiley) +{ + g_return_val_if_fail(smiley != NULL, NULL); + + return smiley->checksum; +} + +gconstpointer +purple_smiley_get_data(const PurpleSmiley *smiley, size_t *len) +{ + g_return_val_if_fail(smiley != NULL, NULL); + + if (smiley->img) { + if (len != NULL) + *len = purple_imgstore_get_size(smiley->img); + + return purple_imgstore_get_data(smiley->img); + } + + return NULL; +} + +const char * +purple_smiley_get_extension(const PurpleSmiley *smiley) +{ + if (smiley->img != NULL) + return purple_imgstore_get_extension(smiley->img); + + return NULL; +} + +char *purple_smiley_get_full_path(PurpleSmiley *smiley) +{ + g_return_val_if_fail(smiley != NULL, NULL); + + if (smiley->img == NULL) + return NULL; + + return get_file_full_path(purple_imgstore_get_filename(smiley->img)); +} + +static void add_smiley_to_list(gpointer key, gpointer value, gpointer user_data) +{ + GList** returninglist = (GList**)user_data; + + *returninglist = g_list_append(*returninglist, value); +} + +GList * +purple_smileys_get_all(void) +{ + GList *returninglist = NULL; + + g_hash_table_foreach(smiley_shortcut_index, add_smiley_to_list, &returninglist); + + return returninglist; +} + +PurpleSmiley * +purple_smileys_find_by_shortcut(const char *shortcut) +{ + g_return_val_if_fail(shortcut != NULL, NULL); + + return g_hash_table_lookup(smiley_shortcut_index, shortcut); +} + +PurpleSmiley * +purple_smileys_find_by_checksum(const char *checksum) +{ + g_return_val_if_fail(checksum != NULL, NULL); + + return g_hash_table_lookup(smiley_checksum_index, checksum); +} + +const char * +purple_smileys_get_storing_dir(void) +{ + return smileys_dir; +} + +void +purple_smileys_init() +{ + smiley_shortcut_index = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + smiley_checksum_index = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + + smileys_dir = g_build_filename(purple_user_dir(), SMILEYS_DEFAULT_FOLDER, NULL); + + purple_smileys_load(); +} + +void +purple_smileys_uninit() +{ + if (save_timer != 0) { + purple_timeout_remove(save_timer); + save_timer = 0; + sync_smileys(); + } + + g_hash_table_destroy(smiley_shortcut_index); + g_hash_table_destroy(smiley_checksum_index); + g_free(smileys_dir); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/smiley.h Sun May 25 04:30:41 2008 +0000 @@ -0,0 +1,271 @@ +/** + * @file smiley.h Smiley API + * @ingroup core + * @since 2.5.0 + */ + +/* purple + * + * Purple 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + * + */ + +#ifndef _PURPLE_SMILEY_H_ +#define _PURPLE_SMILEY_H_ + +#include <glib-object.h> + +#include "imgstore.h" +#include "util.h" + +/** + * A custom smiley. + * This contains everything Purple will ever need to know about a custom smiley. + * Everything. + * + * PurpleSmiley is a GObject. + */ +typedef struct _PurpleSmiley PurpleSmiley; +typedef struct _PurpleSmileyClass PurpleSmileyClass; + +#define PURPLE_TYPE_SMILEY (purple_smiley_get_type ()) +#define PURPLE_SMILEY(smiley) (G_TYPE_CHECK_INSTANCE_CAST ((smiley), PURPLE_TYPE_SMILEY, PurpleSmiley)) +#define PURPLE_SMILEY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PURPLE_TYPE_SMILEY, PurpleSmileyClass)) +#define PURPLE_IS_SMILEY(smiley) (G_TYPE_CHECK_INSTANCE_TYPE ((smiley), PURPLE_TYPE_SMILEY)) +#define PURPLE_IS_SMILEY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PURPLE_TYPE_SMILEY)) +#define PURPLE_SMILEY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PURPLE_TYPE_SMILEY, PurpleSmileyClass)) + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************/ +/** @name Custom Smiley API */ +/**************************************************************************/ +/*@{*/ + +/** + * GObject foo. + * @internal. + */ +GType purple_smiley_get_type(void); + +/** + * Creates a new custom smiley structure and populates it. + * + * If a custom smiley with the informed shortcut already exist, it + * will be automaticaly returned. + * + * @param img The image associated with the smiley. + * @param shortcut The custom smiley associated shortcut. + * + * @return The custom smiley structure filled up. + */ +PurpleSmiley * +purple_smiley_new(PurpleStoredImage *img, const char *shortcut); + +/** + * Creates a new custom smiley structure and populates it. + * + * The data is retrieved from an already existent file. + * + * If a custom smiley with the informed shortcut already exist, it + * will be automaticaly returned. + * + * @param shortcut The custom smiley associated shortcut. + * @param filepath The image file to be imported to a + * new custom smiley. + * + * @return The custom smiley structure filled up. + */ +PurpleSmiley * +purple_smiley_new_from_file(const char *shortcut, const char *filepath); + +/** + * Destroy the custom smiley and release the associated resources. + * + * @param smiley The custom smiley. + */ +void +purple_smiley_delete(PurpleSmiley *smiley); + +/** + * Changes the custom smiley's shortcut. + * + * @param smiley The custom smiley. + * @param shortcut The custom smiley associated shortcut. + * + * @return TRUE whether the shortcut is not associated with another + * custom smiley and the parameters are valid. FALSE otherwise. + */ +gboolean +purple_smiley_set_shortcut(PurpleSmiley *smiley, const char *shortcut); + +/** + * Changes the custom smiley's data. + * + * When the filename controling is made outside this API, the param + * #keepfilename must be TRUE. + * Otherwise, the file and filename will be regenerated, and the + * old one will be removed. + * + * @param smiley The custom smiley. + * @param smiley_data The custom smiley data. + * @param smiley_data_len The custom smiley data length. + * @param keepfilename The current custom smiley's filename must be + * kept. + */ +void +purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data, + size_t smiley_data_len, gboolean keepfilename); + +/** + * Returns the custom smiley's associated shortcut. + * + * @param smiley The custom smiley. + * + * @return The shortcut. + */ +const char *purple_smiley_get_shortcut(const PurpleSmiley *smiley); + +/** + * Returns the custom smiley data's checksum. + * + * @param smiley The custom smiley. + * + * @return The checksum. + */ +const char *purple_smiley_get_checksum(const PurpleSmiley *smiley); + +/** + * Returns the PurpleStoredImage with the reference counter incremented. + * + * The returned PurpleStoredImage reference counter must be decremented + * after use. + * + * @param smiley The custom smiley. + * + * @return A PurpleStoredImage reference. + */ +PurpleStoredImage *purple_smiley_get_stored_image(const PurpleSmiley *smiley); + +/** + * Returns the custom smiley's data. + * + * @param smiley The custom smiley. + * @param len If not @c NULL, the length of the icon data returned + * will be set in the location pointed to by this. + * + * @return A pointer to the custom smiley data. + */ +gconstpointer purple_smiley_get_data(const PurpleSmiley *smiley, size_t *len); + +/** + * Returns an extension corresponding to the custom smiley's file type. + * + * @param smiley The custom smiley. + * + * @return The custom smiley's extension, "icon" if unknown, or @c NULL if + * the image data has disappeared. + */ +const char *purple_smiley_get_extension(const PurpleSmiley *smiley); + +/** + * Returns a full path to an custom smiley. + * + * If the custom smiley has data and the file exists in the cache, this + * will return a full path to the cached file. + * + * In general, it is not appropriate to be poking in the file cached + * directly. If you find yourself wanting to use this function, think + * very long and hard about it, and then don't. + * + * @param smiley The custom smiley. + * + * @return A full path to the file, or @c NULL under various conditions. + * The caller should use #g_free to free the returned string. + */ +char *purple_smiley_get_full_path(PurpleSmiley *smiley); + +/*@}*/ + + +/**************************************************************************/ +/** @name Custom Smiley Subsystem API */ +/**************************************************************************/ +/*@{*/ + +/** + * Returns a list of all custom smileys. The caller should free the list. + * + * @return A list of all custom smileys. + */ +GList * +purple_smileys_get_all(void); + +/** + * Returns the custom smiley given it's shortcut. + * + * @param shortcut The custom smiley's shortcut. + * + * @return The custom smiley (with a reference for the caller) if found, + * or @c NULL if not found. + */ +PurpleSmiley * +purple_smileys_find_by_shortcut(const char *shortcut); + +/** + * Returns the custom smiley given it's checksum. + * + * @param checksum The custom smiley's checksum. + * + * @return The custom smiley (with a reference for the caller) if found, + * or @c NULL if not found. + */ +PurpleSmiley * +purple_smileys_find_by_checksum(const char *checksum); + +/** + * Returns the directory used to store custom smiley cached files. + * + * The default directory is PURPLEDIR/smileys, unless otherwise specified + * by purple_buddy_icons_set_cache_dir(). + * + * @return The directory to store custom smyles cached files to. + */ +const char *purple_smileys_get_storing_dir(void); + +/** + * Initializes the custom smiley subsystem. + */ +void purple_smileys_init(void); + +/** + * Uninitializes the custom smiley subsystem. + */ +void purple_smileys_uninit(void); + +/*@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _PURPLE_SMILEY_H_ */ +
--- a/libpurple/status.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/status.c Sun May 25 04:30:41 2008 +0000 @@ -111,6 +111,12 @@ gboolean active; + /* + * The current values of the attributes for this status. The + * key is a string containing the name of the attribute. It is + * a borrowed reference from the list of attrs in the + * PurpleStatusType. The value is a PurpleValue. + */ GHashTable *attr_values; }; @@ -563,7 +569,7 @@ status->presence = presence; status->attr_values = - g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)purple_value_destroy); for (l = purple_status_type_get_attrs(status_type); l != NULL; l = l->next) @@ -573,7 +579,7 @@ PurpleValue *new_value = purple_value_dup(value); g_hash_table_insert(status->attr_values, - g_strdup(purple_status_attr_get_id(attr)), + (char *)purple_status_attr_get_id(attr), new_value); }
--- a/libpurple/util.c Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/util.c Sun May 25 04:30:41 2008 +0000 @@ -939,7 +939,8 @@ else if(IS_ENTITY("'")) pln = "\'"; else if(*(text+1) == '#' && - (sscanf(text, "&#%u%1[;]", £, temp) == 2 || sscanf(text, "&#x%x%1[;]", £, temp) == 2) && + (sscanf(text, "&#%u%1[;]", £, temp) == 2 || + sscanf(text, "&#x%x%1[;]", £, temp) == 2) && pound != 0) { static char buf[7]; int buflen = g_unichar_to_utf8((gunichar)pound, buf); @@ -2891,7 +2892,7 @@ } char * -purple_util_get_image_filename(gconstpointer image_data, size_t image_len) +purple_util_get_image_checksum(gconstpointer image_data, size_t image_len) { PurpleCipherContext *context; gchar digest[41]; @@ -2912,9 +2913,18 @@ } purple_cipher_context_destroy(context); + return g_strdup(digest); +} + +char * +purple_util_get_image_filename(gconstpointer image_data, size_t image_len) +{ /* Return the filename */ - return g_strdup_printf("%s.%s", digest, + char *checksum = purple_util_get_image_checksum(image_data, image_len); + char *filename = g_strdup_printf("%s.%s", checksum, purple_util_get_image_extension(image_data, image_len)); + g_free(checksum); + return filename; } gboolean
--- a/libpurple/util.h Mon May 19 16:18:32 2008 +0000 +++ b/libpurple/util.h Sun May 25 04:30:41 2008 +0000 @@ -706,6 +706,11 @@ purple_util_get_image_extension(gconstpointer data, size_t len); /** + * Returns a SHA-1 hash string of the data passed in. + */ +char *purple_util_get_image_checksum(gconstpointer image_data, size_t image_len); + +/** * @return A hex encoded version of the SHA-1 hash of the data passed * in with the correct file extention appended. The file * extension is determined by calling
--- a/pidgin.spec.in Mon May 19 16:18:32 2008 +0000 +++ b/pidgin.spec.in Sun May 25 04:30:41 2008 +0000 @@ -228,6 +228,7 @@ --disable-schemas-install \ %{!?_with_dbus:--disable-dbus} \ %{!?_with_avahi:--disable-avahi} \ + %{!?_with_meanwhile:--disable-meanwhile} \ %{?_without_gstreamer:--disable-gstreamer} \ %{?_without_gtkspell:--disable-gtkspell} \ %{?_without_nm:--disable-nm} \ @@ -473,6 +474,9 @@ %endif %changelog +* Mon May 19 2008 Stu Tomlinson <stu@nosnilmot.com> +- Fix building without meanwhile support + * Fri May 16 2008 Stu Tomlinson <stu@nosnilmot.com> - Add "--without nm" support to build without NetworkManager
--- a/pidgin/Makefile.am Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/Makefile.am Sun May 25 04:30:41 2008 +0000 @@ -111,6 +111,7 @@ gtksavedstatuses.c \ gtkscrollbook.c \ gtksession.c \ + gtksmiley.c \ gtksound.c \ gtksourceiter.c \ gtksourceundomanager.c \ @@ -163,6 +164,7 @@ gtksavedstatuses.h \ gtkscrollbook.h \ gtksession.h \ + gtksmiley.h \ gtksound.h \ gtksourceiter.h \ gtksourceundomanager.h \
--- a/pidgin/Makefile.mingw Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/Makefile.mingw Sun May 25 04:30:41 2008 +0000 @@ -84,6 +84,7 @@ gtkroomlist.c \ gtksavedstatuses.c \ gtkscrollbook.c \ + gtksmiley.c \ gtksound.c \ gtksourceiter.c \ gtksourceundomanager.c \
--- a/pidgin/gtkaccount.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkaccount.c Sun May 25 04:30:41 2008 +0000 @@ -176,14 +176,7 @@ } if (dialog->icon_img != NULL) { - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); - gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(dialog->icon_img), - purple_imgstore_get_size(dialog->icon_img), NULL); - gdk_pixbuf_loader_close(loader, NULL); - pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); - if (pixbuf) - g_object_ref(pixbuf); - g_object_unref(loader); + pixbuf = pidgin_pixbuf_from_imgstore(dialog->icon_img); } if (pixbuf && dialog->prpl_info && @@ -255,6 +248,25 @@ } } +static gboolean +screenname_focus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog) +{ + GHashTable *table; + const char *label; + + table = dialog->prpl_info->get_account_text_table(NULL); + label = g_hash_table_lookup(table, "login_label"); + + if(!strcmp(gtk_entry_get_text(GTK_ENTRY(widget)), label)) { + gtk_entry_set_text(GTK_ENTRY(widget), ""); + gtk_widget_modify_text(widget, GTK_STATE_NORMAL,NULL); + } + + g_hash_table_destroy(table); + + return FALSE; +} + static void screenname_changed_cb(GtkEntry *entry, AccountPrefsDialog *dialog) { @@ -270,6 +282,32 @@ } } +static gboolean +screenname_nofocus_cb(GtkWidget *widget, GdkEventFocus *event, AccountPrefsDialog *dialog) +{ + GdkColor color = {0, 34952, 35466, 34181}; + GHashTable *table; + const char *label; + + table = dialog->prpl_info->get_account_text_table(NULL); + label = g_hash_table_lookup(table, "login_label"); + + if (*gtk_entry_get_text(GTK_ENTRY(widget)) == '\0') { + /* We have to avoid hitting the screenname_changed_cb function + * because it enables buttons we don't want enabled yet ;) + */ + g_signal_handlers_block_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog); + gtk_entry_set_text(GTK_ENTRY(widget), label); + /* Make sure we can hit it again */ + g_signal_handlers_unblock_by_func(widget, G_CALLBACK(screenname_changed_cb), dialog); + gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &color); + } + + g_hash_table_destroy(table); + + return FALSE; +} + static void icon_filesel_choose_cb(const char *filename, gpointer data) { @@ -410,6 +448,25 @@ add_pref_box(dialog, vbox, _("_Username:"), dialog->screenname_entry); + if (dialog->account != NULL) + username = g_strdup(purple_account_get_username(dialog->account)); + + if (!username && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(dialog->prpl_info, get_account_text_table)) { + GdkColor color = {0, 34952, 35466, 34181}; + GHashTable *table; + const char *label; + table = dialog->prpl_info->get_account_text_table(NULL); + label = g_hash_table_lookup(table, "login_label"); + + gtk_entry_set_text(GTK_ENTRY(dialog->screenname_entry), label); + g_signal_connect(G_OBJECT(dialog->screenname_entry), "focus-in-event", + G_CALLBACK(screenname_focus_cb), dialog); + g_signal_connect(G_OBJECT(dialog->screenname_entry), "focus-out-event", + G_CALLBACK(screenname_nofocus_cb), dialog); + gtk_widget_modify_text(dialog->screenname_entry, GTK_STATE_NORMAL, &color); + g_hash_table_destroy(table); + } + g_signal_connect(G_OBJECT(dialog->screenname_entry), "changed", G_CALLBACK(screenname_changed_cb), dialog); @@ -419,9 +476,6 @@ else user_splits = dialog->prpl_info->user_splits; - if (dialog->account != NULL) - username = g_strdup(purple_account_get_username(dialog->account)); - if (dialog->user_split_entries != NULL) { g_list_free(dialog->user_split_entries); dialog->user_split_entries = NULL; @@ -1470,7 +1524,8 @@ add_login_options(dialog, vbox); add_user_options(dialog, vbox); - button = gtk_check_button_new_with_label(_("Create this new account on the server")); + button = gtk_check_button_new_with_mnemonic( + _("Create _this new account on the server")); gtk_box_pack_start(GTK_BOX(main_vbox), button, FALSE, FALSE, 0); gtk_widget_show(button); dialog->register_button = button; @@ -1513,6 +1568,8 @@ /* Show the window. */ gtk_widget_show(win); + if (!account) + gtk_widget_grab_focus(dialog->protocol_menu); } /************************************************************************** @@ -1753,39 +1810,6 @@ return FALSE; } -static gboolean -configure_cb(GtkWidget *w, GdkEventConfigure *event, AccountsWindow *dialog) -{ - if (GTK_WIDGET_VISIBLE(w)) { - int old_width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width"); - int col_width; - int difference; - - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/accounts/dialog/width", event->width); - purple_prefs_set_int(PIDGIN_PREFS_ROOT "/accounts/dialog/height", event->height); - - col_width = gtk_tree_view_column_get_width(dialog->screenname_col); - - if (col_width == 0) - return FALSE; - - difference = (MAX(old_width, event->width) - - MIN(old_width, event->width)); - - if (difference == 0) - return FALSE; - - if (old_width < event->width) - gtk_tree_view_column_set_min_width(dialog->screenname_col, - col_width + difference); - else - gtk_tree_view_column_set_max_width(dialog->screenname_col, - col_width - difference); - } - - return FALSE; -} - static void add_account_cb(GtkWidget *w, AccountsWindow *dialog) { @@ -1908,14 +1932,14 @@ column = gtk_tree_view_column_new_with_attributes(_("Enabled"), renderer, "active", COLUMN_ENABLED, NULL); - gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); - gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_resizable(column, FALSE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); /* Screen Name column */ column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, _("Username")); - gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); /* Buddy Icon */ renderer = gtk_cell_renderer_pixbuf_new(); @@ -1934,8 +1958,8 @@ /* Protocol name */ column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, _("Protocol")); - gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); - gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_resizable(column, FALSE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); /* Icon */ renderer = gtk_cell_renderer_pixbuf_new(); @@ -1977,21 +2001,14 @@ } if (img != NULL) { - GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); GdkPixbuf *buddyicon_pixbuf; - - gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(img), - purple_imgstore_get_size(img), NULL); - gdk_pixbuf_loader_close(loader, NULL); - buddyicon_pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); - + buddyicon_pixbuf = pidgin_pixbuf_from_imgstore(img); purple_imgstore_unref(img); if (buddyicon_pixbuf != NULL) { buddyicon = gdk_pixbuf_scale_simple(buddyicon_pixbuf, 22, 22, GDK_INTERP_HYPER); + g_object_unref(G_OBJECT(buddyicon_pixbuf)); } - - g_object_unref(loader); } gtk_list_store_set(store, iter, @@ -2196,6 +2213,7 @@ gtk_container_add(GTK_CONTAINER(sw), treeview); add_columns(treeview, dialog); + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(treeview)); if (populate_accounts_list(dialog)) gtk_notebook_set_current_page(GTK_NOTEBOOK(accounts_window->notebook), 1); @@ -2265,8 +2283,6 @@ g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(accedit_win_destroy_cb), accounts_window); - g_signal_connect(G_OBJECT(win), "configure_event", - G_CALLBACK(configure_cb), accounts_window); /* Setup the vbox */ vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(win), FALSE, PIDGIN_HIG_BORDER);
--- a/pidgin/gtkblist.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkblist.c Sun May 25 04:30:41 2008 +0000 @@ -57,6 +57,7 @@ #include "gtkroomlist.h" #include "gtkstatusbox.h" #include "gtkscrollbook.h" +#include "gtksmiley.h" #include "gtkutils.h" #include "pidgin/minidialog.h" #include "pidgin/pidgintooltip.h" @@ -1533,6 +1534,48 @@ return FALSE; } +static void +set_node_custom_icon_cb(const gchar *filename, gpointer data) +{ + if (filename) { + PurpleBlistNode *node = (PurpleBlistNode*)data; + + purple_buddy_icons_node_set_custom_icon_from_file(node, + filename); + } +} + +static void +set_node_custom_icon(GtkWidget *w, PurpleBlistNode *node) +{ + /* This doesn't keep track of the returned dialog (so that successive + * calls could be made to re-display that dialog). Do we want that? */ + GtkWidget *win = pidgin_buddy_icon_chooser_new(NULL, set_node_custom_icon_cb, node); + gtk_widget_show_all(win); +} + +static void +remove_node_custom_icon(GtkWidget *w, PurpleBlistNode *node) +{ + purple_buddy_icons_node_set_custom_icon(node, NULL, 0); +} + +static void +add_buddy_icon_menu_items(GtkWidget *menu, PurpleBlistNode *node) +{ + GtkWidget *item; + + pidgin_new_item_from_stock(menu, _("Set Custom Icon"), NULL, + G_CALLBACK(set_node_custom_icon), node, 0, + 0, NULL); + + item = pidgin_new_item_from_stock(menu, _("Remove Custom Icon"), NULL, + G_CALLBACK(remove_node_custom_icon), node, + 0, 0, NULL); + if (!purple_buddy_icons_node_has_custom_icon(node)) + gtk_widget_set_sensitive(item, FALSE); +} + static GtkWidget * create_group_menu (PurpleBlistNode *node, PurpleGroup *g) { @@ -1556,12 +1599,13 @@ NULL, G_CALLBACK(gtk_blist_menu_showoffline_cb), node, 0, 0, NULL); } + add_buddy_icon_menu_items(menu, node); + pidgin_append_blist_node_extended_menu(menu, node); return menu; } - static GtkWidget * create_chat_menu(PurpleBlistNode *node, PurpleChat *c) { @@ -1594,6 +1638,8 @@ pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(pidgin_blist_remove_cb), node, 0, 0, NULL); + add_buddy_icon_menu_items(menu, node); + return menu; } @@ -1615,6 +1661,8 @@ pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(pidgin_blist_remove_cb), node, 0, 0, NULL); + add_buddy_icon_menu_items(menu, node); + pidgin_separator(menu); pidgin_new_item_from_stock(menu, _("_Collapse"), GTK_STOCK_ZOOM_OUT, @@ -1639,6 +1687,8 @@ if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { pidgin_separator(menu); + add_buddy_icon_menu_items(menu, node); + if(gtknode->contact_expanded) { pidgin_new_item_from_stock(menu, _("_Collapse"), GTK_STOCK_ZOOM_OUT, @@ -2498,51 +2548,71 @@ static GdkPixbuf *pidgin_blist_get_buddy_icon(PurpleBlistNode *node, - gboolean scaled, gboolean greyed) -{ - GdkPixbuf *buf, *ret = NULL; + gboolean scaled, gboolean greyed) +{ + gsize len; GdkPixbufLoader *loader; - PurpleBuddyIcon *icon = NULL; - const guchar *data = NULL; - gsize len; PurpleBuddy *buddy = NULL; + PurpleGroup *group = NULL; + const guchar *data = NULL; + GdkPixbuf *buf, *ret = NULL; + PurpleBuddyIcon *icon = NULL; PurpleAccount *account = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; + PurpleContact *contact = NULL; PurpleStoredImage *custom_img; - - if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurplePluginProtocolInfo *prpl_info = NULL; + gint orig_width, orig_height, scale_width, scale_height; + + if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { buddy = purple_contact_get_priority_buddy((PurpleContact*)node); - } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { + contact = (PurpleContact*)node; + } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { buddy = (PurpleBuddy*)node; + contact = purple_buddy_get_contact(buddy); + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + group = (PurpleGroup*)node; + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { + /* We don't need to do anything here. We just need to not fall + * into the else block and return. */ } else { return NULL; } - if(buddy == NULL) - return NULL; - - account = purple_buddy_get_account(buddy); - - if(account && account->gc) + if (buddy) { + account = purple_buddy_get_account(buddy); + } + + if(account && account->gc) { prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl); + } #if 0 if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons")) return NULL; #endif - custom_img = purple_buddy_icons_find_custom_icon(purple_buddy_get_contact(buddy)); - if (custom_img) - { + /* If we have a contact then this is either a contact or a buddy and + * we want to fetch the custom icon for the contact. If we don't have + * a contact then this is a group or some other type of node and we + * want to use that directly. */ + if (contact) { + custom_img = purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact); + } else { + custom_img = purple_buddy_icons_node_find_custom_icon(node); + } + + if (custom_img) { data = purple_imgstore_get_data(custom_img); len = purple_imgstore_get_size(custom_img); } if (data == NULL) { - /* Not sure I like this...*/ - if (!(icon = purple_buddy_icons_find(buddy->account, buddy->name))) - return NULL; - data = purple_buddy_icon_get_data(icon, &len); + if (buddy) { + /* Not sure I like this...*/ + if (!(icon = purple_buddy_icons_find(buddy->account, buddy->name))) + return NULL; + data = purple_buddy_icon_get_data(icon, &len); + } if(data == NULL) return NULL; @@ -2560,56 +2630,68 @@ g_object_ref(G_OBJECT(buf)); g_object_unref(G_OBJECT(loader)); - if (buf) { - int orig_width, orig_height; - int scale_width, scale_height; - - if (greyed) { + if (!buf) { + return NULL; + } + + if (greyed) { + gboolean offline = FALSE, idle = FALSE; + + if (buddy) { PurplePresence *presence = purple_buddy_get_presence(buddy); if (!PURPLE_BUDDY_IS_ONLINE(buddy)) - gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); + offline = TRUE; if (purple_presence_is_idle(presence)) - gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); + idle = TRUE; + } else if (group) { + if (purple_blist_get_group_online_count(group) == 0) + offline = TRUE; } - /* i'd use the pidgin_buddy_icon_get_scale_size() thing, - * but it won't tell me the original size, which I need for scaling - * purposes */ - scale_width = orig_width = gdk_pixbuf_get_width(buf); - scale_height = orig_height = gdk_pixbuf_get_height(buf); - - if (prpl_info && prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_DISPLAY) - purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &scale_width, &scale_height); - - if (scaled || scale_height > 200 || scale_width > 200) { - GdkPixbuf *tmpbuf; - float scale_size = scaled ? 32.0 : 200.0; - if(scale_height > scale_width) { - scale_width = scale_size * (double)scale_width / (double)scale_height; - scale_height = scale_size; - } else { - scale_height = scale_size * (double)scale_height / (double)scale_width; - scale_width = scale_size; - } - /* scale & round before making square, so rectangular (but non-square) - * images get rounded corners too */ - tmpbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height); - gdk_pixbuf_fill(tmpbuf, 0x00000000); - gdk_pixbuf_scale(buf, tmpbuf, 0, 0, scale_width, scale_height, 0, 0, (double)scale_width/(double)orig_width, (double)scale_height/(double)orig_height, GDK_INTERP_BILINEAR); - if (pidgin_gdk_pixbuf_is_opaque(tmpbuf)) - pidgin_gdk_pixbuf_make_round(tmpbuf); - ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_size, scale_size); - gdk_pixbuf_fill(ret, 0x00000000); - gdk_pixbuf_copy_area(tmpbuf, 0, 0, scale_width, scale_height, ret, (scale_size-scale_width)/2, (scale_size-scale_height)/2); - g_object_unref(G_OBJECT(tmpbuf)); + if (offline) + gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); + + if (idle) + gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); + } + + /* I'd use the pidgin_buddy_icon_get_scale_size() thing, but it won't + * tell me the original size, which I need for scaling purposes. */ + scale_width = orig_width = gdk_pixbuf_get_width(buf); + scale_height = orig_height = gdk_pixbuf_get_height(buf); + + if (prpl_info && prpl_info->icon_spec.scale_rules & PURPLE_ICON_SCALE_DISPLAY) + purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &scale_width, &scale_height); + + if (scaled || scale_height > 200 || scale_width > 200) { + GdkPixbuf *tmpbuf; + float scale_size = scaled ? 32.0 : 200.0; + if(scale_height > scale_width) { + scale_width = scale_size * (double)scale_width / (double)scale_height; + scale_height = scale_size; } else { - ret = gdk_pixbuf_scale_simple(buf,scale_width,scale_height, GDK_INTERP_BILINEAR); + scale_height = scale_size * (double)scale_height / (double)scale_width; + scale_width = scale_size; } - g_object_unref(G_OBJECT(buf)); - } + /* Scale & round before making square, so rectangular (but + * non-square) images get rounded corners too. */ + tmpbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_width, scale_height); + gdk_pixbuf_fill(tmpbuf, 0x00000000); + gdk_pixbuf_scale(buf, tmpbuf, 0, 0, scale_width, scale_height, 0, 0, (double)scale_width/(double)orig_width, (double)scale_height/(double)orig_height, GDK_INTERP_BILINEAR); + if (pidgin_gdk_pixbuf_is_opaque(tmpbuf)) + pidgin_gdk_pixbuf_make_round(tmpbuf); + ret = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, scale_size, scale_size); + gdk_pixbuf_fill(ret, 0x00000000); + gdk_pixbuf_copy_area(tmpbuf, 0, 0, scale_width, scale_height, ret, (scale_size-scale_width)/2, (scale_size-scale_height)/2); + g_object_unref(G_OBJECT(tmpbuf)); + } else { + ret = gdk_pixbuf_scale_simple(buf,scale_width,scale_height, GDK_INTERP_BILINEAR); + } + g_object_unref(G_OBJECT(buf)); return ret; } + /* # - Status Icon * P - Protocol Icon * A - Buddy Icon @@ -3188,6 +3270,7 @@ { N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL }, { N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 1, "<Item>", NULL }, { N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "<Item>", NULL }, + { N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY }, { N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS }, { N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, { N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL }, @@ -3262,7 +3345,8 @@ g_list_length(purple_conv_chat_get_users(PURPLE_CONV_CHAT(conv)))); if (prpl_info && (prpl_info->options & OPT_PROTO_CHAT_TOPIC)) { - char *topic = g_markup_escape_text(purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv)), -1); + const char *chattopic = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv)); + char *topic = chattopic ? g_markup_escape_text(chattopic, -1) : NULL; g_string_append_printf(str, _("\n<b>Topic:</b> %s"), topic ? topic : _("(no topic set)")); g_free(topic); } @@ -5831,14 +5915,16 @@ return FALSE; } -/*This version of pidgin_blist_update_group can take the original buddy -or a group, but has much better algorithmic performance with a pre-known buddy*/ -static void pidgin_blist_update_group(PurpleBuddyList *list, PurpleBlistNode *node) -{ +/* This version of pidgin_blist_update_group can take the original buddy or a + * group, but has much better algorithmic performance with a pre-known buddy. + */ +static void pidgin_blist_update_group(PurpleBuddyList *list, + PurpleBlistNode *node) +{ + gint count; PurpleGroup *group; - int count; + PurpleBlistNode* gnode; gboolean show = FALSE, show_offline = FALSE; - PurpleBlistNode* gnode; g_return_if_fail(node != NULL); @@ -5873,12 +5959,13 @@ } if (show) { + gchar *title; + gboolean biglist; GtkTreeIter iter; GtkTreePath *path; gboolean expanded; GdkColor bgcolor; - char *title; - + GdkPixbuf *avatar = NULL; if(!insert_node(list, gnode, &iter)) return; @@ -5890,17 +5977,23 @@ gtk_tree_path_free(path); title = pidgin_get_group_title(gnode, expanded); + biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); + + if (biglist) { + avatar = pidgin_blist_get_buddy_icon(gnode, TRUE, TRUE); + } gtk_tree_store_set(gtkblist->treemodel, &iter, STATUS_ICON_VISIBLE_COLUMN, FALSE, STATUS_ICON_COLUMN, NULL, NAME_COLUMN, title, NODE_COLUMN, gnode, - /* BGCOLOR_COLUMN, &bgcolor, */ + /* BGCOLOR_COLUMN, &bgcolor, */ GROUP_EXPANDER_COLUMN, TRUE, GROUP_EXPANDER_VISIBLE_COLUMN, TRUE, CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE, - BUDDY_ICON_VISIBLE_COLUMN, FALSE, + BUDDY_ICON_COLUMN, avatar, + BUDDY_ICON_VISIBLE_COLUMN, biglist, IDLE_VISIBLE_COLUMN, FALSE, EMBLEM_VISIBLE_COLUMN, FALSE, -1); @@ -6190,7 +6283,7 @@ STATUS_ICON_COLUMN, status, STATUS_ICON_VISIBLE_COLUMN, TRUE, BUDDY_ICON_COLUMN, avatar ? avatar : gtkblist->empty_avatar, - BUDDY_ICON_VISIBLE_COLUMN, purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"), + BUDDY_ICON_VISIBLE_COLUMN, showicons, EMBLEM_COLUMN, emblem, EMBLEM_VISIBLE_COLUMN, emblem != NULL, PROTOCOL_ICON_COLUMN, prpl_icon,
--- a/pidgin/gtkconv.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkconv.c Sun May 25 04:30:41 2008 +0000 @@ -97,10 +97,10 @@ #define PIDGIN_CONV_ALL ((1 << 7) - 1) +/* XXX: These color defines shouldn't really be here. But the nick-color + * generation algorithm uses them, so keeping these around until we fix that. */ #define DEFAULT_SEND_COLOR "#204a87" -#define DEFAULT_RECV_COLOR "#cc0000" #define DEFAULT_HIGHLIGHT_COLOR "#AF7F00" -#define DEFAULT_ACTION_COLOR "#062585" #define BUDDYICON_SIZE_MIN 32 #define BUDDYICON_SIZE_MAX 96 @@ -164,10 +164,9 @@ static void update_typing_message(PidginConversation *gtkconv, const char *message); static const char *item_factory_translate_func (const char *path, gpointer func_data); gboolean pidgin_conv_has_focus(PurpleConversation *conv); -static void pidgin_conv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data); -static void pidgin_conv_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data); static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background); static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast); +static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, gboolean create); static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields); static void focus_out_from_menubar(GtkWidget *wid, PidginWindow *win); static void pidgin_conv_tab_pack(PidginWindow *win, PidginConversation *gtkconv); @@ -2569,10 +2568,6 @@ PurpleBuddy *b = purple_find_buddy(conv->account, conv->name); if (b) emblem = pidgin_blist_get_emblem((PurpleBlistNode*)b); - } else { - PurpleChat *c = purple_blist_find_chat(conv->account, conv->name); - if (c) - emblem = pidgin_blist_get_emblem((PurpleBlistNode*)c); } g_return_if_fail(status != NULL); @@ -2780,10 +2775,22 @@ custom_icon_sel_cb(const char *filename, gpointer data) { if (filename) { + const gchar *name; + PurpleBuddy *buddy; + PurpleContact *contact; PidginConversation *gtkconv = data; PurpleConversation *conv = gtkconv->active_conv; PurpleAccount *account = purple_conversation_get_account(conv); - pidgin_set_custom_buddy_icon(account, purple_conversation_get_name(conv), filename); + + name = purple_conversation_get_name(conv); + buddy = purple_find_buddy(account, name); + if (!buddy) { + purple_debug_info("custom-icon", "You can only set custom icons for people on your buddylist.\n"); + return; + } + contact = purple_buddy_get_contact(buddy); + + purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, filename); } } @@ -2825,12 +2832,21 @@ static void remove_custom_icon_cb(GtkWidget *widget, PidginConversation *gtkconv) { - PurpleConversation *conv; + const gchar *name; + PurpleBuddy *buddy; PurpleAccount *account; - - conv = gtkconv->active_conv; + PurpleContact *contact; + PurpleConversation *conv = gtkconv->active_conv; + account = purple_conversation_get_account(conv); - pidgin_set_custom_buddy_icon(account, purple_conversation_get_name(conv), NULL); + name = purple_conversation_get_name(conv); + buddy = purple_find_buddy(account, name); + if (!buddy) { + return; + } + contact = purple_buddy_get_contact(buddy); + + purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, NULL); } static void @@ -2931,7 +2947,7 @@ if (buddy) { PurpleContact *contact = purple_buddy_get_contact(buddy); - if (contact && purple_buddy_icons_has_custom_icon(contact)) + if (contact && purple_buddy_icons_node_has_custom_icon((PurpleBlistNode*)contact)) { pidgin_new_item_from_stock(menu, _("Remove Custom Icon"), NULL, G_CALLBACK(remove_custom_icon_cb), gtkconv, @@ -3948,6 +3964,7 @@ gboolean is_buddy; gchar *tmp, *alias_key, *name, *alias; int flags; + GdkColor *color = NULL; alias = cb->alias; name = cb->name; @@ -3974,71 +3991,50 @@ alias_key = g_utf8_collate_key(tmp, -1); g_free(tmp); - if (is_me) - { - GdkColor send_color; - gdk_color_parse(DEFAULT_SEND_COLOR, &send_color); + if (is_me) { + GtkTextTag *tag = gtk_text_tag_table_lookup( + gtk_text_buffer_get_tag_table(GTK_IMHTML(gtkconv->imhtml)->text_buffer), + "send-name"); + g_object_get(tag, "foreground-gdk", &color, NULL); + } else { + color = (GdkColor*)get_nick_color(gtkconv, name); + } #if GTK_CHECK_VERSION(2,6,0) - gtk_list_store_insert_with_values(ls, &iter, + gtk_list_store_insert_with_values(ls, &iter, /* - * The GTK docs are mute about the effects of the "row" value for performance. - * X-Chat hardcodes their value to 0 (prepend) and -1 (append), so we will too. - * It *might* be faster to search the gtk_list_store and set row accurately, - * but no one in #gtk+ seems to know anything about it either. - * Inserting in the "wrong" location has no visible ill effects. - F.P. - */ - -1, /* "row" */ - CHAT_USERS_ICON_COLUMN, pixbuf, - CHAT_USERS_ALIAS_COLUMN, alias, - CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, - CHAT_USERS_NAME_COLUMN, name, - CHAT_USERS_FLAGS_COLUMN, flags, - CHAT_USERS_COLOR_COLUMN, &send_color, - CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, - -1); - } - else - { - gtk_list_store_insert_with_values(ls, &iter, - -1, /* "row" */ - CHAT_USERS_ICON_COLUMN, pixbuf, - CHAT_USERS_ALIAS_COLUMN, alias, - CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, - CHAT_USERS_NAME_COLUMN, name, - CHAT_USERS_FLAGS_COLUMN, flags, - CHAT_USERS_COLOR_COLUMN, get_nick_color(gtkconv, name), - CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, - -1); +* The GTK docs are mute about the effects of the "row" value for performance. +* X-Chat hardcodes their value to 0 (prepend) and -1 (append), so we will too. +* It *might* be faster to search the gtk_list_store and set row accurately, +* but no one in #gtk+ seems to know anything about it either. +* Inserting in the "wrong" location has no visible ill effects. - F.P. +*/ + -1, /* "row" */ + CHAT_USERS_ICON_COLUMN, pixbuf, + CHAT_USERS_ALIAS_COLUMN, alias, + CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, + CHAT_USERS_NAME_COLUMN, name, + CHAT_USERS_FLAGS_COLUMN, flags, + CHAT_USERS_COLOR_COLUMN, color, + CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, + -1); #else - gtk_list_store_append(ls, &iter); - gtk_list_store_set(ls, &iter, - CHAT_USERS_ICON_COLUMN, pixbuf, - CHAT_USERS_ALIAS_COLUMN, alias, - CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, - CHAT_USERS_NAME_COLUMN, name, - CHAT_USERS_FLAGS_COLUMN, flags, - CHAT_USERS_COLOR_COLUMN, &send_color, - CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, - -1); - } - else - { - gtk_list_store_append(ls, &iter); - gtk_list_store_set(ls, &iter, - CHAT_USERS_ICON_COLUMN, pixbuf, - CHAT_USERS_ALIAS_COLUMN, alias, - CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, - CHAT_USERS_NAME_COLUMN, name, - CHAT_USERS_FLAGS_COLUMN, flags, - CHAT_USERS_COLOR_COLUMN, get_nick_color(gtkconv, name), - CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, - -1); + gtk_list_store_append(ls, &iter); + gtk_list_store_set(ls, &iter, + CHAT_USERS_ICON_COLUMN, pixbuf, + CHAT_USERS_ALIAS_COLUMN, alias, + CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, + CHAT_USERS_NAME_COLUMN, name, + CHAT_USERS_FLAGS_COLUMN, flags, + CHAT_USERS_COLOR_COLUMN, color, + CHAT_USERS_WEIGHT_COLUMN, is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, + -1); #endif - } if (pixbuf) g_object_unref(pixbuf); + if (is_me && color) + gdk_color_free(color); g_free(alias_key); } @@ -4433,6 +4429,7 @@ GtkTreeModel *model; char *normalized_name; GtkTreeIter iter; + GtkTextTag *texttag; int f; g_return_if_fail(buddy != NULL); @@ -4470,6 +4467,11 @@ g_free(normalized_name); blist_node_aliased_cb((PurpleBlistNode *)buddy, NULL, conv); + + texttag = get_buddy_tag(conv, purple_buddy_get_name(buddy), FALSE); /* XXX: do we want the normalized name? */ + if (texttag) { + g_object_set(texttag, "weight", is_buddy ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, NULL); + } } static void @@ -5270,6 +5272,12 @@ nbr_nick_colors = NUM_NICK_COLORS; nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->imhtml)->base[GTK_STATE_NORMAL]); } + + /* We don't want to see the custom smileys if our buddy send us the + * defined shortcut. */ + pidgin_themes_smiley_themeize(gtkconv->imhtml); + /* We want to see our smileys in the entry */ + pidgin_themes_smiley_themeize_custom(gtkconv->entry); } static void @@ -5446,7 +5454,8 @@ return FALSE; } -static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who) { +static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, gboolean create) +{ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); GtkTextTag *buddytag; gchar *str; @@ -5457,9 +5466,12 @@ gtk_text_buffer_get_tag_table( GTK_IMHTML(gtkconv->imhtml)->text_buffer), str); - if (buddytag == NULL) { + if (buddytag == NULL && create) { buddytag = gtk_text_buffer_create_tag( - GTK_IMHTML(gtkconv->imhtml)->text_buffer, str, NULL); + GTK_IMHTML(gtkconv->imhtml)->text_buffer, str, + "foreground-gdk", get_nick_color(gtkconv, who), + "weight", purple_find_buddy(purple_conversation_get_account(conv), who) ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, + NULL); g_signal_connect(G_OBJECT(buddytag), "event", G_CALLBACK(buddytag_event), conv); @@ -5565,7 +5577,6 @@ char buf2[BUF_LONG]; gboolean show_date; char *mdate; - char color[10]; char *str; char *with_font_tag; char *sml_attrib = NULL; @@ -5576,8 +5587,6 @@ char *bracket; int tag_count = 0; gboolean is_rtl_message = FALSE; - GtkSmileyTree *tree = NULL; - GHashTable *smiley_data = NULL; g_return_if_fail(conv != NULL); gtkconv = PIDGIN_CONVERSATION(conv); @@ -5736,14 +5745,8 @@ if (!(flags & PURPLE_MESSAGE_RECV)) { - /* Temporarily revert to the original smiley-data to avoid showing up - * custom smileys of the buddy when sending message - */ - tree = GTK_IMHTML(gtkconv->imhtml)->default_smilies; - GTK_IMHTML(gtkconv->imhtml)->default_smilies = - GTK_IMHTML(gtkconv->entry)->default_smilies; - smiley_data = GTK_IMHTML(gtkconv->imhtml)->smiley_data; - GTK_IMHTML(gtkconv->imhtml)->smiley_data = GTK_IMHTML(gtkconv->entry)->smiley_data; + /* We want to see our own smileys. Need to revert it after send*/ + pidgin_themes_smiley_themeize_custom(gtkconv->imhtml); } /* TODO: These colors should not be hardcoded so log.c can use them */ @@ -5774,169 +5777,109 @@ char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup("")); /* The initial offset is to deal with * escaped entities making the string longer */ - int tag_start_offset = alias ? (strlen(alias_escaped) - strlen(alias)) : 0; + int tag_start_offset = 0; int tag_end_offset = 0; + const char *tagname = NULL; + + GtkTextIter start, end; + GtkTextMark *mark; + GtkTextTag *tag; + GtkTextBuffer *buffer = GTK_IMHTML(gtkconv->imhtml)->text_buffer; /* Enforce direction on alias */ if (is_rtl_message) str_embed_direction_chars(&alias_escaped); + str = g_malloc(1024); if (flags & PURPLE_MESSAGE_WHISPER) { - str = g_malloc(1024); - /* If we're whispering, it's not an autoresponse. */ if (purple_message_meify(new_message, -1 )) { g_snprintf(str, 1024, "***%s", alias_escaped); - strcpy(color, "#6C2585"); tag_start_offset += 3; + tagname = "whisper-action-name"; } else { g_snprintf(str, 1024, "*%s*:", alias_escaped); tag_start_offset += 1; tag_end_offset = 2; - strcpy(color, "#00FF00"); + tagname = "whisper-name"; } - } - else { + } else { if (purple_message_meify(new_message, -1)) { - GdkColor *col; - str = g_malloc(1024); - if (flags & PURPLE_MESSAGE_AUTO_RESP) { g_snprintf(str, 1024, "%s ***%s", AUTO_RESPONSE, alias_escaped); - tag_start_offset += 4 - + strlen(AUTO_RESPONSE); + tag_start_offset += strlen(AUTO_RESPONSE) - 6 + 4; } else { g_snprintf(str, 1024, "***%s", alias_escaped); tag_start_offset += 3; } if (flags & PURPLE_MESSAGE_NICK) - gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL); + tagname = "highlight-name"; else - gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "action-name-color", &col, NULL); - - if(col) { - g_snprintf(color, sizeof(color), "#%02X%02X%02X", - col->red >> 8, col->green >> 8, col->blue >> 8); - gdk_color_free(col); - } else { - if (flags & PURPLE_MESSAGE_NICK) - strcpy(color, DEFAULT_HIGHLIGHT_COLOR); - else - strcpy(color, DEFAULT_ACTION_COLOR); - } - } - else { - str = g_malloc(1024); + tagname = "action-name"; + } else { if (flags & PURPLE_MESSAGE_AUTO_RESP) { g_snprintf(str, 1024, "%s %s", alias_escaped, AUTO_RESPONSE); - tag_start_offset += 1 - + strlen(AUTO_RESPONSE); + tag_start_offset += strlen(AUTO_RESPONSE) - 6 + 1; } else { g_snprintf(str, 1024, "%s:", alias_escaped); tag_end_offset = 1; } + if (flags & PURPLE_MESSAGE_NICK) { - GdkColor *col; - gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "highlight-name-color", &col, NULL); - if(col) { - g_snprintf(color, sizeof(color), "#%02X%02X%02X", - col->red >> 8, col->green >> 8, col->blue >> 8); - gdk_color_free(col); - } else { - strcpy(color, DEFAULT_HIGHLIGHT_COLOR); + tagname = "highlight-name"; + } else if (flags & PURPLE_MESSAGE_RECV) { + /* The tagname for chats is handled by get_buddy_tag */ + if (type == PURPLE_CONV_TYPE_IM) { + tagname = "receive-name"; } - } - else if (flags & PURPLE_MESSAGE_RECV) { - if (type == PURPLE_CONV_TYPE_CHAT) { - const GdkColor *col = get_nick_color(gtkconv, name); - - g_snprintf(color, sizeof(color), "#%02X%02X%02X", - col->red >> 8, col->green >> 8, col->blue >> 8); - } else { - GdkColor *col; - gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "receive-name-color", &col, NULL); - if(col) { - g_snprintf(color, sizeof(color), "#%02X%02X%02X", - col->red >> 8, col->green >> 8, col->blue >> 8); - gdk_color_free(col); - } else { - strcpy(color, DEFAULT_RECV_COLOR); - } - } - } - else if (flags & PURPLE_MESSAGE_SEND) { - GdkColor *col; - gtk_widget_style_get(GTK_WIDGET(gtkconv->imhtml), "send-name-color", &col, NULL); - if(col) { - g_snprintf(color, sizeof(color), "#%02X%02X%02X", - col->red >> 8, col->green >> 8, col->blue >> 8); - gdk_color_free(col); - } else { - strcpy(color, DEFAULT_SEND_COLOR); - } - } - else { + } else if (flags & PURPLE_MESSAGE_SEND) { + tagname = "send-name"; + } else { purple_debug_error("gtkconv", "message missing flags\n"); - strcpy(color, "#000000"); } } } g_free(alias_escaped); - /* Are we in a chat where we can tell which users are buddies? */ - if (prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME) && - purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { - - /* Bold buddies to make them stand out from non-buddies. */ - if (flags & PURPLE_MESSAGE_SEND || - flags & PURPLE_MESSAGE_NICK || - purple_find_buddy(account, name) != NULL) { - g_snprintf(buf2, BUF_LONG, - "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--%s --></FONT>" - "<B>%s</B></FONT> ", - color, sml_attrib ? sml_attrib : "", mdate, str); - } else { - g_snprintf(buf2, BUF_LONG, - "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--%s --></FONT>" - "%s</FONT> ", - color, sml_attrib ? sml_attrib : "", mdate, str); - - } - } else { - /* Bold everyone's name to make the name stand out from the message. */ - g_snprintf(buf2, BUF_LONG, - "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--%s --></FONT>" - "<B>%s</B></FONT> ", - color, sml_attrib ? sml_attrib : "", mdate, str); - } - + if (tagname) + tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tagname); + else + tag = get_buddy_tag(conv, name, TRUE); + + if (GTK_IMHTML(gtkconv->imhtml)->show_comments) { + /* The color for the timestamp has to be set in the font-tags, unfortunately. + * Applying the nick-tag to timestamps would work, but that can make it + * bold. I thought applying the "comment" tag again, which has "weight" set + * to PANGO_WEIGHT_NORMAL, would remove the boldness. But it doesn't. So + * this will have to do. I don't terribly like it. -- sadrul */ + GdkColor *color = NULL; + gboolean set = FALSE; + char colcode[] = "COLOR=\"#XXXXXX\""; + g_object_get(G_OBJECT(tag), "foreground-set", &set, "foreground-gdk", &color, NULL); + if (set && color) + g_snprintf(colcode, sizeof(colcode), "COLOR=\"#%02x%02x%02x\"", + color->red >> 8, color->green >> 8, color->blue >> 8); + else + colcode[0] = '\0'; + g_snprintf(buf2, BUF_LONG, "<FONT %s SIZE=\"2\"><!--%s --></FONT>", colcode, mdate); + gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL); + if (color) + gdk_color_free(color); + } + + gtk_text_buffer_get_end_iter(buffer, &end); + mark = gtk_text_buffer_create_mark(buffer, NULL, &end, TRUE); + + g_snprintf(buf2, BUF_LONG, "<FONT %s>%s</FONT> ", sml_attrib ? sml_attrib : "", str); gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all | GTK_IMHTML_NO_SCROLL); - if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT && - !(flags & PURPLE_MESSAGE_SEND)) { - - GtkTextIter start, end; - GtkTextTag *buddytag = get_buddy_tag(conv, name); - - gtk_text_buffer_get_end_iter( - GTK_IMHTML(gtkconv->imhtml)->text_buffer, - &end); - gtk_text_iter_backward_chars(&end, - tag_end_offset + 1); - - gtk_text_buffer_get_end_iter( - GTK_IMHTML(gtkconv->imhtml)->text_buffer, - &start); - gtk_text_iter_backward_chars(&start, - strlen(str) + 1 - tag_start_offset); - - gtk_text_buffer_apply_tag( - GTK_IMHTML(gtkconv->imhtml)->text_buffer, - buddytag, &start, &end); - } + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_get_iter_at_mark(buffer, &start, mark); + gtk_text_buffer_apply_tag(buffer, tag, &start, &end); + gtk_text_buffer_delete_mark(buffer, mark); g_free(str); @@ -5954,8 +5897,7 @@ length += pre_len + post_len; g_free(pre); - } - else + } else with_font_tag = g_memdup(new_message, length); gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), @@ -5989,8 +5931,7 @@ if (!(flags & PURPLE_MESSAGE_RECV)) { /* Restore the smiley-data */ - GTK_IMHTML(gtkconv->imhtml)->default_smilies = tree; - GTK_IMHTML(gtkconv->imhtml)->smiley_data = smiley_data; + pidgin_themes_smiley_themeize(gtkconv->imhtml); } purple_signal_emit(pidgin_conversations_get_handle(), @@ -6218,119 +6159,24 @@ return FALSE; } -static void pidgin_conv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data) -{ - GtkIMHtmlSmiley *smiley; - - smiley = (GtkIMHtmlSmiley *)user_data; - smiley->icon = gdk_pixbuf_loader_get_animation(loader); - - if (smiley->icon) - g_object_ref(G_OBJECT(smiley->icon)); -#ifdef DEBUG_CUSTOM_SMILEY - purple_debug_info("custom-smiley", "pidgin_conv_custom_smiley_allocated(): got GdkPixbufAnimation %p for smiley '%s'\n", smiley->icon, smiley->smile); -#endif -} - -static void pidgin_conv_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data) -{ - GtkIMHtmlSmiley *smiley; - GtkWidget *icon = NULL; - GtkTextChildAnchor *anchor = NULL; - GSList *current = NULL; - - smiley = (GtkIMHtmlSmiley *)user_data; - if (!smiley->imhtml) { -#ifdef DEBUG_CUSTOM_SMILEY - purple_debug_error("custom-smiley", "pidgin_conv_custom_smiley_closed(): orphan smiley found: %p\n", smiley); -#endif - g_object_unref(G_OBJECT(loader)); - smiley->loader = NULL; - return; - } - - for (current = smiley->anchors; current; current = g_slist_next(current)) { - - icon = gtk_image_new_from_animation(smiley->icon); - -#ifdef DEBUG_CUSTOM_SMILEY - purple_debug_info("custom-smiley", "pidgin_conv_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n", - icon, smiley->icon, smiley->smile); -#endif - if (icon) { - GList *wids; - gtk_widget_show(icon); - - anchor = GTK_TEXT_CHILD_ANCHOR(current->data); - wids = gtk_text_child_anchor_get_widgets(anchor); - - g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free); - g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley->smile), g_free); - - if (smiley->imhtml) { - if (wids) { - GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data)); - g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL); - g_list_free(children); - gtk_container_add(GTK_CONTAINER(wids->data), icon); - } else - gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->imhtml), icon, anchor); - } - g_list_free(wids); - } - - } - - g_slist_free(smiley->anchors); - smiley->anchors = NULL; - - g_object_unref(G_OBJECT(loader)); - smiley->loader = NULL; -} - static gboolean add_custom_smiley_for_imhtml(GtkIMHtml *imhtml, const char *sml, const char *smile) { GtkIMHtmlSmiley *smiley; - GdkPixbufLoader *loader; smiley = gtk_imhtml_smiley_get(imhtml, sml, smile); if (smiley) { - if (!(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) { return FALSE; } - - /* Close the old GdkPixbufAnimation, then create a new one for - * the smiley we are about to receive */ - g_object_unref(G_OBJECT(smiley->icon)); - - /* XXX: Is it necessary to _unref the loader first? */ - smiley->loader = gdk_pixbuf_loader_new(); - smiley->icon = NULL; - - g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(pidgin_conv_custom_smiley_allocated), smiley); - g_signal_connect(smiley->loader, "closed", G_CALLBACK(pidgin_conv_custom_smiley_closed), smiley); - + gtk_imhtml_smiley_reload(smiley); return TRUE; } - loader = gdk_pixbuf_loader_new(); - - /* this is wrong, this file ought not call g_new on GtkIMHtmlSmiley */ - /* Let gtk_imhtml have a gtk_imhtml_smiley_new function, and let - GtkIMHtmlSmiley by opaque */ - smiley = g_new0(GtkIMHtmlSmiley, 1); - smiley->file = NULL; - smiley->smile = g_strdup(smile); - smiley->loader = loader; - smiley->flags = smiley->flags | GTK_IMHTML_SMILEY_CUSTOM; - - g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(pidgin_conv_custom_smiley_allocated), smiley); - g_signal_connect(smiley->loader, "closed", G_CALLBACK(pidgin_conv_custom_smiley_closed), smiley); - + smiley = gtk_imhtml_smiley_create(NULL, smile, FALSE, GTK_IMHTML_SMILEY_CUSTOM); gtk_imhtml_associate_smiley(imhtml, sml, smiley); + g_signal_connect_swapped(imhtml, "destroy", G_CALLBACK(gtk_imhtml_smiley_destroy), smiley); return TRUE; } @@ -6555,6 +6401,11 @@ if(conv->features & PURPLE_CONNECTION_NO_IMAGES) buttons &= ~GTK_IMHTML_IMAGE; + if (conv->features & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY) + buttons |= GTK_IMHTML_CUSTOM_SMILEY; + else + buttons &= ~GTK_IMHTML_CUSTOM_SMILEY; + gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons); if (account != NULL) gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), purple_account_get_protocol_id(account)); @@ -7023,7 +6874,7 @@ { PurpleContact *contact = purple_buddy_get_contact(buddy); if (contact) { - custom_img = purple_buddy_icons_find_custom_icon(contact); + custom_img = purple_buddy_icons_node_find_custom_icon((PurpleBlistNode*)contact); if (custom_img) { /* There is a custom icon for this user */ data = purple_imgstore_get_data(custom_img);
--- a/pidgin/gtkdialogs.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkdialogs.c Sun May 25 04:30:41 2008 +0000 @@ -686,7 +686,7 @@ #ifdef LIBZEPHYR_EXT g_string_append(str, " <b>Zephyr library (libzephyr):</b> External<br/>"); #else - g_string_append(str, " <b>Zephyr library (libzephyr):</b> Not External<br/>"); + g_string_append(str, " <b>Zephyr library (libzephyr):</b> Internal<br/>"); #endif #ifdef ZEPHYR_USES_KERBEROS
--- a/pidgin/gtkimhtml.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkimhtml.c Sun May 25 04:30:41 2008 +0000 @@ -32,10 +32,14 @@ #include "internal.h" #include "pidgin.h" #include "pidginstock.h" +#include "gtkutils.h" +#include "smiley.h" +#include "imgstore.h" #include "debug.h" #include "util.h" #include "gtkimhtml.h" +#include "gtksmiley.h" #include "gtksourceiter.h" #include "gtksourceundomanager.h" #include "gtksourceview-marshal.h" @@ -397,6 +401,55 @@ gtk_imhtml_scroll_to_end(imhtml, FALSE); } +#define DEFAULT_SEND_COLOR "#204a87" +#define DEFAULT_RECV_COLOR "#cc0000" +#define DEFAULT_HIGHLIGHT_COLOR "#AF7F00" +#define DEFAULT_ACTION_COLOR "#062585" +#define DEFAULT_WHISPER_ACTION_COLOR "#6C2585" +#define DEFAULT_WHISPER_COLOR "#00FF00" + +static void (*parent_style_set)(GtkWidget *widget, GtkStyle *prev_style); + +static void +gtk_imhtml_style_set(GtkWidget *widget, GtkStyle *prev_style) +{ + int i; + struct { + const char *tag; + const char *color; + const char *def; + } styles[] = { + {"send-name", "send-name-color", DEFAULT_SEND_COLOR}, + {"receive-name", "receive-name-color", DEFAULT_RECV_COLOR}, + {"highlight-name", "highlight-name-color", DEFAULT_HIGHLIGHT_COLOR}, + {"action-name", "action-name-color", DEFAULT_ACTION_COLOR}, + {"whisper-action-name", "whisper-action-name-color", DEFAULT_WHISPER_ACTION_COLOR}, + {"whisper-name", "whisper-name-color", DEFAULT_WHISPER_COLOR}, + {NULL, NULL, NULL} + }; + GtkIMHtml *imhtml = GTK_IMHTML(widget); + GtkTextTagTable *table = gtk_text_buffer_get_tag_table(imhtml->text_buffer); + + for (i = 0; styles[i].tag; i++) { + GdkColor *color = NULL; + GtkTextTag *tag = gtk_text_tag_table_lookup(table, styles[i].tag); + if (!tag) { + purple_debug_warning("gtkimhtml", "Cannot find tag '%s'. This should never happen. Please file a bug.\n", styles[i].tag); + continue; + } + gtk_widget_style_get(widget, styles[i].color, &color, NULL); + if (color) { + g_object_set(tag, "foreground-gdk", color, NULL); + gdk_color_free(color); + } else { + GdkColor defcolor; + gdk_color_parse(styles[i].def, &defcolor); + g_object_set(tag, "foreground-gdk", &defcolor, NULL); + } + } + parent_style_set(widget, prev_style); +} + static gint gtk_imhtml_tip_paint (GtkIMHtml *imhtml) { @@ -1433,6 +1486,8 @@ widget_class->expose_event = gtk_imhtml_expose_event; parent_size_allocate = widget_class->size_allocate; widget_class->size_allocate = gtk_imhtml_size_allocate; + parent_style_set = widget_class->style_set; + widget_class->style_set = gtk_imhtml_style_set; gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-color", _("Hyperlink color"), @@ -1458,6 +1513,14 @@ _("Action Message Name Color"), _("Color to draw the name of an action message."), GDK_TYPE_COLOR, G_PARAM_READABLE)); + gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-action-name-color", + _("Action Message Name Color for Whispered Message"), + _("Color to draw the name of an action message."), + GDK_TYPE_COLOR, G_PARAM_READABLE)); + gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-name-color", + _("Whisper Message Name Color"), + _("Color to draw the name of an action message."), + GDK_TYPE_COLOR, G_PARAM_READABLE)); /* Customizable typing notification ... sort of. Example: * GtkIMHtml::typing-notification-font = "monospace italic light 8.0" @@ -1519,9 +1582,18 @@ gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL); + gtk_text_buffer_create_tag(imhtml->text_buffer, "comment", "weight", PANGO_WEIGHT_NORMAL, #if FALSE && GTK_CHECK_VERSION(2,10,10) - gtk_text_buffer_create_tag(imhtml->text_buffer, "comment", "invisible", FALSE, NULL); + "invisible", FALSE, #endif + NULL); + + gtk_text_buffer_create_tag(imhtml->text_buffer, "send-name", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(imhtml->text_buffer, "receive-name", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(imhtml->text_buffer, "highlight-name", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(imhtml->text_buffer, "action-name", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-action-name", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-name", "weight", PANGO_WEIGHT_BOLD, NULL); /* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */ imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2); @@ -3063,7 +3135,7 @@ #else if (imhtml->show_comments && !(options & GTK_IMHTML_NO_COMMENTS)) { wpos = g_snprintf (ws, len, "%s", tag); - gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); + gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL); } #endif ws[0] = '\0'; wpos = 0; @@ -3631,6 +3703,15 @@ gtk_widget_show(image->filesel); } +static void +gtk_imhtml_custom_smiley_save(GtkWidget *w, GtkIMHtmlImage *image) +{ + /* Create an add dialog */ + PidginSmiley *editor = pidgin_smiley_edit(NULL, NULL); + pidgin_smiley_editor_set_shortcut(editor, image->filename); + pidgin_smiley_editor_set_image(editor, image->pixbuf); +} + /* * So, um, AIM Direct IM lets you send any file, not just images. You can * just insert a sound or a file or whatever in a conversation. It's @@ -3655,6 +3736,19 @@ g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), image); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + /* Add menu item for adding custom smiley to local smileys */ + /* we only add the menu if the image is of "custom smiley size" + <= 96x96 pixels */ + if (image->width <= 96 && image->height <= 96) { + text = g_strdup_printf(_("_Add Custom Smiley...")); + img = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + item = gtk_image_menu_item_new_with_mnemonic(text); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); + g_signal_connect(G_OBJECT(item), "activate", + G_CALLBACK(gtk_imhtml_custom_smiley_save), image); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + gtk_widget_show_all(menu); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event_button->button, event_button->time); @@ -3676,7 +3770,7 @@ GdkPixbufAnimation *anim = NULL; GtkIMHtmlScalable *image = NULL; gboolean ret; - + if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3) return FALSE; @@ -4912,7 +5006,67 @@ g_snprintf(buf, sizeof(buf), "<font size=\"%s\">", &name[10]); return buf; } else { - return ""; + char *str = buf; + gboolean isset; + int ivalue = 0; + GdkColor *color = NULL; + GObject *obj = G_OBJECT(tag); + gboolean empty = TRUE; + + str += g_snprintf(str, sizeof(buf) - (str - buf), "<span style='"); + + /* Weight */ + g_object_get(obj, "weight-set", &isset, "weight", &ivalue, NULL); + if (isset) { + const char *weight = ""; + if (ivalue >= PANGO_WEIGHT_ULTRABOLD) + weight = "bolder"; + else if (ivalue >= PANGO_WEIGHT_BOLD) + weight = "bold"; + else if (ivalue >= PANGO_WEIGHT_NORMAL) + weight = "normal"; + else + weight = "lighter"; + + str += g_snprintf(str, sizeof(buf) - (str - buf), "font-weight: %s;", weight); + empty = FALSE; + } + + /* Foreground color */ + g_object_get(obj, "foreground-set", &isset, "foreground-gdk", &color, NULL); + if (isset && color) { + str += g_snprintf(str, sizeof(buf) - (str - buf), + "color: #%02x%02x%02x;", + color->red >> 8, color->green >> 8, color->blue >> 8); + gdk_color_free(color); + empty = FALSE; + } + + /* Background color */ + g_object_get(obj, "background-set", &isset, "background-gdk", &color, NULL); + if (isset && color) { + str += g_snprintf(str, sizeof(buf) - (str - buf), + "background: #%02x%02x%02x;", + color->red >> 8, color->green >> 8, color->blue >> 8); + gdk_color_free(color); + empty = FALSE; + } + + /* Underline */ + g_object_get(obj, "underline-set", &isset, "underline", &ivalue, NULL); + if (isset) { + switch (ivalue) { + case PANGO_UNDERLINE_NONE: + case PANGO_UNDERLINE_ERROR: + break; + default: + str += g_snprintf(str, sizeof(buf) - (str - buf), "text-decoration: underline;"); + } + } + + g_snprintf(str, sizeof(buf) - (str - buf), "'>"); + + return (empty ? "" : buf); } } @@ -4944,6 +5098,16 @@ } else if (strncmp(name, "FONT SIZE ", 10) == 0) { return "</font>"; } else { + const char *props[] = {"weight-set", "foreground-set", "background-set", + "size-set", "underline-set", NULL}; + int i; + for (i = 0; props[i]; i++) { + gboolean set = FALSE; + g_object_get(G_OBJECT(tag), props[i], &set, NULL); + if (set) + return "</span>"; + } + return ""; } } @@ -5273,7 +5437,150 @@ if (flags & PURPLE_CONNECTION_NO_IMAGES) buttons &= ~GTK_IMHTML_IMAGE; + if (flags & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY) + buttons |= GTK_IMHTML_CUSTOM_SMILEY; + else + buttons &= ~GTK_IMHTML_CUSTOM_SMILEY; + gtk_imhtml_set_format_functions(imhtml, buttons); } - +/******* + * GtkIMHtmlSmiley functions + *******/ +static void gtk_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data) +{ + GtkIMHtmlSmiley *smiley; + + smiley = (GtkIMHtmlSmiley *)user_data; + smiley->icon = gdk_pixbuf_loader_get_animation(loader); + + if (smiley->icon) + g_object_ref(G_OBJECT(smiley->icon)); +#ifdef DEBUG_CUSTOM_SMILEY + purple_debug_info("custom-smiley", "gtk_custom_smiley_allocated(): got GdkPixbufAnimation %p for smiley '%s'\n", smiley->icon, smiley->smile); +#endif +} + +static void gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data) +{ + GtkIMHtmlSmiley *smiley; + GtkWidget *icon = NULL; + GtkTextChildAnchor *anchor = NULL; + GSList *current = NULL; + + smiley = (GtkIMHtmlSmiley *)user_data; + if (!smiley->imhtml) { +#ifdef DEBUG_CUSTOM_SMILEY + purple_debug_error("custom-smiley", "gtk_custom_smiley_closed(): orphan smiley found: %p\n", smiley); +#endif + g_object_unref(G_OBJECT(loader)); + smiley->loader = NULL; + return; + } + + for (current = smiley->anchors; current; current = g_slist_next(current)) { + + icon = gtk_image_new_from_animation(smiley->icon); + +#ifdef DEBUG_CUSTOM_SMILEY + purple_debug_info("custom-smiley", "gtk_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n", + icon, smiley->icon, smiley->smile); +#endif + if (icon) { + GList *wids; + gtk_widget_show(icon); + + anchor = GTK_TEXT_CHILD_ANCHOR(current->data); + wids = gtk_text_child_anchor_get_widgets(anchor); + + g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free); + g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley->smile), g_free); + + if (smiley->imhtml) { + if (wids) { + GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data)); + g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL); + g_list_free(children); + gtk_container_add(GTK_CONTAINER(wids->data), icon); + } else + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->imhtml), icon, anchor); + } + g_list_free(wids); + } + + } + + g_slist_free(smiley->anchors); + smiley->anchors = NULL; + + g_object_unref(G_OBJECT(loader)); + smiley->loader = NULL; +} + +static void +gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data) +{ +#define CUSTOM_SMILEY_SIZE 96 /* XXX: Should this be a theme setting? */ + if (width <= CUSTOM_SMILEY_SIZE && height <= CUSTOM_SMILEY_SIZE) + return; + + if (width >= height) { + height = height * CUSTOM_SMILEY_SIZE / width; + width = CUSTOM_SMILEY_SIZE; + } else { + width = width * CUSTOM_SMILEY_SIZE / height; + height = CUSTOM_SMILEY_SIZE; + } + + gdk_pixbuf_loader_set_size(loader, width, height); +} + +void +gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley) +{ + if (smiley->icon) + g_object_unref(smiley->icon); + if (smiley->loader) + g_object_unref(smiley->loader); /* XXX: does this crash? */ + + smiley->icon = NULL; + smiley->loader = NULL; + + if (smiley->file) { + /* We do not use the pixbuf loader for a smiley that can be loaded + * from a file. (e.g., local custom smileys) + */ + return; + } + + smiley->loader = gdk_pixbuf_loader_new(); + + g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(gtk_custom_smiley_allocated), smiley); + g_signal_connect(smiley->loader, "closed", G_CALLBACK(gtk_custom_smiley_closed), smiley); + g_signal_connect(smiley->loader, "size_prepared", G_CALLBACK(gtk_custom_smiley_size_prepared), smiley); +} + +GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide, + GtkIMHtmlSmileyFlags flags) +{ + GtkIMHtmlSmiley *smiley = g_new0(GtkIMHtmlSmiley, 1); + smiley->file = g_strdup(file); + smiley->smile = g_strdup(shortcut); + smiley->hidden = hide; + smiley->flags = flags; + gtk_imhtml_smiley_reload(smiley); + return smiley; +} + +void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley) +{ + g_free(smiley->smile); + g_free(smiley->file); + if (smiley->icon) + g_object_unref(smiley->icon); + if (smiley->loader) + g_object_unref(smiley->loader); + g_free(smiley); +} +
--- a/pidgin/gtkimhtml.h Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkimhtml.h Sun May 25 04:30:41 2008 +0000 @@ -76,6 +76,8 @@ GTK_IMHTML_SMILEY = 1 << 11, GTK_IMHTML_LINKDESC = 1 << 12, GTK_IMHTML_STRIKE = 1 << 13, + /** Show custom smileys when appropriate. @since 2.5.0 */ + GTK_IMHTML_CUSTOM_SMILEY = 1 << 14, GTK_IMHTML_ALL = -1 } GtkIMHtmlButtons; @@ -852,6 +854,12 @@ */ void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags); +GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide, + GtkIMHtmlSmileyFlags flags); + +void gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley); + +void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley); /*@}*/ #ifdef __cplusplus
--- a/pidgin/gtkimhtmltoolbar.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Sun May 25 04:30:41 2008 +0000 @@ -36,6 +36,7 @@ #include "gtkdialogs.h" #include "gtkimhtmltoolbar.h" +#include "gtksmiley.h" #include "gtkthemes.h" #include "gtkutils.h" @@ -579,8 +580,7 @@ } static gboolean -close_smiley_dialog(GtkWidget *widget, GdkEvent *event, - GtkIMHtmlToolbar *toolbar) +close_smiley_dialog(GtkIMHtmlToolbar *toolbar) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smiley), FALSE); return FALSE; @@ -601,24 +601,30 @@ g_free(escaped_smiley); - close_smiley_dialog(NULL, NULL, toolbar); + close_smiley_dialog(toolbar); } /* smiley buttons list */ struct smiley_button_list { int width, height; GtkWidget *button; + const GtkIMHtmlSmiley *smiley; struct smiley_button_list *next; }; static struct smiley_button_list * -sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar, int *width, char *filename, char *face) +sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar, + int *width, const GtkIMHtmlSmiley *smiley, + const GSList *custom_smileys) { GtkWidget *image; GtkWidget *button; GtkRequisition size; struct smiley_button_list *cur; struct smiley_button_list *it, *it_last; + const gchar *filename = smiley->file; + gchar *face = smiley->smile; + PurpleSmiley *psmiley = NULL; cur = g_new0(struct smiley_button_list, 1); it = ls; @@ -626,6 +632,35 @@ image = gtk_image_new_from_file(filename); gtk_widget_size_request(image, &size); + + if (size.width > 24 && + smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) { /* This is a custom smiley, let's scale it */ + GdkPixbuf *pixbuf = NULL; + GtkImageType type; + + type = gtk_image_get_storage_type(GTK_IMAGE(image)); + + if (type == GTK_IMAGE_PIXBUF) { + pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image)); + } else if (type == GTK_IMAGE_ANIMATION) { + GdkPixbufAnimation *animation; + + animation = gtk_image_get_animation(GTK_IMAGE(image)); + + pixbuf = gdk_pixbuf_animation_get_static_image(animation); + } + + if (pixbuf != NULL) { + GdkPixbuf *resized; + resized = gdk_pixbuf_scale_simple(pixbuf, 24, 24, + GDK_INTERP_HYPER); + + gtk_image_set_from_pixbuf(GTK_IMAGE(image), resized); /* This unrefs pixbuf */ + gtk_widget_size_request(image, &size); + g_object_unref(G_OBJECT(resized)); + } + } + (*width) += size.width; button = gtk_button_new(); @@ -639,10 +674,26 @@ /* these look really weird with borders */ gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + psmiley = purple_smileys_find_by_shortcut(smiley->smile); + /* If this is a "non-custom" smiley, check to see if its shortcut is + "shadowed" by any custom smiley. This can only happen if the connection + is custom smiley-enabled */ + if (psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) { + gtk_tooltips_set_tip(toolbar->tooltips, button, + _("This smiley is disabled because a custom smiley exists for this shortcut."), + NULL); + gtk_widget_set_sensitive(button, FALSE); + } else if (psmiley) { + /* Remove the button if the smiley is destroyed */ + g_signal_connect_object(G_OBJECT(psmiley), "destroy", G_CALLBACK(gtk_widget_destroy), + button, G_CONNECT_SWAPPED); + } + /* set current element to add */ cur->height = size.height; cur->width = size.width; cur->button = button; + cur->smiley = smiley; cur->next = ls; /* check where to insert by height */ @@ -661,7 +712,7 @@ smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley) { while (list) { - GtkIMHtmlSmiley *cur = list->data; + GtkIMHtmlSmiley *cur = (GtkIMHtmlSmiley *) list->data; if (!strcmp(cur->file, smiley->file)) return FALSE; list = list->next; @@ -675,7 +726,7 @@ if ((event->type == GDK_KEY_PRESS && event->key.keyval == GDK_Escape) || (event->type == GDK_BUTTON_PRESS && event->button.button == 1)) { - close_smiley_dialog(NULL, NULL, toolbar); + close_smiley_dialog(toolbar); return TRUE; } @@ -683,11 +734,43 @@ } static void +add_smiley_list(GtkWidget *container, struct smiley_button_list *list, + int max_width, gboolean custom) +{ + GtkWidget *line; + int line_width = 0; + + if (!list) + return; + + line = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0); + for (; list; list = list->next) { + if (custom != !!(list->smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) + continue; + gtk_box_pack_start(GTK_BOX(line), list->button, FALSE, FALSE, 0); + gtk_widget_show(list->button); + line_width += list->width; + if (line_width >= max_width) { + if (list->next) { + line = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0); + } + line_width = 0; + } + } +} + +static void insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar) { - GtkWidget *dialog; + GtkWidget *dialog, *vbox; GtkWidget *smiley_table = NULL; GSList *smileys, *unique_smileys = NULL; + const GSList *custom_smileys = NULL; + gboolean supports_custom = FALSE; + GtkRequisition req; + GtkWidget *scrolled, *viewport; if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smiley))) { destroy_smiley_dialog(toolbar); @@ -701,61 +784,74 @@ smileys = pidgin_themes_get_proto_smileys(NULL); while(smileys) { - GtkIMHtmlSmiley *smiley = smileys->data; + GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) smileys->data; if(!smiley->hidden) { - if(smiley_is_unique(unique_smileys, smiley)) + if(smiley_is_unique(unique_smileys, smiley)) { unique_smileys = g_slist_append(unique_smileys, smiley); + } } smileys = smileys->next; } + supports_custom = (gtk_imhtml_get_format_functions(GTK_IMHTML(toolbar->imhtml)) & GTK_IMHTML_CUSTOM_SMILEY); + if (toolbar->imhtml && supports_custom) { + const GSList *iterator = NULL; + custom_smileys = pidgin_smileys_get_all(); + + for (iterator = custom_smileys ; iterator ; + iterator = g_slist_next(iterator)) { + GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) iterator->data; + unique_smileys = g_slist_append(unique_smileys, smiley); + } + } dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE); - gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog), FALSE, 0); if (unique_smileys != NULL) { - struct smiley_button_list *ls, *it, *it_tmp; - GtkWidget *line; - int line_width = 0; - int max_line_width, num_lines; - int col=0; + struct smiley_button_list *ls; + int max_line_width, num_lines, button_width = 0; /* We use hboxes packed in a vbox */ ls = NULL; - line = gtk_hbox_new(FALSE, 0); - line_width = 0; max_line_width = 0; num_lines = floor(sqrt(g_slist_length(unique_smileys))); smiley_table = gtk_vbox_new(FALSE, 0); + if (supports_custom) { + GtkWidget *manage = gtk_button_new_with_mnemonic(_("_Manage custom smileys")); + GtkRequisition req; + g_signal_connect(G_OBJECT(manage), "clicked", + G_CALLBACK(pidgin_smiley_manager_show), NULL); + g_signal_connect_swapped(G_OBJECT(manage), "clicked", + G_CALLBACK(gtk_widget_destroy), dialog); + gtk_box_pack_end(GTK_BOX(vbox), manage, FALSE, TRUE, 0); + gtk_widget_size_request(manage, &req); + button_width = req.width; + } + /* create list of smileys sorted by height */ while (unique_smileys) { - GtkIMHtmlSmiley *smiley = unique_smileys->data; + GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) unique_smileys->data; if (!smiley->hidden) { - ls = sort_smileys(ls, toolbar, &max_line_width, smiley->file, smiley->smile); + ls = sort_smileys(ls, toolbar, &max_line_width, smiley, custom_smileys); } unique_smileys = g_slist_delete_link(unique_smileys, unique_smileys); } + /* The window will be at least as wide as the 'Manage ..' button */ + max_line_width = MAX(button_width, max_line_width / num_lines); + /* pack buttons of the list */ - max_line_width = max_line_width / num_lines; - it = ls; - while (it != NULL) - { - it_tmp = it; - gtk_box_pack_start(GTK_BOX(line), it->button, FALSE, FALSE, 0); - gtk_widget_show(it->button); - line_width += it->width; - if (line_width >= max_line_width) { - gtk_box_pack_start(GTK_BOX(smiley_table), line, FALSE, FALSE, 0); - line = gtk_hbox_new(FALSE, 0); - line_width = 0; - col = 0; - } - col++; - it = it->next; - g_free(it_tmp); + add_smiley_list(smiley_table, ls, max_line_width, FALSE); + if (supports_custom) { + gtk_box_pack_start(GTK_BOX(smiley_table), gtk_hseparator_new(), TRUE, FALSE, 0); + add_smiley_list(smiley_table, ls, max_line_width, TRUE); } - gtk_box_pack_start(GTK_BOX(smiley_table), line, FALSE, TRUE, 0); + while (ls) { + struct smiley_button_list *tmp = ls->next; + g_free(ls); + ls = tmp; + } gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK); } @@ -765,19 +861,41 @@ g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar); } - g_signal_connect(G_OBJECT(dialog), "key-press-event", (GCallback)smiley_dialog_input_cb, toolbar); - gtk_container_add(GTK_CONTAINER(pidgin_dialog_get_vbox(GTK_DIALOG(dialog))), smiley_table); + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_NONE); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_NEVER, GTK_POLICY_NEVER); + gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0); + gtk_widget_show(scrolled); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), smiley_table); gtk_widget_show(smiley_table); + viewport = gtk_widget_get_parent(smiley_table); + gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); + /* connect signals */ - g_signal_connect(G_OBJECT(dialog), "delete_event", - G_CALLBACK(close_smiley_dialog), toolbar); + g_signal_connect_swapped(G_OBJECT(dialog), "destroy", G_CALLBACK(close_smiley_dialog), toolbar); + g_signal_connect(G_OBJECT(dialog), "key-press-event", G_CALLBACK(smiley_dialog_input_cb), toolbar); + + gtk_window_set_transient_for(GTK_WINDOW(dialog), + GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar)))); /* show everything */ gtk_widget_show_all(dialog); - gtk_window_set_transient_for(GTK_WINDOW(dialog), - GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar)))); + + gtk_widget_size_request(viewport, &req); + gtk_widget_set_size_request(scrolled, MIN(300, req.width), MIN(290, req.height)); + + /* The window has to be made resizable, and the scrollbars in the scrolled window + * enabled only after setting the desired size of the window. If we do either of + * these tasks before now, GTK+ miscalculates the required size, and erronously + * makes one or both scrollbars visible (sometimes). + * I too think this hack is gross. But I couldn't find a better way -- sadrul */ + gtk_window_set_resizable(GTK_WINDOW(dialog), TRUE); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + #ifdef _WIN32 winpidgin_ensure_onscreen(dialog); #endif
--- a/pidgin/gtkmain.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkmain.c Sun May 25 04:30:41 2008 +0000 @@ -62,6 +62,7 @@ #include "gtkroomlist.h" #include "gtksavedstatuses.h" #include "gtksession.h" +#include "gtksmiley.h" #include "gtksound.h" #include "gtkthemes.h" #include "gtkutils.h" @@ -315,6 +316,7 @@ pidgin_roomlist_init(); pidgin_log_init(); pidgin_docklet_init(); + pidgin_smileys_init(); } static GHashTable *ui_info = NULL; @@ -331,6 +333,7 @@ pidgin_plugins_save(); /* Uninit */ + pidgin_smileys_uninit(); pidgin_conversations_uninit(); pidgin_status_uninit(); pidgin_docklet_uninit();
--- a/pidgin/gtkplugin.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkplugin.c Sun May 25 04:30:41 2008 +0000 @@ -421,6 +421,8 @@ "<span size=\"smaller\">%s</span>", name, version); gtk_label_set_markup(plugin_name, buf); + g_free(name); + g_free(version); g_free(buf); gtk_text_buffer_set_text(plugin_desc, purple_plugin_get_description(plug), -1); @@ -694,6 +696,8 @@ gtk_label_set_markup(GTK_LABEL(label), _("<b>Filename:</b>")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + g_object_unref(sg); + return GTK_WIDGET(vbox); }
--- a/pidgin/gtkpounce.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkpounce.c Sun May 25 04:30:41 2008 +0000 @@ -100,6 +100,7 @@ GtkWidget *play_sound_entry; GtkWidget *play_sound_browse; GtkWidget *play_sound_test; + GtkWidget *play_sound_reset; GtkWidget *save_pounce; @@ -166,13 +167,26 @@ pounce_test_sound(GtkWidget *w, GtkWidget *entry) { const char *filename; + gboolean temp_mute; + + temp_mute = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/sound/mute"); + + if (temp_mute) purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", FALSE); filename = gtk_entry_get_text(GTK_ENTRY(entry)); - if (filename != NULL && *filename != '\0') + if (filename != NULL && *filename != '\0' && strcmp(filename, _("(default)"))) purple_sound_play_file(filename, NULL); else purple_sound_play_event(PURPLE_SOUND_POUNCE_DEFAULT, NULL); + + if (temp_mute) purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/sound/mute", TRUE); +} + +static void +pounce_reset_sound(GtkWidget *w, GtkWidget *entry) +{ + gtk_entry_set_text(GTK_ENTRY(entry), _("(default)")); } static void @@ -304,7 +318,7 @@ message = NULL; } if (*command == '\0') command = NULL; - if (*sound == '\0') sound = NULL; + if (*sound == '\0' || !strcmp(sound, _("(default)"))) sound = NULL; /* If the pounce has already been triggered, let's pretend it is a new one */ if (dialog->pounce != NULL @@ -652,7 +666,7 @@ /* Create the "Action" frame. */ frame = pidgin_make_frame(vbox2, _("Action")); - table = gtk_table_new(3, 5, FALSE); + table = gtk_table_new(3, 6, FALSE); gtk_container_add(GTK_CONTAINER(frame), table); gtk_table_set_col_spacings(GTK_TABLE(table), PIDGIN_HIG_BORDER); gtk_widget_show(table); @@ -674,8 +688,11 @@ dialog->popup_entry = gtk_entry_new(); dialog->exec_cmd_browse = gtk_button_new_with_mnemonic(_("Brows_e...")); dialog->play_sound_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(dialog->play_sound_entry), _("(default)")); + gtk_editable_set_editable(GTK_EDITABLE(dialog->play_sound_entry), FALSE); dialog->play_sound_browse = gtk_button_new_with_mnemonic(_("Br_owse...")); dialog->play_sound_test = gtk_button_new_with_mnemonic(_("Pre_view")); + dialog->play_sound_reset = gtk_button_new_with_mnemonic(_("Reset")); gtk_widget_set_sensitive(send_msg_imhtml, FALSE); gtk_widget_set_sensitive(dialog->exec_cmd_entry, FALSE); @@ -684,6 +701,7 @@ gtk_widget_set_sensitive(dialog->play_sound_entry, FALSE); gtk_widget_set_sensitive(dialog->play_sound_browse, FALSE); gtk_widget_set_sensitive(dialog->play_sound_test, FALSE); + gtk_widget_set_sensitive(dialog->play_sound_reset, FALSE); g_object_unref(sg); @@ -698,6 +716,7 @@ gtk_size_group_add_widget(sg, dialog->play_sound_entry); gtk_size_group_add_widget(sg, dialog->play_sound_browse); gtk_size_group_add_widget(sg, dialog->play_sound_test); + gtk_size_group_add_widget(sg, dialog->play_sound_reset); g_object_unref(sg); sg = NULL; @@ -706,11 +725,11 @@ GTK_FILL, 0, 0, 0); gtk_table_attach(GTK_TABLE(table), dialog->popup, 0, 1, 1, 2, GTK_FILL, 0, 0, 0); - gtk_table_attach(GTK_TABLE(table), dialog->popup_entry, 1, 4, 1, 2, + gtk_table_attach(GTK_TABLE(table), dialog->popup_entry, 1, 5, 1, 2, GTK_FILL, 0, 0, 0); - gtk_table_attach(GTK_TABLE(table), dialog->send_msg, 0, 4, 2, 3, + gtk_table_attach(GTK_TABLE(table), dialog->send_msg, 0, 5, 2, 3, GTK_FILL, 0, 0, 0); - gtk_table_attach(GTK_TABLE(table), send_msg_imhtml, 0, 4, 3, 4, + gtk_table_attach(GTK_TABLE(table), send_msg_imhtml, 0, 5, 3, 4, GTK_FILL, 0, 0, 0); gtk_table_attach(GTK_TABLE(table), dialog->exec_cmd, 0, 1, 4, 5, GTK_FILL, 0, 0, 0); @@ -726,6 +745,8 @@ GTK_FILL | GTK_EXPAND, 0, 0, 0); gtk_table_attach(GTK_TABLE(table), dialog->play_sound_test, 3, 4, 5, 6, GTK_FILL | GTK_EXPAND, 0, 0, 0); + gtk_table_attach(GTK_TABLE(table), dialog->play_sound_reset, 4, 5, 5, 6, + GTK_FILL | GTK_EXPAND, 0, 0, 0); gtk_table_set_row_spacings(GTK_TABLE(table), PIDGIN_HIG_BOX_SPACE / 2); @@ -741,6 +762,7 @@ gtk_widget_show(dialog->play_sound_entry); gtk_widget_show(dialog->play_sound_browse); gtk_widget_show(dialog->play_sound_test); + gtk_widget_show(dialog->play_sound_reset); g_signal_connect(G_OBJECT(dialog->message_recv), "clicked", G_CALLBACK(message_recv_toggle), @@ -771,6 +793,7 @@ g_ptr_array_add(sound_widgets,dialog->play_sound_entry); g_ptr_array_add(sound_widgets,dialog->play_sound_browse); g_ptr_array_add(sound_widgets,dialog->play_sound_test); + g_ptr_array_add(sound_widgets,dialog->play_sound_reset); g_signal_connect(G_OBJECT(dialog->play_sound), "clicked", G_CALLBACK(pidgin_toggle_sensitive_array), @@ -781,6 +804,9 @@ g_signal_connect(G_OBJECT(dialog->play_sound_test), "clicked", G_CALLBACK(pounce_test_sound), dialog->play_sound_entry); + g_signal_connect(G_OBJECT(dialog->play_sound_reset), "clicked", + G_CALLBACK(pounce_reset_sound), + dialog->play_sound_entry); g_object_set_data_full(G_OBJECT(dialog->window), "sound-widgets", sound_widgets, (GDestroyNotify)g_ptr_array_free); @@ -795,8 +821,6 @@ G_CALLBACK(save_pounce_cb), dialog); g_signal_connect(G_OBJECT(dialog->exec_cmd_entry), "activate", G_CALLBACK(save_pounce_cb), dialog); - g_signal_connect(G_OBJECT(dialog->play_sound_entry), "activate", - G_CALLBACK(save_pounce_cb), dialog); /* Create the "Options" frame. */ frame = pidgin_make_frame(vbox2, _("Options")); @@ -924,7 +948,7 @@ "play-sound", "filename")) != NULL) { - gtk_entry_set_text(GTK_ENTRY(dialog->play_sound_entry), value); + gtk_entry_set_text(GTK_ENTRY(dialog->play_sound_entry), (value && *value != '\0') ? value : _("(default)")); } } else
--- a/pidgin/gtkprefs.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkprefs.c Sun May 25 04:30:41 2008 +0000 @@ -2004,18 +2004,18 @@ gtk_editable_set_editable(GTK_EDITABLE(sound_entry), FALSE); gtk_box_pack_start(GTK_BOX(hbox), sound_entry, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE); - button = gtk_button_new_with_label(_("Test")); + button = gtk_button_new_with_mnemonic(_("_Browse...")); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(select_sound), NULL); + gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1); + + button = gtk_button_new_with_mnemonic(_("Pre_view")); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(test_sound), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1); - button = gtk_button_new_with_label(_("Reset")); + button = gtk_button_new_with_mnemonic(_("_Reset")); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(reset_sound), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1); - button = gtk_button_new_with_label(_("Choose...")); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(select_sound), NULL); - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1); - gtk_widget_show_all(ret); g_object_unref(sg);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtksmiley.c Sun May 25 04:30:41 2008 +0000 @@ -0,0 +1,705 @@ +/** + * @file gtksmiley.c GTK+ Smiley Manager API + * @ingroup pidgin + */ + +/* + * pidgin + * + * Pidgin 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "internal.h" +#include "pidgin.h" + +#include "debug.h" +#include "notify.h" +#include "smiley.h" + +#include "gtkimhtml.h" +#include "gtksmiley.h" +#include "gtkutils.h" +#include "pidginstock.h" + +#define PIDGIN_RESPONSE_EDIT 1000 + +struct _PidginSmiley +{ + PurpleSmiley *smiley; + GtkWidget *parent; + GtkWidget *smile; + GtkWidget *smiley_image; + gchar *filename; + GdkPixbuf *custom_pixbuf; +}; + +typedef struct +{ + GtkWidget *window; + + GtkWidget *treeview; + GtkListStore *model; +} SmileyManager; + +enum +{ + ICON, + SHORTCUT, + SMILEY, + N_COL +}; + +static SmileyManager *smiley_manager = NULL; +static GSList *gtk_smileys = NULL; + +static void +pidgin_smiley_destroy(PidginSmiley *smiley) +{ + gtk_widget_destroy(smiley->parent); + g_free(smiley->filename); + if (smiley->custom_pixbuf) + gdk_pixbuf_unref(smiley->custom_pixbuf); + g_free(smiley); +} + +/****************************************************************************** + * GtkIMHtmlSmileys stuff + *****************************************************************************/ +/* Perhaps these should be in gtkimhtml.c instead. -- sadrul */ +static void add_gtkimhtml_to_list(GtkIMHtmlSmiley *gtksmiley) +{ + gtk_smileys = g_slist_prepend(gtk_smileys, gtksmiley); + + purple_debug_info("gtksmiley", "adding %s to gtk_smileys\n", gtksmiley->smile); +} + +static void +shortcut_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gtksmiley) +{ + g_free(gtksmiley->smile); + gtksmiley->smile = g_strdup(purple_smiley_get_shortcut(smiley)); +} + +static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley) +{ + GtkIMHtmlSmiley *gtksmiley; + gchar *filename; + const gchar *file; + + file = purple_imgstore_get_filename(purple_smiley_get_stored_image(smiley)); + + filename = g_build_filename(purple_smileys_get_storing_dir(), file, NULL); + + gtksmiley = gtk_imhtml_smiley_create(filename, purple_smiley_get_shortcut(smiley), + FALSE, GTK_IMHTML_SMILEY_CUSTOM); + g_free(filename); + + /* Make sure the shortcut for the GtkIMHtmlSmiley is updated with the PurpleSmiley */ + g_signal_connect(G_OBJECT(smiley), "notify::shortcut", + G_CALLBACK(shortcut_changed_cb), gtksmiley); + + return gtksmiley; +} + +void pidgin_smiley_del_from_list(PurpleSmiley *smiley) +{ + GSList *list = NULL; + GtkIMHtmlSmiley *gtksmiley; + + if (gtk_smileys == NULL) + return; + + list = gtk_smileys; + + for (; list; list = list->next) { + gtksmiley = (GtkIMHtmlSmiley*)list->data; + + if (strcmp(gtksmiley->smile, purple_smiley_get_shortcut(smiley))) + continue; + + gtk_imhtml_smiley_destroy(gtksmiley); + g_signal_handlers_disconnect_matched(G_OBJECT(smiley), G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, gtksmiley); + break; + } + + if (list) + gtk_smileys = g_slist_delete_link(gtk_smileys, list); +} + +void pidgin_smiley_add_to_list(PurpleSmiley *smiley) +{ + GtkIMHtmlSmiley *gtksmiley; + + gtksmiley = smiley_purple_to_gtkimhtml(smiley); + add_gtkimhtml_to_list(gtksmiley); + g_signal_connect(G_OBJECT(smiley), "destroy", G_CALLBACK(pidgin_smiley_del_from_list), NULL); +} + +void pidgin_smileys_init(void) +{ + GList *smileys; + PurpleSmiley *smiley; + + if (gtk_smileys != NULL) + return; + + smileys = purple_smileys_get_all(); + + for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { + smiley = (PurpleSmiley*)smileys->data; + + pidgin_smiley_add_to_list(smiley); + } +} + +void pidgin_smileys_uninit(void) +{ + GSList *list; + GtkIMHtmlSmiley *gtksmiley; + + list = gtk_smileys; + + if (list == NULL) + return; + + for (; list; list = g_slist_delete_link(list, list)) { + gtksmiley = (GtkIMHtmlSmiley*)list->data; + gtk_imhtml_smiley_destroy(gtksmiley); + } + + gtk_smileys = NULL; +} + +GSList *pidgin_smileys_get_all(void) +{ + return gtk_smileys; +} + +/****************************************************************************** + * Manager stuff + *****************************************************************************/ + +static void refresh_list(void); + +/****************************************************************************** + * The Add dialog + ******************************************************************************/ + +static void do_add(GtkWidget *widget, PidginSmiley *s) +{ + const gchar *entry; + PurpleSmiley *emoticon; + + entry = gtk_entry_get_text(GTK_ENTRY(s->smile)); + emoticon = purple_smileys_find_by_shortcut(entry); + if (emoticon && emoticon != s->smiley) { + purple_notify_error(s->parent, _("Custom Smiley"), + _("Duplicate Shortcut"), + _("A custom smiley for the selected shortcut already exists. Please specify a different shortcut.")); + return; + } + + if (s->smiley) { + if (s->filename) { + gchar *data = NULL; + size_t len; + GError *err = NULL; + + if (!g_file_get_contents(s->filename, &data, &len, &err)) { + purple_debug_error("gtksmiley", "Error reading %s: %s\n", + s->filename, err->message); + g_error_free(err); + + return; + } + purple_smiley_set_data(s->smiley, (guchar*)data, len, FALSE); + } + purple_smiley_set_shortcut(s->smiley, entry); + } else { + if ((s->filename == NULL && s->custom_pixbuf == NULL) + || *entry == 0) { + purple_notify_error(s->parent, _("Custom Smiley"), + _("More Data needed"), + s->filename ? _("Please provide a shortcut to associate with the smiley.") + : _("Please select an image for the smiley.")); + return; + } + + purple_debug_info("gtksmiley", "adding a new smiley\n"); + + if (s->filename == NULL) { + /* Get the smiley from the custom pixbuf */ + gchar *buffer = NULL; + gsize size = 0; + gchar *filename; + + gdk_pixbuf_save_to_bufferv(s->custom_pixbuf, &buffer, &size, + "png", NULL, NULL, NULL); + filename = purple_util_get_image_filename(buffer, size); + s->filename = g_build_filename(purple_smileys_get_storing_dir(), filename, NULL); + purple_util_write_data_to_file_absolute(s->filename, buffer, size); + g_free(filename); + g_free(buffer); + } + emoticon = purple_smiley_new_from_file(entry, s->filename); + pidgin_smiley_add_to_list(emoticon); + } + + if (smiley_manager != NULL) + refresh_list(); + + gtk_widget_destroy(s->parent); +} + +static void do_add_select_cb(GtkWidget *widget, gint resp, PidginSmiley *s) +{ + switch (resp) { + case GTK_RESPONSE_ACCEPT: + do_add(widget, s); + break; + case GTK_RESPONSE_DELETE_EVENT: + case GTK_RESPONSE_CANCEL: + gtk_widget_destroy(s->parent); + break; + default: + purple_debug_error("gtksmiley", "no valid response\n"); + break; + } +} + +static void do_add_file_cb(const char *filename, gpointer data) +{ + PidginSmiley *s = data; + GdkPixbuf *pixbuf; + + if (!filename) + return; + + g_free(s->filename); + s->filename = g_strdup(filename); + pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, 64, 64, FALSE, NULL); + gtk_image_set_from_pixbuf(GTK_IMAGE(s->smiley_image), pixbuf); + if (pixbuf) + gdk_pixbuf_unref(pixbuf); + gtk_widget_grab_focus(s->smile); +} + +static void +open_image_selector(GtkWidget *widget, PidginSmiley *psmiley) +{ + GtkWidget *file_chooser; + file_chooser = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)), + do_add_file_cb, psmiley); + gtk_window_set_title(GTK_WINDOW(file_chooser), _("Custom Smiley")); + gtk_window_set_role(GTK_WINDOW(file_chooser), "file-selector-custom-smiley"); + gtk_widget_show_all(file_chooser); +} + +PidginSmiley * +pidgin_smiley_edit(GtkWidget *widget, PurpleSmiley *smiley) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *filech; + GtkWidget *window; + GdkPixbuf *pixbuf = NULL; + PurpleStoredImage *stored_img; + + PidginSmiley *s = g_new0(PidginSmiley, 1); + s->smiley = smiley; + + window = gtk_dialog_new_with_buttons(smiley ? _("Edit Smiley") : _("Add Smiley"), + widget ? GTK_WINDOW(widget) : NULL, + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, + smiley ? GTK_STOCK_SAVE : GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + s->parent = window; + + gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER); + + g_signal_connect(window, "response", G_CALLBACK(do_add_select_cb), s); + + /* The vbox */ + vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), vbox); + gtk_widget_show(vbox); + + /* The hbox */ + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); + gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox); + + label = gtk_label_new_with_mnemonic(_("Smiley _Image")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + filech = gtk_button_new(); + gtk_box_pack_end(GTK_BOX(hbox), filech, FALSE, FALSE, 0); + pidgin_set_accessible_label(filech, label); + + s->smiley_image = gtk_image_new(); + gtk_container_add(GTK_CONTAINER(filech), s->smiley_image); + if (smiley && (stored_img = purple_smiley_get_stored_image(smiley))) { + pixbuf = pidgin_pixbuf_from_imgstore(stored_img); + purple_imgstore_unref(stored_img); + } else { + GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL); + pixbuf = gtk_widget_render_icon(window, PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, + icon_size, "PidginSmiley"); + } + + gtk_image_set_from_pixbuf(GTK_IMAGE(s->smiley_image), pixbuf); + if (pixbuf != NULL) + g_object_unref(G_OBJECT(pixbuf)); + g_signal_connect(G_OBJECT(filech), "clicked", G_CALLBACK(open_image_selector), s); + + gtk_widget_show_all(hbox); + + /* info */ + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); + gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)),hbox); + + /* Smiley shortcut */ + label = gtk_label_new_with_mnemonic(_("Smiley S_hortcut")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + s->smile = gtk_entry_new(); + gtk_entry_set_activates_default(GTK_ENTRY(s->smile), TRUE); + pidgin_set_accessible_label(s->smile, label); + if (smiley) + gtk_entry_set_text(GTK_ENTRY(s->smile), purple_smiley_get_shortcut(smiley)); + + g_signal_connect(s->smile, "activate", G_CALLBACK(do_add), s); + + gtk_box_pack_end(GTK_BOX(hbox), s->smile, FALSE, FALSE, 0); + gtk_widget_show(s->smile); + + gtk_widget_show(hbox); + + gtk_widget_show(GTK_WIDGET(window)); + g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(pidgin_smiley_destroy), s); + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(purple_notify_close_with_handle), s); + + return s; +} + +void +pidgin_smiley_editor_set_shortcut(PidginSmiley *editor, const gchar *shortcut) +{ + gtk_entry_set_text(GTK_ENTRY(editor->smile), shortcut ? shortcut : ""); +} + +void +pidgin_smiley_editor_set_image(PidginSmiley *editor, GdkPixbuf *image) +{ + if (editor->custom_pixbuf) + gdk_pixbuf_unref(editor->custom_pixbuf); + editor->custom_pixbuf = image ? gdk_pixbuf_ref(image) : NULL; + if (image) + gtk_image_set_from_pixbuf(GTK_IMAGE(editor->smiley_image), image); +} + +/****************************************************************************** + * Delete smiley + *****************************************************************************/ +static void delete_foreach(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + PurpleSmiley *smiley = NULL; + SmileyManager *dialog; + + dialog = (SmileyManager*)data; + + gtk_tree_model_get(model, iter, + SMILEY, &smiley, + -1); + + if(smiley != NULL) { + g_object_unref(G_OBJECT(smiley)); + pidgin_smiley_del_from_list(smiley); + purple_smiley_delete(smiley); + } +} + +static void append_to_list(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + GList **list = data; + *list = g_list_prepend(*list, gtk_tree_path_copy(path)); +} + +static void smiley_delete(SmileyManager *dialog) +{ + GtkTreeSelection *selection; + GList *list = NULL; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); + gtk_tree_selection_selected_foreach(selection, delete_foreach, dialog); + gtk_tree_selection_selected_foreach(selection, append_to_list, &list); + + while (list) { + GtkTreeIter iter; + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, list->data)) + gtk_list_store_remove(GTK_LIST_STORE(dialog->model), &iter); + gtk_tree_path_free(list->data); + list = g_list_delete_link(list, list); + } +} +/****************************************************************************** + * The Smiley Manager + *****************************************************************************/ +static void add_columns(GtkWidget *treeview, SmileyManager *dialog) +{ + GtkCellRenderer *rend; + GtkTreeViewColumn *column; + + /* Icon */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Smiley")); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_add_attribute(column, rend, "pixbuf", ICON); + + /* Shortcut */ + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Shortcut")); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, TRUE); + gtk_tree_view_column_add_attribute(column, rend, "text", SHORTCUT); +} + +static void store_smiley_add(PurpleSmiley *smiley) +{ + GtkTreeIter iter; + PurpleStoredImage *img; + GdkPixbuf *sized_smiley = NULL; + + if (smiley_manager == NULL) + return; + + img = purple_smiley_get_stored_image(smiley); + + if (img != NULL) { + GdkPixbuf *smiley_image = pidgin_pixbuf_from_imgstore(img); + purple_imgstore_unref(img); + + if (smiley_image != NULL) + sized_smiley = gdk_pixbuf_scale_simple(smiley_image, + 22, 22, GDK_INTERP_HYPER); + g_object_unref(G_OBJECT(smiley_image)); + } + + + gtk_list_store_append(smiley_manager->model, &iter); + + gtk_list_store_set(smiley_manager->model, &iter, + ICON, sized_smiley, + SHORTCUT, purple_smiley_get_shortcut(smiley), + SMILEY, smiley, + -1); + + if (sized_smiley != NULL) + g_object_unref(G_OBJECT(sized_smiley)); +} + +static void populate_smiley_list(SmileyManager *dialog) +{ + GList *list; + PurpleSmiley *emoticon; + + gtk_list_store_clear(dialog->model); + + for(list = purple_smileys_get_all(); list != NULL; + list = g_list_delete_link(list, list)) { + emoticon = (PurpleSmiley*)list->data; + + store_smiley_add(emoticon); + } +} + +static void smile_selected_cb(GtkTreeSelection *sel, SmileyManager *dialog) +{ + gint selected; + + selected = gtk_tree_selection_count_selected_rows(sel); + + gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog->window), + GTK_RESPONSE_NO, selected > 0); + + gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog->window), + PIDGIN_RESPONSE_EDIT, selected > 0); +} + +static void +smiley_edit_iter(SmileyManager *dialog, GtkTreeIter *iter) +{ + PurpleSmiley *smiley = NULL; + gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), iter, SMILEY, &smiley, -1); + pidgin_smiley_edit(gtk_widget_get_toplevel(GTK_WIDGET(dialog->treeview)), smiley); + g_object_unref(G_OBJECT(smiley)); +} + +static void smiley_edit_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) +{ + GtkTreeIter iter; + SmileyManager *dialog = data; + + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path); + smiley_edit_iter(dialog, &iter); +} + +static void +edit_selected_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + smiley_edit_iter(data, iter); +} + +static GtkWidget *smiley_list_create(SmileyManager *dialog) +{ + GtkWidget *sw; + GtkWidget *treeview; + GtkTreeSelection *sel; + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); + gtk_widget_show(sw); + + /* Create the list model */ + dialog->model = gtk_list_store_new(N_COL, + GDK_TYPE_PIXBUF, /* ICON */ + G_TYPE_STRING, /* SHORTCUT */ + G_TYPE_OBJECT /* SMILEY */ + ); + + /* the actual treeview */ + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); + dialog->treeview = treeview; + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dialog->model), SHORTCUT, GTK_SORT_ASCENDING); + g_object_unref(G_OBJECT(dialog->model)); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE); + gtk_container_add(GTK_CONTAINER(sw), treeview); + + g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(smile_selected_cb), dialog); + g_signal_connect(G_OBJECT(treeview), "row_activated", G_CALLBACK(smiley_edit_cb), dialog); + + gtk_widget_show(treeview); + + add_columns(treeview, dialog); + populate_smiley_list(dialog); + + return sw; +} + +static void refresh_list() +{ + populate_smiley_list(smiley_manager); +} + +static void smiley_manager_select_cb(GtkWidget *widget, gint resp, SmileyManager *dialog) +{ + GtkTreeSelection *selection = NULL; + + switch (resp) { + case GTK_RESPONSE_YES: + pidgin_smiley_edit(dialog->window, NULL); + break; + case GTK_RESPONSE_NO: + smiley_delete(dialog); + break; + case GTK_RESPONSE_DELETE_EVENT: + case GTK_RESPONSE_CLOSE: + gtk_widget_destroy(dialog->window); + g_free(smiley_manager); + smiley_manager = NULL; + break; + case PIDGIN_RESPONSE_EDIT: + /* Find smiley of selection... */ + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); + gtk_tree_selection_selected_foreach(selection, edit_selected_cb, dialog); + break; + default: + purple_debug_info("gtksmiley", "No valid selection\n"); + break; + } +} + +void pidgin_smiley_manager_show(void) +{ + SmileyManager *dialog; + GtkWidget *win; + GtkWidget *sw; + GtkWidget *vbox; + + if (smiley_manager) { + gtk_window_present(GTK_WINDOW(smiley_manager->window)); + return; + } + + dialog = g_new0(SmileyManager, 1); + smiley_manager = dialog; + + dialog->window = win = gtk_dialog_new_with_buttons( + _("Custom Smiley Manager"), + NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_ADD, GTK_RESPONSE_YES, + GTK_STOCK_EDIT, PIDGIN_RESPONSE_EDIT, + GTK_STOCK_DELETE, GTK_RESPONSE_NO, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + gtk_window_set_default_size(GTK_WINDOW(win), 50, 400); + gtk_window_set_role(GTK_WINDOW(win), "custom_smiley_manager"); + gtk_container_set_border_width(GTK_CONTAINER(win),PIDGIN_HIG_BORDER); + gtk_dialog_set_response_sensitive(GTK_DIALOG(win), GTK_RESPONSE_NO, FALSE); + gtk_dialog_set_response_sensitive(GTK_DIALOG(win), PIDGIN_RESPONSE_EDIT, + FALSE); + + g_signal_connect(win, "response", G_CALLBACK(smiley_manager_select_cb), + dialog); + + /* The vbox */ + vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(win)->vbox), vbox); + gtk_widget_show(vbox); + + /* get the scrolled window with all stuff */ + sw = smiley_list_create(dialog); + gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); + gtk_widget_show(sw); + + gtk_widget_show(win); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/gtksmiley.h Sun May 25 04:30:41 2008 +0000 @@ -0,0 +1,103 @@ +/** + * @file gtksmiley.h GTK+ Custom Smiley API + * @ingroup pidgin + * @since 2.5.0 + */ + +/* pidgin + * + * Pidgin 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PIDGIN_GTKSMILEY_H_ +#define _PIDGIN_GTKSMILEY_H_ + +#include "smiley.h" + +typedef struct _PidginSmiley PidginSmiley; + +/** + * Add a PurpleSmiley to the GtkIMHtmlSmiley's list to be able to use it + * in pidgin + * + * @param smiley The smiley to be added. + */ +void pidgin_smiley_add_to_list(PurpleSmiley *smiley); + +/** + * Delete a PurpleSmiley from the GtkIMHtmlSmiley's list + * + * @param smiley The smiley to be deleted. + */ +void pidgin_smiley_del_from_list(PurpleSmiley *smiley); + +/** + * Load the GtkIMHtml list + */ +void pidgin_smileys_init(void); + +/** + * Uninit the GtkIMHtml list + */ +void pidgin_smileys_uninit(void); + +/** + * Returns a GSList with the GtkIMHtmlSmiley of each custom smiley + * + * @constreturn A GtkIMHmlSmiley list + */ +GSList* pidgin_smileys_get_all(void); + +/****************************************************************************** + * Smiley Manager + *****************************************************************************/ +/** + * Displays the Smiley Manager Window + */ +void pidgin_smiley_manager_show(void); + +/** + * Shows an editor for a smiley. + * + * @param widget The parent widget to be linked or @c NULL + * @param smiley The PurpleSmiley to be edited, or @c NULL for a new smiley + * @return The smiley add dialog + * + * @see pidgin_smiley_editor_set_shortcut + * @see pidgin_smiley_editor_set_image + */ +PidginSmiley *pidgin_smiley_edit(GtkWidget *widget, PurpleSmiley *smiley); + +/** + * Set the shortcut in a smiley add dialog + * + * @param editor A smiley editor dialog (created by pidgin_smiley_edit) + * @param shortcut The shortcut to set + */ +void pidgin_smiley_editor_set_shortcut(PidginSmiley *editor, const gchar *shortcut); + +/** + * Set the image in a smiley add dialog + * + * @param editor A smiley editor dialog + * @param image A GdkPixbuf image + */ +void pidgin_smiley_editor_set_image(PidginSmiley *editor, GdkPixbuf *image); + +#endif /* _PIDGIN_GTKSMILEY_H_*/
--- a/pidgin/gtksound.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtksound.c Sun May 25 04:30:41 2008 +0000 @@ -488,29 +488,13 @@ return; volume = (float)(CLAMP(purple_prefs_get_int(PIDGIN_PREFS_ROOT "/sound/volume"),0,100)) / 50; if (!strcmp(method, "automatic")) { - if (purple_running_gnome()) { - sink = gst_element_factory_make("gconfaudiosink", "sink"); - } - if (!sink) - sink = gst_element_factory_make("autoaudiosink", "sink"); - if (!sink) { - purple_debug_error("sound", "Unable to create GStreamer audiosink.\n"); - return; - } + sink = gst_element_factory_make("gconfaudiosink", "sink"); } #ifndef _WIN32 else if (!strcmp(method, "esd")) { sink = gst_element_factory_make("esdsink", "sink"); - if (!sink) { - purple_debug_error("sound", "Unable to create GStreamer audiosink.\n"); - return; - } } else if (!strcmp(method, "alsa")) { sink = gst_element_factory_make("alsasink", "sink"); - if (!sink) { - purple_debug_error("sound", "Unable to create GStreamer audiosink.\n"); - return; - } } #endif else { @@ -518,6 +502,11 @@ return; } + if (strcmp(method, "automatic") != 0 && !sink) { + purple_debug_error("sound", "Unable to create GStreamer audiosink.\n"); + return; + } + play = gst_element_factory_make("playbin", "play"); if (play == NULL) {
--- a/pidgin/gtkthemes.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkthemes.c Sun May 25 04:30:41 2008 +0000 @@ -31,6 +31,7 @@ #include "gtkconv.h" #include "gtkdialogs.h" #include "gtkimhtml.h" +#include "gtksmiley.h" #include "gtkthemes.h" GSList *smiley_themes = NULL; @@ -119,7 +120,7 @@ g_free(theme_dir); } -void pidgin_themes_smiley_themeize(GtkWidget *imhtml) +static void _pidgin_themes_smiley_themeize(GtkWidget *imhtml, gboolean custom) { struct smiley_list *list; if (!current_smiley_theme) @@ -134,10 +135,30 @@ gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data); icons = icons->next; } + + if (custom == TRUE) { + icons = pidgin_smileys_get_all(); + + while (icons) { + gtk_imhtml_associate_smiley(GTK_IMHTML(imhtml), sml, icons->data); + icons = icons->next; + } + } + list = list->next; } } +void pidgin_themes_smiley_themeize(GtkWidget *imhtml) +{ + _pidgin_themes_smiley_themeize(imhtml, FALSE); +} + +void pidgin_themes_smiley_themeize_custom(GtkWidget *imhtml) +{ + _pidgin_themes_smiley_themeize(imhtml, TRUE); +} + static void pidgin_themes_destroy_smiley_theme_smileys(struct smiley_theme *theme) { @@ -274,7 +295,6 @@ } else if (load && list) { gboolean hidden = FALSE; char *sfile = NULL; - gboolean have_used_sfile = FALSE; if (*i == '!' && *(i + 1) == ' ') { hidden = TRUE; @@ -288,17 +308,12 @@ i++; l[li++] = *(i++); } + l[li] = 0; if (!sfile) { - l[li] = 0; sfile = g_build_filename(dirname, l, NULL); } else { - GtkIMHtmlSmiley *smiley = g_new0(GtkIMHtmlSmiley, 1); - l[li] = 0; - smiley->file = sfile; - smiley->smile = g_strdup(l); - smiley->hidden = hidden; + GtkIMHtmlSmiley *smiley = gtk_imhtml_smiley_create(sfile, l, hidden, 0); list->smileys = g_slist_prepend(list->smileys, smiley); - have_used_sfile = TRUE; } while (isspace(*i)) i++; @@ -306,8 +321,7 @@ } - if (!have_used_sfile) - g_free(sfile); + g_free(sfile); } } @@ -340,8 +354,9 @@ PurpleConversation *conv = cnv->data; if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) { + /* We want to see our custom smileys on our entry if we write the shortcut */ pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->imhtml); - pidgin_themes_smiley_themeize(PIDGIN_CONVERSATION(conv)->entry); + pidgin_themes_smiley_themeize_custom(PIDGIN_CONVERSATION(conv)->entry); } } }
--- a/pidgin/gtkthemes.h Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkthemes.h Sun May 25 04:30:41 2008 +0000 @@ -51,6 +51,11 @@ void pidgin_themes_smiley_themeize(GtkWidget *); +/** + * @since 2.5.0 + */ +void pidgin_themes_smiley_themeize_custom(GtkWidget *); + void pidgin_themes_smiley_theme_probe(void); void pidgin_themes_load_smiley_theme(const char *file, gboolean load);
--- a/pidgin/gtkutils.c Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkutils.c Sun May 25 04:30:41 2008 +0000 @@ -103,7 +103,7 @@ g_signal_connect(G_OBJECT(imhtml), "url_clicked", G_CALLBACK(url_clicked_cb), NULL); - pidgin_themes_smiley_themeize(imhtml); + pidgin_themes_smiley_themeize_custom(imhtml); gtk_imhtml_set_funcs(GTK_IMHTML(imhtml), >kimhtml_cbs); @@ -1436,7 +1436,7 @@ static void dnd_image_ok_callback(_DndData *data, int choice) { - char *filedata; + gchar *filedata; size_t size; struct stat st; GError *err = NULL; @@ -1444,6 +1444,8 @@ PidginConversation *gtkconv; GtkTextIter iter; int id; + PurpleBuddy *buddy; + PurpleContact *contact; switch (choice) { case DND_BUDDY_ICON: if (g_stat(data->filename, &st)) { @@ -1459,7 +1461,13 @@ return; } - pidgin_set_custom_buddy_icon(data->account, data->who, data->filename); + buddy = purple_find_buddy(data->account, data->who); + if (!buddy) { + purple_debug_info("custom-icon", "You can only set custom icons for people on your buddylist.\n"); + break; + } + contact = purple_buddy_get_contact(buddy); + purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, data->filename); break; case DND_FILE_TRANSFER: serv_send_file(purple_account_get_connection(data->account), data->who, data->filename); @@ -2869,12 +2877,11 @@ } #endif /* ! Gtk 2.6.0 */ +#ifndef PURPLE_DISABLE_DEPRECATED void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename) { PurpleBuddy *buddy; PurpleContact *contact; - gpointer data = NULL; - size_t len = 0; buddy = purple_find_buddy(account, who); if (!buddy) { @@ -2883,20 +2890,9 @@ } contact = purple_buddy_get_contact(buddy); - - if (filename) { - const char *prpl_id = purple_account_get_protocol_id(account); - PurplePlugin *prpl = purple_find_prpl(prpl_id); - - data = pidgin_convert_buddy_icon(prpl, filename, &len); - - /* We don't want to delete the old icon if the new one didn't load. */ - if (data == NULL) - return; - } - - purple_buddy_icons_set_custom_icon(contact, data, len); + purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, filename); } +#endif char *pidgin_make_pretty_arrows(const char *str) { @@ -3469,6 +3465,20 @@ #endif } +GdkPixbuf * pidgin_pixbuf_from_imgstore(PurpleStoredImage *image) +{ + GdkPixbuf *pixbuf; + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + gdk_pixbuf_loader_write(loader, purple_imgstore_get_data(image), + purple_imgstore_get_size(image), NULL); + gdk_pixbuf_loader_close(loader, NULL); + pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); + if (pixbuf) + g_object_ref(pixbuf); + g_object_unref(loader); + return pixbuf; +} + gchar * pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis) {
--- a/pidgin/gtkutils.h Mon May 19 16:18:32 2008 +0000 +++ b/pidgin/gtkutils.h Sun May 25 04:30:41 2008 +0000 @@ -638,6 +638,7 @@ GError **error); #endif +#ifndef PURPLE_DISABLE_DEPRECATED /** * Set or unset a custom buddyicon for a user. * @@ -645,8 +646,10 @@ * @param who The name of the user. * @param filename The path of the custom icon. If this is @c NULL, then any * previously set custom buddy icon for the user is removed. + * @deprecated See purple_buddy_icons_node_set_custom_icon_from_file() */ void pidgin_set_custom_buddy_icon(PurpleAccount *account, const char *who, const char *filename); +#endif /** * Converts "->" and "<-" in strings to Unicode arrow characters, for use in referencing @@ -689,7 +692,7 @@ */ GtkWidget *pidgin_make_mini_dialog(PurpleConnection *handle, const char* stock_id, const char *primary, const char *secondary, - void *user_data, ...); + void *user_data, ...) G_GNUC_NULL_TERMINATED; /** * This is a callback function to be used for Ctrl+F searching in treeviews. @@ -809,6 +812,16 @@ */ GtkWidget *pidgin_add_widget_to_vbox(GtkBox *vbox, const char *widget_label, GtkSizeGroup *sg, GtkWidget *widget, gboolean expand, GtkWidget **p_label); +/** + * Create a GdkPixbuf from a PurpleStoredImage. + * + * @param image A PurpleStoredImage. + * + * @return A GdkPixbuf created from the stored image. + * @since 2.5.0 + */ +GdkPixbuf * pidgin_pixbuf_from_imgstore(PurpleStoredImage *image); + gchar *pidgin_gtk_ellipsis_text(GtkWidget *widget, const char *text, gint min_width, gchar *ellipsis); #endif /* _PIDGINUTILS_H_ */
--- a/po/POTFILES.in Mon May 19 16:18:32 2008 +0000 +++ b/po/POTFILES.in Sun May 25 04:30:41 2008 +0000 @@ -180,6 +180,7 @@ libpurple/request.h libpurple/savedstatuses.c libpurple/server.c +libpurple/smiley.c libpurple/sslconn.c libpurple/status.c libpurple/util.c @@ -209,6 +210,7 @@ pidgin/gtkrequest.c pidgin/gtkroomlist.c pidgin/gtksavedstatuses.c +pidgin/gtksmiley.c pidgin/gtksound.c pidgin/gtkstatusbox.c pidgin/gtkutils.c
--- a/po/de.po Mon May 19 16:18:32 2008 +0000 +++ b/po/de.po Sun May 25 04:30:41 2008 +0000 @@ -11,9 +11,9 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-05-17 21:01+0200\n" -"PO-Revision-Date: 2008-05-17 19:31+0200\n" -"Last-Translator: Björn Voigt <bjoern@cs.tu-berlin.de>\n" +"POT-Creation-Date: 2008-05-24 10:32+0200\n" +"PO-Revision-Date: 2008-05-24 10:31+0200\n" +"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n" "Language-Team: Deutsch <de@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -100,6 +100,10 @@ msgid "Alias:" msgstr "Alias:" +#. Register checkbox +msgid "Create this account on the server" +msgstr "Dieses Konto auf dem Server anlegen" + #. Cancel button #. Cancel msgid "Cancel" @@ -2092,8 +2096,10 @@ msgid "ABI version mismatch %d.%d.x (need %d.%d.x)" msgstr "ABI Versionskonflikt %d.%d.x (brauche %d.%d.x)" -msgid "Plugin does not implement all required functions" -msgstr "Plugin enthält nicht alle benötigten Funktionen" +msgid "" +"Plugin does not implement all required functions (list_icon, login and close)" +msgstr "" +"Plugin enthält nicht alle benötigten Funktionen (list_icon, login und close)" #, c-format msgid "" @@ -2757,13 +2763,11 @@ "dann installieren Sie ActiveTCL von http://www.activestate.com\n" msgid "" -"The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://" -"developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-" -"LocalMessaging for more information." +"The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d." +"pidgin.im/BonjourWindows for more information." msgstr "" "Apples Bonjour-Toolkit für Windows konnte nicht gefunden werden, für weitere " -"Informationen besuchen Sie die FAQ unter: http://developer.pidgin.im/wiki/" -"Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging." +"Informationen besuchen Sie die FAQ unter: http://d.pidgin.im/BonjourWindows" msgid "Unable to listen for incoming IM connections\n" msgstr "Kann nicht auf eingehende IM-Verbindungen hören\n" @@ -4507,8 +4511,8 @@ msgstr "XMPP-Nachrichtenfehler" #, c-format -msgid " (Code %s)" -msgstr " (Code %s)" +msgid "(Code %s)" +msgstr "(Code %s)" msgid "XML Parse error" msgstr "Fehler bei Einlesen von XML-Daten" @@ -4806,6 +4810,9 @@ msgid "Nudging %s..." msgstr "%s anstoßen..." +msgid "E-mail Address..." +msgstr "E-Mail-Adresse..." + msgid "Your new MSN friendly name is too long." msgstr "Ihr neuer MSN-Benutzername zu lang." @@ -9683,6 +9690,21 @@ msgid "Accept chat invitation?" msgstr "Akzeptieren Sie die Chat-Einladung?" +#. Shortcut +msgid "Shortcut" +msgstr "Tastenkombination" + +msgid "The text-shortcut for the smiley" +msgstr "Die Tastenkombination für den Smiley" + +#. Stored Image +msgid "Stored Image" +msgstr "Gespeichertes Bild" + +#, fuzzy +msgid "Stored Image. (that'll have to do for now)" +msgstr "Gespeichertes Bild. (Das muss erstmal reichen)" + msgid "SSL Connection Failed" msgstr "SSL-Verbindung gescheitert" @@ -9957,8 +9979,8 @@ msgid "_Basic" msgstr "_Einfach" -msgid "Create this new account on the server" -msgstr "Dieses neue Konto auf dem Server anlegen" +msgid "Create _this new account on the server" +msgstr "Dieses _neue Konto auf dem Server anlegen" msgid "_Advanced" msgstr "_Erweitert" @@ -10064,6 +10086,12 @@ msgid "_Remove" msgstr "_Entfernen" +msgid "Set Custom Icon" +msgstr "Setze benutzerdefiniertes Icon" + +msgid "Remove Custom Icon" +msgstr "Benutzerdefiniertes Icon entfernen" + msgid "Add _Buddy..." msgstr "_Buddy hinzufügen..." @@ -10176,6 +10204,9 @@ msgid "/Tools/_Certificates" msgstr "/Werkzeuge/_Zertifikate" +msgid "/Tools/Smile_y" +msgstr "/Werkzeuge/Smile_y" + msgid "/Tools/Plu_gins" msgstr "/Werkzeuge/Plu_gins" @@ -10521,9 +10552,6 @@ msgid "Change Size" msgstr "Ändere Größe" -msgid "Remove Custom Icon" -msgstr "Benutzerdefiniertes Icon entfernen" - msgid "Show All" msgstr "Alle anzeigen" @@ -10963,6 +10991,9 @@ msgid "Norwegian Nynorsk" msgstr "Norwegisch (Nynorsk)" +msgid "Occitan" +msgstr "Okzitanisch" + msgid "Punjabi" msgstr "Pandschabi" @@ -11338,6 +11369,13 @@ msgid "Color to draw the name of an action message." msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird." +#, fuzzy +msgid "Action Message Name Color for Whispered Message" +msgstr "Farbe des Absendernamens für Aktions-Nachrichten" + +msgid "Whisper Message Name Color" +msgstr "Farbe des Absendernamens für Flüster-Nachrichten" + msgid "Typing notification color" msgstr "Farbe der Tipp-Benachrichtigung" @@ -11408,6 +11446,10 @@ msgid "_Save Image..." msgstr "Bild _speichern..." +#, fuzzy +msgid "_Add Custom Smiley..." +msgstr "Benutzerdefinierten Smiley _hinzufügen..." + msgid "Select Font" msgstr "Schriftart wählen" @@ -11446,9 +11488,19 @@ msgid "Insert Image" msgstr "Bild einfügen" +msgid "" +"This smiley is disabled because a custom smiley exists for this shortcut." +msgstr "" +"Dieser Smiley ist deaktiviert, da ein benutzerdefinierter Smiley für diese " +"Tastenkombination existiert." + msgid "Smile!" msgstr "Lächeln!" +#, fuzzy +msgid "_Manage custom smileys" +msgstr "Benutzerdefinierte Smileys _verwalten" + msgid "This theme has no available smileys." msgstr "Dieses Thema verfügt über keine Smileys." @@ -12164,6 +12216,12 @@ msgid "Play" msgstr "Abspielen" +msgid "_Browse..." +msgstr "Aus_wählen..." + +msgid "_Reset" +msgstr "_Zurücksetzen" + msgid "_Report idle time:" msgstr "Inaktivitätszei_ten anzeigen:" @@ -12342,6 +12400,47 @@ msgid "Status for %s" msgstr "Status für %s" +msgid "Custom Smiley" +msgstr "Benutzerdefinierter Smiley" + +msgid "Duplicate Shortcut" +msgstr "Doppelte Tastenkombination" + +msgid "" +"A custom smiley for the selected shortcut already exists. Please specify a " +"different shortcut." +msgstr "" +"Für diese Tastenkombination existiert bereits ein benutzerdefinierter " +"Smiley. Bitten wählen Sie eine andere Tastenkombination." + +msgid "More Data needed" +msgstr "Weitere Daten benötigt" + +msgid "Please provide a shortcut to associate with the smiley." +msgstr "Bitte geben Sie eine Tastenkombination für den Smiley an." + +msgid "Please select an image for the smiley." +msgstr "Bitte wählen Sie ein Bild für den Smiley." + +msgid "Edit Smiley" +msgstr "Smiley bearbeiten" + +msgid "Add Smiley" +msgstr "Smiley hinzufügen" + +msgid "Smiley _Image" +msgstr "Smiley-_Bild" + +#. Smiley shortcut +msgid "Smiley S_hortcut" +msgstr "_Tastenkombination" + +msgid "Smiley" +msgstr "Smiley" + +msgid "Custom Smiley Manager" +msgstr "Verwaltung für benutzerdefinierte Smileys" + msgid "Waiting for network connection" msgstr "Warte auf Netzwerkverbindung"