Mercurial > pidgin
changeset 26737:8b7d1aed6d59
merge of '8ddbfe5d6f5c2455497d11ccbfff769ed4e9fad4'
and 'ef988478f33650d37a90cb7c58a9ac5af81b525b'
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Tue, 28 Apr 2009 19:08:06 +0000 |
parents | a0e48796defb (diff) 3613804b8f8d (current diff) |
children | 913ce5e88249 |
files | libpurple/media.c libpurple/media.h libpurple/protocols/jabber/jabber.c |
diffstat | 218 files changed, 6215 insertions(+), 2671 deletions(-) [+] |
line wrap: on
line diff
--- a/AUTHORS Tue Apr 28 19:05:59 2009 +0000 +++ b/AUTHORS Tue Apr 28 19:08:06 2009 +0000 @@ -9,6 +9,7 @@ ------------------ Daniel 'datallah' Atallah - Developer +Paul 'darkrain42' Aurich - Developer John 'rekkanoryo' Bailey - Developer Ethan 'Paco-Paco' Blanton - Developer Thomas Butter - Developer @@ -24,6 +25,7 @@ Bartosz Oler - Developer Etan 'deryni' Reisner - Developer Tim 'marv' Ringenbach - Developer +Michael 'Maiku' Ruprecht - Developer, voice and video Elliott 'QuLogic' Sales de Andrade - Developer Luke 'LSchiere' Schierer - Support Megan 'Cae' Schneider - support/QA @@ -35,7 +37,6 @@ Crazy Patch Writers: ------------------- -Paul 'darkrain42' Aurich Marcus 'malu' Lundblad Dennis 'EvilDennisR' Ristuccia Peter 'Fmoo' Ruibal
--- a/COPYRIGHT Tue Apr 28 19:05:59 2009 +0000 +++ b/COPYRIGHT Tue Apr 28 19:08:06 2009 +0000 @@ -19,6 +19,7 @@ Christopher Ayoup Alex Badea John Bailey +Arunan Balasubramaniam R. Tyler Ballance Chris Banal Luca Barbato @@ -29,6 +30,7 @@ Derek Battams Martin Bayard Curtis Beattie +Carlos Bederian Dave Bell Igor Belyi Brian Bernas @@ -332,10 +334,9 @@ Joao Luís Marques Pinto Aleksander Piotrowski Julien Pivotto +Robey Pointer Eric Polino <aluink@gmail.com> Ari Pollak -Robey Pointer -Eric Polino Stephen Pope Nathan Poznick Jory A. Pratt
--- a/ChangeLog Tue Apr 28 19:05:59 2009 +0000 +++ b/ChangeLog Tue Apr 28 19:08:06 2009 +0000 @@ -8,17 +8,34 @@ in a group on the buddy list. * Removed the unmaintained and unneeded toc protocol plugin. * Fixed NTLM authentication on big-endian systems. + * Dragging a buddy onto a chat pops up a chat-invitation dialog. + (Carlos Bederian) + + libpurple: + * Various memory cleanups when unloading libpurple. (Nick Hebner) XMPP: * Add voice & video support with Jingle (XEP-0166, 0167, 0176, & 0177), and voice support with GTalk and GMail. (Mike "Maiku" Ruprecht) * Add support for in-band bytestreams for file transfers (XEP-0047). - * Add support for sending attentions (equivalent to "buzz" and "nudge") - using the command /buzz (XEP-0224). + * Add support for sending and receiving attentions (equivalent to "buzz" + and "nudge") using the command /buzz. (XEP-0224) + * A buddy's local time is displayed in the Get Info dialog if the remote + client supports it. + * The set_chat_topic function can unset the chat topic. + * Fix crash on connection with recent gstreamer0.10-plugins-bad. + * Don't create a new conversation window for incoming messages of + type 'headline'. + * The Ad-Hoc commands associated with our server are now always shown at + login. + * Support showing and reporting idle times in the buddy list. (XEP-0256) + * Support most recent version of User Avatar. (XEP-0084 v1.1) IRC: * Correctly handle WHOIS for users who are joined to a large number of channels. + * Notify the user if a /nick command fails, rather than trying + fallback nicks. Pidgin: * Added -f command line option to tell Pidgin to ignore NetworkManager @@ -31,9 +48,17 @@ the next line. * Created a unified Buddy Pounce notification window for all pounces where "Pop up a notification" is selected, which avoids having a - new dialog box every time a pounce is triggered. (Jorge Villaseñor) + new dialog box every time a pounce is triggered. (Jorge Villaseñor) * The New Account dialog is now broken into three tabs. Proxy configuration has been moved from the Advanced tab to the new tab. + * The nicks of the persons who leave the chatroom are italicized in the + chat's conversation history. The nicks are un-italicized when they + rejoin. + + Finch: + * The hardware cursor is updated correctly. This will be useful + especially for users of braille terminals, screen readers etc. + * Added a TinyURL plugin, which aids copying longer URLs. version 2.5.5 (03/01/2009): libpurple:
--- a/ChangeLog.API Tue Apr 28 19:05:59 2009 +0000 +++ b/ChangeLog.API Tue Apr 28 19:08:06 2009 +0000 @@ -9,6 +9,8 @@ * PURPLE_CONTACT * PURPLE_BUDDY * PURPLE_CHAT + * account-actions-changed (see account-signals.dox) + * purple_buddy_destroy * purple_buddy_get_protocol_data * purple_buddy_set_protocol_data * purple_buddy_get_local_buddy_alias @@ -17,9 +19,13 @@ * purple_blist_set_ui_data * purple_blist_node_get_ui_data * purple_blist_node_set_ui_data + * purple_chat_destroy * purple_connection_get_protocol_data * purple_connection_set_protocol_data + * purple_contact_destroy + * purple_conv_chat_invite_user * purple_global_proxy_set_info + * purple_group_destroy * purple_log_get_activity_score * purple_network_force_online * purple_network_set_stun_server @@ -27,6 +33,7 @@ * purple_network_get_stun_ip * purple_network_get_turn_ip * purple_prpl_get_media_caps + * purple_prpl_got_account_actions * purple_prpl_initiate_media * purple_request_field_get_group * purple_request_field_get_ui_data @@ -72,6 +79,14 @@ * pidgin_sound_is_customized * pidgin_utils_init, pidgin_utils_uninit * pidgin_notify_pounce_add + * PidginBlistTheme, PidginBlistThemeLoader API + * PidginIconTheme, PidginStatusIconTheme, PidginIconThemeLoader + API + * pidgin_stock_id_from_status_primitive + + libgnt: + Added: + * GntProgressBar and functions (Saleem Abdulrasool) perl: Changed:
--- a/configure.ac Tue Apr 28 19:05:59 2009 +0000 +++ b/configure.ac Tue Apr 28 19:08:06 2009 +0000 @@ -706,9 +706,13 @@ PKG_CHECK_MODULES(LIBXML, [libxml-2.0 >= 2.6.0], , [ AC_MSG_RESULT(no) AC_MSG_ERROR([ - You must have libxml2 >= 2.6.0 development headers installed to build. ])]) +PKG_CHECK_EXISTS([libxml-2.0 >= 2.6.18], , [ + AC_MSG_WARN([ +Versions of libxml2 < 2.6.18 may contain bugs that could cause XMPP messages to be discarded. +])]) + AC_SUBST(LIBXML_CFLAGS) AC_SUBST(LIBXML_LIBS) @@ -748,13 +752,33 @@ fi dnl ####################################################################### +dnl # Check for GStreamer Interfaces +dnl ####################################################################### +if test "x$enable_gst" != "xno"; then + AC_ARG_ENABLE(gstreamer-interfaces, + [AC_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer interface support])], + enable_gstinterfaces="$enableval", enable_gstinterfaces="yes") + if test "x$enable_gstinterfaces" != "xno"; then + PKG_CHECK_MODULES(GSTINTERFACES, [gstreamer-interfaces-0.10], [ + AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer interfaces for X overlay support]) + AC_SUBST(GSTINTERFACES_CFLAGS) + AC_SUBST(GSTINTERFACES_LIBS) + ], [ + enable_gstinterfaces="no" + ]) + fi +else + enable_gstinterfaces="no" +fi + +dnl ####################################################################### dnl # Check for Farsight dnl ####################################################################### AC_ARG_ENABLE(farsight, [AC_HELP_STRING([--disable-farsight], [compile without farsight support])], enable_farsight="$enableval", enable_farsight="yes") if test "x$enable_farsight" != "xno"; then - PKG_CHECK_MODULES(FARSIGHT, [farsight2-0.10 >= 0.0.8 gstreamer-0.10 gstreamer-plugins-base-0.10 libxml-2.0], [ + PKG_CHECK_MODULES(FARSIGHT, [farsight2-0.10 >= 0.0.9], [ AC_DEFINE(USE_FARSIGHT, 1, [Use Farsight for voice and video]) AC_SUBST(FARSIGHT_CFLAGS) AC_SUBST(FARSIGHT_LIBS) @@ -764,36 +788,23 @@ fi dnl ####################################################################### -dnl # Check for GStreamer-properties -dnl ####################################################################### -AC_ARG_ENABLE(gstprops, - [AC_HELP_STRING([--disable-gstprops], [compile without gstreamer props])], - enable_gstprops="$enableval", enable_gstprops="yes") -if test "x$enable_gstprops" != "xno"; -then - dnl gstreamer-libs-$GST_MAJORMINOR - dnl gstreamer-gconf-$GST_MAJORMINOR - PKG_CHECK_MODULES(GSTPROPS, [gstreamer-0.10 gstreamer-plugins-base-0.10 libxml-2.0], [ - GSTPROPS_LIBS="$GSTPROPS_LIBS -lgstinterfaces-0.10" - AC_DEFINE(USE_GSTPROPS, 1, [Use GStreamer property probe for finding devices]) - AC_SUBST(GSTPROPS_LIBS) - AC_SUBST(GSTPROPS_CFLAGS) - ], [ - enable_gstprops="no" - ]) -fi - -dnl ####################################################################### dnl # Check for Voice and Video support dnl ####################################################################### AC_ARG_ENABLE(vv, [AC_HELP_STRING([--disable-vv], [compile without voice and video support])], - enable_vv="$enableval", enable_vv="yes") + [enable_vv="$enableval" force_vv=$enableval], [enable_vv="yes" force_vv=no]) if test "x$enable_vv" != "xno"; then - if test "x$enable_farsight" != "xno" -a "x$enable_gstprops" != "xno"; then + if test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farsight" != "xno"; then AC_DEFINE(USE_VV, 1, [Use voice and video]) else enable_vv="no" + if test "x$force_vv" = "xyes"; then + AC_MSG_ERROR([ +Dependencies for voice/video were not met. +Install the necessary gstreamer and farsight packages first. +Or use --disable-vv if you do not need voice/video support. + ]) + fi fi fi @@ -975,7 +986,7 @@ gadu_includes="yes" gadu_libs="yes" ], [ - AC_MSG_RESULT(no) + gadu_includes="no" ]) else if test "$ac_gadu_includes" != "no"; then
--- a/doc/TCL-HOWTO.dox Tue Apr 28 19:05:59 2009 +0000 +++ b/doc/TCL-HOWTO.dox Tue Apr 28 19:08:06 2009 +0000 @@ -173,6 +173,7 @@ purple::connection displayname gc purple::connection handle purple::connection list +purple::connection state @endcode @c purple::connection is a collection of subcommands pertaining to @@ -192,6 +193,9 @@ this list are appropriate as @c gc arguments to the other @c purple::connection subcommands or other commands requiring a gc. + @c state returns the PurpleConnectionState of this account as one of + the strings "connected", "disconnected", or "connecting". + @code purple::conv_send account who text @endcode
--- a/doc/account-signals.dox Tue Apr 28 19:05:59 2009 +0000 +++ b/doc/account-signals.dox Tue Apr 28 19:08:06 2009 +0000 @@ -9,6 +9,7 @@ @signal account-setting-info @signal account-set-info @signal account-status-changed + @signal account-actions-changed @signal account-alias-changed @signal account-authorization-requested @signal account-authorization-denied @@ -97,6 +98,15 @@ @param new The status after change. @endsignaldef + @signaldef account-actions-changed + @signalproto +void (*account_actions_changed)(PurpleAccount *account); + @endsignalproto + @signaldesc + Emitted when the account actions are changed after initial connection. + @param account The account whose actions changed. + @endsignaldef + @signaldef account-alias-changed @signalproto void (*account_alias_changed)(PurpleAccount *account, const char *old);
--- a/doc/finch.1.in Tue Apr 28 19:05:59 2009 +0000 +++ b/doc/finch.1.in Tue Apr 28 19:08:06 2009 +0000 @@ -59,7 +59,7 @@ Display the version information window. .SH GNT Shortcuts -You can use the following shortcuts: +You can use the following shortcuts (see the "\*QWidget Actions\*U" section for a more complete list): .TP .B Alt \+ a Bring up a list of available actions. You can use this list to access the @@ -378,6 +378,8 @@ [GntWidget::binding] .br f11 = context-menu +.br +c-x = context-menu [GntWindow::binding] .br
--- a/doc/pidgin.1.in Tue Apr 28 19:05:59 2009 +0000 +++ b/doc/pidgin.1.in Tue Apr 28 19:08:06 2009 +0000 @@ -602,7 +602,9 @@ .br Daniel 'datallah' Atallah (developer) .br - John 'rekkanoryo' Bailey (developer) + Paul 'darkrain42' Aurich (developer) +.br + John 'rekkanoryo' Bailey (developer and bugmaster) .br Ethan 'Paco-Paco' Blanton (developer) .br @@ -632,6 +634,8 @@ .br Tim 'marv' Ringenbach (developer) <\fImarv_sf@users.sf.net\fR> .br + Michael 'Maiku' Ruprecht (developer, voice and video) +.br Elliott 'QuLogic' Sales de Andrade (developer) .br Luke 'LSchiere' Schierer (support)
--- a/finch/gntaccount.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/gntaccount.c Tue Apr 28 19:08:06 2009 +0000 @@ -669,8 +669,13 @@ account_toggled(GntWidget *widget, void *key, gpointer null) { PurpleAccount *account = key; + gboolean enabled = gnt_tree_get_choice(GNT_TREE(widget), key); - purple_account_set_enabled(account, FINCH_UI, gnt_tree_get_choice(GNT_TREE(widget), key)); + if (enabled) + purple_savedstatus_activate_for_account(purple_savedstatus_get_current(), + account); + + purple_account_set_enabled(account, FINCH_UI, enabled); } static gboolean
--- a/finch/gntblist.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/gntblist.c Tue Apr 28 19:08:06 2009 +0000 @@ -61,7 +61,7 @@ #include <string.h> #define PREF_ROOT "/finch/blist" -#define TYPING_TIMEOUT 4000 +#define TYPING_TIMEOUT_S 4 #define SHOW_EMPTY_GROUP_TIMEOUT 60 @@ -935,7 +935,7 @@ else if (PURPLE_BLIST_NODE_IS_GROUP(node)) return purple_group_get_name((PurpleGroup*)node); - snprintf(text, sizeof(text) - 1, "%s %s", status, name); + g_snprintf(text, sizeof(text) - 1, "%s %s", status, name); return text; } @@ -2016,7 +2016,7 @@ } if (ggblist->typing) - g_source_remove(ggblist->typing); + purple_timeout_remove(ggblist->typing); remove_peripherals(ggblist); if (ggblist->tagged) g_list_free(ggblist->tagged); @@ -2253,7 +2253,7 @@ end: g_free(escnewmessage); if (ggblist->typing) - g_source_remove(ggblist->typing); + purple_timeout_remove(ggblist->typing); ggblist->typing = 0; return FALSE; } @@ -2272,7 +2272,7 @@ /* Move the focus to the entry box */ /* XXX: Make sure the selected status can have a message */ gnt_box_move_focus(GNT_BOX(ggblist->window), 1); - ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL); + ggblist->typing = purple_timeout_add_seconds(TYPING_TIMEOUT_S, (GSourceFunc)remove_typing_cb, NULL); } else if (now->type == STATUS_SAVED_ALL) { @@ -2298,7 +2298,7 @@ return FALSE; if (ggblist->typing) - g_source_remove(ggblist->typing); + purple_timeout_remove(ggblist->typing); ggblist->typing = 0; if (text[0] == '\r' && text[1] == 0) @@ -2308,7 +2308,7 @@ return TRUE; } - ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL); + ggblist->typing = purple_timeout_add_seconds(TYPING_TIMEOUT_S, (GSourceFunc)remove_typing_cb, NULL); return FALSE; } @@ -2642,7 +2642,7 @@ char menuid[128]; FinchBlistManager *manager = iter->data; GntMenuItem *item = gnt_menuitem_new(_(manager->name)); - snprintf(menuid, sizeof(menuid), "grouping-%s", manager->id); + g_snprintf(menuid, sizeof(menuid), "grouping-%s", manager->id); gnt_menuitem_set_id(GNT_MENU_ITEM(item), menuid); gnt_menu_add_item(GNT_MENU(subsub), item); g_object_set_data_full(G_OBJECT(item), "grouping-id", g_strdup(manager->id), g_free); @@ -3123,6 +3123,8 @@ PURPLE_CALLBACK(reconstruct_accounts_menu), NULL); purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_blist_get_handle(), PURPLE_CALLBACK(reconstruct_accounts_menu), NULL); + purple_signal_connect(purple_accounts_get_handle(), "account-actions-changed", finch_blist_get_handle(), + PURPLE_CALLBACK(reconstruct_accounts_menu), NULL); purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", finch_blist_get_handle(), PURPLE_CALLBACK(buddy_status_changed), ggblist); purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", finch_blist_get_handle(),
--- a/finch/gntconv.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/gntconv.c Tue Apr 28 19:08:06 2009 +0000 @@ -559,44 +559,11 @@ } static void -invite_select_cb(FinchConv *fc, PurpleRequestFields *fields) -{ - PurpleConversation *conv = fc->active_conv; - const char *buddy = purple_request_fields_get_string(fields, "screenname"); - const char *message = purple_request_fields_get_string(fields, "message"); - serv_chat_invite(purple_conversation_get_gc(conv), - purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), - message, buddy); - -} - -static void invite_cb(GntMenuItem *item, gpointer ggconv) { - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - fields = purple_request_fields_new(); - - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE); - purple_request_field_set_type_hint(field, "screenname"); - purple_request_field_set_required(field, TRUE); - purple_request_field_group_add_field(group, field); - field = purple_request_field_string_new("message", _("Invite message"), NULL, FALSE); - purple_request_field_group_add_field(group, field); - purple_request_fields(finch_conv_get_handle(), _("Invite"), - NULL, - _("Please enter the name of the user " - "you wish to invite,\nalong with an optional invite message."), - fields, - _("OK"), G_CALLBACK(invite_select_cb), - _("Cancel"), NULL, - NULL, NULL, NULL, - ggconv); + FinchConv *fc = ggconv; + PurpleConversation *conv = fc->active_conv; + purple_conv_chat_invite_user(PURPLE_CONV_CHAT(conv), NULL, NULL, TRUE); } static void
--- a/finch/gntmedia.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/gntmedia.c Tue Apr 28 19:08:06 2009 +0000 @@ -37,11 +37,11 @@ #include "cmds.h" #include "conversation.h" #include "debug.h" -#include "media-gst.h" #include "mediamanager.h" /* An incredibly large part of the following is from gtkmedia.c */ #ifdef USE_VV +#include "media-gst.h" #undef hangup @@ -254,16 +254,15 @@ } else if (state == PURPLE_MEDIA_STATE_NEW && sid != NULL && name != NULL && purple_media_is_initiator(media, sid, name) == FALSE) { - PurpleConnection *pc; + PurpleAccount *account; PurpleBuddy *buddy; const gchar *alias; PurpleMediaSessionType type = purple_media_get_session_type(media, sid); gchar *message = NULL; - pc = purple_media_get_connection(gntmedia->priv->media); - buddy = purple_find_buddy( - purple_connection_get_account(pc), name); + account = purple_media_get_account(gntmedia->priv->media); + buddy = purple_find_buddy(account, name); alias = buddy ? purple_buddy_get_contact_alias(buddy) : name; if (type & PURPLE_MEDIA_AUDIO) { @@ -272,7 +271,7 @@ alias); } else { message = g_strdup_printf( - _("%s is trying to start an unsuppoted media session type with you."), + _("%s is trying to start an unsupported media session type with you."), alias); } finch_media_emit_message(gntmedia, message); @@ -386,13 +385,12 @@ static gboolean finch_new_media(PurpleMediaManager *manager, PurpleMedia *media, - PurpleConnection *gc, gchar *name, gpointer null) + PurpleAccount *account, gchar *name, gpointer null) { GntWidget *gntmedia; PurpleConversation *conv; - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, - purple_connection_get_account(gc), name); + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name); gntmedia = finch_media_new(media); g_signal_connect(G_OBJECT(gntmedia), "message", G_CALLBACK(gntmedia_message_cb), conv);
--- a/finch/gntnotify.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/gntnotify.c Tue Apr 28 19:08:06 2009 +0000 @@ -263,7 +263,7 @@ userinfo_hash(PurpleAccount *account, const char *who) { char key[256]; - snprintf(key, sizeof(key), "%s - %s", purple_account_get_username(account), purple_normalize(account, who)); + g_snprintf(key, sizeof(key), "%s - %s", purple_account_get_username(account), purple_normalize(account, who)); return g_utf8_strup(key, -1); }
--- a/finch/libgnt/Makefile.am Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/Makefile.am Tue Apr 28 19:08:06 2009 +0000 @@ -28,6 +28,7 @@ gntmenu.c \ gntmenuitem.c \ gntmenuitemcheck.c \ + gntprogressbar.c \ gntslider.c \ gntstyle.c \ gnttextview.c \ @@ -56,6 +57,7 @@ gntmenu.h \ gntmenuitem.h \ gntmenuitemcheck.h \ + gntprogressbar.h \ gntslider.h \ gntstyle.h \ gnttextview.h \
--- a/finch/libgnt/gnt.h Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gnt.h Tue Apr 28 19:08:06 2009 +0000 @@ -48,6 +48,10 @@ #define G_PARAM_STATIC_BLURB G_PARAM_PRIVATE #endif +#if !GLIB_CHECK_VERSION(2,14,0) + #define g_timeout_add_seconds(time, callback, data) g_timeout_add(time * 1000, callback, data) +#endif + /** * Initialize GNT. */
--- a/finch/libgnt/gntbox.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gntbox.c Tue Apr 28 19:08:06 2009 +0000 @@ -78,13 +78,11 @@ g_list_foreach(box->list, (GFunc)gnt_widget_draw, NULL); - gnt_box_sync_children(box); - if (box->title && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) { int pos, right; char *title = g_strdup(box->title); - + get_title_thingies(box, title, &pos, &right); if (gnt_widget_has_focus(widget)) @@ -96,8 +94,8 @@ mvwaddch(widget->window, 0, right, ACS_LTEE | gnt_color_pair(GNT_COLOR_NORMAL)); g_free(title); } - - GNTDEBUG; + + gnt_box_sync_children(box); } static void @@ -723,6 +721,9 @@ if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) pos = 0; + if (!box->active) + find_focusable_widget(box); + for (iter = box->list; iter; iter = iter->next) { GntWidget *w = GNT_WIDGET(iter->data); @@ -764,6 +765,9 @@ copywin(w->window, widget->window, 0, 0, y, x, y + height - 1, x + width - 1, FALSE); gnt_widget_set_position(w, x + widget->priv.x, y + widget->priv.y); + if (w == box->active) { + wmove(widget->window, y + getcury(w->window), x + getcurx(w->window)); + } } }
--- a/finch/libgnt/gntcheckbox.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gntcheckbox.c Tue Apr 28 19:08:06 2009 +0000 @@ -42,7 +42,7 @@ type = GNT_COLOR_HIGHLIGHT; else type = GNT_COLOR_NORMAL; - + wbkgdset(widget->window, '\0' | gnt_color_pair(type)); text = g_strdup_printf("[%c]", cb->checked ? 'X' : ' '); @@ -51,7 +51,8 @@ wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_NORMAL)); mvwaddstr(widget->window, 0, 4, GNT_BUTTON(cb)->priv->text); - + wmove(widget->window, 0, 1); + GNTDEBUG; }
--- a/finch/libgnt/gntcombobox.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gntcombobox.c Tue Apr 28 19:08:06 2009 +0000 @@ -73,7 +73,7 @@ char *text = NULL, *s; GntColorType type; int len; - + if (box->dropdown && box->selected) text = gnt_tree_get_selection_text(GNT_TREE(box->dropdown)); @@ -94,6 +94,7 @@ whline(widget->window, ' ' | gnt_color_pair(type), widget->priv.width - 4 - len); mvwaddch(widget->window, 1, widget->priv.width - 3, ACS_VLINE | gnt_color_pair(GNT_COLOR_NORMAL)); mvwaddch(widget->window, 1, widget->priv.width - 2, ACS_DARROW | gnt_color_pair(GNT_COLOR_NORMAL)); + wmove(widget->window, 1, 1); g_free(text); GNTDEBUG;
--- a/finch/libgnt/gntentry.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gntentry.c Tue Apr 28 19:08:06 2009 +0000 @@ -271,6 +271,7 @@ GntEntry *entry = GNT_ENTRY(widget); int stop; gboolean focus; + int curpos; if ((focus = gnt_widget_has_focus(widget))) wbkgdset(widget->window, '\0' | gnt_color_pair(GNT_COLOR_TEXT_NORMAL)); @@ -289,9 +290,10 @@ if (stop < widget->priv.width) mvwhline(widget->window, 0, stop, ENTRY_CHAR, widget->priv.width - stop); + curpos = gnt_util_onscreen_width(entry->scroll, entry->cursor); if (focus) - mvwchgat(widget->window, 0, gnt_util_onscreen_width(entry->scroll, entry->cursor), - 1, A_REVERSE, GNT_COLOR_TEXT_NORMAL, NULL); + mvwchgat(widget->window, 0, curpos, 1, A_REVERSE, GNT_COLOR_TEXT_NORMAL, NULL); + wmove(widget->window, 0, curpos); GNTDEBUG; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/libgnt/gntprogressbar.c Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,253 @@ +/** + * GNT - The GLib Ncurses Toolkit + * + * GNT 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 library 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 "gntprogressbar.h" +#include "gntutils.h" + +#include <string.h> + +typedef struct _GntProgressBarPrivate +{ + gdouble fraction; + gboolean show_value; + GntProgressBarOrientation orientation; +} GntProgressBarPrivate; + +struct _GntProgressBar +{ + GntWidget parent; +#if !GLIB_CHECK_VERSION(2,4,0) + GntProgressBarPrivate priv; +#endif +}; + +#if GLIB_CHECK_VERSION(2,4,0) +#define GNT_PROGRESS_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBarPrivate)) +#else +#define GNT_PROGRESS_BAR_GET_PRIVATE(o) &(GNT_PROGRESS_BAR(o)->priv) +#endif + +static GntWidgetClass *parent_class = NULL; + + +static void +gnt_progress_bar_draw (GntWidget *widget) +{ + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (GNT_PROGRESS_BAR (widget)); + gchar progress[8]; + gint start, end, i, pos; + int color; + + g_snprintf (progress, sizeof (progress), "%.1f%%", priv->fraction * 100); + color = gnt_color_pair(GNT_COLOR_NORMAL); + + switch (priv->orientation) { + case GNT_PROGRESS_LEFT_TO_RIGHT: + case GNT_PROGRESS_RIGHT_TO_LEFT: + start = (priv->orientation == GNT_PROGRESS_LEFT_TO_RIGHT ? 0 : (1.0 - priv->fraction) * widget->priv.width); + end = (priv->orientation == GNT_PROGRESS_LEFT_TO_RIGHT ? widget->priv.width * priv->fraction : widget->priv.width); + + /* background */ + for (i = 0; i < widget->priv.height; i++) + mvwhline (widget->window, i, 0, ' ' | color, widget->priv.width); + + /* foreground */ + for (i = 0; i < widget->priv.height; i++) + mvwhline (widget->window, i, start, ACS_CKBOARD | color | A_REVERSE, end); + + /* text */ + if (priv->show_value) { + pos = widget->priv.width / 2 - strlen (progress) / 2; + for (i = 0; i < progress[i]; i++, pos++) { + wattrset (widget->window, color | ((pos < start || pos > end) ? A_NORMAL : A_REVERSE)); + mvwprintw (widget->window, widget->priv.height / 2, pos, "%c", progress[i]); + } + wattrset (widget->window, color); + } + + break; + case GNT_PROGRESS_TOP_TO_BOTTOM: + case GNT_PROGRESS_BOTTOM_TO_TOP: + start = (priv->orientation == GNT_PROGRESS_TOP_TO_BOTTOM ? 0 : (1.0 - priv->fraction) * widget->priv.height); + end = (priv->orientation == GNT_PROGRESS_TOP_TO_BOTTOM ? widget->priv.height * priv->fraction : widget->priv.height); + + /* background */ + for (i = 0; i < widget->priv.width; i++) + mvwvline (widget->window, 0, i, ' ' | color, widget->priv.height); + + /* foreground */ + for (i = 0; i < widget->priv.width; i++) + mvwvline (widget->window, start, i, ACS_CKBOARD | color | A_REVERSE, end); + + /* text */ + if (priv->show_value) { + pos = widget->priv.height / 2 - strlen (progress) / 2; + for (i = 0; i < progress[i]; i++, pos++) { + wattrset (widget->window, color | ((pos < start || pos > end) ? A_NORMAL : A_REVERSE)); + mvwprintw (widget->window, pos, widget->priv.width / 2, "%c\n", progress[i]); + } + wattrset (widget->window, color); + } + + break; + default: + g_assert_not_reached (); + } +} + +static void +gnt_progress_bar_size_request (GntWidget *widget) +{ + gnt_widget_set_size (widget, widget->priv.minw, widget->priv.minh); +} + +static void +gnt_progress_bar_class_init (gpointer klass, gpointer class_data) +{ + GObjectClass *g_class = G_OBJECT_CLASS (klass); + + parent_class = GNT_WIDGET_CLASS (klass); + +#if GLIB_CHECK_VERSION(2,4,0) + g_type_class_add_private (g_class, sizeof (GntProgressBarPrivate)); +#endif + + parent_class->draw = gnt_progress_bar_draw; + parent_class->size_request = gnt_progress_bar_size_request; +} + +static void +gnt_progress_bar_init (GTypeInstance *instance, gpointer g_class) +{ + GntWidget *widget = GNT_WIDGET (instance); + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (GNT_PROGRESS_BAR (widget)); + + gnt_widget_set_take_focus (widget, FALSE); + GNT_WIDGET_SET_FLAGS (widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | GNT_WIDGET_GROW_X); + + widget->priv.minw = 8; + widget->priv.minh = 1; + + priv->show_value = TRUE; +} + +GType +gnt_progress_bar_get_type (void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof (GntProgressBarClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + gnt_progress_bar_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GntProgressBar), + 0, /* n_preallocs */ + gnt_progress_bar_init, /* instance_init */ + NULL /* value_table */ + }; + + type = g_type_register_static (GNT_TYPE_WIDGET, "GntProgressBar", &info, 0); + } + + return type; +} + +GntWidget * +gnt_progress_bar_new (void) +{ + GntWidget *widget = g_object_new (GNT_TYPE_PROGRESS_BAR, NULL); + return widget; +} + +void +gnt_progress_bar_set_fraction (GntProgressBar *pbar, gdouble fraction) +{ + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (pbar); + + if (fraction > 1.0) + priv->fraction = 1.0; + else if (fraction < 0.0) + priv->fraction = 0.0; + else + priv->fraction = fraction; + + if ((GNT_WIDGET_FLAGS(pbar) & GNT_WIDGET_MAPPED)) + gnt_widget_draw(GNT_WIDGET(pbar)); +} + +void +gnt_progress_bar_set_orientation (GntProgressBar *pbar, + GntProgressBarOrientation orientation) +{ + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (pbar); + GntWidget *widget = GNT_WIDGET(pbar); + + priv->orientation = orientation; + if (orientation == GNT_PROGRESS_LEFT_TO_RIGHT || + orientation == GNT_PROGRESS_RIGHT_TO_LEFT) { + GNT_WIDGET_SET_FLAGS(pbar, GNT_WIDGET_GROW_X); + GNT_WIDGET_UNSET_FLAGS(pbar, GNT_WIDGET_GROW_Y); + widget->priv.minw = 8; + widget->priv.minh = 1; + } else { + GNT_WIDGET_UNSET_FLAGS(pbar, GNT_WIDGET_GROW_X); + GNT_WIDGET_SET_FLAGS(pbar, GNT_WIDGET_GROW_Y); + widget->priv.minw = 1; + widget->priv.minh = 8; + } + + if ((GNT_WIDGET_FLAGS(pbar) & GNT_WIDGET_MAPPED)) + gnt_widget_draw(GNT_WIDGET(pbar)); +} + +void +gnt_progress_bar_set_show_progress (GntProgressBar *pbar, gboolean show) +{ + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (pbar); + priv->show_value = show; +} + +gdouble +gnt_progress_bar_get_fraction (GntProgressBar *pbar) +{ + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (pbar); + return priv->fraction; +} + +GntProgressBarOrientation +gnt_progress_bar_get_orientation (GntProgressBar *pbar) +{ + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (pbar); + return priv->orientation; +} + +gboolean +gnt_progress_bar_get_show_progress (GntProgressBar *pbar) +{ + GntProgressBarPrivate *priv = GNT_PROGRESS_BAR_GET_PRIVATE (pbar); + return priv->show_value; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/libgnt/gntprogressbar.h Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,132 @@ +/** + * @file gntprogressbar.h Progress Bar API + * @ingroup gnt + */ +/* + * GNT - The GLib Ncurses Toolkit + * + * GNT 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 library 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 GNT_PROGRESS_BAR_H +#define GNT_PROGRESS_BAR_H + +#include "gnt.h" +#include "gntwidget.h" + +#define GNT_TYPE_PROGRESS_BAR (gnt_progress_bar_get_type ()) +#define GNT_PROGRESS_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBar)) +#define GNT_PROGRESS_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNT_TYPE_PROGRESS_BAR, GntProgressBarClass)) +#define GNT_IS_PROGRESS_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNT_TYPE_PROGRESS_BAR)) +#define GNT_IS_PROGRESS_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GNT_TYPE_PROGRESS_BAR)) +#define GNT_PROGRESS_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBarClass)) + +typedef enum _GntProgressBarOrientation +{ + GNT_PROGRESS_LEFT_TO_RIGHT, + GNT_PROGRESS_RIGHT_TO_LEFT, + GNT_PROGRESS_BOTTOM_TO_TOP, + GNT_PROGRESS_TOP_TO_BOTTOM, +} GntProgressBarOrientation; + +typedef struct _GntProgressBar GntProgressBar; + +typedef struct _GntProgressBarClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +} GntProgressBarClass; + +G_BEGIN_DECLS + +/** + * Get the GType for GntProgressBar + * @return The GType for GntProrgressBar + **/ +GType +gnt_progress_bar_get_type (void); + +/** + * Create a new GntProgressBar + * @return The new GntProgressBar + **/ +GntWidget * +gnt_progress_bar_new (void); + +/** + * Set the progress for a progress bar + * + * @param pbar The GntProgressBar + * @param fraction The value between 0 and 1 to display + **/ +void +gnt_progress_bar_set_fraction (GntProgressBar *pbar, gdouble fraction); + +/** + * Set the orientation for a progress bar + * + * @param pbar The GntProgressBar + * @param orientation The orientation to use + **/ +void +gnt_progress_bar_set_orientation (GntProgressBar *pbar, GntProgressBarOrientation orientation); + +/** + * Controls whether the progress value is shown + * + * @param pbar The GntProgressBar + * @param show A boolean indicating if the value is shown + **/ +void +gnt_progress_bar_set_show_progress (GntProgressBar *pbar, gboolean show); + +/** + * Get the progress that is displayed + * + * @param pbar The GntProgressBar + * @return The progress displayed as a value between 0 and 1 + **/ +gdouble +gnt_progress_bar_get_fraction (GntProgressBar *pbar); + +/** + * Get the orientation for the progress bar + * + * @param pbar The GntProgressBar + * @return The current orientation of the progress bar + **/ +GntProgressBarOrientation +gnt_progress_bar_get_orientation (GntProgressBar *pbar); + +/** + * Get a boolean describing if the progress value is shown + * + * @param pbar The GntProgressBar + * @return A boolean @c true if the progress value is shown, @c false otherwise. + **/ +gboolean +gnt_progress_bar_get_show_progress (GntProgressBar *pbar); + +G_END_DECLS + +#endif /* GNT_PROGRESS_BAR_H */
--- a/finch/libgnt/gnttextview.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gnttextview.c Tue Apr 28 19:08:06 2009 +0000 @@ -177,7 +177,7 @@ gnt_color_pair(GNT_COLOR_HIGHLIGHT_D)); } - GNTDEBUG; + wmove(widget->window, 0, 0); } static void @@ -799,6 +799,7 @@ break; } } + gnt_widget_draw(GNT_WIDGET(view)); return count; }
--- a/finch/libgnt/gnttree.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gnttree.c Tue Apr 28 19:08:06 2009 +0000 @@ -28,7 +28,7 @@ #include <string.h> #include <ctype.h> -#define SEARCH_TIMEOUT 4000 /* 4 secs */ +#define SEARCH_TIMEOUT_S 4 /* 4 secs */ #define SEARCHING(tree) (tree->priv->search && tree->priv->search->len > 0) #define COLUMN_INVISIBLE(tree, index) (tree->columns[index].flags & GNT_TREE_COLUMN_INVISIBLE) @@ -420,6 +420,7 @@ GntTreeRow *row; int pos, up, down = 0; int rows, scrcol; + int current = 0; if (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_MAPPED)) return; @@ -431,7 +432,7 @@ if (tree->top == NULL) tree->top = tree->root; - if (tree->current == NULL) { + if (tree->current == NULL && tree->root != NULL) { tree->current = tree->root; tree_selection_changed(tree, NULL, tree->current); } @@ -490,6 +491,13 @@ tree->top = get_next(tree->top); row = tree->top; scrcol = widget->priv.width - 1 - 2 * pos; /* exclude the borders and the scrollbar */ + + if (tree->current && !row_matches_search(tree->current)) { + GntTreeRow *old = tree->current; + tree->current = tree->top; + tree_selection_changed(tree, old, tree->current); + } + for (i = start + pos; row && i < widget->priv.height - pos; i++, row = get_next(row)) { @@ -518,6 +526,7 @@ if (row == tree->current) { + current = i; attr |= A_BOLD; if (gnt_widget_has_focus(widget)) attr |= gnt_color_pair(GNT_COLOR_HIGHLIGHT); @@ -606,6 +615,7 @@ mvwaddnstr(widget->window, widget->priv.height - pos - 1, pos, tree->priv->search->str, str - tree->priv->search->str); } + wmove(widget->window, current, pos); gnt_widget_queue_update(widget); } @@ -818,7 +828,7 @@ gnt_bindable_perform_action_key(GNT_BINDABLE(tree), text); } g_source_remove(tree->priv->search_timeout); - tree->priv->search_timeout = g_timeout_add(SEARCH_TIMEOUT, search_timeout, tree); + tree->priv->search_timeout = g_timeout_add_seconds(SEARCH_TIMEOUT_S, search_timeout, tree); return TRUE; } else if (text[0] == ' ' && text[1] == 0) { /* Space pressed */ @@ -930,7 +940,7 @@ return FALSE; GNT_WIDGET_SET_FLAGS(GNT_WIDGET(tree), GNT_WIDGET_DISABLE_ACTIONS); tree->priv->search = g_string_new(NULL); - tree->priv->search_timeout = g_timeout_add(SEARCH_TIMEOUT, search_timeout, tree); + tree->priv->search_timeout = g_timeout_add_seconds(SEARCH_TIMEOUT_S, search_timeout, tree); return TRUE; }
--- a/finch/libgnt/gntwm.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/libgnt/gntwm.c Tue Apr 28 19:08:06 2009 +0000 @@ -135,6 +135,17 @@ src = widget->window; dst = node->window; copywin(src, dst, node->scroll, 0, 0, 0, getmaxy(dst) - 1, getmaxx(dst) - 1, 0); + + /* Update the hardware cursor */ + if (GNT_IS_WINDOW(widget) || GNT_IS_BOX(widget)) { + GntWidget *active = GNT_BOX(widget)->active; + if (active) { + int curx = active->priv.x + getcurx(active->window); + int cury = active->priv.y + getcury(active->window); + if (wmove(node->window, cury - widget->priv.y, curx - widget->priv.x) != OK) + wmove(node->window, 0, 0); + } + } } /** @@ -397,7 +408,7 @@ wm->positions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE)) read_window_positions(wm); - g_timeout_add(IDLE_CHECK_INTERVAL * 1000, check_idle, NULL); + g_timeout_add_seconds(IDLE_CHECK_INTERVAL, check_idle, NULL); time(&last_active_time); gnt_wm_switch_workspace(wm, 0); } @@ -1101,8 +1112,8 @@ g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, GINT_TO_POINTER(TRUE)); g_signal_emit(wm, signals[SIG_TERMINAL_REFRESH], 0); + gnt_ws_draw_taskbar(wm->cws, TRUE); update_screen(wm); - gnt_ws_draw_taskbar(wm->cws, TRUE); curs_set(0); /* endwin resets the cursor to normal */ return TRUE; @@ -1872,8 +1883,8 @@ } } + gnt_ws_draw_taskbar(wm->cws, FALSE); update_screen(wm); - gnt_ws_draw_taskbar(wm->cws, FALSE); } void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget) @@ -1885,6 +1896,7 @@ { GntWS *s; int pos; + gboolean transient = !!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT); s = gnt_wm_widget_find_workspace(wm, widget); @@ -1908,10 +1920,12 @@ if (s->ordered && wm->cws == s) gnt_wm_raise_window(wm, s->ordered->data); } + } else if (transient && wm->cws && wm->cws->ordered) { + gnt_wm_update_window(wm, wm->cws->ordered->data); } + gnt_ws_draw_taskbar(wm->cws, FALSE); update_screen(wm); - gnt_ws_draw_taskbar(wm->cws, FALSE); } time_t gnt_wm_get_idle_time() @@ -2119,7 +2133,7 @@ if (write_timeout) { g_source_remove(write_timeout); } - write_timeout = g_timeout_add(10000, write_already, wm); + write_timeout = g_timeout_add_seconds(10, write_already, wm); } void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y) @@ -2181,8 +2195,8 @@ GntNode *nd = g_hash_table_lookup(wm->nodes, wm->_list.window); top_panel(nd->panel); } + gnt_ws_draw_taskbar(wm->cws, FALSE); update_screen(wm); - gnt_ws_draw_taskbar(wm->cws, FALSE); } void gnt_wm_update_window(GntWM *wm, GntWidget *widget) @@ -2207,8 +2221,8 @@ if (ws == wm->cws || GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) { gnt_wm_copy_win(widget, node); + gnt_ws_draw_taskbar(wm->cws, FALSE); update_screen(wm); - gnt_ws_draw_taskbar(wm->cws, FALSE); } else if (ws && ws != wm->cws && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_URGENT)) { if (!act || (act && !g_list_find(act, ws))) act = g_list_prepend(act, ws);
--- a/finch/plugins/Makefile.am Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/plugins/Makefile.am Tue Apr 28 19:08:06 2009 +0000 @@ -2,6 +2,7 @@ gntgf_la_LDFLAGS = -module -avoid-version gnthistory_la_LDFLAGS = -module -avoid-version gntlastlog_la_LDFLAGS = -module -avoid-version +gnttinyurl_la_LDFLAGS = -module -avoid-version grouping_la_LDFLAGS = -module -avoid-version if PLUGINS @@ -11,6 +12,7 @@ gntgf.la \ gnthistory.la \ gntlastlog.la \ + gnttinyurl.la \ grouping.la plugindir = $(libdir)/finch @@ -19,6 +21,7 @@ gntgf_la_SOURCES = gntgf.c gnthistory_la_SOURCES = gnthistory.c gntlastlog_la_SOURCES = lastlog.c +gnttinyurl_la_SOURCES = gnttinyurl.c grouping_la_SOURCES = grouping.c gntclipboard_la_CFLAGS = $(X11_CFLAGS) @@ -28,6 +31,7 @@ gntgf_la_LIBADD = $(GLIB_LIBS) $(X11_LIBS) $(top_builddir)/finch/libgnt/libgnt.la gnthistory_la_LIBADD = $(GLIB_LIBS) gntlastlog_la_LIBADD = $(GLIB_LIBS) +gnttinyurl_la_LIBADD = $(GLIB_LIBS) grouping_la_LIBADD = $(GLIB_LIBS) $(top_builddir)/finch/libgnt/libgnt.la endif # PLUGINS
--- a/finch/plugins/gntgf.c Tue Apr 28 19:05:59 2009 +0000 +++ b/finch/plugins/gntgf.c Tue Apr 28 19:08:06 2009 +0000 @@ -47,6 +47,7 @@ #include <blist.h> #include <conversation.h> #include <debug.h> +#include <eventloop.h> #include <util.h> #include <gnt.h> @@ -75,7 +76,7 @@ { toasters = g_list_remove(toasters, toast); gnt_widget_destroy(toast->window); - g_source_remove(toast->timer); + purple_timeout_remove(toast->timer); g_free(toast); } @@ -220,7 +221,7 @@ } gnt_widget_draw(window); - toast->timer = g_timeout_add(4000, (GSourceFunc)remove_toaster, toast); + toast->timer = purple_timeout_add_seconds(4, (GSourceFunc)remove_toaster, toast); toasters = g_list_prepend(toasters, toast); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/plugins/gnttinyurl.c Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,415 @@ +/** + * @file gnttinyurl.c + * + * Copyright (C) 2009 Richard Nelson <wabz@whatsbeef.net> + * + * 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 <glib.h> + +#define PLUGIN_STATIC_NAME TinyURL +#define PREFS_BASE "/plugins/gnt/tinyurl" +#define PREF_LENGTH PREFS_BASE "/length" +#define PREF_URL PREFS_BASE "/url" + + +#include <conversation.h> +#include <signals.h> + +#include <glib.h> + +#include <plugin.h> +#include <version.h> +#include <debug.h> +#include <notify.h> + +#include <gntconv.h> + +#include <gntplugin.h> +#include <gnttextview.h> + +static int tag_num = 0; + +typedef struct +{ + PurpleConversation *conv; + gchar *tag; + int num; +} CbInfo; + +/* 3 functions from util.c */ +static gboolean +badchar(char c) +{ + switch (c) { + case ' ': + case ',': + case '\0': + case '\n': + case '\r': + case '<': + case '>': + case '"': + case '\'': + return TRUE; + default: + return FALSE; + } +} + +static gboolean +badentity(const char *c) +{ + if (!g_ascii_strncasecmp(c, "<", 4) || + !g_ascii_strncasecmp(c, ">", 4) || + !g_ascii_strncasecmp(c, """, 6)) { + return TRUE; + } + return FALSE; +} + +static GList *extract_urls(char *text) { + const char *t, *c, *q = NULL; + char *url_buf; + GList *ret = NULL; + gboolean inside_html = FALSE; + int inside_paren = 0; + c = text; + while (*c) { + if (*c == '(' && !inside_html) { + inside_paren++; + c++; + } + if (inside_html) { + if (*c == '>') { + inside_html = FALSE; + } else if (!q && (*c == '\"' || *c == '\'')) { + q = c; + } else if(q) { + if(*c == *q) + q = NULL; + } + } else if (*c == '<') { + inside_html = TRUE; + if (!g_ascii_strncasecmp(c, "<A", 2)) { + while (1) { + if (*c == '>') { + inside_html = FALSE; + break; + } + c++; + if (!(*c)) + break; + } + } + } else if ((*c=='h') && (!g_ascii_strncasecmp(c, "http://", 7) || + (!g_ascii_strncasecmp(c, "https://", 8)))) { + t = c; + while (1) { + if (badchar(*t) || badentity(t)) { + + if ((!g_ascii_strncasecmp(c, "http://", 7) && (t - c == 7)) || + (!g_ascii_strncasecmp(c, "https://", 8) && (t - c == 8))) { + break; + } + + if (*(t) == ',' && (*(t + 1) != ' ')) { + t++; + continue; + } + + if (*(t - 1) == '.') + t--; + if ((*(t - 1) == ')' && (inside_paren > 0))) { + t--; + } + + url_buf = g_strndup(c, t - c); + if (!g_list_find_custom(ret, url_buf, (GCompareFunc)strcmp)) { + purple_debug_info("TinyURL", "Added URL %s\n", url_buf); + ret = g_list_append(ret, g_strdup(url_buf)); + } + c = t; + break; + } + t++; + + } + } else if (!g_ascii_strncasecmp(c, "www.", 4) && (c == text || badchar(c[-1]) || badentity(c-1))) { + if (c[4] != '.') { + t = c; + while (1) { + if (badchar(*t) || badentity(t)) { + if (t - c == 4) { + break; + } + + if (*(t) == ',' && (*(t + 1) != ' ')) { + t++; + continue; + } + + if (*(t - 1) == '.') + t--; + if ((*(t - 1) == ')' && (inside_paren > 0))) { + t--; + } + url_buf = g_strndup(c, t - c); + if (!g_list_find_custom(ret, url_buf, (GCompareFunc)strcmp)) { + purple_debug_info("TinyURL", "Added URL %s\n", url_buf); + ret = g_list_append(ret, url_buf); + } + c = t; + break; + } + t++; + } + } + } + if (*c == ')' && !inside_html) { + inside_paren--; + c++; + } + if (*c == 0) + break; + c++; + } + return ret; +} + +static void url_fetched(PurpleUtilFetchUrlData *url_data, gpointer cb_data, + const gchar *url_text, gsize len, const gchar *error_message) +{ + CbInfo *data = (CbInfo *)cb_data; + PurpleConversation *conv = data->conv; + GList *convs = purple_get_conversations(); + /* ensure the conversation still exists */ + for (; convs; convs = convs->next) { + if ((PurpleConversation *)(convs->data) == conv) { + FinchConv *fconv = FINCH_CONV(conv); + gchar *str = g_strdup_printf("[%d] %s", data->num, url_text); + GntTextView *tv = GNT_TEXT_VIEW(fconv->tv); + gnt_text_view_tag_change(tv, data->tag, str, FALSE); + g_free(str); + g_free(data->tag); + return; + } + } + g_free(data->tag); + purple_debug_info("TinyURL", "Conversation no longer exists... :(\n"); +} + +static void free_urls(gpointer data, gpointer null) +{ + g_free(data); +} + +static gboolean receiving_msg(PurpleAccount *account, char **sender, char **message, + PurpleConversation *conv, PurpleMessageFlags *flags) { + GString *t; + GList *iter, *urls; + int c = 0; + + if (!(*flags & PURPLE_MESSAGE_RECV) || *flags & PURPLE_MESSAGE_INVISIBLE) + return FALSE; + + t = g_string_new(*message); + urls = purple_conversation_get_data(conv, "TinyURLs"); + if (urls != NULL) /* message was cancelled somewhere? Reset. */ + g_list_foreach(urls, free_urls, NULL); + g_list_free(urls); + urls = extract_urls(t->str); + g_free(*message); + for (iter = urls; iter; iter = iter->next) { + if (g_utf8_strlen((char *)iter->data, -1) >= purple_prefs_get_int(PREF_LENGTH)) { + int pos, x = 0; + gchar *j, *s, *str, *orig; + glong len = g_utf8_strlen(iter->data, -1); + s = g_strdup(t->str); + orig = s; + str = g_strdup_printf("[%d]", ++c); + while ((j = strstr(s, iter->data))) { /* replace all occurrences */ + pos = j - orig + (x++ * 3); + s = j + len; + t = g_string_insert(t, pos + len, str); + if (*s == '\0') break; + } + g_free(orig); + g_free(str); + continue; + } else { + if (iter->prev) { + iter = iter->prev; + g_free(iter->next->data); + urls = g_list_delete_link(urls, iter->next); + } else { + g_free(iter->data); + g_list_free(urls); + urls = NULL; + } + } + } + *message = t->str; + g_string_free(t, FALSE); + if (conv == NULL) + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, *sender); + purple_conversation_set_data(conv, "TinyURLs", urls); + return FALSE; +} + +static void received_msg(PurpleAccount *account, char *sender, char *message, + PurpleConversation *conv, PurpleMessageFlags flags) { + int c; + GList *urls, *iter; + FinchConv *fconv = FINCH_CONV(conv); + GntTextView *tv = GNT_TEXT_VIEW(fconv->tv); + + urls = purple_conversation_get_data(conv, "TinyURLs"); + if (!(flags & PURPLE_MESSAGE_RECV) || urls == NULL) + return; + + for (iter = urls, c = 0; iter; iter = iter->next) { + int i; + CbInfo *cbdata; + gchar *url, *str, *tmp; + cbdata = g_new(CbInfo, 1); + cbdata->num = ++c; + cbdata->tag = g_strdup_printf("%s%d", "tiny_", tag_num++); + cbdata->conv = conv; + tmp = purple_unescape_html((char *)iter->data); + if (g_ascii_strncasecmp(tmp, "http://", 7) && g_ascii_strncasecmp(tmp, "https://", 8)) { + url = g_strdup_printf("%shttp%%3A%%2F%%2F%s", purple_prefs_get_string(PREF_URL), purple_url_encode(tmp)); + } else { + url = g_strdup_printf("%s%s", purple_prefs_get_string(PREF_URL), purple_url_encode(tmp)); + } + g_free(tmp); + purple_util_fetch_url(url, TRUE, "finch", FALSE, url_fetched, cbdata); + i = gnt_text_view_get_lines_below(tv); + str = g_strdup_printf(_("\nFetching TinyURL...")); + gnt_text_view_append_text_with_tag((tv), str, GNT_TEXT_FLAG_DIM, cbdata->tag); + g_free(str); + if (i == 0) + gnt_text_view_scroll(tv, 0); + g_free(iter->data); + g_free(url); + } + g_list_free(urls); + purple_conversation_set_data(conv, "TinyURLs", NULL); +} + +static void +free_conv_urls(PurpleConversation *conv) +{ + GList *urls = purple_conversation_get_data(conv, "TinyURLs"); + if (urls) + g_list_foreach(urls, free_urls, NULL); + g_list_free(urls); +} + +static gboolean +plugin_load(PurplePlugin *plugin) { + purple_signal_connect(purple_conversations_get_handle(), + "wrote-im-msg", + plugin, PURPLE_CALLBACK(received_msg), NULL); + purple_signal_connect(purple_conversations_get_handle(), + "wrote-chat-msg", + plugin, PURPLE_CALLBACK(received_msg), NULL); + purple_signal_connect(purple_conversations_get_handle(), + "receiving-im-msg", + plugin, PURPLE_CALLBACK(receiving_msg), NULL); + purple_signal_connect(purple_conversations_get_handle(), + "receiving-chat-msg", + plugin, PURPLE_CALLBACK(receiving_msg), NULL); + purple_signal_connect(purple_conversations_get_handle(), + "deleting-conversation", + plugin, PURPLE_CALLBACK(free_conv_urls), NULL); + + return TRUE; +} + +static PurplePluginPrefFrame * +get_plugin_pref_frame(PurplePlugin *plugin) { + + PurplePluginPrefFrame *frame; + PurplePluginPref *pref; + + frame = purple_plugin_pref_frame_new(); + + pref = purple_plugin_pref_new_with_name(PREF_LENGTH); + purple_plugin_pref_set_label(pref, _("Only create TinyURL for urls" + " of this length or greater")); + purple_plugin_pref_frame_add(frame, pref); + pref = purple_plugin_pref_new_with_name(PREF_URL); + purple_plugin_pref_set_label(pref, _("TinyURL (or other) address prefix")); + purple_plugin_pref_frame_add(frame, pref); + + return frame; +} + +static PurplePluginUiInfo prefs_info = { + get_plugin_pref_frame, + 0, /* page_num (Reserved) */ + NULL, /* frame (Reserved) */ + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static PurplePluginInfo info = +{ + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_STANDARD, + FINCH_PLUGIN_TYPE, + 0, + NULL, + PURPLE_PRIORITY_DEFAULT, + "TinyURL", + N_("TinyURL"), + DISPLAY_VERSION, + N_("TinyURL plugin"), + N_("When receiving a message with URL(s), TinyURL for easier copying"), + "Richard Nelson <wabz@whatsbeef.net>", + PURPLE_WEBSITE, + plugin_load, + NULL, + NULL, + NULL, + NULL, + &prefs_info, /**< prefs_info */ + NULL, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void +init_plugin(PurplePlugin *plugin) { + purple_prefs_add_none(PREFS_BASE); + purple_prefs_add_int(PREF_LENGTH, 30); + purple_prefs_add_string(PREF_URL, "http://tinyurl.com/api-create.php?url="); +} + +PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
--- a/libpurple/Makefile.am Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/Makefile.am Tue Apr 28 19:08:06 2009 +0000 @@ -113,7 +113,6 @@ idle.h \ imgstore.h \ log.h \ - marshallers.h \ media.h \ media-gst.h \ mediamanager.h \ @@ -153,7 +152,7 @@ xmlnode.h \ whiteboard.h -purple_builtheaders = purple.h version.h +purple_builtheaders = purple.h version.h marshallers.h marshallers.h: marshallers.list @echo "Generating marshallers.h" @@ -280,8 +279,8 @@ $(NETWORKMANAGER_LIBS) \ $(INTLLIBS) \ $(FARSIGHT_LIBS) \ - $(GSTPROPS_LIBS) \ $(GSTREAMER_LIBS) \ + $(GSTINTERFACES_LIBS) \ -lm AM_CPPFLAGS = \ @@ -295,8 +294,8 @@ $(DBUS_CFLAGS) \ $(LIBXML_CFLAGS) \ $(FARSIGHT_CFLAGS) \ - $(GSTPROPS_CFLAGS) \ $(GSTREAMER_CFLAGS) \ + $(GSTINTERFACES_CFLAGS) \ $(NETWORKMANAGER_CFLAGS) # INSTALL_SSL_CERTIFICATES is true when SSL_CERTIFICATES_DIR is empty.
--- a/libpurple/Makefile.mingw Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/Makefile.mingw Tue Apr 28 19:08:06 2009 +0000 @@ -35,6 +35,7 @@ buddyicon.c \ certificate.c \ cipher.c \ + circbuffer.c \ cmds.c \ connection.c \ conversation.c \ @@ -44,10 +45,11 @@ dnssrv.c \ eventloop.c \ ft.c \ - circbuffer.c \ idle.c \ imgstore.c \ log.c \ + media.c \ + mediamanager.c \ mime.c \ nat-pmp.c \ network.c \ @@ -66,22 +68,22 @@ server.c \ signals.c \ smiley.c \ - sound.c \ + sound-theme-loader.c \ sound-theme.c \ - sound-theme-loader.c \ + sound.c \ sslconn.c \ status.c \ stringref.c \ stun.c \ - theme.c \ theme-loader.c \ theme-manager.c \ + theme.c \ upnp.c \ util.c \ value.c \ version.c \ + whiteboard.c \ xmlnode.c \ - whiteboard.c \ win32/giowin32.c \ win32/libc_interface.c \ win32/win32dep.c
--- a/libpurple/account.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/account.c Tue Apr 28 19:08:06 2009 +0000 @@ -2742,6 +2742,10 @@ purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_STATUS)); + purple_signal_register(handle, "account-actions-changed", + purple_marshal_VOID__POINTER, NULL, 1, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT)); + purple_signal_register(handle, "account-alias-changed", purple_marshal_VOID__POINTER_POINTER, NULL, 2, purple_value_new(PURPLE_TYPE_SUBTYPE, @@ -2797,4 +2801,7 @@ purple_signals_disconnect_by_handle(handle); purple_signals_unregister_by_instance(handle); + + for (; accounts; accounts = g_list_delete_link(accounts, accounts)) + purple_account_destroy(accounts->data); }
--- a/libpurple/blist.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/blist.c Tue Apr 28 19:08:06 2009 +0000 @@ -699,10 +699,23 @@ return purplebuddylist ? purplebuddylist->root : NULL; } -GHashTable * +static void +append_buddy(gpointer key, gpointer value, gpointer user_data) +{ + GSList **list = user_data; + *list = g_slist_prepend(*list, value); +} + +GSList * purple_blist_get_buddies() { - return purplebuddylist ? purplebuddylist->buddies : NULL; + GSList *buddies = NULL; + + if (!purplebuddylist) + return NULL; + + g_hash_table_foreach(purplebuddylist->buddies, append_buddy, &buddies); + return buddies; } void * @@ -1202,6 +1215,16 @@ return chat; } +void +purple_chat_destroy(PurpleChat *chat) +{ + g_hash_table_destroy(chat->components); + g_hash_table_destroy(chat->node.settings); + g_free(chat->alias); + PURPLE_DBUS_UNREGISTER_POINTER(chat); + g_free(chat); +} + PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias) { PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); @@ -1229,6 +1252,42 @@ } void +purple_buddy_destroy(PurpleBuddy *buddy) +{ + PurplePlugin *prpl; + PurplePluginProtocolInfo *prpl_info; + + /* + * Tell the owner PRPL that we're about to free the buddy so it + * can free proto_data + */ + prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account)); + if (prpl) { + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + if (prpl_info && prpl_info->buddy_free) + prpl_info->buddy_free(buddy); + } + + /* Delete the node */ + purple_buddy_icon_unref(buddy->icon); + g_hash_table_destroy(buddy->node.settings); + purple_presence_destroy(buddy->presence); + g_free(buddy->name); + g_free(buddy->alias); + g_free(buddy->server_alias); + + PURPLE_DBUS_UNREGISTER_POINTER(buddy); + g_free(buddy); + + /* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can + * g_object_ref() it when connecting the callback and + * g_object_unref() it in the handler. That way, it won't + * get freed while the timeout is pending and this line can + * be removed. */ + while (g_source_remove_by_user_data((gpointer *)buddy)); +} + +void purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon) { g_return_if_fail(buddy != NULL); @@ -1519,6 +1578,15 @@ return contact; } +void +purple_contact_destroy(PurpleContact *contact) +{ + g_hash_table_destroy(contact->node.settings); + g_free(contact->alias); + PURPLE_DBUS_UNREGISTER_POINTER(contact); + g_free(contact); +} + void purple_contact_set_alias(PurpleContact *contact, const char *alias) { purple_blist_alias_contact(contact,alias); @@ -1588,6 +1656,15 @@ return group; } +void +purple_group_destroy(PurpleGroup *group) +{ + g_hash_table_destroy(group->node.settings); + g_free(group->name); + PURPLE_DBUS_UNREGISTER_POINTER(group); + g_free(group); +} + void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node) { PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); @@ -1848,9 +1925,7 @@ ops->remove(purplebuddylist, node); /* Delete the node */ - g_hash_table_destroy(contact->node.settings); - PURPLE_DBUS_UNREGISTER_POINTER(contact); - g_free(contact); + purple_contact_destroy(contact); } } @@ -1861,8 +1936,6 @@ PurpleContact *contact; PurpleGroup *group; struct _purple_hbuddy hb; - PurplePlugin *prpl; - PurplePluginProtocolInfo *prpl_info = NULL; g_return_if_fail(buddy != NULL); @@ -1918,33 +1991,7 @@ /* Signal that the buddy has been removed before freeing the memory for it */ purple_signal_emit(purple_blist_get_handle(), "buddy-removed", buddy); - /* - * Tell the owner PRPL that we're about to free the buddy so it - * can free proto_data - */ - prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account)); - if (prpl) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - if (prpl_info && prpl_info->buddy_free) - prpl_info->buddy_free(buddy); - - /* Delete the node */ - purple_buddy_icon_unref(buddy->icon); - g_hash_table_destroy(buddy->node.settings); - purple_presence_destroy(buddy->presence); - g_free(buddy->name); - g_free(buddy->alias); - g_free(buddy->server_alias); - - PURPLE_DBUS_UNREGISTER_POINTER(buddy); - g_free(buddy); - - /* FIXME: Once PurpleBuddy is a GObject, timeout callbacks can - * g_object_ref() it when connecting the callback and - * g_object_unref() it in the handler. That way, it won't - * get freed while the timeout is pending and this line can - * be removed. */ - while (g_source_remove_by_user_data((gpointer *)buddy)); + purple_buddy_destroy(buddy); /* If the contact is empty then remove it */ if ((contact != NULL) && !cnode->child) @@ -1988,11 +2035,7 @@ ops->remove(purplebuddylist, node); /* Delete the node */ - g_hash_table_destroy(chat->components); - g_hash_table_destroy(chat->node.settings); - g_free(chat->alias); - PURPLE_DBUS_UNREGISTER_POINTER(chat); - g_free(chat); + purple_chat_destroy(chat); } void purple_blist_remove_group(PurpleGroup *group) @@ -2033,10 +2076,7 @@ } /* Delete the node */ - g_hash_table_destroy(group->node.settings); - g_free(group->name); - PURPLE_DBUS_UNREGISTER_POINTER(group); - g_free(group); + purple_group_destroy(group); } PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact) @@ -2587,6 +2627,28 @@ } static void +purple_blist_node_destroy(PurpleBlistNode *node) +{ + PurpleBlistNode *child, *next_child; + + child = node->child; + while (child) { + next_child = child->next; + purple_blist_node_destroy(child); + child = next_child; + } + + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) + purple_buddy_destroy((PurpleBuddy*)node); + else if (PURPLE_BLIST_NODE_IS_CHAT(node)) + purple_chat_destroy((PurpleChat*)node); + else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) + purple_contact_destroy((PurpleContact*)node); + else if (PURPLE_BLIST_NODE_IS_GROUP(node)) + purple_group_destroy((PurpleGroup*)node); +} + +static void purple_blist_node_setting_free(gpointer data) { PurpleValue *value; @@ -2874,12 +2936,21 @@ void purple_blist_uninit(void) { - if (save_timer != 0) - { + PurpleBlistNode *node, *next_node; + + if (save_timer != 0) { purple_timeout_remove(save_timer); save_timer = 0; purple_blist_sync(); } + node = purple_blist_get_root(); + while (node) { + next_node = node->next; + purple_blist_node_destroy(node); + node = next_node; + } + purplebuddylist->root = NULL; + purple_signals_unregister_by_instance(purple_blist_get_handle()); }
--- a/libpurple/blist.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/blist.h Tue Apr 28 19:08:06 2009 +0000 @@ -118,8 +118,8 @@ /** * A Buddy list node. This can represent a group, a buddy, or anything else. - * This is a base class for struct buddy and struct group and for anything - * else that wants to put itself in the buddy list. */ + * This is a base class for PurpleBuddy, PurpleContact, PurpleGroup, and for + * anything else that wants to put itself in the buddy list. */ struct _PurpleBlistNode { PurpleBlistNodeType type; /**< The type of node this is */ PurpleBlistNode *prev; /**< The sibling before this buddy. */ @@ -207,7 +207,7 @@ PurpleBlistNode *node); /**< This will update a node in the buddy list. */ void (*remove)(PurpleBuddyList *list, PurpleBlistNode *node); /**< This removes a node from the list */ - void (*destroy)(PurpleBuddyList *list); /**< When the list gets destroyed, this gets called to destroy the UI. */ + void (*destroy)(PurpleBuddyList *list); /**< When the list is destroyed, this is called to destroy the UI. */ void (*set_visible)(PurpleBuddyList *list, gboolean show); /**< Hides or unhides the buddy list */ void (*request_add_buddy)(PurpleAccount *account, const char *username, @@ -260,13 +260,14 @@ PurpleBlistNode *purple_blist_get_root(void); /** - * Returns the hash table of every buddy in the list. + * Returns a list of every buddy in the list. * - * @return The hash table of every buddy in the list. + * @return A list of every buddy in the list. Caller is responsible for + * freeing the list. * * @since 2.6.0 */ -GHashTable *purple_blist_get_buddies(void); +GSList *purple_blist_get_buddies(void); /** * Returns the UI data for the list. @@ -275,7 +276,7 @@ * * @since 2.6.0 */ -void *purple_blist_get_ui_data(void); +gpointer purple_blist_get_ui_data(void); /** * Sets the UI data for the list. @@ -284,7 +285,7 @@ * * @since 2.6.0 */ -void purple_blist_set_ui_data(void *ui_data); +void purple_blist_set_ui_data(gpointer ui_data); /** * Returns the next node of a given node. This function is to be used to iterate @@ -359,7 +360,7 @@ * @return The UI data. * @since 2.6.0 */ -void *purple_blist_node_get_ui_data(const PurpleBlistNode *node); +gpointer purple_blist_node_get_ui_data(const PurpleBlistNode *node); /** * Sets the UI data of a given node. @@ -369,7 +370,7 @@ * * @since 2.6.0 */ -void purple_blist_node_set_ui_data(PurpleBlistNode *node, void *ui_data); +void purple_blist_node_set_ui_data(PurpleBlistNode *node, gpointer ui_data); /** * Shows the buddy list, creating a new one if necessary. @@ -392,6 +393,8 @@ /** * Updates a buddy's status. * + * This should only be called from within Purple. + * * @param buddy The buddy whose status has changed. * @param old_status The status from which we are changing. */ @@ -478,6 +481,13 @@ PurpleChat *purple_chat_new(PurpleAccount *account, const char *alias, GHashTable *components); /** + * Destroys a chat + * + * @param chat The chat to destroy + */ +void purple_chat_destroy(PurpleChat *chat); + +/** * Adds a new chat to the buddy list. * * The chat will be inserted right after node or appended to the end @@ -491,16 +501,30 @@ void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node); /** - * Creates a new buddy + * Creates a new buddy. + * + * This function only creates the PurpleBuddy. Use purple_blist_add_buddy + * to add the buddy to the list and purple_account_add_buddy to sync up + * with the server. * * @param account The account this buddy will get added to * @param name The name of the new buddy * @param alias The alias of the new buddy (or NULL if unaliased) * @return A newly allocated buddy + * + * @see purple_account_add_buddy + * @see purple_blist_add_buddy */ PurpleBuddy *purple_buddy_new(PurpleAccount *account, const char *name, const char *alias); /** + * Destroys a buddy + * + * @param buddy The buddy to destroy + */ +void purple_buddy_destroy(PurpleBuddy *buddy); + +/** * Sets a buddy's icon. * * This should only be called from within Purple. You probably want to @@ -603,7 +627,7 @@ * Creates a new group * * You can't have more than one group with the same name. Sorry. If you pass - * this the * name of a group that already exists, it will return that group. + * this the name of a group that already exists, it will return that group. * * @param name The name of the new group * @return A new group struct @@ -611,6 +635,13 @@ PurpleGroup *purple_group_new(const char *name); /** + * Destroys a group + * + * @param group The group to destroy +*/ +void purple_group_destroy(PurpleGroup *group); + +/** * Adds a new group to the buddy list. * * The new group will be inserted after insert or prepended to the list if @@ -629,6 +660,13 @@ PurpleContact *purple_contact_new(void); /** + * Destroys a contact + * + * @param contact The contact to destroy + */ +void purple_contact_destroy(PurpleContact *contact); + +/** * Adds a new contact to the buddy list. * * The new contact will be inserted after insert or prepended to the list if @@ -698,18 +736,22 @@ /** * Removes a buddy from the buddy list and frees the memory allocated to it. - * This doesn't actually try to remove the buddy from the server list, nor does - * it clean up the prpl_data. + * This doesn't actually try to remove the buddy from the server list. * * @param buddy The buddy to be removed + * + * @see purple_account_remove_buddy */ void purple_blist_remove_buddy(PurpleBuddy *buddy); /** * Removes a contact, and any buddies it contains, and frees the memory - * allocated to it. + * allocated to it. This calls purple_blist_remove_buddy and therefore + * doesn't remove the buddies from the server list. * * @param contact The contact to be removed + * + * @see purple_blist_remove_buddy */ void purple_blist_remove_contact(PurpleContact *contact); @@ -821,7 +863,7 @@ * Finds all PurpleBuddy structs given a name and an account * * @param account The account this buddy belongs to - * @param name The buddy's name (or NULL to return all buddies in the account) + * @param name The buddy's name (or NULL to return all buddies for the account) * * @return A GSList of buddies (which must be freed), or NULL if the buddy doesn't exist */ @@ -916,7 +958,7 @@ const char *purple_group_get_name(PurpleGroup *group); /** - * Called when an account gets signed on. Tells the UI to update all the + * Called when an account connects. Tells the UI to update all the * buddies. * * @param account The account @@ -925,7 +967,7 @@ /** - * Called when an account gets signed off. Sets the presence of all the buddies to 0 + * Called when an account disconnects. Sets the presence of all the buddies to 0 * and tells the UI to update them. * * @param account The account
--- a/libpurple/buddyicon.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/buddyicon.c Tue Apr 28 19:08:06 2009 +0000 @@ -1299,6 +1299,7 @@ g_hash_table_destroy(icon_file_cache); g_hash_table_destroy(pointer_icon_cache); g_free(old_icons_dir); + g_free(cache_dir); } void purple_buddy_icon_get_scale_size(PurpleBuddyIconSpec *spec, int *width, int *height)
--- a/libpurple/conversation.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/conversation.c Tue Apr 28 19:08:06 2009 +0000 @@ -33,7 +33,7 @@ #include "signals.h" #include "util.h" -#define SEND_TYPED_TIMEOUT 5000 +#define SEND_TYPED_TIMEOUT_SECONDS 5 static GList *conversations = NULL; static GList *ims = NULL; @@ -1122,8 +1122,9 @@ { g_return_if_fail(im != NULL); - im->send_typed_timeout = purple_timeout_add(SEND_TYPED_TIMEOUT, send_typed_cb, - purple_conv_im_get_conversation(im)); + im->send_typed_timeout = purple_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS, + send_typed_cb, + purple_conv_im_get_conversation(im)); } void @@ -2004,6 +2005,66 @@ purple_conversation_update(chat->conv, PURPLE_CONV_UPDATE_CHATLEFT); } +static void +invite_user_to_chat(gpointer data, PurpleRequestFields *fields) +{ + PurpleConversation *conv; + PurpleConvChat *chat; + const char *user, *message; + + conv = data; + chat = PURPLE_CONV_CHAT(conv); + user = purple_request_fields_get_string(fields, "screenname"); + message = purple_request_fields_get_string(fields, "message"); + + serv_chat_invite(purple_conversation_get_gc(conv), chat->id, message, user); +} + +void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user, + const char *message, gboolean confirm) +{ + PurpleAccount *account; + PurpleConversation *conv; + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + + g_return_if_fail(chat); + + if (!user || !*user || !message || !*message) + confirm = TRUE; + + conv = chat->conv; + account = conv->account; + + if (!confirm) { + serv_chat_invite(purple_account_get_connection(account), + purple_conv_chat_get_id(chat), message, user); + return; + } + + fields = purple_request_fields_new(); + group = purple_request_field_group_new(_("Invite to chat")); + purple_request_fields_add_group(fields, group); + + field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE); + purple_request_field_group_add_field(group, field); + purple_request_field_set_required(field, TRUE); + purple_request_field_set_type_hint(field, "screenname"); + + field = purple_request_field_string_new("message", _("Message"), message, FALSE); + purple_request_field_group_add_field(group, field); + + purple_request_fields(conv, _("Invite to chat"), NULL, + _("Please enter the name of the user you wish to invite, " + "along with an optional invite message."), + fields, + _("Invite"), G_CALLBACK(invite_user_to_chat), + _("Cancel"), NULL, + account, user, conv, + conv); +} + gboolean purple_conv_chat_has_left(PurpleConvChat *chat) {
--- a/libpurple/conversation.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/conversation.h Tue Apr 28 19:08:06 2009 +0000 @@ -1300,6 +1300,22 @@ void purple_conv_chat_left(PurpleConvChat *chat); /** + * Invite a user to a chat. + * The user will be prompted to enter the user's name or a message if one is + * not given. + * + * @param chat The chat. + * @param user The user to invite to the chat. + * @param message The message to send with the invitation. + * @param confirm Prompt before sending the invitation. The user is always + * prompted if either #user or #message is @c NULL. + * + * @since 2.6.0 + */ +void purple_conv_chat_invite_user(PurpleConvChat *chat, const char *user, + const char *message, gboolean confirm); + +/** * Returns true if we're no longer in this chat, * and just left the window open. *
--- a/libpurple/core.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/core.c Tue Apr 28 19:08:06 2009 +0000 @@ -216,15 +216,10 @@ /* The SSL plugins must be uninit before they're unloaded */ purple_ssl_uninit(); - /* Unload all plugins before the UI because UI plugins might call - * UI-specific functions */ - purple_debug_info("main", "Unloading all plugins\n"); - purple_plugins_destroy_all(); - - /* Shut down the UI before all the subsystems */ - ops = purple_core_get_ui_ops(); - if (ops != NULL && ops->quit != NULL) - ops->quit(); + /* Unload all non-loader, non-prpl plugins before shutting down + * subsystems. */ + purple_debug_info("main", "Unloading normal plugins\n"); + purple_plugins_unload(PURPLE_PLUGIN_STANDARD); /* Save .xml files, remove signals, etc. */ purple_smileys_uninit(); @@ -247,7 +242,16 @@ purple_imgstore_uninit(); purple_network_uninit(); - /* Everything after this must not try to read any prefs */ + /* Everything after unloading all plugins must not fail if prpls aren't + * around */ + purple_debug_info("main", "Unloading all plugins\n"); + purple_plugins_destroy_all(); + + ops = purple_core_get_ui_ops(); + if (ops != NULL && ops->quit != NULL) + ops->quit(); + + /* Everything after prefs_uninit must not try to read any prefs */ purple_prefs_uninit(); purple_plugins_uninit(); #ifdef HAVE_DBUS @@ -255,8 +259,9 @@ #endif purple_cmds_uninit(); - /* Everything after this cannot try to write things to the confdir */ + /* Everything after util_uninit cannot try to write things to the confdir */ purple_util_uninit(); + purple_log_uninit(); purple_signals_uninit();
--- a/libpurple/dnssrv.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/dnssrv.c Tue Apr 28 19:08:06 2009 +0000 @@ -65,6 +65,10 @@ DNS_FREE_TYPE FreeType) = NULL; #endif +struct _PurpleTxtResponse { + char *content; +}; + struct _PurpleSrvQueryData { union { PurpleSrvCallback srv; @@ -183,7 +187,7 @@ ret = g_list_insert_sorted(ret, srvres, responsecompare); } else if (query.type == T_TXT) { txtres = g_new0(PurpleTxtResponse, 1); - strncpy(txtres->content, (gchar*)(++cp), dlen-1); + txtres->content = g_strndup((gchar*)(++cp), dlen-1); ret = g_list_append(ret, txtres); cp += dlen - 1; } else { @@ -246,29 +250,30 @@ } cb(res, size, query_data->extradata); } else if (type == T_TXT) { + GSList *responses = NULL; PurpleTxtResponse *res; - PurpleTxtResponse *tmp; PurpleTxtCallback cb = query_data->cb.txt; if (read(source, &size, sizeof(int)) == sizeof(int)) { ssize_t red; purple_debug_info("dnssrv","found %d TXT entries\n", size); - tmp = res = g_new0(PurpleTxtResponse, size); + res = g_new0(PurpleTxtResponse, 1); for (i = 0; i < size; i++) { - red = read(source, tmp++, sizeof(PurpleTxtResponse)); + red = read(source, res, sizeof(PurpleTxtResponse)); if (red != sizeof(PurpleTxtResponse)) { purple_debug_error("dnssrv","unable to read txt " "response: %s\n", g_strerror(errno)); size = 0; g_free(res); - res = NULL; + g_slist_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); + g_slist_free(responses); + responses = NULL; + break; } } } else { purple_debug_info("dnssrv","found 0 TXT entries; errno is %i\n", errno); - size = 0; - res = NULL; } - cb(res, size, query_data->extradata); + cb(responses, query_data->extradata); } else { purple_debug_info("dnssrv","type unknown of DNS result entry; errno is %i\n", errno); } @@ -316,22 +321,12 @@ PurpleTxtResponse *txtres_tmp = NULL; GSList *lst = query_data->results; - size = g_slist_length(lst); - - if(query_data->cb.txt && size > 0) - txtres_tmp = txtres = g_new0(PurpleTxtResponse, size); - while (lst) { - if(query_data->cb.txt) - memcpy(txtres_tmp++, lst->data, sizeof(PurpleTxtResponse)); - g_free(lst->data); - lst = g_slist_remove(lst, lst->data); + purple_debug_info("dnssrv", "found %d TXT entries\n", g_slist_length(lst)); + + if (query_data->cb.txt) { + query_data->results = NULL; + query_data->cb.txt(lst, query_data->extradata); } - - query_data->results = NULL; - - purple_debug_info("dnssrv", "found %d TXT entries\n", size); - - if(query_data->cb.txt) query_data->cb.txt(txtres, size, query_data->extradata); } else { purple_debug_error("dnssrv", "unknown query type"); } @@ -389,7 +384,33 @@ MyDnsRecordListFree(dr, DnsFreeRecordList); query_data->results = lst; } else if (type == T_TXT) { - #error IMPLEMENTATION MISSING + PDNS_RECORD dr_tmp; + GSList *lst = NULL; + DNS_TXT_DATA *txt_data; + PurpleTxtResponse *txtres; + + for (dr_tmp = dr; dr_tmp != NULL; dr_tmp = dr_tmp->pNext) { + GString *s; + int i; + + /* Discard any incorrect entries. I'm not sure if this is necessary */ + if (dr_tmp->wType != type || strcmp(dr_tmp->pName, query_data->query) != 0) { + continue; + } + + txt_data = &dr_tmp->Data.TXT; + txtres = g_new0(PurpleTxtResponse, 1); + + s = g_string_new(""); + for (i = 0; i < txt_data->dwStringCount; ++i) + s = g_string_append(s, txt_data->pStringArray[i]); + txtres->content = g_string_free(s, FALSE); + + lst = g_slist_append(lst, txtres); + } + + MyDnsRecordListFree(dr, DnsFreeRecordList); + query_data->results = lst; } else { } @@ -531,14 +552,14 @@ if(pipe(in) || pipe(out)) { purple_debug_error("dnssrv", "Could not create pipe\n"); g_free(query); - cb(NULL, 0, extradata); + cb(NULL, extradata); return NULL; } pid = fork(); if (pid == -1) { purple_debug_error("dnssrv", "Could not create process!\n"); - cb(NULL, 0, extradata); + cb(NULL, extradata); g_free(query); return NULL; } @@ -599,7 +620,7 @@ } } - /* The query isn't going to happen, so finish the SRV lookup now. + /* The query isn't going to happen, so finish the TXT lookup now. * Asynchronously call the callback since stuff may not expect * the callback to be called before this returns */ if (query_data->error_message != NULL) @@ -639,3 +660,19 @@ { purple_srv_cancel(query_data); } + +const gchar * +purple_txt_response_get_content(PurpleTxtResponse *resp) +{ + g_return_val_if_fail(resp != NULL, NULL); + + return resp->content; +} + +void purple_txt_response_destroy(PurpleTxtResponse *resp) +{ + g_return_if_fail(resp != NULL); + + g_free(resp->content); + g_free(resp); +}
--- a/libpurple/dnssrv.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/dnssrv.h Tue Apr 28 19:08:06 2009 +0000 @@ -32,6 +32,8 @@ typedef struct _PurpleSrvResponse PurpleSrvResponse; typedef struct _PurpleTxtResponse PurpleTxtResponse; +#include <glib.h> + struct _PurpleSrvResponse { char hostname[256]; int port; @@ -39,12 +41,15 @@ int pref; }; -struct _PurpleTxtResponse { - char content[256]; -}; +typedef void (*PurpleSrvCallback)(PurpleSrvResponse *resp, int results, gpointer data); -typedef void (*PurpleSrvCallback)(PurpleSrvResponse *resp, int results, gpointer data); -typedef void (*PurpleTxtCallback)(PurpleTxtResponse *resp, int results, gpointer data); +/** + * Callback that returns the data retrieved from a DNS TXT lookup. + * + * @param responses A GSList of PurpleTxtResponse objects. + * @param data The extra data passed to purple_txt_resolve. + */ +typedef void (*PurpleTxtCallback)(GSList *responses, gpointer data); /** * Queries an SRV record. @@ -71,6 +76,8 @@ * @param domain Domain name to query (e.g. "blubb.com") * @param cb A callback which will be called with the results * @param extradata Extra data to be passed to the callback + * + * @since 2.6.0 */ PurpleSrvQueryData *purple_txt_resolve(const char *owner, const char *domain, PurpleTxtCallback cb, gpointer extradata); @@ -78,9 +85,27 @@ * Cancel an TXT DNS query. * * @param query_data The request to cancel. + * @since 2.6.0 */ void purple_txt_cancel(PurpleSrvQueryData *query_data); +/** + * Get the value of the current TXT record. + * + * @param resp The TXT response record + * @returns The value of the current TXT record. + * @since 2.6.0 + */ +const gchar *purple_txt_response_get_content(PurpleTxtResponse *resp); + +/** + * Destroy a TXT DNS response object. + * + * @param response The PurpleTxtResponse to destroy. + * @since 2.6.0 + */ +void purple_txt_response_destroy(PurpleTxtResponse *resp); + #ifdef __cplusplus } #endif
--- a/libpurple/ft.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/ft.c Tue Apr 28 19:08:06 2009 +0000 @@ -458,7 +458,7 @@ { PurpleXferType type; struct stat st; - char *msg, *utf8; + char *msg, *utf8, *base; PurpleAccount *account; PurpleBuddy *buddy; @@ -505,7 +505,9 @@ purple_xfer_set_local_filename(xfer, filename); purple_xfer_set_size(xfer, st.st_size); - utf8 = g_filename_to_utf8(g_basename(filename), -1, NULL, NULL, NULL); + base = g_path_get_basename(filename); + utf8 = g_filename_to_utf8(base, -1, NULL, NULL, NULL); + g_free(base); purple_xfer_set_filename(xfer, utf8); msg = g_strdup_printf(_("Offering to send %s to %s"),
--- a/libpurple/internal.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/internal.h Tue Apr 28 19:08:06 2009 +0000 @@ -222,7 +222,6 @@ # endif #endif -#include <glib.h> #include <glib-object.h> #ifndef G_DEFINE_TYPE
--- a/libpurple/media-gst.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/media-gst.h Tue Apr 28 19:08:06 2009 +0000 @@ -24,8 +24,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __MEDIA_GST_H_ -#define __MEDIA_GST_H_ +#ifndef _PURPLE_MEDIA_GST_H_ +#define _PURPLE_MEDIA_GST_H_ #include "media.h" #include "mediamanager.h" @@ -81,6 +81,8 @@ * Gets the element type's GType. * * @return The element type's GType. + * + * @since 2.6.0 */ GType purple_media_element_type_get_type(void); @@ -88,6 +90,8 @@ * Gets the element info's GType. * * @return The element info's GType. + * + * @since 2.6.0 */ GType purple_media_element_info_get_type(void); @@ -98,6 +102,8 @@ * @param sess_id The session id of the session to get the source from. * * @return The source retrieved. + * + * @since 2.6.0 */ GstElement *purple_media_get_src(PurpleMedia *media, const gchar *sess_id); @@ -109,6 +115,8 @@ * @param participant Optionally, the participant of the stream to get the tee from. * * @return The GstTee element from the chosen session/stream. + * + * @since 2.6.0 */ GstElement *purple_media_get_tee(PurpleMedia *media, const gchar *session_id, const gchar *participant); @@ -120,6 +128,8 @@ * @param manager The media manager to get the pipeline from. * * @return The pipeline. + * + * @since 2.6.0 */ GstElement *purple_media_manager_get_pipeline(PurpleMediaManager *manager); @@ -128,6 +138,8 @@ * * @param manager The media manager to use to obtain the source/sink. * @param type The type of source/sink to get. + * + * @since 2.6.0 */ GstElement *purple_media_manager_get_element(PurpleMediaManager *manager, PurpleMediaSessionType type, PurpleMedia *media, @@ -158,4 +170,4 @@ G_END_DECLS -#endif /* __MEDIA_GST_H_ */ +#endif /* _PURPLE_MEDIA_GST_H_ */
--- a/libpurple/media.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/media.c Tue Apr 28 19:08:06 2009 +0000 @@ -28,15 +28,18 @@ #include "internal.h" -#include "connection.h" -#include "marshallers.h" +#include "account.h" #include "media.h" -#include "media-gst.h" #include "mediamanager.h" #include "network.h" #include "debug.h" +#ifdef USE_GSTREAMER +#include "marshallers.h" +#include "media-gst.h" +#endif + #ifdef USE_VV #include <gst/farsight/fs-conference-iface.h> @@ -109,7 +112,7 @@ { #ifdef USE_VV PurpleMediaManager *manager; - PurpleConnection *pc; + PurpleAccount *account; FsConference *conference; gboolean initiator; gpointer prpl_data; @@ -166,7 +169,7 @@ enum { PROP_0, PROP_MANAGER, - PROP_CONNECTION, + PROP_ACCOUNT, PROP_CONFERENCE, PROP_INITIATOR, PROP_PRPL_DATA, @@ -298,10 +301,10 @@ PURPLE_TYPE_MEDIA_MANAGER, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); - g_object_class_install_property(gobject_class, PROP_CONNECTION, - g_param_spec_pointer("connection", - "PurpleConnection", - "The connection this media session is on.", + g_object_class_install_property(gobject_class, PROP_ACCOUNT, + g_param_spec_pointer("account", + "PurpleAccount", + "The account this media session is on.", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_CONFERENCE, @@ -528,8 +531,8 @@ purple_media_setup_pipeline(media); break; - case PROP_CONNECTION: - media->priv->pc = g_value_get_pointer(value); + case PROP_ACCOUNT: + media->priv->account = g_value_get_pointer(value); break; case PROP_CONFERENCE: { if (media->priv->conference) @@ -564,8 +567,8 @@ case PROP_MANAGER: g_value_set_object(value, media->priv->manager); break; - case PROP_CONNECTION: - g_value_set_pointer(value, media->priv->pc); + case PROP_ACCOUNT: + g_value_set_pointer(value, media->priv->account); break; case PROP_CONFERENCE: g_value_set_object(value, media->priv->conference); @@ -1847,7 +1850,7 @@ #endif GList * -purple_media_get_session_names(PurpleMedia *media) +purple_media_get_session_ids(PurpleMedia *media) { #ifdef USE_VV g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); @@ -1908,6 +1911,7 @@ } #endif +#ifdef USE_GSTREAMER GstElement * purple_media_get_src(PurpleMedia *media, const gchar *sess_id) { @@ -1920,6 +1924,7 @@ return NULL; #endif } +#endif /* USE_GSTREAMER */ #ifdef USE_VV static PurpleMediaSession * @@ -2055,14 +2060,14 @@ } #endif -PurpleConnection * -purple_media_get_connection(PurpleMedia *media) +PurpleAccount * +purple_media_get_account(PurpleMedia *media) { #ifdef USE_VV - PurpleConnection *pc; + PurpleAccount *account; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - g_object_get(G_OBJECT(media), "connection", &pc, NULL); - return pc; + g_object_get(G_OBJECT(media), "account", &account, NULL); + return account; #else return NULL; #endif @@ -2658,12 +2663,13 @@ } GList * -purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *name) +purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id, + const gchar *participant) { #ifdef USE_VV PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - stream = purple_media_get_stream(media, sess_id, name); + stream = purple_media_get_stream(media, sess_id, participant); return purple_media_candidate_list_from_fs(stream->local_candidates); #else return NULL; @@ -2672,20 +2678,21 @@ void purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id, - const gchar *name, GList *remote_candidates) + const gchar *participant, + GList *remote_candidates) { #ifdef USE_VV PurpleMediaStream *stream; GError *err = NULL; g_return_if_fail(PURPLE_IS_MEDIA(media)); - stream = purple_media_get_stream(media, sess_id, name); + stream = purple_media_get_stream(media, sess_id, participant); if (stream == NULL) { purple_debug_error("media", "purple_media_add_remote_candidates: " "couldn't find stream %s %s.\n", - sess_id, name); + sess_id, participant); return; } @@ -2711,12 +2718,12 @@ GList * purple_media_get_active_local_candidates(PurpleMedia *media, - const gchar *sess_id, const gchar *name) + const gchar *sess_id, const gchar *participant) { #ifdef USE_VV PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - stream = purple_media_get_stream(media, sess_id, name); + stream = purple_media_get_stream(media, sess_id, participant); return purple_media_candidate_list_from_fs( stream->active_local_candidates); #else @@ -2726,12 +2733,12 @@ GList * purple_media_get_active_remote_candidates(PurpleMedia *media, - const gchar *sess_id, const gchar *name) + const gchar *sess_id, const gchar *participant) { #ifdef USE_VV PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - stream = purple_media_get_stream(media, sess_id, name); + stream = purple_media_get_stream(media, sess_id, participant); return purple_media_candidate_list_from_fs( stream->active_remote_candidates); #else @@ -2741,7 +2748,8 @@ #endif gboolean -purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id, const gchar *name, GList *codecs) +purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id, + const gchar *participant, GList *codecs) { #ifdef USE_VV PurpleMediaStream *stream; @@ -2750,7 +2758,7 @@ GError *err = NULL; g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); - stream = purple_media_get_stream(media, sess_id, name); + stream = purple_media_get_stream(media, sess_id, participant); if (stream == NULL) return FALSE; @@ -3024,7 +3032,7 @@ stream->session->id, stream->participant); } - iter = purple_media_get_session_names(media); + iter = purple_media_get_session_ids(media); for (; iter; iter = g_list_delete_link(iter, iter)) { gchar *session_name = iter->data; purple_media_manager_remove_output_windows( @@ -3034,6 +3042,7 @@ #endif } +#ifdef USE_GSTREAMER GstElement * purple_media_get_tee(PurpleMedia *media, const gchar *session_id, const gchar *participant) @@ -3056,4 +3065,5 @@ return NULL; #endif } - +#endif /* USE_GSTREAMER */ +
--- a/libpurple/media.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/media.h Tue Apr 28 19:08:06 2009 +0000 @@ -24,11 +24,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __MEDIA_H_ -#define __MEDIA_H_ - -#include "signals.h" -#include "util.h" +#ifndef _PURPLE_MEDIA_H_ +#define _PURPLE_MEDIA_H_ #include <glib.h> #include <glib-object.h> @@ -129,6 +126,9 @@ PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, } PurpleMediaNetworkProtocol; +#include "signals.h" +#include "util.h" + #ifdef __cplusplus extern "C" { #endif @@ -137,6 +137,8 @@ * Gets the media session type's GType * * @return The media session type's GType. + * + * @since 2.6.0 */ GType purple_media_session_type_get_type(void); @@ -144,6 +146,8 @@ * Gets the media candidate type's GType * * @return The media candidate type's GType. + * + * @since 2.6.0 */ GType purple_media_candidate_type_get_type(void); @@ -151,6 +155,8 @@ * Gets the media network protocol's GType * * @return The media network protocol's GType. + * + * @since 2.6.0 */ GType purple_media_network_protocol_get_type(void); @@ -158,6 +164,8 @@ * Gets the media class's GType * * @return The media class's GType. + * + * @since 2.6.0 */ GType purple_media_get_type(void); @@ -165,6 +173,8 @@ * Gets the type of the state-changed enum * * @return The state-changed enum's GType + * + * @since 2.6.0 */ GType purple_media_state_changed_get_type(void); @@ -172,6 +182,8 @@ * Gets the type of the info type enum * * @return The info type enum's GType + * + * @since 2.6.0 */ GType purple_media_info_type_get_type(void); @@ -179,6 +191,8 @@ * Gets the type of the media candidate structure. * * @return The media canditate's GType + * + * @since 2.6.0 */ GType purple_media_candidate_get_type(void); @@ -193,6 +207,8 @@ * @param port The network port. * * @return The newly created PurpleMediaCandidate instance. + * + * @since 2.6.0 */ PurpleMediaCandidate *purple_media_candidate_new( const gchar *foundation, guint component_id, @@ -206,6 +222,8 @@ * @param candidates The list of candidates to be copied. * * @return The copy of the GList. + * + * @since 2.6.0 */ GList *purple_media_candidate_list_copy(GList *candidates); @@ -213,6 +231,8 @@ * Frees a GList of PurpleMediaCandidate and its contents. * * @param candidates The list of candidates to be freed. + * + * @since 2.6.0 */ void purple_media_candidate_list_free(GList *candidates); @@ -235,6 +255,8 @@ * Gets the type of the media codec structure. * * @return The media codec's GType + * + * @since 2.6.0 */ GType purple_media_codec_get_type(void); @@ -247,6 +269,8 @@ * @param clock_rate The clock rate this codec encodes at, if applicable. * * @return The newly created PurpleMediaCodec. + * + * @since 2.6.0 */ PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name, PurpleMediaSessionType media_type, guint clock_rate); @@ -263,6 +287,8 @@ * @param codec The codec to create the string of. * * @return The new string representation. + * + * @since 2.6.0 */ gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec); @@ -272,6 +298,8 @@ * @param codec The codec to add the parameter to. * @param name The name of the parameter to add. * @param value The value of the parameter to add. + * + * @since 2.6.0 */ void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec, const gchar *name, const gchar *value); @@ -281,6 +309,8 @@ * * @param codec The codec to remove the parameter from. * @param param A pointer to the parameter to remove. + * + * @since 2.6.0 */ void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec, PurpleKeyValuePair *param); @@ -293,6 +323,8 @@ * @param value The value to search for or NULL. * * @return The value found or NULL. + * + * @since 2.6.0 */ PurpleKeyValuePair *purple_media_codec_get_optional_parameter( PurpleMediaCodec *codec, const gchar *name, @@ -304,6 +336,8 @@ * @param codecs The list of codecs to be copied. * * @return The copy of the GList. + * + * @since 2.6.0 */ GList *purple_media_codec_list_copy(GList *codecs); @@ -311,26 +345,32 @@ * Frees a GList of PurpleMediaCodec and its contents. * * @param codecs The list of codecs to be freed. + * + * @since 2.6.0 */ void purple_media_codec_list_free(GList *codecs); /** - * Gets a list of session names. + * Gets a list of session IDs. * - * @param media The media session to retrieve session names from. + * @param media The media session from which to retrieve session IDs. * - * @return GList of session names. + * @return GList of session IDs. The caller must free the list. + * + * @since 2.6.0 */ -GList *purple_media_get_session_names(PurpleMedia *media); +GList *purple_media_get_session_ids(PurpleMedia *media); /** - * Gets the PurpleConnection this media session is on. + * Gets the PurpleAccount this media session is on. * - * @param media The media session to retrieve the connection from. + * @param media The media session to retrieve the account from. * - * @return The connection retrieved. + * @return The account retrieved. + * + * @since 2.6.0 */ -PurpleConnection *purple_media_get_connection(PurpleMedia *media); +PurpleAccount *purple_media_get_account(PurpleMedia *media); /** * Gets the prpl data from the media session. @@ -338,6 +378,8 @@ * @param media The media session to retrieve the prpl data from. * * @return The prpl data retrieved. + * + * @since 2.6.0 */ gpointer purple_media_get_prpl_data(PurpleMedia *media); @@ -346,6 +388,8 @@ * * @param media The media session to set the prpl data on. * @param prpl_data The data to set on the media session. + * + * @since 2.6.0 */ void purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data); @@ -355,6 +399,8 @@ * @param media The media object to set the state on. * @param error The format of the error message to send in the signal. * @param ... The arguments to plug into the format. + * + * @since 2.6.0 */ void purple_media_error(PurpleMedia *media, const gchar *error, ...); @@ -364,6 +410,8 @@ * @param media The media object with which to end streams. * @param session_id The session to end streams on. * @param participant The participant to end streams with. + * + * @since 2.6.0 */ void purple_media_end(PurpleMedia *media, const gchar *session_id, const gchar *participant); @@ -376,6 +424,8 @@ * @param session_id The id of the session of the stream being signaled. * @param participant The participant of the stream being signaled. * @param local TRUE if the info originated locally, FALSE if on the remote end. + * + * @since 2.6.0 */ void purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type, const gchar *session_id, const gchar *participant, @@ -397,6 +447,8 @@ * @param params The parameters to pass to Farsight. * * @return @c TRUE The stream was added successfully, @c FALSE otherwise. + * + * @since 2.6.0 */ gboolean purple_media_add_stream(PurpleMedia *media, const gchar *sess_id, const gchar *who, PurpleMediaSessionType type, @@ -410,6 +462,8 @@ * @param sess_id The session id of the session to get the type from. * * @return The retreived session type. + * + * @since 2.6.0 */ PurpleMediaSessionType purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id); @@ -419,6 +473,8 @@ * @param media The media object to get the manager instance from. * * @return The PurpleMediaManager instance retrieved. + * + * @since 2.6.0 */ struct _PurpleMediaManager *purple_media_get_manager(PurpleMedia *media); @@ -429,6 +485,8 @@ * @param sess_id The session id of the session to get the codecs from. * * @return The retreieved codecs. + * + * @since 2.6.0 */ GList *purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id); @@ -437,12 +495,14 @@ * * @param media The media object to find the session in. * @param sess_id The session id of the session find the stream in. - * @param name The name of the remote user to add the candidates for. + * @param participant The name of the remote user to add the candidates for. * @param remote_candidates The remote candidates to add. + * + * @since 2.6.0 */ void purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id, - const gchar *name, + const gchar *participant, GList *remote_candidates); /** @@ -450,11 +510,13 @@ * * @param media The media object to find the session in. * @param sess_id The session id of the session to find the stream in. - * @param name The name of the remote user to get the candidates from. + * @param participant The name of the remote user to get the candidates from. + * + * @since 2.6.0 */ GList *purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id, - const gchar *name); + const gchar *participant); #if 0 /* @@ -467,24 +529,26 @@ * * @param media The media object to find the session in. * @param sess_id The session id of the session to find the stream in. - * @param name The name of the remote user to get the active candidate from. + * @param participant The name of the remote user to get the active candidate + * from. * * @return The active candidates retrieved. */ GList *purple_media_get_active_local_candidates(PurpleMedia *media, - const gchar *sess_id, const gchar *name); + const gchar *sess_id, const gchar *participant); /** * Gets the active remote candidates for the stream. * * @param media The media object to find the session in. * @param sess_id The session id of the session to find the stream in. - * @param name The name of the remote user to get the remote candidate from. + * @param participant The name of the remote user to get the remote candidate + * from. * * @return The remote candidates retrieved. */ GList *purple_media_get_active_remote_candidates(PurpleMedia *media, - const gchar *sess_id, const gchar *name); + const gchar *sess_id, const gchar *participant); #endif /** @@ -492,12 +556,14 @@ * * @param media The media object to find the session in. * @param sess_id The session id of the session find the stream in. - * @param name The name of the remote user to get the candidates from. + * @param participant The name of the remote user to set the candidates from. * * @return @c TRUE The codecs were set successfully, or @c FALSE otherwise. + * + * @since 2.6.0 */ gboolean purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id, - const gchar *name, GList *codecs); + const gchar *participant, GList *codecs); /** * Returns whether or not the candidates for set of streams are prepared @@ -507,6 +573,8 @@ * @param participant The remote user to check for. * * @return @c TRUE All streams for the given session_id/participant combination have candidates prepared, @c FALSE otherwise. + * + * @since 2.6.0 */ gboolean purple_media_candidates_prepared(PurpleMedia *media, const gchar *session_id, const gchar *participant); @@ -519,6 +587,8 @@ * @param codec The codec to set the session to stream. * * @return @c TRUE The codec was successfully changed, or @c FALSE otherwise. + * + * @since 2.6.0 */ gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec); @@ -529,6 +599,8 @@ * @param sess_id The session id of the session to check. * * @return @c TRUE The codecs are ready, or @c FALSE otherwise. + * + * @since 2.6.0 */ gboolean purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id); @@ -540,6 +612,8 @@ * @param participant The participant of the stream to check. * * @return TRUE if the local user is the stream's initator, else FALSE. + * + * @since 2.6.0 */ gboolean purple_media_is_initiator(PurpleMedia *media, const gchar *sess_id, const gchar *participant); @@ -552,6 +626,8 @@ * @param participant The participant to check. * * @return @c TRUE The selected streams have been accepted, or @c FALSE otherwise. + * + * @since 2.6.0 */ gboolean purple_media_accepted(PurpleMedia *media, const gchar *sess_id, const gchar *participant); @@ -562,6 +638,8 @@ * @param media The media object the sessions are in. * @param session_id The session to select (if any). * @param level The level to set the volume to. + * + * @since 2.6.0 */ void purple_media_set_input_volume(PurpleMedia *media, const gchar *session_id, double level); @@ -572,6 +650,8 @@ * @param session_id The session to limit the streams to (if any). * @param participant The participant to limit the streams to (if any). * @param level The level to set the volume to. + * + * @since 2.6.0 */ void purple_media_set_output_volume(PurpleMedia *media, const gchar *session_id, const gchar *participant, double level); @@ -585,6 +665,8 @@ * @param window_id The window id use for embedding the video in. * * @return An id to reference the output window. + * + * @since 2.6.0 */ gulong purple_media_set_output_window(PurpleMedia *media, const gchar *session_id, const gchar *participant, @@ -594,6 +676,8 @@ * Removes all output windows from a given media session. * * @param media The instance to remove all output windows from. + * + * @since 2.6.0 */ void purple_media_remove_output_windows(PurpleMedia *media); @@ -603,4 +687,4 @@ G_END_DECLS -#endif /* __MEDIA_H_ */ +#endif /* _PURPLE_MEDIA_H_ */
--- a/libpurple/mediamanager.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/mediamanager.c Tue Apr 28 19:08:06 2009 +0000 @@ -26,12 +26,15 @@ #include "internal.h" -#include "connection.h" +#include "account.h" #include "debug.h" +#include "media.h" +#include "mediamanager.h" + +#ifdef USE_GSTREAMER #include "marshallers.h" -#include "media.h" #include "media-gst.h" -#include "mediamanager.h" +#endif #ifdef USE_VV @@ -218,6 +221,7 @@ } #endif +#ifdef USE_GSTREAMER GstElement * purple_media_manager_get_pipeline(PurpleMediaManager *manager) { @@ -246,10 +250,11 @@ return NULL; #endif } +#endif /* USE_GSTREAMER */ PurpleMedia * purple_media_manager_create_media(PurpleMediaManager *manager, - PurpleConnection *gc, + PurpleAccount *account, const char *conference_type, const char *remote_user, gboolean initiator) @@ -261,8 +266,7 @@ gboolean signal_ret; if (conference == NULL) { - purple_conv_present_error(remote_user, - purple_connection_get_account(gc), + purple_conv_present_error(remote_user, account, _("Error creating conference.")); purple_debug_error("media", "Conference == NULL\n"); return NULL; @@ -270,7 +274,7 @@ media = PURPLE_MEDIA(g_object_new(purple_media_get_type(), "manager", manager, - "connection", gc, + "account", account, "conference", conference, "initiator", initiator, NULL)); @@ -278,8 +282,7 @@ ret = gst_element_set_state(GST_ELEMENT(conference), GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { - purple_conv_present_error(remote_user, - purple_connection_get_account(gc), + purple_conv_present_error(remote_user, account, _("Error creating conference.")); purple_debug_error("media", "Failed to start conference.\n"); g_object_unref(media); @@ -287,7 +290,7 @@ } g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0, - media, gc, remote_user, &signal_ret); + media, account, remote_user, &signal_ret); if (signal_ret == FALSE) { g_object_unref(media); @@ -312,8 +315,8 @@ } GList * -purple_media_manager_get_media_by_connection(PurpleMediaManager *manager, - PurpleConnection *pc) +purple_media_manager_get_media_by_account(PurpleMediaManager *manager, + PurpleAccount *account) { #ifdef USE_VV GList *media = NULL; @@ -323,7 +326,7 @@ iter = manager->priv->medias; for (; iter; iter = g_list_next(iter)) { - if (purple_media_get_connection(iter->data) == pc) { + if (purple_media_get_account(iter->data) == account) { media = g_list_prepend(media, iter->data); } } @@ -368,6 +371,7 @@ } #endif +#ifdef USE_GSTREAMER GstElement * purple_media_manager_get_element(PurpleMediaManager *manager, PurpleMediaSessionType type, PurpleMedia *media, @@ -497,7 +501,6 @@ g_object_unref(info2); return FALSE; } - g_object_unref(info2); manager->priv->elements = g_list_prepend(manager->priv->elements, info); @@ -560,7 +563,8 @@ if (info2 == NULL) purple_media_manager_register_element(manager, info); - g_object_unref(info2); + else + g_object_unref(info2); type = purple_media_element_info_get_element_type(info); @@ -613,6 +617,7 @@ return NULL; } +#endif /* USE_GSTREAMER */ #ifdef USE_VV static void @@ -846,6 +851,7 @@ #endif } +#ifdef USE_GSTREAMER /* * PurpleMediaElementType @@ -1117,3 +1123,5 @@ return NULL; } +#endif /* USE_GSTREAMER */ +
--- a/libpurple/mediamanager.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/mediamanager.h Tue Apr 28 19:08:06 2009 +0000 @@ -24,13 +24,18 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __MEDIA_MANAGER_H_ -#define __MEDIA_MANAGER_H_ +#ifndef _PURPLE_MEDIA_MANAGER_H_ +#define _PURPLE_MEDIA_MANAGER_H_ #include <glib.h> #include <glib-object.h> -#include "connection.h" +/** @copydoc _PurpleMediaManager */ +typedef struct _PurpleMediaManager PurpleMediaManager; +/** @copydoc _PurpleMediaManagerClass */ +typedef struct _PurpleMediaManagerClass PurpleMediaManagerClass; + +#include "account.h" #include "media.h" G_BEGIN_DECLS @@ -42,17 +47,12 @@ #define PURPLE_IS_MEDIA_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_MANAGER)) #define PURPLE_MEDIA_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerClass)) -/** @copydoc _PurpleMediaManager */ -typedef struct _PurpleMediaManager PurpleMediaManager; -/** @copydoc _PurpleMediaManagerClass */ -typedef struct _PurpleMediaManagerClass PurpleMediaManagerClass; - #ifdef __cplusplus extern "C" { #endif /**************************************************************************/ -/** @cname Media Manager API */ +/** @name Media Manager API */ /**************************************************************************/ /*@{*/ @@ -60,6 +60,8 @@ * Gets the media manager's GType. * * @return The media manager's GType. + * + * @since 2.6.0 */ GType purple_media_manager_get_type(void); @@ -67,6 +69,8 @@ * Gets the "global" media manager object. It's created if it doesn't already exist. * * @return The "global" instance of the media manager object. + * + * @since 2.6.0 */ PurpleMediaManager *purple_media_manager_get(void); @@ -74,14 +78,16 @@ * Creates a media session. * * @param manager The media manager to create the session under. - * @param gc The connection to create the session on. + * @param account The account to create the session on. * @param conference_type The conference type to feed into Farsight2. * @param remote_user The remote user to initiate the session with. * * @return A newly created media session. + * + * @since 2.6.0 */ PurpleMedia *purple_media_manager_create_media(PurpleMediaManager *manager, - PurpleConnection *gc, + PurpleAccount *account, const char *conference_type, const char *remote_user, gboolean initiator); @@ -92,25 +98,31 @@ * @param manager The media manager to get all of the sessions from. * * @return A list of all the media sessions. + * + * @since 2.6.0 */ GList *purple_media_manager_get_media(PurpleMediaManager *manager); /** - * Gets all of the media sessions for a given connection. + * Gets all of the media sessions for a given account. * * @param manager The media manager to get the sessions from. - * @param pc The connection the sessions are on. + * @param account The account the sessions are on. + * + * @return A list of the media sessions on the given account. * - * @return A list of the media sessions on the given connection. + * @since 2.6.0 */ -GList *purple_media_manager_get_media_by_connection( - PurpleMediaManager *manager, PurpleConnection *pc); +GList *purple_media_manager_get_media_by_account( + PurpleMediaManager *manager, PurpleAccount *account); /** * Removes a media session from the media manager. * * @param manager The media manager to remove the media session from. * @param media The media session to remove. + * + * @since 2.6.0 */ void purple_media_manager_remove_media(PurpleMediaManager *manager, @@ -127,6 +139,8 @@ * @param participant The participant the output windows are registered with. * * @return TRUE if it succeeded, FALSE if it failed. + * + * @since 2.6.0 */ gboolean purple_media_manager_create_output_window( PurpleMediaManager *manager, PurpleMedia *media, @@ -142,6 +156,8 @@ * @param window_id The window ID to embed the video in. * * @return A unique ID to the registered output window, 0 if it failed. + * + * @since 2.6.0 */ gulong purple_media_manager_set_output_window(PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id, @@ -154,6 +170,8 @@ * @param output_window_id The ID of the output window. * * @return TRUE if it found the output window and was successful, else FALSE. + * + * @since 2.6.0 */ gboolean purple_media_manager_remove_output_window( PurpleMediaManager *manager, gulong output_window_id); @@ -165,6 +183,8 @@ * @param media The media instance the output windows were registered for. * @param session_id The session the output windows were registered for. * @param participant The participant the output windows were registered for. + * + * @since 2.6.0 */ void purple_media_manager_remove_output_windows( PurpleMediaManager *manager, PurpleMedia *media, @@ -175,6 +195,8 @@ * * @param manager The manager to set the caps on. * @param caps The caps to set. + * + * @since 2.6.0 */ void purple_media_manager_set_ui_caps(PurpleMediaManager *manager, PurpleMediaCaps caps); @@ -185,6 +207,8 @@ * @param manager The manager to get caps from. * * @return caps The caps retrieved. + * + * @since 2.6.0 */ PurpleMediaCaps purple_media_manager_get_ui_caps(PurpleMediaManager *manager); @@ -196,4 +220,4 @@ G_END_DECLS -#endif /* __MEDIA_MANAGER_H_ */ +#endif /* _PURPLE_MEDIA_MANAGER_H_ */
--- a/libpurple/mime.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/mime.c Tue Apr 28 19:08:06 2009 +0000 @@ -25,9 +25,6 @@ #include <string.h> #include <glib.h> -#include <glib/ghash.h> -#include <glib/glist.h> -#include <glib/gstring.h> #include "internal.h"
--- a/libpurple/mime.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/mime.h Tue Apr 28 19:08:06 2009 +0000 @@ -25,7 +25,6 @@ #define _PURPLE_MIME_H #include <glib.h> -#include <glib/glist.h> #ifdef __cplusplus extern "C" {
--- a/libpurple/network.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/network.c Tue Apr 28 19:08:06 2009 +0000 @@ -825,8 +825,13 @@ *ip = g_strdup(dst); purple_debug_info("network", "set IP address: %s\n", *ip); } - - g_slist_free(hosts); + + while (hosts != NULL) { + hosts = g_slist_delete_link(hosts, hosts); + /* Free the address */ + g_free(hosts->data); + hosts = g_slist_delete_link(hosts, hosts); + } } void
--- a/libpurple/plugin.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/plugin.c Tue Apr 28 19:08:06 2009 +0000 @@ -1201,6 +1201,11 @@ purple_signals_disconnect_by_handle(handle); purple_signals_unregister_by_instance(handle); + + while (search_paths) { + g_free(search_paths->data); + search_paths = g_list_delete_link(search_paths, search_paths); + } } /************************************************************************** @@ -1229,6 +1234,21 @@ } void +purple_plugins_unload(PurplePluginType type) +{ +#ifdef PURPLE_PLUGINS + GList *l; + + for (l = plugins; l; l = l->next) { + PurplePlugin *plugin = l->data; + if (plugin->info->type == type && purple_plugin_is_loaded(plugin)) + purple_plugin_unload(plugin); + } + +#endif /* PURPLE_PLUGINS */ +} + +void purple_plugins_destroy_all(void) { #ifdef PURPLE_PLUGINS
--- a/libpurple/plugin.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/plugin.h Tue Apr 28 19:08:06 2009 +0000 @@ -29,7 +29,7 @@ #ifndef _PURPLE_PLUGIN_H_ #define _PURPLE_PLUGIN_H_ -#include <glib/glist.h> +#include <glib.h> #include <gmodule.h> #include "signals.h" #include "value.h" @@ -105,6 +105,20 @@ void *ui_info; /**< Used only by UI-specific plugins to build a preference screen with a custom UI */ void *extra_info; PurplePluginUiInfo *prefs_info; /**< Used by any plugin to display preferences. If #ui_info has been specified, this will be ignored. */ + + /** + * This callback has a different use depending on whether this + * plugin type is PURPLE_PLUGIN_STANDARD or PURPLE_PLUGIN_PROTOCOL. + * + * If PURPLE_PLUGIN_STANDARD then the list of actions will show up + * in the Tools menu, under a submenu with the name of the plugin. + * context will be NULL. + * + * If PURPLE_PLUGIN_PROTOCOL then the list of actions will show up + * in the Accounts menu, under a submenu with the name of the + * account. context will be set to the PurpleConnection for that + * account. This callback will only be called for online accounts. + */ GList *(*actions)(PurplePlugin *plugin, gpointer context); void (*_purple_reserved1)(void); @@ -503,6 +517,11 @@ void purple_plugins_unload_all(void); /** + * Unloads all plugins of a specific type. + */ +void purple_plugins_unload(PurplePluginType type); + +/** * Destroys all registered plugins. */ void purple_plugins_destroy_all(void);
--- a/libpurple/plugins/filectl.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/plugins/filectl.c Tue Apr 28 19:08:06 2009 +0000 @@ -220,7 +220,7 @@ plugin_load(PurplePlugin *plugin) { init_file(); - check = purple_timeout_add(5000, (GSourceFunc)check_file, NULL); + check = purple_timeout_add_seconds(5, (GSourceFunc)check_file, NULL); return TRUE; }
--- a/libpurple/plugins/joinpart.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/plugins/joinpart.c Tue Apr 28 19:08:06 2009 +0000 @@ -194,7 +194,7 @@ PURPLE_CALLBACK(received_chat_msg_cb), users); /* Cleanup every 5 minutes */ - id = purple_timeout_add(1000 * 60 * 5, (GSourceFunc)clean_users_hash, users); + id = purple_timeout_add_seconds(60 * 5, (GSourceFunc)clean_users_hash, users); data = g_new(gpointer, 2); data[0] = users;
--- a/libpurple/plugins/perl/perl-common.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/plugins/perl/perl-common.c Tue Apr 28 19:08:06 2009 +0000 @@ -403,7 +403,7 @@ static SV * purple_perl_sv_from_subtype(const PurpleValue *value, void *arg) { - const char *stash = NULL; + const char *stash = "Purple"; /* ? */ switch (purple_value_get_subtype(value)) { case PURPLE_SUBTYPE_ACCOUNT: @@ -442,6 +442,9 @@ case PURPLE_SUBTYPE_STATUS: stash = "Purple::Status"; break; + case PURPLE_SUBTYPE_SAVEDSTATUS: + stash = "Purple::SavedStatus"; + break; case PURPLE_SUBTYPE_LOG: stash = "Purple::Log"; break; @@ -451,10 +454,19 @@ case PURPLE_SUBTYPE_XMLNODE: stash = "Purple::XMLNode"; break; - - default: - stash = "Purple"; /* ? */ - } + case PURPLE_SUBTYPE_USERINFO: + stash = "Purple::NotifyUserInfo"; + break; + case PURPLE_SUBTYPE_STORED_IMAGE: + stash = "Purple::StoredImage"; + break; + case PURPLE_SUBTYPE_CERTIFICATEPOOL: + stash = "Purple::Certificate::Pool"; + break; + case PURPLE_SUBTYPE_UNKNOWN: + stash = "Purple::Unknown"; + break; + } return sv_2mortal(purple_perl_bless_object(arg, stash)); }
--- a/libpurple/plugins/statenotify.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/plugins/statenotify.c Tue Apr 28 19:08:06 2009 +0000 @@ -71,9 +71,9 @@ void *data) { if (purple_prefs_get_bool("/plugins/core/statenotify/notify_idle")) { - if (idle) { + if (idle && !old_idle) { write_status(buddy, _("%s has become idle.")); - } else { + } else if (!idle && old_idle) { write_status(buddy, _("%s is no longer idle.")); } }
--- a/libpurple/plugins/tcl/tcl_cmds.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/plugins/tcl/tcl_cmds.c Tue Apr 28 19:08:06 2009 +0000 @@ -683,8 +683,9 @@ int tcl_cmd_connection(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_Obj *list, *elem; - const char *cmds[] = { "account", "displayname", "handle", "list", NULL }; - enum { CMD_CONN_ACCOUNT, CMD_CONN_DISPLAYNAME, CMD_CONN_HANDLE, CMD_CONN_LIST } cmd; + const char *cmds[] = { "account", "displayname", "handle", "list", "state", NULL }; + enum { CMD_CONN_ACCOUNT, CMD_CONN_DISPLAYNAME, CMD_CONN_HANDLE, + CMD_CONN_LIST, CMD_CONN_STATE } cmd; int error; GList *cur; PurpleConnection *gc; @@ -739,6 +740,25 @@ } Tcl_SetObjResult(interp, list); break; + case CMD_CONN_STATE: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "gc"); + return TCL_ERROR; + } + if ((gc = tcl_validate_gc(objv[2], interp)) == NULL) + return TCL_ERROR; + switch (purple_connection_get_state(gc)) { + case PURPLE_DISCONNECTED: + Tcl_SetObjResult(interp, Tcl_NewStringObj("disconnected", -1)); + break; + case PURPLE_CONNECTED: + Tcl_SetObjResult(interp, Tcl_NewStringObj("connected", -1)); + break; + case PURPLE_CONNECTING: + Tcl_SetObjResult(interp, Tcl_NewStringObj("connecting", -1)); + break; + } + break; } return TCL_OK;
--- a/libpurple/protocols/bonjour/jabber.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Tue Apr 28 19:08:06 2009 +0000 @@ -237,7 +237,7 @@ }; static void -_match_buddies_by_address(gpointer key, gpointer value, gpointer data) +_match_buddies_by_address(gpointer value, gpointer data) { PurpleBuddy *pb = value; PurpleAccount *account = NULL; @@ -638,6 +638,7 @@ char *address_text = NULL; struct _match_buddies_by_address_t *mbba; BonjourJabberConversation *bconv; + GSList *buddies; /* Check that it is a read condition */ if (condition != PURPLE_INPUT_READ) @@ -658,7 +659,10 @@ mbba = g_new0(struct _match_buddies_by_address_t, 1); mbba->address = address_text; mbba->jdata = jdata; - g_hash_table_foreach(purple_blist_get_buddies(), _match_buddies_by_address, mbba); + + buddies = purple_blist_get_buddies(); + g_slist_foreach(buddies, _match_buddies_by_address, mbba); + g_slist_free(buddies); if (mbba->matched_buddies == NULL) { purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n"); @@ -850,11 +854,15 @@ bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) { BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data; struct _match_buddies_by_address_t *mbba; + GSList *buddies; mbba = g_new0(struct _match_buddies_by_address_t, 1); mbba->address = bconv->ip; mbba->jdata = jdata; - g_hash_table_foreach(purple_blist_get_buddies(), _match_buddies_by_address, mbba); + + buddies = purple_blist_get_buddies(); + g_slist_foreach(buddies, _match_buddies_by_address, mbba); + g_slist_free(buddies); /* If there is exactly one match, use it */ if(mbba->matched_buddies != NULL) {
--- a/libpurple/protocols/bonjour/parser.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/bonjour/parser.c Tue Apr 28 19:08:06 2009 +0000 @@ -153,6 +153,18 @@ xmlnode_insert_data(bconv->current, (const char*) text, text_len); } +static void +bonjour_parser_structured_error_handler(void *user_data, xmlErrorPtr error) +{ + BonjourJabberConversation *bconv = user_data; + + purple_debug_error("jabber", "XML parser error for BonjourJabberConversation %p: " + "Domain %i, code %i, level %i: %s", + bconv, + error->domain, error->code, error->level, + (error->message ? error->message : "(null)\n")); +} + static xmlSAXHandler bonjour_parser_libxml = { NULL, /*internalSubset*/ NULL, /*isStandalone*/ @@ -185,7 +197,7 @@ NULL, /*_private*/ bonjour_parser_element_start_libxml, /*startElementNs*/ bonjour_parser_element_end_libxml, /*endElementNs*/ - NULL /*serror*/ + bonjour_parser_structured_error_handler /*serror*/ }; void
--- a/libpurple/protocols/irc/msgs.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/irc/msgs.c Tue Apr 28 19:08:06 2009 +0000 @@ -128,7 +128,7 @@ irc_blist_timeout(irc); if (!irc->timer) - irc->timer = purple_timeout_add(45000, (GSourceFunc)irc_blist_timeout, (gpointer)irc); + irc->timer = purple_timeout_add_seconds(45, (GSourceFunc)irc_blist_timeout, (gpointer)irc); } void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args) @@ -1004,10 +1004,25 @@ void irc_msg_nickused(struct irc_conn *irc, const char *name, const char *from, char **args) { char *newnick, *buf, *end; + PurpleConnection *gc = purple_account_get_connection(irc->account); if (!args || !args[1]) return; + if (gc && purple_connection_get_state(gc) == PURPLE_CONNECTED) { + /* We only want to do the following dance if the connection + has not been successfully completed. If it has, just + notify the user that their /nick command didn't go. */ + buf = g_strdup_printf(_("The nickname \"%s\" is already being used."), + irc->reqnick); + purple_notify_error(gc, _("Nickname in use"), + _("Nickname in use"), buf); + g_free(buf); + g_free(irc->reqnick); + irc->reqnick = NULL; + return; + } + if (strlen(args[1]) < strlen(irc->reqnick) || irc->nickused) newnick = g_strdup(args[1]); else
--- a/libpurple/protocols/jabber/Makefile.am Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/Makefile.am Tue Apr 28 19:08:06 2009 +0000 @@ -63,6 +63,8 @@ adhoccommands.h \ pep.c \ pep.h \ + useravatar.c \ + useravatar.h \ usermood.c \ usermood.h \ usernick.c \
--- a/libpurple/protocols/jabber/Makefile.mingw Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/Makefile.mingw Tue Apr 28 19:08:06 2009 +0000 @@ -46,7 +46,7 @@ adhoccommands.c \ auth.c \ buddy.c \ - bosh.c + bosh.c \ caps.c \ chat.c \ data.c \
--- a/libpurple/protocols/jabber/adhoccommands.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/adhoccommands.c Tue Apr 28 19:08:06 2009 +0000 @@ -39,29 +39,18 @@ GList *actionslist; } JabberAdHocActionInfo; -void jabber_adhoc_disco_result_cb(JabberStream *js, xmlnode *packet, gpointer data) { - const char *from = xmlnode_get_attrib(packet, "from"); - const char *type = xmlnode_get_attrib(packet, "type"); - const char *node; - xmlnode *query, *item; - JabberID *jabberid; +static void +jabber_adhoc_got_buddy_list(JabberStream *js, const char *from, xmlnode *query) +{ + JabberID *jid; JabberBuddy *jb; JabberBuddyResource *jbr = NULL; - - if(strcmp(type, "result")) - return; + xmlnode *item; - query = xmlnode_get_child_with_namespace(packet,"query","http://jabber.org/protocol/disco#items"); - if(!query) - return; - node = xmlnode_get_attrib(query,"node"); - if(!node || strcmp(node, "http://jabber.org/protocol/commands")) - return; - - if((jabberid = jabber_id_new(from))) { - if(jabberid->resource && (jb = jabber_buddy_find(js, from, TRUE))) - jbr = jabber_buddy_find_resource(jb, jabberid->resource); - jabber_id_free(jabberid); + if ((jid = jabber_id_new(from))) { + if (jid->resource && (jb = jabber_buddy_find(js, from, TRUE))) + jbr = jabber_buddy_find_resource(jb, jid->resource); + jabber_id_free(jid); } if(!jbr) @@ -95,7 +84,30 @@ } } -static void jabber_adhoc_parse(JabberStream *js, xmlnode *packet, gpointer data); +void +jabber_adhoc_disco_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ + xmlnode *query; + const char *node; + + if (type == JABBER_IQ_ERROR) + return; + + query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#items"); + if (!query) + return; + node = xmlnode_get_attrib(query, "node"); + if (!purple_strequal(node, "http://jabber.org/protocol/commands")) + return; + + jabber_adhoc_got_buddy_list(js, from, query); +} + +static void jabber_adhoc_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data); static void do_adhoc_action_cb(JabberStream *js, xmlnode *result, const char *actionhandle, gpointer user_data) { xmlnode *command; @@ -131,13 +143,16 @@ jabber_iq_send(iq); } -static void jabber_adhoc_parse(JabberStream *js, xmlnode *packet, gpointer data) { +static void +jabber_adhoc_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ xmlnode *command = xmlnode_get_child_with_namespace(packet, "command", "http://jabber.org/protocol/commands"); const char *status = xmlnode_get_attrib(command,"status"); xmlnode *xdata = xmlnode_get_child_with_namespace(command,"x","jabber:x:data"); - const char *type = xmlnode_get_attrib(packet,"type"); - if(type && !strcmp(type,"error")) { + if (type == JABBER_IQ_ERROR) { char *msg = jabber_parse_error(js, packet, NULL); if(!msg) msg = g_strdup(_("Unknown Error")); @@ -147,8 +162,6 @@ g_free(msg); return; } - if(!type || strcmp(type,"result")) - return; if(!status) return; @@ -159,7 +172,7 @@ if(note) { char *data = xmlnode_get_data(note); - purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), data, NULL); + purple_notify_info(NULL, from, data, NULL); g_free(data); } @@ -199,7 +212,7 @@ actionInfo = g_new0(JabberAdHocActionInfo, 1); actionInfo->sessionid = g_strdup(xmlnode_get_attrib(command,"sessionid")); - actionInfo->who = g_strdup(xmlnode_get_attrib(packet,"from")); + actionInfo->who = g_strdup(from); actionInfo->node = g_strdup(xmlnode_get_attrib(command,"node")); actionInfo->actionslist = actionslist; @@ -218,8 +231,9 @@ } } -static void jabber_adhoc_server_got_list_cb(JabberStream *js, xmlnode *packet, gpointer data) { - xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#items"); +static void +jabber_adhoc_got_server_list(JabberStream *js, const char *from, xmlnode *query) +{ xmlnode *item; if(!query) @@ -249,6 +263,29 @@ js->commands = g_list_append(js->commands,cmd); } + + if (js->state == JABBER_STREAM_CONNECTED) + purple_prpl_got_account_actions(purple_connection_get_account(js->gc)); +} + +static void +jabber_adhoc_server_got_list_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ + xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#items"); + + jabber_adhoc_got_server_list(js, from, query); + +} + +void jabber_adhoc_got_list(JabberStream *js, const char *from, xmlnode *query) +{ + if (purple_strequal(from, js->user->domain)) { + jabber_adhoc_got_server_list(js, from, query); + } else { + jabber_adhoc_got_buddy_list(js, from, query); + } } void jabber_adhoc_server_get_list(JabberStream *js) {
--- a/libpurple/protocols/jabber/adhoccommands.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/adhoccommands.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,21 +19,25 @@ * */ -#ifndef _PURPLE_JABBER_ADHOCCOMMANDS_H_ -#define _PURPLE_JABBER_ADHOCCOMMANDS_H_ +#ifndef PURPLE_JABBER_ADHOCCOMMANDS_H_ +#define PURPLE_JABBER_ADHOCCOMMANDS_H_ #include "jabber.h" /* Implementation of XEP-0050 */ -void jabber_adhoc_disco_result_cb(JabberStream *js, xmlnode *packet, gpointer data); +void jabber_adhoc_disco_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data); void jabber_adhoc_execute(JabberStream *js, JabberAdHocCommands *cmd); void jabber_adhoc_execute_action(PurpleBlistNode *node, gpointer data); +void jabber_adhoc_got_list(JabberStream *js, const char *from, xmlnode *query); + void jabber_adhoc_server_get_list(JabberStream *js); void jabber_adhoc_init_server_commands(JabberStream *js, GList **m); -#endif /* _PURPLE_JABBER_ADHOCCOMMANDS_H_ */ +#endif /* PURPLE_JABBER_ADHOCCOMMANDS_H_ */
--- a/libpurple/protocols/jabber/auth.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/auth.c Tue Apr 28 19:08:06 2009 +0000 @@ -30,14 +30,16 @@ #include "util.h" #include "xmlnode.h" +#include "auth.h" +#include "disco.h" +#include "jabber.h" #include "jutil.h" -#include "auth.h" -#include "jabber.h" #include "iq.h" #include "notify.h" -static void auth_old_result_cb(JabberStream *js, xmlnode *packet, - gpointer data); +static void auth_old_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data); gboolean jabber_process_starttls(JabberStream *js, xmlnode *packet) @@ -566,12 +568,12 @@ #endif } -static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void auth_old_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - const char *type = xmlnode_get_attrib(packet, "type"); - - if(type && !strcmp(type, "result")) { - jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); + if (type == JABBER_IQ_RESULT) { + jabber_disco_items_server(js); } else { PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; char *msg = jabber_parse_error(js, packet, &reason); @@ -593,24 +595,20 @@ } } -static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void auth_old_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JabberIq *iq; xmlnode *query, *x; - const char *type = xmlnode_get_attrib(packet, "type"); const char *pw = purple_connection_get_password(js->gc); - if(!type) { - purple_connection_error_reason (js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Invalid response from server.")); - return; - } else if(!strcmp(type, "error")) { + if (type == JABBER_IQ_ERROR) { PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; char *msg = jabber_parse_error(js, packet, &reason); purple_connection_error_reason (js->gc, reason, msg); g_free(msg); - } else if(!strcmp(type, "result")) { + } else if (type == JABBER_IQ_RESULT) { query = xmlnode_get_child(packet, "query"); if(js->stream_id && xmlnode_get_child(query, "digest")) { char *s, *hash;
--- a/libpurple/protocols/jabber/auth.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/auth.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_AUTH_H_ -#define _PURPLE_JABBER_AUTH_H_ +#ifndef PURPLE_JABBER_AUTH_H_ +#define PURPLE_JABBER_AUTH_H_ #include "jabber.h" #include "xmlnode.h" @@ -32,4 +32,4 @@ void jabber_auth_handle_success(JabberStream *js, xmlnode *packet); void jabber_auth_handle_failure(JabberStream *js, xmlnode *packet); -#endif /* _PURPLE_JABBER_AUTH_H_ */ +#endif /* PURPLE_JABBER_AUTH_H_ */
--- a/libpurple/protocols/jabber/bosh.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/bosh.c Tue Apr 28 19:08:06 2009 +0000 @@ -79,7 +79,10 @@ PurpleBOSHConnection *bosh; PurpleSslConnection *psc; int fd; - int ie_handle; + guint readh; + guint writeh; + + PurpleCircBuffer *write_buffer; gboolean ready; int requests; /* number of outstanding HTTP requests */ @@ -92,7 +95,8 @@ }; static void http_connection_connect(PurpleHTTPConnection *conn); -static void http_connection_send_request(PurpleHTTPConnection *conn, const GString *req); +static void http_connection_send_request(PurpleHTTPConnection *conn, + const GString *req); void jabber_bosh_init(void) { @@ -127,6 +131,8 @@ conn->fd = -1; conn->ready = FALSE; + conn->write_buffer = purple_circ_buffer_new(0 /* default grow size */); + return conn; } @@ -136,8 +142,12 @@ if (conn->buf) g_string_free(conn->buf, TRUE); - if (conn->ie_handle) - purple_input_remove(conn->ie_handle); + if (conn->write_buffer) + purple_circ_buffer_destroy(conn->write_buffer); + if (conn->readh) + purple_input_remove(conn->readh); + if (conn->writeh) + purple_input_remove(conn->writeh); if (conn->psc) purple_ssl_close(conn->psc); if (conn->fd >= 0) @@ -176,8 +186,15 @@ g_free(passwd); conn->js = js; - /* FIXME: This doesn't seem very random */ - conn->rid = rand() % 100000 + 1728679472; + + /* + * Random 64-bit integer masked off by 2^52 - 1. + * + * This should produce a random integer in the range [0, 2^52). It's + * unlikely we'll send enough packets in one session to overflow the rid. + */ + conn->rid = ((guint64)g_random_int() << 32) | g_random_int(); + conn->rid &= 0xFFFFFFFFFFFFF; conn->pending = purple_circ_buffer_new(0 /* default grow size */); @@ -572,9 +589,14 @@ conn->fd = -1; } - if (conn->ie_handle) { - purple_input_remove(conn->ie_handle); - conn->ie_handle = 0; + if (conn->readh) { + purple_input_remove(conn->readh); + conn->readh = 0; + } + + if (conn->writeh) { + purple_input_remove(conn->writeh); + conn->writeh = 0; } if (conn->bosh->pipelining) @@ -689,7 +711,7 @@ /* * If the socket is closed, the processing really needs to know about - * it. Handle that now (it will be handled again post-processing). + * it. Handle that now. */ http_connection_disconnected(conn); @@ -755,7 +777,7 @@ } conn->fd = source; - conn->ie_handle = purple_input_add(conn->fd, PURPLE_INPUT_READ, + conn->readh = purple_input_add(conn->fd, PURPLE_INPUT_READ, http_connection_read_cb, conn); connection_common_established_cb(conn); } @@ -790,13 +812,59 @@ } } +static int +http_connection_do_send(PurpleHTTPConnection *conn, const char *data, int len) +{ + int ret; + + if (conn->psc) + ret = purple_ssl_write(conn->psc, data, len); + else + ret = write(conn->fd, data, len); + + return ret; +} + +static void +http_connection_send_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleHTTPConnection *conn = data; + int ret; + int writelen = purple_circ_buffer_get_max_read(conn->write_buffer); + + if (writelen == 0) { + purple_input_remove(conn->writeh); + conn->writeh = 0; + return; + } + + ret = http_connection_do_send(conn, conn->write_buffer->outptr, writelen); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret <= 0) { + /* + * TODO: Handle this better. Probably requires a PurpleBOSHConnection + * buffer that stores what is "being sent" until the + * PurpleHTTPConnection reports it is fully sent. + */ + purple_connection_error_reason(conn->bosh->js->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Write error")); + return; + } + + purple_circ_buffer_mark_read(conn->write_buffer, ret); +} + static void http_connection_send_request(PurpleHTTPConnection *conn, const GString *req) { - char *packet; + char *data; int ret; + size_t len; - packet = g_strdup_printf("POST %s HTTP/1.1\r\n" + data = g_strdup_printf("POST %s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: %s\r\n" "Content-Encoding: text/xml; charset=utf-8\r\n" @@ -805,25 +873,35 @@ conn->bosh->path, conn->bosh->host, bosh_useragent, req->len, req->str); - /* TODO: Better error handling, circbuffer or possible integration with - * low-level code in jabber.c */ - if (conn->psc) - ret = purple_ssl_write(conn->psc, packet, strlen(packet)); - else - ret = write(conn->fd, packet, strlen(packet)); + len = strlen(data); ++conn->requests; ++conn->bosh->requests; - g_free(packet); - if (ret < 0 && errno == EAGAIN) - purple_debug_error("jabber", "BOSH write would have blocked\n"); + if (conn->writeh == 0) + ret = http_connection_do_send(conn, data, len); + else { + ret = -1; + errno = EAGAIN; + } - if (ret <= 0) { + if (ret < 0 && errno != EAGAIN) { + /* + * TODO: Handle this better. Probably requires a PurpleBOSHConnection + * buffer that stores what is "being sent" until the + * PurpleHTTPConnection reports it is fully sent. + */ purple_connection_error_reason(conn->bosh->js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Write error")); return; + } else if (ret < len) { + if (ret < 0) + ret = 0; + if (conn->writeh == 0) + conn->writeh = purple_input_add(conn->psc ? conn->psc->fd : conn->fd, + PURPLE_INPUT_WRITE, http_connection_send_cb, conn); + purple_circ_buffer_append(conn->write_buffer, data + ret, len - ret); } }
--- a/libpurple/protocols/jabber/bosh.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/bosh.h Tue Apr 28 19:08:06 2009 +0000 @@ -1,5 +1,5 @@ /** - * @file bosh.h Buddy handlers + * @file bosh.h Bidirectional-streams over Synchronous HTTP (BOSH) (XEP-0124 and XEP-0206) * * purple * @@ -19,8 +19,8 @@ * 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_JABBER_BOSH_H_ -#define _PURPLE_JABBER_BOSH_H_ +#ifndef PURPLE_JABBER_BOSH_H_ +#define PURPLE_JABBER_BOSH_H_ typedef struct _PurpleBOSHConnection PurpleBOSHConnection; @@ -38,4 +38,4 @@ void jabber_bosh_connection_close(PurpleBOSHConnection *conn); void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn, const char *data); void jabber_bosh_connection_refresh(PurpleBOSHConnection *conn); -#endif /* _PURPLE_JABBER_BOSH_H_ */ +#endif /* PURPLE_JABBER_BOSH_H_ */
--- a/libpurple/protocols/jabber/buddy.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/buddy.c Tue Apr 28 19:08:06 2009 +0000 @@ -32,12 +32,11 @@ #include "jabber.h" #include "iq.h" #include "presence.h" +#include "useravatar.h" #include "xdata.h" #include "pep.h" #include "adhoccommands.h" -#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024) - typedef struct { long idle_seconds; } JabberBuddyInfoResource; @@ -98,36 +97,41 @@ for(l = jb->resources; l; l = l->next) { - if(!jbr && !resource) { - jbr = l->data; - } else if(!resource) { - if(((JabberBuddyResource *)l->data)->priority > jbr->priority) - jbr = l->data; - else if(((JabberBuddyResource *)l->data)->priority == jbr->priority) { + JabberBuddyResource *tmp = (JabberBuddyResource *) l->data; + if (!jbr && !resource) { + jbr = tmp; + } else if (!resource) { + if (tmp->priority > jbr->priority) + jbr = tmp; + else if (tmp->priority == jbr->priority) { /* Determine if this resource is more available than the one we've currently chosen */ - switch(((JabberBuddyResource *)l->data)->state) { + switch(tmp->state) { case JABBER_BUDDY_STATE_ONLINE: case JABBER_BUDDY_STATE_CHAT: /* This resource is online/chatty. Prefer to one which isn't either. */ - if ((jbr->state != JABBER_BUDDY_STATE_ONLINE) && (jbr->state != JABBER_BUDDY_STATE_CHAT)) - jbr = l->data; + if (((jbr->state != JABBER_BUDDY_STATE_ONLINE) && (jbr->state != JABBER_BUDDY_STATE_CHAT)) + || (jbr->idle && !tmp->idle) + || (jbr->idle && tmp->idle && tmp->idle > jbr->idle)) + jbr = tmp; break; case JABBER_BUDDY_STATE_AWAY: case JABBER_BUDDY_STATE_DND: /* This resource is away/dnd. Prefer to one which is extended away, unavailable, or unknown. */ - if ((jbr->state == JABBER_BUDDY_STATE_XA) || (jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || + if (((jbr->state == JABBER_BUDDY_STATE_XA) || (jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) - jbr = l->data; + || (jbr->idle && !tmp->idle) + || (jbr->idle && tmp->idle && tmp->idle > jbr->idle)) + jbr = tmp; break; case JABBER_BUDDY_STATE_XA: /* This resource is extended away. That's better than unavailable or unknown. */ if ((jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) - jbr = l->data; + jbr = tmp; break; case JABBER_BUDDY_STATE_UNAVAILABLE: /* This resource is unavailable. That's better than unknown. */ if ((jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) - jbr = l->data; + jbr = tmp; break; case JABBER_BUDDY_STATE_UNKNOWN: case JABBER_BUDDY_STATE_ERROR: @@ -135,9 +139,9 @@ break; } } - } else if(((JabberBuddyResource *)l->data)->name) { - if(!strcmp(((JabberBuddyResource *)l->data)->name, resource)) { - jbr = l->data; + } else if(tmp->name) { + if(!strcmp(tmp->name, resource)) { + jbr = tmp; break; } } @@ -155,6 +159,7 @@ jbr->jb = jb; jbr->name = g_strdup(resource); jbr->capabilities = JABBER_CAP_XHTML; + jbr->tz_off = PURPLE_NO_TZ_OFF; jb->resources = g_list_append(jb->resources, jbr); } jbr->priority = priority; @@ -203,21 +208,6 @@ jabber_buddy_resource_free(jbr); } -const char *jabber_buddy_get_status_msg(JabberBuddy *jb) -{ - JabberBuddyResource *jbr; - - if(!jb) - return NULL; - - jbr = jabber_buddy_find_resource(jb, NULL); - - if(!jbr) - return NULL; - - return jbr->status; -} - /******* * This is the old vCard stuff taken from the old prpl. vCards, by definition * are a temporary thing until jabber can get its act together and come up @@ -486,129 +476,25 @@ iq = jabber_iq_new(js, JABBER_IQ_SET); xmlnode_insert_child(iq->node, vc_node); jabber_iq_send(iq); + + /* Send presence to update vcard-temp:x:update */ + jabber_presence_send(js, FALSE); } } void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) { - if(((JabberStream*)purple_connection_get_protocol_data(gc))->pep) { - /* XEP-0084: User Avatars */ - if(img) { - /* - * TODO: This is pretty gross. The Jabber PRPL really shouldn't - * do voodoo to try to determine the image type, height - * and width. - */ - /* A PNG header, including the IHDR, but nothing else */ - const struct { - guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */ - struct { - guint32 length; /* must be 0x0d */ - guchar type[4]; /* must be 'I' 'H' 'D' 'R' */ - guint32 width; - guint32 height; - guchar bitdepth; - guchar colortype; - guchar compression; - guchar filter; - guchar interlace; - } ihdr; - } *png = purple_imgstore_get_data(img); /* ATTN: this is in network byte order! */ - - /* check if the data is a valid png file (well, at least to some extend) */ - if(png->signature[0] == 0x89 && - png->signature[1] == 0x50 && - png->signature[2] == 0x4e && - png->signature[3] == 0x47 && - png->signature[4] == 0x0d && - png->signature[5] == 0x0a && - png->signature[6] == 0x1a && - png->signature[7] == 0x0a && - ntohl(png->ihdr.length) == 0x0d && - png->ihdr.type[0] == 'I' && - png->ihdr.type[1] == 'H' && - png->ihdr.type[2] == 'D' && - png->ihdr.type[3] == 'R') { - /* parse PNG header to get the size of the image (yes, this is required) */ - guint32 width = ntohl(png->ihdr.width); - guint32 height = ntohl(png->ihdr.height); - xmlnode *publish, *item, *data, *metadata, *info; - char *lengthstring, *widthstring, *heightstring; - - /* compute the sha1 hash */ - char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), purple_imgstore_get_size(img)); - char *base64avatar; - - publish = xmlnode_new("publish"); - xmlnode_set_attrib(publish,"node",AVATARNAMESPACEDATA); - - item = xmlnode_new_child(publish, "item"); - xmlnode_set_attrib(item, "id", hash); - - data = xmlnode_new_child(item, "data"); - xmlnode_set_namespace(data,AVATARNAMESPACEDATA); + PurpleAccount *account = purple_connection_get_account(gc); - base64avatar = purple_base64_encode(purple_imgstore_get_data(img), purple_imgstore_get_size(img)); - xmlnode_insert_data(data,base64avatar,-1); - g_free(base64avatar); - - /* publish the avatar itself */ - jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish); - - /* next step: publish the metadata */ - publish = xmlnode_new("publish"); - xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA); - - item = xmlnode_new_child(publish, "item"); - xmlnode_set_attrib(item, "id", hash); - - metadata = xmlnode_new_child(item, "metadata"); - xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA); - - info = xmlnode_new_child(metadata, "info"); - xmlnode_set_attrib(info, "id", hash); - xmlnode_set_attrib(info, "type", "image/png"); - lengthstring = g_strdup_printf("%u", (unsigned)purple_imgstore_get_size(img)); - xmlnode_set_attrib(info, "bytes", lengthstring); - g_free(lengthstring); - widthstring = g_strdup_printf("%u", width); - xmlnode_set_attrib(info, "width", widthstring); - g_free(widthstring); - heightstring = g_strdup_printf("%u", height); - xmlnode_set_attrib(info, "height", heightstring); - g_free(heightstring); + /* Publish the avatar as specified in XEP-0084 */ + jabber_avatar_set(gc->proto_data, img); + /* Set the image in our vCard */ + jabber_set_info(gc, purple_account_get_user_info(account)); - /* publish the metadata */ - jabber_pep_publish((JabberStream*)purple_connection_get_protocol_data(gc), publish); - - g_free(hash); - } else { - purple_debug_error("jabber", "jabber_set_buddy_icon received non-png data"); - } - } else { - /* remove the metadata */ - xmlnode *metadata, *item; - xmlnode *publish = xmlnode_new("publish"); - xmlnode_set_attrib(publish,"node",AVATARNAMESPACEMETA); - - item = xmlnode_new_child(publish, "item"); - - metadata = xmlnode_new_child(item, "metadata"); - xmlnode_set_namespace(metadata,AVATARNAMESPACEMETA); - - xmlnode_new_child(metadata, "stop"); - - /* publish the metadata */ - jabber_pep_publish((JabberStream*)gc->proto_data, publish); - } - } - - /* vCard avatars do not have an image type requirement so update our - * vCard avatar regardless of image type for those poor older clients - */ - jabber_set_info(gc, purple_account_get_user_info(gc->account)); - - jabber_presence_send(gc->proto_data, FALSE); + /* TODO: Fake image to ourselves, since a number of servers do not echo + * back our presence to us. To do this without uselessly copying the data + * of the image, we need purple_buddy_icons_set_for_user_image (i.e. takes + * an existing icon/stored image). */ } /* @@ -794,6 +680,21 @@ purple_notify_user_info_prepend_pair(user_info, _("Operating System"), jbr->client.os); } } + if (jbr && jbr->tz_off != PURPLE_NO_TZ_OFF) { + time_t now_t; + struct tm *now; + char *timestamp; + time(&now_t); + now_t += jbr->tz_off; + now = gmtime(&now_t); + + timestamp = g_strdup_printf("%s %c%02d%02d", purple_time_format(now), + jbr->tz_off < 0 ? '-' : '+', + abs(jbr->tz_off / (60*60)), + abs((jbr->tz_off % (60*60)) / 60)); + purple_notify_user_info_prepend_pair(user_info, _("Local Time"), timestamp); + g_free(timestamp); + } if(jbir) { if(jbir->idle_seconds > 0) { char *idle = purple_str_seconds_to_string(jbir->idle_seconds); @@ -912,7 +813,7 @@ feature = _("User Gaming"); else if(!strcmp(feature, "http://jabber.org/protocol/viewing")) feature = _("User Viewing"); - else if(!strcmp(feature, "urn:xmpp:ping") || !strcmp(feature, "http://www.xmpp.org/extensions/xep-0199.html#ns")) + else if(!strcmp(feature, "urn:xmpp:ping")) feature = _("Ping"); else if(!strcmp(feature, "http://www.xmpp.org/extensions/xep-0200.html#ns")) feature = _("Stanza Encryption"); @@ -964,6 +865,22 @@ } } + if (jbr->tz_off != PURPLE_NO_TZ_OFF) { + time_t now_t; + struct tm *now; + char *timestamp; + time(&now_t); + now_t += jbr->tz_off; + now = gmtime(&now_t); + + timestamp = g_strdup_printf("%s %c%02d%02d", purple_time_format(now), + jbr->tz_off < 0 ? '-' : '+', + abs(jbr->tz_off / (60*60)), + abs((jbr->tz_off % (60*60)) / 60)); + purple_notify_user_info_prepend_pair(user_info, _("Local Time"), timestamp); + g_free(timestamp); + } + if(jbr->name && (jbir = g_hash_table_lookup(jbi->resources, jbr->name))) { if(jbir->idle_seconds > 0) { char *idle = purple_str_seconds_to_string(jbir->idle_seconds); @@ -1082,7 +999,7 @@ feature = _("User Gaming"); else if(!strcmp(feature, "http://jabber.org/protocol/viewing")) feature = _("User Viewing"); - else if(!strcmp(feature, "urn:xmpp:ping") || !strcmp(feature, "http://www.xmpp.org/extensions/xep-0199.html#ns")) + else if(!strcmp(feature, "urn:xmpp:ping")) feature = _("Ping"); else if(!strcmp(feature, "http://www.xmpp.org/extensions/xep-0200.html#ns")) feature = _("Stanza Encryption"); @@ -1148,11 +1065,17 @@ } } -static void jabber_vcard_save_mine(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_vcard_save_mine(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - xmlnode *vcard; - char *txt; - PurpleStoredImage *img; + xmlnode *vcard, *photo, *binval; + char *txt, *vcard_hash = NULL; + + if (type == JABBER_IQ_ERROR) { + purple_debug_warning("jabber", "Server returned error while retrieving vCard"); + return; + } if((vcard = xmlnode_get_child(packet, "vCard")) || (vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) @@ -1167,10 +1090,29 @@ js->vcard_fetched = TRUE; - if(NULL != (img = purple_buddy_icons_find_account_icon(js->gc->account))) { - jabber_set_buddy_icon(js->gc, img); - purple_imgstore_unref(img); + if (vcard && (photo = xmlnode_get_child(vcard, "PHOTO")) && + (binval = xmlnode_get_child(photo, "BINVAL"))) { + gsize size; + char *bintext = xmlnode_get_data(binval); + guchar *data = purple_base64_decode(bintext, &size); + g_free(bintext); + + if (data) { + vcard_hash = jabber_calculate_data_sha1sum(data, size); + g_free(data); + } } + + /* Republish our vcard if the photo is different than the server's */ + if (!purple_strequal(vcard_hash, js->initial_avatar_hash)) { + PurpleAccount *account = purple_connection_get_account(js->gc); + jabber_set_info(js->gc, purple_account_get_user_info(account)); + } else if (js->initial_avatar_hash) { + /* Our photo is in the vcard, so advertise vcard-temp updates */ + js->avatar_hash = g_strdup(js->initial_avatar_hash); + } + + g_free(vcard_hash); } void jabber_vcard_fetch_mine(JabberStream *js) @@ -1184,9 +1126,10 @@ jabber_iq_send(iq); } -static void jabber_vcard_parse(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_vcard_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - const char *id, *from; char *bare_jid; char *text; char *serverside_alias = NULL; @@ -1195,9 +1138,6 @@ JabberBuddyInfo *jbi = data; PurpleNotifyUserInfo *user_info; - from = xmlnode_get_attrib(packet, "from"); - id = xmlnode_get_attrib(packet, "id"); - if(!jbi) return; @@ -1420,146 +1360,22 @@ jabber_buddy_info_show_if_ready(jbi); } -typedef struct _JabberBuddyAvatarUpdateURLInfo { - JabberStream *js; - char *from; - char *id; -} JabberBuddyAvatarUpdateURLInfo; - -static void do_buddy_avatar_update_fromurl(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message) { - JabberBuddyAvatarUpdateURLInfo *info = user_data; - if(!url_text) { - purple_debug(PURPLE_DEBUG_ERROR, "jabber", - "do_buddy_avatar_update_fromurl got error \"%s\"", error_message); - return; - } - - purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, (void*)url_text, len, info->id); - g_free(info->from); - g_free(info->id); - g_free(info); -} - -static void do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items) { - xmlnode *item, *data; - const char *checksum; - char *b64data; - void *img; - size_t size; - if(!items) - return; - - item = xmlnode_get_child(items, "item"); - if(!item) - return; - - data = xmlnode_get_child_with_namespace(item,"data",AVATARNAMESPACEDATA); - if(!data) - return; - - checksum = xmlnode_get_attrib(item,"id"); - if(!checksum) - return; - - b64data = xmlnode_get_data(data); - if(!b64data) - return; - - img = purple_base64_decode(b64data, &size); - if(!img) { - g_free(b64data); - return; - } - - purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum); - g_free(b64data); -} - -void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) { - PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from); - const char *checksum; - xmlnode *item, *metadata; - if(!buddy) - return; - - checksum = purple_buddy_icons_get_checksum_for_user(buddy); - item = xmlnode_get_child(items,"item"); - metadata = xmlnode_get_child_with_namespace(item, "metadata", AVATARNAMESPACEMETA); - if(!metadata) - return; - /* check if we have received a stop */ - if(xmlnode_get_child(metadata, "stop")) { - purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); - } else { - xmlnode *info, *goodinfo = NULL; - gboolean has_children = FALSE; - - /* iterate over all info nodes to get one we can use */ - for(info = metadata->child; info; info = info->next) { - if(info->type == XMLNODE_TYPE_TAG) - has_children = TRUE; - if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) { - const char *type = xmlnode_get_attrib(info,"type"); - const char *id = xmlnode_get_attrib(info,"id"); - - if(checksum && id && !strcmp(id, checksum)) { - /* we already have that avatar, so we don't have to do anything */ - goodinfo = NULL; - break; - } - /* We'll only pick the png one for now. It's a very nice image format anyways. */ - if(type && id && !goodinfo && !strcmp(type, "image/png")) - goodinfo = info; - } - } - if(has_children == FALSE) { - purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); - } else if(goodinfo) { - const char *url = xmlnode_get_attrib(goodinfo, "url"); - const char *id = xmlnode_get_attrib(goodinfo,"id"); - - /* the avatar might either be stored in a pep node, or on a HTTP/HTTPS URL */ - if(!url) - jabber_pep_request_item(js, from, AVATARNAMESPACEDATA, id, do_buddy_avatar_update_data); - else { - PurpleUtilFetchUrlData *url_data; - JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1); - info->js = js; - - url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE, - MAX_HTTP_BUDDYICON_BYTES, - do_buddy_avatar_update_fromurl, info); - if (url_data) { - info->from = g_strdup(from); - info->id = g_strdup(id); - js->url_datas = g_slist_prepend(js->url_datas, url_data); - } else - g_free(info); - - } - } - } -} - static void jabber_buddy_info_resource_free(gpointer data) { JabberBuddyInfoResource *jbri = data; g_free(jbri); } -static void jabber_version_parse(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_version_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JabberBuddyInfo *jbi = data; - const char *type, *id, *from; xmlnode *query; char *resource_name; g_return_if_fail(jbi != NULL); - type = xmlnode_get_attrib(packet, "type"); - id = xmlnode_get_attrib(packet, "id"); - from = xmlnode_get_attrib(packet, "from"); - jabber_buddy_info_remove_id(jbi, id); if(!from) @@ -1568,7 +1384,7 @@ resource_name = jabber_get_resource(from); if(resource_name) { - if(type && !strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { if((query = xmlnode_get_child(packet, "query"))) { JabberBuddyResource *jbr = jabber_buddy_find_resource(jbi->jb, resource_name); if(jbr) { @@ -1591,19 +1407,17 @@ jabber_buddy_info_show_if_ready(jbi); } -static void jabber_last_parse(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_last_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JabberBuddyInfo *jbi = data; xmlnode *query; char *resource_name; - const char *type, *id, *from, *seconds; + const char *seconds; g_return_if_fail(jbi != NULL); - type = xmlnode_get_attrib(packet, "type"); - id = xmlnode_get_attrib(packet, "id"); - from = xmlnode_get_attrib(packet, "from"); - jabber_buddy_info_remove_id(jbi, id); if(!from) @@ -1612,18 +1426,55 @@ resource_name = jabber_get_resource(from); if(resource_name) { - if(type && !strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { if((query = xmlnode_get_child(packet, "query"))) { seconds = xmlnode_get_attrib(query, "seconds"); if(seconds) { char *end = NULL; long sec = strtol(seconds, &end, 10); - if(end != seconds) { + JabberBuddy *jb = NULL; + char *resource = NULL; + char *buddy_name = NULL; + JabberBuddyResource *jbr = NULL; + + if(end != seconds) { JabberBuddyInfoResource *jbir = g_hash_table_lookup(jbi->resources, resource_name); if(jbir) { jbir->idle_seconds = sec; } } + /* Update the idle time of the buddy resource, if we got it. + This will correct the value when a server doesn't mark + delayed presence and we got the presence when signing on */ + jb = jabber_buddy_find(js, from, FALSE); + if (jb) { + resource = jabber_get_resource(from); + buddy_name = jabber_get_bare_jid(from); + /* if the resource already has an idle time set, we + must have gotten it originally from a presence. In + this case we update it. Otherwise don't update it, to + avoid setting an idle and not getting informed about + the resource getting unidle */ + if (resource && buddy_name) { + jbr = jabber_buddy_find_resource(jb, resource); + + if (jbr->idle) { + if (sec) { + jbr->idle = time(NULL) - sec; + } else { + jbr->idle = 0; + } + + if (jbr == + jabber_buddy_find_resource(jb, NULL)) { + purple_prpl_got_user_idle(js->gc->account, + buddy_name, jbr->idle, jbr->idle); + } + } + } + g_free(resource); + g_free(buddy_name); + } } } } @@ -1633,6 +1484,56 @@ jabber_buddy_info_show_if_ready(jbi); } +static void jabber_time_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ + JabberBuddyInfo *jbi = data; + JabberBuddyResource *jbr; + char *resource_name; + + g_return_if_fail(jbi != NULL); + + jabber_buddy_info_remove_id(jbi, id); + + if (!from) + return; + + resource_name = jabber_get_resource(from); + jbr = resource_name ? jabber_buddy_find_resource(jbi->jb, resource_name) : NULL; + g_free(resource_name); + if (jbr) { + if (type == JABBER_IQ_RESULT) { + xmlnode *time = xmlnode_get_child(packet, "time"); + xmlnode *tzo = time ? xmlnode_get_child(time, "tzo") : NULL; + char *tzo_data = tzo ? xmlnode_get_data(tzo) : NULL; + if (tzo_data) { + char *c = tzo_data; + int hours, minutes; + if (tzo_data[0] == 'Z' && tzo_data[1] == '\0') { + jbr->tz_off = 0; + } else { + gboolean offset_positive = (tzo_data[0] == '+'); + /* [+-]HH:MM */ + if (((*c == '+' || *c == '-') && (c = c + 1)) && + sscanf(c, "%02d:%02d", &hours, &minutes) == 2) { + jbr->tz_off = 60*60*hours + 60*minutes; + if (!offset_positive) + jbr->tz_off *= -1; + } else { + purple_debug_info("jabber", "Ignoring malformed timezone %s", + tzo_data); + } + } + + g_free(tzo_data); + } + } + } + + jabber_buddy_info_show_if_ready(jbi); +} + void jabber_buddy_remove_all_pending_buddy_info_requests(JabberStream *js) { if (js->pending_buddy_info_requests) @@ -1764,11 +1665,24 @@ jabber_iq_send(iq); } + if (jbr->tz_off == PURPLE_NO_TZ_OFF && + (!jbr->caps.info || + jabber_resource_has_capability(jbr, "urn:xmpp:time"))) { + xmlnode *child; + iq = jabber_iq_new(js, JABBER_IQ_GET); + xmlnode_set_attrib(iq->node, "to", full_jid); + child = xmlnode_new_child(iq->node, "time"); + xmlnode_set_namespace(child, "urn:xmpp:time"); + jabber_iq_set_callback(iq, jabber_time_parse, jbi); + jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id)); + jabber_iq_send(iq); + } + g_free(full_jid); } js->pending_buddy_info_requests = g_slist_prepend(js->pending_buddy_info_requests, jbi); - jbi->timeout_handle = purple_timeout_add(30000, jabber_buddy_get_info_timeout, jbi); + jbi->timeout_handle = purple_timeout_add_seconds(30, jabber_buddy_get_info_timeout, jbi); } void jabber_buddy_get_info(PurpleConnection *gc, const char *who) @@ -2160,7 +2074,9 @@ g_list_nth_data(row, 0), NULL, NULL); } -static void user_search_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void user_search_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { PurpleNotifySearchResults *results; PurpleNotifySearchColumn *column; @@ -2356,15 +2272,16 @@ }; #endif -static void user_search_fields_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void user_search_fields_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { xmlnode *query, *x; - const char *from, *type; - if(!(from = xmlnode_get_attrib(packet, "from"))) + if (!from) return; - if(!(type = xmlnode_get_attrib(packet, "type")) || !strcmp(type, "error")) { + if (type == JABBER_IQ_ERROR) { char *msg = jabber_parse_error(js, packet, NULL); if(!msg)
--- a/libpurple/protocols/jabber/buddy.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/buddy.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_BUDDY_H_ -#define _PURPLE_JABBER_BUDDY_H_ +#ifndef PURPLE_JABBER_BUDDY_H_ +#define PURPLE_JABBER_BUDDY_H_ typedef enum { JABBER_BUDDY_STATE_UNKNOWN = -2, @@ -36,9 +36,6 @@ #include "jabber.h" #include "caps.h" -#define AVATARNAMESPACEDATA "http://www.xmpp.org/extensions/xep-0084.html#ns-data" -#define AVATARNAMESPACEMETA "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata" - typedef struct _JabberBuddy { GList *resources; char *error_msg; @@ -69,6 +66,7 @@ int priority; JabberBuddyState state; char *status; + time_t idle; JabberCapabilities capabilities; char *thread_id; enum { @@ -81,6 +79,8 @@ char *name; char *os; } client; + /* tz_off == PURPLE_NO_TZ_OFF when unset */ + long tz_off; struct { JabberCapsClientInfo *info; GList *exts; @@ -97,7 +97,6 @@ int priority, JabberBuddyState state, const char *status); void jabber_buddy_resource_free(JabberBuddyResource *jbr); void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource); -const char *jabber_buddy_get_status_msg(JabberBuddy *jb); void jabber_buddy_get_info(PurpleConnection *gc, const char *who); GList *jabber_blist_node_menu(PurpleBlistNode *node); @@ -105,7 +104,6 @@ void jabber_set_info(PurpleConnection *gc, const char *info); void jabber_setup_set_info(PurplePluginAction *action); void jabber_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img); -void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items); const char *jabber_buddy_state_get_name(JabberBuddyState state); const char *jabber_buddy_state_get_status_id(JabberBuddyState state); @@ -124,4 +122,4 @@ const gchar *cap); gboolean jabber_buddy_has_capability(const JabberBuddy *jb, const gchar *cap); -#endif /* _PURPLE_JABBER_BUDDY_H_ */ +#endif /* PURPLE_JABBER_BUDDY_H_ */
--- a/libpurple/protocols/jabber/caps.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/caps.c Tue Apr 28 19:08:06 2009 +0000 @@ -89,9 +89,11 @@ const JabberCapsKey *key = data; guint nodehash = g_str_hash(key->node); guint verhash = g_str_hash(key->ver); - /* 'hash' was optional in XEP-0115 v1.4 and I think g_str_hash crashes on - * NULL >:O. Okay, maybe I've played too much Zelda, but that looks like - * a Deku Shrub... */ + /* + * 'hash' was optional in XEP-0115 v1.4 and g_str_hash crashes on NULL >:O. + * Okay, maybe I've played too much Zelda, but that looks like + * a Deku Shrub... + */ guint hashhash = (key->hash ? g_str_hash(key->hash) : 0); return nodehash ^ verhash ^ hashhash; } @@ -99,13 +101,10 @@ static gboolean jabber_caps_compare(gconstpointer v1, gconstpointer v2) { const JabberCapsKey *name1 = v1; const JabberCapsKey *name2 = v2; - /* Again, hash might be NULL and I *know* strcmp will crash on NULL. */ - gboolean hasheq = ((!name1->hash && !name2->hash) || - (name1->hash && name2->hash && !strcmp(name1->hash, name2->hash))); - return strcmp(name1->node, name2->node) == 0 && - strcmp(name1->ver, name2->ver) == 0 && - hasheq; + return g_str_equal(name1->node, name2->node) && + g_str_equal(name1->ver, name2->ver) && + purple_strequal(name1->hash, name2->hash); } void jabber_caps_destroy_key(gpointer data) { @@ -247,7 +246,7 @@ xmlnode_free(capsdata); return; } - + for(client = capsdata->child; client; client = client->next) { if(client->type != XMLNODE_TYPE_TAG) continue; @@ -417,16 +416,16 @@ } static void -jabber_caps_client_iqcb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_caps_client_iqcb(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *packet, gpointer data) { xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#info"); jabber_caps_cbplususerdata *userdata = data; JabberCapsClientInfo *info = NULL, *value; - const char *type = xmlnode_get_attrib(packet, "type"); JabberCapsKey key; - if (!query || !strcmp(type, "error")) { + if (!query || type == JABBER_IQ_ERROR) { /* Any outstanding exts will be dealt with via ref-counting */ userdata->cb(NULL, NULL, userdata->cb_data); cbplususerdata_unref(userdata); @@ -501,17 +500,17 @@ } ext_iq_data; static void -jabber_caps_ext_iqcb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_caps_ext_iqcb(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *packet, gpointer data) { xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/disco#info"); xmlnode *child; ext_iq_data *userdata = data; - const char *type = xmlnode_get_attrib(packet, "type"); GList *features = NULL; JabberCapsNodeExts *node_exts; - if (!query || !strcmp(type, "error")) { + if (!query || type == JABBER_IQ_ERROR) { cbplususerdata_unref(userdata->data); g_free(userdata); return; @@ -552,7 +551,7 @@ jabber_caps_cbplususerdata *userdata; if (ext && *ext && hash) - purple_debug_warning("jabber", "Ignoring exts in new-style caps from %s\n", + purple_debug_info("jabber", "Ignoring exts in new-style caps from %s\n", who); /* Using this in a read-only fashion, so the cast is OK */
--- a/libpurple/protocols/jabber/caps.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/caps.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * */ -#ifndef _PURPLE_JABBER_CAPS_H_ -#define _PURPLE_JABBER_CAPS_H_ +#ifndef PURPLE_JABBER_CAPS_H_ +#define PURPLE_JABBER_CAPS_H_ typedef struct _JabberCapsClientInfo JabberCapsClientInfo; @@ -99,4 +99,4 @@ */ void jabber_caps_broadcast_change(void); -#endif /* _PURPLE_JABBER_CAPS_H_ */ +#endif /* PURPLE_JABBER_CAPS_H_ */
--- a/libpurple/protocols/jabber/chat.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/chat.c Tue Apr 28 19:08:06 2009 +0000 @@ -376,21 +376,19 @@ jabber_iq_send(iq); } -static void jabber_chat_room_configure_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_chat_room_configure_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { xmlnode *query, *x; - const char *type = xmlnode_get_attrib(packet, "type"); - const char *from = xmlnode_get_attrib(packet, "from"); char *msg; JabberChat *chat; JabberID *jid; - if(!type || !from) + if (!from) return; - - - if(!strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { jid = jabber_id_new(from); if(!jid) @@ -416,7 +414,7 @@ return; } } - } else if(!strcmp(type, "error")) { + } else if (type == JABBER_IQ_ERROR) { char *msg = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Configuration error"), _("Configuration error"), msg); @@ -486,11 +484,12 @@ g_free(room_jid); } -static void jabber_chat_register_x_data_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void +jabber_chat_register_x_data_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - const char *type = xmlnode_get_attrib(packet, "type"); - - if(type && !strcmp(type, "error")) { + if (type == JABBER_IQ_ERROR) { char *msg = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg); @@ -521,19 +520,19 @@ jabber_iq_send(iq); } -static void jabber_chat_register_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_chat_register_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { xmlnode *query, *x; - const char *type = xmlnode_get_attrib(packet, "type"); - const char *from = xmlnode_get_attrib(packet, "from"); char *msg; JabberChat *chat; JabberID *jid; - if(!type || !from) + if (!from) return; - if(!strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { jid = jabber_id_new(from); if(!jid) @@ -559,7 +558,7 @@ return; } } - } else if(!strcmp(type, "error")) { + } else if (type == JABBER_IQ_ERROR) { char *msg = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Registration error"), _("Registration error"), msg); @@ -598,37 +597,25 @@ /* merge this with the function below when we get everyone on the same page wrt /commands */ void jabber_chat_change_topic(JabberChat *chat, const char *topic) { - if(topic && *topic) { - JabberMessage *jm; - jm = g_new0(JabberMessage, 1); - jm->js = chat->js; - jm->type = JABBER_MESSAGE_GROUPCHAT; - jm->subject = purple_markup_strip_html(topic); - jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); - jabber_message_send(jm); - jabber_message_free(jm); - } else { - const char *cur = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(chat->conv)); - char *buf, *tmp, *tmp2; + JabberMessage *jm; + + jm = g_new0(JabberMessage, 1); + jm->js = chat->js; + jm->type = JABBER_MESSAGE_GROUPCHAT; + jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); - if(cur) { - tmp = g_markup_escape_text(cur, -1); - tmp2 = purple_markup_linkify(tmp); - buf = g_strdup_printf(_("current topic is: %s"), tmp2); - g_free(tmp); - g_free(tmp2); - } else - buf = g_strdup(_("No topic is set")); - purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), "", buf, - PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL)); - g_free(buf); - } + if (topic && *topic) + jm->subject = purple_markup_strip_html(topic); + else + jm->subject = g_strdup(""); + jabber_message_send(jm); + jabber_message_free(jm); } void jabber_chat_set_topic(PurpleConnection *gc, int id, const char *topic) { - JabberStream *js = gc->proto_data; + JabberStream *js = purple_connection_get_protocol_data(gc); JabberChat *chat = jabber_chat_find_by_id(js, id); if(!chat) @@ -690,16 +677,17 @@ g_free(room_jid); } -static void roomlist_disco_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void roomlist_disco_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { xmlnode *query; xmlnode *item; - const char *type; if(!js->roomlist) return; - if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) { + if (type == JABBER_IQ_ERROR) { char *err = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Error"), _("Error retrieving room list"), err); @@ -988,13 +976,17 @@ return TRUE; } -static void jabber_chat_disco_traffic_cb(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_chat_disco_traffic_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JabberChat *chat; - /*xmlnode *query;*/ - int id = GPOINTER_TO_INT(data); +#if 0 + xmlnode *query, *x; +#endif + int chat_id = GPOINTER_TO_INT(data); - if(!(chat = jabber_chat_find_by_id(js, id))) + if(!(chat = jabber_chat_find_by_id(js, chat_id))) return; /* defaults, in case the conference server doesn't @@ -1002,8 +994,9 @@ chat->xhtml = TRUE; /* disabling this until more MUC servers support - * announcing this - if(xmlnode_get_child(packet, "error")) { + * announcing this */ +#if 0 + if (type == JABBER_IQ_ERROR) { return; } @@ -1019,7 +1012,7 @@ chat->xhtml = TRUE; } } - */ +#endif } void jabber_chat_disco_traffic(JabberChat *chat)
--- a/libpurple/protocols/jabber/chat.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/chat.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_CHAT_H_ -#define _PURPLE_JABBER_CHAT_H_ +#ifndef PURPLE_JABBER_CHAT_H_ +#define PURPLE_JABBER_CHAT_H_ #include "internal.h" #include "connection.h" @@ -94,4 +94,4 @@ char *jabber_roomlist_room_serialize(PurpleRoomlistRoom *room); -#endif /* _PURPLE_JABBER_CHAT_H_ */ +#endif /* PURPLE_JABBER_CHAT_H_ */
--- a/libpurple/protocols/jabber/data.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/data.c Tue Apr 28 19:08:06 2009 +0000 @@ -200,25 +200,26 @@ } void -jabber_data_parse(JabberStream *js, xmlnode *packet) +jabber_data_parse(JabberStream *js, const char *who, JabberIqType type, + const char *id, xmlnode *data_node) { JabberIq *result = NULL; - const char *who = xmlnode_get_attrib(packet, "from"); - xmlnode *data_node = xmlnode_get_child(packet, "data"); - const JabberData *data = - jabber_data_find_local_by_cid(xmlnode_get_attrib(data_node, "cid")); + const char *cid = xmlnode_get_attrib(data_node, "cid"); + const JabberData *data = cid ? jabber_data_find_local_by_cid(cid) : NULL; if (!data) { xmlnode *item_not_found = xmlnode_new("item-not-found"); result = jabber_iq_new(js, JABBER_IQ_ERROR); - xmlnode_set_attrib(result->node, "to", who); - xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + if (who) + xmlnode_set_attrib(result->node, "to", who); + xmlnode_set_attrib(result->node, "id", id); xmlnode_insert_child(result->node, item_not_found); } else { result = jabber_iq_new(js, JABBER_IQ_RESULT); - xmlnode_set_attrib(result->node, "to", who); - xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id")); + if (who) + xmlnode_set_attrib(result->node, "to", who); + xmlnode_set_attrib(result->node, "id", id); xmlnode_insert_child(result->node, jabber_data_get_xml_definition(data)); } @@ -235,6 +236,8 @@ g_free, jabber_data_delete); remote_data_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_data_delete); + + jabber_iq_register_handler("data", XEP_0231_NAMESPACE, jabber_data_parse); } void
--- a/libpurple/protocols/jabber/data.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/data.h Tue Apr 28 19:08:06 2009 +0000 @@ -14,8 +14,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ -#ifndef JABBER_DATA_H -#define JABBER_DATA_H +#ifndef PURPLE_JABBER_DATA_H +#define PURPLE_JABBER_DATA_H #include "xmlnode.h" #include "jabber.h" @@ -65,9 +65,10 @@ void jabber_data_associate_remote(JabberData *data); /* handles iq requests */ -void jabber_data_parse(JabberStream *js, xmlnode *packet); +void jabber_data_parse(JabberStream *js, const char *who, JabberIqType type, + const char *id, xmlnode *data_node); void jabber_data_init(void); void jabber_data_uninit(void); -#endif /* JABBER_DATA_H */ +#endif /* PURPLE_JABBER_DATA_H */
--- a/libpurple/protocols/jabber/disco.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/disco.c Tue Apr 28 19:08:06 2009 +0000 @@ -23,17 +23,17 @@ #include "prefs.h" #include "debug.h" +#include "adhoccommands.h" #include "buddy.h" +#include "disco.h" #include "google.h" #include "iq.h" -#include "disco.h" #include "jabber.h" #include "jingle/jingle.h" +#include "pep.h" #include "presence.h" #include "roster.h" -#include "pep.h" -#include "adhoccommands.h" - +#include "useravatar.h" struct _jabber_disco_info_cb_data { gpointer data; @@ -46,9 +46,11 @@ } static void -jabber_disco_bytestream_server_cb(JabberStream *js, xmlnode *packet, gpointer data) { +jabber_disco_bytestream_server_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ JabberBytestreamsStreamhost *sh = data; - const char *from = xmlnode_get_attrib(packet, "from"); xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", "http://jabber.org/protocol/bytestreams"); @@ -86,33 +88,27 @@ } -void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) { - const char *from = xmlnode_get_attrib(packet, "from"); - const char *type = xmlnode_get_attrib(packet, "type"); +void jabber_disco_info_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *in_query) +{ - if(!from || !type) + if(!from) return; - if(!strcmp(type, "get")) { + if(type == JABBER_IQ_GET) { xmlnode *query, *identity, *feature; JabberIq *iq; + const char *node = xmlnode_get_attrib(in_query, "node"); + char *node_uri = NULL; - xmlnode *in_query; - const char *node = NULL; - char *node_uri = NULL; - /* create custom caps node URI */ node_uri = g_strconcat(CAPS0115_NODE, "#", jabber_caps_get_own_hash(js), NULL); - if((in_query = xmlnode_get_child(packet, "query"))) { - node = xmlnode_get_attrib(in_query, "node"); - } - - iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "http://jabber.org/protocol/disco#info"); - jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id")); + jabber_iq_set_id(iq, id); xmlnode_set_attrib(iq->node, "to", from); query = xmlnode_get_child(iq->node, "query"); @@ -146,8 +142,22 @@ if (!feat->is_enabled || feat->is_enabled(js, feat->namespace)) { feature = xmlnode_new_child(query, "feature"); xmlnode_set_attrib(feature, "var", feat->namespace); - } + } } +#ifdef USE_VV + } else if (g_str_equal(node, CAPS0115_NODE "#" "voice-v1")) { + /* + * HUGE HACK! We advertise this ext (see jabber_presence_create_js + * where we add <c/> to the <presence/>) for the Google Talk + * clients that don't actually check disco#info features. + * + * This specific feature is redundant but is what + * node='http://mail.google.com/xmpp/client/caps', ver='1.1' + * advertises as 'voice-v1'. + */ + xmlnode *feature = xmlnode_new_child(query, "feature"); + xmlnode_set_attrib(feature, "var", "http://www.google.com/xmpp/protocol/voice/v1"); +#endif } else { xmlnode *error, *inf; @@ -163,8 +173,7 @@ } g_free(node_uri); jabber_iq_send(iq); - } else if(!strcmp(type, "result")) { - xmlnode *query = xmlnode_get_child(packet, "query"); + } else if(type == JABBER_IQ_RESULT) { xmlnode *child; JabberID *jid; JabberBuddy *jb; @@ -181,7 +190,7 @@ if(jbr) capabilities = jbr->capabilities; - for(child = query->child; child; child = child->next) { + for(child = in_query->child; child; child = child->next) { if(child->type != XMLNODE_TYPE_TAG) continue; @@ -231,7 +240,7 @@ capabilities |= JABBER_CAP_IQ_SEARCH; else if(!strcmp(var, "jabber:iq:register")) capabilities |= JABBER_CAP_IQ_REGISTER; - else if(!strcmp(var, "http://www.xmpp.org/extensions/xep-0199.html#ns")) + else if(!strcmp(var, "urn:xmpp:ping")) capabilities |= JABBER_CAP_PING; else if(!strcmp(var, "http://jabber.org/protocol/commands")) { capabilities |= JABBER_CAP_ADHOC; @@ -252,7 +261,7 @@ jdicd->callback(js, from, capabilities, jdicd->data); g_hash_table_remove(js->disco_callbacks, from); } - } else if(!strcmp(type, "error")) { + } else if(type == JABBER_IQ_ERROR) { JabberID *jid; JabberBuddy *jb; JabberBuddyResource *jbr = NULL; @@ -276,28 +285,24 @@ } } -void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) { - const char *from = xmlnode_get_attrib(packet, "from"); - const char *type = xmlnode_get_attrib(packet, "type"); - - if(type && !strcmp(type, "get")) { +void jabber_disco_items_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *query) +{ + if(type == JABBER_IQ_GET) { JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "http://jabber.org/protocol/disco#items"); /* preserve node */ - xmlnode *iq_query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items"); - if(iq_query) { - xmlnode *query = xmlnode_get_child_with_namespace(packet,"query","http://jabber.org/protocol/disco#items"); - if(query) { - const char *node = xmlnode_get_attrib(query,"node"); - if(node) - xmlnode_set_attrib(iq_query,"node",node); - } - } + xmlnode *iq_query = xmlnode_get_child(iq->node, "query"); + const char *node = xmlnode_get_attrib(query, "node"); + if(node) + xmlnode_set_attrib(iq_query,"node",node); - jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id")); + jabber_iq_set_id(iq, id); - xmlnode_set_attrib(iq->node, "to", from); + if (from) + xmlnode_set_attrib(iq->node, "to", from); jabber_iq_send(iq); } } @@ -307,8 +312,16 @@ { const char *ft_proxies; + /* + * This *should* happen only if the server supports vcard-temp, but there + * are apparently some servers that don't advertise it even though they + * support it. + */ jabber_vcard_fetch_mine(js); + if (js->pep) + jabber_avatar_fetch_mine(js); + if (!(js->server_caps & JABBER_CAP_GOOGLE_ROSTER)) { /* If the server supports JABBER_CAP_GOOGLE_ROSTER; we will have already requested it */ jabber_roster_request(js); @@ -359,19 +372,18 @@ } static void -jabber_disco_server_info_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_disco_server_info_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { xmlnode *query, *child; - const char *from = xmlnode_get_attrib(packet, "from"); - const char *type = xmlnode_get_attrib(packet, "type"); - if((!from || !type) || - (strcmp(from, js->user->domain))) { + if (!from || strcmp(from, js->user->domain)) { jabber_disco_finish_server_info_result_cb(js); return; } - if(strcmp(type, "result")) { + if (type == JABBER_IQ_ERROR) { /* A common way to get here is for the server not to support xmlns http://jabber.org/protocol/disco#info */ jabber_disco_finish_server_info_result_cb(js); return; @@ -437,19 +449,16 @@ } static void -jabber_disco_server_items_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_disco_server_items_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { xmlnode *query, *child; - const char *from = xmlnode_get_attrib(packet, "from"); - const char *type = xmlnode_get_attrib(packet, "type"); - if(!from || !type) + if (!from || strcmp(from, js->user->domain) != 0) return; - if(strcmp(from, js->user->domain)) - return; - - if(strcmp(type, "result")) + if (type == JABBER_IQ_ERROR) return; while(js->chat_servers) { @@ -524,5 +533,3 @@ jabber_iq_send(iq); } - -
--- a/libpurple/protocols/jabber/disco.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/disco.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,20 +19,22 @@ * 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_JABBER_DISCO_H_ -#define _PURPLE_JABBER_DISCO_H_ +#ifndef PURPLE_JABBER_DISCO_H_ +#define PURPLE_JABBER_DISCO_H_ #include "jabber.h" typedef void (JabberDiscoInfoCallback)(JabberStream *js, const char *who, JabberCapabilities capabilities, gpointer data); -void jabber_disco_info_parse(JabberStream *js, xmlnode *packet); -void jabber_disco_items_parse(JabberStream *js, xmlnode *packet); +void jabber_disco_info_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *in_query); +void jabber_disco_items_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *query); void jabber_disco_items_server(JabberStream *js); void jabber_disco_info_do(JabberStream *js, const char *who, JabberDiscoInfoCallback *callback, gpointer data); -#endif /* _PURPLE_JABBER_DISCO_H_ */ +#endif /* PURPLE_JABBER_DISCO_H_ */
--- a/libpurple/protocols/jabber/google.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/google.c Tue Apr 28 19:08:06 2009 +0000 @@ -340,7 +340,8 @@ session->remote_jid = jid; session->media = purple_media_manager_create_media( - purple_media_manager_get(), js->gc, + purple_media_manager_get(), + purple_connection_get_account(js->gc), "fsrtpconference", session->remote_jid, TRUE); purple_media_set_prpl_data(session->media, session); @@ -374,7 +375,7 @@ } static void -google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { JabberIq *result; GList *codecs = NULL; @@ -389,8 +390,10 @@ return; } - session->media = purple_media_manager_create_media(purple_media_manager_get(), js->gc, - "fsrtpconference", session->remote_jid, FALSE); + session->media = purple_media_manager_create_media( + purple_media_manager_get(), + purple_connection_get_account(js->gc), + "fsrtpconference", session->remote_jid, FALSE); purple_media_set_prpl_data(session->media, session); @@ -440,13 +443,13 @@ purple_media_codec_list_free(codecs); result = jabber_iq_new(js, JABBER_IQ_RESULT); - jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + jabber_iq_set_id(result, iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); jabber_iq_send(result); } static void -google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { JabberIq *result; GList *list = NULL; @@ -480,13 +483,13 @@ purple_media_candidate_list_free(list); result = jabber_iq_new(js, JABBER_IQ_RESULT); - jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + jabber_iq_set_id(result, iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); jabber_iq_send(result); } static void -google_session_handle_accept(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +google_session_handle_accept(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { xmlnode *desc_element = xmlnode_get_child(sess, "description"); xmlnode *codec_element = xmlnode_get_child(desc_element, "payload-type"); @@ -514,58 +517,54 @@ NULL, NULL, FALSE); result = jabber_iq_new(js, JABBER_IQ_RESULT); - jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + jabber_iq_set_id(result, iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); jabber_iq_send(result); } static void -google_session_handle_reject(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +google_session_handle_reject(JabberStream *js, GoogleSession *session, xmlnode *sess) { purple_media_end(session->media, NULL, NULL); } static void -google_session_handle_terminate(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess) +google_session_handle_terminate(JabberStream *js, GoogleSession *session, xmlnode *sess) { purple_media_end(session->media, NULL, NULL); } static void -google_session_parse_iq(JabberStream *js, GoogleSession *session, xmlnode *packet) +google_session_parse_iq(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { - xmlnode *sess = xmlnode_get_child(packet, "session"); const char *type = xmlnode_get_attrib(sess, "type"); if (!strcmp(type, "initiate")) { - google_session_handle_initiate(js, session, packet, sess); + google_session_handle_initiate(js, session, sess, iq_id); } else if (!strcmp(type, "accept")) { - google_session_handle_accept(js, session, packet, sess); + google_session_handle_accept(js, session, sess, iq_id); } else if (!strcmp(type, "reject")) { - google_session_handle_reject(js, session, packet, sess); + google_session_handle_reject(js, session, sess); } else if (!strcmp(type, "terminate")) { - google_session_handle_terminate(js, session, packet, sess); + google_session_handle_terminate(js, session, sess); } else if (!strcmp(type, "candidates")) { - google_session_handle_candidates(js, session, packet, sess); + google_session_handle_candidates(js, session, sess, iq_id); } } void -jabber_google_session_parse(JabberStream *js, xmlnode *packet) +jabber_google_session_parse(JabberStream *js, const char *from, + JabberIqType type, const char *iq_id, + xmlnode *session_node) { GoogleSession *session = NULL; GoogleSessionId id; - xmlnode *session_node; xmlnode *desc_node; GList *iter = NULL; - if (strcmp(xmlnode_get_attrib(packet, "type"), "set")) - return; - - session_node = xmlnode_get_child(packet, "session"); - if (!session_node) + if (type != JABBER_IQ_SET) return; id.id = (gchar*)xmlnode_get_attrib(session_node, "id"); @@ -576,8 +575,9 @@ if (!id.initiator) return; - iter = purple_media_manager_get_media_by_connection( - purple_media_manager_get(), js->gc); + iter = purple_media_manager_get_media_by_account( + purple_media_manager_get(), + purple_connection_get_account(js->gc)); for (; iter; iter = g_list_delete_link(iter, iter)) { GoogleSession *gsession = purple_media_get_prpl_data(iter->data); @@ -591,7 +591,7 @@ } if (session) { - google_session_parse_iq(js, session, packet); + google_session_parse_iq(js, session, session_node, iq_id); return; } @@ -608,18 +608,18 @@ session->js = js; session->remote_jid = g_strdup(session->id.initiator); - google_session_parse_iq(js, session, packet); + google_session_parse_iq(js, session, session_node, iq_id); } #endif /* USE_VV */ static void -jabber_gmail_parse(JabberStream *js, xmlnode *packet, gpointer nul) +jabber_gmail_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer nul) { - const char *type = xmlnode_get_attrib(packet, "type"); xmlnode *child; - xmlnode *message, *sender_node, *subject_node; - const char *from, *to, *url, *tid; - char *subject; + xmlnode *message; + const char *to, *url; const char *in_str; char *to_name; char *default_tos[1]; @@ -629,7 +629,7 @@ const char **tos, **froms, **urls; char **subjects; - if (strcmp(type, "result")) + if (type == JABBER_IQ_ERROR) return; child = xmlnode_get_child(packet, "mailbox"); @@ -670,6 +670,10 @@ message= xmlnode_get_child(child, "mail-thread-info"); for (i=0; message; message = xmlnode_get_next_twin(message), i++) { + xmlnode *sender_node, *subject_node; + const char *from, *tid; + char *subject; + subject_node = xmlnode_get_child(message, "subject"); sender_node = xmlnode_get_child(message, "senders"); sender_node = xmlnode_get_child(sender_node, "sender"); @@ -727,9 +731,9 @@ } void -jabber_gmail_poke(JabberStream *js, xmlnode *packet) +jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *new_mail) { - const char *type; xmlnode *query; JabberIq *iq; @@ -737,11 +741,8 @@ if (!purple_account_get_check_mail(js->gc->account)) return; - type = xmlnode_get_attrib(packet, "type"); - - /* Is this an initial incoming mail notification? If so, send a request for more info */ - if (strcmp(type, "set") || !xmlnode_get_child(packet, "new-mail")) + if (type != JABBER_IQ_SET) return; purple_debug(PURPLE_DEBUG_MISC, "jabber", @@ -1155,52 +1156,86 @@ } } - g_slist_free(hosts); + while (hosts != NULL) { + hosts = g_slist_delete_link(hosts, hosts); + /* Free the address */ + g_free(hosts->data); + hosts = g_slist_delete_link(hosts, hosts); + } } static void -jabber_google_jingle_info_cb(JabberStream *js, xmlnode *result, - gpointer nullus) -{ - if (result) { - const xmlnode *query = - xmlnode_get_child_with_namespace(result, "query", - GOOGLE_JINGLE_INFO_NAMESPACE); +jabber_google_jingle_info_common(JabberStream *js, const char *from, + JabberIqType type, xmlnode *query) +{ + const xmlnode *stun = xmlnode_get_child(query, "stun"); + gchar *my_bare_jid; - if (query) { - const xmlnode *stun = xmlnode_get_child(query, "stun"); + /* + * Make sure that random people aren't sending us STUN servers. Per + * http://code.google.com/apis/talk/jep_extensions/jingleinfo.html, these + * stanzas are stamped from our bare JID. + */ + if (from) { + my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); + if (!purple_strequal(from, my_bare_jid)) { + purple_debug_warning("jabber", "got google:jingleinfo with invalid from (%s)\n", + from); + g_free(my_bare_jid); + return; + } - purple_debug_info("jabber", "got google:jingleinfo\n"); - - if (stun) { - xmlnode *server = xmlnode_get_child(stun, "server"); + g_free(my_bare_jid); + } - if (server) { - const gchar *host = xmlnode_get_attrib(server, "host"); - const gchar *udp = xmlnode_get_attrib(server, "udp"); + if (type == JABBER_IQ_ERROR || type == JABBER_IQ_GET) + return; + + purple_debug_info("jabber", "got google:jingleinfo\n"); + + if (stun) { + xmlnode *server = xmlnode_get_child(stun, "server"); - if (host && udp) { - int port = atoi(udp); - /* if there, would already be an ongoing query, - cancel it */ - if (js->stun_query) - purple_dnsquery_destroy(js->stun_query); + if (server) { + const gchar *host = xmlnode_get_attrib(server, "host"); + const gchar *udp = xmlnode_get_attrib(server, "udp"); - js->stun_query = purple_dnsquery_a(host, port, - jabber_google_stun_lookup_cb, js); - } - } + if (host && udp) { + int port = atoi(udp); + /* if there, would already be an ongoing query, + cancel it */ + if (js->stun_query) + purple_dnsquery_destroy(js->stun_query); + + js->stun_query = purple_dnsquery_a(host, port, + jabber_google_stun_lookup_cb, js); } - /* should perhaps handle relays later on, or maybe wait until - Google supports a common standard... */ } } + /* should perhaps handle relays later on, or maybe wait until + Google supports a common standard... */ +} + +static void +jabber_google_jingle_info_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ + xmlnode *query = xmlnode_get_child_with_namespace(packet, "query", + GOOGLE_JINGLE_INFO_NAMESPACE); + + if (query) + jabber_google_jingle_info_common(js, from, type, query); + else + purple_debug_warning("jabber", "Got invalid google:jingleinfo\n"); } void -jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet) +jabber_google_handle_jingle_info(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *child) { - jabber_google_jingle_info_cb(js, packet, NULL); + jabber_google_jingle_info_common(js, from, type, child); } void
--- a/libpurple/protocols/jabber/google.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/google.h Tue Apr 28 19:08:06 2009 +0000 @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef _PURPLE_GOOGLE_H_ -#define _PURPLE_GOOGLE_H_ +#ifndef PURPLE_JABBER_GOOGLE_H_ +#define PURPLE_JABBER_GOOGLE_H_ /* This is a place for Google Talk-specific XMPP extensions to live * such that they don't intermingle with code for the XMPP RFCs and XEPs :) */ @@ -31,7 +31,8 @@ #define GOOGLE_JINGLE_INFO_NAMESPACE "google:jingleinfo" void jabber_gmail_init(JabberStream *js); -void jabber_gmail_poke(JabberStream *js, xmlnode *node); +void jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *new_mail); void jabber_google_roster_init(JabberStream *js); void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item); @@ -50,9 +51,11 @@ char *jabber_google_format_to_html(const char *text); gboolean jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type); -void jabber_google_session_parse(JabberStream *js, xmlnode *node); +void jabber_google_session_parse(JabberStream *js, const char *from, JabberIqType type, const char *iq, xmlnode *session); -void jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet); +void jabber_google_handle_jingle_info(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *child); void jabber_google_send_jingle_info(JabberStream *js); -#endif /* _PURPLE_GOOGLE_H_ */ +#endif /* PURPLE_JABBER_GOOGLE_H_ */
--- a/libpurple/protocols/jabber/ibb.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/ibb.c Tue Apr 28 19:08:06 2009 +0000 @@ -46,12 +46,10 @@ } JabberIBBSession * -jabber_ibb_session_create_from_xmlnode(JabberStream *js, xmlnode *packet, - gpointer user_data) +jabber_ibb_session_create_from_xmlnode(JabberStream *js, const char *from, + const char *id, xmlnode *open, gpointer user_data) { JabberIBBSession *sess = NULL; - xmlnode *open = xmlnode_get_child_with_namespace(packet, "open", - XEP_0047_NAMESPACE); const gchar *sid = xmlnode_get_attrib(open, "sid"); const gchar *block_size = xmlnode_get_attrib(open, "block-size"); @@ -66,9 +64,8 @@ return NULL; } - sess = jabber_ibb_session_create(js, sid, - xmlnode_get_attrib(packet, "from"), user_data); - sess->id = g_strdup(xmlnode_get_attrib(packet, "id")); + sess = jabber_ibb_session_create(js, sid, from, user_data); + sess->id = g_strdup(id); sess->block_size = atoi(block_size); /* if we create a session from an incoming <open/> request, it means the session is immediatly open... */ @@ -198,11 +195,13 @@ } static void -jabber_ibb_session_opened_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_ibb_session_opened_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JabberIBBSession *sess = (JabberIBBSession *) data; - if (strcmp(xmlnode_get_attrib(packet, "type"), "error") == 0) { + if (type == JABBER_IQ_ERROR) { sess->state = JABBER_IBB_SESSION_ERROR; } else { sess->state = JABBER_IBB_SESSION_OPENED; @@ -274,10 +273,11 @@ } static void -jabber_ibb_session_send_acknowledge_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_ibb_session_send_acknowledge_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JabberIBBSession *sess = (JabberIBBSession *) data; - xmlnode *error = xmlnode_get_child(packet, "error"); if (sess) { /* reset callback */ @@ -286,7 +286,7 @@ sess->last_iq_id = NULL; } - if (error) { + if (type == JABBER_IQ_ERROR) { jabber_ibb_session_close(sess); sess->state = JABBER_IBB_SESSION_ERROR; @@ -351,7 +351,7 @@ } static void -jabber_ibb_send_error_response(JabberStream *js, xmlnode *packet) +jabber_ibb_send_error_response(JabberStream *js, const char *to, const char *id) { JabberIq *result = jabber_iq_new(js, JABBER_IQ_ERROR); xmlnode *error = xmlnode_new("error"); @@ -361,9 +361,8 @@ "urn:ietf:params:xml:ns:xmpp-stanzas"); xmlnode_set_attrib(error, "code", "440"); xmlnode_set_attrib(error, "type", "cancel"); - jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); - xmlnode_set_attrib(result->node, "to", - xmlnode_get_attrib(packet, "from")); + jabber_iq_set_id(result, id); + xmlnode_set_attrib(result->node, "to", to); xmlnode_insert_child(error, item_not_found); xmlnode_insert_child(result->node, error); @@ -371,20 +370,17 @@ } void -jabber_ibb_parse(JabberStream *js, xmlnode *packet) +jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type, + const char *id, xmlnode *child) { - xmlnode *data = xmlnode_get_child_with_namespace(packet, "data", - XEP_0047_NAMESPACE); - xmlnode *close = xmlnode_get_child_with_namespace(packet, "close", - XEP_0047_NAMESPACE); - xmlnode *open = xmlnode_get_child_with_namespace(packet, "open", - XEP_0047_NAMESPACE); - const gchar *sid = - data ? xmlnode_get_attrib(data, "sid") : - close ? xmlnode_get_attrib(close, "sid") : NULL; + const char *name = child->name; + gboolean data = g_str_equal(name, "data"); + gboolean close = g_str_equal(name, "close"); + gboolean open = g_str_equal(name, "open"); + const gchar *sid = (data || close) ? + xmlnode_get_attrib(child, "sid") : NULL; JabberIBBSession *sess = sid ? g_hash_table_lookup(jabber_ibb_sessions, sid) : NULL; - const gchar *who = xmlnode_get_attrib(packet, "from"); if (sess) { @@ -394,7 +390,7 @@ purple_debug_error("jabber", "Got IBB iq from wrong JID, ignoring\n"); } else if (data) { - const gchar *seq_attr = xmlnode_get_attrib(data, "seq"); + const gchar *seq_attr = xmlnode_get_attrib(child, "seq"); guint16 seq = (seq_attr ? atoi(seq_attr) : 0); /* reject the data, and set the session in error if we get an @@ -403,12 +399,11 @@ /* sequence # is the expected... */ JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT); - jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); - xmlnode_set_attrib(result->node, "to", - xmlnode_get_attrib(packet, "from")); + jabber_iq_set_id(result, id); + xmlnode_set_attrib(result->node, "to", who); if (sess->data_received_cb) { - gchar *base64 = xmlnode_get_data(data); + gchar *base64 = xmlnode_get_data(child); gsize size; gpointer rawdata = purple_base64_decode(base64, &size); @@ -475,20 +470,19 @@ iterator = g_list_next(iterator)) { JabberIBBOpenHandler *handler = iterator->data; - if (handler(js, packet)) { + if (handler(js, who, id, child)) { result = jabber_iq_new(js, JABBER_IQ_RESULT); - xmlnode_set_attrib(result->node, "to", - xmlnode_get_attrib(packet, "from")); - jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); + xmlnode_set_attrib(result->node, "to", who); + jabber_iq_set_id(result, id); jabber_iq_send(result); return; } } /* no open callback returned success, reject */ - jabber_ibb_send_error_response(js, packet); + jabber_ibb_send_error_response(js, who, id); } else { /* send error reply */ - jabber_ibb_send_error_response(js, packet); + jabber_ibb_send_error_response(js, who, id); } } @@ -508,6 +502,10 @@ jabber_ibb_init(void) { jabber_ibb_sessions = g_hash_table_new(g_str_hash, g_str_equal); + + jabber_iq_register_handler("close", XEP_0047_NAMESPACE, jabber_ibb_parse); + jabber_iq_register_handler("data", XEP_0047_NAMESPACE, jabber_ibb_parse); + jabber_iq_register_handler("open", XEP_0047_NAMESPACE, jabber_ibb_parse); } void
--- a/libpurple/protocols/jabber/ibb.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/ibb.h Tue Apr 28 19:08:06 2009 +0000 @@ -14,8 +14,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ -#ifndef _PURPLE_JABBER_IBB_H_ -#define _PURPLE_JABBER_IBB_H_ +#ifndef PURPLE_JABBER_IBB_H_ +#define PURPLE_JABBER_IBB_H_ #include "jabber.h" #include "iq.h" @@ -32,7 +32,8 @@ typedef void (JabberIBBErrorCallback)(JabberIBBSession *); typedef void (JabberIBBSentCallback)(JabberIBBSession *); -typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, xmlnode *packet); +typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, const char *from, + const char *id, xmlnode *open); typedef enum { JABBER_IBB_SESSION_NOT_OPENED, @@ -71,7 +72,7 @@ JabberIBBSession *jabber_ibb_session_create(JabberStream *js, const gchar *sid, const gchar *who, gpointer user_data); JabberIBBSession *jabber_ibb_session_create_from_xmlnode(JabberStream *js, - xmlnode *packet, gpointer user_data); + const gchar *from, const gchar *id, xmlnode *open, gpointer user_data); void jabber_ibb_session_destroy(JabberIBBSession *sess); @@ -107,7 +108,8 @@ gpointer jabber_ibb_session_get_user_data(JabberIBBSession *sess); /* handle incoming packet */ -void jabber_ibb_parse(JabberStream *js, xmlnode *packet); +void jabber_ibb_parse(JabberStream *js, const char *who, JabberIqType type, + const char *id, xmlnode *child); /* add a handler for open session */ void jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb); @@ -116,4 +118,4 @@ void jabber_ibb_init(void); void jabber_ibb_uninit(void); -#endif /* _PURPLE_JABBER_IBB_H_ */ +#endif /* PURPLE_JABBER_IBB_H_ */
--- a/libpurple/protocols/jabber/iq.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/iq.c Tue Apr 28 19:08:06 2009 +0000 @@ -144,23 +144,19 @@ g_free(iq); } -static void jabber_iq_last_parse(JabberStream *js, xmlnode *packet) +static void jabber_iq_last_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet) { JabberIq *iq; - const char *type; - const char *from; - const char *id; xmlnode *query; char *idle_time; - type = xmlnode_get_attrib(packet, "type"); - from = xmlnode_get_attrib(packet, "from"); - id = xmlnode_get_attrib(packet, "id"); - - if(type && !strcmp(type, "get")) { + if(type == JABBER_IQ_GET) { iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:last"); jabber_iq_set_id(iq, id); - xmlnode_set_attrib(iq->node, "to", from); + if (from) + xmlnode_set_attrib(iq->node, "to", from); query = xmlnode_get_child(iq->node, "query"); @@ -172,88 +168,67 @@ } } -static void jabber_iq_time_parse(JabberStream *js, xmlnode *packet) +static void jabber_iq_time_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *child) { - const char *type, *from, *id, *xmlns; + const char *xmlns; JabberIq *iq; - xmlnode *query; time_t now_t; + struct tm now_local; + struct tm now_utc; struct tm *now; time(&now_t); now = localtime(&now_t); - - type = xmlnode_get_attrib(packet, "type"); - from = xmlnode_get_attrib(packet, "from"); - id = xmlnode_get_attrib(packet, "id"); + memcpy(&now_local, now, sizeof(struct tm)); + now = gmtime(&now_t); + memcpy(&now_utc, now, sizeof(struct tm)); - /* we're gonna throw this away in a moment, but we need it - * to get the xmlns, so we can figure out if this is - * jabber:iq:time or urn:xmpp:time */ - query = xmlnode_get_child(packet, "query"); - xmlns = xmlnode_get_namespace(query); + xmlns = xmlnode_get_namespace(child); - if(type && !strcmp(type, "get")) { + if(type == JABBER_IQ_GET) { xmlnode *utc; - const char *date; + const char *date, *tz, *display; - iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, xmlns); + iq = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(iq, id); - xmlnode_set_attrib(iq->node, "to", from); - - query = xmlnode_get_child(iq->node, "query"); + if (from) + xmlnode_set_attrib(iq->node, "to", from); - date = purple_utf8_strftime("%Y%m%dT%T", now); - utc = xmlnode_new_child(query, "utc"); - xmlnode_insert_data(utc, date, -1); + child = xmlnode_new_child(iq->node, child->name); + xmlnode_set_namespace(child, xmlns); + utc = xmlnode_new_child(child, "utc"); if(!strcmp("urn:xmpp:time", xmlns)) { - xmlnode_insert_data(utc, "Z", 1); /* of COURSE the thing that is the same is different */ + tz = purple_get_tzoff_str(&now_local, TRUE); + xmlnode_insert_data(xmlnode_new_child(child, "tzo"), tz, -1); - date = purple_get_tzoff_str(now, TRUE); - xmlnode_insert_data(xmlnode_new_child(query, "tzo"), date, -1); + date = purple_utf8_strftime("%FT%TZ", &now_utc); + xmlnode_insert_data(utc, date, -1); } else { /* jabber:iq:time */ - date = purple_utf8_strftime("%Z", now); - xmlnode_insert_data(xmlnode_new_child(query, "tz"), date, -1); + tz = purple_utf8_strftime("%Z", &now_local); + xmlnode_insert_data(xmlnode_new_child(child, "tz"), tz, -1); - date = purple_utf8_strftime("%d %b %Y %T", now); - xmlnode_insert_data(xmlnode_new_child(query, "display"), date, -1); + date = purple_utf8_strftime("%Y%m%dT%T", &now_utc); + xmlnode_insert_data(utc, date, -1); + + display = purple_utf8_strftime("%d %b %Y %T", &now_local); + xmlnode_insert_data(xmlnode_new_child(child, "display"), display, -1); } jabber_iq_send(iq); } } -static void urn_xmpp_ping_parse(JabberStream *js, xmlnode *packet) -{ - const char *type, *id, *from; - JabberIq *iq; - - type = xmlnode_get_attrib(packet, "type"); - from = xmlnode_get_attrib(packet, "from"); - id = xmlnode_get_attrib(packet, "id"); - - if(type && !strcmp(type, "get")) { - iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "urn:xmpp:ping"); - - jabber_iq_set_id(iq, id); - xmlnode_set_attrib(iq->node, "to", from); - - jabber_iq_send(iq); - } else { - /* XXX: error */ - } -} - -static void jabber_iq_version_parse(JabberStream *js, xmlnode *packet) +static void jabber_iq_version_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet) { JabberIq *iq; - const char *type, *from, *id; xmlnode *query; - type = xmlnode_get_attrib(packet, "type"); - - if(type && !strcmp(type, "get")) { + if(type == JABBER_IQ_GET) { GHashTable *ui_info; const char *ui_name = NULL, *ui_version = NULL; #if 0 @@ -266,11 +241,10 @@ osinfo.machine); } #endif - from = xmlnode_get_attrib(packet, "from"); - id = xmlnode_get_attrib(packet, "id"); iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version"); - xmlnode_set_attrib(iq->node, "to", from); + if (from) + xmlnode_set_attrib(iq->node, "to", from); jabber_iq_set_id(iq, id); query = xmlnode_get_child(iq->node, "query"); @@ -311,33 +285,56 @@ void jabber_iq_parse(JabberStream *js, xmlnode *packet) { JabberCallbackData *jcd; - xmlnode *query, *error, *x; + xmlnode *child, *error, *x; const char *xmlns; - const char *type, *id, *from; - JabberIqHandler *jih; + const char *iq_type, *id, *from; + JabberIqType type = JABBER_IQ_NONE; - query = xmlnode_get_child(packet, "query"); - type = xmlnode_get_attrib(packet, "type"); + /* + * child will be either the first tag child or NULL if there is no child. + * Historically, we used just the 'query' subchild, but newer XEPs use + * differently named children. Grabbing the first child is (for the time + * being) sufficient. + */ + for (child = packet->child; child; child = child->next) { + if (child->type == XMLNODE_TYPE_TAG) + break; + } + + iq_type = xmlnode_get_attrib(packet, "type"); from = xmlnode_get_attrib(packet, "from"); id = xmlnode_get_attrib(packet, "id"); - if(type == NULL || !(!strcmp(type, "get") || !strcmp(type, "set") - || !strcmp(type, "result") || !strcmp(type, "error"))) { + if (iq_type) { + if (!strcmp(iq_type, "get")) + type = JABBER_IQ_GET; + else if (!strcmp(iq_type, "set")) + type = JABBER_IQ_SET; + else if (!strcmp(iq_type, "result")) + type = JABBER_IQ_RESULT; + else if (!strcmp(iq_type, "error")) + type = JABBER_IQ_ERROR; + } + + if (type == JABBER_IQ_NONE) { purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n", - type ? type : "(null)"); + iq_type ? iq_type : "(null)"); return; } /* All IQs must have an ID, so send an error for a set/get that doesn't */ if(!id || !*id) { - if(!strcmp(type, "set") || !strcmp(type, "get")) { + if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); xmlnode_free(iq->node); iq->node = xmlnode_copy(packet); - xmlnode_set_attrib(iq->node, "to", from); - xmlnode_remove_attrib(iq->node, "from"); + if (from) { + xmlnode_set_attrib(iq->node, "to", from); + xmlnode_remove_attrib(iq->node, "from"); + } + xmlnode_set_attrib(iq->node, "type", "error"); /* This id is clearly not useful, but we must put something there for a valid stanza */ iq->id = jabber_get_next_id(js); @@ -349,79 +346,46 @@ jabber_iq_send(iq); } else - purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", type); + purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", + iq_type); return; } /* First, lets see if a special callback got registered */ - - if(!strcmp(type, "result") || !strcmp(type, "error")) { - if(id && *id && (jcd = g_hash_table_lookup(js->iq_callbacks, id))) { - jcd->callback(js, packet, jcd->data); + if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) { + if((jcd = g_hash_table_lookup(js->iq_callbacks, id))) { + jcd->callback(js, from, type, id, packet, jcd->data); jabber_iq_remove_callback_by_id(js, id); return; } } /* Apparently not, so lets see if we have a pre-defined handler */ + if(child && (xmlns = xmlnode_get_namespace(child))) { + char *key = g_strdup_printf("%s %s", child->name, xmlns); + JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key); + g_free(key); - if(query && (xmlns = xmlnode_get_namespace(query))) { - if((jih = g_hash_table_lookup(iq_handlers, xmlns))) { - jih(js, packet); + if(jih) { + jih(js, from, type, id, child); return; } } -#ifdef USE_VV - if (xmlnode_get_child_with_namespace(packet, "session", "http://www.google.com/session")) { - jabber_google_session_parse(js, packet); - return; - } -#endif - - if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) { - jabber_si_parse(js, packet); - return; - } - - if(xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) { - jabber_gmail_poke(js, packet); - return; - } - purple_debug_info("jabber", "jabber_iq_parse\n"); - if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) { - jabber_ping_parse(js, packet); - return; - } - - if (xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE)) { - jabber_data_parse(js, packet); - return; - } - - if (xmlnode_get_child_with_namespace(packet, "data", XEP_0047_NAMESPACE) - || xmlnode_get_child_with_namespace(packet, "close", XEP_0047_NAMESPACE) - || xmlnode_get_child_with_namespace(packet, "open", XEP_0047_NAMESPACE)) { - jabber_ibb_parse(js, packet); - return; - } - - if (xmlnode_get_child_with_namespace(packet, "jingle", JINGLE)) { - jingle_parse(js, packet); - return; - } - /* If we get here, send the default error reply mandated by XMPP-CORE */ - if(!strcmp(type, "set") || !strcmp(type, "get")) { + if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); xmlnode_free(iq->node); iq->node = xmlnode_copy(packet); - xmlnode_set_attrib(iq->node, "to", from); - xmlnode_remove_attrib(iq->node, "from"); + if (from) { + xmlnode_set_attrib(iq->node, "to", from); + xmlnode_remove_attrib(iq->node, "from"); + } + xmlnode_set_attrib(iq->node, "type", "error"); error = xmlnode_new_child(iq->node, "error"); xmlnode_set_attrib(error, "type", "cancel"); @@ -433,31 +397,50 @@ } } -void jabber_iq_register_handler(const char *xmlns, JabberIqHandler *handlerfunc) +void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc) { - g_hash_table_replace(iq_handlers, g_strdup(xmlns), handlerfunc); + /* + * This is valid because nodes nor namespaces cannot have spaces in them + * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and + * http://www.w3.org/TR/REC-xml-names/) + */ + char *key = g_strdup_printf("%s %s", node, xmlns); + g_hash_table_replace(iq_handlers, key, handlerfunc); } void jabber_iq_init(void) { iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - jabber_iq_register_handler("jabber:iq:roster", jabber_roster_parse); - jabber_iq_register_handler("jabber:iq:oob", jabber_oob_parse); - jabber_iq_register_handler("http://jabber.org/protocol/bytestreams", jabber_bytestreams_parse); - jabber_iq_register_handler("jabber:iq:last", jabber_iq_last_parse); - jabber_iq_register_handler("jabber:iq:time", jabber_iq_time_parse); - jabber_iq_register_handler("urn:xmpp:time", jabber_iq_time_parse); - jabber_iq_register_handler("jabber:iq:version", jabber_iq_version_parse); - jabber_iq_register_handler("http://jabber.org/protocol/disco#info", jabber_disco_info_parse); - jabber_iq_register_handler("http://jabber.org/protocol/disco#items", jabber_disco_items_parse); - jabber_iq_register_handler("jabber:iq:register", jabber_register_parse); - jabber_iq_register_handler("urn:xmpp:ping", urn_xmpp_ping_parse); - jabber_iq_register_handler(JINGLE, jingle_parse); + jabber_iq_register_handler("jingle", JINGLE, jingle_parse); + jabber_iq_register_handler("mailbox", "google:mail:notify", + jabber_gmail_poke); + jabber_iq_register_handler("new-mail", "google:mail:notify", + jabber_gmail_poke); + jabber_iq_register_handler("ping", "urn:xmpp:ping", jabber_ping_parse); + jabber_iq_register_handler("query", GOOGLE_JINGLE_INFO_NAMESPACE, + jabber_google_handle_jingle_info); + jabber_iq_register_handler("query", "http://jabber.org/protocol/bytestreams", + jabber_bytestreams_parse); + jabber_iq_register_handler("query", "http://jabber.org/protocol/disco#info", + jabber_disco_info_parse); + jabber_iq_register_handler("query", "http://jabber.org/protocol/disco#items", + jabber_disco_items_parse); + jabber_iq_register_handler("query", "jabber:iq:last", jabber_iq_last_parse); + jabber_iq_register_handler("query", "jabber:iq:oob", jabber_oob_parse); + jabber_iq_register_handler("query", "jabber:iq:register", + jabber_register_parse); + jabber_iq_register_handler("query", "jabber:iq:roster", + jabber_roster_parse); + jabber_iq_register_handler("query", "jabber:iq:time", jabber_iq_time_parse); + jabber_iq_register_handler("query", "jabber:iq:version", + jabber_iq_version_parse); +#ifdef USE_VV + jabber_iq_register_handler("session", "http://www.google.com/session", + jabber_google_session_parse); +#endif + jabber_iq_register_handler("time", "urn:xmpp:time", jabber_iq_time_parse); - /* handle Google jingleinfo */ - jabber_iq_register_handler(GOOGLE_JINGLE_INFO_NAMESPACE, - jabber_google_handle_jingle_info); } void jabber_iq_uninit(void) @@ -465,4 +448,3 @@ g_hash_table_destroy(iq_handlers); iq_handlers = NULL; } -
--- a/libpurple/protocols/jabber/iq.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/iq.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,12 +19,8 @@ * 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_JABBER_IQ_H_ -#define _PURPLE_JABBER_IQ_H_ - -#include "jabber.h" - -typedef struct _JabberIq JabberIq; +#ifndef PURPLE_JABBER_IQ_H_ +#define PURPLE_JABBER_IQ_H_ typedef enum { JABBER_IQ_SET, @@ -34,9 +30,51 @@ JABBER_IQ_NONE } JabberIqType; -typedef void (JabberIqHandler)(JabberStream *js, xmlnode *packet); +#include "jabber.h" + +typedef struct _JabberIq JabberIq; -typedef void (JabberIqCallback)(JabberStream *js, xmlnode *packet, gpointer data); +/** + * A JabberIqHandler is called to process an incoming IQ stanza. + * Handlers typically process unsolicited incoming GETs or SETs for their + * registered namespace, but may be called to handle the results of a + * GET or SET that we generated if no JabberIqCallback was generated + * The handler may be called for the results of a GET or SET (RESULT or ERROR) + * that we generated + * if the generating function did not register a JabberIqCallback. + * + * @param js The JabberStream object. + * @param from The remote entity (the from attribute on the <iq/> stanza) + * @param type The IQ type. + * @param id The IQ id (the id attribute on the <iq/> stanza) + * @param child The child element of the <iq/> stanza that matches the name + * and namespace registered with jabber_iq_register_handler. + * + * @see jabber_iq_register_handler() + * @see JabberIqCallback + */ +typedef void (JabberIqHandler)(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *child); + +/** + * A JabberIqCallback is called to process the results of a GET or SET that + * we send to a remote entity. The callback is matched based on the id + * of the incoming stanza (which matches the one on the initial stanza). + * + * @param js The JabberStream object. + * @param from The remote entity (the from attribute on the <iq/> stanza) + * @param type The IQ type. The only possible values are JABBER_IQ_RESULT + * and JABBER_IQ_ERROR. + * @param id The IQ id (the id attribute on the <iq/> stanza) + * @param packet The <iq/> stanza + * @param data The callback data passed to jabber_iq_set_callback() + * + * @see jabber_iq_set_callback() + */ +typedef void (JabberIqCallback)(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data); struct _JabberIq { JabberIqType type; @@ -65,6 +103,7 @@ void jabber_iq_init(void); void jabber_iq_uninit(void); -void jabber_iq_register_handler(const char *xmlns, JabberIqHandler *func); +void jabber_iq_register_handler(const char *node, const char *xmlns, + JabberIqHandler *func); -#endif /* _PURPLE_JABBER_IQ_H_ */ +#endif /* PURPLE_JABBER_IQ_H_ */
--- a/libpurple/protocols/jabber/jabber.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Tue Apr 28 19:08:06 2009 +0000 @@ -28,6 +28,7 @@ #include "conversation.h" #include "debug.h" #include "dnssrv.h" +#include "imgstore.h" #include "message.h" #include "notify.h" #include "pluginpref.h" @@ -36,6 +37,7 @@ #include "prpl.h" #include "request.h" #include "server.h" +#include "status.h" #include "util.h" #include "version.h" #include "xmlnode.h" @@ -81,6 +83,10 @@ "xmlns:stream='http://etherx.jabber.org/streams' " "version='1.0'>", js->user->domain); + if (js->reinit) + /* Close down the current stream to keep the XML parser happy */ + jabber_parser_close_stream(js); + /* setup the parser fresh for each stream */ jabber_parser_setup(js); jabber_send_raw(js, open_stream, -1); @@ -89,10 +95,11 @@ } static void -jabber_session_initialized_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_session_initialized_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - const char *type = xmlnode_get_attrib(packet, "type"); - if(type && !strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { jabber_disco_items_server(js); if(js->unregistration) jabber_unregister_account_cb(js); @@ -116,13 +123,13 @@ jabber_iq_send(iq); } -static void jabber_bind_result_cb(JabberStream *js, xmlnode *packet, - gpointer data) +static void jabber_bind_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - const char *type = xmlnode_get_attrib(packet, "type"); xmlnode *bind; - if(type && !strcmp(type, "result") && + if (type == JABBER_IQ_RESULT && (bind = xmlnode_get_child_with_namespace(packet, "bind", "urn:ietf:params:xml:ns:xmpp-bind"))) { xmlnode *jid; char *full_jid; @@ -456,13 +463,7 @@ g_free(txt); } -static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused) -{ - purple_timeout_remove(js->keepalive_timeout); - js->keepalive_timeout = -1; -} - -static gboolean jabber_pong_timeout(PurpleConnection *gc) +static gboolean jabber_keepalive_timeout(PurpleConnection *gc) { JabberStream *js = gc->proto_data; purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, @@ -476,14 +477,9 @@ JabberStream *js = gc->proto_data; if (js->keepalive_timeout == -1) { - JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); - - xmlnode *ping = xmlnode_new_child(iq->node, "ping"); - xmlnode_set_namespace(ping, "urn:xmpp:ping"); - - js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc); - jabber_iq_set_callback(iq, jabber_pong_cb, NULL); - jabber_iq_send(iq); + jabber_ping_jid(js, js->user->domain); + js->keepalive_timeout = purple_timeout_add_seconds(120, + (GSourceFunc)(jabber_keepalive_timeout), gc); } } @@ -594,14 +590,13 @@ } static void -txt_resolved_cb(PurpleTxtResponse *resp, int results, gpointer data) +txt_resolved_cb(GSList *responses, gpointer data) { JabberStream *js = data; - int n; - + js->srv_query_data = NULL; - if (results == 0) { + if (responses == NULL) { gchar *tmp; tmp = g_strdup_printf(_("Could not find alternative XMPP connection methods after failing to connect directly.\n")); purple_connection_error_reason (js->gc, @@ -609,10 +604,11 @@ g_free(tmp); return; } - - for (n = 0; n < results; n++) { + + while (responses) { + PurpleTxtResponse *resp = responses->data; gchar **token; - token = g_strsplit(resp[n].content, "=", 2); + token = g_strsplit(purple_txt_response_get_content(resp), "=", 2); if (!strcmp(token[0], "_xmpp-client-xbosh")) { purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); js->bosh = jabber_bosh_connection_init(js, token[1]); @@ -621,12 +617,20 @@ break; } g_strfreev(token); + purple_txt_response_destroy(resp); + responses = g_slist_delete_link(responses, responses); } + if (js->bosh) { jabber_bosh_connection_connect(js->bosh); } else { purple_debug_info("jabber","Didn't find an alternative connection method.\n"); } + + if (responses) { + g_slist_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); + g_slist_free(responses); + } } static void @@ -677,6 +681,9 @@ static void tls_init(JabberStream *js) { + /* Close down the current stream to keep the XML parser happy */ + jabber_parser_close_stream(js); + purple_input_remove(js->gc->inpa); js->gc->inpa = 0; js->gsc = purple_ssl_connect_with_host_fd(js->gc->account, js->fd, @@ -749,6 +756,8 @@ const char *connect_server = purple_account_get_string(account, "connect_server", ""); JabberStream *js; + PurplePresence *presence; + PurpleStoredImage *image; JabberBuddy *my_jb = NULL; gc->flags |= PURPLE_CONNECTION_HTML | @@ -767,7 +776,7 @@ js->user = jabber_id_new(purple_account_get_username(account)); js->next_id = g_random_int(); js->write_buffer = purple_circ_buffer_new(512); - js->old_length = 0; + js->old_length = -1; js->keepalive_timeout = -1; /* Set the default protocol version to 1.0. Overridden in parser.c. */ js->protocol_version = JABBER_PROTO_1_0; @@ -776,6 +785,13 @@ js->stun_port = 0; js->stun_query = NULL; + /* if we are idle, set idle-ness on the stream (this could happen if we get + disconnected and the reconnects while being idle. I don't think it makes + sense to do this when registering a new account... */ + presence = purple_account_get_presence(account); + if (purple_presence_is_idle(presence)) + js->idle = purple_presence_get_idle_time(presence); + if(!js->user) { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, @@ -790,6 +806,17 @@ return; } + /* + * Calculate the avatar hash for our current image so we know (when we + * fetch our vCard and PEP avatar) if we should send our avatar to the + * server. + */ + if ((image = purple_buddy_icons_find_account_icon(account))) { + js->initial_avatar_hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(image), + purple_imgstore_get_size(image)); + purple_imgstore_unref(image); + } + if((my_jb = jabber_buddy_find(js, purple_account_get_username(account), TRUE))) my_jb->subscription |= JABBER_SUB_BOTH; @@ -863,14 +890,15 @@ } static void -jabber_registration_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_registration_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { PurpleAccount *account = purple_connection_get_account(js->gc); - const char *type = xmlnode_get_attrib(packet, "type"); char *buf; char *to = data; - if(!strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { if(js->registration) { buf = g_strdup_printf(_("Registration of %s@%s successful"), js->user->node, js->user->domain); @@ -898,13 +926,14 @@ } g_free(to); if(js->registration) - jabber_connection_schedule_close(js); + jabber_connection_schedule_close(js); } static void -jabber_unregistration_result_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_unregistration_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - const char *type = xmlnode_get_attrib(packet, "type"); char *buf; char *to = data; @@ -912,7 +941,7 @@ * the server, so there should always be a 'to' address. */ g_return_if_fail(to != NULL); - if(!strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { buf = g_strdup_printf(_("Registration from %s successfully removed"), to); purple_notify_info(NULL, _("Unregistration Successful"), @@ -1061,31 +1090,30 @@ jabber_iq_send(iq); } -void jabber_register_parse(JabberStream *js, xmlnode *packet) +void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *query) { PurpleAccount *account = purple_connection_get_account(js->gc); - const char *type; - const char *from; PurpleRequestFields *fields; PurpleRequestFieldGroup *group; PurpleRequestField *field; - xmlnode *query, *x, *y; + xmlnode *x, *y; char *instructions; JabberRegisterCBData *cbdata; gboolean registered = FALSE; - if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) + if (type != JABBER_IQ_RESULT) return; - from = xmlnode_get_attrib(packet, "from"); + if (!from) + from = js->serverFQDN; + g_return_if_fail(from != NULL); if(js->registration) { /* get rid of the login thingy */ purple_connection_set_state(js->gc, PURPLE_CONNECTED); } - query = xmlnode_get_child(packet, "query"); - if(xmlnode_get_child(query, "registered")) { registered = TRUE; @@ -1296,37 +1324,62 @@ jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); + /* TODO: Just use purple_url_parse? */ + if (!g_ascii_strncasecmp(connect_server, "http://", 7) || !g_ascii_strncasecmp(connect_server, "https://", 8)) { + js->use_bosh = TRUE; + js->bosh = jabber_bosh_connection_init(js, connect_server); + if (!js->bosh) { + purple_connection_error_reason (js->gc, + PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, + _("Malformed BOSH Connect Server")); + return; + } + jabber_bosh_connection_connect(js->bosh); + return; + } else { + js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain); + } + if(purple_account_get_bool(account, "old_ssl", FALSE)) { if(purple_ssl_is_supported()) { js->gsc = purple_ssl_connect(account, server, purple_account_get_int(account, "port", 5222), jabber_login_callback_ssl, jabber_ssl_connect_failure, gc); + if (!js->gsc) { + purple_connection_error_reason (js->gc, + PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, + _("Unable to establish SSL connection")); + } } else { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable")); } + + return; } - if(!js->gsc) { - if (connect_server[0]) { - jabber_login_connect(js, js->user->domain, server, - purple_account_get_int(account, - "port", 5222), TRUE); - } else { - js->srv_query_data = purple_srv_resolve("xmpp-client", - "tcp", - js->user->domain, - srv_resolved_cb, - js); - } + if (connect_server[0]) { + jabber_login_connect(js, js->user->domain, server, + purple_account_get_int(account, + "port", 5222), TRUE); + } else { + js->srv_query_data = purple_srv_resolve("xmpp-client", + "tcp", + js->user->domain, + srv_resolved_cb, + js); } } -static void jabber_unregister_account_iq_cb(JabberStream *js, xmlnode *packet, gpointer data) { +static void +jabber_unregister_account_iq_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ PurpleAccount *account = purple_connection_get_account(js->gc); - const char *type = xmlnode_get_attrib(packet,"type"); - if(!strcmp(type,"error")) { + + if (type == JABBER_IQ_ERROR) { char *msg = jabber_parse_error(js, packet, NULL); purple_notify_error(js->gc, _("Error unregistering account"), @@ -1334,7 +1387,7 @@ g_free(msg); if(js->unregistration_cb) js->unregistration_cb(account, FALSE, js->unregistration_user_data); - } else if(!strcmp(type,"result")) { + } else { purple_notify_info(js->gc, _("Account successfully unregistered"), _("Account successfully unregistered"), NULL); if(js->unregistration_cb) @@ -1387,6 +1440,11 @@ jabber_unregister_account_cb(js); } +/* TODO: As Will pointed out in IRC, after being notified by the core to + * shutdown, we should async. wait for the server to send us the stream + * termination before destorying everything. That seems like it would require + * changing the semantics of prpl->close(), so it's a good idea for 3.0.0. + */ void jabber_close(PurpleConnection *gc) { JabberStream *js = gc->proto_data; @@ -1462,6 +1520,7 @@ g_free(js->stream_id); if(js->user) jabber_id_free(js->user); + g_free(js->initial_avatar_hash); g_free(js->avatar_hash); g_free(js->caps_hash); @@ -1580,9 +1639,15 @@ JabberStream *js = gc->proto_data; js->idle = idle ? time(NULL) - idle : idle; + + /* send out an updated prescence */ + purple_debug_info("jabber", "sending updated presence for idle\n"); + jabber_presence_send(js, FALSE); } -static void jabber_blocklist_parse(JabberStream *js, xmlnode *packet, gpointer data) +static void jabber_blocklist_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { xmlnode *blocklist, *item; PurpleAccount *account; @@ -1804,10 +1869,11 @@ } else if(jb && !PURPLE_BUDDY_IS_ONLINE(b) && jb->error_msg) { ret = g_strdup(jb->error_msg); } else { + PurplePresence *presence = purple_buddy_get_presence(b); + PurpleStatus *status =purple_presence_get_active_status(presence); char *stripped; - if(!(stripped = purple_markup_strip_html(jabber_buddy_get_status_msg(jb)))) { - PurplePresence *presence = purple_buddy_get_presence(b); + if(!(stripped = purple_markup_strip_html(purple_status_get_attr_string(status, "message")))) { if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { PurpleStatus *status = purple_presence_get_status(presence, "tune"); stripped = g_strdup(purple_status_get_attr_string(status, PURPLE_TUNE_TITLE)); @@ -1823,6 +1889,56 @@ return ret; } +static void +jabber_tooltip_add_resource_text(JabberBuddyResource *jbr, + PurpleNotifyUserInfo *user_info, gboolean multiple_resources) +{ + char *text = NULL; + char *res = NULL; + char *label, *value; + const char *state; + + if(jbr->status) { + char *tmp; + text = purple_strreplace(jbr->status, "\n", "<br />\n"); + tmp = purple_markup_strip_html(text); + g_free(text); + text = g_markup_escape_text(tmp, -1); + g_free(tmp); + } + + if(jbr->name) + res = g_strdup_printf(" (%s)", jbr->name); + + state = jabber_buddy_state_get_name(jbr->state); + if (text != NULL && !purple_utf8_strcasecmp(state, text)) { + g_free(text); + text = NULL; + } + + label = g_strdup_printf("%s%s", _("Status"), (res ? res : "")); + value = g_strdup_printf("%s%s%s", state, (text ? ": " : ""), (text ? text : "")); + + purple_notify_user_info_add_pair(user_info, label, value); + g_free(label); + g_free(value); + g_free(text); + + /* if the resource is idle, show that */ + /* only show it if there is more than one resource available for + the buddy, since the "general" idleness will be shown anyway, + this way we can see see the idleness of lower-priority resources */ + if (jbr->idle && multiple_resources) { + gchar *idle_str = + purple_str_seconds_to_string(time(NULL) - jbr->idle); + label = g_strdup_printf("%s%s", _("Idle"), (res ? res : "")); + purple_notify_user_info_add_pair(user_info, label, idle_str); + g_free(idle_str); + g_free(label); + } + g_free(res); +} + void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) { JabberBuddy *jb; @@ -1846,28 +1962,28 @@ const char *sub; GList *l; const char *mood; - + gboolean multiple_resources = + jb->resources && g_list_next(jb->resources); + JabberBuddyResource *top_jbr = jabber_buddy_find_resource(jb, NULL); + + /* resource-specific info for the top resource */ + if (top_jbr) { + jabber_tooltip_add_resource_text(top_jbr, user_info, + multiple_resources); + } + + for(l=jb->resources; l; l = l->next) { + jbr = l->data; + /* the remaining resources */ + if (jbr != top_jbr) { + jabber_tooltip_add_resource_text(jbr, user_info, + multiple_resources); + } + } + if (full) { PurpleStatus *status; - if(jb->subscription & JABBER_SUB_FROM) { - if(jb->subscription & JABBER_SUB_TO) - sub = _("Both"); - else if(jb->subscription & JABBER_SUB_PENDING) - sub = _("From (To pending)"); - else - sub = _("From"); - } else { - if(jb->subscription & JABBER_SUB_TO) - sub = _("To"); - else if(jb->subscription & JABBER_SUB_PENDING) - sub = _("None (To pending)"); - else - sub = _("None"); - } - - purple_notify_user_info_add_pair(user_info, _("Subscription"), sub); - status = purple_presence_get_active_status(presence); mood = purple_status_get_attr_string(status, "mood"); if(mood != NULL) { @@ -1892,47 +2008,25 @@ g_free(playing); } } - } - - for(l=jb->resources; l; l = l->next) { - char *text = NULL; - char *res = NULL; - char *label, *value; - const char *state; - - jbr = l->data; - - if(jbr->status) { - char *tmp; - text = purple_strreplace(jbr->status, "\n", "<br />\n"); - tmp = purple_markup_strip_html(text); - g_free(text); - text = g_markup_escape_text(tmp, -1); - g_free(tmp); + + if(jb->subscription & JABBER_SUB_FROM) { + if(jb->subscription & JABBER_SUB_TO) + sub = _("Both"); + else if(jb->subscription & JABBER_SUB_PENDING) + sub = _("From (To pending)"); + else + sub = _("From"); + } else { + if(jb->subscription & JABBER_SUB_TO) + sub = _("To"); + else if(jb->subscription & JABBER_SUB_PENDING) + sub = _("None (To pending)"); + else + sub = _("None"); } - if(jbr->name) - res = g_strdup_printf(" (%s)", jbr->name); - - state = jabber_buddy_state_get_name(jbr->state); - if (text != NULL && !purple_utf8_strcasecmp(state, text)) { - g_free(text); - text = NULL; - } - - label = g_strdup_printf("%s%s", - _("Status"), (res ? res : "")); - value = g_strdup_printf("%s%s%s", - state, - (text ? ": " : ""), - (text ? text : "")); - - purple_notify_user_info_add_pair(user_info, label, value); - - g_free(label); - g_free(value); - g_free(text); - g_free(res); + purple_notify_user_info_add_pair(user_info, _("Subscription"), sub); + } if(!PURPLE_BUDDY_IS_ONLINE(b) && jb->error_msg) { @@ -2055,14 +2149,11 @@ } static void -jabber_password_change_result_cb(JabberStream *js, xmlnode *packet, - gpointer data) +jabber_password_change_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { - const char *type; - - type = xmlnode_get_attrib(packet, "type"); - - if(type && !strcmp(type, "result")) { + if (type == JABBER_IQ_RESULT) { purple_notify_info(js->gc, _("Password Changed"), _("Password Changed"), _("Your password has been changed.")); @@ -2428,7 +2519,25 @@ if (!chat) return PURPLE_CMD_RET_FAILED; - jabber_chat_change_topic(chat, args ? args[0] : NULL); + if (args && args[0] && *args[0]) + jabber_chat_change_topic(chat, args[0]); + else { + const char *cur = purple_conv_chat_get_topic(PURPLE_CONV_CHAT(conv)); + char *buf, *tmp, *tmp2; + + if (cur) { + tmp = g_markup_escape_text(cur, -1); + tmp2 = purple_markup_linkify(tmp); + buf = g_strdup_printf(_("current topic is: %s"), tmp2); + g_free(tmp); + g_free(tmp2); + } else + buf = g_strdup(_("No topic is set")); + purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "", buf, + PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG, time(NULL)); + g_free(buf); + } + return PURPLE_CMD_RET_OK; } @@ -2594,10 +2703,16 @@ static PurpleCmdRet jabber_cmd_ping(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) { + PurpleAccount *account; + PurpleConnection *pc; + if(!args || !args[0]) return PURPLE_CMD_RET_FAILED; - if(!jabber_ping_jid(conv, args[0])) { + account = purple_conversation_get_account(conv); + pc = purple_account_get_connection(account); + + if(!jabber_ping_jid(purple_connection_get_protocol_data(pc), args[0])) { *error = g_strdup_printf(_("Unable to ping user %s"), args[0]); return PURPLE_CMD_RET_FAILED; } @@ -2730,8 +2845,8 @@ } #ifdef USE_VV -static gboolean -feature_audio_enabled(JabberStream *js, const char *namespace) +gboolean +jabber_audio_enabled(JabberStream *js, const char *namespace) { PurpleMediaManager *manager = purple_media_manager_get(); PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager); @@ -2740,7 +2855,7 @@ } static gboolean -feature_video_enabled(JabberStream *js, const char *namespace) +jabber_video_enabled(JabberStream *js, const char *namespace) { PurpleMediaManager *manager = purple_media_manager_get(); PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager); @@ -2749,7 +2864,7 @@ } typedef struct { - PurpleConnection *pc; + PurpleAccount *account; gchar *who; PurpleMediaSessionType type; @@ -2772,7 +2887,7 @@ GList *labels = purple_request_field_choice_get_labels(field); gchar *who = g_strdup_printf("%s/%s", request->who, (gchar*)g_list_nth_data(labels, selected_id)); - jabber_initiate_media(request->pc, who, request->type); + jabber_initiate_media(request->account, who, request->type); g_free(who); g_free(request->who); @@ -2781,11 +2896,12 @@ #endif gboolean -jabber_initiate_media(PurpleConnection *gc, const char *who, +jabber_initiate_media(PurpleAccount *account, const char *who, PurpleMediaSessionType type) { #ifdef USE_VV - JabberStream *js = (JabberStream *) gc->proto_data; + JabberStream *js = (JabberStream *) + purple_account_get_connection(account)->proto_data; JabberBuddy *jb; JabberBuddyResource *jbr = NULL; char *resource; @@ -2810,11 +2926,9 @@ JINGLE_APP_RTP_SUPPORT_AUDIO) && jabber_resource_has_capability(jbr, GOOGLE_VOICE_CAP)) - return jabber_google_session_initiate( - gc->proto_data, who, type); + return jabber_google_session_initiate(js, who, type); else - return jingle_rtp_initiate_media( - gc->proto_data, who, type); + return jingle_rtp_initiate_media(js, who, type); } jb = jabber_buddy_find(js, who, FALSE); @@ -2826,14 +2940,14 @@ char *msg; if(!jb) { - msg = g_strdup_printf(_("Unable to initiate media with %s, invalid JID"), who); + msg = g_strdup_printf(_("Unable to initiate media with %s: invalid JID"), who); } else if(jb->subscription & JABBER_SUB_TO) { - msg = g_strdup_printf(_("Unable to initiate media with %s, user is not online"), who); + msg = g_strdup_printf(_("Unable to initiate media with %s: user is not online"), who); } else { - msg = g_strdup_printf(_("Unable to initiate media with %s, not subscribed to user presence"), who); + msg = g_strdup_printf(_("Unable to initiate media with %s: not subscribed to user presence"), who); } - purple_notify_error(js->gc, _("Media Initiation Failed"), + purple_notify_error(account, _("Media Initiation Failed"), _("Media Initiation Failed"), msg); g_free(msg); return FALSE; @@ -2844,7 +2958,7 @@ gboolean result; jbr = jb->resources->data; name = g_strdup_printf("%s/%s", who, jbr->name); - result = jabber_initiate_media(gc, name, type); + result = jabber_initiate_media(account, name, type); g_free(name); return result; } else { @@ -2864,7 +2978,7 @@ PurpleMediaCaps caps; gchar *name; name = g_strdup_printf("%s/%s", who, ljbr->name); - caps = jabber_get_media_caps(gc, name); + caps = jabber_get_media_caps(account, name); g_free(name); if ((type & PURPLE_MEDIA_AUDIO) && @@ -2899,26 +3013,26 @@ gboolean result; purple_request_field_destroy(field); name = g_strdup_printf("%s/%s", who, jbr->name); - result = jabber_initiate_media(gc, name, type); + result = jabber_initiate_media(account, name, type); g_free(name); return result; } - msg = g_strdup_printf(_("Please select the resource of %s to which you would like to start a media session with."), who); + msg = g_strdup_printf(_("Please select the resource of %s with which you would like to start a media session."), who); fields = purple_request_fields_new(); group = purple_request_field_group_new(NULL); request = g_new0(JabberMediaRequest, 1); - request->pc = gc; + request->account = account; request->who = g_strdup(who); request->type = type; purple_request_field_group_add_field(group, field); purple_request_fields_add_group(fields, group); - purple_request_fields(gc, _("Select a Resource"), msg, NULL, - fields, _("Initiate Media"), + purple_request_fields(account, _("Select a Resource"), msg, + NULL, fields, _("Initiate Media"), G_CALLBACK(jabber_media_ok_cb), _("Cancel"), G_CALLBACK(jabber_media_cancel_cb), - gc->account, who, NULL, request); + account, who, NULL, request); g_free(msg); return TRUE; @@ -2927,10 +3041,11 @@ return FALSE; } -PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who) +PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who) { #ifdef USE_VV - JabberStream *js = (JabberStream *) gc->proto_data; + JabberStream *js = (JabberStream *) + purple_account_get_connection(account)->proto_data; JabberBuddy *jb; JabberBuddyResource *jbr; PurpleMediaCaps caps = PURPLE_MEDIA_CAPS_NONE; @@ -2996,7 +3111,7 @@ gchar *name; jbr = jb->resources->data; name = g_strdup_printf("%s/%s", who, jbr->name); - caps = jabber_get_media_caps(gc, name); + caps = jabber_get_media_caps(account, name); g_free(name); } else { /* we've got multiple resources, combine their caps */ @@ -3007,7 +3122,7 @@ gchar *name; jbr = l->data; name = g_strdup_printf("%s/%s", who, jbr->name); - caps |= jabber_get_media_caps(gc, name); + caps |= jabber_get_media_caps(account, name); g_free(name); } } @@ -3181,12 +3296,13 @@ /* Jingle features! */ jabber_add_feature(JINGLE, 0); jabber_add_feature(JINGLE_TRANSPORT_RAWUDP, 0); - jabber_add_feature(JINGLE_TRANSPORT_ICEUDP, 0); + #ifdef USE_VV - jabber_add_feature("http://www.google.com/xmpp/protocol/session", feature_audio_enabled); - jabber_add_feature("http://www.google.com/xmpp/protocol/voice/v1", feature_audio_enabled); - jabber_add_feature(JINGLE_APP_RTP_SUPPORT_AUDIO, feature_audio_enabled); - jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO, feature_video_enabled); + jabber_add_feature("http://www.google.com/xmpp/protocol/session", jabber_audio_enabled); + jabber_add_feature("http://www.google.com/xmpp/protocol/voice/v1", jabber_audio_enabled); + jabber_add_feature(JINGLE_APP_RTP_SUPPORT_AUDIO, jabber_audio_enabled); + jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO, jabber_video_enabled); + jabber_add_feature(JINGLE_TRANSPORT_ICEUDP, 0); #endif /* IPC functions */
--- a/libpurple/protocols/jabber/jabber.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_H_ -#define _PURPLE_JABBER_H_ +#ifndef PURPLE_JABBER_H_ +#define PURPLE_JABBER_H_ typedef enum { JABBER_CAP_NONE = 0, @@ -60,6 +60,7 @@ #include "sslconn.h" #include "dnsquery.h" +#include "iq.h" #include "jutil.h" #include "xmlnode.h" #include "buddy.h" @@ -158,6 +159,7 @@ GList *file_transfers; time_t idle; + time_t old_idle; JabberID *user; PurpleConnection *gc; @@ -165,6 +167,7 @@ gboolean registration; + char *initial_avatar_hash; char *avatar_hash; GSList *pending_avatar_requests; @@ -299,7 +302,8 @@ void jabber_stream_set_state(JabberStream *js, JabberStreamState state); -void jabber_register_parse(JabberStream *js, xmlnode *packet); +void jabber_register_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *query); void jabber_register_start(JabberStream *js); char *jabber_get_next_id(JabberStream *js); @@ -355,12 +359,15 @@ gboolean jabber_offline_message(const PurpleBuddy *buddy); int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len); GList *jabber_actions(PurplePlugin *plugin, gpointer context); -gboolean jabber_initiate_media(PurpleConnection *gc, const char *who, + +gboolean jabber_audio_enabled(JabberStream *js, const char *unused); +gboolean jabber_initiate_media(PurpleAccount *account, const char *who, PurpleMediaSessionType type); -PurpleMediaCaps jabber_get_media_caps(PurpleConnection *gc, const char *who); +PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who); + void jabber_register_commands(void); void jabber_init_plugin(PurplePlugin *plugin); void jabber_uninit_plugin(void); -#endif /* _PURPLE_JABBER_H_ */ +#endif /* PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/jingle/content.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/content.c Tue Apr 28 19:08:06 2009 +0000 @@ -391,7 +391,13 @@ jingle_content_parse(xmlnode *content) { const gchar *type = xmlnode_get_namespace(xmlnode_get_child(content, "description")); - return JINGLE_CONTENT_CLASS(g_type_class_ref(jingle_get_type(type)))->parse(content); + GType jingle_type = jingle_get_type(type); + + if (jingle_type != G_TYPE_NONE) { + return JINGLE_CONTENT_CLASS(g_type_class_ref(jingle_type))->parse(content); + } else { + return NULL; + } } static xmlnode *
--- a/libpurple/protocols/jabber/jingle/content.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/content.h Tue Apr 28 19:08:06 2009 +0000 @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef JINGLE_CONTENT_H -#define JINGLE_CONTENT_H +#ifndef PURPLE_JABBER_JINGLE_CONTENT_H +#define PURPLE_JABBER_JINGLE_CONTENT_H #include "jabber.h" @@ -113,5 +113,5 @@ G_END_DECLS -#endif /* JINGLE_CONTENT_H */ +#endif /* PURPLE_JABBER_JINGLE_CONTENT_H */
--- a/libpurple/protocols/jabber/jingle/iceudp.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/iceudp.h Tue Apr 28 19:08:06 2009 +0000 @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef JINGLE_ICEUDP_H -#define JINGLE_ICEUDP_H +#ifndef PURPLE_JABBER_JINGLE_ICEUDP_H +#define PURPLE_JABBER_JINGLE_ICEUDP_H #include <glib.h> #include <glib-object.h> @@ -110,5 +110,5 @@ G_END_DECLS -#endif /* JINGLE_ICEUDP_H */ +#endif /* PURPLE_JABBER_JINGLE_ICEUDP_H */
--- a/libpurple/protocols/jabber/jingle/jingle.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.c Tue Apr 28 19:08:06 2009 +0000 @@ -359,28 +359,21 @@ } void -jingle_parse(JabberStream *js, xmlnode *packet) +jingle_parse(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *jingle) { - const gchar *type = xmlnode_get_attrib(packet, "type"); - xmlnode *jingle; const gchar *action; const gchar *sid; JingleActionType action_type; JingleSession *session; - if (!type || strcmp(type, "set")) { - /* send iq error here */ - return; - } - - /* is this a Jingle package? */ - if (!(jingle = xmlnode_get_child(packet, "jingle"))) { - /* send iq error here */ + if (type != JABBER_IQ_SET) { + /* TODO: send iq error here */ return; } if (!(action = xmlnode_get_attrib(jingle, "action"))) { - /* send iq error here */ + /* TODO: send iq error here */ return; } @@ -409,9 +402,10 @@ /* send iq error */ return; } else { - session = jingle_session_create(js, sid, - xmlnode_get_attrib(packet, "to"), - xmlnode_get_attrib(packet, "from"), FALSE); + char *own_jid = g_strdup_printf("%s@%s/%s", js->user->node, + js->user->domain, js->user->resource); + session = jingle_session_create(js, sid, own_jid, from, FALSE); + g_free(own_jid); } }
--- a/libpurple/protocols/jabber/jingle/jingle.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.h Tue Apr 28 19:08:06 2009 +0000 @@ -16,8 +16,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA */ -#ifndef JINGLE_H -#define JINGLE_H +#ifndef PURPLE_JABBER_JINGLE_H +#define PURPLE_JABBER_JINGLE_H #include "jabber.h" @@ -69,7 +69,8 @@ GType jingle_get_type(const gchar *type); -void jingle_parse(JabberStream *js, xmlnode *packet); +void jingle_parse(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *child); void jingle_terminate_sessions(JabberStream *js); @@ -83,4 +84,4 @@ G_END_DECLS -#endif /* JINGLE_H */ +#endif /* PURPLE_JABBER_JINGLE_H */
--- a/libpurple/protocols/jabber/jingle/rawudp.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/rawudp.h Tue Apr 28 19:08:06 2009 +0000 @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef JINGLE_RAWUDP_H -#define JINGLE_RAWUDP_H +#ifndef PURPLE_JABBER_JINGLE_RAWUDP_H +#define PURPLE_JABBER_JINGLE_RAWUDP_H #include <glib.h> #include <glib-object.h> @@ -97,5 +97,5 @@ G_END_DECLS -#endif /* JINGLE_RAWUDP_H */ +#endif /* PURPLE_JABBER_JINGLE_RAWUDP_H */
--- a/libpurple/protocols/jabber/jingle/rtp.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/rtp.c Tue Apr 28 19:08:06 2009 +0000 @@ -204,8 +204,9 @@ { JabberStream *js = jingle_session_get_js(session); PurpleMedia *media = NULL; - GList *iter = purple_media_manager_get_media_by_connection( - purple_media_manager_get(), js->gc); + GList *iter = purple_media_manager_get_media_by_account( + purple_media_manager_get(), + purple_connection_get_account(js->gc)); for (; iter; iter = g_list_delete_link(iter, iter)) { JingleSession *media_session = @@ -438,12 +439,13 @@ } static void -jingle_rtp_initiate_ack_cb(JabberStream *js, xmlnode *packet, gpointer data) +jingle_rtp_initiate_ack_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JingleSession *session = data; - if (!strcmp(xmlnode_get_attrib(packet, "type"), "error") || - xmlnode_get_child(packet, "error")) { + if (type == JABBER_IQ_ERROR || xmlnode_get_child(packet, "error")) { purple_media_end(jingle_rtp_get_media(session), NULL, NULL); g_object_unref(session); return; @@ -465,6 +467,9 @@ { purple_debug_info("jingle-rtp", "stream-info: type %d " "id: %s name: %s\n", type, sid, name); + + g_return_if_fail(JINGLE_IS_SESSION(session)); + if (type == PURPLE_MEDIA_INFO_HANGUP) { jabber_iq_send(jingle_session_terminate_packet( session, "success")); @@ -517,9 +522,11 @@ JabberStream *js = jingle_session_get_js(session); gchar *remote_jid = jingle_session_get_remote_jid(session); - PurpleMedia *media = purple_media_manager_create_media(purple_media_manager_get(), - js->gc, "fsrtpconference", remote_jid, - jingle_session_is_initiator(session)); + PurpleMedia *media = purple_media_manager_create_media( + purple_media_manager_get(), + purple_connection_get_account(js->gc), + "fsrtpconference", remote_jid, + jingle_session_is_initiator(session)); g_free(remote_jid); if (!media) {
--- a/libpurple/protocols/jabber/jingle/rtp.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/rtp.h Tue Apr 28 19:08:06 2009 +0000 @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef JINGLE_RTP_H -#define JINGLE_RTP_H +#ifndef PURPLE_JABBER_JINGLE_RTP_H +#define PURPLE_JABBER_JINGLE_RTP_H #include "config.h" @@ -88,5 +88,5 @@ #endif /* USE_VV */ -#endif /* JINGLE_RTP_H */ +#endif /* PURPLE_JABBER_JINGLE_RTP_H */
--- a/libpurple/protocols/jabber/jingle/session.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/session.c Tue Apr 28 19:08:06 2009 +0000 @@ -363,17 +363,18 @@ g_hash_table_lookup(js->sessions, sid) : NULL; } +#if GLIB_CHECK_VERSION(2,4,0) static gboolean find_by_jid_ghr(gpointer key, gpointer value, gpointer user_data) { JingleSession *session = (JingleSession *)value; const gchar *jid = user_data; - gboolean use_bare = strchr(jid, '/') == NULL; + gboolean use_bare = g_utf8_strchr(jid, -1, '/') == NULL; gchar *remote_jid = jingle_session_get_remote_jid(session); gchar *cmp_jid = use_bare ? jabber_get_bare_jid(remote_jid) : g_strdup(remote_jid); g_free(remote_jid); - if (!strcmp(jid, cmp_jid)) { + if (g_str_equal(jid, cmp_jid)) { g_free(cmp_jid); return TRUE; } @@ -382,12 +383,58 @@ return FALSE; } +#else /* GLIB_CHECK_VERSION 2.4.0 */ + +/* Ugly code; g_hash_table_find version above is much nicer */ +struct session_find_jid +{ + const gchar *jid; + JingleSession *ret; + gboolean use_bare; +}; + +static void find_by_jid_ghr(gpointer key, gpointer value, gpointer user_data) +{ + JingleSession *session = (JingleSession *)value; + struct session_find_jid *data = user_data; + gchar *remote_jid; + gchar *cmp_jid; + + if (data->ret != NULL) + return; + + remote_jid = jingle_session_get_remote_jid(session); + cmp_jid = data->use_bare ? jabber_get_bare_jid(remote_jid) + : g_strdup(remote_jid); + g_free(remote_jid); + + if (g_str_equal(data->jid, cmp_jid)) + data->ret = session; + + g_free(cmp_jid); +} +#endif /* GLIB_CHECK_VERSION 2.4.0 */ + JingleSession * jingle_session_find_by_jid(JabberStream *js, const gchar *jid) { +#if GLIB_CHECK_VERSION(2,4,0) return js->sessions != NULL ? g_hash_table_find(js->sessions, find_by_jid_ghr, (gpointer)jid) : NULL; +#else + struct session_find_jid data; + + if (js->sessions == NULL) + return NULL; + + data.jid = jid; + data.ret = NULL; + data.use_bare = g_utf8_strchr(jid, -1, '/') == NULL; + + g_hash_table_foreach(js->sessions, find_by_jid_ghr, &data); + return data.ret; +#endif } static xmlnode *
--- a/libpurple/protocols/jabber/jingle/session.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/session.h Tue Apr 28 19:08:06 2009 +0000 @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef JINGLE_SESSION_H -#define JINGLE_SESSION_H +#ifndef PURPLE_JABBER_JINGLE_SESSION_H +#define PURPLE_JABBER_JINGLE_SESSION_H #include "iq.h" #include "jabber.h" @@ -111,5 +111,5 @@ G_END_DECLS -#endif /* JINGLE_SESSION_H */ +#endif /* PURPLE_JABBER_JINGLE_SESSION_H */
--- a/libpurple/protocols/jabber/jingle/transport.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/transport.h Tue Apr 28 19:08:06 2009 +0000 @@ -18,8 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#ifndef JINGLE_TRANSPORT_H -#define JINGLE_TRANSPORT_H +#ifndef PURPLE_JABBER_JINGLE_TRANSPORT_H +#define PURPLE_JABBER_JINGLE_TRANSPORT_H #include <glib.h> #include <glib-object.h> @@ -84,5 +84,5 @@ G_END_DECLS -#endif /* JINGLE_TRANSPORT_H */ +#endif /* PURPLE_JABBER_JINGLE_TRANSPORT_H */
--- a/libpurple/protocols/jabber/jutil.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/jutil.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_JUTIL_H_ -#define _PURPLE_JABBER_JUTIL_H_ +#ifndef PURPLE_JABBER_JUTIL_H_ +#define PURPLE_JABBER_JUTIL_H_ typedef struct _JabberID { char *node; @@ -43,4 +43,4 @@ PurpleConversation *jabber_find_unnormalized_conv(const char *name, PurpleAccount *account); char *jabber_calculate_data_sha1sum(gconstpointer data, size_t len); -#endif /* _PURPLE_JABBER_JUTIL_H_ */ +#endif /* PURPLE_JABBER_JUTIL_H_ */
--- a/libpurple/protocols/jabber/libxmpp.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Tue Apr 28 19:08:06 2009 +0000 @@ -216,19 +216,24 @@ #endif PurpleAccountUserSplit *split; PurpleAccountOption *option; + /* Translators: 'domain' is used here in the context of Internet domains, e.g. pidgin.im */ split = purple_account_user_split_new(_("Domain"), NULL, '@'); purple_account_user_split_set_reverse(split, FALSE); prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); + split = purple_account_user_split_new(_("Resource"), NULL, '/'); purple_account_user_split_set_reverse(split, FALSE); prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); + option = purple_account_option_bool_new(_("Require SSL/TLS"), "require_tls", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_bool_new(_("Force old (port 5223) SSL"), "old_ssl", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_bool_new( _("Allow plaintext auth over unencrypted streams"), "auth_plain_in_clear", FALSE); @@ -294,18 +299,10 @@ jabber_ibb_init(); jabber_si_init(); - jabber_add_feature(AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb); - jabber_add_feature(AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb); jabber_add_feature("http://www.xmpp.org/extensions/xep-0224.html#ns", jabber_buzz_isenabled); jabber_add_feature(XEP_0231_NAMESPACE, jabber_custom_smileys_isenabled); jabber_add_feature(XEP_0047_NAMESPACE, NULL); - - jabber_pep_register_handler(AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata); - -#ifdef USE_VV - jabber_add_feature("http://www.xmpp.org/extensions/xep-0167.html#ns", NULL); -#endif }
--- a/libpurple/protocols/jabber/message.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/message.c Tue Apr 28 19:08:06 2009 +0000 @@ -24,6 +24,7 @@ #include "notify.h" #include "server.h" #include "util.h" +#include "adhoccommands.h" #include "buddy.h" #include "chat.h" #include "data.h" @@ -477,7 +478,9 @@ } JabberDataRef; static void -jabber_message_get_data_cb(JabberStream *js, xmlnode *packet, gpointer data) +jabber_message_get_data_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { JabberDataRef *ref = (JabberDataRef *) data; PurpleConversation *conv = ref->conv; @@ -595,8 +598,11 @@ /* The following tests expect xmlns != NULL */ continue; } else if(!strcmp(child->name, "subject") && !strcmp(xmlns,"jabber:client")) { - if(!jm->subject) + if(!jm->subject) { jm->subject = xmlnode_get_data(child); + if(!jm->subject) + jm->subject = g_strdup(""); + } } else if(!strcmp(child->name, "thread") && !strcmp(xmlns,"jabber:client")) { if(!jm->thread_id) jm->thread_id = xmlnode_get_data(child); @@ -624,24 +630,28 @@ purple_debug_info("jabber", "found %d smileys\n", g_list_length(smiley_refs)); - if (jm->type == JABBER_MESSAGE_GROUPCHAT) { - JabberID *jid = jabber_id_new(jm->from); - JabberChat *chat = NULL; + if (smiley_refs) { + if (jm->type == JABBER_MESSAGE_GROUPCHAT) { + JabberID *jid = jabber_id_new(jm->from); + JabberChat *chat = NULL; - if (jid) { - chat = jabber_chat_find(js, jid->node, jid->domain); - if (chat) conv = chat->conv; - } + if (jid) { + chat = jabber_chat_find(js, jid->node, jid->domain); + if (chat) conv = chat->conv; + } - jabber_id_free(jid); - } else { - conv = - purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, - who, account); - if (!conv) { - /* we need to create the conversation here */ - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, - account, who); + jabber_id_free(jid); + } else if (jm->type == JABBER_MESSAGE_NORMAL || + jm->type == JABBER_MESSAGE_CHAT) { + conv = + purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, + who, account); + if (!conv) { + /* we need to create the conversation here */ + conv = + purple_conversation_new(PURPLE_CONV_TYPE_IM, + account, who); + } } } @@ -673,7 +683,7 @@ TRUE)) { const JabberData *data = jabber_data_find_remote_by_cid(cid); - /* if data is already known, we add write it immediatly */ + /* if data is already known, we write it immediatly */ if (data) { purple_debug_info("jabber", "data is already known\n"); @@ -771,6 +781,12 @@ } else { jm->etc = g_list_append(jm->etc, child); } + } else if (g_str_equal(child->name, "query")) { + const char *node = xmlnode_get_attrib(child, "node"); + if (purple_strequal(xmlns, "http://jabber.org/protocol/disco#items") + && purple_strequal(node, "http://jabber.org/protocol/commands")) { + jabber_adhoc_got_list(js, jm->from, child); + } } }
--- a/libpurple/protocols/jabber/message.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/message.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_MESSAGE_H_ -#define _PURPLE_JABBER_MESSAGE_H_ +#ifndef PURPLE_JABBER_MESSAGE_H_ +#define PURPLE_JABBER_MESSAGE_H_ #include "buddy.h" #include "jabber.h" @@ -84,4 +84,4 @@ gboolean jabber_custom_smileys_isenabled(JabberStream *js, const const gchar *namespace); -#endif /* _PURPLE_JABBER_MESSAGE_H_ */ +#endif /* PURPLE_JABBER_MESSAGE_H_ */
--- a/libpurple/protocols/jabber/oob.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/oob.c Tue Apr 28 19:08:06 2009 +0000 @@ -187,18 +187,18 @@ jabber_oob_xfer_recv_error(xfer, "404"); } -void jabber_oob_parse(JabberStream *js, xmlnode *packet) { +void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *querynode) { JabberOOBXfer *jox; PurpleXfer *xfer; char *filename; char *url; - const char *type; - xmlnode *querynode, *urlnode; + xmlnode *urlnode; - if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "set")) + if(type != JABBER_IQ_SET) return; - if(!(querynode = xmlnode_get_child(packet, "query"))) + if(!from) return; if(!(urlnode = xmlnode_get_child(querynode, "url"))) @@ -211,10 +211,9 @@ g_free(url); jox->js = js; jox->headers = g_string_new(""); - jox->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); + jox->iq_id = g_strdup(id); - xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, - xmlnode_get_attrib(packet, "from")); + xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from); if (xfer) { xfer->data = jox;
--- a/libpurple/protocols/jabber/oob.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/oob.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,9 +19,12 @@ * 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_JABBER_OOB_H_ -#define _PURPLE_JABBER_OOB_H_ +#ifndef PURPLE_JABBER_OOB_H_ +#define PURPLE_JABBER_OOB_H_ + +#include "jabber.h" -void jabber_oob_parse(JabberStream *js, xmlnode *packet); +void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *querynode); -#endif /* _PURPLE_JABBER_OOB_H_ */ +#endif /* PURPLE_JABBER_OOB_H_ */
--- a/libpurple/protocols/jabber/parser.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/parser.c Tue Apr 28 19:08:06 2009 +0000 @@ -207,6 +207,12 @@ jabber_parser_free(js); } +void +jabber_parser_close_stream(JabberStream *js) +{ + xmlParseChunk(js->context, "</stream:stream>", 16 /* length */, 0); +} + void jabber_parser_free(JabberStream *js) { if (js->context) { xmlParseChunk(js->context, NULL,0,1); @@ -226,8 +232,17 @@ xmlParseChunk(js->context, "", 0, 0); } else if ((ret = xmlParseChunk(js->context, buf, len, 0)) != XML_ERR_OK) { xmlError *err = xmlCtxtGetLastError(js->context); + /* + * libxml2 uses a global setting to determine whether or not to store + * warnings. Other libraries may set this, which causes err to be + * NULL. See #8136 for details. + */ + xmlErrorLevel level = XML_ERR_WARNING; - switch (err->level) { + if (err) + level = err->level; + + switch (level) { case XML_ERR_NONE: purple_debug_info("jabber", "xmlParseChunk returned info %i\n", ret); break;
--- a/libpurple/protocols/jabber/parser.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/parser.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,13 +19,14 @@ * 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_JABBER_PARSER_H_ -#define _PURPLE_JABBER_PARSER_H_ +#ifndef PURPLE_JABBER_PARSER_H_ +#define PURPLE_JABBER_PARSER_H_ #include "jabber.h" void jabber_parser_setup(JabberStream *js); +void jabber_parser_close_stream(JabberStream *js); void jabber_parser_free(JabberStream *js); void jabber_parser_process(JabberStream *js, const char *buf, int len); -#endif /* _PURPLE_JABBER_PARSER_H_ */ +#endif /* PURPLE_JABBER_PARSER_H_ */
--- a/libpurple/protocols/jabber/pep.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/pep.c Tue Apr 28 19:08:06 2009 +0000 @@ -24,6 +24,7 @@ #include "pep.h" #include "iq.h" #include <string.h> +#include "useravatar.h" #include "usermood.h" #include "usernick.h" #include "usertune.h" @@ -35,6 +36,7 @@ pep_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); /* register PEP handlers */ + jabber_avatar_init(); jabber_mood_init(); jabber_tune_init(); jabber_nick_init(); @@ -60,8 +62,11 @@ g_hash_table_replace(pep_handlers, g_strdup(xmlns), handlerfunc); } -static void do_pep_iq_request_item_callback(JabberStream *js, xmlnode *packet, gpointer data) { - const char *from = xmlnode_get_attrib(packet,"from"); +static void +do_pep_iq_request_item_callback(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ xmlnode *pubsub = xmlnode_get_child_with_namespace(packet,"pubsub","http://jabber.org/protocol/pubsub"); xmlnode *items = NULL; JabberPEPHandler *cb = data; @@ -101,7 +106,12 @@ /* this may be called even when the own server doesn't support pep! */ JabberPEPHandler *jph; GList *itemslist; - char *jid = jabber_get_bare_jid(jm->from); + char *jid; + + if (jm->type != JABBER_MESSAGE_EVENT) + return; + + jid = jabber_get_bare_jid(jm->from); for(itemslist = jm->eventitems; itemslist; itemslist = itemslist->next) { xmlnode *items = (xmlnode*)itemslist->data; @@ -115,6 +125,25 @@ g_free(jid); } +void jabber_pep_delete_node(JabberStream *js, const gchar *node) +{ + JabberIq *iq; + xmlnode *pubsub, *del; + + g_return_if_fail(node != NULL); + g_return_if_fail(js->pep); + + iq = jabber_iq_new(js, JABBER_IQ_SET); + + pubsub = xmlnode_new_child(iq->node, "pubsub"); + xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub#owner"); + + del = xmlnode_new_child(pubsub, "delete"); + xmlnode_set_attrib(del, "node", node); + + jabber_iq_send(iq); +} + void jabber_pep_publish(JabberStream *js, xmlnode *publish) { JabberIq *iq; xmlnode *pubsub;
--- a/libpurple/protocols/jabber/pep.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/pep.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * */ -#ifndef _PURPLE_JABBER_PEP_H_ -#define _PURPLE_JABBER_PEP_H_ +#ifndef PURPLE_JABBER_PEP_H_ +#define PURPLE_JABBER_PEP_H_ #include "jabber.h" #include "message.h" @@ -73,6 +73,11 @@ void jabber_handle_event(JabberMessage *jm); +/** + * Delete the specified PEP node. + */ +void jabber_pep_delete_node(JabberStream *js, const gchar *node); + /* * Publishes PEP item(s) * @@ -81,4 +86,4 @@ */ void jabber_pep_publish(JabberStream *js, xmlnode *publish); -#endif /* _PURPLE_JABBER_PEP_H_ */ +#endif /* PURPLE_JABBER_PEP_H_ */
--- a/libpurple/protocols/jabber/ping.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/ping.c Tue Apr 28 19:08:06 2009 +0000 @@ -23,50 +23,58 @@ #include "internal.h" #include "debug.h" -#include "xmlnode.h" #include "jabber.h" #include "ping.h" #include "iq.h" -void -jabber_ping_parse(JabberStream *js, xmlnode *packet) +static void jabber_keepalive_pong_cb(JabberStream *js) { - JabberIq *iq; - - purple_debug_info("jabber", "jabber_ping_parse\n"); - - iq = jabber_iq_new(js, JABBER_IQ_RESULT); - - xmlnode_set_attrib(iq->node, "to", xmlnode_get_attrib(packet, "from") ); - - jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id")); - - jabber_iq_send(iq); + purple_timeout_remove(js->keepalive_timeout); + js->keepalive_timeout = -1; } -static void jabber_ping_result_cb(JabberStream *js, xmlnode *packet, - gpointer data) +void +jabber_ping_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *ping) { - const char *type = xmlnode_get_attrib(packet, "type"); + if (type == JABBER_IQ_GET) { + JabberIq *iq = jabber_iq_new(js, JABBER_IQ_RESULT); + + if (from) + xmlnode_set_attrib(iq->node, "to", from); + xmlnode_set_attrib(iq->node, "id", id); - purple_debug_info("jabber", "jabber_ping_result_cb\n"); - if(type && !strcmp(type, "result")) { + jabber_iq_send(iq); + } else if (type == JABBER_IQ_SET) { + /* XXX: error */ + } +} + +static void jabber_ping_result_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) +{ + if (purple_strequal(from, js->user->domain)) + /* If the pong is from the server, assume it's a result of the + * keepalive functions */ + jabber_keepalive_pong_cb(js); + + if (type == JABBER_IQ_RESULT) { purple_debug_info("jabber", "PONG!\n"); } else { purple_debug_info("jabber", "(not supported)\n"); } } -gboolean jabber_ping_jid(PurpleConversation *conv, const char *jid) +gboolean jabber_ping_jid(JabberStream *js, const char *jid) { JabberIq *iq; xmlnode *ping; - purple_debug_info("jabber", "jabber_ping_jid\n"); - - iq = jabber_iq_new(conv->account->gc->proto_data, JABBER_IQ_GET); - xmlnode_set_attrib(iq->node, "to", jid); + iq = jabber_iq_new(js, JABBER_IQ_GET); + if (jid) + xmlnode_set_attrib(iq->node, "to", jid); ping = xmlnode_new_child(iq->node, "ping"); xmlnode_set_namespace(ping, "urn:xmpp:ping"); @@ -74,7 +82,5 @@ jabber_iq_set_callback(iq, jabber_ping_result_cb, NULL); jabber_iq_send(iq); - - return TRUE; }
--- a/libpurple/protocols/jabber/ping.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/ping.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,17 +19,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _PURPLE_JABBER_PING_H_ -#define _PURPLE_JABBER_PING_H_ +#ifndef PURPLE_JABBER_PING_H_ +#define PURPLE_JABBER_PING_H_ #include "jabber.h" -#include "conversation.h" - -void jabber_ping_parse(JabberStream *js, - xmlnode *packet); +#include "iq.h" +#include "xmlnode.h" - -gboolean jabber_ping_jid(PurpleConversation *conv, const char *jid); +void jabber_ping_parse(JabberStream *js, const char *from, + JabberIqType, const char *id, xmlnode *child); +gboolean jabber_ping_jid(JabberStream *js, const char *jid); - -#endif /* _PURPLE_JABBER_PING_H_ */ +#endif /* PURPLE_JABBER_PING_H_ */
--- a/libpurple/protocols/jabber/presence.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/presence.c Tue Apr 28 19:08:06 2009 +0000 @@ -154,17 +154,25 @@ /* check if there are any differences to the <presence> and send them in that case */ if (force || allowBuzz != js->allowBuzz || js->old_state != state || CHANGED(js->old_msg, stripped) || js->old_priority != priority || - CHANGED(js->old_avatarhash, js->avatar_hash)) { + CHANGED(js->old_avatarhash, js->avatar_hash) || js->old_idle != js->idle) { /* Need to update allowBuzz before creating the presence (with caps) */ js->allowBuzz = allowBuzz; presence = jabber_presence_create_js(js, state, stripped, priority); - if(js->avatar_hash) { - x = xmlnode_new_child(presence, "x"); - xmlnode_set_namespace(x, "vcard-temp:x:update"); + /* Per XEP-0153 4.1, we must always send the <x> */ + x = xmlnode_new_child(presence, "x"); + xmlnode_set_namespace(x, "vcard-temp:x:update"); + /* + * FIXME: Per XEP-0153 4.3.2 bullet 2, we must not publish our + * image hash if another resource has logged in and updated the + * vcard avatar. Requires changes in jabber_presence_parse. + */ + if (js->vcard_fetched) { + /* Always publish a <photo>; it's empty if we have no image. */ photo = xmlnode_new_child(x, "photo"); - xmlnode_insert_data(photo, js->avatar_hash, -1); + if (js->avatar_hash) + xmlnode_insert_data(photo, js->avatar_hash, -1); } jabber_send(js, presence); @@ -182,6 +190,7 @@ js->old_avatarhash = g_strdup(js->avatar_hash); js->old_state = state; js->old_priority = priority; + js->old_idle = js->idle; } g_free(stripped); @@ -263,6 +272,16 @@ g_free(pstr); } + /* if we are idle and not offline, include idle */ + if (js->idle && state != JABBER_BUDDY_STATE_UNAVAILABLE) { + xmlnode *query = xmlnode_new_child(presence, "query"); + gchar seconds[10]; + g_snprintf(seconds, 10, "%d", (int) (time(NULL) - js->idle)); + + xmlnode_set_namespace(query, "jabber:iq:last"); + xmlnode_set_attrib(query, "seconds", seconds); + } + /* JEP-0115 */ /* calculate hash */ jabber_caps_calculate_own_hash(js); @@ -273,6 +292,19 @@ xmlnode_set_attrib(c, "hash", "sha-1"); xmlnode_set_attrib(c, "ver", jabber_caps_get_own_hash(js)); +#ifdef USE_VV + /* + * MASSIVE HUGE DISGUSTING HACK + * This is a huge hack. As far as I can tell, Google Talk's gmail client + * doesn't bother to check the actual features we advertise; they + * just assume that if we specify a 'voice-v1' ext (ignoring that + * these are to be assigned no semantic value), we support receiving voice + * calls. + */ + if (jabber_audio_enabled(js, NULL /* unused */)) + xmlnode_set_attrib(c, "ext", "voice-v1"); +#endif + return presence; } @@ -302,14 +334,14 @@ g_free(jap); } -static void jabber_vcard_parse_avatar(JabberStream *js, xmlnode *packet, gpointer blah) +static void +jabber_vcard_parse_avatar(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer blah) { JabberBuddy *jb = NULL; xmlnode *vcard, *photo, *binval; char *text; - guchar *data; - gsize size; - const char *from = xmlnode_get_attrib(packet, "from"); if(!from) return; @@ -324,10 +356,13 @@ (( (binval = xmlnode_get_child(photo, "BINVAL")) && (text = xmlnode_get_data(binval))) || (text = xmlnode_get_data(photo)))) { - char *hash; + guchar *data; + gchar *hash; + gsize size; data = purple_base64_decode(text, &size); hash = jabber_calculate_data_sha1sum(data, size); + purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash); g_free(hash); g_free(text); @@ -397,6 +432,7 @@ JabberBuddyResource *jbr = NULL, *found_jbr = NULL; PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; gboolean delayed = FALSE; + const gchar *stamp = NULL; /* from <delayed/> element */ PurpleBuddy *b = NULL; char *buddy_name; JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN; @@ -404,6 +440,7 @@ gboolean muc = FALSE; char *avatar_hash = NULL; xmlnode *caps = NULL; + int idle = 0; if(!(jb = jabber_buddy_find(js, from, TRUE))) return; @@ -486,12 +523,14 @@ } else if(!strcmp(y->name, "delay") && !strcmp(xmlns, "urn:xmpp:delay")) { /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ delayed = TRUE; + stamp = xmlnode_get_attrib(y, "stamp"); } else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) { caps = y; /* store for later, when creating buddy resource */ } else if(!strcmp(y->name, "x")) { if(!strcmp(xmlns, "jabber:x:delay")) { /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ delayed = TRUE; + stamp = xmlnode_get_attrib(y, "stamp"); } else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) { xmlnode *z; @@ -542,9 +581,26 @@ avatar_hash = xmlnode_get_data(photo); } } + } else if (!strcmp(y->name, "query") && + !strcmp(xmlnode_get_namespace(y), "jabber:iq:last")) { + /* resource has specified idle */ + const gchar *seconds = xmlnode_get_attrib(y, "seconds"); + if (seconds) { + /* we may need to take "delayed" into account here */ + idle = atoi(seconds); + } } } + if (idle && delayed && stamp) { + /* if we have a delayed presence, we need to add the delay to the idle + value */ + time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL, + NULL); + purple_debug_info("jabber", "got delay %s yielding %ld s offset\n", + stamp, offset); + idle += offset; + } if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) { static int i = 1; @@ -719,6 +775,12 @@ } else { jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state, status); + if (idle) { + jbr->idle = time(NULL) - idle; + } else { + jbr->idle = 0; + } + if(caps) { /* handle XEP-0115 */ const char *node = xmlnode_get_attrib(caps,"node"); @@ -743,6 +805,7 @@ if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { jabber_google_presence_incoming(js, buddy_name, found_jbr); purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL); + purple_prpl_got_user_idle(js->gc->account, buddy_name, found_jbr->idle, found_jbr->idle); } else { purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL); }
--- a/libpurple/protocols/jabber/presence.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/presence.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_PRESENCE_H_ -#define _PURPLE_JABBER_PRESENCE_H_ +#ifndef PURPLE_JABBER_PRESENCE_H_ +#define PURPLE_JABBER_PRESENCE_H_ #include "buddy.h" #include "jabber.h" @@ -45,4 +45,4 @@ void jabber_presence_fake_to_self(JabberStream *js, const PurpleStatus *status); void purple_status_to_jabber(const PurpleStatus *status, JabberBuddyState *state, char **msg, int *priority); -#endif /* _PURPLE_JABBER_PRESENCE_H_ */ +#endif /* PURPLE_JABBER_PRESENCE_H_ */
--- a/libpurple/protocols/jabber/roster.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/roster.c Tue Apr 28 19:08:06 2009 +0000 @@ -145,10 +145,10 @@ g_slist_free(buddies); } -void jabber_roster_parse(JabberStream *js, xmlnode *packet) +void jabber_roster_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *query) { - xmlnode *query, *item, *group; - const char *from = xmlnode_get_attrib(packet, "from"); + xmlnode *item, *group; if(from) { char *from_norm; @@ -169,10 +169,6 @@ return; } - query = xmlnode_get_child(packet, "query"); - if(!query) - return; - js->currently_parsing_roster_push = TRUE; for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item))
--- a/libpurple/protocols/jabber/roster.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/roster.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,14 +19,15 @@ * 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_JABBER_ROSTER_H_ -#define _PURPLE_JABBER_ROSTER_H_ +#ifndef PURPLE_JABBER_ROSTER_H_ +#define PURPLE_JABBER_ROSTER_H_ #include "jabber.h" void jabber_roster_request(JabberStream *js); -void jabber_roster_parse(JabberStream *js, xmlnode *packet); +void jabber_roster_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *query); void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); @@ -39,4 +40,4 @@ void jabber_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -#endif /* _PURPLE_JABBER_ROSTER_H_ */ +#endif /* PURPLE_JABBER_ROSTER_H_ */
--- a/libpurple/protocols/jabber/si.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/si.c Tue Apr 28 19:08:06 2009 +0000 @@ -311,20 +311,18 @@ } } -void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet) +void jabber_bytestreams_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *query) { PurpleXfer *xfer; JabberSIXfer *jsx; - xmlnode *query, *streamhost; - const char *sid, *from, *type; + xmlnode *streamhost; + const char *sid; - if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "set")) + if(type != JABBER_IQ_SET) return; - if(!(from = xmlnode_get_attrib(packet, "from"))) - return; - - if(!(query = xmlnode_get_child(packet, "query"))) + if(!from) return; if(!(sid = xmlnode_get_attrib(query, "sid"))) @@ -340,7 +338,7 @@ if(jsx->iq_id) g_free(jsx->iq_id); - jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); + jsx->iq_id = g_strdup(id); for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost; streamhost = xmlnode_get_next_twin(streamhost)) { @@ -685,13 +683,14 @@ } static void -jabber_si_connect_proxy_cb(JabberStream *js, xmlnode *packet, - gpointer data) +jabber_si_connect_proxy_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { PurpleXfer *xfer = data; JabberSIXfer *jsx; xmlnode *query, *streamhost_used; - const char *from, *type, *jid; + const char *jid; GList *matched; /* TODO: This need to send errors if we don't see what we're looking for */ @@ -708,37 +707,34 @@ jsx = xfer->data; - if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) { - purple_debug_info("jabber", - "jabber_si_xfer_connect_proxy_cb: type = %s\n", - type); - if (type && !strcmp(type, "error")) { - /* if IBB is available, open IBB session */ - purple_debug_info("jabber", - "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n", - jsx->stream_method); - if (jsx->stream_method & STREAM_METHOD_IBB) { - purple_debug_info("jabber", "IBB is possible, try it\n"); - /* if we are the sender and haven't already opened an IBB - session, do so now (we might already have failed to open - the bytestream proxy ourselves when receiving this <iq/> */ - if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND - && !jsx->ibb_session) { - jabber_si_xfer_ibb_send_init(js, xfer); - } else { - jsx->ibb_timeout_handle = purple_timeout_add_seconds(30, - jabber_si_bytestreams_ibb_timeout_cb, xfer); - } - /* if we are receiver, just wait for IBB open stanza, callback - is already set up */ + if(type != JABBER_IQ_RESULT) { + purple_debug_info("jabber", + "jabber_si_xfer_connect_proxy_cb: type = error\n"); + /* if IBB is available, open IBB session */ + purple_debug_info("jabber", + "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n", + jsx->stream_method); + if (jsx->stream_method & STREAM_METHOD_IBB) { + purple_debug_info("jabber", "IBB is possible, try it\n"); + /* if we are the sender and haven't already opened an IBB + session, do so now (we might already have failed to open + the bytestream proxy ourselves when receiving this <iq/> */ + if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND + && !jsx->ibb_session) { + jabber_si_xfer_ibb_send_init(js, xfer); } else { - purple_xfer_cancel_remote(xfer); + jsx->ibb_timeout_handle = purple_timeout_add_seconds(30, + jabber_si_bytestreams_ibb_timeout_cb, xfer); } + /* if we are receiver, just wait for IBB open stanza, callback + is already set up */ + } else { + purple_xfer_cancel_remote(xfer); } return; } - if(!(from = xmlnode_get_attrib(packet, "from"))) + if (!from) return; if(!(query = xmlnode_get_child(packet, "query"))) @@ -1019,16 +1015,15 @@ } static gboolean -jabber_si_xfer_ibb_open_cb(JabberStream *js, xmlnode *packet) +jabber_si_xfer_ibb_open_cb(JabberStream *js, const char *who, const char *id, + xmlnode *open) { - const gchar *who = xmlnode_get_attrib(packet, "from"); - xmlnode *open = xmlnode_get_child(packet, "open"); const gchar *sid = xmlnode_get_attrib(open, "sid"); PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who); if (xfer) { JabberSIXfer *jsx = (JabberSIXfer *) xfer->data; JabberIBBSession *sess = - jabber_ibb_session_create_from_xmlnode(js, packet, xfer); + jabber_ibb_session_create_from_xmlnode(js, who, id, open, xfer); const char *filename; jabber_si_bytestreams_ibb_timeout_remove(jsx); @@ -1183,8 +1178,9 @@ } } -static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet, - gpointer data) +static void jabber_si_xfer_send_method_cb(JabberStream *js, const char *from, + JabberIqType type, const char *id, + xmlnode *packet, gpointer data) { PurpleXfer *xfer = data; xmlnode *si, *feature, *x, *field, *value; @@ -1585,17 +1581,15 @@ purple_xfer_request(xfer); } -void jabber_si_parse(JabberStream *js, xmlnode *packet) +void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *si) { JabberSIXfer *jsx; PurpleXfer *xfer; - xmlnode *si, *file, *feature, *x, *field, *option, *value; - const char *stream_id, *filename, *filesize_c, *profile, *from; + xmlnode *file, *feature, *x, *field, *option, *value; + const char *stream_id, *filename, *filesize_c, *profile; size_t filesize = 0; - if(!(si = xmlnode_get_child(packet, "si"))) - return; - if(!(profile = xmlnode_get_attrib(si, "profile")) || strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) return; @@ -1618,7 +1612,7 @@ if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) return; - if(!(from = xmlnode_get_attrib(packet, "from"))) + if(!from) return; /* if they've already sent us this file transfer with the same damn id @@ -1659,7 +1653,7 @@ jsx->js = js; jsx->stream_id = g_strdup(stream_id); - jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); + jsx->iq_id = g_strdup(id); xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from); g_return_if_fail(xfer != NULL); @@ -1683,6 +1677,8 @@ void jabber_si_init(void) { + jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse); + jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb); }
--- a/libpurple/protocols/jabber/si.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/si.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,18 +19,20 @@ * 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_JABBER_SI_H_ -#define _PURPLE_JABBER_SI_H_ +#ifndef PURPLE_JABBER_SI_H_ +#define PURPLE_JABBER_SI_H_ #include "ft.h" #include "jabber.h" -void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet); -void jabber_si_parse(JabberStream *js, xmlnode *packet); +void jabber_bytestreams_parse(JabberStream *js, const char *from, + JabberIqType type, const char *id, xmlnode *query); +void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type, + const char *id, xmlnode *si); PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who); void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file); void jabber_si_init(void); void jabber_si_uninit(void); -#endif /* _PURPLE_JABBER_SI_H_ */ +#endif /* PURPLE_JABBER_SI_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/useravatar.c Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,373 @@ +/* + * purple - Jabber Protocol Plugin + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "internal.h" + +#include "useravatar.h" +#include "pep.h" +#include "debug.h" + +#define MAX_HTTP_BUDDYICON_BYTES (200 * 1024) + +static void update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items); + +void jabber_avatar_init(void) +{ + jabber_pep_register_handler(NS_AVATAR_0_12_METADATA, + update_buddy_metadata); + + jabber_add_feature(NS_AVATAR_1_1_METADATA, + jabber_pep_namespace_only_when_pep_enabled_cb); + jabber_add_feature(NS_AVATAR_1_1_DATA, + jabber_pep_namespace_only_when_pep_enabled_cb); + + jabber_pep_register_handler(NS_AVATAR_1_1_METADATA, + update_buddy_metadata); +} + +static void +remove_avatar_0_12_nodes(JabberStream *js) +{ + jabber_pep_delete_node(js, NS_AVATAR_0_12_METADATA); + jabber_pep_delete_node(js, NS_AVATAR_0_12_DATA); +} + +void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img) +{ + xmlnode *publish, *metadata, *item; + + if (!js->pep) + return; + + remove_avatar_0_12_nodes(js); + + if (!img) { + publish = xmlnode_new("publish"); + xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA); + + item = xmlnode_new_child(publish, "item"); + metadata = xmlnode_new_child(item, "metadata"); + xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA); + + /* publish */ + jabber_pep_publish(js, publish); + } else { + /* + * TODO: This is pretty gross. The Jabber PRPL really shouldn't + * do voodoo to try to determine the image type, height + * and width. + */ + /* A PNG header, including the IHDR, but nothing else */ + const struct { + guchar signature[8]; /* must be hex 89 50 4E 47 0D 0A 1A 0A */ + struct { + guint32 length; /* must be 0x0d */ + guchar type[4]; /* must be 'I' 'H' 'D' 'R' */ + guint32 width; + guint32 height; + guchar bitdepth; + guchar colortype; + guchar compression; + guchar filter; + guchar interlace; + } ihdr; + } *png = purple_imgstore_get_data(img); /* ATTN: this is in network byte order! */ + + /* check if the data is a valid png file (well, at least to some extent) */ + if(png->signature[0] == 0x89 && + png->signature[1] == 0x50 && + png->signature[2] == 0x4e && + png->signature[3] == 0x47 && + png->signature[4] == 0x0d && + png->signature[5] == 0x0a && + png->signature[6] == 0x1a && + png->signature[7] == 0x0a && + ntohl(png->ihdr.length) == 0x0d && + png->ihdr.type[0] == 'I' && + png->ihdr.type[1] == 'H' && + png->ihdr.type[2] == 'D' && + png->ihdr.type[3] == 'R') { + /* parse PNG header to get the size of the image (yes, this is required) */ + guint32 width = ntohl(png->ihdr.width); + guint32 height = ntohl(png->ihdr.height); + xmlnode *data, *info; + char *lengthstring, *widthstring, *heightstring; + + /* compute the sha1 hash */ + char *hash = jabber_calculate_data_sha1sum(purple_imgstore_get_data(img), + purple_imgstore_get_size(img)); + char *base64avatar = purple_base64_encode(purple_imgstore_get_data(img), + purple_imgstore_get_size(img)); + + publish = xmlnode_new("publish"); + xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_DATA); + + item = xmlnode_new_child(publish, "item"); + xmlnode_set_attrib(item, "id", hash); + + data = xmlnode_new_child(item, "data"); + xmlnode_set_namespace(data, NS_AVATAR_1_1_DATA); + + xmlnode_insert_data(data, base64avatar, -1); + /* publish the avatar itself */ + jabber_pep_publish(js, publish); + + g_free(base64avatar); + + lengthstring = g_strdup_printf("%" G_GSIZE_FORMAT, + purple_imgstore_get_size(img)); + widthstring = g_strdup_printf("%u", width); + heightstring = g_strdup_printf("%u", height); + + /* publish the metadata */ + publish = xmlnode_new("publish"); + xmlnode_set_attrib(publish, "node", NS_AVATAR_1_1_METADATA); + + item = xmlnode_new_child(publish, "item"); + xmlnode_set_attrib(item, "id", hash); + + metadata = xmlnode_new_child(item, "metadata"); + xmlnode_set_namespace(metadata, NS_AVATAR_1_1_METADATA); + + info = xmlnode_new_child(metadata, "info"); + xmlnode_set_attrib(info, "id", hash); + xmlnode_set_attrib(info, "type", "image/png"); + xmlnode_set_attrib(info, "bytes", lengthstring); + xmlnode_set_attrib(info, "width", widthstring); + xmlnode_set_attrib(info, "height", heightstring); + + jabber_pep_publish(js, publish); + + g_free(lengthstring); + g_free(widthstring); + g_free(heightstring); + g_free(hash); + } else { + purple_debug_error("jabber", "Cannot set PEP avatar to non-PNG data\n"); + } + } +} + +static void +do_got_own_avatar_cb(JabberStream *js, const char *from, xmlnode *items) +{ + xmlnode *item = NULL, *metadata = NULL, *info = NULL; + PurpleAccount *account = purple_connection_get_account(js->gc); + const char *server_hash = NULL; + const char *ns; + + if ((item = xmlnode_get_child(items, "item")) && + (metadata = xmlnode_get_child(item, "metadata")) && + (info = xmlnode_get_child(metadata, "info"))) { + server_hash = xmlnode_get_attrib(info, "id"); + } + + if (!metadata) + return; + + ns = xmlnode_get_namespace(metadata); + if (!ns) + return; + + /* + * We no longer publish avatars to the older namespace. If there is one + * there, delete it. + */ + if (g_str_equal(ns, NS_AVATAR_0_12_METADATA) && server_hash) { + remove_avatar_0_12_nodes(js); + return; + } + + /* Publish ours if it's different than the server's */ + if (!purple_strequal(server_hash, js->initial_avatar_hash)) { + PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account); + jabber_avatar_set(js, img); + purple_imgstore_unref(img); + } +} + +void jabber_avatar_fetch_mine(JabberStream *js) +{ + char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); + jabber_pep_request_item(js, jid, NS_AVATAR_0_12_METADATA, NULL, + do_got_own_avatar_cb); + jabber_pep_request_item(js, jid, NS_AVATAR_1_1_METADATA, NULL, + do_got_own_avatar_cb); + g_free(jid); +} + +typedef struct _JabberBuddyAvatarUpdateURLInfo { + JabberStream *js; + char *from; + char *id; +} JabberBuddyAvatarUpdateURLInfo; + +static void +do_buddy_avatar_update_fromurl(PurpleUtilFetchUrlData *url_data, + gpointer user_data, const gchar *url_text, + gsize len, const gchar *error_message) +{ + JabberBuddyAvatarUpdateURLInfo *info = user_data; + if(!url_text) { + purple_debug(PURPLE_DEBUG_ERROR, "jabber", + "do_buddy_avatar_update_fromurl got error \"%s\"", + error_message); + goto out; + } + + purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc), info->from, (void*)url_text, len, info->id); + +out: + g_free(info->from); + g_free(info->id); + g_free(info); +} + +static void +do_buddy_avatar_update_data(JabberStream *js, const char *from, xmlnode *items) +{ + xmlnode *item, *data; + const char *checksum, *ns; + char *b64data; + void *img; + size_t size; + if(!items) + return; + + item = xmlnode_get_child(items, "item"); + if(!item) + return; + + data = xmlnode_get_child(item, "data"); + if(!data) + return; + + ns = xmlnode_get_namespace(data); + /* Make sure the namespace is one of the two valid possibilities */ + if (!ns || (!g_str_equal(ns, NS_AVATAR_0_12_DATA) && + !g_str_equal(ns, NS_AVATAR_1_1_DATA))) + return; + + checksum = xmlnode_get_attrib(item,"id"); + if(!checksum) + return; + + b64data = xmlnode_get_data(data); + if(!b64data) + return; + + img = purple_base64_decode(b64data, &size); + if(!img) { + g_free(b64data); + return; + } + + purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum); + g_free(b64data); +} + +static void +update_buddy_metadata(JabberStream *js, const char *from, xmlnode *items) +{ + PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from); + const char *checksum, *ns; + xmlnode *item, *metadata; + if(!buddy) + return; + + if (!items) + return; + + item = xmlnode_get_child(items,"item"); + if (!item) + return; + + metadata = xmlnode_get_child(item, "metadata"); + if(!metadata) + return; + + ns = xmlnode_get_namespace(metadata); + /* Make sure the namespace is one of the two valid possibilities */ + if (!ns || (!g_str_equal(ns, NS_AVATAR_0_12_METADATA) && + !g_str_equal(ns, NS_AVATAR_1_1_METADATA))) + return; + + checksum = purple_buddy_icons_get_checksum_for_user(buddy); + + /* check if we have received a stop */ + if(xmlnode_get_child(metadata, "stop")) { + purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); + } else { + xmlnode *info, *goodinfo = NULL; + gboolean has_children = FALSE; + + /* iterate over all info nodes to get one we can use */ + for(info = metadata->child; info; info = info->next) { + if(info->type == XMLNODE_TYPE_TAG) + has_children = TRUE; + if(info->type == XMLNODE_TYPE_TAG && !strcmp(info->name,"info")) { + const char *type = xmlnode_get_attrib(info,"type"); + const char *id = xmlnode_get_attrib(info,"id"); + + if(checksum && id && !strcmp(id, checksum)) { + /* we already have that avatar, so we don't have to do anything */ + goodinfo = NULL; + break; + } + /* We'll only pick the png one for now. It's a very nice image format anyways. */ + if(type && id && !goodinfo && !strcmp(type, "image/png")) + goodinfo = info; + } + } + if(has_children == FALSE) { + purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, NULL, 0, NULL); + } else if(goodinfo) { + const char *url = xmlnode_get_attrib(goodinfo, "url"); + const char *id = xmlnode_get_attrib(goodinfo,"id"); + + /* the avatar might either be stored in a pep node, or on a HTTP(S) URL */ + if(!url) { + const char *data_ns; + data_ns = (g_str_equal(ns, NS_AVATAR_0_12_METADATA) ? + NS_AVATAR_0_12_DATA : NS_AVATAR_1_1_DATA); + jabber_pep_request_item(js, from, data_ns, id, + do_buddy_avatar_update_data); + } else { + PurpleUtilFetchUrlData *url_data; + JabberBuddyAvatarUpdateURLInfo *info = g_new0(JabberBuddyAvatarUpdateURLInfo, 1); + info->js = js; + + url_data = purple_util_fetch_url_len(url, TRUE, NULL, TRUE, + MAX_HTTP_BUDDYICON_BYTES, + do_buddy_avatar_update_fromurl, info); + if (url_data) { + info->from = g_strdup(from); + info->id = g_strdup(id); + js->url_datas = g_slist_prepend(js->url_datas, url_data); + } else + g_free(info); + + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/useravatar.h Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,43 @@ +/* + * purple - Jabber Protocol Plugin + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _PURPLE_JABBER_USERAVATAR_H_ +#define _PURPLE_JABBER_USERAVATAR_H_ + +#include "jabber.h" +#include "imgstore.h" + +/* Implementation of XEP-0084 */ + +#define NS_AVATAR_0_12_DATA "http://www.xmpp.org/extensions/xep-0084.html#ns-data" +#define NS_AVATAR_0_12_METADATA "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata" + +#define NS_AVATAR_1_1_DATA "urn:xmpp:avatar:data" +#define NS_AVATAR_1_1_METADATA "urn:xmpp:avatar:metadata" + +void jabber_avatar_init(void); +void jabber_avatar_set(JabberStream *js, PurpleStoredImage *img); + +void jabber_avatar_fetch_mine(JabberStream *js); + +#endif /* _PURPLE_JABBER_USERAVATAR_H_ */
--- a/libpurple/protocols/jabber/usermood.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/usermood.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * */ -#ifndef _PURPLE_JABBER_USERMOOD_H_ -#define _PURPLE_JABBER_USERMOOD_H_ +#ifndef PURPLE_JABBER_USERMOOD_H_ +#define PURPLE_JABBER_USERMOOD_H_ #include "jabber.h" @@ -34,4 +34,4 @@ const char *mood, /* must be one of the valid strings defined in the XEP */ const char *text /* might be NULL */); -#endif /* _PURPLE_JABBER_USERMOOD_H_ */ +#endif /* PURPLE_JABBER_USERMOOD_H_ */
--- a/libpurple/protocols/jabber/usernick.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/usernick.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * */ -#ifndef _PURPLE_JABBER_USERNICK_H_ -#define _PURPLE_JABBER_USERNICK_H_ +#ifndef PURPLE_JABBER_USERNICK_H_ +#define PURPLE_JABBER_USERNICK_H_ #include "jabber.h" @@ -29,4 +29,4 @@ void jabber_nick_init(void); void jabber_nick_init_action(GList **m); -#endif /* _PURPLE_JABBER_USERNICK_H_ */ +#endif /* PURPLE_JABBER_USERNICK_H_ */
--- a/libpurple/protocols/jabber/usertune.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/usertune.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * */ -#ifndef _PURPLE_JABBER_USERTUNE_H_ -#define _PURPLE_JABBER_USERTUNE_H_ +#ifndef PURPLE_JABBER_USERTUNE_H_ +#define PURPLE_JABBER_USERTUNE_H_ #include "jabber.h" @@ -40,4 +40,4 @@ void jabber_tune_set(PurpleConnection *gc, const PurpleJabberTuneInfo *tuneinfo); -#endif /* _PURPLE_JABBER_USERTUNE_H_ */ +#endif /* PURPLE_JABBER_USERTUNE_H_ */
--- a/libpurple/protocols/jabber/xdata.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/jabber/xdata.h Tue Apr 28 19:08:06 2009 +0000 @@ -19,8 +19,8 @@ * 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_JABBER_XDATA_H_ -#define _PURPLE_JABBER_XDATA_H_ +#ifndef PURPLE_JABBER_XDATA_H_ +#define PURPLE_JABBER_XDATA_H_ #include "jabber.h" #include "xmlnode.h" @@ -35,4 +35,4 @@ void *jabber_x_data_request(JabberStream *js, xmlnode *packet, jabber_x_data_cb cb, gpointer user_data); void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GList *actions, int defaultaction, jabber_x_data_action_cb cb, gpointer user_data); -#endif /* _PURPLE_JABBER_XDATA_H_ */ +#endif /* PURPLE_JABBER_XDATA_H_ */
--- a/libpurple/protocols/msn/msg.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msn/msg.c Tue Apr 28 19:08:06 2009 +0000 @@ -984,3 +984,67 @@ g_hash_table_destroy(body); } +void +msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + GHashTable *body; + const gchar *guid; + + g_return_if_fail(cmdproc != NULL); + g_return_if_fail(msg != NULL); + + body = msn_message_get_hashtable_from_body(msg); + + if (body == NULL) { + purple_debug_warning("msn", + "Unable to parse invite msg body.\n"); + return; + } + + guid = g_hash_table_lookup(body, "Application-GUID"); + + if (guid == NULL) { + const gchar *cmd = g_hash_table_lookup( + body, "Invitation-Command"); + + if (cmd && !strcmp(cmd, "CANCEL")) { + const gchar *code = g_hash_table_lookup( + body, "Cancel-Code"); + purple_debug_info("msn", + "MSMSGS invitation cancelled: %s.\n", + code ? code : "no reason given"); + } else + purple_debug_warning("msn", "Invite msg missing " + "Application-GUID.\n"); + } else if (!strcmp(guid, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}")) { + purple_debug_info("msn", "Computer call\n"); + + if (cmdproc->session) { + PurpleConversation *conv = NULL; + gchar *from = msg->remote_user; + gchar *buf = NULL; + + if (from) + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, from, + cmdproc->session->account); + if (conv) + buf = g_strdup_printf( + _("%s sent you a voice chat " + "invite, which is not yet " + "supported."), from); + if (buf) { + purple_conversation_write(conv, NULL, buf, + PURPLE_MESSAGE_SYSTEM | + PURPLE_MESSAGE_NOTIFY, + time(NULL)); + g_free(buf); + } + } + } else + purple_debug_warning("msn", + "Unhandled invite msg with GUID %s.\n", guid); + + g_hash_table_destroy(body); +} +
--- a/libpurple/protocols/msn/msn.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msn/msn.h Tue Apr 28 19:08:06 2009 +0000 @@ -76,6 +76,8 @@ #define BUDDY_ALIAS_MAXLEN 387 +#define MSN_CAM_GUID "4BD96FC0-AB17-4425-A14A-439185962DC8" +#define MSN_CAM_REQUEST_GUID "1C9AA97E-9C05-4583-A3BD-908A196F1E92" #define MSN_FT_GUID "5D3E02AB-6190-11D3-BBBB-00C04F795683" #define MSN_OBJ_GUID "A4268EEC-FEC5-49E5-95C3-F126696BDBF6"
--- a/libpurple/protocols/msn/session.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msn/session.c Tue Apr 28 19:08:06 2009 +0000 @@ -303,7 +303,7 @@ for (l = remote_user->group_ids; l != NULL; l = l->next) { const char *name = msn_userlist_find_group_name(remote_user->userlist, l->data); - if (name && !g_strcasecmp(group_name, name)) + if (name && !g_ascii_strcasecmp(group_name, name)) { found = TRUE; break;
--- a/libpurple/protocols/msn/slp.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msn/slp.c Tue Apr 28 19:08:06 2009 +0000 @@ -377,6 +377,50 @@ purple_xfer_request(xfer); } + } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) { + purple_debug_info("msn", "Cam request.\n"); + if (slpcall && slpcall->slplink && + slpcall->slplink->session) { + PurpleConversation *conv; + gchar *from = slpcall->slplink->remote_user; + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, from, + slpcall->slplink->session->account); + if (conv) { + char *buf; + buf = g_strdup_printf( + _("%s requests to view your " + "webcam, but this request is " + "not yet supported."), from); + purple_conversation_write(conv, NULL, buf, + PURPLE_MESSAGE_SYSTEM | + PURPLE_MESSAGE_NOTIFY, + time(NULL)); + g_free(buf); + } + } + } else if (!strcmp(euf_guid, MSN_CAM_GUID)) { + purple_debug_info("msn", "Cam invite.\n"); + if (slpcall && slpcall->slplink && + slpcall->slplink->session) { + PurpleConversation *conv; + gchar *from = slpcall->slplink->remote_user; + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, from, + slpcall->slplink->session->account); + if (conv) { + char *buf; + buf = g_strdup_printf( + _("%s has sent you a webcam " + "invite, which is not yet " + "supported."), from); + purple_conversation_write(conv, NULL, buf, + PURPLE_MESSAGE_SYSTEM | + PURPLE_MESSAGE_NOTIFY, + time(NULL)); + g_free(buf); + } + } } else purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); }
--- a/libpurple/protocols/msn/slplink.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msn/slplink.c Tue Apr 28 19:08:06 2009 +0000 @@ -46,7 +46,7 @@ pload = msn_message_gen_payload(msg, &pload_size); if (!purple_util_write_data_to_file_absolute(tmp, pload, pload_size)) { - purple_debug_error("msn", "could not save debug file"); + purple_debug_error("msn", "could not save debug file\n"); } g_free(tmp); } @@ -682,7 +682,9 @@ size = st.st_size; if(!file_name) { - u8 = purple_utf8_try_convert(g_basename(file_path)); + base = g_path_get_basename(file_path); + u8 = purple_utf8_try_convert(base); + g_free(base); file_name = u8; }
--- a/libpurple/protocols/msn/switchboard.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msn/switchboard.c Tue Apr 28 19:08:06 2009 +0000 @@ -1237,10 +1237,8 @@ msn_emoticon_msg); msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast", msn_datacast_msg); -#if 0 - msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite", + msn_table_add_msg_type(cbs_table, "text/x-msmsgsinvite", msn_invite_msg); -#endif } void
--- a/libpurple/protocols/msn/userlist.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msn/userlist.c Tue Apr 28 19:08:06 2009 +0000 @@ -448,7 +448,7 @@ g_return_val_if_fail(user->passport != NULL, NULL); - if (!g_strcasecmp(passport, user->passport)){ + if (!g_ascii_strcasecmp(passport, user->passport)){ return user; } } @@ -470,7 +470,7 @@ continue; } - if ( !g_strcasecmp(uid, user->uid) ) { + if ( !g_ascii_strcasecmp(uid, user->uid) ) { return user; } } @@ -492,7 +492,7 @@ continue; } - if (!g_strcasecmp(number, user->phone.mobile)) { + if (!g_ascii_strcasecmp(number, user->phone.mobile)) { return user; } } @@ -524,7 +524,7 @@ { MsnGroup *group = l->data; - if (!g_strcasecmp(group->id,id)) + if (!g_ascii_strcasecmp(group->id,id)) return group; } @@ -543,7 +543,7 @@ { MsnGroup *group = l->data; - if ((group->name != NULL) && !g_strcasecmp(name, group->name)) + if ((group->name != NULL) && !g_ascii_strcasecmp(name, group->name)) return group; } @@ -784,7 +784,7 @@ { user = (MsnUser *)l->data; - if (!g_strcasecmp(who, user->passport)) { + if (!g_ascii_strcasecmp(who, user->passport)) { userlist->pending = g_list_delete_link(userlist->pending, l); break; }
--- a/libpurple/protocols/msnp9/httpconn.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msnp9/httpconn.c Tue Apr 28 19:08:06 2009 +0000 @@ -703,7 +703,7 @@ httpconn->inpa = purple_input_add(httpconn->fd, PURPLE_INPUT_READ, read_cb, data); - httpconn->timer = purple_timeout_add(2000, msn_httpconn_poll, httpconn); + httpconn->timer = purple_timeout_add_seconds(3, msn_httpconn_poll, httpconn); msn_httpconn_process_queue(httpconn); }
--- a/libpurple/protocols/msnp9/slp.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msnp9/slp.c Tue Apr 28 19:08:06 2009 +0000 @@ -33,8 +33,8 @@ #include "smiley.h" -/* ms to delay between sending buddy icon requests to the server. */ -#define BUDDY_ICON_DELAY 20000 +/* Seconds to delay between sending buddy icon requests to the server. */ +#define BUDDY_ICON_DELAY 20 static void send_ok(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content); @@ -1058,8 +1058,8 @@ purple_timeout_remove(userlist->buddy_icon_request_timer); } - /* Wait BUDDY_ICON_DELAY ms before freeing our window slot and requesting the next icon. */ - userlist->buddy_icon_request_timer = purple_timeout_add(BUDDY_ICON_DELAY, + /* Wait BUDDY_ICON_DELAY_S seconds before freeing our window slot and requesting the next icon. */ + userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, msn_release_buddy_icon_request_timeout, userlist); }
--- a/libpurple/protocols/msnp9/slpcall.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msnp9/slpcall.c Tue Apr 28 19:08:06 2009 +0000 @@ -68,7 +68,7 @@ msn_slplink_add_slpcall(slplink, slpcall); - slpcall->timer = purple_timeout_add(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpcall); + slpcall->timer = purple_timeout_add_seconds(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpcall); return slpcall; }
--- a/libpurple/protocols/msnp9/slpcall.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msnp9/slpcall.h Tue Apr 28 19:08:06 2009 +0000 @@ -33,7 +33,7 @@ #include "slpsession.h" /* The official client seems to timeout slp calls after 5 minutes */ -#define MSN_SLPCALL_TIMEOUT 300000 +#define MSN_SLPCALL_TIMEOUT 300 typedef enum {
--- a/libpurple/protocols/msnp9/transaction.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/msnp9/transaction.c Tue Apr 28 19:08:06 2009 +0000 @@ -211,7 +211,7 @@ purple_timeout_remove(trans->timer); } trans->timeout_cb = cb; - trans->timer = purple_timeout_add(60000, transaction_timeout, trans); + trans->timer = purple_timeout_add_seconds(60, transaction_timeout, trans); } void
--- a/libpurple/protocols/myspace/myspace.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/myspace/myspace.c Tue Apr 28 19:08:06 2009 +0000 @@ -1245,7 +1245,7 @@ /* Disable due to problems with timeouts. TODO: fix. */ #ifdef MSIM_USE_KEEPALIVE - purple_timeout_add(MSIM_KEEPALIVE_INTERVAL_CHECK, + purple_timeout_add_seconds(MSIM_KEEPALIVE_INTERVAL_CHECK, (GSourceFunc)msim_check_alive, session); #endif
--- a/libpurple/protocols/myspace/myspace.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/myspace/myspace.h Tue Apr 28 19:08:06 2009 +0000 @@ -114,8 +114,8 @@ #define MSIM_KEEPALIVE_INTERVAL (3 * 60) /*#define MSIM_USE_KEEPALIVE*/ -/* Time to check if alive (milliseconds) */ -#define MSIM_KEEPALIVE_INTERVAL_CHECK (30 * 1000) +/* Time to check if alive (seconds) */ +#define MSIM_KEEPALIVE_INTERVAL_CHECK 30 /* Time to check for new mail (milliseconds) */ #define MSIM_MAIL_INTERVAL_CHECK (60 * 1000)
--- a/libpurple/protocols/oscar/oscar.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Tue Apr 28 19:08:06 2009 +0000 @@ -1268,7 +1268,7 @@ aim_ssi_reqdata(od); if (od->getblisttimer > 0) purple_timeout_remove(od->getblisttimer); - od->getblisttimer = purple_timeout_add(30000, purple_ssi_rerequestdata, od); + od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od); aim_locate_reqrights(od); aim_buddylist_reqrights(od, conn); @@ -5047,7 +5047,7 @@ _("The AIM servers were temporarily unable to send " "your buddy list. Your buddy list is not lost, and " "will probably become available in a few minutes.")); - od->getblisttimer = purple_timeout_add(30000, purple_ssi_rerequestdata, od); + od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od); return 1; }
--- a/libpurple/protocols/oscar/peer.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/oscar/peer.c Tue Apr 28 19:08:06 2009 +0000 @@ -812,7 +812,7 @@ (conn->client_connect_data != NULL)) { /* Connecting... */ - conn->connect_timeout_timer = purple_timeout_add(5000, + conn->connect_timeout_timer = purple_timeout_add_seconds(5, peer_connection_tooktoolong, conn); return; }
--- a/libpurple/protocols/qq/ChangeLog Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/qq/ChangeLog Tue Apr 28 19:08:06 2009 +0000 @@ -1,3 +1,6 @@ +2009.04.23 - flos <lonicerae(at)gmail.com> + * Fixed a bug of updating buddy who is not in user's buddy list + 2009.02.25 - flos <lonicerae(at)gmail.com> * Changed text 'ZipCode' to 'Postal Code'
--- a/libpurple/protocols/qq/buddy_info.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Tue Apr 28 19:08:06 2009 +0000 @@ -191,7 +191,7 @@ } switch (field_infos[index].type) { case QQ_FIELD_BOOL: - purple_notify_user_info_add_pair(user_info, field_infos[index].text, + purple_notify_user_info_add_pair(user_info, _(field_infos[index].text), strtol(segments[index], NULL, 10) ? _("True") : _("False")); break; case QQ_FIELD_CHOICE: @@ -200,7 +200,7 @@ choice_num = 0; } - purple_notify_user_info_add_pair(user_info, field_infos[index].text, field_infos[index].choice[choice_num]); + purple_notify_user_info_add_pair(user_info, _(field_infos[index].text), field_infos[index].choice[choice_num]); break; case QQ_FIELD_LABEL: case QQ_FIELD_STRING: @@ -208,7 +208,7 @@ default: if (strlen(segments[index]) != 0) { utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT); - purple_notify_user_info_add_pair(user_info, field_infos[index].text, utf8_value); + purple_notify_user_info_add_pair(user_info, _(field_infos[index].text), utf8_value); g_free(utf8_value); } break; @@ -348,18 +348,18 @@ utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT); if (field_infos[index].type == QQ_FIELD_STRING) { field = purple_request_field_string_new( - field_infos[index].id, field_infos[index].text, utf8_value, FALSE); + field_infos[index].id, _(field_infos[index].text), utf8_value, FALSE); } else { field = purple_request_field_string_new( - field_infos[index].id, field_infos[index].text, utf8_value, TRUE); + field_infos[index].id, _(field_infos[index].text), utf8_value, TRUE); } purple_request_field_group_add_field(group, field); g_free(utf8_value); break; case QQ_FIELD_BOOL: field = purple_request_field_bool_new( - field_infos[index].id, field_infos[index].text, - strtol(segments[index], NULL, 10) ? TRUE : FALSE); + field_infos[index].id, _(field_infos[index].text), + strtol(segments[index], NULL, 10) ? TRUE : FALSE); purple_request_field_group_add_field(group, field); break; case QQ_FIELD_CHOICE: @@ -374,7 +374,7 @@ } } field = purple_request_field_choice_new( - field_infos[index].id, field_infos[index].text, choice_num); + field_infos[index].id, _(field_infos[index].text), choice_num); for (i = 0; i < field_infos[index].choice_size; i++) { purple_request_field_choice_add(field, field_infos[index].choice[i]); } @@ -606,21 +606,21 @@ /* after getting info or modify myself, refresh the buddy list accordingly */ static void update_buddy_info(PurpleConnection *gc, gchar **segments) { - PurpleBuddy *buddy; - qq_data *qd; - qq_buddy_data *bd; + PurpleBuddy *buddy = NULL; + qq_data *qd = NULL; + qq_buddy_data *bd = NULL; guint32 uid; gchar *who; gchar *alias_utf8; + PurpleAccount *account = purple_connection_get_account(gc); - qd = (qq_data *)purple_connection_get_protocol_data(gc); uid = strtoul(segments[QQ_INFO_UID], NULL, 10); who = uid_to_purple_name(uid); - qq_filter_str(segments[QQ_INFO_NICK]); alias_utf8 = qq_to_utf8(segments[QQ_INFO_NICK], QQ_CHARSET_DEFAULT); + if (uid == qd->uid) { /* it is me */ purple_debug_info("QQ", "Got my info\n"); qd->my_icon = strtol(segments[QQ_INFO_FACE], NULL, 10); @@ -631,12 +631,14 @@ buddy = qq_buddy_find_or_new(gc, uid); } else { buddy = purple_find_buddy(gc->account, who); + /* purple_debug_info("QQ", "buddy=%p\n", (void*)buddy); */ } /* if the buddy is null, the api will catch it and return null here */ bd = purple_buddy_get_protocol_data(buddy); + /* purple_debug_info("QQ", "bd=%p\n", (void*)bd); */ - if (buddy == NULL || bd) { + if (bd == NULL || buddy == NULL) { g_free(who); g_free(alias_utf8); return; @@ -646,6 +648,7 @@ bd->age = strtol(segments[QQ_INFO_AGE], NULL, 10); bd->gender = strtol(segments[QQ_INFO_GENDER], NULL, 10); bd->face = strtol(segments[QQ_INFO_FACE], NULL, 10); + if (alias_utf8 != NULL) { if (bd->nickname) g_free(bd->nickname); bd->nickname = g_strdup(alias_utf8);
--- a/libpurple/protocols/qq/qq.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/qq/qq.c Tue Apr 28 19:08:06 2009 +0000 @@ -674,8 +674,8 @@ g_string_append(info, "wd<br>\n"); g_string_append(info, "x6719620<br>\n"); g_string_append(info, "netelk<br>\n"); - g_string_append(info, "and more, please let me know... thank you!<br>\n"); - g_string_append(info, "<br>\n"); + g_string_append(info, _("and more, please let me know... thank you!))")); + g_string_append(info, "<br>\n<br>\n"); g_string_append(info, _("<p><i>And, all the boys in the backroom...</i><br>\n")); g_string_append(info, _("<i>Feel free to join us!</i> :)")); g_string_append(info, "</body></html>");
--- a/libpurple/protocols/sametime/sametime.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/sametime/sametime.c Tue Apr 28 19:08:06 2009 +0000 @@ -28,8 +28,6 @@ /* glib includes */ #include <glib.h> -#include <glib/ghash.h> -#include <glib/glist.h> /* purple includes */ #include "internal.h" @@ -810,7 +808,7 @@ static void blist_schedule(struct mwPurplePluginData *pd) { if(pd->save_event) return; - pd->save_event = purple_timeout_add(BLIST_SAVE_SECONDS * 1000, + pd->save_event = purple_timeout_add_seconds(BLIST_SAVE_SECONDS, blist_save_cb, pd); }
--- a/libpurple/protocols/yahoo/yahoo.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Tue Apr 28 19:08:06 2009 +0000 @@ -2829,6 +2829,7 @@ p2p_data->host_username = g_strdup(who); p2p_data->val_13 = val_13; p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER; + p2p_data->source = -1; purple_network_listen(YAHOO_PAGER_PORT_P2P, SOCK_STREAM, yahoo_p2p_server_listen_cb, p2p_data); @@ -2932,10 +2933,9 @@ if (base64) { guint32 ip; - char *tmp2; YahooFriend *f; char *host_ip; - struct yahoo_p2p_data *p2p_data = g_new0(struct yahoo_p2p_data, 1); + struct yahoo_p2p_data *p2p_data; decoded = purple_base64_decode(base64, &len); if (len) { @@ -2944,9 +2944,7 @@ g_free(tmp); } - tmp2 = g_strndup((const gchar *)decoded, len); /* so its \0 terminated...*/ - ip = strtol(tmp2, NULL, 10); - g_free(tmp2); + ip = strtol((gchar *)decoded, NULL, 10); g_free(decoded); host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff); @@ -2964,17 +2962,21 @@ val_11 = f->session_id; } - p2p_data->host_username = g_strdup(who); + p2p_data = g_new0(struct yahoo_p2p_data, 1); + p2p_data->host_username = g_strdup(who); p2p_data->val_13 = val_13; p2p_data->session_id = val_11; p2p_data->host_ip = host_ip; p2p_data->gc = gc; p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT; + p2p_data->source = -1; /* connect to host */ if((purple_proxy_connect(NULL, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) { - yahoo_p2p_disconnect_destroy_data(p2p_data); purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip); + g_free(p2p_data->host_ip); + g_free(p2p_data->host_username); + g_free(p2p_data); } } } @@ -4416,7 +4418,7 @@ "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s; path=/; domain=.yahoo.com;\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n" "Host: validate.msg.yahoo.com\r\n" - "Content-Length: %d\r\n" + "Content-Length: %" G_GSIZE_FORMAT "\r\n" "Cache-Control: no-cache\r\n\r\n%s", YAHOO_CLIENT_VERSION, yd->cookie_t, yd->cookie_y, strlen(validate_request_str), validate_request_str);
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Tue Apr 28 19:08:06 2009 +0000 @@ -1029,12 +1029,7 @@ xd->port = YAHOO_XFER_RELAY_PORT; url = g_strdup_printf("%ld.%ld.%ld.%ld", d, c, b, a); - if (!purple_url_parse(url, &(xd->host), &(xd->port), &(xd->path), NULL, NULL)) { - purple_xfer_cancel_remote(xfer); - g_free(url); - return; - } - g_free(url); + /* Free the address... */ g_free(hosts->data); hosts = g_slist_remove(hosts, hosts->data); @@ -1048,6 +1043,13 @@ hosts = g_slist_remove(hosts, hosts->data); } + if (!purple_url_parse(url, &(xd->host), &(xd->port), &(xd->path), NULL, NULL)) { + purple_xfer_cancel_remote(xfer); + g_free(url); + return; + } + g_free(url); + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id); filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); @@ -1385,7 +1387,13 @@ strcpy(time_str + strlen(time_str) - 1, "\0"); if (xd->txbuflen == 0) { - xd->txbuf = g_strdup_printf("HTTP/1.0 200 OK\r\nDate: %s GMT\r\nServer: Y!/1.0\r\nMIME-version: 1.0\r\nLast-modified: %s GMT\r\nContent-length: %d\r\n\r\n", time_str, time_str, xfer->size); + xd->txbuf = g_strdup_printf("HTTP/1.0 200 OK\r\n" + "Date: %s GMT\r\n" + "Server: Y!/1.0\r\n" + "MIME-version: 1.0\r\n" + "Last-modified: %s GMT\r\n" + "Content-length: %" G_GSIZE_FORMAT "\r\n\r\n", + time_str, time_str, xfer->size); xd->txbuflen = strlen(xd->txbuf); xd->txbuf_written = 0; }
--- a/libpurple/protocols/zephyr/ZVariables.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/zephyr/ZVariables.c Tue Apr 28 19:08:06 2009 +0000 @@ -186,7 +186,7 @@ #define max(a,b) ((a > b) ? (a) : (b)) #endif - if (g_strncasecmp(bfr, var, max(strlen(var), cp - bfr))) + if (g_ascii_strncasecmp(bfr, var, max(strlen(var), cp - bfr))) return(0); /* var is not the var in bfr ==> no match */
--- a/libpurple/protocols/zephyr/zephyr.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Tue Apr 28 19:08:06 2009 +0000 @@ -960,7 +960,7 @@ tc = tree_child(ptree,0)->contents; /* g_strcasecmp() is deprecated. What is the encoding here??? */ - if (ptree->num_children > 0 && tc && !g_strcasecmp(tc, key)) { + if (ptree->num_children > 0 && tc && !g_ascii_strcasecmp(tc, key)) { return ptree; } else { parse_tree *result = &null_parse_tree; @@ -1880,7 +1880,7 @@ } else if (use_tzc(zephyr)) { zephyr->nottimer = purple_timeout_add(100, check_notify_tzc, gc); } - zephyr->loctimer = purple_timeout_add(20000, check_loc, gc); + zephyr->loctimer = purple_timeout_add_seconds(20, check_loc, gc); }
--- a/libpurple/prpl.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/prpl.c Tue Apr 28 19:08:06 2009 +0000 @@ -182,6 +182,17 @@ } void +purple_prpl_got_account_actions(PurpleAccount *account) +{ + + g_return_if_fail(account != NULL); + g_return_if_fail(purple_account_is_connected(account)); + + purple_signal_emit(purple_accounts_get_handle(), "account-actions-changed", + account); +} + +void purple_prpl_got_user_idle(PurpleAccount *account, const char *name, gboolean idle, time_t idle_time) { @@ -515,7 +526,7 @@ if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, initiate_media)) { /* should check that the protocol supports this media type here? */ - return prpl_info->initiate_media(gc, who, type); + return prpl_info->initiate_media(account, who, type); } else #endif return FALSE; @@ -538,7 +549,7 @@ if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_media_caps)) { - return prpl_info->get_media_caps(gc, who); + return prpl_info->get_media_caps(account, who); } #endif return PURPLE_MEDIA_CAPS_NONE;
--- a/libpurple/prpl.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/prpl.h Tue Apr 28 19:08:06 2009 +0000 @@ -464,22 +464,22 @@ /** * Initiate a media session with the given contact. * - * @param conn The connection to initiate the media session on. + * @param account The account to initiate the media session on. * @param who The remote user to initiate the session with. * @param type The type of media session to initiate. * @return TRUE if the call succeeded else FALSE. (Doesn't imply the media session or stream will be successfully created) */ - gboolean (*initiate_media)(PurpleConnection *gc, const char *who, + gboolean (*initiate_media)(PurpleAccount *account, const char *who, PurpleMediaSessionType type); /** * Checks to see if the given contact supports the given type of media session. * - * @param conn The connection the contact is on. + * @param account The account the contact is on. * @param who The remote user to check for media capability with. * @return The media caps the contact supports. */ - PurpleMediaCaps (*get_media_caps)(PurpleConnection *gc, + PurpleMediaCaps (*get_media_caps)(PurpleAccount *account, const char *who); }; @@ -661,6 +661,20 @@ const char *status_id, ...) G_GNUC_NULL_TERMINATED; /** + * Notifies Purple that our account's actions have changed. This is only + * called after the initial connection. Emits the account-actions-changed + * signal. + * + * This is meant to be called from protocol plugins. + * + * @param account The account. + * + * @see account-actions-changed + * @since 2.6.0 + */ +void purple_prpl_got_account_actions(PurpleAccount *account); + +/** * Notifies Purple that a buddy's idle state and time have changed. * * This is meant to be called from protocol plugins.
--- a/libpurple/server.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/server.c Tue Apr 28 19:08:06 2009 +0000 @@ -587,6 +587,11 @@ account = purple_connection_get_account(gc); + /* + * XXX: Should we be setting this here, or relying on prpls to set it? + */ + flags |= PURPLE_MESSAGE_RECV; + 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)) { @@ -630,11 +635,6 @@ if (conv == NULL) conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account); - /* - * XXX: Should we be setting this here, or relying on prpls to set it? - */ - flags |= PURPLE_MESSAGE_RECV; - if (conv == NULL) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name); @@ -939,6 +939,14 @@ if (!conv) return; + /* Did I send the message? */ + if (purple_strequal(purple_conv_chat_get_nick(chat), who)) { + flags |= PURPLE_MESSAGE_SEND; + flags &= ~PURPLE_MESSAGE_RECV; /* Just in case some prpl sets it! */ + } else { + flags |= PURPLE_MESSAGE_RECV; + } + /* * Make copies of the message and the sender in case plugins want * to free these strings and replace them with a modifed version.
--- a/libpurple/smiley.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/smiley.h Tue Apr 28 19:08:06 2009 +0000 @@ -61,44 +61,41 @@ /*@{*/ /** - * GObject foo. + * GObject-fu. * @internal. */ GType purple_smiley_get_type(void); /** - * Creates a new custom smiley structure and populates it. + * Creates a new custom smiley from a PurpleStoredImage. * - * If a custom smiley with the informed shortcut already exist, it + * If a custom smiley with the given shortcut already exists, it * will be automaticaly returned. * * @param img The image associated with the smiley. - * @param shortcut The custom smiley associated shortcut. + * @param shortcut The associated shortcut (e.g. "(homer)"). * - * @return The custom smiley structure filled up. + * @return The custom smiley. */ PurpleSmiley * purple_smiley_new(PurpleStoredImage *img, const char *shortcut); /** - * Creates a new custom smiley structure and populates it. + * Creates a new custom smiley, reading the image data from a file. * - * The data is retrieved from an already existent file. - * - * If a custom smiley with the informed shortcut already exist, it + * If a custom smiley with the given shortcut already exists, 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. + * @param shortcut The associated shortcut (e.g. "(homer)"). + * @param filepath The image file. * - * @return The custom smiley structure filled up. + * @return The custom smiley. */ PurpleSmiley * purple_smiley_new_from_file(const char *shortcut, const char *filepath); /** - * Destroy the custom smiley and release the associated resources. + * Destroys the custom smiley and release the associated resources. * * @param smiley The custom smiley. */ @@ -109,32 +106,28 @@ * Changes the custom smiley's shortcut. * * @param smiley The custom smiley. - * @param shortcut The custom smiley associated shortcut. + * @param shortcut The new shortcut. A custom smiley with this shortcut + * cannot already be in use. * - * @return TRUE whether the shortcut is not associated with another - * custom smiley and the parameters are valid. FALSE otherwise. + * @return TRUE if the shortcut was changed. 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. + * Changes the custom smiley's image data. * * @param smiley The custom smiley. - * @param smiley_data The custom smiley data. - * @param smiley_data_len The custom smiley data length. + * @param smiley_data The custom smiley data, which the smiley code + * takes ownership of and will free. + * @param smiley_data_len The length of the data in @a smiley_data. */ void purple_smiley_set_data(PurpleSmiley *smiley, guchar *smiley_data, size_t smiley_data_len); /** - * Returns the custom smiley's associated shortcut. + * Returns the custom smiley's associated shortcut (e.g. "(homer)"). * * @param smiley The custom smiley. * @@ -155,11 +148,11 @@ * Returns the PurpleStoredImage with the reference counter incremented. * * The returned PurpleStoredImage reference counter must be decremented - * after use. + * when the caller is done using it. * * @param smiley The custom smiley. * - * @return A PurpleStoredImage reference. + * @return A PurpleStoredImage. */ PurpleStoredImage *purple_smiley_get_stored_image(const PurpleSmiley *smiley); @@ -167,7 +160,7 @@ * Returns the custom smiley's data. * * @param smiley The custom smiley. - * @param len If not @c NULL, the length of the icon data returned + * @param len If not @c NULL, the length of the image data returned * will be set in the location pointed to by this. * * @return A pointer to the custom smiley data. @@ -194,6 +187,8 @@ * directly. If you find yourself wanting to use this function, think * very long and hard about it, and then don't. * + * Think some more. + * * @param smiley The custom smiley. * * @return A full path to the file, or @c NULL under various conditions. @@ -210,7 +205,8 @@ /*@{*/ /** - * Returns a list of all custom smileys. The caller should free the list. + * Returns a list of all custom smileys. The caller is responsible for freeing + * the list. * * @return A list of all custom smileys. */ @@ -218,23 +214,21 @@ purple_smileys_get_all(void); /** - * Returns the custom smiley given it's shortcut. + * Returns a custom smiley given its 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. + * @return The custom smiley 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. + * Returns a custom smiley given its 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. + * @return The custom smiley if found, or @c NULL if not found. */ PurpleSmiley * purple_smileys_find_by_checksum(const char *checksum); @@ -242,10 +236,9 @@ /** * 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(). + * The default directory is PURPLEDIR/custom_smiley. * - * @return The directory to store custom smyles cached files to. + * @return The directory in which to store custom smileys cached files. */ const char *purple_smileys_get_storing_dir(void);
--- a/libpurple/sound-theme.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/sound-theme.h Tue Apr 28 19:08:06 2009 +0000 @@ -73,6 +73,7 @@ /** * Returns a copy of the filename for the sound event. * + * @param theme The theme. * @param event The purple sound event to look up. * * @returns The filename of the sound event. @@ -83,6 +84,7 @@ /** * Returns a copy of the directory and filename for the sound event * + * @param theme The theme. * @param event The purple sound event to look up * * @returns The directory + '/' + filename of the sound event. This is @@ -94,8 +96,9 @@ /** * Sets the filename for a given sound event * - * @param event the purple sound event to look up - * @param filename the name of the file to be used for the event + * @param theme The theme. + * @param event the purple sound event to look up + * @param filename the name of the file to be used for the event */ void purple_sound_theme_set_file(PurpleSoundTheme *theme, const gchar *event,
--- a/libpurple/stun.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/stun.c Tue Apr 28 19:08:06 2009 +0000 @@ -341,6 +341,12 @@ } if (!purple_network_listen_range(12108, 12208, SOCK_DGRAM, hbn_listen_cb, hosts)) { + while(hosts) { + hosts = g_slist_remove(hosts, hosts->data); + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + } + nattype.status = PURPLE_STUN_STATUS_UNKNOWN; nattype.lookup_time = time(NULL); do_callbacks();
--- a/libpurple/theme-loader.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/theme-loader.h Tue Apr 28 19:08:06 2009 +0000 @@ -82,7 +82,8 @@ /** * Creates a new PurpleTheme * - * @param dir The directory containing the theme + * @param loader The theme loader + * @param dir The directory containing the theme * * @returns A PurpleTheme containing the information from the directory */
--- a/libpurple/theme-manager.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/theme-manager.c Tue Apr 28 19:08:06 2009 +0000 @@ -130,6 +130,7 @@ theme_dir = g_build_filename(purple_dir, type, NULL); theme = purple_theme_loader_build(loader, theme_dir); + g_free(theme_dir); if (PURPLE_IS_THEME(theme)) purple_theme_manager_add_theme(theme);
--- a/libpurple/theme-manager.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/theme-manager.h Tue Apr 28 19:08:06 2009 +0000 @@ -1,5 +1,5 @@ /** - * @file thememanager.h Theme Manager API + * @file theme-manager.h Theme Manager API */ /*
--- a/libpurple/util.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/util.h Tue Apr 28 19:08:06 2009 +0000 @@ -31,23 +31,26 @@ #include <stdio.h> +typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData; +typedef struct _PurpleMenuAction PurpleMenuAction; +typedef struct _PurpleKeyValuePair PurpleKeyValuePair; + #include "account.h" #include "xmlnode.h" #include "notify.h" + #ifdef __cplusplus extern "C" { #endif -typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData; - -typedef struct _PurpleMenuAction +struct _PurpleMenuAction { char *label; PurpleCallback callback; gpointer data; GList *children; -} PurpleMenuAction; +}; typedef char *(*PurpleInfoFieldFormatCallback)(const char *field, size_t len); @@ -57,12 +60,12 @@ * This is used by, among other things, purple_gtk_combo* functions to pass in a * list of key-value pairs so it can display a user-friendly value. */ -typedef struct _PurpleKeyValuePair +struct _PurpleKeyValuePair { gchar *key; void *value; -} PurpleKeyValuePair; +}; /** * Creates a new PurpleMenuAction.
--- a/libpurple/win32/win32dep.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/win32/win32dep.c Tue Apr 28 19:08:06 2009 +0000 @@ -467,7 +467,14 @@ WSACleanup(); g_free(app_data_dir); + g_free(install_dir); + g_free(lib_dir); + g_free(locale_dir); + app_data_dir = NULL; + install_dir = NULL; + lib_dir = NULL; + locale_dir = NULL; libpurpledll_hInstance = NULL; }
--- a/libpurple/xmlnode.c Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/xmlnode.c Tue Apr 28 19:08:06 2009 +0000 @@ -647,6 +647,28 @@ purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg); } +static void +xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error) +{ + struct _xmlnode_parser_data *xpd = user_data; + + if (error && (error->level == XML_ERR_ERROR || + error->level == XML_ERR_FATAL)) { + xpd->error = TRUE; + purple_debug_error("xmlnode", "XML parser error for xmlnode %p: " + "Domain %i, code %i, level %i: %s", + user_data, error->domain, error->code, error->level, + error->message ? error->message : "(null)\n"); + } else if (error) + purple_debug_warning("xmlnode", "XML parser error for xmlnode %p: " + "Domain %i, code %i, level %i: %s", + user_data, error->domain, error->code, error->level, + error->message ? error->message : "(null)\n"); + else + purple_debug_warning("xmlnode", "XML parser error for xmlnode %p\n", + user_data); +} + static xmlSAXHandler xmlnode_parser_libxml = { NULL, /* internalSubset */ NULL, /* isStandalone */ @@ -679,7 +701,7 @@ NULL, /* _private */ xmlnode_parser_element_start_libxml, /* startElementNs */ xmlnode_parser_element_end_libxml, /* endElementNs */ - NULL, /* serror */ + xmlnode_parser_structural_error_libxml, /* serror */ }; xmlnode *
--- a/libpurple/xmlnode.h Tue Apr 28 19:05:59 2009 +0000 +++ b/libpurple/xmlnode.h Tue Apr 28 19:08:06 2009 +0000 @@ -273,6 +273,8 @@ * @param child The child node. * * @return The parent or NULL. + * + * @since 2.6.0 */ xmlnode *xmlnode_get_parent(const xmlnode *child); @@ -333,11 +335,14 @@ * root node of an XML document will parse the entire document * into a tree of nodes, and return the xmlnode of the root. * - * @param str The string of xml. - * @param description The description of the file being parsed - * @process The utility that is calling xmlnode_from_file + * @param dir The directory where the file is located + * @param filename The filename + * @param description A description of the file being parsed. Displayed to + * the user if the file cannot be read. + * @param process The subsystem that is calling xmlnode_from_file. Used as + * the category for debugging. * - * @return The new node. + * @return The new node or NULL if an error occurred. * * @since 2.6.0 */
--- a/pidgin/eggtrayicon.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/eggtrayicon.h Tue Apr 28 19:08:06 2009 +0000 @@ -21,8 +21,7 @@ #ifndef __EGG_TRAY_ICON_H__ #define __EGG_TRAY_ICON_H__ -#include <gtk/gtkplug.h> -#include <gtk/gtkversion.h> +#include <gtk/gtk.h> #include <gdk/gdkx.h> G_BEGIN_DECLS
--- a/pidgin/gtkaccount.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkaccount.c Tue Apr 28 19:08:06 2009 +0000 @@ -413,7 +413,11 @@ if (dialog->protocol_menu != NULL) { +#if GTK_CHECK_VERSION(2,12,0) + g_object_ref(G_OBJECT(dialog->protocol_menu)); +#else gtk_widget_ref(dialog->protocol_menu); +#endif hbox = g_object_get_data(G_OBJECT(dialog->protocol_menu), "container"); gtk_container_remove(GTK_CONTAINER(hbox), dialog->protocol_menu); } @@ -440,13 +444,21 @@ { dialog->protocol_menu = pidgin_protocol_option_menu_new( dialog->protocol_id, G_CALLBACK(set_account_protocol_cb), dialog); +#if GTK_CHECK_VERSION(2,12,0) + g_object_ref(G_OBJECT(dialog->protocol_menu)); +#else gtk_widget_ref(dialog->protocol_menu); +#endif } hbox = add_pref_box(dialog, vbox, _("Pro_tocol:"), dialog->protocol_menu); g_object_set_data(G_OBJECT(dialog->protocol_menu), "container", hbox); +#if GTK_CHECK_VERSION(2,12,0) + g_object_unref(G_OBJECT(dialog->protocol_menu)); +#else gtk_widget_unref(dialog->protocol_menu); +#endif /* Username */ dialog->username_entry = gtk_entry_new();
--- a/pidgin/gtkblist-theme-loader.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkblist-theme-loader.c Tue Apr 28 19:08:06 2009 +0000 @@ -21,6 +21,7 @@ */ #include <stdlib.h> +#include <string.h> #include "xmlnode.h" @@ -37,6 +38,22 @@ * Buddy List Theme Builder *****************************************************************************/ +static PidginThemeFont * +pidgin_theme_font_parse(xmlnode *node) +{ + const char *font; + const char *colordesc; + GdkColor color; + + font = xmlnode_get_attrib(node, "font"); + + if ((colordesc = xmlnode_get_attrib(node, "color")) == NULL || + !gdk_color_parse(colordesc, &color)) + gdk_color_parse(DEFAULT_TEXT_COLOR, &color); + + return pidgin_theme_font_new(font, &color); +} + static PurpleTheme * pidgin_blist_loader_build(const gchar *dir) { @@ -44,11 +61,36 @@ gchar *filename_full, *data; const gchar *temp; gboolean success = TRUE; - GdkColor *bgcolor, *expanded_bgcolor, *collapsed_bgcolor, *contact_color; - GdkColor color; - FontColorPair *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *message_nick_said, *status; - PidginBlistLayout *layout; + GdkColor bgcolor, expanded_bgcolor, collapsed_bgcolor, contact_color; + PidginThemeFont *expanded, *collapsed, *contact, *online, *away, *offline, *idle, *message, *message_nick_said, *status; + PidginBlistLayout layout; PidginBlistTheme *theme; + int i; + struct { + const char *tag; + PidginThemeFont **font; + } lookups[] = { + {"contact_text", &contact}, + {"online_text", &online}, + {"away_text", &away}, + {"offline_text", &offline}, + {"idle_text", &idle}, + {"message_text", &message}, + {"message_nick_said_text", &message_nick_said}, + {"status_text", &status}, + {NULL, NULL} + }; + + expanded = NULL; + collapsed = NULL; + contact = NULL; + online = NULL; + away = NULL; + offline = NULL; + idle = NULL; + message = NULL; + message_nick_said = NULL; + status = NULL; /* Find the theme file */ g_return_val_if_fail(dir != NULL, NULL); @@ -63,145 +105,62 @@ sub_node = xmlnode_get_child(root_node, "description"); data = xmlnode_get_data(sub_node); - /* init all structs and colors */ - bgcolor = g_new0(GdkColor, 1); - expanded_bgcolor = g_new0(GdkColor, 1); - collapsed_bgcolor = g_new0(GdkColor, 1); - - layout = g_new0(PidginBlistLayout, 1); - - contact_color = g_new0(GdkColor, 1); - - expanded = g_new0(FontColorPair, 1); - collapsed = g_new0(FontColorPair, 1); - contact = g_new0(FontColorPair, 1); - online = g_new0(FontColorPair, 1); - away = g_new0(FontColorPair, 1); - offline = g_new0(FontColorPair, 1); - idle = g_new0(FontColorPair, 1); - message = g_new0(FontColorPair, 1); - message_nick_said = g_new0(FontColorPair, 1); - status = g_new0(FontColorPair, 1); - /* <blist> */ if ((success = (sub_node = xmlnode_get_child(root_node, "blist")) != NULL)) { - if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, bgcolor)) - gdk_colormap_alloc_color(gdk_colormap_get_system(), bgcolor, FALSE, TRUE); - else { - g_free(bgcolor); - bgcolor = NULL; - } + if ((temp = xmlnode_get_attrib(sub_node, "color")) != NULL && gdk_color_parse(temp, &bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), &bgcolor, FALSE, TRUE); + else + memset(&bgcolor, 0, sizeof(GdkColor)); } /* <groups> */ if ((success = (success && (sub_node = xmlnode_get_child(root_node, "groups")) != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "expanded")) != NULL))) { - expanded->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - - if ((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color)) - expanded->color = g_strdup(temp); - else expanded->color = g_strdup(DEFAULT_TEXT_COLOR); + expanded = pidgin_theme_font_parse(sub_sub_node); - if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, expanded_bgcolor)) - gdk_colormap_alloc_color(gdk_colormap_get_system(), expanded_bgcolor, FALSE, TRUE); - else { - g_free(expanded_bgcolor); - expanded_bgcolor = NULL; - } + if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &expanded_bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), &expanded_bgcolor, FALSE, TRUE); + else + memset(&expanded_bgcolor, 0, sizeof(GdkColor)); } if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "collapsed")) != NULL))) { - collapsed->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - - if((temp = xmlnode_get_attrib(sub_sub_node, "text_color")) != NULL && gdk_color_parse(temp, &color)) - collapsed->color = g_strdup(temp); - else collapsed->color = g_strdup(DEFAULT_TEXT_COLOR); + collapsed = pidgin_theme_font_parse(sub_sub_node); - if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, collapsed_bgcolor)) - gdk_colormap_alloc_color(gdk_colormap_get_system(), collapsed_bgcolor, FALSE, TRUE); - else { - g_free(collapsed_bgcolor); - collapsed_bgcolor = NULL; - } + if ((temp = xmlnode_get_attrib(sub_sub_node, "background")) != NULL && gdk_color_parse(temp, &collapsed_bgcolor)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), &collapsed_bgcolor, FALSE, TRUE); + else + memset(&collapsed_bgcolor, 0, sizeof(GdkColor)); } /* <buddys> */ if ((success = (success && (sub_node = xmlnode_get_child(root_node, "buddys")) != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "placement")) != NULL))) { - layout->status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0; - layout->text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1; - layout->emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2; - layout->protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3; - layout->buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4; - layout->show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1; + layout.status_icon = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) : 0; + layout.text = (temp = xmlnode_get_attrib(sub_sub_node, "name")) != NULL ? atoi(temp) : 1; + layout.emblem = (temp = xmlnode_get_attrib(sub_sub_node, "emblem")) != NULL ? atoi(temp) : 2; + layout.protocol_icon = (temp = xmlnode_get_attrib(sub_sub_node, "protocol_icon")) != NULL ? atoi(temp) : 3; + layout.buddy_icon = (temp = xmlnode_get_attrib(sub_sub_node, "buddy_icon")) != NULL ? atoi(temp) : 4; + layout.show_status = (temp = xmlnode_get_attrib(sub_sub_node, "status_icon")) != NULL ? atoi(temp) != 0 : 1; } if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "background")) != NULL))) { - if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), contact_color)) - gdk_colormap_alloc_color(gdk_colormap_get_system(), contact_color, FALSE, TRUE); - else { - g_free(contact_color); - contact_color = NULL; - } - } - - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "contact_text")) != NULL))) { - contact->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - contact->color = g_strdup(temp); - else contact->color = g_strdup(DEFAULT_TEXT_COLOR); - } - - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "online_text")) != NULL))) { - online->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - online->color = g_strdup(temp); - else online->color = g_strdup(DEFAULT_TEXT_COLOR); - } - - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "away_text")) != NULL))) { - away->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - away->color = g_strdup(temp); - else away->color = g_strdup(DEFAULT_TEXT_COLOR); + if(gdk_color_parse(xmlnode_get_attrib(sub_sub_node, "color"), &contact_color)) + gdk_colormap_alloc_color(gdk_colormap_get_system(), &contact_color, FALSE, TRUE); + else + memset(&contact_color, 0, sizeof(GdkColor)); } - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "offline_text")) != NULL))) { - offline->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - online->color = g_strdup(temp); - else online->color = g_strdup(DEFAULT_TEXT_COLOR); - } - - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "idle_text")) != NULL))) { - idle->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - idle->color = g_strdup(temp); - else online->color = g_strdup(DEFAULT_TEXT_COLOR); - } - - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "message_text")) != NULL))) { - message->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - message->color = g_strdup(temp); - else message->color = g_strdup(DEFAULT_TEXT_COLOR); - } - - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "message_nick_said_text")) != NULL))) { - message_nick_said->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - message_nick_said->color = g_strdup(temp); - else message_nick_said->color = g_strdup(DEFAULT_TEXT_COLOR); - } - - if ((success = (success && sub_node != NULL && (sub_sub_node = xmlnode_get_child(sub_node, "status_text")) != NULL))) { - status->font = g_strdup(xmlnode_get_attrib(sub_sub_node, "font")); - if(gdk_color_parse(temp = xmlnode_get_attrib(sub_sub_node, "color"), &color)) - status->color = g_strdup(temp); - else status->color = g_strdup(DEFAULT_TEXT_COLOR); + for (i = 0; success && lookups[i].tag; i++) { + if ((success = (sub_node != NULL && + (sub_sub_node = xmlnode_get_child(sub_node, lookups[i].tag)) != NULL))) { + *(lookups[i].font) = pidgin_theme_font_parse(sub_sub_node); + } else { + *(lookups[i].font) = NULL; + } } /* name is required for theme manager */ @@ -215,13 +174,13 @@ "image", xmlnode_get_attrib(root_node, "image"), "directory", dir, "description", data, - "background-color", bgcolor, - "layout", layout, - "expanded-color", expanded_bgcolor, + "background-color", &bgcolor, + "layout", &layout, + "expanded-color", &expanded_bgcolor, "expanded-text", expanded, - "collapsed-color", collapsed_bgcolor, + "collapsed-color", &collapsed_bgcolor, "collapsed-text", collapsed, - "contact-color", contact_color, + "contact-color", &contact_color, "contact", contact, "online", online, "away", away, @@ -231,6 +190,15 @@ "message_nick_said", message_nick_said, "status", status, NULL); + for (i = 0; lookups[i].tag; i++) { + if (*lookups[i].font) { + pidgin_theme_font_free(*lookups[i].font); + } + } + + pidgin_theme_font_free(expanded); + pidgin_theme_font_free(collapsed); + xmlnode_free(root_node); g_free(data);
--- a/pidgin/gtkblist-theme-loader.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkblist-theme-loader.h Tue Apr 28 19:08:06 2009 +0000 @@ -1,5 +1,5 @@ /** - * @file gtkblist-loader.h Pidgin Buddy List Theme Loader Class API + * @file gtkblist-theme-loader.h Pidgin Buddy List Theme Loader Class API */ /* pidgin
--- a/pidgin/gtkblist-theme.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkblist-theme.c Tue Apr 28 19:08:06 2009 +0000 @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "internal.h" #include "gtkblist-theme.h" #define PIDGIN_BLIST_THEME_GET_PRIVATE(Gobject) \ @@ -37,27 +38,34 @@ /* groups */ GdkColor *expanded_color; - FontColorPair *expanded; + PidginThemeFont *expanded; GdkColor *collapsed_color; - FontColorPair *collapsed; + PidginThemeFont *collapsed; /* buddy */ GdkColor *contact_color; - FontColorPair *contact; + PidginThemeFont *contact; - FontColorPair *online; - FontColorPair *away; - FontColorPair *offline; - FontColorPair *idle; - FontColorPair *message; - FontColorPair *message_nick_said; + PidginThemeFont *online; + PidginThemeFont *away; + PidginThemeFont *offline; + PidginThemeFont *idle; + PidginThemeFont *message; + PidginThemeFont *message_nick_said; - FontColorPair *status; + PidginThemeFont *status; } PidginBlistThemePrivate; +struct _PidginThemeFont +{ + gchar *font; + gchar color[10]; + GdkColor *gdkcolor; +}; + /****************************************************************************** * Globals *****************************************************************************/ @@ -92,18 +100,85 @@ * Helpers *****************************************************************************/ +PidginThemeFont * +pidgin_theme_font_new(const gchar *face, GdkColor *color) +{ + PidginThemeFont *font = g_new0(PidginThemeFont, 1); + font->font = g_strdup(face); + if (color) + pidgin_theme_font_set_color(font, color); + return font; +} + void -free_font_and_color(FontColorPair *pair) +pidgin_theme_font_free(PidginThemeFont *pair) { if (pair != NULL) { - if (pair->font) - g_free(pair->font); - if (pair->color) - g_free(pair->color); + g_free(pair->font); + if (pair->gdkcolor) + gdk_color_free(pair->gdkcolor); g_free(pair); } } +static PidginThemeFont * +copy_font_and_color(const PidginThemeFont *pair) +{ + PidginThemeFont *copy = g_new0(PidginThemeFont, 1); + copy->font = g_strdup(pair->font); + strncpy(copy->color, pair->color, sizeof(copy->color) - 1); + if (pair->gdkcolor) + copy->gdkcolor = gdk_color_copy(pair->gdkcolor); + return copy; +} + +void +pidgin_theme_font_set_font_face(PidginThemeFont *font, const gchar *face) +{ + g_return_if_fail(font); + g_return_if_fail(face); + + g_free(font->font); + font->font = g_strdup(face); +} + +void +pidgin_theme_font_set_color(PidginThemeFont *font, const GdkColor *color) +{ + g_return_if_fail(font); + + if (font->gdkcolor) + gdk_color_free(font->gdkcolor); + + font->gdkcolor = color ? gdk_color_copy(color) : NULL; + if (color) + g_snprintf(font->color, sizeof(font->color), + "#%02x%02x%02x", color->red >> 8, color->green >> 8, color->blue >> 8); + else + font->color[0] = '\0'; +} + +const gchar * +pidgin_theme_font_get_font_face(PidginThemeFont *font) +{ + g_return_val_if_fail(font, NULL); + return font->font; +} + +const GdkColor * +pidgin_theme_font_get_color(PidginThemeFont *font) +{ + g_return_val_if_fail(font, NULL); + return font->gdkcolor; +} + +const gchar * +pidgin_theme_font_get_color_describe(PidginThemeFont *font) +{ + g_return_val_if_fail(font, NULL); + return font->color[0] ? font->color : NULL; +} + /****************************************************************************** * GObject Stuff *****************************************************************************/ @@ -123,7 +198,7 @@ switch (param_id) { case PROP_BACKGROUND_COLOR: - g_value_set_pointer(value, pidgin_blist_theme_get_background_color(theme)); + g_value_set_boxed(value, pidgin_blist_theme_get_background_color(theme)); break; case PROP_OPACITY: g_value_set_double(value, pidgin_blist_theme_get_opacity(theme)); @@ -132,19 +207,19 @@ g_value_set_pointer(value, pidgin_blist_theme_get_layout(theme)); break; case PROP_EXPANDED_COLOR: - g_value_set_pointer(value, pidgin_blist_theme_get_expanded_background_color(theme)); + g_value_set_boxed(value, pidgin_blist_theme_get_expanded_background_color(theme)); break; case PROP_EXPANDED_TEXT: g_value_set_pointer(value, pidgin_blist_theme_get_expanded_text_info(theme)); break; case PROP_COLLAPSED_COLOR: - g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_background_color(theme)); + g_value_set_boxed(value, pidgin_blist_theme_get_collapsed_background_color(theme)); break; case PROP_COLLAPSED_TEXT: g_value_set_pointer(value, pidgin_blist_theme_get_collapsed_text_info(theme)); break; case PROP_CONTACT_COLOR: - g_value_set_pointer(value, pidgin_blist_theme_get_contact_color(theme)); + g_value_set_boxed(value, pidgin_blist_theme_get_contact_color(theme)); break; case PROP_CONTACT: g_value_set_pointer(value, pidgin_blist_theme_get_contact_text_info(theme)); @@ -184,7 +259,7 @@ switch (param_id) { case PROP_BACKGROUND_COLOR: - pidgin_blist_theme_set_background_color(theme, g_value_get_pointer(value)); + pidgin_blist_theme_set_background_color(theme, g_value_get_boxed(value)); break; case PROP_OPACITY: pidgin_blist_theme_set_opacity(theme, g_value_get_double(value)); @@ -193,19 +268,19 @@ pidgin_blist_theme_set_layout(theme, g_value_get_pointer(value)); break; case PROP_EXPANDED_COLOR: - pidgin_blist_theme_set_expanded_background_color(theme, g_value_get_pointer(value)); + pidgin_blist_theme_set_expanded_background_color(theme, g_value_get_boxed(value)); break; case PROP_EXPANDED_TEXT: pidgin_blist_theme_set_expanded_text_info(theme, g_value_get_pointer(value)); break; case PROP_COLLAPSED_COLOR: - pidgin_blist_theme_set_collapsed_background_color(theme, g_value_get_pointer(value)); + pidgin_blist_theme_set_collapsed_background_color(theme, g_value_get_boxed(value)); break; case PROP_COLLAPSED_TEXT: pidgin_blist_theme_set_collapsed_text_info(theme, g_value_get_pointer(value)); break; case PROP_CONTACT_COLOR: - pidgin_blist_theme_set_contact_color(theme, g_value_get_pointer(value)); + pidgin_blist_theme_set_contact_color(theme, g_value_get_boxed(value)); break; case PROP_CONTACT: pidgin_blist_theme_set_contact_text_info(theme, g_value_get_pointer(value)); @@ -245,20 +320,29 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(obj); /* Buddy List */ + if (priv->bgcolor) + gdk_color_free(priv->bgcolor); g_free(priv->layout); /* Group */ - free_font_and_color(priv->expanded); - free_font_and_color(priv->collapsed); + if (priv->expanded_color) + gdk_color_free(priv->expanded_color); + pidgin_theme_font_free(priv->expanded); + if (priv->collapsed_color) + gdk_color_free(priv->collapsed_color); + pidgin_theme_font_free(priv->collapsed); /* Buddy */ - free_font_and_color(priv->contact); - free_font_and_color(priv->online); - free_font_and_color(priv->away); - free_font_and_color(priv->offline); - free_font_and_color(priv->message); - free_font_and_color(priv->message_nick_said); - free_font_and_color(priv->status); + if (priv->contact_color) + gdk_color_free(priv->contact_color); + pidgin_theme_font_free(priv->contact); + pidgin_theme_font_free(priv->online); + pidgin_theme_font_free(priv->away); + pidgin_theme_font_free(priv->offline); + pidgin_theme_font_free(priv->idle); + pidgin_theme_font_free(priv->message); + pidgin_theme_font_free(priv->message_nick_said); + pidgin_theme_font_free(priv->status); g_free(priv); @@ -278,81 +362,81 @@ obj_class->finalize = pidgin_blist_theme_finalize; /* Buddy List */ - pspec = g_param_spec_pointer("background-color", "Background Color", - "The background color for the buddy list", - G_PARAM_READWRITE); + pspec = g_param_spec_boxed("background-color", _("Background Color"), + _("The background color for the buddy list"), + GDK_TYPE_COLOR, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_BACKGROUND_COLOR, pspec); - pspec = g_param_spec_pointer("layout", "Layout", - "The layout of icons, name, and status of the blist", + pspec = g_param_spec_pointer("layout", _("Layout"), + _("The layout of icons, name, and status of the blist"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_LAYOUT, pspec); /* Group */ - pspec = g_param_spec_pointer("expanded-color", "Expanded Background Color", - "The background color of an expanded group", - G_PARAM_READWRITE); + pspec = g_param_spec_boxed("expanded-color", _("Expanded Background Color"), + _("The background color of an expanded group"), + GDK_TYPE_COLOR, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_EXPANDED_COLOR, pspec); - pspec = g_param_spec_pointer("expanded-text", "Expanded Text", - "The text information for when a group is expanded", + pspec = g_param_spec_pointer("expanded-text", _("Expanded Text"), + _("The text information for when a group is expanded"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_EXPANDED_TEXT, pspec); - pspec = g_param_spec_pointer("collapsed-color", "Collapsed Background Color", - "The background color of a collapsed group", - G_PARAM_READWRITE); + pspec = g_param_spec_boxed("collapsed-color", _("Collapsed Background Color"), + _("The background color of a collapsed group"), + GDK_TYPE_COLOR, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_COLLAPSED_COLOR, pspec); - pspec = g_param_spec_pointer("collapsed-text", "Collapsed Text", - "The text information for when a group is collapsed", + pspec = g_param_spec_pointer("collapsed-text", _("Collapsed Text"), + _("The text information for when a group is collapsed"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_COLLAPSED_TEXT, pspec); /* Buddy */ - pspec = g_param_spec_pointer("contact-color", "Contact/Chat Background Color", - "The background color of a contact or chat", - G_PARAM_READWRITE); + pspec = g_param_spec_boxed("contact-color", _("Contact/Chat Background Color"), + _("The background color of a contact or chat"), + GDK_TYPE_COLOR, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_CONTACT_COLOR, pspec); - pspec = g_param_spec_pointer("contact", "Contact Text", - "The text information for when a contact is expanded", + pspec = g_param_spec_pointer("contact", _("Contact Text"), + _("The text information for when a contact is expanded"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_CONTACT, pspec); - pspec = g_param_spec_pointer("online", "On-line Text", - "The text information for when a buddy is online", + pspec = g_param_spec_pointer("online", _("On-line Text"), + _("The text information for when a buddy is online"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_ONLINE, pspec); - pspec = g_param_spec_pointer("away", "Away Text", - "The text information for when a buddy is away", + pspec = g_param_spec_pointer("away", _("Away Text"), + _("The text information for when a buddy is away"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_AWAY, pspec); - pspec = g_param_spec_pointer("offline", "Off-line Text", - "The text information for when a buddy is off-line", + pspec = g_param_spec_pointer("offline", _("Off-line Text"), + _("The text information for when a buddy is off-line"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_OFFLINE, pspec); - pspec = g_param_spec_pointer("idle", "Idle Text", - "The text information for when a buddy is idle", + pspec = g_param_spec_pointer("idle", _("Idle Text"), + _("The text information for when a buddy is idle"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_IDLE, pspec); - pspec = g_param_spec_pointer("message", "Message Text", - "The text information for when a buddy has an unread message", + pspec = g_param_spec_pointer("message", _("Message Text"), + _("The text information for when a buddy has an unread message"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_MESSAGE, pspec); - pspec = g_param_spec_pointer("message_nick_said", "Message (Nick Said) Text", - "The text information for when a chat has an unread message that mentions your nick", + pspec = g_param_spec_pointer("message_nick_said", _("Message (Nick Said) Text"), + _("The text information for when a chat has an unread message that mentions your nick"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_MESSAGE_NICK_SAID, pspec); - pspec = g_param_spec_pointer("status", "Status Text", - "The text information for a buddy's status", + pspec = g_param_spec_pointer("status", _("Status Text"), + _("The text information for a buddy's status"), G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_STATUS, pspec); } @@ -435,7 +519,7 @@ return priv->expanded_color; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -459,7 +543,7 @@ return priv->collapsed_color; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -483,7 +567,7 @@ return priv->contact_color; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -495,7 +579,7 @@ return priv->contact; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -507,7 +591,7 @@ return priv->online; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -519,7 +603,7 @@ return priv->away; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -531,7 +615,7 @@ return priv->offline; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -543,7 +627,7 @@ return priv->idle; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -555,7 +639,7 @@ return priv->message; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -567,7 +651,7 @@ return priv->message_nick_said; } -FontColorPair * +PidginThemeFont * pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme) { PidginBlistThemePrivate *priv; @@ -581,7 +665,7 @@ /* Set Methods */ void -pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color) +pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, const GdkColor *color) { PidginBlistThemePrivate *priv; @@ -589,7 +673,9 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - priv->bgcolor = color; + if (priv->bgcolor) + gdk_color_free(priv->bgcolor); + priv->bgcolor = gdk_color_copy(color); } void @@ -605,7 +691,7 @@ } void -pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout) +pidgin_blist_theme_set_layout(PidginBlistTheme *theme, const PidginBlistLayout *layout) { PidginBlistThemePrivate *priv; @@ -614,11 +700,11 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); g_free(priv->layout); - priv->layout = layout; + priv->layout = g_memdup(layout, sizeof(PidginBlistLayout)); } void -pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color) +pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, const GdkColor *color) { PidginBlistThemePrivate *priv; @@ -626,11 +712,13 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - priv->expanded_color = color; + if (priv->expanded_color) + gdk_color_free(priv->expanded_color); + priv->expanded_color = gdk_color_copy(color); } void -pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -638,12 +726,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->expanded); - priv->expanded = pair; + pidgin_theme_font_free(priv->expanded); + priv->expanded = copy_font_and_color(pair); } void -pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color) +pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, const GdkColor *color) { PidginBlistThemePrivate *priv; @@ -651,11 +739,13 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - priv->collapsed_color = color; + if (priv->collapsed_color) + gdk_color_free(priv->collapsed_color); + priv->collapsed_color = gdk_color_copy(color); } void -pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -663,12 +753,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->collapsed); - priv->collapsed = pair; + pidgin_theme_font_free(priv->collapsed); + priv->collapsed = copy_font_and_color(pair); } void -pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color) +pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, const GdkColor *color) { PidginBlistThemePrivate *priv; @@ -676,11 +766,13 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - priv->contact_color = color; + if (priv->contact_color) + gdk_color_free(priv->contact_color); + priv->contact_color = gdk_color_copy(color); } void -pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -688,12 +780,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->contact); - priv->contact = pair; + pidgin_theme_font_free(priv->contact); + priv->contact = copy_font_and_color(pair); } void -pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -701,12 +793,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->online); - priv->online = pair; + pidgin_theme_font_free(priv->online); + priv->online = copy_font_and_color(pair); } void -pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -714,12 +806,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->away); - priv->away = pair; + pidgin_theme_font_free(priv->away); + priv->away = copy_font_and_color(pair); } void -pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -727,12 +819,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->offline); - priv->offline = pair; + pidgin_theme_font_free(priv->offline); + priv->offline = copy_font_and_color(pair); } void -pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -740,12 +832,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->idle); - priv->idle = pair; + pidgin_theme_font_free(priv->idle); + priv->idle = copy_font_and_color(pair); } void -pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -753,12 +845,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->message); - priv->message = pair; + pidgin_theme_font_free(priv->message); + priv->message = copy_font_and_color(pair); } void -pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -766,12 +858,12 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->message_nick_said); - priv->message_nick_said = pair; + pidgin_theme_font_free(priv->message_nick_said); + priv->message_nick_said = copy_font_and_color(pair); } void -pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair) +pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair) { PidginBlistThemePrivate *priv; @@ -779,6 +871,6 @@ priv = PIDGIN_BLIST_THEME_GET_PRIVATE(G_OBJECT(theme)); - free_font_and_color(priv->status); - priv->status = pair; + pidgin_theme_font_free(priv->status); + priv->status = copy_font_and_color(pair); }
--- a/pidgin/gtkblist-theme.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkblist-theme.h Tue Apr 28 19:08:06 2009 +0000 @@ -59,12 +59,15 @@ PurpleThemeClass parent_class; }; +#if 0 typedef struct { - gchar *font; - gchar *color; + const gchar *font; + const gchar *color; -} FontColorPair; +} PidginThemeFont; +#endif +typedef struct _PidginThemeFont PidginThemeFont; typedef struct { @@ -78,13 +81,68 @@ } PidginBlistLayout; /**************************************************************************/ -/** @name FontColorPair API */ +/** @name PidginThemeFont API */ /**************************************************************************/ /** + * Create a new PidginThemeFont. + * + * @param face The font face + * @param color The color of the font + * + * @return A newly created PidginThemeFont + */ +PidginThemeFont * pidgin_theme_font_new(const gchar *face, GdkColor *color); + +/** * Frees a font and color pair + * + * @param font The theme font + */ +void pidgin_theme_font_free(PidginThemeFont *font); + +/** + * Set the font-face of a PidginThemeFont. + * + * @param font The PidginThemeFont + * @param face The font-face */ -void free_font_and_color(FontColorPair *pair); +void pidgin_theme_font_set_font_face(PidginThemeFont *font, const gchar *face); + +/** + * Set the color of a PidginThemeFont. + * + * @param font The PidginThemeFont + * @param color The color + */ +void pidgin_theme_font_set_color(PidginThemeFont *font, const GdkColor *color); + +/** + * Get the font-face of a PidginThemeFont. + * + * @param font The PidginThemeFont + * + * @return The font-face, or NULL if none is set. + */ +const gchar * pidgin_theme_font_get_font_face(PidginThemeFont *font); + +/** + * Get the color of a PidginThemeFont as a GdkColor object. + * + * @param font The PidginThemeFont + * + * @return The color, or NULL if none is set. + */ +const GdkColor * pidgin_theme_font_get_color(PidginThemeFont *font); + +/** + * Get the color of a PidginThemeFont. + * + * @param font The PidginThemeFont + * + * @return The color, or NULL if none is set. + */ +const gchar * pidgin_theme_font_get_color_describe(PidginThemeFont *font); /**************************************************************************/ /** @name Purple Buddy List Theme API */ @@ -102,6 +160,8 @@ /** * Returns the background color of the buddy list. * + * @param theme The PidginBlist theme. + * * @returns A gdk color. */ GdkColor *pidgin_blist_theme_get_background_color(PidginBlistTheme *theme); @@ -110,6 +170,8 @@ * Returns the opacity of the buddy list window * (0.0 or clear to 1.0 fully opaque). * + * @param theme The PidginBlist theme. + * * @returns The opacity */ gdouble pidgin_blist_theme_get_opacity(PidginBlistTheme *theme); @@ -117,6 +179,8 @@ /** * Returns the layout to be used with the buddy list. * + * @param theme The PidginBlist theme. + * * @returns The buddy list layout. */ PidginBlistLayout *pidgin_blist_theme_get_layout(PidginBlistTheme *theme); @@ -124,6 +188,8 @@ /** * Returns the background color to be used with expanded groups. * + * @param theme The PidginBlist theme. + * * @returns A gdk color. */ GdkColor *pidgin_blist_theme_get_expanded_background_color(PidginBlistTheme *theme); @@ -131,13 +197,17 @@ /** * Returns the text font and color to be used with expanded groups. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_expanded_text_info(PidginBlistTheme *theme); /** * Returns the background color to be used with collapsed groups. * + * @param theme The PidginBlist theme. + * * @returns A gdk color. */ GdkColor *pidgin_blist_theme_get_collapsed_background_color(PidginBlistTheme *theme); @@ -145,13 +215,17 @@ /** * Returns the text font and color to be used with collapsed groups. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_collapsed_text_info(PidginBlistTheme *theme); /** * Returns the colors to be used for contacts and chats. * + * @param theme The PidginBlist theme. + * * @returns A gdkcolor for contacts and chats. */ GdkColor *pidgin_blist_theme_get_contact_color(PidginBlistTheme *theme); @@ -159,72 +233,90 @@ /** * Returns the text font and color to be used for expanded contacts. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_contact_text_info(PidginBlistTheme *theme); /** * Returns the text font and color to be used for online buddies. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_online_text_info(PidginBlistTheme *theme); /** * Returns the text font and color to be used for away and idle buddies. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_away_text_info(PidginBlistTheme *theme); /** * Returns the text font and color to be used for offline buddies. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_offline_text_info(PidginBlistTheme *theme); /** * Returns the text font and color to be used for idle buddies. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_idle_text_info(PidginBlistTheme *theme); /** * Returns the text font and color to be used for buddies with unread messages. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_unread_message_text_info(PidginBlistTheme *theme); /** * Returns the text font and color to be used for chats with unread messages * that mention your nick. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_unread_message_nick_said_text_info(PidginBlistTheme *theme); /** * Returns the text font and color to be used for a buddy's status message. * + * @param theme The PidginBlist theme. + * * @returns A font and color pair. */ - FontColorPair *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme); + PidginThemeFont *pidgin_blist_theme_get_status_text_info(PidginBlistTheme *theme); /* Set Methods */ /** * Sets the background color to be used for this buddy list theme. * + * @param theme The PidginBlist theme. * @param color The new background color. */ -void pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, GdkColor *color); +void pidgin_blist_theme_set_background_color(PidginBlistTheme *theme, const GdkColor *color); /** * Sets the opacity to be used for this buddy list theme. * + * @param theme The PidginBlist theme. * @param opacity The new opacity setting. */ void pidgin_blist_theme_set_opacity(PidginBlistTheme *theme, gdouble opacity); @@ -232,101 +324,115 @@ /** * Sets the buddy list layout to be used for this buddy list theme. * + * @param theme The PidginBlist theme. * @param layout The new layout. */ -void pidgin_blist_theme_set_layout(PidginBlistTheme *theme, PidginBlistLayout *layout); +void pidgin_blist_theme_set_layout(PidginBlistTheme *theme, const PidginBlistLayout *layout); /** * Sets the background color to be used for expanded groups. * + * @param theme The PidginBlist theme. * @param color The new background color. */ -void pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, GdkColor *color); +void pidgin_blist_theme_set_expanded_background_color(PidginBlistTheme *theme, const GdkColor *color); /** * Sets the text color and font to be used for expanded groups. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_expanded_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the background color to be used for collapsed groups. * + * @param theme The PidginBlist theme. * @param color The new background color. */ -void pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, GdkColor *color); +void pidgin_blist_theme_set_collapsed_background_color(PidginBlistTheme *theme, const GdkColor *color); /** * Sets the text color and font to be used for expanded groups. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_collapsed_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the background color to be used for contacts and chats. * + * @param theme The PidginBlist theme. * @param color The color to use for contacts and chats. */ -void pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, GdkColor *color); +void pidgin_blist_theme_set_contact_color(PidginBlistTheme *theme, const GdkColor *color); /** * Sets the text color and font to be used for expanded contacts. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_contact_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the text color and font to be used for online buddies. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_online_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the text color and font to be used for away and idle buddies. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_away_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the text color and font to be used for offline buddies. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_offline_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the text color and font to be used for idle buddies. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_idle_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the text color and font to be used for buddies with unread messages. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_unread_message_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the text color and font to be used for a chat with unread messages * that mention your nick. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_unread_message_nick_said_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); /** * Sets the text color and font to be used for buddy status messages. * - * @param pair The new text font at color pair. + * @param theme The PidginBlist theme. + * @param pair The new text font and color pair. */ -void pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, FontColorPair *pair); +void pidgin_blist_theme_set_status_text_info(PidginBlistTheme *theme, const PidginThemeFont *pair); G_END_DECLS #endif /* PIDGIN_BLIST_THEME_H */
--- a/pidgin/gtkblist.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkblist.c Tue Apr 28 19:08:06 2009 +0000 @@ -3902,6 +3902,24 @@ return ret; } +static const char * +theme_font_get_color_default(PidginThemeFont *font, const char *def) +{ + const char *ret; + if (!font || !(ret = pidgin_theme_font_get_color_describe(font))) + ret = def; + return ret; +} + +static const char * +theme_font_get_face_default(PidginThemeFont *font, const char *def) +{ + const char *ret; + if (!font || !(ret = pidgin_theme_font_get_font_face(font))) + ret = def; + return ret; +} + gchar * pidgin_blist_get_name_markup(PurpleBuddy *b, gboolean selected, gboolean aliased) { @@ -3916,7 +3934,7 @@ PurpleConversation *conv = find_conversation_with_buddy(b); gboolean hidden_conv = FALSE; gboolean biglist = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); - FontColorPair *pair = NULL; + PidginThemeFont *statusfont = NULL, *namefont = NULL; PidginBlistTheme *theme; if (conv != NULL) { @@ -4034,46 +4052,29 @@ /* choose the colors of the text */ theme = pidgin_blist_get_theme(); - - if (purple_presence_is_idle(presence)) { - if (theme) - pair = pidgin_blist_theme_get_idle_text_info(theme); - status_color = name_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; - status_font = name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; - - } else if (!purple_presence_is_online(presence)) { - if (theme) - pair = pidgin_blist_theme_get_offline_text_info(theme); - name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL; - name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; - - if (theme) - pair = pidgin_blist_theme_get_status_text_info(theme); - status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; - status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; - - } else if (purple_presence_is_available(presence)) { - if (theme) - pair = pidgin_blist_theme_get_online_text_info(theme); - name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL; - name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; - - if (theme) - pair = pidgin_blist_theme_get_status_text_info(theme); - status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; - status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; - - } else { - if (theme) - pair = pidgin_blist_theme_get_away_text_info(theme); - name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL; - name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; - - if (theme) - pair = pidgin_blist_theme_get_status_text_info(theme); - status_color = (pair != NULL && pair->color != NULL) ? pair->color : "dim grey"; - status_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; - } + name_color = NULL; + + if (theme) { + if (purple_presence_is_idle(presence)) { + namefont = statusfont = pidgin_blist_theme_get_idle_text_info(theme); + name_color = "dim grey"; + } else if (!purple_presence_is_online(presence)) { + namefont = pidgin_blist_theme_get_offline_text_info(theme); + statusfont = pidgin_blist_theme_get_status_text_info(theme); + } else if (purple_presence_is_available(presence)) { + namefont = pidgin_blist_theme_get_online_text_info(theme); + statusfont = pidgin_blist_theme_get_status_text_info(theme); + } else { + namefont = pidgin_blist_theme_get_away_text_info(theme); + statusfont = pidgin_blist_theme_get_status_text_info(theme); + } + } + + name_color = theme_font_get_color_default(namefont, name_color); + name_font = theme_font_get_face_default(namefont, ""); + + status_color = theme_font_get_color_default(statusfont, "dim grey"); + status_font = theme_font_get_face_default(statusfont, ""); if (aliased && selected) { if (theme) { @@ -4651,6 +4652,12 @@ } static void +account_actions_changed(PurpleAccount *account, gpointer data) +{ + pidgin_blist_update_accounts_menu(); +} + +static void account_status_changed(PurpleAccount *account, PurpleStatus *old, PurpleStatus *new, PidginBuddyList *gtkblist) { @@ -5505,6 +5512,7 @@ GtkWidget *sep; GtkWidget *label; char *pretty, *tmp; + const char *theme_name; GtkAccelGroup *accel_group; GtkTreeSelection *selection; GtkTargetEntry dte[] = {{"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, @@ -5523,7 +5531,11 @@ gtkblist = PIDGIN_BLIST(list); priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist); - priv->current_theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"), "blist")); + theme_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"); + if (theme_name && *theme_name) + priv->current_theme = PIDGIN_BLIST_THEME(purple_theme_manager_find_theme(theme_name, "blist")); + else + priv->current_theme = NULL; gtkblist->empty_avatar = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); gdk_pixbuf_fill(gtkblist->empty_avatar, 0x00000000); @@ -5790,7 +5802,7 @@ purple_blist_set_visible(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/list_visible")); /* start the refresh timer */ - gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)pidgin_blist_refresh_timer, list); + gtkblist->refresh_timer = purple_timeout_add_seconds(30, (GSourceFunc)pidgin_blist_refresh_timer, list); handle = pidgin_blist_get_handle(); @@ -5831,6 +5843,8 @@ purple_signal_connect(handle, "account-error-changed", gtkblist, PURPLE_CALLBACK(update_account_error_state), gtkblist); + purple_signal_connect(handle, "account-actions-changed", gtkblist, + PURPLE_CALLBACK(account_actions_changed), NULL); handle = pidgin_account_get_handle(); purple_signal_connect(handle, "account-modified", gtkblist, @@ -5911,7 +5925,7 @@ blist = purple_get_blist(); gtkblist = PIDGIN_BLIST(purple_get_blist()); - gtkblist->refresh_timer = g_timeout_add(30000,(GSourceFunc)pidgin_blist_refresh_timer, blist); + gtkblist->refresh_timer = purple_timeout_add_seconds(30,(GSourceFunc)pidgin_blist_refresh_timer, blist); } static gboolean get_iter_from_node(PurpleBlistNode *node, GtkTreeIter *iter) { @@ -6191,8 +6205,8 @@ char *mark, *esc; PurpleBlistNode *selected_node = NULL; GtkTreeIter iter; - FontColorPair *pair; - gchar *text_color, *text_font; + PidginThemeFont *pair; + gchar const *text_color, *text_font; PidginBlistTheme *theme; group = (PurpleGroup*)gnode; @@ -6218,8 +6232,8 @@ pair = pidgin_blist_theme_get_collapsed_text_info(theme); - text_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color; - text_font = (pair == NULL || pair->font == NULL) ? "" : pair->font; + text_color = selected ? NULL : theme_font_get_color_default(pair, NULL); + text_font = theme_font_get_face_default(pair, ""); esc = g_markup_escape_text(group->name, -1); if (text_color) { @@ -6277,7 +6291,7 @@ if (idle_secs > 0) { - FontColorPair *pair = NULL; + PidginThemeFont *pair = NULL; const gchar *textcolor; time_t t; int ihrs, imin; @@ -6286,18 +6300,18 @@ ihrs = (t - idle_secs) / 3600; imin = ((t - idle_secs) / 60) % 60; - if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL && pair->color != NULL) - textcolor = pair->color; + if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL) + textcolor = pidgin_theme_font_get_color_describe(pair); else textcolor = NULL; if (textcolor) { idle = g_strdup_printf("<span color='%s' font_desc='%s'>%d:%02d</span>", - textcolor, (pair == NULL || pair->font == NULL) ? "" : pair->font, + textcolor, theme_font_get_face_default(pair, ""), ihrs, imin); } else { idle = g_strdup_printf("<span font_desc='%s'>%d:%02d</span>", - (pair == NULL || pair->font == NULL) ? "" : pair->font, + theme_font_get_face_default(pair, ""), ihrs, imin); } } @@ -6382,7 +6396,7 @@ const gchar *fg_color, *font; GdkColor *color = NULL; PidginBlistTheme *theme = pidgin_blist_get_theme(); - FontColorPair *pair; + PidginThemeFont *pair; gboolean selected = (gtkblist->selected_node == cnode); mark = g_markup_escape_text(purple_contact_get_alias(contact), -1); @@ -6395,8 +6409,8 @@ color = pidgin_blist_theme_get_contact_color(theme); } - font = (pair == NULL || pair->font == NULL) ? "" : pair->font; - fg_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color; + font = theme_font_get_face_default(pair, ""); + fg_color = selected ? NULL : theme_font_get_color_default(pair, NULL); if (fg_color) { tmp = g_strdup_printf("<span font_desc='%s' color='%s'>%s</span>", @@ -6493,7 +6507,7 @@ PurpleConversation *conv; gboolean hidden = FALSE; GdkColor *bgcolor = NULL; - FontColorPair *pair; + PidginThemeFont *pair; PidginBlistTheme *theme; gboolean selected = (gtkblist->selected_node == node); gboolean nick_said = FALSE; @@ -6531,12 +6545,10 @@ else pair = pidgin_blist_theme_get_online_text_info(theme); - font = (pair == NULL || pair->font == NULL) ? "" : pair->font; - if (selected || pair == NULL || pair->color == NULL) + font = theme_font_get_face_default(pair, ""); + if (selected || !(color = theme_font_get_color_default(pair, NULL))) /* nick_said color is the same as gtkconv:tab-label-attention */ color = (nick_said ? "#006aff" : NULL); - else - color = pair->color; if (color) { tmp = g_strdup_printf("<span font_desc='%s' color='%s' weight='%s'>%s</span>", @@ -6625,14 +6637,14 @@ purple_signals_disconnect_by_handle(gtkblist); if (gtkblist->headline_close) - gdk_pixbuf_unref(gtkblist->headline_close); + g_object_unref(G_OBJECT(gtkblist->headline_close)); gtk_widget_destroy(gtkblist->window); pidgin_blist_tooltip_destroy(); if (gtkblist->refresh_timer) - g_source_remove(gtkblist->refresh_timer); + purple_timeout_remove(gtkblist->refresh_timer); if (gtkblist->timeout) g_source_remove(gtkblist->timeout); if (gtkblist->drag_timeout) @@ -7447,7 +7459,7 @@ if(gtknode->recent_signonoff_timer > 0) purple_timeout_remove(gtknode->recent_signonoff_timer); - gtknode->recent_signonoff_timer = purple_timeout_add(10000, + gtknode->recent_signonoff_timer = purple_timeout_add_seconds(10, (GSourceFunc)buddy_signonoff_timeout_cb, buddy); }
--- a/pidgin/gtkcellrendererexpander.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkcellrendererexpander.c Tue Apr 28 19:08:06 2009 +0000 @@ -30,7 +30,6 @@ */ #include <gtk/gtk.h> -#include <gtk/gtktreeview.h> #include "gtkcellrendererexpander.h" static void pidgin_cell_renderer_expander_get_property (GObject *object,
--- a/pidgin/gtkcellrendererexpander.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkcellrendererexpander.h Tue Apr 28 19:08:06 2009 +0000 @@ -21,7 +21,7 @@ #ifndef _PIDGINCELLRENDEREREXPANDER_H_ #define _PIDGINCELLRENDEREREXPANDER_H_ -#include <gtk/gtkcellrenderer.h> +#include <gtk/gtk.h> #ifdef __cplusplus extern "C" {
--- a/pidgin/gtkcellrendererprogress.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkcellrendererprogress.h Tue Apr 28 19:08:06 2009 +0000 @@ -21,7 +21,7 @@ #ifndef _PIDGINCELLRENDERERPROGRESS_H_ #define _PIDGINCELLRENDERERPROGRESS_H_ -#include <gtk/gtkcellrenderer.h> +#include <gtk/gtk.h> #ifdef __cplusplus extern "C" {
--- a/pidgin/gtkconv.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkconv.c Tue Apr 28 19:08:06 2009 +0000 @@ -2512,13 +2512,49 @@ return get_prpl_icon_list(account); } +static const char * +pidgin_conv_get_icon_stock(PurpleConversation *conv) +{ + PurpleAccount *account = NULL; + const char *stock = NULL; + + g_return_val_if_fail(conv != NULL, NULL); + + account = purple_conversation_get_account(conv); + g_return_val_if_fail(account != NULL, NULL); + + /* Use the buddy icon, if possible */ + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + const char *name = NULL; + PurpleBuddy *b; + name = purple_conversation_get_name(conv); + b = purple_find_buddy(account, name); + if (b != NULL) { + PurplePresence *p = purple_buddy_get_presence(b); + PurpleStatus *active = purple_presence_get_active_status(p); + PurpleStatusType *type = purple_status_get_type(active); + PurpleStatusPrimitive prim = purple_status_type_get_primitive(type); + stock = pidgin_stock_id_from_status_primitive(prim); + } else { + stock = PIDGIN_STOCK_STATUS_PERSON; + } + } else { + stock = PIDGIN_STOCK_STATUS_CHAT; + } + + return stock; +} + static GdkPixbuf * pidgin_conv_get_icon(PurpleConversation *conv, GtkWidget *parent, const char *icon_size) { PurpleAccount *account = NULL; const char *name = NULL; + const char *stock = NULL; GdkPixbuf *status = NULL; PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); + GtkIconSize size; + g_return_val_if_fail(conv != NULL, NULL); account = purple_conversation_get_account(conv); @@ -2531,40 +2567,17 @@ if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { PurpleBuddy *b = purple_find_buddy(account, name); if (b != NULL) { - PurplePresence *p = purple_buddy_get_presence(b); /* I hate this hack. It fixes a bug where the pending message icon * displays in the conv tab even though it shouldn't. * A better solution would be great. */ if (ops && ops->update) ops->update(NULL, (PurpleBlistNode*)b); - - /* XXX Seanegan: We really need a util function to return a pixbuf for a Presence to avoid all this switching */ - if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY)) - status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, parent, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY)) - status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, parent, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE)) - status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, parent, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, parent, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, parent, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, parent, icon_size); - } - } - - /* If they don't have a buddy icon, then use the PRPL icon */ - if (status == NULL) { - GtkIconSize size = gtk_icon_size_from_name(icon_size); - if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_PERSON, - size, "GtkWidget"); - } else { - status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_CHAT, - size, "GtkWidget"); - } - } + } + } + + stock = pidgin_conv_get_icon_stock(conv); + size = gtk_icon_size_from_name(icon_size); + status = gtk_widget_render_icon (parent, stock, size, "GtkWidget"); return status; } @@ -2582,9 +2595,9 @@ PidginConversation *gtkconv; PidginWindow *win; GList *l; - GdkPixbuf *status = NULL; - GdkPixbuf *infopane_status = NULL; GdkPixbuf *emblem = NULL; + const char *status = NULL; + const char *infopane_status = NULL; g_return_if_fail(conv != NULL); @@ -2593,8 +2606,7 @@ if (conv != gtkconv->active_conv) return; - status = pidgin_conv_get_tab_icon(conv, TRUE); - infopane_status = pidgin_conv_get_tab_icon(conv, FALSE); + status = infopane_status = pidgin_conv_get_icon_stock(conv); if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { PurpleBuddy *b = purple_find_buddy(conv->account, conv->name); @@ -2604,8 +2616,8 @@ g_return_if_fail(status != NULL); - gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->icon), status); - gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status); + g_object_set(G_OBJECT(gtkconv->icon), "stock", status, NULL); + g_object_set(G_OBJECT(gtkconv->menu_icon), "stock", status, NULL); gtk_list_store_set(GTK_LIST_STORE(gtkconv->infopane_model), &(gtkconv->infopane_iter), @@ -2633,9 +2645,6 @@ gtk_widget_queue_resize(gtkconv->infopane); gtk_widget_queue_draw(gtkconv->infopane); - if (status != NULL) - g_object_unref(status); - if (pidgin_conv_window_is_active_conversation(conv) && (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM || gtkconv->u.im->anim == NULL)) @@ -3071,16 +3080,13 @@ PurpleConversation *conv = (PurpleConversation*)l->data; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - GtkWidget *icon = gtk_image_new(); - GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC); + GtkWidget *icon = gtk_image_new_from_stock(pidgin_conv_get_icon_stock(conv), + gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC)); GtkWidget *item; gchar *text = g_strdup_printf("%s (%d)", gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)), gtkconv->unseen_count); - gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf); - g_object_unref(pbuf); - item = gtk_image_menu_item_new_with_label(text); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), icon); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(unseen_conv_menu_cb), conv); @@ -3941,12 +3947,9 @@ update_send_to_selection(win); } -static GdkPixbuf * +static const char * get_chat_buddy_status_icon(PurpleConvChat *chat, const char *name, PurpleConvChatBuddyFlags flags) { - PidginConversation *gtkconv = PIDGIN_CONVERSATION(chat->conv); - GdkPixbuf *pixbuf, *scale, *scale2; - char *filename; const char *image = NULL; if (flags & PURPLE_CBFLAGS_FOUNDER) { @@ -3962,28 +3965,7 @@ } else { return NULL; } - - pixbuf = gtk_widget_render_icon (gtkconv->tab_cont, image, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), - "GtkTreeView"); - - if (!pixbuf) - return NULL; - - scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); - g_object_unref(pixbuf); - - if (flags && purple_conv_chat_is_user_ignored(chat, name)) { -/* TODO: the .../status/default directory isn't installed, should it be? */ - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "default", "ignored.png", NULL); - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - g_free(filename); - scale2 = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); - g_object_unref(pixbuf); - gdk_pixbuf_composite(scale2, scale, 0, 0, 16, 16, 0, 0, 1, 1, GDK_INTERP_BILINEAR, 192); - g_object_unref(scale2); - } - - return scale; + return image; } static void @@ -3995,7 +3977,7 @@ PurpleConnection *gc; PurplePluginProtocolInfo *prpl_info; GtkListStore *ls; - GdkPixbuf *pixbuf; + const char *stock; GtkTreeIter iter; gboolean is_me = FALSE; gboolean is_buddy; @@ -4017,7 +3999,7 @@ ls = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list))); - pixbuf = get_chat_buddy_status_icon(chat, name, flags); + stock = get_chat_buddy_status_icon(chat, name, flags); if (!strcmp(chat->nick, purple_normalize(conv->account, old_name != NULL ? old_name : name))) is_me = TRUE; @@ -4034,6 +4016,11 @@ "send-name"); g_object_get(tag, "foreground-gdk", &color, NULL); } else { + GtkTextTag *tag; + if ((tag = get_buddy_tag(conv, name, 0, FALSE))) + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL); + if ((tag = get_buddy_tag(conv, name, PURPLE_MESSAGE_NICK, FALSE))) + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_NORMAL, NULL); color = (GdkColor*)get_nick_color(gtkconv, name); } @@ -4047,7 +4034,7 @@ * Inserting in the "wrong" location has no visible ill effects. - F.P. */ -1, /* "row" */ - CHAT_USERS_ICON_COLUMN, pixbuf, + CHAT_USERS_ICON_STOCK_COLUMN, stock, CHAT_USERS_ALIAS_COLUMN, alias, CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, CHAT_USERS_NAME_COLUMN, name, @@ -4058,7 +4045,7 @@ #else gtk_list_store_append(ls, &iter); gtk_list_store_set(ls, &iter, - CHAT_USERS_ICON_COLUMN, pixbuf, + CHAT_USERS_ICON_STOCK_COLUMN, stock, CHAT_USERS_ALIAS_COLUMN, alias, CHAT_USERS_ALIAS_KEY_COLUMN, alias_key, CHAT_USERS_NAME_COLUMN, name, @@ -4068,8 +4055,6 @@ -1); #endif - if (pixbuf) - g_object_unref(pixbuf); if (is_me && color) gdk_color_free(color); g_free(alias_key); @@ -4716,16 +4701,18 @@ ls = gtk_list_store_new(CHAT_USERS_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, - GDK_TYPE_COLOR, G_TYPE_INT); + GDK_TYPE_COLOR, G_TYPE_INT, G_TYPE_STRING); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(ls), CHAT_USERS_ALIAS_KEY_COLUMN, sort_chat_users, NULL, NULL); list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls)); rend = gtk_cell_renderer_pixbuf_new(); - + g_object_set(G_OBJECT(rend), + "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), + NULL); col = gtk_tree_view_column_new_with_attributes(NULL, rend, - "pixbuf", CHAT_USERS_ICON_COLUMN, NULL); + "stock-id", CHAT_USERS_ICON_STOCK_COLUMN, NULL); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column(GTK_TREE_VIEW(list), col); ul_width = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/chat/userlist_width"); @@ -4752,7 +4739,7 @@ "foreground-set", TRUE, "weight-set", TRUE, NULL); - g_object_set(G_OBJECT(rend), "editable", TRUE, NULL); + g_object_set(G_OBJECT(rend), "editable", TRUE, NULL); col = gtk_tree_view_column_new_with_attributes(NULL, rend, "text", CHAT_USERS_ALIAS_COLUMN, @@ -4843,7 +4830,7 @@ pidgin_conv_create_tooltip, NULL); gtkconv->infopane = gtk_cell_view_new(); - gtkconv->infopane_model = gtk_list_store_new(CONV_NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF); + gtkconv->infopane_model = gtk_list_store_new(CONV_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, GDK_TYPE_PIXBUF); gtk_cell_view_set_model(GTK_CELL_VIEW(gtkconv->infopane), GTK_TREE_MODEL(gtkconv->infopane_model)); g_object_unref(gtkconv->infopane_model); @@ -4866,8 +4853,10 @@ rend = gtk_cell_renderer_pixbuf_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, FALSE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_ICON_COLUMN, NULL); - g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "stock-id", CONV_ICON_COLUMN, NULL); + g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, + "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), + NULL); rend = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkconv->infopane), rend, TRUE); @@ -4981,11 +4970,17 @@ PurpleConversation *conv = gtkconv->active_conv; PidginWindow *win = gtkconv->win; PurpleConversation *c; + PurpleAccount *convaccount = purple_conversation_get_account(conv); + PurpleConnection *gc = purple_account_get_connection(convaccount); + PurplePluginProtocolInfo *prpl_info = gc ? PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl) : NULL; + if (sd->target == gdk_atom_intern("PURPLE_BLIST_NODE", FALSE)) { PurpleBlistNode *n = NULL; PurpleBuddy *b; PidginConversation *gtkconv = NULL; + PurpleAccount *buddyaccount; + const char *buddyname; n = *(PurpleBlistNode **)sd->data; @@ -4996,32 +4991,44 @@ else return; + buddyaccount = purple_buddy_get_account(b); + buddyname = purple_buddy_get_name(b); /* - * If we already have an open conversation with this buddy, then - * just move the conv to this window. Otherwise, create a new - * conv and add it to this window. + * If a buddy is dragged to a chat window of the same protocol, + * invite him to the chat. */ - c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, b->name, b->account); - if (c != NULL) { - PidginWindow *oldwin; - gtkconv = PIDGIN_CONVERSATION(c); - oldwin = gtkconv->win; - if (oldwin != win) { - pidgin_conv_window_remove_gtkconv(oldwin, gtkconv); - pidgin_conv_window_add_gtkconv(win, gtkconv); - } + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT && + prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_invite) && + strcmp(purple_account_get_protocol_id(convaccount), + purple_account_get_protocol_id(buddyaccount)) == 0) { + purple_conv_chat_invite_user(PURPLE_CONV_CHAT(conv), buddyname, NULL, TRUE); } else { - c = purple_conversation_new(PURPLE_CONV_TYPE_IM, b->account, b->name); - gtkconv = PIDGIN_CONVERSATION(c); - if (gtkconv->win != win) - { - pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); - pidgin_conv_window_add_gtkconv(win, gtkconv); + /* + * If we already have an open conversation with this buddy, then + * just move the conv to this window. Otherwise, create a new + * conv and add it to this window. + */ + c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, buddyname, buddyaccount); + if (c != NULL) { + PidginWindow *oldwin; + gtkconv = PIDGIN_CONVERSATION(c); + oldwin = gtkconv->win; + if (oldwin != win) { + pidgin_conv_window_remove_gtkconv(oldwin, gtkconv); + pidgin_conv_window_add_gtkconv(win, gtkconv); + } + } else { + c = purple_conversation_new(PURPLE_CONV_TYPE_IM, buddyaccount, buddyname); + gtkconv = PIDGIN_CONVERSATION(c); + if (gtkconv->win != win) { + pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); + pidgin_conv_window_add_gtkconv(win, gtkconv); + } } - } - - /* Make this conversation the active conversation */ - pidgin_conv_window_switch_gtkconv(win, gtkconv); + + /* Make this conversation the active conversation */ + pidgin_conv_window_switch_gtkconv(win, gtkconv); + } gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); } @@ -5040,15 +5047,22 @@ purple_notify_error(win, NULL, _("You are not currently signed on with an account that " "can add that buddy."), NULL); - } - else - { - c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username); - gtkconv = PIDGIN_CONVERSATION(c); - if (gtkconv->win != win) - { - pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); - pidgin_conv_window_add_gtkconv(win, gtkconv); + } else { + /* + * If a buddy is dragged to a chat window of the same protocol, + * invite him to the chat. + */ + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT && + prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_invite) && + strcmp(purple_account_get_protocol_id(convaccount), protocol) == 0) { + purple_conv_chat_invite_user(PURPLE_CONV_CHAT(conv), username, NULL, TRUE); + } else { + c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, username); + gtkconv = PIDGIN_CONVERSATION(c); + if (gtkconv->win != win) { + pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv); + pidgin_conv_window_add_gtkconv(win, gtkconv); + } } } } @@ -5060,7 +5074,7 @@ } else if (sd->target == gdk_atom_intern("text/uri-list", FALSE)) { if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) - pidgin_dnd_file_manage(sd, purple_conversation_get_account(conv), purple_conversation_get_name(conv)); + pidgin_dnd_file_manage(sd, convaccount, purple_conversation_get_name(conv)); gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); } else @@ -6038,6 +6052,7 @@ PurpleConvChatBuddy *cbuddy; GtkTreeIter iter; GtkTreeModel *model; + GtkTextTag *tag; int f = 1; chat = PURPLE_CONV_CHAT(conv); @@ -6065,6 +6080,11 @@ g_free(val); } + if ((tag = get_buddy_tag(conv, old_name, 0, FALSE))) + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL); + if ((tag = get_buddy_tag(conv, old_name, PURPLE_MESSAGE_NICK, FALSE))) + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL); + if (!purple_conv_chat_find_user(chat, old_name)) return; @@ -6087,6 +6107,7 @@ char tmp[BUF_LONG]; int num_users; gboolean f; + GtkTextTag *tag; chat = PURPLE_CONV_CHAT(conv); gtkconv = PIDGIN_CONVERSATION(conv); @@ -6119,6 +6140,11 @@ g_free(val); } while (f); + + if ((tag = get_buddy_tag(conv, l->data, 0, FALSE))) + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL); + if ((tag = get_buddy_tag(conv, l->data, PURPLE_MESSAGE_NICK, FALSE))) + g_object_set(G_OBJECT(tag), "style", PANGO_STYLE_ITALIC, NULL); } g_snprintf(tmp, sizeof(tmp), @@ -6801,7 +6827,8 @@ wrote_msg_update_unseen_cb(PurpleAccount *account, const char *who, const char *message, PurpleConversation *conv, PurpleMessageFlags flags, gpointer null) { - if (conv == NULL || PIDGIN_IS_PIDGIN_CONVERSATION(conv)) + PidginConversation *gtkconv = conv ? PIDGIN_CONVERSATION(conv) : NULL; + if (conv == NULL || (gtkconv && gtkconv->win != hidden_convwin)) return; if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) { PidginUnseenState unseen = PIDGIN_UNSEEN_NONE; @@ -7514,7 +7541,7 @@ } /* In case a conversation is started after the buddy has signed-on/off */ - g_timeout_add(11000, (GSourceFunc)update_buddy_status_timeout, buddy); + purple_timeout_add_seconds(11, (GSourceFunc)update_buddy_status_timeout, buddy); } static void @@ -9391,6 +9418,12 @@ /* Status icon. */ gtkconv->icon = gtk_image_new(); gtkconv->menu_icon = gtk_image_new(); + g_object_set(G_OBJECT(gtkconv->icon), + "icon-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC), + NULL); + g_object_set(G_OBJECT(gtkconv->menu_icon), + "icon-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC), + NULL); gtk_widget_show(gtkconv->icon); update_tab_icon(conv);
--- a/pidgin/gtkconv.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkconv.h Tue Apr 28 19:08:06 2009 +0000 @@ -51,6 +51,7 @@ CHAT_USERS_FLAGS_COLUMN, CHAT_USERS_COLOR_COLUMN, CHAT_USERS_WEIGHT_COLUMN, + CHAT_USERS_ICON_STOCK_COLUMN, /** @since 2.6.0 */ CHAT_USERS_COLUMNS };
--- a/pidgin/gtkdebug.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkdebug.c Tue Apr 28 19:08:06 2009 +0000 @@ -94,7 +94,7 @@ if(debug_win->timer != 0) { const gchar *text; - g_source_remove(debug_win->timer); + purple_timeout_remove(debug_win->timer); text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression)); purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text); @@ -552,7 +552,7 @@ } if(win->timer == 0) - win->timer = purple_timeout_add(5000, (GSourceFunc)regex_timer_cb, win); + win->timer = purple_timeout_add_seconds(5, (GSourceFunc)regex_timer_cb, win); regex_compile(win); }
--- a/pidgin/gtkdialogs.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkdialogs.c Tue Apr 28 19:08:06 2009 +0000 @@ -73,6 +73,7 @@ /* Order: Alphabetical by Last Name */ static const struct developer developers[] = { {"Daniel 'datallah' Atallah", NULL, NULL}, + {"Paul 'darkrain42' Aurich", NULL, NULL }, {"John 'rekkanoryo' Bailey", N_("bug master"), "rekkanoryo@pidgin.im"}, {"Ethan 'Paco-Paco' Blanton", NULL, NULL}, {"Hylke Bons", N_("artist"), "h.bons@student.rug.nl"}, @@ -90,6 +91,7 @@ {"Bartosz Oler", NULL, NULL}, {"Etan 'deryni' Reisner", NULL, NULL}, {"Tim 'marv' Ringenbach", NULL, NULL}, + {"Michael 'Maiku' Ruprecht", N_("voice and video"), NULL}, {"Elliott 'QuLogic' Sales de Andrade", NULL, NULL}, {"Luke 'LSchiere' Schierer", N_("support"), "lschiere@users.sf.net"}, {"Evan Schoenberg", NULL, NULL}, @@ -101,7 +103,6 @@ /* Order: Alphabetical by Last Name */ static const struct developer patch_writers[] = { - {"Paul 'darkrain42' Aurich", NULL, NULL }, {"Marcus 'malu' Lundblad", NULL, NULL}, {"Dennis 'EvilDennisR' Ristuccia", N_("Senior Contributor/QA"), NULL}, {"Peter 'Fmoo' Ruibal", NULL, NULL}, @@ -427,7 +428,7 @@ #endif gtk_widget_destroy(logo); logo = gtk_image_new_from_pixbuf(pixbuf); - gdk_pixbuf_unref(pixbuf); + g_object_unref(G_OBJECT(pixbuf)); /* Insert the logo */ obj = gtk_widget_get_accessible(logo); tmp = g_strconcat(PIDGIN_NAME, " " DISPLAY_VERSION, NULL);
--- a/pidgin/gtkdnd-hints.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkdnd-hints.h Tue Apr 28 19:08:06 2009 +0000 @@ -25,7 +25,7 @@ #define _PIDGIN_DND_HINTS_H_ #include <glib.h> -#include <gtk/gtkwidget.h> +#include <gtk/gtk.h> /** * Conversation drag-and-drop arrow types.
--- a/pidgin/gtkdocklet.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkdocklet.c Tue Apr 28 19:08:06 2009 +0000 @@ -482,7 +482,7 @@ } static GtkWidget * -new_menu_item_with_status_icon(GtkWidget *menu, const char *str, PurpleStatusPrimitive primitive, GtkSignalFunc sf, gpointer data, guint accel_key, guint accel_mods, char *mod) +new_menu_item_with_status_icon(GtkWidget *menu, const char *str, PurpleStatusPrimitive primitive, GCallback cb, gpointer data, guint accel_key, guint accel_mods, char *mod) { GtkWidget *menuitem; GdkPixbuf *pixbuf; @@ -493,8 +493,8 @@ if (menu) gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - if (sf) - g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); + if (cb) + g_signal_connect(G_OBJECT(menuitem), "activate", cb, data); pixbuf = pidgin_create_status_icon(primitive, menu, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); image = gtk_image_new_from_pixbuf(pixbuf);
--- a/pidgin/gtkicon-theme-loader.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkicon-theme-loader.h Tue Apr 28 19:08:06 2009 +0000 @@ -1,5 +1,5 @@ /** - * @file gtkicon-loader.h Pidgin Icon Theme Loader Class API + * @file gtkicon-theme-loader.h Pidgin Icon Theme Loader Class API */ /* purple
--- a/pidgin/gtkicon-theme.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkicon-theme.h Tue Apr 28 19:08:06 2009 +0000 @@ -1,5 +1,5 @@ /** - * @file icon-theme.h Pidgin Icon Theme Class API + * @file gtkicon-theme.h Pidgin Icon Theme Class API */ /* pidgin @@ -72,6 +72,7 @@ /** * Returns a copy of the filename for the icon event or NULL if it is not set * + * @param theme the theme * @param event the pidgin icon event to look up * * @returns the filename of the icon event @@ -82,6 +83,7 @@ /** * Sets the filename for a given icon id, setting the icon to NULL will remove the icon from the theme * + * @param theme the theme * @param icon_id a string representing what the icon is to be used for * @param filename the name of the file to be used for the given id */
--- a/pidgin/gtkimhtml.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkimhtml.c Tue Apr 28 19:08:06 2009 +0000 @@ -45,7 +45,7 @@ #include "gtksourceundomanager.h" #include "gtksourceview-marshal.h" #include <gtk/gtk.h> -#include <glib/gerror.h> +#include <glib.h> #include <gdk/gdkkeysyms.h> #include <string.h> #include <ctype.h> @@ -782,7 +782,7 @@ gc, TRUE, visible_rect.x, visible_rect.y, visible_rect.width, visible_rect.height); - gdk_gc_unref(gc); + g_object_unref(G_OBJECT(gc)); if (GTK_WIDGET_CLASS (parent_class)->expose_event) return (* GTK_WIDGET_CLASS (parent_class)->expose_event) @@ -873,7 +873,7 @@ !gtk_text_iter_begins_tag(&cur, NULL)); } - gdk_gc_unref(gc); + g_object_unref(G_OBJECT(gc)); if (GTK_WIDGET_CLASS (parent_class)->expose_event) return (* GTK_WIDGET_CLASS (parent_class)->expose_event) @@ -1384,7 +1384,7 @@ gtk_widget_destroy(imhtml->tip_window); } if(imhtml->tip_timer) - gtk_timeout_remove(imhtml->tip_timer); + g_source_remove(imhtml->tip_timer); for(scalables = imhtml->scalables; scalables; scalables = scalables->next) { struct scalable_data *sd = scalables->data; @@ -1451,7 +1451,7 @@ GObjectClass *gobject_class; object_class = (GtkObjectClass*) klass; gobject_class = (GObjectClass*) klass; - parent_class = gtk_type_class(GTK_TYPE_TEXT_VIEW); + parent_class = g_type_class_ref(GTK_TYPE_TEXT_VIEW); signals[URL_CLICKED] = g_signal_new("url_clicked", G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_FIRST, @@ -3320,7 +3320,8 @@ pos++; } else if ((pos == 0 || wpos == 0 || isspace(*(c - 1))) && (len_protocol = gtk_imhtml_is_protocol(c)) > 0 && - c[len_protocol] && !isspace(c[len_protocol])) { + c[len_protocol] && !isspace(c[len_protocol]) && + (c[len_protocol] != '<' || !gtk_imhtml_is_tag(c + 1, NULL, NULL, NULL))) { br = FALSE; if (wpos > 0) { gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
--- a/pidgin/gtkimhtml.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkimhtml.h Tue Apr 28 19:08:06 2009 +0000 @@ -26,9 +26,7 @@ #define _PIDGINIMHTML_H_ #include <gdk/gdk.h> -#include <gtk/gtktextview.h> -#include <gtk/gtktooltips.h> -#include <gtk/gtkimage.h> +#include <gtk/gtk.h> #include "gtksourceundomanager.h" #include "connection.h" @@ -42,13 +40,13 @@ **************************************************************************/ /*@{*/ -#define GTK_TYPE_IMHTML (gtk_imhtml_get_type ()) -#define GTK_IMHTML(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_IMHTML, GtkIMHtml)) -#define GTK_IMHTML_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IMHTML, GtkIMHtmlClass)) -#define GTK_IS_IMHTML(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_IMHTML)) -#define GTK_IS_IMHTML_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IMHTML)) +#define GTK_TYPE_IMHTML (gtk_imhtml_get_type()) +#define GTK_IMHTML(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_IMHTML, GtkIMHtml)) +#define GTK_IMHTML_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_IMHTML, GtkIMHtmlClass)) +#define GTK_IS_IMHTML(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_IMHTML)) +#define GTK_IS_IMHTML_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_IMHTML)) #define GTK_IMHTML_SCALABLE(obj) ((GtkIMHtmlScalable *)obj) -#define GTK_IMHTML_ANIMATION(obj) ((GtkIMHtmlAnimation *)obj) +#define GTK_IMHTML_ANIMATION(obj) ((GtkIMHtmlAnimation *)obj) typedef struct _GtkIMHtml GtkIMHtml; typedef struct _GtkIMHtmlClass GtkIMHtmlClass;
--- a/pidgin/gtkimhtmltoolbar.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Tue Apr 28 19:08:06 2009 +0000 @@ -1198,7 +1198,7 @@ GObjectClass *gobject_class; object_class = (GtkObjectClass*) class; gobject_class = (GObjectClass*) class; - parent_class = gtk_type_class(GTK_TYPE_HBOX); + parent_class = g_type_class_ref(GTK_TYPE_HBOX); gobject_class->finalize = gtk_imhtmltoolbar_finalize; purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/toolbar");
--- a/pidgin/gtkimhtmltoolbar.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkimhtmltoolbar.h Tue Apr 28 19:08:06 2009 +0000 @@ -23,7 +23,7 @@ #ifndef _PIDGINIMHTMLTOOLBAR_H_ #define _PIDGINIMHTMLTOOLBAR_H_ -#include <gtk/gtkvbox.h> +#include <gtk/gtk.h> #include "gtkimhtml.h" #ifdef __cplusplus @@ -32,11 +32,11 @@ #define DEFAULT_FONT_FACE "Helvetica 12" -#define GTK_TYPE_IMHTMLTOOLBAR (gtk_imhtmltoolbar_get_type ()) -#define GTK_IMHTMLTOOLBAR(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbar)) -#define GTK_IMHTMLTOOLBAR_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbarClass)) -#define GTK_IS_IMHTMLTOOLBAR(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_IMHTMLTOOLBAR)) -#define GTK_IS_IMHTMLTOOLBAR_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IMHTMLTOOLBAR)) +#define GTK_TYPE_IMHTMLTOOLBAR (gtk_imhtmltoolbar_get_type()) +#define GTK_IMHTMLTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbar)) +#define GTK_IMHTMLTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbarClass)) +#define GTK_IS_IMHTMLTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_IMHTMLTOOLBAR)) +#define GTK_IS_IMHTMLTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_IMHTMLTOOLBAR)) typedef struct _GtkIMHtmlToolbar GtkIMHtmlToolbar; typedef struct _GtkIMHtmlToolbarClass GtkIMHtmlToolbarClass;
--- a/pidgin/gtkmain.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkmain.c Tue Apr 28 19:08:06 2009 +0000 @@ -787,7 +787,7 @@ DBusMessage *message = dbus_message_new_method_call(DBUS_SERVICE_PURPLE, DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "PurpleBlistSetVisible"); gboolean tr = TRUE; - dbus_message_append_args(message, DBUS_TYPE_UINT32, &tr, DBUS_TYPE_INVALID); + dbus_message_append_args(message, DBUS_TYPE_INT32, &tr, DBUS_TYPE_INVALID); dbus_connection_send_with_reply_and_block(conn, message, -1, NULL); dbus_message_unref(message); #endif
--- a/pidgin/gtkmedia.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkmedia.c Tue Apr 28 19:08:06 2009 +0000 @@ -97,7 +97,6 @@ GtkWidget *recv_widget; GtkWidget *local_video; GtkWidget *remote_video; - PurpleConnection *pc; guint timeout_id; PurpleMediaSessionType request_type; @@ -432,7 +431,7 @@ { PurpleConversation *conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname, - purple_connection_get_account(gtkmedia->priv->pc)); + purple_media_get_account(gtkmedia->priv->media)); if (conv != NULL) purple_conversation_write(conv, NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); @@ -476,7 +475,7 @@ { PurpleConversation *conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname, - purple_connection_get_account(gtkmedia->priv->pc)); + purple_media_get_account(gtkmedia->priv->media)); if (conv != NULL) purple_conversation_write(conv, NULL, error, PURPLE_MESSAGE_ERROR, time(NULL)); @@ -495,25 +494,6 @@ gtk_widget_show(GTK_WIDGET(gtkmedia)); } -static gboolean -plug_delete_event_cb(GtkWidget *widget, gpointer data) -{ - return TRUE; -} - -static gboolean -plug_removed_cb(GtkWidget *widget, gpointer data) -{ - return TRUE; -} - -static void -socket_realize_cb(GtkWidget *widget, gpointer data) -{ - gtk_socket_add_id(GTK_SOCKET(widget), - gtk_plug_get_id(GTK_PLUG(data))); -} - static void pidgin_media_accept_cb(PurpleMedia *media, int index) { @@ -531,15 +511,14 @@ static gboolean pidgin_request_timeout_cb(PidginMedia *gtkmedia) { - PurpleConnection *pc; + PurpleAccount *account; PurpleBuddy *buddy; const gchar *alias; PurpleMediaSessionType type; gchar *message = NULL; - pc = purple_media_get_connection(gtkmedia->priv->media); - buddy = purple_find_buddy(purple_connection_get_account(pc), - gtkmedia->priv->screenname); + account = purple_media_get_account(gtkmedia->priv->media); + buddy = purple_find_buddy(account, gtkmedia->priv->screenname); alias = buddy ? purple_buddy_get_contact_alias(buddy) : gtkmedia->priv->screenname; type = gtkmedia->priv->request_type; @@ -560,7 +539,7 @@ purple_request_accept_cancel(gtkmedia, "Media invitation", message, NULL, PURPLE_DEFAULT_ACTION_NONE, - (void*)pc, gtkmedia->priv->screenname, NULL, + (void*)account, gtkmedia->priv->screenname, NULL, gtkmedia->priv->media, pidgin_media_accept_cb, pidgin_media_reject_cb); @@ -570,21 +549,97 @@ } static void +#if GTK_CHECK_VERSION(2,12,0) +pidgin_media_input_volume_changed(GtkScaleButton *range, double value, + PurpleMedia *media) +{ + double val = (double)value * 100.0; +#else pidgin_media_input_volume_changed(GtkRange *range, PurpleMedia *media) { double val = (double)gtk_range_get_value(GTK_RANGE(range)); +#endif purple_prefs_set_int("/pidgin/media/audio/volume/input", val); - val /= 10.0; - purple_media_set_input_volume(media, NULL, val); + purple_media_set_input_volume(media, NULL, val / 10.0); } static void +#if GTK_CHECK_VERSION(2,12,0) +pidgin_media_output_volume_changed(GtkScaleButton *range, double value, + PurpleMedia *media) +{ + double val = (double)value * 100.0; +#else pidgin_media_output_volume_changed(GtkRange *range, PurpleMedia *media) { double val = (double)gtk_range_get_value(GTK_RANGE(range)); +#endif purple_prefs_set_int("/pidgin/media/audio/volume/output", val); - val /= 10.0; - purple_media_set_output_volume(media, NULL, NULL, val); + purple_media_set_output_volume(media, NULL, NULL, val / 10.0); +} + +static GtkWidget * +pidgin_media_add_audio_widget(PidginMedia *gtkmedia, + PurpleMediaSessionType type) +{ + GtkWidget *volume_widget, *progress_parent, *volume, *progress; + double value; + + if (type & PURPLE_MEDIA_SEND_AUDIO) { + value = purple_prefs_get_int( + "/pidgin/media/audio/volume/input"); + } else if (type & PURPLE_MEDIA_RECV_AUDIO) { + value = purple_prefs_get_int( + "/pidgin/media/audio/volume/output"); + } else + g_return_val_if_reached(NULL); + +#if GTK_CHECK_VERSION(2,12,0) + /* Setup widget structure */ + volume_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); + progress_parent = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(volume_widget), + progress_parent, TRUE, TRUE, 0); + + /* Volume button */ + volume = gtk_volume_button_new(); + gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), value/100.0); + gtk_box_pack_end(GTK_BOX(volume_widget), + volume, FALSE, FALSE, 0); +#else + /* Setup widget structure */ + volume_widget = gtk_vbox_new(FALSE, 0); + progress_parent = volume_widget; + + /* Volume slider */ + volume = gtk_hscale_new_with_range(0.0, 100.0, 5.0); + gtk_range_set_increments(GTK_RANGE(volume), 5.0, 25.0); + gtk_range_set_value(GTK_RANGE(volume), value); + gtk_scale_set_draw_value(GTK_SCALE(volume), FALSE); + gtk_box_pack_end(GTK_BOX(volume_widget), + volume, TRUE, FALSE, 0); +#endif + + /* Volume level indicator */ + progress = gtk_progress_bar_new(); + gtk_widget_set_size_request(progress, 250, 10); + gtk_box_pack_end(GTK_BOX(progress_parent), progress, TRUE, FALSE, 0); + + if (type & PURPLE_MEDIA_SEND_AUDIO) { + g_signal_connect (G_OBJECT(volume), "value-changed", + G_CALLBACK(pidgin_media_input_volume_changed), + gtkmedia->priv->media); + gtkmedia->priv->send_progress = progress; + } else if (type & PURPLE_MEDIA_RECV_AUDIO) { + g_signal_connect (G_OBJECT(volume), "value-changed", + G_CALLBACK(pidgin_media_output_volume_changed), + gtkmedia->priv->media); + gtkmedia->priv->recv_progress = progress; + } + + gtk_widget_show_all(volume_widget); + + return volume_widget; } static void @@ -619,27 +674,12 @@ PidginMediaRealizeData *data; GtkWidget *aspect; GtkWidget *remote_video; - GtkWidget *plug; - GtkWidget *socket; GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(recv_widget), aspect, TRUE, TRUE, 0); - plug = gtk_plug_new(0); - g_signal_connect(G_OBJECT(plug), "delete-event", - G_CALLBACK(plug_delete_event_cb), plug); - gtk_widget_show(plug); - - socket = gtk_socket_new(); - g_signal_connect(G_OBJECT(socket), "realize", - G_CALLBACK(socket_realize_cb), plug); - g_signal_connect(G_OBJECT(socket), "plug-removed", - G_CALLBACK(plug_removed_cb), NULL); - gtk_container_add(GTK_CONTAINER(aspect), socket); - gtk_widget_show(socket); - data = g_new0(PidginMediaRealizeData, 1); data->gtkmedia = gtkmedia; data->session_id = g_strdup(sid); @@ -649,7 +689,7 @@ gtk_widget_modify_bg(remote_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(remote_video), "realize", G_CALLBACK(realize_cb), data); - gtk_container_add(GTK_CONTAINER(plug), remote_video); + gtk_container_add(GTK_CONTAINER(aspect), remote_video); gtk_widget_set_size_request (GTK_WIDGET(remote_video), 320, 240); gtk_widget_show(remote_video); gtk_widget_show(aspect); @@ -660,27 +700,12 @@ PidginMediaRealizeData *data; GtkWidget *aspect; GtkWidget *local_video; - GtkWidget *plug; - GtkWidget *socket; GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(send_widget), aspect, TRUE, TRUE, 0); - plug = gtk_plug_new(0); - g_signal_connect(G_OBJECT(plug), "delete-event", - G_CALLBACK(plug_delete_event_cb), plug); - gtk_widget_show(plug); - - socket = gtk_socket_new(); - g_signal_connect(G_OBJECT(socket), "realize", - G_CALLBACK(socket_realize_cb), plug); - g_signal_connect(G_OBJECT(socket), "plug-removed", - G_CALLBACK(plug_removed_cb), NULL); - gtk_container_add(GTK_CONTAINER(aspect), socket); - gtk_widget_show(socket); - data = g_new0(PidginMediaRealizeData, 1); data->gtkmedia = gtkmedia; data->session_id = g_strdup(sid); @@ -690,7 +715,7 @@ gtk_widget_modify_bg(local_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(local_video), "realize", G_CALLBACK(realize_cb), data); - gtk_container_add(GTK_CONTAINER(plug), local_video); + gtk_container_add(GTK_CONTAINER(aspect), local_video); gtk_widget_set_size_request (GTK_WIDGET(local_video), 160, 120); gtk_widget_show(local_video); @@ -700,28 +725,13 @@ } if (type & PURPLE_MEDIA_RECV_AUDIO) { - GtkWidget *volume = gtk_hscale_new_with_range(0.0, 100.0, 5.0); - gtk_range_set_increments(GTK_RANGE(volume), 5.0, 25.0); - gtk_range_set_value(GTK_RANGE(volume), - purple_prefs_get_int( - "/pidgin/media/audio/volume/output")); - gtk_scale_set_draw_value(GTK_SCALE(volume), FALSE); - g_signal_connect (G_OBJECT(volume), "value-changed", - G_CALLBACK(pidgin_media_output_volume_changed), - media); gtk_box_pack_end(GTK_BOX(recv_widget), - volume, FALSE, FALSE, 0); - gtk_widget_show(volume); - - gtkmedia->priv->recv_progress = gtk_progress_bar_new(); - gtk_widget_set_size_request(gtkmedia->priv->recv_progress, 320, 10); - gtk_box_pack_end(GTK_BOX(recv_widget), - gtkmedia->priv->recv_progress, FALSE, FALSE, 0); - gtk_widget_show(gtkmedia->priv->recv_progress); + pidgin_media_add_audio_widget(gtkmedia, + PURPLE_MEDIA_RECV_AUDIO), FALSE, FALSE, 0); } if (type & PURPLE_MEDIA_SEND_AUDIO) { GstElement *media_src; - GtkWidget *hbox, *volume; + GtkWidget *hbox; hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_end(GTK_BOX(send_widget), hbox, FALSE, FALSE, 0); @@ -735,28 +745,13 @@ gtk_widget_show(gtkmedia->priv->mute); gtk_widget_show(GTK_WIDGET(hbox)); - volume = gtk_hscale_new_with_range(0.0, 100.0, 5.0); - gtk_range_set_increments(GTK_RANGE(volume), 5.0, 25.0); - gtk_range_set_value(GTK_RANGE(volume), - purple_prefs_get_int( - "/pidgin/media/audio/volume/input")); - gtk_scale_set_draw_value(GTK_SCALE(volume), FALSE); - g_signal_connect (G_OBJECT(volume), "value-changed", - G_CALLBACK (pidgin_media_input_volume_changed), - media); - gtk_box_pack_end(GTK_BOX(send_widget), - volume, FALSE, FALSE, 0); - gtk_widget_show(volume); - media_src = purple_media_get_src(media, sid); gtkmedia->priv->send_level = gst_bin_get_by_name( GST_BIN(media_src), "sendlevel"); - gtkmedia->priv->send_progress = gtk_progress_bar_new(); - gtk_widget_set_size_request(gtkmedia->priv->send_progress, 320, 10); gtk_box_pack_end(GTK_BOX(send_widget), - gtkmedia->priv->send_progress, FALSE, FALSE, 0); - gtk_widget_show(gtkmedia->priv->send_progress); + pidgin_media_add_audio_widget(gtkmedia, + PURPLE_MEDIA_SEND_AUDIO), FALSE, FALSE, 0); gtk_widget_show(gtkmedia->priv->mute); } @@ -932,15 +927,13 @@ static gboolean pidgin_media_new_cb(PurpleMediaManager *manager, PurpleMedia *media, - PurpleConnection *pc, gchar *screenname, gpointer nul) + PurpleAccount *account, gchar *screenname, gpointer nul) { PidginMedia *gtkmedia = PIDGIN_MEDIA( pidgin_media_new(media, screenname)); - PurpleBuddy *buddy = purple_find_buddy( - purple_connection_get_account(pc), screenname); + PurpleBuddy *buddy = purple_find_buddy(account, screenname); const gchar *alias = buddy ? purple_buddy_get_contact_alias(buddy) : screenname; - gtkmedia->priv->pc = pc; gtk_window_set_title(GTK_WINDOW(gtkmedia), alias); if (purple_media_is_initiator(media, NULL, NULL) == TRUE)
--- a/pidgin/gtkmedia.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkmedia.h Tue Apr 28 19:08:06 2009 +0000 @@ -1,8 +1,9 @@ /** - * @file media.h Account API - * @ingroup core - * - * Pidgin + * @file gtkmedia.h Pidgin Media 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
--- a/pidgin/gtkmenutray.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkmenutray.c Tue Apr 28 19:08:06 2009 +0000 @@ -21,9 +21,7 @@ #include "gtkmenutray.h" -#include <gtk/gtkeventbox.h> -#include <gtk/gtkiconfactory.h> -#include <gtk/gtkversion.h> +#include <gtk/gtk.h> /****************************************************************************** * Enums
--- a/pidgin/gtkmenutray.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkmenutray.h Tue Apr 28 19:08:06 2009 +0000 @@ -24,16 +24,14 @@ #ifndef PIDGIN_MENU_TRAY_H #define PIDGIN_MENU_TRAY_H -#include <gtk/gtkhbox.h> -#include <gtk/gtkmenuitem.h> -#include <gtk/gtktooltips.h> +#include <gtk/gtk.h> -#define PIDGIN_TYPE_MENU_TRAY (pidgin_menu_tray_get_gtype()) -#define PIDGIN_MENU_TRAY(obj) (GTK_CHECK_CAST((obj), PIDGIN_TYPE_MENU_TRAY, PidginMenuTray)) -#define PIDGIN_MENU_TRAY_CLASS(klass) (GTK_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MENU_TRAY, PidginMenuTrayClass)) -#define PIDGIN_IS_MENU_TRAY(obj) (GTK_CHECK_TYPE((obj), PIDGIN_TYPE_MENU_TRAY)) -#define PIDGIN_IS_MENU_TRAY_CLASS(klass) (GTK_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_MENU_TRAY)) -#define PIDGIN_MENU_TRAY_GET_CLASS(obj) (GTK_CHECK_GET_CLASS((obj), PIDGIN_TYPE_MENU_TRAY, PidginMenuTrayClass)) +#define PIDGIN_TYPE_MENU_TRAY (pidgin_menu_tray_get_gtype()) +#define PIDGIN_MENU_TRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MENU_TRAY, PidginMenuTray)) +#define PIDGIN_MENU_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MENU_TRAY, PidginMenuTrayClass)) +#define PIDGIN_IS_MENU_TRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_MENU_TRAY)) +#define PIDGIN_IS_MENU_TRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_MENU_TRAY)) +#define PIDGIN_MENU_TRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_MENU_TRAY, PidginMenuTrayClass)) typedef struct _PidginMenuTray PidginMenuTray; typedef struct _PidginMenuTrayClass PidginMenuTrayClass;
--- a/pidgin/gtknotify.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtknotify.h Tue Apr 28 19:08:06 2009 +0000 @@ -32,8 +32,10 @@ /** * Adds a buddy pounce to the buddy pounce dialog * + * @param account The account + * @param pounce The pounce * @param alias The buddy alias - * @param event Event description + * @param event Event description * @param message Pounce message * @param date Pounce date */
--- a/pidgin/gtkplugin.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkplugin.c Tue Apr 28 19:08:06 2009 +0000 @@ -135,7 +135,13 @@ gtk_list_store_append (ls, &iter); - name = g_markup_escape_text(plug->info->name ? _(plug->info->name) : g_basename(plug->path), -1); + if (plug->info->name) { + name = g_markup_escape_text(_(plug->info->name), -1); + } else { + char *tmp = g_path_get_basename(plug->path); + name = g_markup_escape_text(tmp, -1); + g_free(tmp); + } version = g_markup_escape_text(purple_plugin_get_version(plug), -1); summary = g_markup_escape_text(purple_plugin_get_summary(plug), -1);
--- a/pidgin/gtkprefs.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkprefs.c Tue Apr 28 19:08:06 2009 +0000 @@ -638,7 +638,7 @@ gtk_list_store_set(prefs_sound_themes, &iter, 0, pixbuf, 2, purple_theme_get_name(theme), -1); if (pixbuf != NULL) - gdk_pixbuf_unref(pixbuf); + g_object_unref(G_OBJECT(pixbuf)); } else if (PIDGIN_IS_BLIST_THEME(theme) || PIDGIN_IS_STATUS_ICON_THEME(theme)){ GtkListStore *store; @@ -665,7 +665,7 @@ g_free(markup); if (pixbuf != NULL) - gdk_pixbuf_unref(pixbuf); + g_object_unref(G_OBJECT(pixbuf)); } } @@ -702,7 +702,7 @@ gtk_list_store_set(prefs_status_icon_themes, &iter, 0, pixbuf, 1, "<b>(Default)</b> - None\n<span color='dim grey'>" "The default Pidgin status icon theme</span>", 2, "", -1); - gdk_pixbuf_unref(pixbuf); + g_object_unref(G_OBJECT(pixbuf)); } /* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
--- a/pidgin/gtkprefs.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkprefs.h Tue Apr 28 19:08:06 2009 +0000 @@ -92,6 +92,8 @@ * @return An hbox containing both the label and the entry. Can be used to set * the widgets to sensitive or insensitive based on the value of a * checkbox. + * + * @since 2.6.0 */ GtkWidget *pidgin_prefs_labeled_password(GtkWidget *page, const gchar *title, const char *key, GtkSizeGroup *sg);
--- a/pidgin/gtksavedstatuses.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtksavedstatuses.c Tue Apr 28 19:08:06 2009 +0000 @@ -398,23 +398,7 @@ static const gchar * get_stock_icon_from_primitive(PurpleStatusPrimitive type) { - switch (type) { - case PURPLE_STATUS_AVAILABLE: - return PIDGIN_STOCK_STATUS_AVAILABLE; - case PURPLE_STATUS_AWAY: - return PIDGIN_STOCK_STATUS_AWAY; - case PURPLE_STATUS_EXTENDED_AWAY: - return PIDGIN_STOCK_STATUS_XA; - case PURPLE_STATUS_INVISIBLE: - return PIDGIN_STOCK_STATUS_INVISIBLE; - case PURPLE_STATUS_OFFLINE: - return PIDGIN_STOCK_STATUS_OFFLINE; - case PURPLE_STATUS_UNAVAILABLE: - return PIDGIN_STOCK_STATUS_BUSY; - default: - /* this shouldn't happen */ - return NULL; - } + return pidgin_stock_id_from_status_primitive(type); } static void @@ -1503,16 +1487,19 @@ gtk_size_group_add_widget(sg, label); dialog->model = gtk_list_store_new(SUBSTATUS_NUM_COLUMNS, - GDK_TYPE_PIXBUF, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(dialog->model)); dialog->box = GTK_COMBO_BOX(combo); rend = GTK_CELL_RENDERER(gtk_cell_renderer_pixbuf_new()); + g_object_set(G_OBJECT(rend), + "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), + NULL); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend, - "pixbuf", SUBSTATUS_COLUMN_ICON, NULL); + "stock-id", SUBSTATUS_COLUMN_ICON, NULL); rend = GTK_CELL_RENDERER(gtk_cell_renderer_text_new()); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, TRUE); @@ -1574,8 +1561,8 @@ for (list = purple_account_get_status_types(account); list; list = list->next) { PurpleStatusType *status_type; - GdkPixbuf *pixbuf; const char *id, *name; + PurpleStatusPrimitive prim; status_type = list->data; @@ -1588,17 +1575,15 @@ continue; id = purple_status_type_get_id(status_type); - pixbuf = pidgin_create_status_icon(purple_status_type_get_primitive(status_type), combo, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); + prim = purple_status_type_get_primitive(status_type); name = purple_status_type_get_name(status_type); gtk_list_store_append(dialog->model, &iter); gtk_list_store_set(dialog->model, &iter, - SUBSTATUS_COLUMN_ICON, pixbuf, + SUBSTATUS_COLUMN_ICON, pidgin_stock_id_from_status_primitive(prim), SUBSTATUS_COLUMN_STATUS_ID, id, SUBSTATUS_COLUMN_STATUS_NAME, name, -1); - if (pixbuf != NULL) - g_object_unref(pixbuf); if ((status_id != NULL) && !strcmp(status_id, id)) { gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); @@ -1705,18 +1690,15 @@ { GtkTreeIter iter; gboolean currently_selected = FALSE; - GdkPixbuf *pixbuf = pidgin_create_status_icon(primitive, w, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_PRIMITIVE, - SS_MENU_ICON_COLUMN, pixbuf, + SS_MENU_ICON_COLUMN, pidgin_stock_id_from_status_primitive(primitive), SS_MENU_TEXT_COLUMN, purple_primitive_get_name_from_type(primitive), SS_MENU_DATA_COLUMN, GINT_TO_POINTER(primitive), SS_MENU_EMBLEM_VISIBLE_COLUMN, FALSE, -1); - if (pixbuf != NULL) - g_object_unref(pixbuf); if (purple_savedstatus_is_transient(current_status) && !purple_savedstatus_has_substatuses(current_status) @@ -1730,23 +1712,20 @@ pidgin_status_menu_update_iter(GtkWidget *combobox, GtkListStore *store, GtkTreeIter *iter, PurpleSavedStatus *status) { - GdkPixbuf *pixbuf; + PurpleStatusPrimitive primitive; if (store == NULL) store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox))); - pixbuf = pidgin_create_status_icon(purple_savedstatus_get_type(status), - combobox, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); + primitive = purple_savedstatus_get_type(status); gtk_list_store_set(store, iter, SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS, - SS_MENU_ICON_COLUMN, pixbuf, + SS_MENU_ICON_COLUMN, pidgin_stock_id_from_status_primitive(primitive), SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status), SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)), SS_MENU_EMBLEM_COLUMN, GTK_STOCK_SAVE, SS_MENU_EMBLEM_VISIBLE_COLUMN, TRUE, -1); - if (pixbuf) - g_object_unref(G_OBJECT(pixbuf)); } static gboolean @@ -1828,7 +1807,7 @@ GtkCellRenderer *icon_rend; GtkCellRenderer *emblem_rend; - model = gtk_list_store_new(SS_MENU_NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, + model = gtk_list_store_new(SS_MENU_NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN); combobox = gtk_combo_box_new(); @@ -1875,10 +1854,13 @@ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), icon_rend, FALSE); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), text_rend, TRUE); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), emblem_rend, FALSE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), icon_rend, "pixbuf", SS_MENU_ICON_COLUMN, NULL); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), icon_rend, "stock-id", SS_MENU_ICON_COLUMN, NULL); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), text_rend, "markup", SS_MENU_TEXT_COLUMN, NULL); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), emblem_rend, "stock-id", SS_MENU_EMBLEM_COLUMN, "visible", SS_MENU_EMBLEM_VISIBLE_COLUMN, NULL); + g_object_set(G_OBJECT(icon_rend), + "stock-size", gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), + NULL); gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index); g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(status_menu_cb), callback);
--- a/pidgin/gtksmiley.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtksmiley.c Tue Apr 28 19:08:06 2009 +0000 @@ -74,7 +74,7 @@ gtk_widget_destroy(smiley->parent); g_free(smiley->filename); if (smiley->custom_pixbuf) - gdk_pixbuf_unref(smiley->custom_pixbuf); + g_object_unref(G_OBJECT(smiley->custom_pixbuf)); g_free(smiley); } @@ -344,7 +344,7 @@ 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); + g_object_unref(G_OBJECT(pixbuf)); gtk_widget_grab_focus(s->smile); } @@ -459,8 +459,8 @@ 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; + g_object_unref(G_OBJECT(editor->custom_pixbuf)); + editor->custom_pixbuf = image ? g_object_ref(G_OBJECT(image)) : NULL; if (image) gtk_image_set_from_pixbuf(GTK_IMAGE(editor->smiley_image), image); }
--- a/pidgin/gtksound.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtksound.c Tue Apr 28 19:08:06 2009 +0000 @@ -226,9 +226,9 @@ account_signon_cb(PurpleConnection *gc, gpointer data) { if (mute_login_sounds_timeout != 0) - g_source_remove(mute_login_sounds_timeout); + purple_timeout_remove(mute_login_sounds_timeout); mute_login_sounds = TRUE; - mute_login_sounds_timeout = purple_timeout_add(10000, unmute_login_sounds_cb, NULL); + mute_login_sounds_timeout = purple_timeout_add_seconds(10, unmute_login_sounds_cb, NULL); } const char *
--- a/pidgin/gtksourceiter.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtksourceiter.h Tue Apr 28 19:08:06 2009 +0000 @@ -28,7 +28,7 @@ #ifndef _PIDGINSOURCEITER_H_ #define _PIDGINSOURCEITER_H_ -#include <gtk/gtktextiter.h> +#include <gtk/gtk.h> G_BEGIN_DECLS
--- a/pidgin/gtksourceundomanager.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtksourceundomanager.h Tue Apr 28 19:08:06 2009 +0000 @@ -26,14 +26,14 @@ #ifndef __GTK_SOURCE_UNDO_MANAGER_H__ #define __GTK_SOURCE_UNDO_MANAGER_H__ -#include <gtk/gtktextbuffer.h> +#include <gtk/gtk.h> -#define GTK_SOURCE_TYPE_UNDO_MANAGER (gtk_source_undo_manager_get_type ()) -#define GTK_SOURCE_UNDO_MANAGER(obj) (GTK_CHECK_CAST ((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManager)) -#define GTK_SOURCE_UNDO_MANAGER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass)) -#define GTK_SOURCE_IS_UNDO_MANAGER(obj) (GTK_CHECK_TYPE ((obj), GTK_SOURCE_TYPE_UNDO_MANAGER)) -#define GTK_SOURCE_IS_UNDO_MANAGER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_SOURCE_TYPE_UNDO_MANAGER)) -#define GTK_SOURCE_UNDO_MANAGER_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass)) +#define GTK_SOURCE_TYPE_UNDO_MANAGER (gtk_source_undo_manager_get_type()) +#define GTK_SOURCE_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManager)) +#define GTK_SOURCE_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass)) +#define GTK_SOURCE_IS_UNDO_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_SOURCE_TYPE_UNDO_MANAGER)) +#define GTK_SOURCE_IS_UNDO_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_SOURCE_TYPE_UNDO_MANAGER)) +#define GTK_SOURCE_UNDO_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_SOURCE_TYPE_UNDO_MANAGER, GtkSourceUndoManagerClass)) typedef struct _GtkSourceUndoManager GtkSourceUndoManager;
--- a/pidgin/gtkstatus-icon-theme.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkstatus-icon-theme.h Tue Apr 28 19:08:06 2009 +0000 @@ -1,5 +1,5 @@ /** - * @file status_icon-theme.h Pidgin Icon Theme Class API + * @file gtkstatus-icon-theme.h Pidgin Icon Theme Class API */ /* pidgin
--- a/pidgin/gtkstatusbox.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkstatusbox.c Tue Apr 28 19:08:06 2009 +0000 @@ -67,7 +67,8 @@ # endif #endif -#define TYPING_TIMEOUT 4000 +/* Timeout for typing notifications in seconds */ +#define TYPING_TIMEOUT 4 static void imhtml_changed_cb(GtkTextBuffer *buffer, void *data); static void imhtml_format_changed_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, void *data); @@ -97,6 +98,9 @@ /** A PidginStatusBoxItemType */ TYPE_COLUMN, + /** This is the stock-id for the icon. */ + ICON_STOCK_COLUMN, + /** * This is a GdkPixbuf (the other columns are strings). * This column is visible. @@ -535,12 +539,12 @@ for (i = 0; i < G_N_ELEMENTS(statusbox->connecting_pixbufs); i++) { if (statusbox->connecting_pixbufs[i] != NULL) - gdk_pixbuf_unref(statusbox->connecting_pixbufs[i]); + g_object_unref(G_OBJECT(statusbox->connecting_pixbufs[i])); } for (i = 0; i < G_N_ELEMENTS(statusbox->typing_pixbufs); i++) { if (statusbox->typing_pixbufs[i] != NULL) - gdk_pixbuf_unref(statusbox->typing_pixbufs[i]); + g_object_unref(G_OBJECT(statusbox->typing_pixbufs[i])); } g_object_unref(G_OBJECT(statusbox->store)); @@ -599,28 +603,14 @@ } static GdkPixbuf * -pidgin_status_box_get_pixbuf(PidginStatusBox *status_box, PurpleStatusPrimitive prim) +pidgin_status_box_get_pixbuf(PidginStatusBox *status_box, const char *stock) { GdkPixbuf *pixbuf; GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); - if (prim == PURPLE_STATUS_UNAVAILABLE) - pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), PIDGIN_STOCK_STATUS_BUSY, - icon_size, "PidginStatusBox"); - else if (prim == PURPLE_STATUS_AWAY) - pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), PIDGIN_STOCK_STATUS_AWAY, - icon_size, "PidginStatusBox"); - else if (prim == PURPLE_STATUS_EXTENDED_AWAY) - pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), PIDGIN_STOCK_STATUS_XA, - icon_size, "PidginStatusBox"); - else if (prim == PURPLE_STATUS_INVISIBLE) - pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), PIDGIN_STOCK_STATUS_INVISIBLE, - icon_size, "PidginStatusBox"); - else if (prim == PURPLE_STATUS_OFFLINE) - pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), PIDGIN_STOCK_STATUS_OFFLINE, - icon_size, "PidginStatusBox"); - else - pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), PIDGIN_STOCK_STATUS_AVAILABLE, - icon_size, "PidginStatusBox"); + + pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), stock, + icon_size, "PidginStatusBox"); + return pixbuf; } @@ -637,7 +627,8 @@ char aa_color[8]; PurpleSavedStatus *saved_status; char *primary, *secondary, *text; - GdkPixbuf *pixbuf, *emblem = NULL; + const char *stock = NULL; + GdkPixbuf *pixbuf = NULL, *emblem = NULL; GtkTreePath *path; gboolean account_status = FALSE; PurpleAccount *acct = (status_box->account) ? status_box->account : status_box->token_status_account; @@ -721,13 +712,15 @@ PurpleStatusType *status_type; PurpleStatusPrimitive prim; if (account_status) { - status_type = purple_status_get_type(purple_account_get_active_status(acct)); + status_type = purple_status_get_type(purple_account_get_active_status(acct)); prim = purple_status_type_get_primitive(status_type); } else { - prim = purple_savedstatus_get_type(saved_status); + prim = purple_savedstatus_get_type(saved_status); } - pixbuf = pidgin_status_box_get_pixbuf(status_box, prim); + stock = pidgin_stock_id_from_status_primitive(prim); + if (stock) + pixbuf = pidgin_status_box_get_pixbuf(status_box, stock); } if (status_box->account != NULL) { @@ -749,12 +742,13 @@ * really need to be a list store?) */ gtk_list_store_set(status_box->store, &(status_box->iter), + ICON_STOCK_COLUMN, (gpointer)stock, ICON_COLUMN, pixbuf, TEXT_COLUMN, text, EMBLEM_COLUMN, emblem, EMBLEM_VISIBLE_COLUMN, (emblem != NULL), -1); - if ((status_box->typing == 0) && (!status_box->connecting)) + if (pixbuf && (status_box->typing == 0) && (!status_box->connecting)) g_object_unref(pixbuf); g_free(text); if (emblem) @@ -923,7 +917,6 @@ add_popular_statuses(PidginStatusBox *statusbox) { GList *list, *cur; - GdkPixbuf *pixbuf; list = purple_savedstatuses_get_popular(6); if (list == NULL) @@ -943,9 +936,6 @@ /* Get an appropriate status icon */ prim = purple_savedstatus_get_type(saved); - - pixbuf = pidgin_status_box_get_pixbuf(statusbox, prim); - if (purple_savedstatus_is_transient(saved)) { /* @@ -966,11 +956,9 @@ } pidgin_status_box_add(statusbox, type, - pixbuf, purple_savedstatus_get_title(saved), stripped, + NULL, purple_savedstatus_get_title(saved), stripped, GINT_TO_POINTER(purple_savedstatus_get_creation_time(saved))); g_free(stripped); - if (pixbuf != NULL) - g_object_unref(G_OBJECT(pixbuf)); } g_list_free(list); @@ -1031,7 +1019,6 @@ { /* Per-account */ GList *l; - GdkPixbuf *pixbuf; for (l = purple_account_get_status_types(account); l != NULL; l = l->next) { @@ -1044,22 +1031,17 @@ prim = purple_status_type_get_primitive(status_type); - pixbuf = pidgin_status_box_get_pixbuf(status_box, prim); - pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), - PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf, + PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, NULL, purple_status_type_get_name(status_type), NULL, GINT_TO_POINTER(purple_status_type_get_primitive(status_type))); - if (pixbuf != NULL) - g_object_unref(pixbuf); } } static void pidgin_status_box_regenerate(PidginStatusBox *status_box) { - GdkPixbuf *pixbuf, *pixbuf2, *pixbuf3, *pixbuf4, *pixbuf5; GtkIconSize icon_size; icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); @@ -1074,8 +1056,6 @@ if (status_box->account == NULL) { - pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_STATUS_AVAILABLE, - icon_size, "PidginStatusBox"); /* Do all the currently enabled accounts have the same statuses? * If so, display them instead of our global list. */ @@ -1083,25 +1063,11 @@ add_account_statuses(status_box, status_box->token_status_account); } else { /* Global */ - pixbuf2 = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_STATUS_AWAY, - icon_size, "PidginStatusBox"); - pixbuf3 = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_STATUS_OFFLINE, - icon_size, "PidginStatusBox"); - pixbuf4 = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_STATUS_INVISIBLE, - icon_size, "PidginStatusBox"); - pixbuf5 = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_STATUS_BUSY, - icon_size, "PidginStatusBox"); - - pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf, _("Available"), NULL, GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE)); - pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf2, _("Away"), NULL, GINT_TO_POINTER(PURPLE_STATUS_AWAY)); - pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf5, _("Do not disturb"), NULL, GINT_TO_POINTER(PURPLE_STATUS_UNAVAILABLE)); - pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf4, _("Invisible"), NULL, GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE)); - pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf3, _("Offline"), NULL, GINT_TO_POINTER(PURPLE_STATUS_OFFLINE)); - - if (pixbuf2) g_object_unref(G_OBJECT(pixbuf2)); - if (pixbuf3) g_object_unref(G_OBJECT(pixbuf3)); - if (pixbuf4) g_object_unref(G_OBJECT(pixbuf4)); - if (pixbuf5) g_object_unref(G_OBJECT(pixbuf5)); + pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, NULL, _("Available"), NULL, GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE)); + pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, NULL, _("Away"), NULL, GINT_TO_POINTER(PURPLE_STATUS_AWAY)); + pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, NULL, _("Do not disturb"), NULL, GINT_TO_POINTER(PURPLE_STATUS_UNAVAILABLE)); + pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, NULL, _("Invisible"), NULL, GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE)); + pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, NULL, _("Offline"), NULL, GINT_TO_POINTER(PURPLE_STATUS_OFFLINE)); } add_popular_statuses(status_box); @@ -1109,7 +1075,6 @@ pidgin_status_box_add_separator(PIDGIN_STATUS_BOX(status_box)); pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_CUSTOM, NULL, _("New status..."), NULL, NULL); pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box), PIDGIN_STATUS_BOX_TYPE_SAVED, NULL, _("Saved statuses..."), NULL, NULL); - if (pixbuf) g_object_unref(G_OBJECT(pixbuf)); status_menu_refresh_iter(status_box); pidgin_status_box_refresh(status_box); @@ -1155,7 +1120,7 @@ /* Reset the status if Escape was pressed */ if (event->keyval == GDK_Escape) { - g_source_remove(status_box->typing); + purple_timeout_remove(status_box->typing); status_box->typing = 0; if (status_box->account != NULL) update_to_reflect_account_status(status_box, status_box->account, @@ -1168,8 +1133,8 @@ } pidgin_status_box_pulse_typing(status_box); - g_source_remove(status_box->typing); - status_box->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box); + purple_timeout_remove(status_box->typing); + status_box->typing = purple_timeout_add_seconds(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box); return FALSE; } @@ -1201,7 +1166,7 @@ for (i = 0; i < G_N_ELEMENTS(status_box->connecting_pixbufs); i++) { if (status_box->connecting_pixbufs[i] != NULL) - gdk_pixbuf_unref(status_box->connecting_pixbufs[i]); + g_object_unref(G_OBJECT(status_box->connecting_pixbufs[i])); } status_box->connecting_index = 0; @@ -1224,7 +1189,7 @@ for (i = 0; i < G_N_ELEMENTS(status_box->typing_pixbufs); i++) { if (status_box->typing_pixbufs[i] != NULL) - gdk_pixbuf_unref(status_box->typing_pixbufs[i]); + g_object_unref(G_OBJECT(status_box->typing_pixbufs[i])); } status_box->typing_index = 0; @@ -1777,9 +1742,9 @@ status_box->vsep = gtk_vseparator_new(); status_box->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); - status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, + status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN); - status_box->dropdown_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING, + status_box->dropdown_store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN); gtk_cell_view_set_model(GTK_CELL_VIEW(status_box->cell_view), GTK_TREE_MODEL(status_box->store)); @@ -1854,7 +1819,7 @@ gtk_tree_view_column_pack_start(status_box->column, icon_rend, FALSE); gtk_tree_view_column_pack_start(status_box->column, text_rend, TRUE); gtk_tree_view_column_pack_start(status_box->column, emblem_rend, FALSE); - gtk_tree_view_column_set_attributes(status_box->column, icon_rend, "pixbuf", ICON_COLUMN, NULL); + gtk_tree_view_column_set_attributes(status_box->column, icon_rend, "pixbuf", ICON_COLUMN, "stock-id", ICON_STOCK_COLUMN, NULL); gtk_tree_view_column_set_attributes(status_box->column, text_rend, "markup", TEXT_COLUMN, NULL); gtk_tree_view_column_set_attributes(status_box->column, emblem_rend, "stock-id", EMBLEM_COLUMN, "visible", EMBLEM_VISIBLE_COLUMN, NULL); gtk_container_add(GTK_CONTAINER(status_box->scrolled_window), status_box->tree_view); @@ -1873,7 +1838,7 @@ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, FALSE); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), status_box->text_rend, TRUE); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, FALSE); - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, "pixbuf", ICON_COLUMN, NULL); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->icon_rend, "pixbuf", ICON_COLUMN, "stock-id", ICON_STOCK_COLUMN, NULL); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), status_box->text_rend, "markup", TEXT_COLUMN, NULL); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(status_box->cell_view), emblem_rend, "pixbuf", EMBLEM_COLUMN, "visible", EMBLEM_VISIBLE_COLUMN, NULL); #if GTK_CHECK_VERSION(2, 6, 0) @@ -2132,7 +2097,8 @@ * * @param status_box The status box itself. * @param type A PidginStatusBoxItemType. - * @param pixbuf The icon to associate with this row in the menu. + * @param pixbuf The icon to associate with this row in the menu. The + * function will try to decide a pixbuf if none is given. * @param title The title of this item. For the primitive entries, * this is something like "Available" or "Away." For * the saved statuses, this is something like @@ -2148,10 +2114,12 @@ * creation timestamp. */ void -pidgin_status_box_add(PidginStatusBox *status_box, PidginStatusBoxItemType type, GdkPixbuf *pixbuf, const char *title, const char *desc, gpointer data) +pidgin_status_box_add(PidginStatusBox *status_box, PidginStatusBoxItemType type, GdkPixbuf *pixbuf, + const char *title, const char *desc, gpointer data) { GtkTreeIter iter; char *text; + const char *stock = NULL; if (desc == NULL) { @@ -2178,9 +2146,27 @@ g_free(escaped_desc); } + if (!pixbuf) { + PurpleStatusPrimitive prim = PURPLE_STATUS_UNSET; + if (type == PIDGIN_STATUS_BOX_TYPE_PRIMITIVE) { + prim = GPOINTER_TO_INT(data); + } else if (type == PIDGIN_STATUS_BOX_TYPE_SAVED_POPULAR || + type == PIDGIN_STATUS_BOX_TYPE_POPULAR) { + PurpleSavedStatus *saved = purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data)); + if (saved) { + prim = purple_savedstatus_get_type(saved); + } + } + + stock = pidgin_stock_id_from_status_primitive(prim); + if (stock) + pixbuf = pidgin_status_box_get_pixbuf(status_box, stock); + } + gtk_list_store_append(status_box->dropdown_store, &iter); gtk_list_store_set(status_box->dropdown_store, &iter, TYPE_COLUMN, type, + ICON_STOCK_COLUMN, stock, ICON_COLUMN, pixbuf, TEXT_COLUMN, text, TITLE_COLUMN, title, @@ -2596,7 +2582,7 @@ return; } - g_source_remove(status_box->typing); + purple_timeout_remove(status_box->typing); status_box->typing = 0; activate_currently_selected_status(status_box); @@ -2624,7 +2610,7 @@ DATA_COLUMN, &data, -1); if (status_box->typing != 0) - g_source_remove(status_box->typing); + purple_timeout_remove(status_box->typing); status_box->typing = 0; if (GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(status_box))) @@ -2692,7 +2678,7 @@ GtkTextIter start, end; GtkTextBuffer *buffer; gtk_widget_show_all(status_box->vbox); - status_box->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box); + status_box->typing = purple_timeout_add_seconds(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box); gtk_widget_grab_focus(status_box->imhtml); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(status_box->imhtml)); gtk_text_buffer_get_bounds(buffer, &start, &end); @@ -2741,9 +2727,9 @@ { if (status_box->typing != 0) { pidgin_status_box_pulse_typing(status_box); - g_source_remove(status_box->typing); + purple_timeout_remove(status_box->typing); } - status_box->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box); + status_box->typing = purple_timeout_add_seconds(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, status_box); } pidgin_status_box_refresh(status_box); }
--- a/pidgin/gtkstatusbox.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkstatusbox.h Tue Apr 28 19:08:06 2009 +0000 @@ -34,8 +34,6 @@ #include "imgstore.h" #include "savedstatuses.h" #include "status.h" -#include <gtk/gtktreemodel.h> -#include <gtk/gtktreeview.h> G_BEGIN_DECLS
--- a/pidgin/gtkutils.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkutils.c Tue Apr 28 19:08:06 2009 +0000 @@ -358,7 +358,7 @@ } GtkWidget *pidgin_new_check_item(GtkWidget *menu, const char *str, - GtkSignalFunc sf, gpointer data, gboolean checked) + GCallback cb, gpointer data, gboolean checked) { GtkWidget *menuitem; menuitem = gtk_check_menu_item_new_with_mnemonic(str); @@ -368,8 +368,8 @@ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), checked); - if (sf) - g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); + if (cb) + g_signal_connect(G_OBJECT(menuitem), "activate", cb, data); gtk_widget_show_all(menuitem); @@ -439,7 +439,7 @@ } -GtkWidget *pidgin_new_item_from_stock(GtkWidget *menu, const char *str, const char *icon, GtkSignalFunc sf, gpointer data, guint accel_key, guint accel_mods, char *mod) +GtkWidget *pidgin_new_item_from_stock(GtkWidget *menu, const char *str, const char *icon, GCallback cb, gpointer data, guint accel_key, guint accel_mods, char *mod) { GtkWidget *menuitem; /* @@ -456,8 +456,8 @@ if (menu) gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - if (sf) - g_signal_connect(G_OBJECT(menuitem), "activate", sf, data); + if (cb) + g_signal_connect(G_OBJECT(menuitem), "activate", cb, data); if (icon != NULL) { image = gtk_image_new_from_stock(icon, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); @@ -950,7 +950,7 @@ "accel changed, scheduling save.\n"); if (!accels_save_timer) - accels_save_timer = g_timeout_add(5000, pidgin_save_accels, + accels_save_timer = purple_timeout_add_seconds(5, pidgin_save_accels, NULL); } @@ -1464,7 +1464,7 @@ str); g_free(str); - return; + break; } buddy = purple_find_buddy(data->account, data->who); @@ -1494,7 +1494,7 @@ g_error_free(err); g_free(str); - return; + break; } id = purple_imgstore_add_with_id(filedata, size, data->filename); @@ -1627,7 +1627,7 @@ _("Set as buddy icon"), DND_BUDDY_ICON, (ft ? _("Send image file") : _("Insert in message")), (ft ? DND_FILE_TRANSFER : DND_IM_IMAGE), NULL); - gdk_pixbuf_unref(pb); + g_object_unref(G_OBJECT(pb)); return; } @@ -1715,29 +1715,42 @@ { GtkIconSize icon_size = gtk_icon_size_from_name(size); GdkPixbuf *pixbuf = NULL; - - if (prim == PURPLE_STATUS_UNAVAILABLE) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_BUSY, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_AWAY) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AWAY, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_EXTENDED_AWAY) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_XA, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_INVISIBLE) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_INVISIBLE, - icon_size, "GtkWidget"); - else if (prim == PURPLE_STATUS_OFFLINE) - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_OFFLINE, - icon_size, "GtkWidget"); - else - pixbuf = gtk_widget_render_icon (w, PIDGIN_STOCK_STATUS_AVAILABLE, - icon_size, "GtkWidget"); + const char *stock = pidgin_stock_id_from_status_primitive(prim); + + pixbuf = gtk_widget_render_icon (w, stock ? stock : PIDGIN_STOCK_STATUS_AVAILABLE, + icon_size, "GtkWidget"); return pixbuf; - } +const char * +pidgin_stock_id_from_status_primitive(PurpleStatusPrimitive prim) +{ + const char *stock = NULL; + switch (prim) { + case PURPLE_STATUS_UNSET: + stock = NULL; + break; + case PURPLE_STATUS_UNAVAILABLE: + stock = PIDGIN_STOCK_STATUS_BUSY; + break; + case PURPLE_STATUS_AWAY: + stock = PIDGIN_STOCK_STATUS_AWAY; + break; + case PURPLE_STATUS_EXTENDED_AWAY: + stock = PIDGIN_STOCK_STATUS_XA; + break; + case PURPLE_STATUS_INVISIBLE: + stock = PIDGIN_STOCK_STATUS_INVISIBLE; + break; + case PURPLE_STATUS_OFFLINE: + stock = PIDGIN_STOCK_STATUS_OFFLINE; + break; + default: + stock = PIDGIN_STOCK_STATUS_AVAILABLE; + break; + } + return stock; +} GdkPixbuf * pidgin_create_prpl_icon(PurpleAccount *account, PidginPrplIconSize size) @@ -2943,7 +2956,7 @@ #endif } -GSList *minidialogs = NULL; +static GSList *minidialogs = NULL; static void * pidgin_utils_get_handle(void)
--- a/pidgin/gtkutils.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkutils.h Tue Apr 28 19:08:06 2009 +0000 @@ -233,14 +233,14 @@ * * @param menu The menu to which to append the check menu item. * @param str The title to use for the newly created menu item. - * @param sf A function to call when the menu item is activated. + * @param cb A function to call when the menu item is activated. * @param data Data to pass to the signal function. * @param checked The initial state of the check item * * @return The newly created menu item. */ GtkWidget *pidgin_new_check_item(GtkWidget *menu, const char *str, - GtkSignalFunc sf, gpointer data, gboolean checked); + GCallback cb, gpointer data, gboolean checked); /** * Creates a menu item. @@ -249,7 +249,7 @@ * @param str The title for the menu item. * @param icon An icon to place to the left of the menu item, * or @c NULL for no icon. - * @param sf A function to call when the menu item is activated. + * @param cb A function to call when the menu item is activated. * @param data Data to pass to the signal function. * @param accel_key Something. * @param accel_mods Something. @@ -258,7 +258,7 @@ * @return The newly created menu item. */ GtkWidget *pidgin_new_item_from_stock(GtkWidget *menu, const char *str, - const char *icon, GtkSignalFunc sf, + const char *icon, GCallback cb, gpointer data, guint accel_key, guint accel_mods, char *mod); @@ -569,6 +569,16 @@ */ GdkPixbuf * pidgin_create_status_icon(PurpleStatusPrimitive primitive, GtkWidget *w, const char *size); +/** + * Returns an appropriate stock-id for a status primitive. + * + * @param prim The status primitive + * + * @return The stock-id + * + * @since 2.6.0 + */ +const char *pidgin_stock_id_from_status_primitive(PurpleStatusPrimitive prim); /** * Append a PurpleMenuAction to a menu.
--- a/pidgin/gtkwhiteboard.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/gtkwhiteboard.c Tue Apr 28 19:08:06 2009 +0000 @@ -624,7 +624,7 @@ update_rect.x, update_rect.y, update_rect.width, update_rect.height); - gdk_gc_unref(gfx_con); + g_object_unref(G_OBJECT(gfx_con)); } /* Uses Bresenham's algorithm (as provided by Wikipedia) */
--- a/pidgin/minidialog.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/minidialog.c Tue Apr 28 19:08:06 2009 +0000 @@ -26,8 +26,7 @@ #include "internal.h" -#include <gtk/gtkhbox.h> -#include <gtk/gtkbutton.h> +#include <gtk/gtk.h> #include "libpurple/prefs.h"
--- a/pidgin/minidialog.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/minidialog.h Tue Apr 28 19:08:06 2009 +0000 @@ -28,8 +28,7 @@ #define __PIDGIN_MINI_DIALOG_H__ #include <glib-object.h> -#include <gtk/gtkvbox.h> -#include <gtk/gtklabel.h> +#include <gtk/gtk.h> G_BEGIN_DECLS
--- a/pidgin/pidginstock.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/pidginstock.c Tue Apr 28 19:08:06 2009 +0000 @@ -201,7 +201,7 @@ #ifdef USE_VV { PIDGIN_STOCK_TOOLBAR_AUDIO_CALL, "toolbar", "audio-call.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, { PIDGIN_STOCK_TOOLBAR_VIDEO_CALL, "toolbar", "video-call.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, - { PIDGIN_STOCK_TOOLBAR_AUDIO_VIDEO_CALL, "toolbar", "audio-video-call.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, + { PIDGIN_STOCK_TOOLBAR_AUDIO_VIDEO_CALL, "toolbar", "audio-video-call.png", FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL }, #endif }; @@ -320,7 +320,7 @@ } static gchar * -find_icon_file(PidginStatusIconTheme *theme, const gchar *size, SizedStockIcon sized_icon, gboolean rtl) +find_icon_file(PidginIconTheme *theme, const gchar *size, SizedStockIcon sized_icon, gboolean rtl) { const gchar *file, *dir; gchar *file_full = NULL; @@ -352,7 +352,7 @@ } static void -add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginStatusIconTheme *theme, +add_sized_icon(GtkIconSet *iconset, GtkIconSize sizeid, PidginIconTheme *theme, const char *size, SizedStockIcon sized_icon, gboolean translucent) { char *filename; @@ -409,6 +409,16 @@ } } +static void +reload_settings(void) +{ +#if GTK_CHECK_VERSION(2,4,0) + GtkSettings *setting = NULL; + setting = gtk_settings_get_default(); + gtk_rc_reset_styles(setting); +#endif +} + /***************************************************************************** * Public API functions *****************************************************************************/ @@ -447,9 +457,9 @@ translucent = gtk_icon_set_new(); #define ADD_SIZED_ICON(name, size) if (sized_status_icons[i].name) { \ - add_sized_icon(normal, name, theme, size, sized_status_icons[i], FALSE); \ + add_sized_icon(normal, name, PIDGIN_ICON_THEME(theme), size, sized_status_icons[i], FALSE); \ if (sized_status_icons[i].translucent_name) \ - add_sized_icon(translucent, name, theme, size, sized_status_icons[i], TRUE); \ + add_sized_icon(translucent, name, PIDGIN_ICON_THEME(theme), size, sized_status_icons[i], TRUE); \ } ADD_SIZED_ICON(microscopic, "11"); ADD_SIZED_ICON(extra_small, "16"); @@ -471,52 +481,45 @@ gtk_widget_destroy(win); g_object_unref(G_OBJECT(icon_factory)); + reload_settings(); } void -pidgin_stock_init(void) +pidgin_stock_load_stock_icon_theme(PidginStockIconTheme *theme) { GtkIconFactory *icon_factory; - size_t i; + gint i; GtkWidget *win; - PidginIconThemeLoader *loader; - const gchar *path = NULL; - - if (stock_initted) - return; - stock_initted = TRUE; + if (theme != NULL) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/stock/icon-theme", + purple_theme_get_name(PURPLE_THEME(theme))); + purple_prefs_set_path(PIDGIN_PREFS_ROOT "/stock/icon-theme-dir", + purple_theme_get_dir(PURPLE_THEME(theme))); + } + else { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/stock/icon-theme", ""); + purple_prefs_set_path(PIDGIN_PREFS_ROOT "/stock/icon-theme-dir", ""); + } - /* Setup the status icon theme */ - loader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "status-icon", NULL); - purple_theme_manager_register_type(PURPLE_THEME_LOADER(loader)); - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/status/icon-theme", ""); - purple_prefs_add_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", ""); - - /* Setup the icon factory. */ icon_factory = gtk_icon_factory_new(); gtk_icon_factory_add_default(icon_factory); - /* Er, yeah, a hack, but it works. :) */ win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_realize(win); /* All non-sized icons */ - for (i = 0; i < G_N_ELEMENTS(stock_icons); i++) - { + for (i = 0; i < G_N_ELEMENTS(stock_icons); i++) { GtkIconSource *source; GtkIconSet *iconset; gchar *filename; - if (stock_icons[i].dir == NULL) - { + if (stock_icons[i].dir == NULL) { /* GTK+ Stock icon */ iconset = gtk_style_lookup_icon_set(gtk_widget_get_style(win), stock_icons[i].filename); - } - else - { + } else { filename = find_file(stock_icons[i].dir, stock_icons[i].filename); if (filename == NULL) @@ -540,21 +543,13 @@ gtk_icon_set_unref(iconset); } - /* register custom icon sizes */ - microscopic = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11); - extra_small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16); - small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22); - medium = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MEDIUM, 32, 32); - large = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_LARGE, 48, 48); - huge = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_HUGE, 64, 64); - /* All non-status sized icons */ for (i = 0; i < G_N_ELEMENTS(sized_stock_icons); i++) { GtkIconSet *iconset = gtk_icon_set_new(); #define ADD_SIZED_ICON(name, size) if (sized_stock_icons[i].name) \ - add_sized_icon(iconset, name, NULL, size, sized_stock_icons[i], FALSE); + add_sized_icon(iconset, name, PIDGIN_ICON_THEME(theme), size, sized_stock_icons[i], FALSE); ADD_SIZED_ICON(microscopic, "11"); ADD_SIZED_ICON(extra_small, "16"); ADD_SIZED_ICON(small, "22"); @@ -569,6 +564,40 @@ gtk_widget_destroy(win); g_object_unref(G_OBJECT(icon_factory)); + reload_settings(); +} + +void +pidgin_stock_init(void) +{ + PidginIconThemeLoader *loader, *stockloader; + const gchar *path = NULL; + + if (stock_initted) + return; + + stock_initted = TRUE; + + /* Setup the status icon theme */ + loader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "status-icon", NULL); + purple_theme_manager_register_type(PURPLE_THEME_LOADER(loader)); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/status/icon-theme", ""); + purple_prefs_add_path(PIDGIN_PREFS_ROOT "/status/icon-theme-dir", ""); + + stockloader = g_object_new(PIDGIN_TYPE_ICON_THEME_LOADER, "type", "stock-icon", NULL); + purple_theme_manager_register_type(PURPLE_THEME_LOADER(stockloader)); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/stock/icon-theme", ""); + purple_prefs_add_path(PIDGIN_PREFS_ROOT "/stock/icon-theme-dir", ""); + + /* register custom icon sizes */ + microscopic = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, 11, 11); + extra_small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, 16, 16); + small = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_SMALL, 22, 22); + medium = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_MEDIUM, 32, 32); + large = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_LARGE, 48, 48); + huge = gtk_icon_size_register(PIDGIN_ICON_SIZE_TANGO_HUGE, 64, 64); + + pidgin_stock_load_stock_icon_theme(NULL); /* Pre-load Status icon theme - this avoids a bug with displaying the correct icon in the tray, theme is destroyed after*/ if (purple_prefs_get_string(PIDGIN_PREFS_ROOT "/icon/status/theme") && @@ -583,3 +612,31 @@ /* Register the stock items. */ gtk_stock_add_static(stock_items, G_N_ELEMENTS(stock_items)); } + +static void +pidgin_stock_icon_theme_class_init(PidginStockIconThemeClass *klass) +{ +} + +GType +pidgin_stock_icon_theme_get_type(void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (PidginStockIconThemeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)pidgin_stock_icon_theme_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PidginStockIconTheme), + 0, /* n_preallocs */ + NULL, + NULL, /* value table */ + }; + type = g_type_register_static(PIDGIN_TYPE_ICON_THEME, + "PidginStockIconTheme", &info, 0); + } + return type; +}
--- a/pidgin/pidginstock.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/pidginstock.h Tue Apr 28 19:08:06 2009 +0000 @@ -23,7 +23,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include <gtk/gtkstock.h> +#include <gtk/gtk.h> #include "gtkstatus-icon-theme.h" #ifndef _PIDGIN_STOCK_H_ @@ -185,15 +185,54 @@ #define PIDGIN_ICON_SIZE_TANGO_HUGE "pidgin-icon-size-tango-huge" /** + * extends PidginIconTheme (gtkicon-theme.h) + * A pidgin stock icon theme. + * This object represents a Pidgin stock icon theme. + * + * PidginStockIconTheme is a PidginIconTheme Object. + */ +typedef struct _PidginStockIconTheme PidginStockIconTheme; +typedef struct _PidginStockIconThemeClass PidginStockIconThemeClass; + +#define PIDGIN_TYPE_STOCK_ICON_THEME (pidgin_stock_icon_theme_get_type ()) +#define PIDGIN_STOCK_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIDGIN_TYPE_STOCK_ICON_THEME, PidginStockIconTheme)) +#define PIDGIN_STOCK_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PIDGIN_TYPE_STOCK_ICON_THEME, PidginStockIconThemeClass)) +#define PIDGIN_IS_STOCK_ICON_THEME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PIDGIN_TYPE_STOCK_ICON_THEME)) +#define PIDGIN_IS_STOCK_ICON_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PIDGIN_TYPE_STOCK_ICON_THEME)) +#define PIDGIN_STOCK_ICON_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PIDGIN_TYPE_STOCK_ICON_THEME, PidginStockIconThemeClass)) + +struct _PidginStockIconTheme +{ + PidginIconTheme parent; +}; + +struct _PidginStockIconThemeClass +{ + PidginIconThemeClass parent_class; +}; + +G_BEGIN_DECLS + +/** + * GObject foo. + * @internal. + */ +GType pidgin_stock_icon_theme_get_type(void); + +/** * Loades all of the icons from the status icon theme into Pidgin stock * * @param theme the theme to load, or null to load all the default icons */ void pidgin_stock_load_status_icon_theme(PidginStatusIconTheme *theme); + +void pidgin_stock_load_stock_icon_theme(PidginStockIconTheme *theme); + /** * Sets up the purple stock repository. */ void pidgin_stock_init(void); +G_END_DECLS #endif /* _PIDGIN_STOCK_H_ */
--- a/pidgin/plugins/Makefile.am Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/Makefile.am Tue Apr 28 19:08:06 2009 +0000 @@ -43,6 +43,7 @@ relnot_la_LDFLAGS = -module -avoid-version sendbutton_la_LDFLAGS = -module -avoid-version spellchk_la_LDFLAGS = -module -avoid-version +themeedit_la_LDFLAGS = -module -avoid-version timestamp_la_LDFLAGS = -module -avoid-version timestamp_format_la_LDFLAGS = -module -avoid-version xmppconsole_la_LDFLAGS = -module -avoid-version @@ -61,6 +62,7 @@ relnot.la \ sendbutton.la \ spellchk.la \ + themeedit.la \ timestamp.la \ timestamp_format.la \ xmppconsole.la @@ -82,6 +84,7 @@ relnot_la_SOURCES = relnot.c sendbutton_la_SOURCES = sendbutton.c spellchk_la_SOURCES = spellchk.c +themeedit_la_SOURCES = themeedit.c themeedit-icon.c themeedit-icon.h timestamp_la_SOURCES = timestamp.c timestamp_format_la_SOURCES = timestamp_format.c xmppconsole_la_SOURCES = xmppconsole.c @@ -99,6 +102,7 @@ relnot_la_LIBADD = $(GLIB_LIBS) sendbutton_la_LIBADD = $(GTK_LIBS) spellchk_la_LIBADD = $(GTK_LIBS) +themeedit_la_LIBADD = $(GTK_LIBS) timestamp_la_LIBADD = $(GTK_LIBS) timestamp_format_la_LIBADD = $(GTK_LIBS) xmppconsole_la_LIBADD = $(GTK_LIBS)
--- a/pidgin/plugins/cap/cap.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/cap/cap.c Tue Apr 28 19:08:06 2009 +0000 @@ -135,7 +135,7 @@ /* g_free(stats->hourly_usage); */ /* g_free(stats->daily_usage); */ if (stats->timeout_source_id != 0) - g_source_remove(stats->timeout_source_id); + purple_timeout_remove(stats->timeout_source_id); g_free(stats); } @@ -352,7 +352,7 @@ if (buddy == NULL) return; - interval = purple_prefs_get_int("/plugins/gtk/cap/max_msg_difference") * 1000 * 60; + interval = purple_prefs_get_int("/plugins/gtk/cap/max_msg_difference") * 60; words = word_count(message); stats = get_stats_for(buddy); @@ -361,9 +361,9 @@ stats->last_message = time(NULL); stats->last_message_status_id = purple_status_get_id(get_status_for(buddy)); if(stats->timeout_source_id != 0) - g_source_remove(stats->timeout_source_id); + purple_timeout_remove(stats->timeout_source_id); - stats->timeout_source_id = g_timeout_add(interval, max_message_difference_cb, stats); + stats->timeout_source_id = purple_timeout_add_seconds(interval, max_message_difference_cb, stats); } /* received-im-msg */ @@ -386,7 +386,7 @@ * then cancel the timeout callback. */ if(stats->timeout_source_id != 0) { purple_debug_info("cap", "Cancelling timeout callback\n"); - g_source_remove(stats->timeout_source_id); + purple_timeout_remove(stats->timeout_source_id); stats->timeout_source_id = 0; } @@ -697,7 +697,7 @@ static void cancel_conversation_timeouts(gpointer key, gpointer value, gpointer user_data) { CapStatistics *stats = value; if(stats->timeout_source_id != 0) { - g_source_remove(stats->timeout_source_id); + purple_timeout_remove(stats->timeout_source_id); stats->timeout_source_id = 0; } }
--- a/pidgin/plugins/contact_priority.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/contact_priority.c Tue Apr 28 19:08:06 2009 +0000 @@ -31,7 +31,7 @@ select_account(GtkWidget *widget, PurpleAccount *account, gpointer data) { gtk_spin_button_set_value(GTK_SPIN_BUTTON(data), - (gdouble)purple_account_get_int(account, "score", 0)); + (gdouble)purple_account_get_int(account, "score", 0)); } static void @@ -142,18 +142,18 @@ spin = gtk_spin_button_new((GtkAdjustment *)adj, 1, 0); optmenu = pidgin_account_option_menu_new(NULL, TRUE, - G_CALLBACK(select_account), - NULL, spin); + G_CALLBACK(select_account), + NULL, spin); gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0); /* this is where we set up the spin button we made above */ account = g_object_get_data(G_OBJECT(gtk_menu_get_active(GTK_MENU(gtk_option_menu_get_menu(GTK_OPTION_MENU(optmenu))))), - "account"); + "account"); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), - (gdouble)purple_account_get_int(account, "score", 0)); + (gdouble)purple_account_get_int(account, "score", 0)); gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(spin), GTK_ADJUSTMENT(adj)); g_signal_connect(G_OBJECT(spin), "value-changed", - G_CALLBACK(account_update), optmenu); + G_CALLBACK(account_update), optmenu); gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0); gtk_widget_show_all(ret); @@ -178,29 +178,29 @@ PURPLE_PLUGIN_MAGIC, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_STANDARD, /**< type */ + PURPLE_PLUGIN_STANDARD, /**< type */ PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ + 0, /**< flags */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ - CONTACT_PRIORITY_PLUGIN_ID, /**< id */ - N_("Contact Priority"), /**< name */ - DISPLAY_VERSION, /**< version */ + CONTACT_PRIORITY_PLUGIN_ID, /**< id */ + N_("Contact Priority"), /**< name */ + DISPLAY_VERSION, /**< version */ /**< summary */ N_("Allows for controlling the values associated with different buddy states."), /**< description */ N_("Allows for changing the point values of idle/away/offline states for buddies in contact priority computations."), - "Etan Reisner <deryni@eden.rutgers.edu>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ + "Etan Reisner <deryni@eden.rutgers.edu>", /**< author */ + PURPLE_WEBSITE, /**< homepage */ - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - &ui_info, /**< ui_info */ - NULL, /**< extra_info */ - NULL, /**< prefs_info */ - NULL, /**< actions */ + NULL, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + &ui_info, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL, /**< actions */ /* padding */ NULL,
--- a/pidgin/plugins/mailchk.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/mailchk.c Tue Apr 28 19:08:06 2009 +0000 @@ -13,7 +13,7 @@ #define UNREAD_MAIL 0x02 #define NEW_MAIL 0x04 -static guint32 timer = 0; +static guint timer = 0; static GtkWidget *mail = NULL; static gint @@ -93,7 +93,7 @@ PurpleBuddyList *list = purple_get_blist(); if (list && PURPLE_IS_GTK_BLIST(list) && !timer) { check_timeout(NULL); /* we want the box to be drawn immediately */ - timer = g_timeout_add(2000, check_timeout, NULL); + timer = purple_timeout_add_seconds(2, check_timeout, NULL); } } @@ -102,7 +102,7 @@ { PurpleBuddyList *list = purple_get_blist(); if ((!list || !PURPLE_IS_GTK_BLIST(list) || !PIDGIN_BLIST(list)->vbox) && timer) { - g_source_remove(timer); + purple_timeout_remove(timer); timer = 0; } } @@ -123,7 +123,7 @@ } if (list && PURPLE_IS_GTK_BLIST(list) && PIDGIN_BLIST(list)->vbox) - timer = g_timeout_add(2000, check_timeout, NULL); + timer = purple_timeout_add_seconds(2, check_timeout, NULL); purple_signal_connect(conn_handle, "signed-on", plugin, PURPLE_CALLBACK(signon_cb), NULL); @@ -137,7 +137,7 @@ plugin_unload(PurplePlugin *plugin) { if (timer) - g_source_remove(timer); + purple_timeout_remove(timer); timer = 0; if (mail) gtk_widget_destroy(mail);
--- a/pidgin/plugins/markerline.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/markerline.c Tue Apr 28 19:08:06 2009 +0000 @@ -84,7 +84,7 @@ gdk_gc_set_rgb_fg_color(gc, &red); gdk_draw_line(event->window, gc, 0, y, visible_rect.width, y); - gdk_gc_unref(gc); + g_object_unref(G_OBJECT(gc)); } return FALSE; }
--- a/pidgin/plugins/musicmessaging/musicmessaging.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/musicmessaging/musicmessaging.c Tue Apr 28 19:08:06 2009 +0000 @@ -529,7 +529,7 @@ args[1] = "-session_id"; session_id = g_string_new(""); - g_string_sprintfa(session_id, "%d", mmconv_from_conv_loc(mmconv->conv)); + g_string_append_printf(session_id, "%d", mmconv_from_conv_loc(mmconv->conv)); args[2] = session_id->str; args[3] = NULL;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/themeedit-icon.c Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,312 @@ +/* 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 "version.h" + +#include "theme-manager.h" + +#include "gtkblist.h" +#include "gtkblist-theme.h" +#include "gtkutils.h" +#include "gtkplugin.h" + +#include "pidginstock.h" +#include "themeedit-icon.h" + +typedef enum +{ + FLAG_SIZE_MICROSOPIC = 0, + FLAG_SIZE_EXTRA_SMALL, + FLAG_SIZE_SMALL, + FLAG_SIZE_MEDIUM, + FLAG_SIZE_LARGE, + FLAG_SIZE_HUGE, + FLAG_SIZE_NONE, +} SectionFlags; + +#define SECTION_FLAGS_ALL (0x3f) + +static const char *stocksizes [] = { + [FLAG_SIZE_MICROSOPIC] = PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC, + [FLAG_SIZE_EXTRA_SMALL] = PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL, + [FLAG_SIZE_SMALL] = PIDGIN_ICON_SIZE_TANGO_SMALL, + [FLAG_SIZE_MEDIUM] = PIDGIN_ICON_SIZE_TANGO_MEDIUM, + [FLAG_SIZE_LARGE] = PIDGIN_ICON_SIZE_TANGO_LARGE, + [FLAG_SIZE_HUGE] = PIDGIN_ICON_SIZE_TANGO_HUGE, + [FLAG_SIZE_NONE] = NULL, +}; + +static const struct options { + const char *stockid; + const char *text; +} statuses[] = { + {PIDGIN_STOCK_STATUS_AVAILABLE, N_("Available")}, + {PIDGIN_STOCK_STATUS_AWAY, N_("Away")}, + {PIDGIN_STOCK_STATUS_XA, N_("Extended Away")}, + {PIDGIN_STOCK_STATUS_BUSY, N_("Busy")}, + {PIDGIN_STOCK_STATUS_OFFLINE, N_("Offline")}, + {PIDGIN_STOCK_STATUS_LOGIN, N_("Just logged in")}, + {PIDGIN_STOCK_STATUS_LOGOUT, N_("Just logged out")}, + {PIDGIN_STOCK_STATUS_PERSON, N_("Icon for Contact/\nIcon for Unknown person")}, + {PIDGIN_STOCK_STATUS_CHAT, N_("Icon for Chat")}, + {NULL, NULL} +}, chatemblems[] = { + {PIDGIN_STOCK_STATUS_IGNORED, N_("Ignored")}, + {PIDGIN_STOCK_STATUS_FOUNDER, N_("Founder")}, + {PIDGIN_STOCK_STATUS_OPERATOR, N_("Operator")}, + {PIDGIN_STOCK_STATUS_HALFOP, N_("Half Operator")}, + {PIDGIN_STOCK_STATUS_VOICE, N_("Voice")}, + {NULL, NULL} +}, dialogicons[] = { + {PIDGIN_STOCK_DIALOG_AUTH, N_("Authorization dialog")}, + {PIDGIN_STOCK_DIALOG_ERROR, N_("Error dialog")}, + {PIDGIN_STOCK_DIALOG_INFO, N_("Information dialog")}, + {PIDGIN_STOCK_DIALOG_MAIL, N_("Mail dialog")}, + {PIDGIN_STOCK_DIALOG_QUESTION, N_("Question dialog")}, + {PIDGIN_STOCK_DIALOG_WARNING, N_("Warning dialog")}, + {NULL, NULL}, + {PIDGIN_STOCK_DIALOG_COOL, N_("What kind of dialog is this?")}, +}; + +static const struct { + const char *heading; + const struct options *options; + SectionFlags flags; +} sections[] = { + {N_("Status Icons"), statuses, SECTION_FLAGS_ALL ^ (1 << FLAG_SIZE_HUGE)}, + {N_("Chatroom Emblems"), chatemblems, FLAG_SIZE_SMALL}, + {N_("Dialog Icons"), dialogicons, (1 << FLAG_SIZE_EXTRA_SMALL) | (1 << FLAG_SIZE_HUGE)}, + {NULL, NULL, 0} +}; + +static PidginStatusIconTheme * +create_icon_theme(GtkWidget *window) +{ + int s, i, j; + char *dirname = "/tmp"; /* FIXME */ + PidginStatusIconTheme *theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, "type", "status-icon", + "author", getlogin(), + "directory", dirname, + NULL); + + for (s = 0; sections[s].heading; s++) { + GtkWidget *vbox = g_object_get_data(G_OBJECT(window), sections[s].heading); + for (i = 0; sections[s].options[i].stockid; i++) { + GtkWidget *image = g_object_get_data(G_OBJECT(vbox), sections[s].options[i].stockid); + GdkPixbuf *pixbuf = g_object_get_data(G_OBJECT(image), "pixbuf"); + if (!pixbuf) + continue; + pidgin_icon_theme_set_icon(PIDGIN_ICON_THEME(theme), sections[s].options[i].stockid, + sections[s].options[i].stockid); + for (j = 0; stocksizes[j]; j++) { + int width, height; + GtkIconSize iconsize; + char size[8]; + char *name; + GdkPixbuf *scale; + GError *error = NULL; + + if (!(sections[s].flags & (1 << j))) + continue; + + iconsize = gtk_icon_size_from_name(stocksizes[j]); + gtk_icon_size_lookup(iconsize, &width, &height); + g_snprintf(size, sizeof(size), "%d", width); + + if (i == 0) { + name = g_build_filename(dirname, size, NULL); + purple_build_dir(name, S_IRUSR | S_IWUSR | S_IXUSR); + g_free(name); + } + + name = g_build_filename(dirname, size, sections[s].options[i].stockid, NULL); + scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); + gdk_pixbuf_save(scale, name, "png", &error, "compression", "9", NULL); + g_free(name); + g_object_unref(G_OBJECT(scale)); + if (error) + g_error_free(error); + } + } + } + return theme; +} + +static void +use_icon_theme(GtkWidget *w, GtkWidget *window) +{ + /* I don't quite understand the icon-theme stuff. For example, I don't + * know why PidginIconTheme needs to be abstract, or how PidginStatusIconTheme + * would be different from other PidginIconTheme's (e.g. PidginStockIconTheme) + * etc., but anyway, this works for now. + * + * Here's an interesting note: A PidginStatusIconTheme can be used for both + * stock and status icons. Like I said, I don't quite know how they could be + * different. So I am going to just keep it as it is, for now anyway, until I + * have the time to dig through this, or someone explains this stuff to me + * clearly. + * -- Sad + */ + PidginStatusIconTheme *theme = create_icon_theme(window); + pidgin_stock_load_status_icon_theme(PIDGIN_STATUS_ICON_THEME(theme)); + pidgin_stock_load_stock_icon_theme((PidginStockIconTheme *)theme); + pidgin_blist_refresh(purple_get_blist()); + g_object_unref(theme); +} + +#ifdef NOT_SADRUL +static void +save_icon_theme(GtkWidget *w, GtkWidget *window) +{ + /* TODO: SAVE! */ + gtk_widget_destroy(window); +} +#endif + +static void +close_icon_theme(GtkWidget *w, GtkWidget *window) +{ + gtk_widget_destroy(window); +} + +static void +stock_icon_selected(const char *filename, gpointer image) +{ + GError *error = NULL; + GdkPixbuf *scale; + int i; + GdkPixbuf *pixbuf; + + if (!filename) + return; + + pixbuf = gdk_pixbuf_new_from_file(filename, &error); + if (error || !pixbuf) { + purple_debug_error("theme-editor-icon", "Unable to load icon file '%s' (%s)\n", + filename, error ? error->message : "Reason unknown"); + if (error) + g_error_free(error); + return; + } + + scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR); + gtk_image_set_from_pixbuf(GTK_IMAGE(image), scale); + g_object_unref(G_OBJECT(scale)); + + /* Update the size previews */ + for (i = 0; stocksizes[i]; i++) { + int width, height; + GtkIconSize iconsize; + GtkWidget *prev = g_object_get_data(G_OBJECT(image), stocksizes[i]); + if (!prev) + continue; + iconsize = gtk_icon_size_from_name(stocksizes[i]); + gtk_icon_size_lookup(iconsize, &width, &height); + scale = gdk_pixbuf_scale_simple(pixbuf, width, height, GDK_INTERP_BILINEAR); + gtk_image_set_from_pixbuf(GTK_IMAGE(prev), scale); + g_object_unref(G_OBJECT(scale)); + } + + /* Save the original pixbuf so we can use it for resizing later */ + g_object_set_data_full(G_OBJECT(image), "pixbuf", pixbuf, + (GDestroyNotify)g_object_unref); +} + +static gboolean +change_stock_image(GtkWidget *widget, GdkEventButton *event, GtkWidget *image) +{ + GtkWidget *win = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)), + stock_icon_selected, image); + gtk_widget_show_all(win); + + return TRUE; +} + +void pidgin_icon_theme_edit(PurplePluginAction *unused) +{ + GtkWidget *dialog; + GtkWidget *box, *vbox; + GtkWidget *notebook; + GtkSizeGroup *sizegroup; + int s, i, j; + dialog = pidgin_create_dialog(_("Pidgin Icon Theme Editor"), 0, "theme-editor-icon", FALSE); + box = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog), FALSE, PIDGIN_HIG_BOX_SPACE); + + notebook = gtk_notebook_new(); + gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE); + sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + for (s = 0; sections[s].heading; s++) { + const char *heading = sections[s].heading; + + box = gtk_vbox_new(FALSE, 0); + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box, gtk_label_new(heading)); + + vbox = pidgin_make_frame(box, heading); + g_object_set_data(G_OBJECT(dialog), heading, vbox); + + for (i = 0; sections[s].options[i].stockid; i++) { + const char *id = sections[s].options[i].stockid; + const char *text = _(sections[s].options[i].text); + + GtkWidget *hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + GtkWidget *label = gtk_label_new(text); + GtkWidget *image = gtk_image_new_from_stock(id, + gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); + GtkWidget *ebox = gtk_event_box_new(); + gtk_container_add(GTK_CONTAINER(ebox), image); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + + g_signal_connect(G_OBJECT(ebox), "button-press-event", G_CALLBACK(change_stock_image), image); + g_object_set_data(G_OBJECT(image), "property-name", (gpointer)id); + + gtk_size_group_add_widget(sizegroup, label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(hbox), ebox, FALSE, FALSE, 0); + + for (j = 0; stocksizes[j]; j++) { + GtkWidget *sh; + + if (!(sections[s].flags & (1 << j))) + continue; + + sh = gtk_image_new_from_stock(id, gtk_icon_size_from_name(stocksizes[j])); + gtk_box_pack_start(GTK_BOX(hbox), sh, FALSE, FALSE, 0); + g_object_set_data(G_OBJECT(image), stocksizes[j], sh); + } + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + g_object_set_data(G_OBJECT(vbox), id, image); + } + } + +#ifdef NOT_SADRUL + pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, G_CALLBACK(save_icon_theme), dialog); +#endif + pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_APPLY, G_CALLBACK(use_icon_theme), dialog); + pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, G_CALLBACK(close_icon_theme), dialog); + gtk_widget_show_all(dialog); + g_object_unref(sizegroup); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/themeedit-icon.h Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,2 @@ +void pidgin_icon_theme_edit(PurplePluginAction *); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/themeedit.c Tue Apr 28 19:08:06 2009 +0000 @@ -0,0 +1,362 @@ +/* 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 "version.h" + +#include "theme-manager.h" + +#include "gtkblist.h" +#include "gtkblist-theme.h" +#include "gtkutils.h" +#include "gtkplugin.h" + +#define PLUGIN_ID "gtk-theme-editor" + +#include "themeedit-icon.h" + +static gboolean +prop_type_is_color(PidginBlistTheme *theme, const char *prop) +{ + PidginBlistThemeClass *klass = PIDGIN_BLIST_THEME_GET_CLASS(theme); + GParamSpec *spec = g_object_class_find_property(G_OBJECT_CLASS(klass), prop); + + return G_IS_PARAM_SPEC_BOXED(spec); +} + +#ifdef NOT_SADRUL +static void +save_blist_theme(GtkWidget *w, GtkWidget *window) +{ + /* TODO: SAVE! */ + gtk_widget_destroy(window); +} +#endif + +static void +close_blist_theme(GtkWidget *w, GtkWidget *window) +{ + gtk_widget_destroy(window); +} + +static void +theme_color_selected(GtkDialog *dialog, gint response, const char *prop) +{ + if (response == GTK_RESPONSE_OK) { + GdkColor color; + PidginBlistTheme *theme; + + gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel), &color); + + theme = pidgin_blist_get_theme(); + + if (prop_type_is_color(theme, prop)) { + g_object_set(G_OBJECT(theme), prop, &color, NULL); + } else { + PidginThemeFont *font = NULL; + g_object_get(G_OBJECT(theme), prop, &font, NULL); + if (!font) { + font = pidgin_theme_font_new(NULL, &color); + g_object_set(G_OBJECT(theme), prop, font, NULL); + pidgin_theme_font_free(font); + } else { + pidgin_theme_font_set_color(font, &color); + } + } + pidgin_blist_set_theme(theme); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + +static void +theme_font_face_selected(GtkWidget *dialog, gint response, gpointer font) +{ + if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY) { + const char *fontname = gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(dialog)); + pidgin_theme_font_set_font_face(font, fontname); + pidgin_blist_refresh(purple_get_blist()); + } + gtk_widget_destroy(dialog); +} + +static void +theme_font_select_face(GtkWidget *widget, gpointer prop) +{ + GtkWidget *dialog; + PidginBlistTheme *theme; + PidginThemeFont *font = NULL; + const char *face; + + theme = pidgin_blist_get_theme(); + g_object_get(G_OBJECT(theme), prop, &font, NULL); + + if (!font) { + font = pidgin_theme_font_new(NULL, NULL); + g_object_set(G_OBJECT(theme), prop, font, NULL); + pidgin_theme_font_free(font); + g_object_get(G_OBJECT(theme), prop, &font, NULL); + } + + face = pidgin_theme_font_get_font_face(font); + dialog = gtk_font_selection_dialog_new(_("Select Font")); + if (face && *face) + gtk_font_selection_set_font_name(GTK_FONT_SELECTION(GTK_FONT_SELECTION_DIALOG(dialog)->fontsel), + face); + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(theme_font_face_selected), + font); + gtk_widget_show_all(dialog); +} + +static void +theme_color_select(GtkWidget *widget, gpointer prop) +{ + GtkWidget *dialog; + PidginBlistTheme *theme; + const GdkColor *color = NULL; + + theme = pidgin_blist_get_theme(); + + if (prop_type_is_color(theme, prop)) { + g_object_get(G_OBJECT(theme), prop, &color, NULL); + } else { + PidginThemeFont *pair = NULL; + g_object_get(G_OBJECT(theme), prop, &pair, NULL); + if (pair) + color = pidgin_theme_font_get_color(pair); + } + + dialog = gtk_color_selection_dialog_new(_("Select Color")); + if (color) + gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel), + color); + g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(theme_color_selected), + prop); + + gtk_widget_show_all(dialog); +} + +static GtkWidget * +pidgin_theme_create_color_selector(const char *text, const char *blurb, const char *prop, + GtkSizeGroup *sizegroup) +{ + GtkWidget *color; + GtkWidget *hbox, *label; + + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + + label = gtk_label_new(_(text)); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_size_group_add_widget(sizegroup, label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_widget_set_tooltip_text(label, blurb); +#endif + + color = pidgin_pixbuf_button_from_stock("", GTK_STOCK_SELECT_COLOR, + PIDGIN_BUTTON_HORIZONTAL); + g_signal_connect(G_OBJECT(color), "clicked", G_CALLBACK(theme_color_select), + (gpointer)prop); + gtk_box_pack_start(GTK_BOX(hbox), color, FALSE, FALSE, 0); + + return hbox; +} + +static GtkWidget * +pidgin_theme_create_font_selector(const char *text, const char *blurb, const char *prop, + GtkSizeGroup *sizegroup) +{ + GtkWidget *color, *font; + GtkWidget *hbox, *label; + + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + + label = gtk_label_new(_(text)); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_size_group_add_widget(sizegroup, label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_widget_set_tooltip_text(label, blurb); +#endif + + font = pidgin_pixbuf_button_from_stock("", GTK_STOCK_SELECT_FONT, + PIDGIN_BUTTON_HORIZONTAL); + g_signal_connect(G_OBJECT(font), "clicked", G_CALLBACK(theme_font_select_face), + (gpointer)prop); + gtk_box_pack_start(GTK_BOX(hbox), font, FALSE, FALSE, 0); + + color = pidgin_pixbuf_button_from_stock("", GTK_STOCK_SELECT_COLOR, + PIDGIN_BUTTON_HORIZONTAL); + g_signal_connect(G_OBJECT(color), "clicked", G_CALLBACK(theme_color_select), + (gpointer)prop); + gtk_box_pack_start(GTK_BOX(hbox), color, FALSE, FALSE, 0); + + return hbox; +} + +static void +pidgin_blist_theme_edit(PurplePluginAction *unused) +{ + GtkWidget *dialog; + GtkWidget *box; + GtkSizeGroup *group; + PidginBlistTheme *theme; + GObjectClass *klass; + int i, j; + static struct { + const char *header; + const char *props[12]; + } sections[] = { + {N_("Contact"), { + "contact-color", + "contact", + "online", + "away", + "offline", + "idle", + "message", + "message_nick_said", + "status", + NULL + } + }, + {N_("Group"), { + "expanded-color", + "expanded-text", + "collapsed-color", + "collapsed-text", + NULL + } + }, + { NULL, { } } + }; + + dialog = pidgin_create_dialog(_("Pidgin Buddylist Theme Editor"), 0, "theme-editor-blist", FALSE); + box = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog), FALSE, PIDGIN_HIG_BOX_SPACE); + + theme = pidgin_blist_get_theme(); + if (!theme) { + theme = g_object_new(PIDGIN_TYPE_BLIST_THEME, "type", "blist", + "author", getlogin(), + NULL); + pidgin_blist_set_theme(theme); + } + klass = G_OBJECT_CLASS(PIDGIN_BLIST_THEME_GET_CLASS(theme)); + + group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + for (i = 0; sections[i].header; i++) { + GtkWidget *vbox; + GtkWidget *hbox; + GParamSpec *spec; + + vbox = pidgin_make_frame(box, _(sections[i].header)); + for (j = 0; sections[i].props[j]; j++) { + const char *label; + const char *blurb; + spec = g_object_class_find_property(klass, sections[i].props[j]); + label = g_param_spec_get_nick(spec); + blurb = g_param_spec_get_blurb(spec); + if (G_IS_PARAM_SPEC_BOXED(spec)) { + hbox = pidgin_theme_create_color_selector(label, blurb, + sections[i].props[j], group); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + } else { + hbox = pidgin_theme_create_font_selector(label, blurb, + sections[i].props[j], group); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + } + } + } + + gtk_dialog_set_has_separator(GTK_DIALOG(dialog), TRUE); +#ifdef NOT_SADRUL + pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, G_CALLBACK(save_blist_theme), dialog); +#endif + pidgin_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, G_CALLBACK(close_blist_theme), dialog); + + gtk_widget_show_all(dialog); + + g_object_unref(group); +} + +static gboolean +plugin_load(PurplePlugin *plugin) +{ + return TRUE; +} + +static GList * +actions(PurplePlugin *plugin, gpointer context) +{ + GList *l = NULL; + PurplePluginAction *act = NULL; + + act = purple_plugin_action_new(_("Edit Buddylist Theme"), pidgin_blist_theme_edit); + l = g_list_append(l, act); + act = purple_plugin_action_new(_("Edit Icon Theme"), pidgin_icon_theme_edit); + l = g_list_append(l, act); + + return l; +} + +static PurplePluginInfo info = +{ + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_STANDARD, /**< type */ + PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ + + PLUGIN_ID, /**< id */ + N_("Pidgin Theme Editor"), /**< name */ + DISPLAY_VERSION, /**< version */ + /** summary */ + N_("Pidgin Theme Editor."), + /** description */ + N_("Pidgin Theme Editor"), + "Sadrul Habib Chowdhury <imadil@gmail.com>", /**< author */ + PURPLE_WEBSITE, /**< homepage */ + + plugin_load, /**< load */ + NULL, /**< unload */ + NULL, /**< destroy */ + + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, + actions, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(themeeditor, init_plugin, info)
--- a/pidgin/plugins/ticker/gtkticker.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/ticker/gtkticker.c Tue Apr 28 19:08:06 2009 +0000 @@ -41,7 +41,7 @@ gboolean include_internals, GtkCallback callback, gpointer callback_data); -static GtkType gtk_ticker_child_type (GtkContainer *container); +static GType gtk_ticker_child_type (GtkContainer *container); static GtkContainerClass *parent_class = NULL; @@ -97,7 +97,7 @@ widget_class = (GtkWidgetClass*) class; container_class = (GtkContainerClass*) class; - parent_class = gtk_type_class (GTK_TYPE_CONTAINER); + parent_class = g_type_class_ref (GTK_TYPE_CONTAINER); gobject_class->finalize = gtk_ticker_finalize; @@ -112,7 +112,7 @@ container_class->child_type = gtk_ticker_child_type; } -static GtkType gtk_ticker_child_type (GtkContainer *container) +static GType gtk_ticker_child_type (GtkContainer *container) { return GTK_TYPE_WIDGET; }
--- a/pidgin/plugins/ticker/gtkticker.h Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/ticker/gtkticker.h Tue Apr 28 19:08:06 2009 +0000 @@ -26,19 +26,18 @@ #include <gdk/gdk.h> -#include <gtk/gtkcontainer.h> -#include <gtk/gtkmain.h> +#include <gtk/gtk.h> #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -#define GTK_TYPE_TICKER (gtk_ticker_get_type ()) -#define GTK_TICKER(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TICKER, GtkTicker)) -#define GTK_TICKER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TICKER, GtkTickerClass)) -#define GTK_IS_TICKER(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TICKER)) -#define GTK_IS_TICKER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TICKER)) +#define GTK_TYPE_TICKER (gtk_ticker_get_type()) +#define GTK_TICKER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_TICKER, GtkTicker)) +#define GTK_TICKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_TICKER, GtkTickerClass)) +#define GTK_IS_TICKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_TICKER)) +#define GTK_IS_TICKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_TICKER)) typedef struct _GtkTicker GtkTicker; @@ -73,7 +72,7 @@ }; -GtkType gtk_ticker_get_type (void); +GType gtk_ticker_get_type (void); GtkWidget* gtk_ticker_new (void); void gtk_ticker_add (GtkTicker *ticker, GtkWidget *widget);
--- a/pidgin/plugins/win32/transparency/win2ktrans.c Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/plugins/win32/transparency/win2ktrans.c Tue Apr 28 19:08:06 2009 +0000 @@ -182,7 +182,7 @@ /* On slider val change, update window's transparency level */ g_signal_connect(GTK_OBJECT(slider), "value-changed", - GTK_SIGNAL_FUNC(change_alpha), win); + G_CALLBACK(change_alpha), win); gtk_box_pack_start(GTK_BOX(hbox), slider, FALSE, TRUE, 5); @@ -563,7 +563,7 @@ button = pidgin_prefs_checkbox(_("_IM window transparency"), OPT_WINTRANS_IM_ENABLED, imtransbox); g_signal_connect(GTK_OBJECT(button), "clicked", - GTK_SIGNAL_FUNC(update_convs_wintrans), + G_CALLBACK(update_convs_wintrans), (gpointer) OPT_WINTRANS_IM_ENABLED); trans_box = gtk_vbox_new(FALSE, 18); @@ -572,12 +572,12 @@ gtk_widget_show(trans_box); g_signal_connect(GTK_OBJECT(button), "clicked", - GTK_SIGNAL_FUNC(pidgin_toggle_sensitive), trans_box); + G_CALLBACK(pidgin_toggle_sensitive), trans_box); button = pidgin_prefs_checkbox(_("_Show slider bar in IM window"), OPT_WINTRANS_IM_SLIDER, trans_box); g_signal_connect(GTK_OBJECT(button), "clicked", - GTK_SIGNAL_FUNC(update_convs_wintrans), + G_CALLBACK(update_convs_wintrans), (gpointer) OPT_WINTRANS_IM_SLIDER); button = pidgin_prefs_checkbox( @@ -587,7 +587,7 @@ button = pidgin_prefs_checkbox(_("Always on top"), OPT_WINTRANS_IM_ONTOP, trans_box); g_signal_connect(GTK_OBJECT(button), "clicked", - GTK_SIGNAL_FUNC(update_convs_wintrans), + G_CALLBACK(update_convs_wintrans), (gpointer) OPT_WINTRANS_IM_ONTOP); gtk_box_pack_start(GTK_BOX(imtransbox), trans_box, FALSE, FALSE, 5); @@ -604,9 +604,9 @@ gtk_widget_set_usize(GTK_WIDGET(slider), 200, -1); g_signal_connect(GTK_OBJECT(slider), "value-changed", - GTK_SIGNAL_FUNC(alpha_change), NULL); + G_CALLBACK(alpha_change), NULL); g_signal_connect(GTK_OBJECT(slider), "focus-out-event", - GTK_SIGNAL_FUNC(alpha_pref_set_int), + G_CALLBACK(alpha_pref_set_int), (gpointer) OPT_WINTRANS_IM_ALPHA); gtk_box_pack_start(GTK_BOX(hbox), slider, FALSE, TRUE, 5); @@ -620,7 +620,7 @@ button = pidgin_prefs_checkbox(_("_Buddy List window transparency"), OPT_WINTRANS_BL_ENABLED, bltransbox); g_signal_connect(GTK_OBJECT(button), "clicked", - GTK_SIGNAL_FUNC(set_blist_trans), + G_CALLBACK(set_blist_trans), (gpointer) OPT_WINTRANS_BL_ENABLED); trans_box = gtk_vbox_new(FALSE, 18); @@ -628,14 +628,14 @@ gtk_widget_set_sensitive(GTK_WIDGET(trans_box), FALSE); gtk_widget_show(trans_box); g_signal_connect(GTK_OBJECT(button), "clicked", - GTK_SIGNAL_FUNC(pidgin_toggle_sensitive), trans_box); + G_CALLBACK(pidgin_toggle_sensitive), trans_box); button = pidgin_prefs_checkbox( _("Remove Buddy List window transparency on focus"), OPT_WINTRANS_BL_ONFOCUS, trans_box); button = pidgin_prefs_checkbox(_("Always on top"), OPT_WINTRANS_BL_ONTOP, trans_box); g_signal_connect(GTK_OBJECT(button), "clicked", - GTK_SIGNAL_FUNC(set_blist_trans), + G_CALLBACK(set_blist_trans), (gpointer) OPT_WINTRANS_BL_ONTOP); gtk_box_pack_start(GTK_BOX(bltransbox), trans_box, FALSE, FALSE, 5); @@ -652,9 +652,9 @@ gtk_widget_set_usize(GTK_WIDGET(slider), 200, -1); g_signal_connect(GTK_OBJECT(slider), "value-changed", - GTK_SIGNAL_FUNC(bl_alpha_change), NULL); + G_CALLBACK(bl_alpha_change), NULL); g_signal_connect(GTK_OBJECT(slider), "focus-out-event", - GTK_SIGNAL_FUNC(alpha_pref_set_int), + G_CALLBACK(alpha_pref_set_int), (gpointer) OPT_WINTRANS_BL_ALPHA); gtk_box_pack_start(GTK_BOX(hbox), slider, FALSE, TRUE, 5);
--- a/pidgin/win32/nsis/pidgin-installer.nsi Tue Apr 28 19:05:59 2009 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Tue Apr 28 19:08:06 2009 +0000 @@ -813,6 +813,7 @@ ; Shortcuts.. Delete "$DESKTOP\Pidgin.lnk" + Delete "$SMPROGRAMS\Pidgin.lnk" Goto done
--- a/po/POTFILES.in Tue Apr 28 19:05:59 2009 +0000 +++ b/po/POTFILES.in Tue Apr 28 19:08:06 2009 +0000 @@ -37,6 +37,7 @@ finch/plugins/gnthistory.c finch/plugins/grouping.c finch/plugins/lastlog.c +finch/plugins/gnttinyurl.c libpurple/account.c libpurple/blist.c libpurple/certificate.c @@ -85,10 +86,10 @@ libpurple/protocols/irc/parse.c libpurple/protocols/jabber/adhoccommands.c libpurple/protocols/jabber/auth.c +libpurple/protocols/jabber/bosh.c libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/chat.c libpurple/protocols/jabber/jabber.c -libpurple/protocols/jabber/jingle.c libpurple/protocols/jabber/libxmpp.c libpurple/protocols/jabber/message.c libpurple/protocols/jabber/parser.c
--- a/po/de.po Tue Apr 28 19:05:59 2009 +0000 +++ b/po/de.po Tue Apr 28 19:08:06 2009 +0000 @@ -11,10 +11,10 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-03-28 16:59+0100\n" -"PO-Revision-Date: 2009-03-28 16:53+0100\n" +"POT-Creation-Date: 2009-04-26 12:11+0200\n" +"PO-Revision-Date: 2009-04-26 12:11+0200\n" "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n" -"Language-Team: German <de@li.org>\n" +"Language-Team: Deutsch <de@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -614,19 +614,6 @@ msgid "Send To" msgstr "Senden an" -msgid "Invite message" -msgstr "Einladungsnachricht" - -msgid "Invite" -msgstr "Einladen" - -msgid "" -"Please enter the name of the user you wish to invite,\n" -"along with an optional invite message." -msgstr "" -"Bitte geben Sie den Benutzernamen der Person ein, die Sie einladen möchten " -"zusammen mit einer optionalen Einladungsnachricht." - msgid "Conversation" msgstr "Unterhaltung" @@ -889,6 +876,41 @@ msgid "System Log" msgstr "System-Mitschnitt" +msgid "Calling ... " +msgstr "Anrufen..." + +msgid "Hangup" +msgstr "Auflegen" + +#. Number of actions +msgid "Accept" +msgstr "Akzeptieren" + +msgid "Reject" +msgstr "Ablehnen" + +msgid "Call in progress." +msgstr "Anruf läuft." + +msgid "The call has been terminated." +msgstr "Der Anruf wurde beendet." + +#, c-format +msgid "%s wishes to start an audio session with you." +msgstr "%s möchte eine Audio-Sitzung mit Ihnen starten." + +#, c-format +msgid "%s is trying to start an unsupported media session type with you." +msgstr "" +"%s versucht, einen nicht unterstützten Typ von Medien-Sitzung mit Ihnen zu " +"starten." + +msgid "You have rejected the call." +msgstr "Sie haben den Anruf abgelehnt." + +msgid "call: Make an audio call." +msgstr "call: Einen Audio-Anruf tätigen." + msgid "Emails" msgstr "E-Mails" @@ -923,6 +945,9 @@ msgid "IM" msgstr "Nachricht" +msgid "Invite" +msgstr "Einladen" + msgid "(none)" msgstr "(kein)" @@ -1092,7 +1117,7 @@ #, c-format msgid "%s has paused while typing to you (%s)" -msgstr "%s hat beim Schreiben an Sie (%s) angehalten" +msgstr "%s hat beim Tippen an Sie (%s) angehalten" #, c-format msgid "%s has signed on (%s)" @@ -1537,6 +1562,30 @@ msgid "Lastlog plugin." msgstr "Verlauf-Plugin." +msgid "" +"\n" +"Fetching TinyURL..." +msgstr "" +"\n" +"Hole TinyURL..." + +msgid "Only create TinyURL for urls of this length or greater" +msgstr "TinyURL nur für URLs mit mindestens dieser Länge generieren" + +msgid "TinyURL (or other) address prefix" +msgstr "" + +msgid "TinyURL" +msgstr "TinyURL" + +msgid "TinyURL plugin" +msgstr "TinyURL-Plugin" + +msgid "When receiving a message with URL(s), TinyURL for easier copying" +msgstr "" +"URLs aus erhaltenen Nachrichten zum einfacheren Kopieren in TinyURLs " +"umwandeln" + msgid "accounts" msgstr "Konten" @@ -1638,13 +1687,6 @@ msgid "SSL Certificate Verification" msgstr "SSL-Zertifikatsüberprüfung" -#. Number of actions -msgid "Accept" -msgstr "Akzeptieren" - -msgid "Reject" -msgstr "Ablehnen" - msgid "_View Certificate..." msgstr "Ze_rtifikat ansehen..." @@ -1792,6 +1834,17 @@ msgid "%s left the room (%s)." msgstr "%s hat den Raum verlassen (%s)." +msgid "Invite to chat" +msgstr "Zum Chat einladen" + +#. Put our happy label in it. +msgid "" +"Please enter the name of the user you wish to invite, along with an optional " +"invite message." +msgstr "" +"Bitte geben Sie den Benutzernamen der Person ein, die Sie einladen möchten " +"zusammen mit einer optionalen Einladungsnachricht." + #, c-format msgid "Failed to get connection: %s" msgstr "Kann keine Verbindung herstellen: %s" @@ -2631,7 +2684,7 @@ msgstr "Nicht nachfragen. Immer als Alarm sichern." msgid "One Time Password" -msgstr "" +msgstr "Einmalpasswort" #. *< type #. *< ui_requirement @@ -2640,13 +2693,13 @@ #. *< priority #. *< id msgid "One Time Password Support" -msgstr "" +msgstr "Unterstützung für Einmalpasswörter" #. *< name #. *< version #. * summary msgid "Enforce that passwords are used only once." -msgstr "" +msgstr "Erzwinge, dass Passwörter nur einmal verwendet werden." #. * description msgid "" @@ -3056,6 +3109,7 @@ msgid "Add to chat..." msgstr "Zum Chat hinzufügen..." +#. Global msgid "Available" msgstr "Verfügbar" @@ -3402,6 +3456,17 @@ "Ihr gewählter Kontoname wurde vom Server abgelehnt. Er enthält vermutlich " "ungültige Zeichen." +#. We only want to do the following dance if the connection +#. has not been successfully completed. If it has, just +#. notify the user that their /nick command didn't go. +#, c-format +msgid "The nickname \"%s\" is already being used." +msgstr "Der Spitzname \"%s\" existiert bereits." + +#, fuzzy +msgid "Nickname in use" +msgstr "Spitzname" + msgid "Cannot change nick" msgstr "Kann den Spitznamen nicht ändern" @@ -3748,6 +3813,9 @@ msgid "Operating System" msgstr "Betriebssystem" +msgid "Local Time" +msgstr "Lokale Zeit" + msgid "Last Activity" msgstr "Letzte Aktivität" @@ -4493,6 +4561,39 @@ msgid "%s has buzzed you!" msgstr "%s hat bei Ihnen angeklopft!" +#, c-format +msgid "Unable to initiate media with %s: invalid JID" +msgstr "Medien-Sitzung mit %s konnte nicht gestartet werden: ungültige JID" + +#, c-format +msgid "Unable to initiate media with %s: user is not online" +msgstr "" +"Medien-Sitzung mit %s konnte nicht gestartet werden: Benutzer ist nicht " +"online" + +#, c-format +msgid "Unable to initiate media with %s: not subscribed to user presence" +msgstr "" +"Medien-Sitzung mit %s konnte nicht gestartet werden: Anwesenheit des " +"Benutzers nicht abonniert" + +msgid "Media Initiation Failed" +msgstr "Medien-Initiierung fehlgeschlagen" + +#, c-format +msgid "" +"Please select the resource of %s with which you would like to start a media " +"session." +msgstr "" +"Bitte wählen Sie die Ressource von %s, mir der Sie eine Medien-Sitzung " +"starten möchten" + +msgid "Select a Resource" +msgstr "Wählen Sie eine Ressource" + +msgid "Initiate Media" +msgstr "Initiiere Medien" + msgid "config: Configure a chat room." msgstr "config: Konfiguriere einen Chatraum." @@ -4687,9 +4788,6 @@ msgstr "" "Bitte wählen Sie die Ressource von %s, an die Sie eine Datei schicken möchten" -msgid "Select a Resource" -msgstr "Wählen Sie eine Ressource" - msgid "Edit User Mood" msgstr "Benutzerstimmung ändern" @@ -7308,13 +7406,11 @@ msgid "Change his/her memo as you like" msgstr "" -#, fuzzy msgid "_Modify" -msgstr "Bearbeiten" - -#, fuzzy +msgstr "_Bearbeiten" + msgid "Memo Modify" -msgstr "Bearbeiten" +msgstr "Memo bearbeiten" msgid "Server says:" msgstr "Server meldet:" @@ -10345,6 +10441,16 @@ msgid "I_M" msgstr "I_M" +#, fuzzy +msgid "_Audio Call" +msgstr "Chat _hinzufügen" + +msgid "Audio/_Video Call" +msgstr "Audio/_Video-Anruf" + +msgid "_Video Call" +msgstr "_Video-Anruf" + msgid "_Send File..." msgstr "_Datei versenden..." @@ -10775,14 +10881,6 @@ msgid "Invite Buddy Into Chat Room" msgstr "Buddy in einen Chatraum einladen" -#. Put our happy label in it. -msgid "" -"Please enter the name of the user you wish to invite, along with an optional " -"invite message." -msgstr "" -"Bitte geben Sie den Benutzernamen der Person ein, die Sie einladen möchten " -"zusammen mit einer optionalen Einladungsnachricht." - msgid "_Buddy:" msgstr "_Buddy:" @@ -10857,6 +10955,18 @@ msgid "/Conversation/Clea_r Scrollback" msgstr "/Unterhaltung/_Leeren" +msgid "/Conversation/M_edia" +msgstr "/Unterhaltung/M_edien" + +msgid "/Conversation/Media/_Audio Call" +msgstr "/Unterhaltung/Medien/_Audio-Anruf" + +msgid "/Conversation/Media/_Video Call" +msgstr "/Unterhaltung/Medien/_Video-Anruf" + +msgid "/Conversation/Media/Audio\\/Video _Call" +msgstr "/Unterhaltung/Medien/A_udio-\\/Video-Anruf" + msgid "/Conversation/Se_nd File..." msgstr "/Unterhaltung/Datei _senden..." @@ -10929,6 +11039,15 @@ msgid "/Conversation/View Log" msgstr "/Unterhaltung/Betrachte Mitschnitt" +msgid "/Conversation/Media/Audio Call" +msgstr "/Unterhaltung/Medien/Audio-Anruf" + +msgid "/Conversation/Media/Video Call" +msgstr "/Unterhaltung/Medien/Video-Anruf" + +msgid "/Conversation/Media/Audio\\/Video Call" +msgstr "/Unterhaltung/Medien/Audio-\\/Video-Anruf" + msgid "/Conversation/Send File..." msgstr "/Unterhaltung/Datei senden ..." @@ -12073,6 +12192,23 @@ msgid "Exiting because another libpurple client is already running.\n" msgstr "Wird geschlossen, da bereits ein anderer libpurple-Client läuft\n" +msgid "/_Media" +msgstr "/_Medien" + +msgid "/Media/_Hangup" +msgstr "/Medien/_Auflegen" + +msgid "Calling..." +msgstr "Anrufen..." + +#, c-format +msgid "%s wishes to start an audio/video session with you." +msgstr "%s möchte eine Audio-/Video-Sitzung mit Ihnen starten." + +#, c-format +msgid "%s wishes to start a video session with you." +msgstr "%s möchte eine Video-Sitzung mit Ihnen starten." + #, c-format msgid "%s has %d new message." msgid_plural "%s has %d new messages." @@ -12240,43 +12376,33 @@ msgid "Pounce Target" msgstr "Alarm-Ziel" -#, fuzzy msgid "Started typing" -msgstr "zu tippen beginnt" - -#, fuzzy +msgstr "Beginnt zu tippen" + msgid "Paused while typing" -msgstr "beim Tippen anhält" - -#, fuzzy +msgstr "Hat beim Tippen angehalten" + msgid "Signed on" -msgstr "sich anmeldet" - -#, fuzzy +msgstr "Hat sich anmeldet" + msgid "Returned from being idle" -msgstr "%s ist nicht mehr inaktiv (%s)" - -#, fuzzy +msgstr "Ist nicht mehr inaktiv" + msgid "Returned from being away" -msgstr "wieder anwesend ist" - -#, fuzzy +msgstr "Ist wieder anwesend" + msgid "Stopped typing" -msgstr "Tippen gestoppt" - -#, fuzzy +msgstr "Hat das Tippen gestoppt" + msgid "Signed off" -msgstr "sich abmeldet" - -#, fuzzy +msgstr "Hat sich abmeldet" + msgid "Became idle" -msgstr "untätig wird" - -#, fuzzy +msgstr "Wurde untätig" + msgid "Went away" -msgstr "Bei Abwesenheit" - -#, fuzzy +msgstr "Ging hinaus" + msgid "Sent a message" msgstr "Eine Nachricht senden" @@ -12420,9 +12546,6 @@ msgid "Cannot start browser configuration program." msgstr "Kann das Browser-Konfigurationsprogramm nicht starten." -msgid "ST_UN server:" -msgstr "ST_UN Server:" - msgid "<span style=\"italic\">Example: stunserver.org</span>" msgstr "<span style=\"italic\">Beispiel: stunserver.org</span>" @@ -12447,6 +12570,10 @@ msgid "_End port:" msgstr "_End-Port:" +#. TURN server +msgid "Relay Server (TURN)" +msgstr "Relay-Server (TURN)" + msgid "Proxy Server & Browser" msgstr "Proxy-Server & Browser" @@ -12477,7 +12604,7 @@ #. This is a global option that affects SOCKS4 usage even with account-specific proxy settings msgid "Use remote DNS with SOCKS4 proxies" -msgstr "Remote-DNS mit SOCKS4-Proxys benuten" +msgstr "Remote-DNS mit SOCKS4-Proxys benutzen" msgid "_User:" msgstr "_Benutzer:" @@ -12831,16 +12958,14 @@ msgstr "_Bild:" #. Shortcut text -#, fuzzy msgid "S_hortcut text:" -msgstr "Tastenkombination" +msgstr "_Verknüpfter Text:" msgid "Smiley" msgstr "Smiley" -#, fuzzy msgid "Shortcut Text" -msgstr "Tastenkombination" +msgstr "Verknüpfter Text" msgid "Custom Smiley Manager" msgstr "Verwaltung für benutzerdefinierte Smileys" @@ -14003,3 +14128,16 @@ msgid "This plugin is useful for debbuging XMPP servers or clients." msgstr "" "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients." + +#~ msgid "Invite message" +#~ msgstr "Einladungsnachricht" + +#~ msgid "" +#~ "Please enter the name of the user you wish to invite,\n" +#~ "along with an optional invite message." +#~ msgstr "" +#~ "Bitte geben Sie den Benutzernamen der Person ein, die Sie einladen " +#~ "möchten zusammen mit einer optionalen Einladungsnachricht." + +#~ msgid "ST_UN server:" +#~ msgstr "ST_UN Server:"