Mercurial > pidgin.yaz
changeset 27760:e9cf897bd873
propagate from branch 'im.pidgin.pidgin' (head ae7f8e3acb446776f833c3b44514295ae56184b3)
to branch 'im.pidgin.pidgin.yaz' (head 952151791a1f3b4e3469a5621f4cfa2de608a8b8)
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Wed, 30 Jan 2008 10:53:22 +0000 |
parents | 54479172725e (current diff) ae5917260eac (diff) |
children | 329c9cc323fb |
files | autogen.sh libpurple/protocols/irc/irc.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/si.c libpurple/protocols/msn/msn.c libpurple/protocols/oscar/oscar.c libpurple/protocols/yahoo/util.c libpurple/protocols/yahoo/yahoo.c libpurple/protocols/yahoo/yahoo_filexfer.c libpurple/util.c pidgin/gtkconv.c pidgin/gtkimhtml.c pidgin/gtkimhtmltoolbar.c pidgin/gtkmain.c pidgin/gtkprefs.c pidgin/gtkutils.c pidgin/gtkutils.h |
diffstat | 120 files changed, 3554 insertions(+), 1220 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Thu Jan 10 05:32:41 2008 +0000 +++ b/COPYRIGHT Wed Jan 30 10:53:22 2008 +0000 @@ -270,6 +270,7 @@ Padraig O'Briain Christopher O'Brien (siege) Jon Oberheide +Yusuke Odate Ruediger Oertel Gudmundur Bjarni Olafsson Bartosz Oler @@ -322,6 +323,7 @@ Jean-Francois Roy Peter Ruibal Sam S. +Thanumalayan S. Pradyumna Sampath Arvind Samptur Tom Samstag @@ -370,6 +372,7 @@ Richard Stellingwerff Charlie Stockman David Stoddard +Andreas Stührk Oleg Sukhodolsky Sun Microsystems Mårten Svantesson (fursten) @@ -425,6 +428,7 @@ Andrew Whewell Simon Wilkinson Dan Willemsen +Justin Williams (Jaywalker) Jason Willis Matt Wilson Dan Winship
--- a/ChangeLog Thu Jan 10 05:32:41 2008 +0000 +++ b/ChangeLog Wed Jan 30 10:53:22 2008 +0000 @@ -9,6 +9,13 @@ now required to use Bonjour. * Partial support for viewing ICQ status notes (Collin from ComBOTS GmbH). + * Support for /notice on IRC. + * Support for Yahoo Messenger 7.0+ file transfer method (Thanumalayan S.) + * Support for retrieving full names and addresses from the address book + on Yahoo! Japan (Yusuke Odate) + * The AIM/ICQ server-side preference for "allow others to see me + as idle" is no longer unconditionally set to "yes" even when + your libpurple preference is "no." Pidgin: * Added the ability to theme conversation name colors (red and blue) @@ -22,15 +29,18 @@ Finch: * Color is used in the buddylist to indicate status, and the conversation window to indicate various message attributes. Look at the sample gntrc - file in the man-page for details. + file in the man page for details. * The default keybinding for dump-screen is now M-D and uses a file request dialog. M-d will properly delete-forward-word, and M-f has been fixed to imitate readline's behavior. * New bindings alt+tab and alt+shift+tab to help navigating between the - higlighted windows (details on the man-page). + higlighted windows (details on the man page). * Recently signed on (or off) buddies blink in the buddy list. - * New action 'Room List' in the action-list can be used to get the list of + * New action 'Room List' in the action list can be used to get the list of available chat rooms for an online account. + * The 'Grouping' plugin can be used for alternate grouping in the + buddylist. The current options are 'Group Online/Offline' and 'No + Group'. version 2.3.1 (12/7/2007): http://developer.pidgin.im/query?status=closed&milestone=2.3.1
--- a/ChangeLog.API Thu Jan 10 05:32:41 2008 +0000 +++ b/ChangeLog.API Wed Jan 30 10:53:22 2008 +0000 @@ -18,6 +18,31 @@ * purple_roomlist_field_get_type * purple_roomlist_field_get_label * purple_roomlist_field_get_hidden + * unlocalized_name field in PurpleAttentionType for UIs that need it. + * Some accessor and mutator functions for PurpleAttentionType: + * purple_attention_type_set_name + * purple_attention_type_set_incoming_desc + * purple_attention_type_set_outgoing_desc + * purple_attention_type_set_icon_name + * purple_attention_type_set_unlocalized_name + * purple_attention_type_get_name + * purple_attention_type_get_incoming_desc + * purple_attention_type_get_outgoing_desc + * purple_attention_type_get_icon_name + * purple_attention_type_get_unlocalized_name + * Add some PurpleBuddyListNode accessor functions: + * purple_blist_node_get_parent + * purple_blist_node_get_first_child + * purple_blist_node_get_sibling_next + * purple_blist_node_get_sibling_prev + * Added last_received to PurpleConnection, the time_t of the + last received packet. + * Added some more accessor functions: + * purple_chat_get_account + * purple_chat_get_components + * purple_connection_get_prpl + * purple_xfer_get_start_time + * purple_xfer_get_end_time Pidgin: Added: @@ -44,6 +69,12 @@ * finch_roomlist_get_ui_ops and finch_roomlist_show_all * finch_request_field_get_widget to get the widget for a request field. + * finch_blist_get_tree to get the GntTree widget representing the + buddy list. + * FinchBlistManager structure to manage the buddylist view, and some + util functions finch_blist_install_manager, + finch_blist_uninstall_manager, finch_blist_manager_find and + finch_blist_manager_add_node. libgnt: * Added gnt_tree_set_row_color to set the color for a row in a tree. @@ -53,6 +84,7 @@ string. * Added gnt_style_get_color to get a color pair from an entry in ~/.gntrc + * Added gnt_tree_get_parent_key to get the key for the parent row. version 2.3.0 (11/24/2007): libpurple:
--- a/autogen.sh Thu Jan 10 05:32:41 2008 +0000 +++ b/autogen.sh Wed Jan 30 10:53:22 2008 +0000 @@ -9,42 +9,42 @@ echo; echo "You must have glib-gettextize installed to compile Pidgin."; echo; - exit; + exit 1; } (intltoolize --version) < /dev/null > /dev/null 2>&1 || { echo; echo "You must have intltool installed to compile Pidgin."; echo; - exit; + exit 1; } (libtoolize --version) < /dev/null > /dev/null 2>&1 || { echo; echo "You must have libtool installed to compile Pidgin."; echo; - exit; + exit 1; } (automake --version) < /dev/null > /dev/null 2>&1 || { echo; echo "You must have automake installed to compile Pidgin."; echo; - exit; + exit 1; } (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo; echo "You must have autoconf installed to compile Pidgin."; echo; - exit; + exit 1; } echo "Generating configuration files for Pidgin, please wait...." echo; echo "Running libtoolize, please ignore non-fatal messages...." -echo n | libtoolize --copy --force || exit; +echo n | libtoolize --copy --force || exit 1; # Add other directories to this list if people continue to experience # brokennesses ... Obviously the real answer is for them to fix it @@ -60,11 +60,11 @@ libtoolize -c -f --automake glib-gettextize --force --copy intltoolize --force --copy -aclocal $ACLOCAL_FLAGS || exit; -autoheader || exit; +aclocal $ACLOCAL_FLAGS || exit 1; +autoheader || exit 1; automake --add-missing --copy; -autoconf || exit; -automake || exit; +autoconf || exit 1; +automake || exit 1; echo; echo "Running ./configure ${CONFIGURE_ARGS} $@"
--- a/finch/finch.h Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/finch.h Wed Jan 30 10:53:22 2008 +0000 @@ -29,3 +29,7 @@ #define FINCH_UI "gnt-purple" #define FINCH_PREFS_ROOT "/finch" + +#define FINCH_GET_DATA(obj) (obj)->ui_data +#define FINCH_SET_DATA(obj, data) (obj)->ui_data = data +
--- a/finch/gntaccount.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntaccount.c Wed Jan 30 10:53:22 2008 +0000 @@ -686,7 +686,7 @@ } g_signal_connect(G_OBJECT(accounts.tree), "toggled", G_CALLBACK(account_toggled), NULL); - + gnt_tree_set_col_width(GNT_TREE(accounts.tree), 0, 40); gnt_tree_set_col_width(GNT_TREE(accounts.tree), 1, 10); gnt_box_add_widget(GNT_BOX(accounts.window), accounts.tree); @@ -708,11 +708,11 @@ gnt_box_add_widget(GNT_BOX(box), button); gnt_util_set_trigger_widget(GNT_WIDGET(accounts.tree), GNT_KEY_DEL, button); g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(delete_account_cb), accounts.tree); - + gnt_box_add_widget(GNT_BOX(accounts.window), box); g_signal_connect(G_OBJECT(accounts.window), "destroy", G_CALLBACK(reset_accounts_win), NULL); - + gnt_widget_show(accounts.window); } @@ -981,7 +981,7 @@ gnt_box_add_widget(GNT_BOX(uihandle), gnt_hline_new()); - widget = finch_retrieve_user_info(account->gc, remote_user); + widget = finch_retrieve_user_info(purple_account_get_connection(account), remote_user); for (iter = GNT_BOX(widget)->list; iter; iter = iter->next) { if (GNT_IS_BUTTON(iter->data)) { gnt_widget_destroy(iter->data);
--- a/finch/gntblist.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntblist.c Wed Jan 30 10:53:22 2008 +0000 @@ -81,6 +81,9 @@ /* These are the menuitems that get regenerated */ GntMenuItem *accounts; GntMenuItem *plugins; + GntMenuItem *grouping; + + FinchBlistManager *manager; } FinchBlist; typedef struct @@ -115,7 +118,12 @@ static void add_chat(PurpleChat *chat, FinchBlist *ggblist); static void add_node(PurpleBlistNode *node, FinchBlist *ggblist); static void node_update(PurpleBuddyList *list, PurpleBlistNode *node); +#if 0 +static gboolean is_contact_online(PurpleContact *contact); +static gboolean is_group_online(PurpleGroup *group); +#endif static void draw_tooltip(FinchBlist *ggblist); +static void tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full); static gboolean remove_typing_cb(gpointer null); static void remove_peripherals(FinchBlist *ggblist); static const char * get_display_name(PurpleBlistNode *node); @@ -125,6 +133,7 @@ static void update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist); static void account_signed_on_cb(PurpleConnection *pc, gpointer null); static void finch_request_add_buddy(PurpleAccount *account, const char *username, const char *grp, const char *alias); +static void menu_group_set_cb(GntMenuItem *item, gpointer null); /* Sort functions */ static int blist_node_compare_position(PurpleBlistNode *n1, PurpleBlistNode *n2); @@ -137,14 +146,178 @@ static int color_offline; static int color_idle; +/** + * Buddy List Manager functions. + */ + +static gboolean default_can_add_node(PurpleBlistNode *node) +{ + gboolean offline = purple_prefs_get_bool(PREF_ROOT "/showoffline"); + + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { + PurpleBuddy *buddy = (PurpleBuddy*)node; + FinchBlistNode *fnode = FINCH_GET_DATA(node); + if (!purple_buddy_get_contact(buddy)) + return FALSE; /* When a new buddy is added and show-offline is set */ + if (PURPLE_BUDDY_IS_ONLINE(buddy)) + return TRUE; /* The buddy is online */ + if (!purple_account_is_connected(purple_buddy_get_account(buddy))) + return FALSE; /* The account is disconnected. Do not show */ + if (offline) + return TRUE; /* We want to see offline buddies too */ + if (fnode && fnode->signed_timer) + return TRUE; /* Show if the buddy just signed off */ + if (purple_blist_node_get_bool(node, "show_offline")) + return TRUE; + } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleBlistNode *nd; + for (nd = purple_blist_node_get_first_child(node); + nd; nd = purple_blist_node_get_sibling_next(nd)) { + if (default_can_add_node(nd)) + return TRUE; + } + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { + PurpleChat *chat = (PurpleChat*)node; + if (purple_account_is_connected(purple_chat_get_account(chat))) + return TRUE; /* Show whenever the account is online */ + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + PurpleBlistNode *nd; + gboolean empty = purple_prefs_get_bool(PREF_ROOT "/emptygroups"); + if (empty) + return TRUE; /* If we want to see empty groups, we can show any group */ + + for (nd = purple_blist_node_get_first_child(node); + nd; nd = purple_blist_node_get_sibling_next(nd)) { + if (default_can_add_node(nd)) + return TRUE; + } + } + + return FALSE; +} + +static gpointer default_find_parent(PurpleBlistNode *node) +{ + gpointer ret = NULL; + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_BUDDY_NODE: + case PURPLE_BLIST_CONTACT_NODE: + case PURPLE_BLIST_CHAT_NODE: + ret = purple_blist_node_get_parent(node); + break; + default: + break; + } + if (ret) + add_node(ret, ggblist); + return ret; +} + +static gboolean default_create_tooltip(gpointer selected_row, GString **body, char **tool_title) +{ + GString *str; + PurpleBlistNode *node = selected_row; + int lastseen = 0; + char *title; + + if (!node || + purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE) + return FALSE; + + str = g_string_new(""); + + if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleBuddy *pr = purple_contact_get_priority_buddy((PurpleContact*)node); + gboolean offline = !PURPLE_BUDDY_IS_ONLINE(pr); + gboolean showoffline = purple_prefs_get_bool(PREF_ROOT "/showoffline"); + const char *name = purple_buddy_get_name(pr); + + title = g_strdup(name); + tooltip_for_buddy(pr, str, TRUE); + for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) { + PurpleBuddy *buddy = (PurpleBuddy*)node; + if (offline) { + int value = purple_blist_node_get_int(node, "last_seen"); + if (value > lastseen) + lastseen = value; + } + if (node == (PurpleBlistNode*)pr) + continue; + if (!purple_account_is_connected(purple_buddy_get_account(buddy))) + continue; + if (!showoffline && !PURPLE_BUDDY_IS_ONLINE(buddy)) + continue; + str = g_string_append(str, "\n----------\n"); + tooltip_for_buddy(buddy, str, FALSE); + } + } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { + PurpleBuddy *buddy = (PurpleBuddy *)node; + tooltip_for_buddy(buddy, str, TRUE); + title = g_strdup(purple_buddy_get_name(buddy)); + if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node)) + lastseen = purple_blist_node_get_int(node, "last_seen"); + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + PurpleGroup *group = (PurpleGroup *)node; + + g_string_append_printf(str, _("Online: %d\nTotal: %d"), + purple_blist_get_group_online_count(group), + purple_blist_get_group_size(group, FALSE)); + + title = g_strdup(purple_group_get_name(group)); + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { + PurpleChat *chat = (PurpleChat *)node; + PurpleAccount *account = purple_chat_get_account(chat); + + g_string_append_printf(str, _("Account: %s (%s)"), + purple_account_get_username(account), + purple_account_get_protocol_name(account)); + + title = g_strdup(purple_chat_get_name(chat)); + } else { + g_string_free(str, TRUE); + return FALSE; + } + + if (lastseen > 0) { + char *tmp = purple_str_seconds_to_string(time(NULL) - lastseen); + g_string_append_printf(str, _("\nLast Seen: %s ago"), tmp); + g_free(tmp); + } + + if (tool_title) + *tool_title = title; + else + g_free(title); + + if (body) + *body = str; + else + g_string_free(str, TRUE); + + return TRUE; +} + +static FinchBlistManager default_manager = +{ + "default", + N_("Default"), + NULL, + NULL, + default_can_add_node, + default_find_parent, + default_create_tooltip, + {NULL, NULL, NULL, NULL} +}; +static GList *managers; + static FinchBlistNode * create_finch_blist_node(PurpleBlistNode *node, gpointer row) { - FinchBlistNode *fnode = node->ui_data; + FinchBlistNode *fnode = FINCH_GET_DATA(node); if (!fnode) { fnode = g_new0(FinchBlistNode, 1); fnode->signed_timer = 0; - node->ui_data = fnode; + FINCH_SET_DATA(node, fnode); } fnode->row = row; return fnode; @@ -153,13 +326,13 @@ static void reset_blist_node_ui_data(PurpleBlistNode *node) { - FinchBlistNode *fnode = node->ui_data; + FinchBlistNode *fnode = FINCH_GET_DATA(node); if (fnode == NULL) return; if (fnode->signed_timer) purple_timeout_remove(fnode->signed_timer); g_free(fnode); - node->ui_data = NULL; + FINCH_SET_DATA(node, NULL); } static int @@ -192,7 +365,7 @@ get_blist_node_flag(PurpleBlistNode *node) { GntTextFormatFlags flag = 0; - FinchBlistNode *fnode = node->ui_data; + FinchBlistNode *fnode = FINCH_GET_DATA(node); if (ggblist->tagged && g_list_find(ggblist->tagged, node)) flag |= GNT_TEXT_FLAG_BOLD; @@ -201,7 +374,7 @@ flag |= GNT_TEXT_FLAG_BLINK; else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact *)node); - fnode = node->ui_data; + fnode = FINCH_GET_DATA(node); if (fnode && fnode->signed_timer) flag |= GNT_TEXT_FLAG_BLINK; } @@ -216,12 +389,16 @@ gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node)); } +#if 0 static gboolean is_contact_online(PurpleContact *contact) { PurpleBlistNode *node; - for (node = ((PurpleBlistNode*)contact)->child; node; node = node->next) { - if (PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node)) + for (node = purple_blist_node_get_first_child(((PurpleBlistNode*)contact)); node; + node = purple_blist_node_get_sibling_next(node)) { + FinchBlistNode *fnode = FINCH_GET_DATA(node); + if (PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node) || + (fnode && fnode->signed_timer)) return TRUE; } return FALSE; @@ -231,7 +408,8 @@ is_group_online(PurpleGroup *group) { PurpleBlistNode *node; - for (node = ((PurpleBlistNode*)group)->child; node; node = node->next) { + for (node = purple_blist_node_get_first_child(((PurpleBlistNode*)group)); node; + node = purple_blist_node_get_sibling_next(node)) { if (PURPLE_BLIST_NODE_IS_CHAT(node) && purple_account_is_connected(((PurpleChat *)node)->account)) return TRUE; @@ -240,14 +418,22 @@ } return FALSE; } +#endif static void new_node(PurpleBlistNode *node) { } -static void add_node(PurpleBlistNode *node, FinchBlist *ggblist) +static void +add_node(PurpleBlistNode *node, FinchBlist *ggblist) { + if (FINCH_GET_DATA(node)) + return; + + if (!ggblist->manager->can_add_node(node)) + return; + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) add_buddy((PurpleBuddy*)node, ggblist); else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) @@ -256,9 +442,15 @@ add_group((PurpleGroup*)node, ggblist); else if (PURPLE_BLIST_NODE_IS_CHAT(node)) add_chat((PurpleChat *)node, ggblist); + draw_tooltip(ggblist); } +void finch_blist_manager_add_node(PurpleBlistNode *node) +{ + add_node(node, ggblist); +} + static void remove_tooltip(FinchBlist *ggblist) { @@ -270,9 +462,10 @@ static void node_remove(PurpleBuddyList *list, PurpleBlistNode *node) { - FinchBlist *ggblist = list->ui_data; - - if (ggblist == NULL || node->ui_data == NULL) + FinchBlist *ggblist = FINCH_GET_DATA(list); + PurpleBlistNode *parent; + + if (ggblist == NULL || FINCH_GET_DATA(node)== NULL) return; gnt_tree_remove(GNT_TREE(ggblist->tree), node); @@ -280,23 +473,16 @@ if (ggblist->tagged) ggblist->tagged = g_list_remove(ggblist->tagged, node); - if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { - PurpleContact *contact = (PurpleContact*)node->parent; - if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) || - contact->currentsize < 1) - node_remove(list, (PurpleBlistNode*)contact); + parent = purple_blist_node_get_parent(node); + for (node = purple_blist_node_get_first_child(node); node; + node = purple_blist_node_get_sibling_next(node)) + node_remove(list, node); + + if (parent) { + if (!ggblist->manager->can_add_node(parent)) + node_remove(list, parent); else - node_update(list, (PurpleBlistNode*)contact); - } else if (!PURPLE_BLIST_NODE_IS_GROUP(node)) { - PurpleGroup *group = (PurpleGroup*)node->parent; - if ((group->currentsize < 1 && !purple_prefs_get_bool(PREF_ROOT "/emptygroups")) || - (!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group))) - node_remove(list, node->parent); - for (node = node->child; node; node = node->next) - reset_blist_node_ui_data(node); - } else { - for (node = node->child; node; node = node->next) - node_remove(list, node); + node_update(list, parent); } draw_tooltip(ggblist); @@ -310,48 +496,39 @@ happens, so maybe someone will figure it out. */ g_return_if_fail(node != NULL); - if (list->ui_data == NULL) + if (FINCH_GET_DATA(list)== NULL) return; /* XXX: this is probably the place to auto-join chats */ if (ggblist->window == NULL) return; - if (node->ui_data != NULL) { + if (FINCH_GET_DATA(node)!= NULL) { gnt_tree_change_text(GNT_TREE(ggblist->tree), node, 0, get_display_name(node)); gnt_tree_sort_row(GNT_TREE(ggblist->tree), node); blist_update_row_flags(node); + if (gnt_tree_get_parent_key(GNT_TREE(ggblist->tree), node) != + ggblist->manager->find_parent(node)) + node_remove(list, node); } if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { PurpleBuddy *buddy = (PurpleBuddy*)node; - if (purple_account_is_connected(buddy->account) && - (PURPLE_BUDDY_IS_ONLINE(buddy) || purple_prefs_get_bool(PREF_ROOT "/showoffline"))) - add_node((PurpleBlistNode*)buddy, list->ui_data); - - node_update(list, node->parent); + add_node((PurpleBlistNode*)buddy, FINCH_GET_DATA(list)); + node_update(list, purple_blist_node_get_parent(node)); } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { - add_chat((PurpleChat *)node, list->ui_data); + add_node(node, FINCH_GET_DATA(list)); } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { - PurpleContact *contact = (PurpleContact*)node; - if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) || - contact->currentsize < 1) - /* nothing */; - else { - if (node->ui_data == NULL) { - /* The core seems to expect the UI to add the buddies. */ - for (node = node->child; node; node = node->next) - add_node(node, list->ui_data); - } + if (FINCH_GET_DATA(node)== NULL) { + /* The core seems to expect the UI to add the buddies. */ + for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) + add_node(node, FINCH_GET_DATA(list)); } } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { - PurpleGroup *group = (PurpleGroup*)node; - if (!purple_prefs_get_bool(PREF_ROOT "/emptygroups") && - ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)) || - group->currentsize < 1)) + if (!ggblist->manager->can_add_node(node)) node_remove(list, node); else - add_node(node, list->ui_data); + add_node(node, FINCH_GET_DATA(list)); } } @@ -362,7 +539,10 @@ return; ggblist = g_new0(FinchBlist, 1); - list->ui_data = ggblist; + FINCH_SET_DATA(list, ggblist); + ggblist->manager = finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT "/grouping")); + if (!ggblist->manager) + ggblist->manager = &default_manager; } static void @@ -399,6 +579,8 @@ purple_blist_add_group(grp, NULL); } + /* XXX: Ask if there's already the same buddy in the same group (#4553) */ + buddy = purple_buddy_new(account, username, alias); purple_blist_add_buddy(buddy, NULL, grp, NULL); purple_account_add_buddy(account, buddy); @@ -447,6 +629,7 @@ GHashTable *hash = NULL; PurpleConnection *gc; gboolean autojoin; + PurplePluginProtocolInfo *info; account = purple_request_fields_get_account(allfields, "account"); name = purple_request_fields_get_string(allfields, "name"); @@ -456,15 +639,15 @@ if (!purple_account_is_connected(account) || !name || !*name) return; - + if (!group || !*group) group = _("Chats"); gc = purple_account_get_connection(account); - - if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) - hash = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, name); - + info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + if (info->chat_info_defaults != NULL) + hash = info->chat_info_defaults(gc, name); + chat = purple_chat_new(account, name, hash); if (chat != NULL) { @@ -476,7 +659,7 @@ purple_blist_alias_chat(chat, alias); purple_blist_node_set_bool((PurpleBlistNode*)chat, "gnt-autojoin", autojoin); if (autojoin) - serv_join_chat(chat->account->gc, chat->components); + serv_join_chat(purple_account_get_connection(purple_chat_get_account(chat)), purple_chat_get_components(chat)); } } @@ -501,7 +684,7 @@ field = purple_request_field_string_new("alias", _("Alias"), alias, FALSE); purple_request_field_group_add_field(group, field); - field = purple_request_field_string_new("group", _("Group"), grp ? grp->name : NULL, FALSE); + field = purple_request_field_string_new("group", _("Group"), grp ? purple_group_get_name(grp) : NULL, FALSE); purple_request_field_group_add_field(group, field); field = purple_request_field_bool_new("autojoin", _("Auto-join"), FALSE); @@ -578,11 +761,14 @@ static void add_group(PurpleGroup *group, FinchBlist *ggblist) { + gpointer parent; PurpleBlistNode *node = (PurpleBlistNode *)group; - if (node->ui_data) + if (FINCH_GET_DATA(node)) return; + parent = ggblist->manager->find_parent((PurpleBlistNode*)group); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group, - gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL)); + gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), + parent, NULL)); gnt_tree_set_expanded(GNT_TREE(ggblist->tree), node, !purple_blist_node_get_bool(node, "collapsed")); } @@ -638,7 +824,7 @@ strncpy(status, "~", sizeof(status) - 1); } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) - return ((PurpleGroup*)node)->name; + return purple_group_get_name((PurpleGroup*)node); snprintf(text, sizeof(text) - 1, "%s %s", status, name); @@ -648,41 +834,39 @@ static void add_chat(PurpleChat *chat, FinchBlist *ggblist) { - PurpleGroup *group; + gpointer parent; PurpleBlistNode *node = (PurpleBlistNode *)chat; - if (node->ui_data) + if (FINCH_GET_DATA(node)) return; - if (!purple_account_is_connected(chat->account)) + if (!purple_account_is_connected(purple_chat_get_account(chat))) return; - group = purple_chat_get_group(chat); - add_node((PurpleBlistNode*)group, ggblist); + parent = ggblist->manager->find_parent((PurpleBlistNode*)chat); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), - group, NULL)); + parent, NULL)); } static void add_contact(PurpleContact *contact, FinchBlist *ggblist) { - PurpleGroup *group; + gpointer parent; PurpleBlistNode *node = (PurpleBlistNode*)contact; const char *name; - if (node->ui_data) + if (FINCH_GET_DATA(node)) return; name = get_display_name(node); if (name == NULL) return; - group = (PurpleGroup*)node->parent; - add_node((PurpleBlistNode*)group, ggblist); + parent = ggblist->manager->find_parent((PurpleBlistNode*)contact); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact, gnt_tree_create_row(GNT_TREE(ggblist->tree), name), - group, NULL)); + parent, NULL)); gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE); } @@ -690,23 +874,19 @@ static void add_buddy(PurpleBuddy *buddy, FinchBlist *ggblist) { - PurpleContact *contact; + gpointer parent; PurpleBlistNode *node = (PurpleBlistNode *)buddy; - - if (node->ui_data) + PurpleContact *contact; + + if (FINCH_GET_DATA(node)) return; - if (!purple_account_is_connected(buddy->account)) - return; - - contact = (PurpleContact*)node->parent; - if (!contact) /* When a new buddy is added and show-offline is set */ - return; - add_node((PurpleBlistNode*)contact, ggblist); + contact = purple_buddy_get_contact(buddy); + parent = ggblist->manager->find_parent((PurpleBlistNode*)buddy); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), - contact, NULL)); + parent, NULL)); blist_update_row_flags((PurpleBlistNode*)buddy); if (buddy == purple_contact_get_priority_buddy(contact)) @@ -756,7 +936,7 @@ purple_buddy_get_account(buddy), purple_buddy_get_name(buddy)); } else { - FinchConv *ggconv = conv->ui_data; + FinchConv *ggconv = FINCH_GET_DATA(conv); gnt_window_present(ggconv->window); } finch_conversation_set_active(conv); @@ -764,7 +944,7 @@ else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { PurpleChat *chat = (PurpleChat*)node; - serv_join_chat(chat->account->gc, chat->components); + serv_join_chat(purple_account_get_connection(purple_chat_get_account(chat)), purple_chat_get_components(chat)); } } @@ -809,7 +989,7 @@ append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node) { GList *list; - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); if(!prpl_info || !prpl_info->blist_node_menu) return; @@ -851,7 +1031,7 @@ else val = g_strdup(purple_request_field_string_get_value(field)); - g_hash_table_replace(chat->components, g_strdup(id), val); /* val should not be free'd */ + g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val); /* val should not be free'd */ } } } @@ -864,22 +1044,24 @@ PurpleRequestField *field; GList *parts, *iter; struct proto_chat_entry *pce; + PurpleConnection *gc; purple_request_fields_add_group(fields, group); - parts = PURPLE_PLUGIN_PROTOCOL_INFO(chat->account->gc->prpl)->chat_info(chat->account->gc); + gc = purple_account_get_connection(purple_chat_get_account(chat)); + parts = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc))->chat_info(gc); for (iter = parts; iter; iter = iter->next) { pce = iter->data; if (pce->is_int) { int val; - const char *str = g_hash_table_lookup(chat->components, pce->identifier); + const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier); if (!str || sscanf(str, "%d", &val) != 1) val = pce->min; field = purple_request_field_int_new(pce->identifier, pce->label, val); } else { field = purple_request_field_string_new(pce->identifier, pce->label, - g_hash_table_lookup(chat->components, pce->identifier), FALSE); + g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE); } purple_request_field_group_add_field(group, field); @@ -920,7 +1102,7 @@ static void finch_add_buddy(PurpleBlistNode *selected, PurpleGroup *grp) { - purple_blist_request_add_buddy(NULL, NULL, grp ? grp->name : NULL, NULL); + purple_blist_request_add_buddy(NULL, NULL, grp ? purple_group_get_name(grp) : NULL, NULL); } static void @@ -961,13 +1143,13 @@ static void finch_blist_get_buddy_info_cb(PurpleBlistNode *selected, PurpleBuddy *buddy) { - finch_retrieve_user_info(buddy->account->gc, purple_buddy_get_name(buddy)); + finch_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy)); } static void finch_blist_menu_send_file_cb(PurpleBlistNode *selected, PurpleBuddy *buddy) { - serv_send_file(buddy->account->gc, buddy->name, NULL); + serv_send_file(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy), NULL); } static void @@ -978,7 +1160,7 @@ b = purple_contact_get_priority_buddy((PurpleContact *)node); else b = (PurpleBuddy *)node; - finch_pounce_editor_show(b->account, b->name, NULL); + finch_pounce_editor_show(purple_buddy_get_account(b), purple_buddy_get_name(b), NULL); } @@ -986,8 +1168,9 @@ create_buddy_menu(GntMenu *menu, PurpleBuddy *buddy) { PurplePluginProtocolInfo *prpl_info; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(buddy->account->gc->prpl); + PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); if (prpl_info && prpl_info->get_info) { add_custom_action(menu, _("Get Info"), @@ -1000,7 +1183,7 @@ if (prpl_info && prpl_info->send_file) { if (!prpl_info->can_receive_file || - prpl_info->can_receive_file(buddy->account->gc, buddy->name)) + prpl_info->can_receive_file(gc, purple_buddy_get_name(buddy))) add_custom_action(menu, _("Send File"), PURPLE_CALLBACK(finch_blist_menu_send_file_cb), buddy); } @@ -1035,11 +1218,12 @@ PurpleGroup *group; cnode = (PurpleBlistNode *)contact; - group = (PurpleGroup*)cnode->parent; - for (bnode = cnode->child; bnode; bnode = bnode->next) { + group = (PurpleGroup*)purple_blist_node_get_parent(cnode); + for (bnode = purple_blist_node_get_first_child(cnode); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) { PurpleBuddy *buddy = (PurpleBuddy*)bnode; - if (purple_account_is_connected(buddy->account)) - purple_account_remove_buddy(buddy->account, buddy, group); + PurpleAccount *account = purple_buddy_get_account(buddy); + if (purple_account_is_connected(account)) + purple_account_remove_buddy(account, buddy, group); } purple_blist_remove_contact(contact); } @@ -1082,7 +1266,7 @@ else if (PURPLE_BLIST_NODE_IS_CHAT(node)) name = purple_chat_get_name((PurpleChat*)node); else if (PURPLE_BLIST_NODE_IS_GROUP(node)) - name = ((PurpleGroup*)node)->name; + name = purple_group_get_name((PurpleGroup*)node); else g_return_if_reached(); @@ -1104,32 +1288,34 @@ { PurpleBlistNode *cnode, *bnode; - cnode = ((PurpleBlistNode*)group)->child; + cnode = purple_blist_node_get_first_child(((PurpleBlistNode*)group)); while (cnode) { if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) { - bnode = cnode->child; - cnode = cnode->next; + bnode = purple_blist_node_get_first_child(cnode); + cnode = purple_blist_node_get_sibling_next(cnode); while (bnode) { PurpleBuddy *buddy; if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) { + PurpleAccount *account; buddy = (PurpleBuddy*)bnode; - bnode = bnode->next; - if (purple_account_is_connected(buddy->account)) { - purple_account_remove_buddy(buddy->account, buddy, group); + bnode = purple_blist_node_get_sibling_next(bnode); + account = purple_buddy_get_account(buddy); + if (purple_account_is_connected(account)) { + purple_account_remove_buddy(account, buddy, group); purple_blist_remove_buddy(buddy); } } else { - bnode = bnode->next; + bnode = purple_blist_node_get_sibling_next(bnode); } } } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) { PurpleChat *chat = (PurpleChat *)cnode; - cnode = cnode->next; - if (purple_account_is_connected(chat->account)) + cnode = purple_blist_node_get_sibling_next(cnode); + if (purple_account_is_connected(purple_chat_get_account(chat))) purple_blist_remove_chat(chat); } else { - cnode = cnode->next; + cnode = purple_blist_node_get_sibling_next(cnode); } } @@ -1172,7 +1358,7 @@ } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { name = purple_chat_get_name((PurpleChat*)node); } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { - name = ((PurpleGroup*)node)->name; + name = purple_group_get_name((PurpleGroup*)node); sec = _("Removing this group will also remove all the buddies in the group"); } else @@ -1216,18 +1402,19 @@ PurpleGroup *tg = NULL; PurpleContact *tc = NULL; - if (target == NULL) + if (target == NULL || + purple_blist_node_get_type(target) == PURPLE_BLIST_OTHER_NODE) return; if (PURPLE_BLIST_NODE_IS_GROUP(target)) tg = (PurpleGroup*)target; else if (PURPLE_BLIST_NODE_IS_BUDDY(target)) { - tc = (PurpleContact*)target->parent; - tg = (PurpleGroup*)target->parent->parent; + tc = (PurpleContact*)purple_blist_node_get_parent(target); + tg = (PurpleGroup*)purple_blist_node_get_parent((PurpleBlistNode*)tc); } else { if (PURPLE_BLIST_NODE_IS_CONTACT(target)) tc = (PurpleContact*)target; - tg = (PurpleGroup*)target->parent; + tg = (PurpleGroup*)purple_blist_node_get_parent(target); } if (ggblist->tagged) { @@ -1300,6 +1487,8 @@ tree = GNT_TREE(ggblist->tree); node = gnt_tree_get_selection_data(tree); + if (node && purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE) + return; if (ggblist->tooltip) remove_tooltip(ggblist); @@ -1328,7 +1517,7 @@ } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { PurpleGroup *group = (PurpleGroup *)node; create_group_menu(GNT_MENU(context), group); - title = g_strdup(group->name); + title = g_strdup(purple_group_get_name(group)); } append_extended_menu(GNT_MENU(context), node); @@ -1393,7 +1582,7 @@ purple_account_get_protocol_name(account)); purple_notify_user_info_add_pair(user_info, _("Account"), tmp); g_free(tmp); - + prpl = purple_find_prpl(purple_account_get_protocol_id(account)); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); if (prpl_info && prpl_info->tooltip_text) { @@ -1442,16 +1631,15 @@ { PurpleBlistNode *node; int x, y, top, width, w, h; - GString *str; + GString *str = NULL; GntTree *tree; GntWidget *widget, *box, *tv; char *title = NULL; - int lastseen = 0; widget = ggblist->tree; tree = GNT_TREE(widget); - if (!gnt_widget_has_focus(ggblist->tree) || + if (!gnt_widget_has_focus(ggblist->tree) || (ggblist->context && !GNT_WIDGET_IS_FLAG_SET(ggblist->context, GNT_WIDGET_INVISIBLE))) return FALSE; @@ -1466,65 +1654,8 @@ if (!node) return FALSE; - str = g_string_new(""); - - if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { - PurpleBuddy *pr = purple_contact_get_priority_buddy((PurpleContact*)node); - gboolean offline = !PURPLE_BUDDY_IS_ONLINE(pr); - gboolean showoffline = purple_prefs_get_bool(PREF_ROOT "/showoffline"); - const char *name = purple_buddy_get_name(pr); - - title = g_strdup(name); - tooltip_for_buddy(pr, str, TRUE); - for (node = node->child; node; node = node->next) { - PurpleBuddy *buddy = (PurpleBuddy*)node; - if (offline) { - int value = purple_blist_node_get_int(node, "last_seen"); - if (value > lastseen) - lastseen = value; - } - if (node == (PurpleBlistNode*)pr) - continue; - if (!purple_account_is_connected(buddy->account)) - continue; - if (!showoffline && !PURPLE_BUDDY_IS_ONLINE(buddy)) - continue; - str = g_string_append(str, "\n----------\n"); - tooltip_for_buddy(buddy, str, FALSE); - } - } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { - PurpleBuddy *buddy = (PurpleBuddy *)node; - tooltip_for_buddy(buddy, str, TRUE); - title = g_strdup(purple_buddy_get_name(buddy)); - if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node)) - lastseen = purple_blist_node_get_int(node, "last_seen"); - } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { - PurpleGroup *group = (PurpleGroup *)node; - - g_string_append_printf(str, _("Online: %d\nTotal: %d"), - purple_blist_get_group_online_count(group), - purple_blist_get_group_size(group, FALSE)); - - title = g_strdup(group->name); - } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { - PurpleChat *chat = (PurpleChat *)node; - PurpleAccount *account = chat->account; - - g_string_append_printf(str, _("Account: %s (%s)"), - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - - title = g_strdup(purple_chat_get_name(chat)); - } else { - g_string_free(str, TRUE); + if (!ggblist->manager->create_tooltip(node, &str, &title)) return FALSE; - } - - if (lastseen > 0) { - char *tmp = purple_str_seconds_to_string(time(NULL) - lastseen); - g_string_append_printf(str, _("\nLast Seen: %s ago"), tmp); - g_free(tmp); - } gnt_widget_get_position(widget, &x, &y); gnt_widget_get_size(widget, &width, NULL); @@ -1684,7 +1815,7 @@ { PurpleBlistNode *node; purple_signals_disconnect_by_handle(finch_blist_get_handle()); - purple_get_blist()->ui_data = NULL; + FINCH_SET_DATA(purple_get_blist(), NULL); node = purple_blist_get_root(); while (node) { @@ -1707,6 +1838,9 @@ PurpleBlistNode *node; PurpleBuddyList *list; + if (ggblist->manager->init) + ggblist->manager->init(); + if (strcmp(purple_prefs_get_string(PREF_ROOT "/sort_type"), "text") == 0) { gnt_tree_set_compare_func(GNT_TREE(ggblist->tree), (GCompareFunc)blist_node_compare_text); @@ -1794,11 +1928,33 @@ redraw_blist(const char *name, PurplePrefType type, gconstpointer val, gpointer data) { PurpleBlistNode *node, *sel; - if (ggblist == NULL || ggblist->window == NULL) + FinchBlistManager *manager; + + if (ggblist == NULL) + return; + + manager = finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT "/grouping")); + if (manager == NULL) + manager = &default_manager; + if (ggblist->manager != manager) { + if (ggblist->manager->uninit) + ggblist->manager->uninit(); + + ggblist->manager = manager; + if (manager->can_add_node == NULL) + manager->can_add_node = default_can_add_node; + if (manager->find_parent == NULL) + manager->find_parent = default_find_parent; + if (manager->create_tooltip == NULL) + manager->create_tooltip = default_create_tooltip; + } + + if (ggblist->window == NULL) return; sel = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)); gnt_tree_remove_all(GNT_TREE(ggblist->tree)); + node = purple_blist_get_root(); for (; node; node = purple_blist_node_next(node, TRUE)) reset_blist_node_ui_data(node); @@ -1833,6 +1989,7 @@ purple_prefs_add_bool(PREF_ROOT "/showoffline", FALSE); purple_prefs_add_bool(PREF_ROOT "/emptygroups", FALSE); purple_prefs_add_string(PREF_ROOT "/sort_type", "text"); + purple_prefs_add_string(PREF_ROOT "/grouping", "default"); purple_prefs_connect_callback(finch_blist_get_handle(), PREF_ROOT "/emptygroups", redraw_blist, NULL); @@ -1840,9 +1997,14 @@ PREF_ROOT "/showoffline", redraw_blist, NULL); purple_prefs_connect_callback(finch_blist_get_handle(), PREF_ROOT "/sort_type", redraw_blist, NULL); + purple_prefs_connect_callback(finch_blist_get_handle(), + PREF_ROOT "/grouping", redraw_blist, NULL); purple_signal_connect(purple_connections_get_handle(), "signed-on", purple_blist_get_handle(), G_CALLBACK(account_signed_on_cb), NULL); + + finch_blist_install_manager(&default_manager); + return; } @@ -2003,7 +2165,7 @@ static int blist_node_compare_position(PurpleBlistNode *n1, PurpleBlistNode *n2) { - while ((n1 = n1->prev) != NULL) + while ((n1 = purple_blist_node_get_sibling_prev(n1)) != NULL) if (n1 == n2) return 1; return -1; @@ -2016,10 +2178,10 @@ char *us1, *us2; int ret; - if (n1->type != n2->type) + if (purple_blist_node_get_type(n1) != purple_blist_node_get_type(n2)) return blist_node_compare_position(n1, n2); - switch (n1->type) + switch (purple_blist_node_get_type(n1)) { case PURPLE_BLIST_CHAT_NODE: s1 = purple_chat_get_name((PurpleChat*)n1); @@ -2051,10 +2213,10 @@ { int ret; - if (n1->type != n2->type) + if (purple_blist_node_get_type(n1) != purple_blist_node_get_type(n2)) return blist_node_compare_position(n1, n2); - switch (n1->type) { + switch (purple_blist_node_get_type(n1)) { case PURPLE_BLIST_CONTACT_NODE: n1 = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)n1); n2 = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)n2); @@ -2082,9 +2244,10 @@ int log = 0; PurpleBlistNode *node; - for (node = c->child; node; node = node->next) { + for (node = purple_blist_node_get_first_child(c); node; node = purple_blist_node_get_sibling_next(node)) { PurpleBuddy *b = (PurpleBuddy*)node; - log += purple_log_get_total_size(PURPLE_LOG_IM, b->name, b->account); + log += purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b), + purple_buddy_get_account(b)); } return log; @@ -2096,15 +2259,15 @@ int ret; PurpleBuddy *b1, *b2; - if (n1->type != n2->type) + if (purple_blist_node_get_type(n1) != purple_blist_node_get_type(n2)) return blist_node_compare_position(n1, n2); - switch (n1->type) { + switch (purple_blist_node_get_type(n1)) { case PURPLE_BLIST_BUDDY_NODE: b1 = (PurpleBuddy*)n1; b2 = (PurpleBuddy*)n2; - ret = purple_log_get_total_size(PURPLE_LOG_IM, b2->name, b2->account) - - purple_log_get_total_size(PURPLE_LOG_IM, b1->name, b1->account); + ret = purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b2), purple_buddy_get_account(b2)) - + purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b1), purple_buddy_get_account(b1)); if (ret != 0) return ret; break; @@ -2165,19 +2328,17 @@ buddy_recent_signed_on_off(gpointer data) { PurpleBlistNode *node = data; - FinchBlistNode *fnode = node->ui_data; - PurpleBuddy *buddy = (PurpleBuddy*)node; + FinchBlistNode *fnode = FINCH_GET_DATA(node); purple_timeout_remove(fnode->signed_timer); fnode->signed_timer = 0; - if (!purple_account_is_connected(buddy->account) || - (!PURPLE_BUDDY_IS_ONLINE(buddy) && !purple_prefs_get_bool(PREF_ROOT "/showoffline"))) { + if (!ggblist->manager->can_add_node(node)) { node_remove(purple_get_blist(), node); } else { update_node_display(node, ggblist); - if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent)) - update_node_display(node->parent, ggblist); + if (purple_blist_node_get_parent(node) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node))) + update_node_display(purple_blist_node_get_parent(node), ggblist); } return FALSE; @@ -2187,7 +2348,7 @@ buddy_signed_on_off_cb(gpointer data) { PurpleBlistNode *node = data; - FinchBlistNode *fnode = node->ui_data; + FinchBlistNode *fnode = FINCH_GET_DATA(node); if (!ggblist || !fnode) return FALSE; @@ -2195,8 +2356,8 @@ purple_timeout_remove(fnode->signed_timer); fnode->signed_timer = purple_timeout_add_seconds(6, (GSourceFunc)buddy_recent_signed_on_off, data); update_node_display(node, ggblist); - if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent)) - update_node_display(node->parent, ggblist); + if (purple_blist_node_get_parent(node) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node))) + update_node_display(purple_blist_node_get_parent(node), ggblist); return FALSE; } @@ -2260,10 +2421,10 @@ PurpleAccount *account = iter->data; PurpleConnection *gc = purple_account_get_connection(account); PurplePlugin *prpl; - + if (!gc || !PURPLE_CONNECTION_IS_CONNECTED(gc)) continue; - prpl = gc->prpl; + prpl = purple_connection_get_prpl(gc); if (PURPLE_PLUGIN_HAS_ACTIONS(prpl)) { item = gnt_menuitem_new(purple_account_get_username(account)); @@ -2273,6 +2434,30 @@ } } +static void +reconstruct_grouping_menu(void) +{ + GList *iter; + GntWidget *subsub; + + if (!ggblist || !ggblist->grouping) + return; + + subsub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(ggblist->grouping, GNT_MENU(subsub)); + + for (iter = managers; iter; iter = iter->next) { + char menuid[128]; + FinchBlistManager *manager = iter->data; + GntMenuItem *item = gnt_menuitem_new(_(manager->name)); + 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); + gnt_menuitem_set_callback(item, menu_group_set_cb, NULL); + } +} + static gboolean auto_join_chats(gpointer data) { @@ -2284,9 +2469,9 @@ node = purple_blist_node_next(node, FALSE)) { if (PURPLE_BLIST_NODE_IS_CHAT(node)) { PurpleChat *chat = (PurpleChat*)node; - if (chat->account == account && + if (purple_chat_get_account(chat) == account && purple_blist_node_get_bool(node, "gnt-autojoin")) - serv_join_chat(purple_account_get_connection(chat->account), chat->components); + serv_join_chat(purple_account_get_connection(account), purple_chat_get_components(chat)); } } return FALSE; @@ -2372,14 +2557,15 @@ if (!purple_account_is_connected(account)) return; - gc = purple_account_get_connection(account); + gc = purple_account_get_connection(account); purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name); chat = purple_blist_find_chat(account, name); if (chat == NULL) { - if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) - hash = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, name); + PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + if (info->chat_info_defaults != NULL) + hash = info->chat_info_defaults(gc, name); } else { - hash = chat->components; + hash = purple_chat_get_components(chat); } serv_join_chat(gc, hash); if (chat == NULL && hash != NULL) @@ -2439,6 +2625,13 @@ } static void +menu_group_set_cb(GntMenuItem *item, gpointer null) +{ + const char *id = g_object_get_data(G_OBJECT(item), "grouping-id"); + purple_prefs_set_string(PREF_ROOT "/grouping", id); +} + +static void create_menu(void) { GntWidget *menu, *sub, *subsub; @@ -2479,7 +2672,7 @@ purple_prefs_get_bool(PREF_ROOT "/emptygroups")); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/emptygroups"); - + item = gnt_menuitem_check_new(_("Offline buddies")); gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-offline-buddies"); gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), @@ -2513,21 +2706,25 @@ subsub = gnt_menu_new(GNT_MENU_POPUP); gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); - item = gnt_menuitem_new("Buddy"); + item = gnt_menuitem_new(_("Buddy")); gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-buddy"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_buddy_cb, NULL); - item = gnt_menuitem_new("Chat"); + item = gnt_menuitem_new(_("Chat")); gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-chat"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_chat_cb, NULL); - item = gnt_menuitem_new("Group"); + item = gnt_menuitem_new(_("Group")); gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-group"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_group_cb, NULL); + ggblist->grouping = item = gnt_menuitem_new(_("Grouping")); + gnt_menu_add_item(GNT_MENU(sub), item); + reconstruct_grouping_menu(); + reconstruct_accounts_menu(); gnt_menu_add_item(GNT_MENU(menu), ggblist->accounts); @@ -2679,3 +2876,43 @@ { gnt_widget_set_size(ggblist->window, width, height); } + +void finch_blist_install_manager(const FinchBlistManager *manager) +{ + if (!g_list_find(managers, manager)) { + managers = g_list_append(managers, (gpointer)manager); + reconstruct_grouping_menu(); + if (strcmp(manager->id, purple_prefs_get_string(PREF_ROOT "/grouping")) == 0) + purple_prefs_trigger_callback(PREF_ROOT "/grouping"); + } +} + +void finch_blist_uninstall_manager(const FinchBlistManager *manager) +{ + if (g_list_find(managers, manager)) { + managers = g_list_remove(managers, manager); + reconstruct_grouping_menu(); + if (strcmp(manager->id, purple_prefs_get_string(PREF_ROOT "/grouping")) == 0) + purple_prefs_trigger_callback(PREF_ROOT "/grouping"); + } +} + +FinchBlistManager * finch_blist_manager_find(const char *id) +{ + GList *iter = managers; + if (!id) + return NULL; + + for (; iter; iter = iter->next) { + FinchBlistManager *m = iter->data; + if (strcmp(id, m->id) == 0) + return m; + } + return NULL; +} + +GntTree * finch_blist_get_tree(void) +{ + return ggblist ? GNT_TREE(ggblist->tree) : NULL; +} +
--- a/finch/gntblist.h Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntblist.h Wed Jan 30 10:53:22 2008 +0000 @@ -27,12 +27,25 @@ #define _GNT_BLIST_H #include "blist.h" +#include "gnttree.h" /********************************************************************** * @name GNT BuddyList API **********************************************************************/ /*@{*/ +typedef struct +{ + const char *id; /**< An identifier for the manager. */ + const char *name; /**< Displayable name for the manager. */ + gboolean (*init)(void); /**< Called right before it's being used. */ + gboolean (*uninit)(void); /**< Called right after it's not being used any more. */ + gboolean (*can_add_node)(PurpleBlistNode *node); /**< Whether a node should be added to the view. */ + gpointer (*find_parent)(PurpleBlistNode *node); /**< Find the parent row for a node. */ + gboolean (*create_tooltip)(gpointer selected_row, GString **body, char **title); /**< Create tooltip for a selected row. */ + gpointer reserved[4]; +} FinchBlistManager; + /** * Get the ui-functions. * @@ -103,6 +116,47 @@ */ gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name); +/** + * Get the tree list of the buddy list. + * @return The GntTree widget. + * @since 2.4.0 + */ +GntTree * finch_blist_get_tree(void); + +/** + * Add an alternate buddy list manager. + * + * @param manager The alternate buddylist manager. + * @since 2.4.0 + */ +void finch_blist_install_manager(const FinchBlistManager *manager); + +/** + * Remove an alternate buddy list manager. + * + * @param manager The buddy list manager to remove. + * @since 2.4.0 + */ +void finch_blist_uninstall_manager(const FinchBlistManager *manager); + +/** + * Find a buddy list manager. + * + * @param id The identifier for the desired buddy list manager. + * + * @return The manager with the requested identifier, if available. @c NULL otherwise. + * @since 2.4.0 + */ +FinchBlistManager * finch_blist_manager_find(const char *id); + +/** + * Request the active buddy list manager to add a node. + * + * @param node The node to add + * @since 2.4.0 + */ +void finch_blist_manager_add_node(PurpleBlistNode *node); + /*@}*/ #endif
--- a/finch/gntcertmgr.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntcertmgr.c Wed Jan 30 10:53:22 2008 +0000 @@ -25,14 +25,13 @@ * */ -#include "internal.h" +#include "finch.h" #include "certificate.h" #include "debug.h" #include "notify.h" #include "request.h" -#include "finch.h" #include "gntcertmgr.h" #include "gntbutton.h" @@ -235,7 +234,7 @@ purple_request_close_with_handle((void *)key); purple_request_yes_no((void *)key, _("Confirm certificate delete"), primary, NULL, - 2, + 1, NULL, NULL, NULL, g_strdup(key), tls_peers_mgmt_delete_confirm_cb,
--- a/finch/gntconn.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntconn.c Wed Jan 30 10:53:22 2008 +0000 @@ -101,13 +101,14 @@ } static void -finch_connection_report_disconnect(PurpleConnection *gc, const char *text) +finch_connection_report_disconnect(PurpleConnection *gc, PurpleConnectionError reason, + const char *text) { FinchAutoRecon *info; PurpleAccount *account = purple_connection_get_account(gc); GList *list; - if (!gc->wants_to_die) { + if (!purple_connection_error_is_fatal(reason)) { info = g_hash_table_lookup(hash, account); if (info == NULL) { @@ -148,7 +149,7 @@ while (list) { PurpleConversation *conv = list->data; list = list->next; - if (conv->account != account || + if (purple_conversation_get_account(conv) != account || purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) continue; purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE)); @@ -179,10 +180,10 @@ NULL, /* connected */ NULL, /* disconnected */ NULL, /* notice */ - finch_connection_report_disconnect, + NULL, NULL, /* network_connected */ NULL, /* network_disconnected */ - NULL, + finch_connection_report_disconnect, NULL, NULL, NULL
--- a/finch/gntconv.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntconv.c Wed Jan 30 10:53:22 2008 +0000 @@ -71,6 +71,20 @@ static int color_message_action; static int color_timestamp; +static PurpleBuddy * +find_buddy_for_conversation(PurpleConversation *conv) +{ + return purple_find_buddy(purple_conversation_get_account(conv), + purple_conversation_get_name(conv)); +} + +static PurpleChat * +find_chat_for_conversation(PurpleConversation *conv) +{ + return purple_blist_find_chat(purple_conversation_get_account(conv), + purple_conversation_get_name(conv)); +} + static PurpleBlistNode * get_conversation_blist_node(PurpleConversation *conv) { @@ -78,11 +92,11 @@ switch (purple_conversation_get_type(conv)) { case PURPLE_CONV_TYPE_IM: - node = (PurpleBlistNode*)purple_find_buddy(conv->account, conv->name); - node = node ? node->parent : NULL; + node = (PurpleBlistNode*)find_buddy_for_conversation(conv); + node = node ? purple_blist_node_get_parent(node) : NULL; break; case PURPLE_CONV_TYPE_CHAT: - node = (PurpleBlistNode*)purple_blist_find_chat(conv->account, conv->name); + node = (PurpleBlistNode*)find_chat_for_conversation(conv); break; default: break; @@ -168,7 +182,7 @@ } g_free(error); } - else if (!purple_account_is_connected(ggconv->active_conv->account)) + else if (!purple_account_is_connected(purple_conversation_get_account(ggconv->active_conv))) { purple_conversation_write(ggconv->active_conv, "", _("Message was not sent, because you are not signed on."), PURPLE_MESSAGE_ERROR | PURPLE_MESSAGE_NO_LOG, time(NULL)); @@ -236,11 +250,12 @@ if (!buddy) return NULL; - for (node = ((PurpleBlistNode*)buddy)->parent->child; node; node = node->next) { + for (node = purple_blist_node_get_first_child(purple_blist_node_get_parent((PurpleBlistNode*)buddy)); + node; node = purple_blist_node_get_sibling_next(node)) { if (node == (PurpleBlistNode*)buddy) continue; if ((ret = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - ((PurpleBuddy*)node)->name, ((PurpleBuddy*)node)->account)) != NULL) + purple_buddy_get_name((PurpleBuddy*)node), purple_buddy_get_account((PurpleBuddy*)node))) != NULL) break; } return ret; @@ -267,7 +282,7 @@ return; im = PURPLE_CONV_IM(conv); - ggc = conv->ui_data; + ggc = FINCH_GET_DATA(conv); if (purple_conv_im_get_typing_state(im) == PURPLE_TYPING) { int scroll; @@ -303,10 +318,10 @@ static void buddy_signed_on_off(PurpleBuddy *buddy, gpointer null) { - PurpleConversation *conv = find_conv_with_contact(buddy->account, buddy->name); + PurpleConversation *conv = find_conv_with_contact(purple_buddy_get_account(buddy), purple_buddy_get_name(buddy)); if (conv == NULL) return; - generate_send_to_menu(conv->ui_data); + generate_send_to_menu(FINCH_GET_DATA(conv)); } static void @@ -315,9 +330,10 @@ GList *list = purple_get_ims(); while (list) { PurpleConversation *conv = list->data; - PurpleConversation *cc = find_conv_with_contact(conv->account, conv->name); + PurpleConversation *cc = find_conv_with_contact( + purple_conversation_get_account(conv), purple_conversation_get_name(conv)); if (cc) - generate_send_to_menu(cc->ui_data); + generate_send_to_menu(FINCH_GET_DATA(cc)); list = list->next; } @@ -331,16 +347,17 @@ GHashTable *comps = NULL; list = list->next; - if (conv->account != gc->account || + if (purple_conversation_get_account(conv) != purple_connection_get_account(gc) || !purple_conversation_get_data(conv, "want-to-rejoin")) continue; - chat = purple_blist_find_chat(conv->account, conv->name); + chat = find_chat_for_conversation(conv); if (chat == NULL) { - if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) - comps = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, conv->name); + PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); + if (info->chat_info_defaults != NULL) + comps = info->chat_info_defaults(gc, purple_conversation_get_name(conv)); } else { - comps = chat->components; + comps = purple_chat_get_components(chat); } serv_join_chat(gc, comps); if (chat == NULL && comps != NULL) @@ -413,15 +430,11 @@ purple_conversation_write(conv, NULL, _("Logging started. Future messages in this conversation will be logged."), - conv->logs ? (PURPLE_MESSAGE_SYSTEM) : - (PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG), - time(NULL)); + PURPLE_MESSAGE_SYSTEM, time(NULL)); } else { purple_conversation_write(conv, NULL, _("Logging stopped. Future messages in this conversation will not be logged."), - conv->logs ? (PURPLE_MESSAGE_SYSTEM) : - (PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG), - time(NULL)); + PURPLE_MESSAGE_SYSTEM, time(NULL)); /* Disable the logging second, so that the above message can be logged. */ purple_conversation_set_logging(conv, FALSE); @@ -462,7 +475,8 @@ GSList *buds; GList *list = NULL; - buds = purple_find_buddies(ggc->active_conv->account, ggc->active_conv->name); + buds = purple_find_buddies(purple_conversation_get_account(ggc->active_conv), + purple_conversation_get_name(ggc->active_conv)); if (!buds) return; @@ -476,7 +490,8 @@ for (; buds; buds = g_slist_delete_link(buds, buds)) { PurpleBlistNode *node = (PurpleBlistNode *)purple_buddy_get_contact((PurpleBuddy *)buds->data); - for (node = node->child; node != NULL; node = node->next) { + for (node = purple_blist_node_get_first_child(node); node != NULL; + node = purple_blist_node_get_sibling_next(node)) { PurpleBuddy *buddy = (PurpleBuddy *)node; PurpleAccount *account = purple_buddy_get_account(buddy); if (purple_account_is_connected(account)) { @@ -529,7 +544,9 @@ if (purple_conversation_get_type(ggc->active_conv) == PURPLE_CONV_TYPE_IM) { PurpleAccount *account = purple_conversation_get_account(ggc->active_conv); - PurplePluginProtocolInfo *pinfo = account->gc ? PURPLE_PLUGIN_PROTOCOL_INFO(account->gc->prpl) : NULL; + PurpleConnection *gc = purple_account_get_connection(account); + PurplePluginProtocolInfo *pinfo = + gc ? PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)) : NULL; if (pinfo && pinfo->get_info) { item = gnt_menuitem_new(_("Get Info")); @@ -543,7 +560,7 @@ if (pinfo && pinfo->send_file && (!pinfo->can_receive_file || - pinfo->can_receive_file(account->gc, purple_conversation_get_name(ggc->active_conv)))) { + pinfo->can_receive_file(gc, purple_conversation_get_name(ggc->active_conv)))) { item = gnt_menuitem_new(_("Send File")); gnt_menu_add_item(GNT_MENU(sub), item); gnt_menuitem_set_callback(item, send_file_cb, ggc); @@ -593,7 +610,7 @@ static void finch_create_conversation(PurpleConversation *conv) { - FinchConv *ggc = conv->ui_data; + FinchConv *ggc = FINCH_GET_DATA(conv); char *title; PurpleConversationType type; PurpleConversation *cc; @@ -603,9 +620,10 @@ if (ggc) return; - cc = find_conv_with_contact(conv->account, conv->name); - if (cc && cc->ui_data) - ggc = cc->ui_data; + account = purple_conversation_get_account(conv); + cc = find_conv_with_contact(account, purple_conversation_get_name(conv)); + if (cc && FINCH_GET_DATA(cc)) + ggc = FINCH_GET_DATA(cc); else ggc = g_new0(FinchConv, 1); @@ -617,14 +635,13 @@ ggc->list = g_list_prepend(ggc->list, conv); ggc->active_conv = conv; - conv->ui_data = ggc; + FINCH_SET_DATA(conv, ggc); - if (cc && cc->ui_data) { + if (cc && FINCH_GET_DATA(cc)) { finch_conversation_set_active(conv); return; } - account = purple_conversation_get_account(conv); type = purple_conversation_get_type(conv); title = get_conversation_title(conv, account); @@ -633,7 +650,7 @@ gnt_box_set_toplevel(GNT_BOX(ggc->window), TRUE); gnt_box_set_pad(GNT_BOX(ggc->window), 0); - switch(conv->type){ + switch (purple_conversation_get_type(conv)) { case PURPLE_CONV_TYPE_UNKNOWN: gnt_widget_set_name(ggc->window, "conversation-window-unknown" ); break; @@ -721,7 +738,7 @@ finch_destroy_conversation(PurpleConversation *conv) { /* do stuff here */ - FinchConv *ggc = conv->ui_data; + FinchConv *ggc = FINCH_GET_DATA(conv); ggc->list = g_list_remove(ggc->list, conv); if (ggc->list && conv == ggc->active_conv) ggc->active_conv = ggc->list->data; @@ -738,7 +755,7 @@ finch_write_common(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime) { - FinchConv *ggconv = conv->ui_data; + FinchConv *ggconv = FINCH_GET_DATA(conv); char *strip, *newline; GntTextFormatFlags fl = 0; int pos; @@ -891,7 +908,7 @@ static void finch_chat_add_users(PurpleConversation *conv, GList *users, gboolean new_arrivals) { - FinchConv *ggc = conv->ui_data; + FinchConv *ggc = FINCH_GET_DATA(conv); GntEntry *entry = GNT_ENTRY(ggc->entry); if (!new_arrivals) @@ -930,7 +947,7 @@ finch_chat_rename_user(PurpleConversation *conv, const char *old, const char *new_n, const char *new_a) { /* Update the name for string completion */ - FinchConv *ggc = conv->ui_data; + FinchConv *ggc = FINCH_GET_DATA(conv); GntEntry *entry = GNT_ENTRY(ggc->entry); GntTree *tree = GNT_TREE(ggc->u.chat->userlist); PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), new_n); @@ -948,7 +965,7 @@ finch_chat_remove_users(PurpleConversation *conv, GList *list) { /* Remove the name from string completion */ - FinchConv *ggc = conv->ui_data; + FinchConv *ggc = FINCH_GET_DATA(conv); GntEntry *entry = GNT_ENTRY(ggc->entry); for (; list; list = list->next) { GntTree *tree = GNT_TREE(ggc->u.chat->userlist); @@ -961,7 +978,7 @@ finch_chat_update_user(PurpleConversation *conv, const char *user) { PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), user); - FinchConv *ggc = conv->ui_data; + FinchConv *ggc = FINCH_GET_DATA(conv); gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist), (gpointer)user, 0, chat_flag_text(cb->flags)); } @@ -1070,7 +1087,7 @@ clear_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, void *data) { - FinchConv *ggconv = conv->ui_data; + FinchConv *ggconv = FINCH_GET_DATA(conv); gnt_text_view_clear(GNT_TEXT_VIEW(ggconv->tv)); purple_conversation_clear_message_history(conv); return PURPLE_CMD_STATUS_OK; @@ -1127,7 +1144,7 @@ static PurpleCmdRet users_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data) { - FinchConv *fc = conv->ui_data; + FinchConv *fc = FINCH_GET_DATA(conv); FinchConvChat *ch; if (!fc) return PURPLE_CMD_STATUS_FAILED; @@ -1231,7 +1248,7 @@ void finch_conversation_set_active(PurpleConversation *conv) { - FinchConv *ggconv = conv->ui_data; + FinchConv *ggconv = FINCH_GET_DATA(conv); PurpleAccount *account; char *title; @@ -1247,7 +1264,7 @@ void finch_conversation_set_info_widget(PurpleConversation *conv, GntWidget *widget) { - FinchConv *fc = conv->ui_data; + FinchConv *fc = FINCH_GET_DATA(conv); int height, width; gnt_box_remove_all(GNT_BOX(fc->info));
--- a/finch/gntft.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntft.c Wed Jan 30 10:53:22 2008 +0000 @@ -23,13 +23,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "finch.h" + #include <gnt.h> #include <gntbox.h> #include <gntbutton.h> #include <gntcheckbox.h> #include <gntlabel.h> #include <gnttree.h> -#include "internal.h" #include "debug.h" #include "notify.h" @@ -41,7 +42,7 @@ #include "prefs.h" #define FINCHXFER(xfer) \ - (PurpleGntXferUiData *)(xfer)->ui_data + (PurpleGntXferUiData *)FINCH_GET_DATA(xfer) typedef struct { @@ -168,9 +169,15 @@ stop_button_cb(GntButton *button) { PurpleXfer *selected_xfer = gnt_tree_get_selection_data(GNT_TREE(xfer_dialog->tree)); - if (selected_xfer && selected_xfer->status != PURPLE_XFER_STATUS_CANCEL_LOCAL && - selected_xfer->status != PURPLE_XFER_STATUS_CANCEL_REMOTE && - selected_xfer->status != PURPLE_XFER_STATUS_DONE) + PurpleXferStatusType status; + + if (!selected_xfer) + return; + + status = purple_xfer_get_status(selected_xfer); + if (status != PURPLE_XFER_STATUS_CANCEL_LOCAL && + status != PURPLE_XFER_STATUS_CANCEL_REMOTE && + status != PURPLE_XFER_STATUS_DONE) purple_xfer_cancel_local(selected_xfer); } @@ -397,14 +404,12 @@ time_t elapsed, now; char *kbsec; - if (xfer->end_time != 0) - now = xfer->end_time; - else + if ((now = purple_xfer_get_end_time(xfer)) == 0) now = time(NULL); kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0; kb_rem = purple_xfer_get_bytes_remaining(xfer) / 1024.0; - elapsed = (xfer->start_time > 0 ? now - xfer->start_time : 0); + elapsed = (purple_xfer_get_start_time(xfer) > 0 ? now - purple_xfer_get_start_time(xfer) : 0); kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0); g_return_if_fail(xfer_dialog != NULL); @@ -463,7 +468,7 @@ /* This is where we're setting xfer->ui_data for the first time. */ data = g_new0(PurpleGntXferUiData, 1); - xfer->ui_data = data; + FINCH_SET_DATA(xfer, data); } static void @@ -475,7 +480,7 @@ if (data) { g_free(data->name); g_free(data); - xfer->ui_data = NULL; + FINCH_SET_DATA(xfer, NULL); } }
--- a/finch/gntidle.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntidle.c Wed Jan 30 10:53:22 2008 +0000 @@ -21,7 +21,7 @@ * */ -#include "internal.h" +#include "finch.h" #include "gntidle.h" #include "gntwm.h"
--- a/finch/gntpounce.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntpounce.c Wed Jan 30 10:53:22 2008 +0000 @@ -35,7 +35,6 @@ #include <gnttree.h> #include <gntutils.h> -#include "internal.h" #include "finch.h" #include "account.h" @@ -808,39 +807,42 @@ if (purple_pounce_action_is_enabled(pounce, "popup-notify")) { - char *tmp; + char *tmp = NULL; const char *name_shown; const char *reason; + struct { + PurplePounceEvent event; + const char *format; + } messages[] = { + {PURPLE_POUNCE_TYPING, _("%s has started typing to you (%s)")}, + {PURPLE_POUNCE_TYPED, _("%s has paused while typing to you (%s)")}, + {PURPLE_POUNCE_SIGNON, _("%s has signed on (%s)")}, + {PURPLE_POUNCE_IDLE_RETURN, _("%s has returned from being idle (%s)")}, + {PURPLE_POUNCE_AWAY_RETURN, _("%s has returned from being away (%s)")}, + {PURPLE_POUNCE_TYPING_STOPPED, _("%s has stopped typing to you (%s)")}, + {PURPLE_POUNCE_SIGNOFF, _("%s has signed off (%s)")}, + {PURPLE_POUNCE_IDLE, _("%s has become idle (%s)")}, + {PURPLE_POUNCE_AWAY, _("%s has gone away. (%s)")}, + {PURPLE_POUNCE_MESSAGE_RECEIVED, _("%s has sent you a message. (%s)")}, + {0, NULL} + }; + int i; reason = purple_pounce_action_get_attribute(pounce, "popup-notify", - "reason"); + "reason"); /* * Here we place the protocol name in the pounce dialog to lessen * confusion about what protocol a pounce is for. */ - tmp = g_strdup_printf( - (events & PURPLE_POUNCE_TYPING) ? - _("%s has started typing to you (%s)") : - (events & PURPLE_POUNCE_TYPED) ? - _("%s has paused while typing to you (%s)") : - (events & PURPLE_POUNCE_SIGNON) ? - _("%s has signed on (%s)") : - (events & PURPLE_POUNCE_IDLE_RETURN) ? - _("%s has returned from being idle (%s)") : - (events & PURPLE_POUNCE_AWAY_RETURN) ? - _("%s has returned from being away (%s)") : - (events & PURPLE_POUNCE_TYPING_STOPPED) ? - _("%s has stopped typing to you (%s)") : - (events & PURPLE_POUNCE_SIGNOFF) ? - _("%s has signed off (%s)") : - (events & PURPLE_POUNCE_IDLE) ? - _("%s has become idle (%s)") : - (events & PURPLE_POUNCE_AWAY) ? - _("%s has gone away. (%s)") : - (events & PURPLE_POUNCE_MESSAGE_RECEIVED) ? - _("%s has sent you a message. (%s)") : - _("Unknown pounce event. Please report this!"), - alias, purple_account_get_protocol_name(account)); + for (i = 0; messages[i].format != NULL; i++) { + if (messages[i].event & events) { + tmp = g_strdup_printf(messages[i].format, alias, + purple_account_get_protocol_name(account)); + break; + } + } + if (tmp == NULL) + tmp = g_strdup(_("Unknown pounce event. Please report this!")); /* * Ok here is where I change the second argument, title, from @@ -880,7 +882,7 @@ purple_conversation_write(conv, NULL, message, PURPLE_MESSAGE_SEND, time(NULL)); - serv_send_im(account->gc, (char *)pouncee, (char *)message, 0); + serv_send_im(purple_account_get_connection(account), (char *)pouncee, (char *)message, 0); } }
--- a/finch/gntrequest.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntrequest.c Wed Jan 30 10:53:22 2008 +0000 @@ -253,7 +253,7 @@ void *user_data, size_t actioncount, va_list actions) { - GntWidget *window, *box, *button; + GntWidget *window, *box, *button, *focus = NULL; int i; window = setup_request_window(title, primary, secondary, PURPLE_REQUEST_ACTION); @@ -272,9 +272,14 @@ g_object_set_data(G_OBJECT(button), "activate-userdata", user_data); g_object_set_data(G_OBJECT(button), "activate-id", GINT_TO_POINTER(i)); g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(request_action_cb), window); + + if (i == default_value) + focus = button; } gnt_widget_show(window); + if (focus) + gnt_box_give_focus_to_child(GNT_BOX(window), focus); return window; } @@ -303,26 +308,26 @@ PurpleRequestFieldType type = purple_request_field_get_type(field); if (type == PURPLE_REQUEST_FIELD_BOOLEAN) { - GntWidget *check = field->ui_data; + GntWidget *check = FINCH_GET_DATA(field); gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(check)); purple_request_field_bool_set_value(field, value); } else if (type == PURPLE_REQUEST_FIELD_STRING) { - GntWidget *entry = field->ui_data; + GntWidget *entry = FINCH_GET_DATA(field); const char *text = gnt_entry_get_text(GNT_ENTRY(entry)); purple_request_field_string_set_value(field, (text && *text) ? text : NULL); } else if (type == PURPLE_REQUEST_FIELD_INTEGER) { - GntWidget *entry = field->ui_data; + GntWidget *entry = FINCH_GET_DATA(field); const char *text = gnt_entry_get_text(GNT_ENTRY(entry)); int value = (text && *text) ? atoi(text) : 0; purple_request_field_int_set_value(field, value); } else if (type == PURPLE_REQUEST_FIELD_CHOICE) { - GntWidget *combo = field->ui_data; + GntWidget *combo = FINCH_GET_DATA(field); int id; id = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo))); purple_request_field_choice_set_value(field, id); @@ -333,7 +338,7 @@ if (purple_request_field_list_get_multi_select(field)) { GList *iter; - GntWidget *tree = field->ui_data; + GntWidget *tree = FINCH_GET_DATA(field); iter = purple_request_field_list_get_items(field); for (; iter; iter = iter->next) @@ -346,7 +351,7 @@ } else { - GntWidget *combo = field->ui_data; + GntWidget *combo = FINCH_GET_DATA(field); gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)); list = g_list_append(list, data); } @@ -356,7 +361,7 @@ } else if (type == PURPLE_REQUEST_FIELD_ACCOUNT) { - GntWidget *combo = field->ui_data; + GntWidget *combo = FINCH_GET_DATA(field); PurpleAccount *acc = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)); purple_request_field_account_set_value(field, acc); } @@ -419,9 +424,10 @@ *screenname = entry; } else if (hint && !strcmp(hint, "group")) { PurpleBlistNode *node; - for (node = purple_blist_get_root(); node; node = node->next) { + for (node = purple_blist_get_root(); node; + node = purple_blist_node_get_sibling_next(node)) { if (PURPLE_BLIST_NODE_IS_GROUP(node)) - gnt_entry_add_suggest(GNT_ENTRY(entry), ((PurpleGroup *)node)->name); + gnt_entry_add_suggest(GNT_ENTRY(entry), purple_group_get_name((PurpleGroup *)node)); } } return entry; @@ -588,35 +594,35 @@ if (type == PURPLE_REQUEST_FIELD_BOOLEAN) { - field->ui_data = create_boolean_field(field); + FINCH_SET_DATA(field, create_boolean_field(field)); } else if (type == PURPLE_REQUEST_FIELD_STRING) { - field->ui_data = create_string_field(field, &screenname); + FINCH_SET_DATA(field, create_string_field(field, &screenname)); } else if (type == PURPLE_REQUEST_FIELD_INTEGER) { - field->ui_data = create_integer_field(field); + FINCH_SET_DATA(field, create_integer_field(field)); } else if (type == PURPLE_REQUEST_FIELD_CHOICE) { - field->ui_data = create_choice_field(field); + FINCH_SET_DATA(field, create_choice_field(field)); } else if (type == PURPLE_REQUEST_FIELD_LIST) { - field->ui_data = create_list_field(field); + FINCH_SET_DATA(field, create_list_field(field)); } else if (type == PURPLE_REQUEST_FIELD_ACCOUNT) { - accountlist = field->ui_data = create_account_field(field); + accountlist = FINCH_SET_DATA(field, create_account_field(field)); } else { - field->ui_data = gnt_label_new_with_format(_("Not implemented yet."), - GNT_TEXT_FLAG_BOLD); + FINCH_SET_DATA(field, gnt_label_new_with_format(_("Not implemented yet."), + GNT_TEXT_FLAG_BOLD)); } gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); - gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(field->ui_data)); + gnt_box_add_widget(GNT_BOX(hbox), GNT_WIDGET(FINCH_GET_DATA(field))); } if (grlist->next) gnt_box_add_widget(GNT_BOX(box), gnt_hline_new());
--- a/finch/gntroomlist.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntroomlist.c Wed Jan 30 10:53:22 2008 +0000 @@ -24,7 +24,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include"internal.h" +#include "finch.h" #include "gntrequest.h" #include "gntroomlist.h" @@ -116,7 +116,7 @@ if (gc == NULL || room == NULL) return; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); if(prpl_info != NULL && prpl_info->roomlist_room_serialize) name = prpl_info->roomlist_room_serialize(room); @@ -238,7 +238,7 @@ PurplePluginProtocolInfo *prpl_info = NULL; PurpleConnection *gc = list->data; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc)); if (prpl_info->roomlist_get_list != NULL) { PurpleAccount *account = purple_connection_get_account(gc); char *text = g_strdup_printf("%s (%s)", @@ -287,10 +287,10 @@ froomlist.accounts = accounts = gnt_combo_box_new(); reset_account_list(account); - gnt_box_add_widget(GNT_BOX(window), froomlist.accounts); - g_signal_connect(G_OBJECT(froomlist.accounts), "selection-changed", + gnt_box_add_widget(GNT_BOX(window), accounts); + g_signal_connect(G_OBJECT(accounts), "selection-changed", G_CALLBACK(roomlist_account_changed), NULL); - froomlist.account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(froomlist.accounts)); + froomlist.account = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(accounts)); froomlist.tree = tree = gnt_tree_new_with_columns(2); gnt_tree_set_show_title(GNT_TREE(tree), TRUE); @@ -341,7 +341,7 @@ static void fl_create(PurpleRoomlist *list) { - list->ui_data = &froomlist; + FINCH_SET_DATA(list, &froomlist); setup_roomlist(NULL); update_roomlist(list); }
--- a/finch/gntsound.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntsound.c Wed Jan 30 10:53:22 2008 +0000 @@ -23,7 +23,6 @@ * 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 "finch.h" #ifdef _WIN32 @@ -142,13 +141,15 @@ char *nick = NULL; char *name = NULL; gboolean ret = FALSE; + PurpleAccount *account; + chat = purple_conversation_get_chat_data(conv); - if (chat == NULL) return ret; - nick = g_strdup(purple_normalize(conv->account, chat->nick)); - name = g_strdup(purple_normalize(conv->account, aname)); + account = purple_conversation_get_account(conv); + nick = g_strdup(purple_normalize(account, chat->nick)); + name = g_strdup(purple_normalize(account, aname)); if (g_utf8_collate(nick, name) == 0) ret = TRUE;
--- a/finch/gntui.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/gntui.c Wed Jan 30 10:53:22 2008 +0000 @@ -19,7 +19,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 "internal.h" +#include "finch.h" #include "gntui.h"
--- a/finch/libgnt/gnttree.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/libgnt/gnttree.c Wed Jan 30 10:53:22 2008 +0000 @@ -39,6 +39,7 @@ { PROP_0, PROP_COLUMNS, + PROP_EXPANDER, }; enum @@ -59,6 +60,7 @@ GCompareFunc compare; int lastvisible; + int expander_level; }; #define TAB_SIZE 3 @@ -338,7 +340,7 @@ row->isselected ? 'X' : ' '); fl = 4; } - else if (row->parent == NULL && row->child) + else if (find_depth(row) < tree->priv->expander_level && row->child) { if (row->collapsed) { @@ -951,6 +953,11 @@ case PROP_COLUMNS: _gnt_tree_init_internals(tree, g_value_get_int(value)); break; + case PROP_EXPANDER: + if (tree->priv->expander_level == g_value_get_int(value)) + break; + tree->priv->expander_level = g_value_get_int(value); + g_object_notify(obj, "expander-level"); default: break; } @@ -965,6 +972,9 @@ case PROP_COLUMNS: g_value_set_int(value, tree->ncol); break; + case PROP_EXPANDER: + g_value_set_int(value, tree->priv->expander_level); + break; default: break; } @@ -995,6 +1005,14 @@ G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB ) ); + g_object_class_install_property(gclass, + PROP_EXPANDER, + g_param_spec_int("expander-level", "Expander level", + "Number of levels to show expander in the tree.", + 0, G_MAXINT, 1, + G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB + ) + ); signals[SIG_SELECTION_CHANGED] = g_signal_new("selection-changed", @@ -1618,6 +1636,7 @@ { GntWidget *widget = g_object_new(GNT_TYPE_TREE, "columns", col, + "expander-level", 1, NULL); return widget; @@ -1841,3 +1860,9 @@ tree->priv->search_func = func; } +gpointer gnt_tree_get_parent_key(GntTree *tree, gpointer key) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + return (row && row->parent) ? row->parent->key : NULL; +} +
--- a/finch/libgnt/gnttree.h Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/libgnt/gnttree.h Wed Jan 30 10:53:22 2008 +0000 @@ -575,6 +575,17 @@ void gnt_tree_set_search_function(GntTree *tree, gboolean (*func)(GntTree *tree, gpointer key, const char *search, const char *current)); +/** + * Get the parent key for a row. + * + * @param tree The tree + * @param key The key for the row. + * + * @return The key of the parent row. + * @since 2.4.0 + */ +gpointer gnt_tree_get_parent_key(GntTree *tree, gpointer key); + G_END_DECLS #endif /* GNT_TREE_H */
--- a/finch/plugins/Makefile.am Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/plugins/Makefile.am Wed Jan 30 10:53:22 2008 +0000 @@ -1,7 +1,8 @@ gntclipboard_la_LDFLAGS = -module -avoid-version gntgf_la_LDFLAGS = -module -avoid-version gnthistory_la_LDFLAGS = -module -avoid-version -gntlastlog_la_LDFLAGS = -module -avoid-version +gntlastlog_la_LDFLAGS = -module -avoid-version +grouping_la_LDFLAGS = -module -avoid-version if PLUGINS @@ -9,7 +10,8 @@ gntclipboard.la \ gntgf.la \ gnthistory.la \ - gntlastlog.la + gntlastlog.la \ + grouping.la plugindir = $(libdir)/finch @@ -17,6 +19,7 @@ gntgf_la_SOURCES = gntgf.c gnthistory_la_SOURCES = gnthistory.c gntlastlog_la_SOURCES = lastlog.c +grouping_la_SOURCES = grouping.c gntclipboard_la_CFLAGS = $(X11_CFLAGS) gntgf_la_CFLAGS = $(X11_CFLAGS) @@ -25,6 +28,7 @@ gntgf_la_LIBADD = $(GLIB_LIBS) $(X11_LIBS) $(top_builddir)/finch/libgnt/libgnt.la gnthistory_la_LIBADD = $(GLIB_LIBS) gntlastlog_la_LIBADD = $(GLIB_LIBS) +grouping_la_LIBADD = $(GLIB_LIBS) $(top_builddir)/finch/libgnt/libgnt.la endif # PLUGINS
--- a/finch/plugins/gnthistory.c Thu Jan 10 05:32:41 2008 +0000 +++ b/finch/plugins/gnthistory.c Wed Jan 30 10:53:22 2008 +0000 @@ -51,8 +51,7 @@ PurpleMessageFlags mflag; convtype = purple_conversation_get_type(c); - if (convtype == PURPLE_CONV_TYPE_IM) - { + if (convtype == PURPLE_CONV_TYPE_IM) { GSList *buddies; GSList *cur; @@ -62,17 +61,17 @@ return; /* Find buddies for this conversation. */ - buddies = purple_find_buddies(account, name); + buddies = purple_find_buddies(account, name); /* If we found at least one buddy, save the first buddy's alias. */ if (buddies != NULL) alias = purple_buddy_get_contact_alias((PurpleBuddy *)buddies->data); - for (cur = buddies; cur != NULL; cur = cur->next) - { - PurpleBlistNode *node = cur->data; - if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL))) - { + for (cur = buddies; cur != NULL; cur = cur->next) { + PurpleBlistNode *node = cur->data; + if ((node != NULL) && + ((purple_blist_node_get_sibling_prev(node) != NULL) || + (purple_blist_node_get_sibling_next(node) != NULL))) { PurpleBlistNode *node2; alias = purple_buddy_get_contact_alias((PurpleBuddy *)node); @@ -80,26 +79,24 @@ /* We've found a buddy that matches this conversation. It's part of a * PurpleContact with more than one PurpleBuddy. Loop through the PurpleBuddies * in the contact and get all the logs. */ - for (node2 = node->parent->child ; node2 != NULL ; node2 = node2->next) - { + for (node2 = purple_blist_node_get_first_child(purple_blist_node_get_parent(node)); + node2 != NULL ; node2 = purple_blist_node_get_sibling_next(node2)) { logs = g_list_concat( - purple_log_get_logs(PURPLE_LOG_IM, - purple_buddy_get_name((PurpleBuddy *)node2), - purple_buddy_get_account((PurpleBuddy *)node2)), - logs); + purple_log_get_logs(PURPLE_LOG_IM, + purple_buddy_get_name((PurpleBuddy *)node2), + purple_buddy_get_account((PurpleBuddy *)node2)), + logs); } break; - } - } - g_slist_free(buddies); + } + } + g_slist_free(buddies); if (logs == NULL) logs = purple_log_get_logs(PURPLE_LOG_IM, name, account); else logs = g_list_sort(logs, purple_log_compare); - } - else if (convtype == PURPLE_CONV_TYPE_CHAT) - { + } else if (convtype == PURPLE_CONV_TYPE_CHAT) { /* If we're not logging, don't show anything. * Otherwise, we might show a very old log. */ if (!purple_prefs_get_bool("/purple/logging/log_chats")) @@ -115,7 +112,7 @@ history = purple_log_read((PurpleLog*)logs->data, &flags); header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), alias, - purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time))); + purple_date_format_full(localtime(&((PurpleLog *)logs->data)->time))); purple_conversation_write(c, "", header, mflag, time(NULL)); g_free(header);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/plugins/grouping.c Wed Jan 30 10:53:22 2008 +0000 @@ -0,0 +1,278 @@ +/** + * @file grouping.c Provides different grouping options. + * + * Copyright (C) 2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define PURPLE_PLUGIN + +#include "internal.h" +#include "purple.h" + +#include "gntblist.h" +#include "gntplugin.h" + +#include "gnttree.h" + +/** + * Online/Offline + */ +static PurpleBlistNode online = {.type = PURPLE_BLIST_OTHER_NODE}, + offline = {.type = PURPLE_BLIST_OTHER_NODE}; + +static gboolean on_offline_init() +{ + GntTree *tree = finch_blist_get_tree(); + + gnt_tree_add_row_after(tree, &online, + gnt_tree_create_row(tree, _("Online")), NULL, NULL); + gnt_tree_add_row_after(tree, &offline, + gnt_tree_create_row(tree, _("Offline")), NULL, &online); + + return TRUE; +} + +static gboolean on_offline_can_add_node(PurpleBlistNode *node) +{ + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_CONTACT_NODE: + { + PurpleContact *contact = (PurpleContact*)node; + if (contact->currentsize > 0) + return TRUE; + return FALSE; + } + break; + case PURPLE_BLIST_BUDDY_NODE: + { + PurpleBuddy *buddy = (PurpleBuddy*)node; + if (PURPLE_BUDDY_IS_ONLINE(buddy)) + return TRUE; + if (purple_prefs_get_bool("/finch/blist/showoffline") && + purple_account_is_connected(purple_buddy_get_account(buddy))) + return TRUE; + return FALSE; + } + break; + case PURPLE_BLIST_CHAT_NODE: + { + PurpleChat *chat = (PurpleChat*)node; + return purple_account_is_connected(purple_chat_get_account(chat)); + } + break; + default: + return FALSE; + } +} + +static gpointer on_offline_find_parent(PurpleBlistNode *node) +{ + gpointer ret = NULL; + + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_CONTACT_NODE: + node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node); + ret = PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node) ? &online : &offline; + break; + case PURPLE_BLIST_BUDDY_NODE: + ret = purple_blist_node_get_parent(node); + finch_blist_manager_add_node(ret); + break; + case PURPLE_BLIST_CHAT_NODE: + ret = &online; + break; + default: + break; + } + return ret; +} + +static gboolean on_offline_create_tooltip(gpointer selected_row, GString **body, char **tool_title) +{ + static FinchBlistManager *def = NULL; + PurpleBlistNode *node = selected_row; + + if (def == NULL) + def = finch_blist_manager_find("default"); + + if (purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE) { + /* There should be some easy way of getting the total online count, + * or total number of chats. Doing a loop here will probably be pretty + * expensive. */ + if (body) + *body = g_string_new(node == &online ? _("Online Buddies") : _("Offline Buddies")); + return TRUE; + } else { + return def ? def->create_tooltip(selected_row, body, tool_title) : FALSE; + } +} + +static FinchBlistManager on_offline = +{ + "on-offline", + N_("Online/Offline"), + on_offline_init, + NULL, + on_offline_can_add_node, + on_offline_find_parent, + on_offline_create_tooltip, + {NULL, NULL, NULL, NULL} +}; + +/** + * Meebo-like Grouping. + */ +static PurpleBlistNode meebo = {.type = PURPLE_BLIST_OTHER_NODE}; +static gboolean meebo_init() +{ + GntTree *tree = finch_blist_get_tree(); + if (!g_list_find(gnt_tree_get_rows(tree), &meebo)) { + gnt_tree_add_row_last(tree, &meebo, + gnt_tree_create_row(tree, _("Offline")), NULL); + } + return TRUE; +} + +static gpointer meebo_find_parent(PurpleBlistNode *node) +{ + static FinchBlistManager *def = NULL; + if (def == NULL) + def = finch_blist_manager_find("default"); + + if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleBuddy *buddy = purple_contact_get_priority_buddy((PurpleContact*)node); + if (buddy && !PURPLE_BUDDY_IS_ONLINE(buddy)) { + return &meebo; + } + } + return def->find_parent(node); +} + +static FinchBlistManager meebo_group = +{ + "meebo", + N_("Meebo"), + meebo_init, + NULL, + NULL, + meebo_find_parent, + NULL, + {NULL, NULL, NULL, NULL} +}; + +/** + * No Grouping. + */ +static gboolean no_group_init() +{ + GntTree *tree = finch_blist_get_tree(); + g_object_set(G_OBJECT(tree), "expander-level", 0, NULL); + return TRUE; +} + +static gboolean no_group_uninit() +{ + GntTree *tree = finch_blist_get_tree(); + g_object_set(G_OBJECT(tree), "expander-level", 1, NULL); + return TRUE; +} + +static gboolean no_group_can_add_node(PurpleBlistNode *node) +{ + return on_offline_can_add_node(node); /* These happen to be the same */ +} + +static gpointer no_group_find_parent(PurpleBlistNode *node) +{ + gpointer ret = NULL; + + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_BUDDY_NODE: + ret = purple_blist_node_get_parent(node); + finch_blist_manager_add_node(ret); + break; + default: + break; + } + return ret; +} + +static FinchBlistManager no_group = +{ + "no-group", + N_("No Grouping"), + no_group_init, + no_group_uninit, + no_group_can_add_node, + no_group_find_parent, + NULL, + {NULL, NULL, NULL, NULL} +}; + +static gboolean +plugin_load(PurplePlugin *plugin) +{ + finch_blist_install_manager(&on_offline); + finch_blist_install_manager(&meebo_group); + finch_blist_install_manager(&no_group); + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin) +{ + finch_blist_uninstall_manager(&on_offline); + finch_blist_uninstall_manager(&meebo_group); + finch_blist_uninstall_manager(&no_group); + return TRUE; +} + +static PurplePluginInfo info = +{ + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_STANDARD, + FINCH_PLUGIN_TYPE, + 0, + NULL, + PURPLE_PRIORITY_DEFAULT, + "grouping", + N_("Grouping"), + VERSION, + N_("Provides alternate buddylist grouping options."), + N_("Provides alternate buddylist grouping options."), + "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", + PURPLE_WEBSITE, + plugin_load, + plugin_unload, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL,NULL,NULL,NULL +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(ignore, init_plugin, info) + +
--- a/libpurple/account.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/account.c Wed Jan 30 10:53:22 2008 +0000 @@ -752,7 +752,7 @@ current_error = g_new0(PurpleConnectionErrorInfo, 1); current_error->type = type; - current_error->description = g_strdup(description); + current_error->description = description; set_current_error(account, current_error); }
--- a/libpurple/account.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/account.h Wed Jan 30 10:53:22 2008 +0000 @@ -410,37 +410,34 @@ void purple_account_set_status_types(PurpleAccount *account, GList *status_types); /** - * Activates or deactivates a status. All changes to the statuses of - * an account go through this function or purple_account_set_status_list. - * - * Only independent statuses can be deactivated with this. To deactivate - * an exclusive status, activate a different (and exclusive?) status. + * Variadic version of purple_account_set_status_list(); the variadic list + * replaces @a attrs, and should be <tt>NULL</tt>-terminated. * - * @param account The account. - * @param status_id The ID of the status. - * @param active The active state. - * @param ... Pairs of attributes for the new status passed in - * as a NULL-terminated list of id/value pairs. + * @copydoc purple_account_set_status_list() */ void purple_account_set_status(PurpleAccount *account, const char *status_id, - gboolean active, ...) G_GNUC_NULL_TERMINATED; + gboolean active, ...) G_GNUC_NULL_TERMINATED; /** * Activates or deactivates a status. All changes to the statuses of - * an account go through this function or purple_account_set_status. + * an account go through this function or purple_account_set_status(). * - * Only independent statuses can be deactivated with this. To deactivate - * an exclusive status, activate a different (and exclusive?) status. + * You can only deactivate an exclusive status by activating another exclusive + * status. So, if @a status_id is an exclusive status and @a active is @c + * FALSE, this function does nothing. * * @param account The account. * @param status_id The ID of the status. - * @param active The active state. - * @param attrs A list of attributes in key/value pairs + * @param active Whether @a status_id is to be activated (<tt>TRUE</tt>) or + * deactivated (<tt>FALSE</tt>). + * @param attrs A list of <tt>const char *</tt> attribute names followed by + * <tt>const char *</tt> attribute values for the status. + * (For example, one pair might be <tt>"message"</tt> followed + * by <tt>"hello, talk to me!"</tt>.) */ void purple_account_set_status_list(PurpleAccount *account, - const char *status_id, - gboolean active, GList *attrs); + const char *status_id, gboolean active, GList *attrs); /** * Clears all protocol-specific settings on an account.
--- a/libpurple/accountopt.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/accountopt.h Wed Jan 30 10:53:22 2008 +0000 @@ -83,7 +83,10 @@ /*@{*/ /** - * Creates a new account option. + * Creates a new account option. If you know what @a type will be in advance, + * consider using purple_account_option_bool_new(), + * purple_account_option_int_new(), purple_account_option_string_new() or + * purple_account_option_list_new() (as appropriate) instead. * * @param type The type of option. * @param text The text of the option. @@ -91,8 +94,8 @@ * * @return The account option. */ -PurpleAccountOption *purple_account_option_new(PurplePrefType type, const char *text, - const char *pref_name); +PurpleAccountOption *purple_account_option_new(PurplePrefType type, + const char *text, const char *pref_name); /** * Creates a new boolean account option. @@ -104,8 +107,7 @@ * @return The account option. */ PurpleAccountOption *purple_account_option_bool_new(const char *text, - const char *pref_name, - gboolean default_value); + const char *pref_name, gboolean default_value); /** * Creates a new integer account option. @@ -117,8 +119,7 @@ * @return The account option. */ PurpleAccountOption *purple_account_option_int_new(const char *text, - const char *pref_name, - int default_value); + const char *pref_name, int default_value); /** * Creates a new string account option. @@ -130,8 +131,7 @@ * @return The account option. */ PurpleAccountOption *purple_account_option_string_new(const char *text, - const char *pref_name, - const char *default_value); + const char *pref_name, const char *default_value); /** * Creates a new list account option. @@ -140,7 +140,7 @@ * strings inside will be freed automatically. * * The list is a list of PurpleKeyValuePair items. The key is the ID stored and - * used internally, and the value is the label displayed. + * used internally, and the <tt>(const char *)</tt> value is the label displayed. * * @param text The text of the option. * @param pref_name The account preference name for the option. @@ -149,8 +149,7 @@ * @return The account option. */ PurpleAccountOption *purple_account_option_list_new(const char *text, - const char *pref_name, - GList *list); + const char *pref_name, GList *list); /** * Destroys an account option. @@ -240,11 +239,13 @@ const char *purple_account_option_get_text(const PurpleAccountOption *option); /** - * Returns the account setting for an account option. + * Returns the name of an account option. This corresponds to the @c pref_name + * parameter supplied to purple_account_option_new() or one of the + * type-specific constructors. * * @param option The account option. * - * @return The account setting. + * @return The option's name. */ const char *purple_account_option_get_setting(const PurpleAccountOption *option);
--- a/libpurple/blist.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/blist.c Wed Jan 30 10:53:22 2008 +0000 @@ -640,10 +640,10 @@ if (purple_account_is_connected(buddy->account)) { - int cmp; - - cmp = purple_presence_compare(purple_buddy_get_presence(new_priority), - purple_buddy_get_presence(buddy)); + int cmp = 1; + if (purple_account_is_connected(new_priority->account)) + cmp = purple_presence_compare(purple_buddy_get_presence(new_priority), + purple_buddy_get_presence(buddy)); if (cmp > 0 || (cmp == 0 && purple_prefs_get_bool("/purple/contact/last_match"))) @@ -753,6 +753,26 @@ return ret; } +PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node) +{ + return node ? node->parent : NULL; +} + +PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node) +{ + return node ? node->child : NULL; +} + +PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node) +{ + return node? node->next : NULL; +} + +PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node) +{ + return node? node->prev : NULL; +} + void purple_blist_update_buddy_status(PurpleBuddy *buddy, PurpleStatus *old_status) { @@ -2232,6 +2252,22 @@ return (PurpleGroup *)(((PurpleBlistNode *)chat)->parent); } +PurpleAccount * +purple_chat_get_account(PurpleChat *chat) +{ + g_return_val_if_fail(chat != NULL, NULL); + + return chat->account; +} + +GHashTable * +purple_chat_get_components(PurpleChat *chat) +{ + g_return_val_if_fail(chat != NULL, NULL); + + return chat->components; +} + PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy) { g_return_val_if_fail(buddy != NULL, NULL);
--- a/libpurple/blist.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/blist.h Wed Jan 30 10:53:22 2008 +0000 @@ -53,13 +53,13 @@ } PurpleBlistNodeType; -#define PURPLE_BLIST_NODE_IS_CHAT(n) ((n)->type == PURPLE_BLIST_CHAT_NODE) -#define PURPLE_BLIST_NODE_IS_BUDDY(n) ((n)->type == PURPLE_BLIST_BUDDY_NODE) -#define PURPLE_BLIST_NODE_IS_CONTACT(n) ((n)->type == PURPLE_BLIST_CONTACT_NODE) -#define PURPLE_BLIST_NODE_IS_GROUP(n) ((n)->type == PURPLE_BLIST_GROUP_NODE) +#define PURPLE_BLIST_NODE_IS_CHAT(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CHAT_NODE) +#define PURPLE_BLIST_NODE_IS_BUDDY(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_BUDDY_NODE) +#define PURPLE_BLIST_NODE_IS_CONTACT(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_CONTACT_NODE) +#define PURPLE_BLIST_NODE_IS_GROUP(n) (purple_blist_node_get_type(n) == PURPLE_BLIST_GROUP_NODE) #define PURPLE_BUDDY_IS_ONLINE(b) \ - ((b) != NULL && purple_account_is_connected((b)->account) && \ + ((b) != NULL && purple_account_is_connected(purple_buddy_get_account(b)) && \ purple_presence_is_online(purple_buddy_get_presence(b))) typedef enum @@ -231,10 +231,66 @@ * @param node A node. * @param offline Whether to include nodes for offline accounts * @return The next node + * @see purple_blist_node_get_parent + * @see purple_blist_node_get_first_child + * @see purple_blist_node_get_sibling_next + * @see purple_blist_node_get_sibling_prev */ PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline); /** + * Returns the parent node of a given node. + * + * @param node A node. + * @return The parent node. + * @since 2.4.0 + * @see purple_blist_node_get_first_child + * @see purple_blist_node_get_sibling_next + * @see purple_blist_node_get_sibling_prev + * @see purple_blist_node_next + */ +PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node); + +/** + * Returns the the first child node of a given node. + * + * @param node A node. + * @return The child node. + * @since 2.4.0 + * @see purple_blist_node_get_parent + * @see purple_blist_node_get_sibling_next + * @see purple_blist_node_get_sibling_prev + * @see purple_blist_node_next + */ +PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node); + +/** + * Returns the sibling node of a given node. + * + * @param node A node. + * @return The sibling node. + * @since 2.4.0 + * @see purple_blist_node_get_parent + * @see purple_blist_node_get_first_child + * @see purple_blist_node_get_sibling_prev + * @see purple_blist_node_next + */ +PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node); + +/** + * Returns the previous sibling node of a given node. + * + * @param node A node. + * @return The sibling node. + * @since 2.4.0 + * @see purple_blist_node_get_parent + * @see purple_blist_node_get_first_child + * @see purple_blist_node_get_sibling_next + * @see purple_blist_node_next + */ +PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node); + +/** * Shows the buddy list, creating a new one if necessary. */ void purple_blist_show(void); @@ -667,6 +723,26 @@ PurpleGroup *purple_chat_get_group(PurpleChat *chat); /** + * Returns the account the chat belongs to. + * + * @param chat The chat. + * + * @return The account the chat belongs to. + * @since 2.4.0 + */ +PurpleAccount *purple_chat_get_account(PurpleChat *chat); + +/** + * Get a hashtable containing information about a chat. + * + * @param chat The chat. + * + * @constreturn The hashtable. + * @since 2.4.0 + */ +GHashTable *purple_chat_get_components(PurpleChat *chat); + +/** * Returns the group of which the buddy is a member. * * @param buddy The buddy
--- a/libpurple/buddyicon.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/buddyicon.c Wed Jan 30 10:53:22 2008 +0000 @@ -701,11 +701,6 @@ } unref_filename(old_icon); - if (img) - g_hash_table_insert(pointer_icon_cache, account, img); - else - g_hash_table_remove(pointer_icon_cache, account); - if (purple_account_is_connected(account)) { PurpleConnection *gc; @@ -729,6 +724,11 @@ } g_free(old_icon); + if (img) + g_hash_table_insert(pointer_icon_cache, account, img); + else + g_hash_table_remove(pointer_icon_cache, account); + return img; }
--- a/libpurple/certificate.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/certificate.c Wed Jan 30 10:53:22 2008 +0000 @@ -568,7 +568,7 @@ _("Single-use Certificate Verification"), primary, secondary, - 1, /* Accept by default */ + 0, /* Accept by default */ NULL, /* No account */ NULL, /* No other user */ NULL, /* No associated conversation */ @@ -1199,7 +1199,7 @@ _("SSL Certificate Verification"), primary, reason, - 2, /* Accept by default */ + 0, /* Accept by default */ NULL, /* No account */ NULL, /* No other user */ NULL, /* No associated conversation */
--- a/libpurple/connection.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/connection.c Wed Jan 30 10:53:22 2008 +0000 @@ -52,8 +52,19 @@ PurpleConnection *gc = data; PurplePluginProtocolInfo *prpl_info = NULL; - if (gc != NULL && gc->prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + if (gc == NULL) + return TRUE; + + /* Only send keep-alives if we haven't heard from the + * server in a while. + */ + if ((time(NULL) - gc->last_received) < KEEPALIVE_INTERVAL) + return TRUE; + + if (gc->prpl == NULL) + return TRUE; + + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); if (prpl_info && prpl_info->keepalive) prpl_info->keepalive(gc); @@ -413,6 +424,14 @@ return gc->account; } +PurplePlugin * +purple_connection_get_prpl(const PurpleConnection *gc) +{ + g_return_val_if_fail(gc != NULL, NULL); + + return gc->prpl; +} + const char * purple_connection_get_password(const PurpleConnection *gc) {
--- a/libpurple/connection.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/connection.h Wed Jan 30 10:53:22 2008 +0000 @@ -251,6 +251,8 @@ gboolean wants_to_die; guint disconnect_timeout; /**< Timer used for nasty stack tricks */ + time_t last_received; /**< When we last received a packet. Set by the + prpl to avoid sending unneeded keepalives */ }; #ifdef __cplusplus @@ -362,7 +364,7 @@ * @return TRUE if the account is connected, otherwise returns FALSE. */ #define PURPLE_CONNECTION_IS_CONNECTED(gc) \ - (gc->state == PURPLE_CONNECTED) + (purple_connection_get_state(gc) == PURPLE_CONNECTED) /** * Returns the connection's account. @@ -374,6 +376,16 @@ PurpleAccount *purple_connection_get_account(const PurpleConnection *gc); /** + * Returns the protocol plugin managing a connection. + * + * @param gc The connection. + * + * @return The protocol plugin. + * @since 2.4.0 + */ +PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc); + +/** * Returns the connection's password. * * @param gc The connection.
--- a/libpurple/dbus-analyze-functions.py Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/dbus-analyze-functions.py Wed Jan 30 10:53:22 2008 +0000 @@ -525,9 +525,10 @@ try: self.processfunction(functiontext, paramtexts) except MyException: - sys.stderr.write(myline + "\n") +# sys.stderr.write(myline + "\n") + pass except: - sys.stderr.write(myline + "\n") +# sys.stderr.write(myline + "\n") raise self.flush() @@ -586,7 +587,7 @@ else: fprefix = "" -sys.stderr.write("%s: Functions not exported:\n" % sys.argv[0]) +#sys.stderr.write("%s: Functions not exported:\n" % sys.argv[0]) if "client" in options: bindings = ClientBindingSet(sys.stdin, fprefix,
--- a/libpurple/dbus-server.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/dbus-server.c Wed Jan 30 10:53:22 2008 +0000 @@ -689,6 +689,7 @@ switch (purple_values[i]->type) { case PURPLE_TYPE_INT: + case PURPLE_TYPE_ENUM: xint = my_arg(gint); dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &xint); break; @@ -729,7 +730,7 @@ if (id == 0 && val != NULL) error = TRUE; /* Some error happened. */ dbus_message_iter_append_basic(iter, - (sizeof(void *) == 4) ? DBUS_TYPE_UINT32 : DBUS_TYPE_UINT64, &id); + (sizeof(id) == sizeof(dbus_int32_t)) ? DBUS_TYPE_INT32 : DBUS_TYPE_INT64, &id); break; default: /* no conversion implemented */ g_return_val_if_reached(TRUE);
--- a/libpurple/dnsquery.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/dnsquery.c Wed Jan 30 10:53:22 2008 +0000 @@ -159,6 +159,24 @@ } #endif +static void +write_to_parent(int fd, const void *buf, size_t count) +{ + ssize_t written; + + written = write(fd, buf, count); + if (written != count) { + if (written < 0) + fprintf(stderr, "dns[%d]: Error writing data to " + "parent: %s\n", getpid(), strerror(errno)); + else + fprintf(stderr, "dns[%d]: Error: Tried to write %" + G_GSIZE_FORMAT " bytes to parent but instead " + "wrote %" G_GSIZE_FORMAT " bytes\n", + getpid(), count, written); + } +} + G_GNUC_NORETURN static void purple_dnsquery_resolver_run(int child_out, int child_in, gboolean show_debug) { @@ -201,7 +219,8 @@ } rc = read(child_in, &dns_params, sizeof(dns_params_t)); if (rc < 0) { - perror("read()"); + fprintf(stderr, "dns[%d]: Error: Could not read dns_params: " + "%s\n", getpid(), strerror(errno)); break; } if (rc == 0) { @@ -210,11 +229,13 @@ _exit(0); } if (dns_params.hostname[0] == '\0') { - printf("dns[%d]: hostname = \"\" (port = %d)!!!\n", getpid(), dns_params.port); + fprintf(stderr, "dns[%d]: Error: Parent requested resolution " + "of an empty hostname (port = %d)!!!\n", getpid(), + dns_params.port); _exit(1); } /* Tell our parent that we read the data successfully */ - write(child_out, &ch, sizeof(ch)); + write_to_parent(child_out, &ch, sizeof(ch)); /* We have the hostname and port, now resolve the IP */ @@ -230,7 +251,7 @@ */ hints.ai_socktype = SOCK_STREAM; rc = getaddrinfo(dns_params.hostname, servname, &hints, &res); - write(child_out, &rc, sizeof(rc)); + write_to_parent(child_out, &rc, sizeof(rc)); if (rc != 0) { close(child_out); if (show_debug) @@ -242,17 +263,17 @@ tmp = res; while (res) { size_t ai_addrlen = res->ai_addrlen; - write(child_out, &ai_addrlen, sizeof(ai_addrlen)); - write(child_out, res->ai_addr, res->ai_addrlen); + write_to_parent(child_out, &ai_addrlen, sizeof(ai_addrlen)); + write_to_parent(child_out, res->ai_addr, res->ai_addrlen); res = res->ai_next; } freeaddrinfo(tmp); - write(child_out, &zero, sizeof(zero)); + write_to_parent(child_out, &zero, sizeof(zero)); #else if (!inet_aton(dns_params.hostname, &sin.sin_addr)) { struct hostent *hp; if (!(hp = gethostbyname(dns_params.hostname))) { - write(child_out, &h_errno, sizeof(int)); + write_to_parent(child_out, &h_errno, sizeof(int)); close(child_out); if (show_debug) printf("DNS Error: %d\n", h_errno); @@ -265,10 +286,10 @@ sin.sin_family = AF_INET; sin.sin_port = htons(dns_params.port); - write(child_out, &zero, sizeof(zero)); - write(child_out, &addrlen, sizeof(addrlen)); - write(child_out, &sin, addrlen); - write(child_out, &zero, sizeof(zero)); + write_to_parent(child_out, &zero, sizeof(zero)); + write_to_parent(child_out, &addrlen, sizeof(addrlen)); + write_to_parent(child_out, &sin, addrlen); + write_to_parent(child_out, &zero, sizeof(zero)); #endif dns_params.hostname[0] = '\0'; }
--- a/libpurple/dnssrv.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/dnssrv.c Wed Jan 30 10:53:22 2008 +0000 @@ -200,12 +200,20 @@ PurpleSrvCallback cb = query_data->cb; int status; - if (read(source, &size, sizeof(int)) > 0) + if (read(source, &size, sizeof(int)) == sizeof(int)) { + ssize_t red; purple_debug_info("dnssrv","found %d SRV entries\n", size); tmp = res = g_new0(PurpleSrvResponse, size); for (i = 0; i < size; i++) { - read(source, tmp++, sizeof(PurpleSrvResponse)); + red = read(source, tmp++, sizeof(PurpleSrvResponse)); + if (red != sizeof(PurpleSrvResponse)) { + purple_debug_error("dnssrv","unable to read srv " + "response: %s\n", g_strerror(errno)); + size = 0; + g_free(res); + res = NULL; + } } } else @@ -307,7 +315,7 @@ /* back to main thread */ /* Note: this should *not* be attached to query_data->handle - it will cause leakage */ - g_idle_add(res_main_thread_cb, query_data); + purple_timeout_add(0, res_main_thread_cb, query_data); g_thread_exit(NULL); return NULL; @@ -350,9 +358,12 @@ /* Child */ if (pid == 0) { + g_free(query); + close(out[0]); close(in[1]); resolve(in[0], out[1]); + /* resolve() does not return */ } close(out[1]); @@ -399,7 +410,7 @@ * Asynchronously call the callback since stuff may not expect * the callback to be called before this returns */ if (query_data->error_message != NULL) - query_data->handle = g_idle_add(res_main_thread_cb, query_data); + query_data->handle = purple_timeout_add(0, res_main_thread_cb, query_data); return query_data; #endif
--- a/libpurple/example/Makefile.am Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/example/Makefile.am Wed Jan 30 10:53:22 2008 +0000 @@ -17,7 +17,8 @@ -DLIBDIR=\"$(libdir)/purple-$(PURPLE_MAJOR_VERSION)/\" \ -DLOCALEDIR=\"$(datadir)/locale\" \ -DSYSCONFDIR=\"$(sysconfdir)\" \ - -I$(top_srcdir)/libpurple/ \ + -I$(top_builddir)/libpurple \ + -I$(top_srcdir)/libpurple \ -I$(top_srcdir) \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \
--- a/libpurple/example/nullclient.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/example/nullclient.c Wed Jan 30 10:53:22 2008 +0000 @@ -21,28 +21,11 @@ * */ -/* XXX: we probably shouldn't include internal.h in examples */ -#include "internal.h" - -#include "account.h" -#include "conversation.h" -#include "core.h" -#include "debug.h" -#include "eventloop.h" -#include "ft.h" -#include "log.h" -#include "notify.h" -#include "prefs.h" -#include "prpl.h" -#include "pounce.h" -#include "savedstatuses.h" -#include "sound.h" -#include "status.h" -#include "util.h" -#include "whiteboard.h" +#include "purple.h" #include <glib.h> +#include <signal.h> #include <string.h> #include <unistd.h> @@ -268,6 +251,7 @@ GMainLoop *loop = g_main_loop_new(NULL, FALSE); PurpleAccount *account; PurpleSavedStatus *status; + char *res; /* libpurple's built-in DNS resolution forks processes to perform * blocking lookups without blocking the main process. It does not @@ -290,12 +274,20 @@ } } printf("Select the protocol [0-%d]: ", i-1); - fgets(name, sizeof(name), stdin); + res = fgets(name, sizeof(name), stdin); + if (!res) { + fprintf(stderr, "Failed to gets protocol selection."); + abort(); + } sscanf(name, "%d", &num); prpl = g_list_nth_data(names, num); printf("Username: "); - fgets(name, sizeof(name), stdin); + res = fgets(name, sizeof(name), stdin); + if (!res) { + fprintf(stderr, "Failed to read user name."); + abort(); + } name[strlen(name) - 1] = 0; /* strip the \n at the end */ /* Create the account */
--- a/libpurple/ft.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/ft.c Wed Jan 30 10:53:22 2008 +0000 @@ -32,6 +32,7 @@ #include "proxy.h" #include "request.h" #include "util.h" +#include "debug.h" #define FT_INITIAL_BUFFER_SIZE 4096 #define FT_MAX_BUFFER_SIZE 65535 @@ -668,6 +669,22 @@ return xfer->remote_port; } +time_t +purple_xfer_get_start_time(const PurpleXfer *xfer) +{ + g_return_val_if_fail(xfer != NULL, 0); + + return xfer->start_time; +} + +time_t +purple_xfer_get_end_time(const PurpleXfer *xfer) +{ + g_return_val_if_fail(xfer != NULL, 0); + + return xfer->end_time; +} + void purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed) { @@ -903,7 +920,12 @@ if (condition & PURPLE_INPUT_READ) { r = purple_xfer_read(xfer, &buffer); if (r > 0) { - fwrite(buffer, 1, r, xfer->dest_fp); + const size_t wc = fwrite(buffer, 1, r, xfer->dest_fp); + if (wc != r) { + purple_debug_error("filetransfer", "Unable to write whole buffer.\n"); + purple_xfer_cancel_remote(xfer); + return; + } } else if(r < 0) { purple_xfer_cancel_remote(xfer); return; @@ -911,6 +933,7 @@ } if (condition & PURPLE_INPUT_WRITE) { + size_t result; size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); /* this is so the prpl can keep the connection open @@ -925,7 +948,13 @@ buffer = g_malloc0(s); - fread(buffer, 1, s, xfer->dest_fp); + result = fread(buffer, 1, s, xfer->dest_fp); + if (result != s) { + purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); + purple_xfer_cancel_remote(xfer); + g_free(buffer); + return; + } /* Write as much as we're allowed to. */ r = purple_xfer_write(xfer, buffer, s);
--- a/libpurple/ft.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/ft.h Wed Jan 30 10:53:22 2008 +0000 @@ -358,6 +358,26 @@ unsigned int purple_xfer_get_remote_port(const PurpleXfer *xfer); /** + * Returns the time the transfer of a file started. + * + * @param xfer The file transfer. + * + * @return The time when the transfer started. + * @since 2.4.0 + */ +time_t purple_xfer_get_start_time(const PurpleXfer *xfer); + +/** + * Returns the time the transfer of a file ended. + * + * @param xfer The file transfer. + * + * @return The time when the transfer ended. + * @since 2.4.0 + */ +time_t purple_xfer_get_end_time(const PurpleXfer *xfer); + +/** * Sets the completed state for the file transfer. * * @param xfer The file transfer.
--- a/libpurple/idle.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/idle.c Wed Jan 30 10:53:22 2008 +0000 @@ -24,6 +24,7 @@ #include "connection.h" #include "debug.h" +#include "eventloop.h" #include "idle.h" #include "log.h" #include "prefs.h" @@ -333,7 +334,7 @@ /* Initialize the idleness asynchronously so it doesn't check idleness, * and potentially try to change the status before the UI is initialized */ - g_idle_add(_do_purple_idle_touch_cb, NULL); + purple_timeout_add(0, _do_purple_idle_touch_cb, NULL); }
--- a/libpurple/log.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/log.c Wed Jan 30 10:53:22 2008 +0000 @@ -1859,11 +1859,15 @@ static char * old_logger_read (PurpleLog *log, PurpleLogReadFlags *flags) { + size_t result; struct old_logger_data *data = log->logger_data; - FILE *file = g_fopen(purple_stringref_value(data->pathref), "rb"); + const char *path = purple_stringref_value(data->pathref); + FILE *file = g_fopen(path, "rb"); char *read = g_malloc(data->length + 1); fseek(file, data->offset, SEEK_SET); - fread(read, data->length, 1, file); + result = fread(read, data->length, 1, file); + if (result != 1) + purple_debug_error("log", "Unable to read from log file: %s\n", path); fclose(file); read[data->length] = '\0'; *flags = 0;
--- a/libpurple/plugins/offlinemsg.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/plugins/offlinemsg.c Wed Jan 30 10:53:22 2008 +0000 @@ -159,7 +159,7 @@ purple_request_action(handle, _("Offline Message"), ask, _("You can edit/delete the pounce from the `Buddy Pounces' dialog"), - 1, + 0, offline->account, offline->who, offline->conv, offline, 2, _("Yes"), record_pounce,
--- a/libpurple/plugins/tcl/tcl_cmds.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/plugins/tcl/tcl_cmds.c Wed Jan 30 10:53:22 2008 +0000 @@ -25,6 +25,7 @@ #include "internal.h" #include "conversation.h" #include "connection.h" +#include "eventloop.h" #include "account.h" #include "server.h" #include "notify.h" @@ -1778,7 +1779,7 @@ } /* We can't unload immediately, but we can unload at the first * known safe opportunity. */ - g_idle_add(unload_self, (gpointer)plugin); + purple_timeout_add(0, unload_self, (gpointer)plugin); return TCL_OK; }
--- a/libpurple/protocols/bonjour/bonjour.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Wed Jan 30 10:53:22 2008 +0000 @@ -673,11 +673,12 @@ /* Try to figure out a good host name to use */ /* TODO: Avoid 'localhost,' if possible */ - if (gethostname(hostname, 255) != 0) { + if (gethostname(hostname, sizeof(hostname)) != 0) { purple_debug_warning("bonjour", "Error when getting host name: %s. Using \"localhost.\"\n", g_strerror(errno)); strcpy(hostname, "localhost"); } + hostname[sizeof(hostname) - 1] = '\0'; default_hostname = g_strdup(hostname); }
--- a/libpurple/protocols/bonjour/jabber.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Wed Jan 30 10:53:22 2008 +0000 @@ -995,7 +995,7 @@ bb->conversation = NULL; } - purple_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv); + bconv->close_timeout = purple_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv); } void @@ -1054,6 +1054,9 @@ if (bconv->context != NULL) bonjour_parser_setup(bconv); + if (bconv->close_timeout != 0) + purple_timeout_remove(bconv->close_timeout); + g_free(bconv->buddy_name); g_free(bconv->ip); g_free(bconv);
--- a/libpurple/protocols/bonjour/jabber.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/bonjour/jabber.h Wed Jan 30 10:53:22 2008 +0000 @@ -47,6 +47,7 @@ gint socket; guint rx_handler; guint tx_handler; + guint close_timeout; PurpleCircBuffer *tx_buf; int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */ gboolean recv_stream_start;
--- a/libpurple/protocols/gg/gg.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/gg/gg.c Wed Jan 30 10:53:22 2008 +0000 @@ -1314,7 +1314,7 @@ _("Unable to read socket")); return; } - + gc->last_received = time(NULL); switch (ev->type) { case GG_EVENT_NONE: /* Nothing happened. */
--- a/libpurple/protocols/irc/dcc_send.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/irc/dcc_send.c Wed Jan 30 10:53:22 2008 +0000 @@ -51,9 +51,14 @@ */ static void irc_dccsend_recv_ack(PurpleXfer *xfer, const guchar *data, size_t size) { unsigned long l; + size_t result; l = htonl(xfer->bytes_sent); - write(xfer->fd, &l, sizeof(l)); + result = write(xfer->fd, &l, sizeof(l)); + if (result != sizeof(l)) { + purple_debug_error("irc", "unable to send acknowledgement: %s\n", g_strerror(errno)); + /* TODO: We should probably close the connection here or something. */ + } } static void irc_dccsend_recv_init(PurpleXfer *xfer) {
--- a/libpurple/protocols/irc/irc.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/irc/irc.c Wed Jan 30 10:53:22 2008 +0000 @@ -572,6 +572,7 @@ { char *cur, *end; + irc->account->gc->last_received = time(NULL); irc->inbufused += len; irc->inbuf[irc->inbufused] = '\0';
--- a/libpurple/protocols/jabber/auth.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/jabber/auth.c Wed Jan 30 10:53:22 2008 +0000 @@ -325,7 +325,7 @@ purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, - 2, js->gc->account, NULL, NULL, js->gc->account, + 1, js->gc->account, NULL, NULL, js->gc->account, allow_cyrus_plaintext_auth, disallow_plaintext_auth); g_free(msg); @@ -344,6 +344,7 @@ * Doing otherwise means that simply compiling with SASL support renders the client unable to connect to servers * which would connect without issue otherwise. -evands */ + js->auth_type = JABBER_AUTH_IQ_AUTH; jabber_auth_start_old(js); return; } @@ -527,7 +528,7 @@ purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), msg, - 2, + 1, purple_connection_get_account(js->gc), NULL, NULL, purple_connection_get_account(js->gc), allow_plaintext_auth, disallow_plaintext_auth); @@ -719,7 +720,7 @@ purple_request_yes_no(js->gc, _("Plaintext Authentication"), _("Plaintext Authentication"), _("This server requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"), - 2, + 1, purple_connection_get_account(js->gc), NULL, NULL, purple_connection_get_account(js->gc), allow_plaintext_auth, disallow_plaintext_auth);
--- a/libpurple/protocols/jabber/jabber.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Wed Jan 30 10:53:22 2008 +0000 @@ -433,6 +433,7 @@ } while((len = purple_ssl_read(gsc, buf, sizeof(buf) - 1)) > 0) { + gc->last_received = time(NULL); buf[len] = '\0'; purple_debug(PURPLE_DEBUG_INFO, "jabber", "Recv (ssl)(%d): %s\n", len, buf); jabber_parser_process(js, buf, len); @@ -460,6 +461,7 @@ return; if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) { + gc->last_received = time(NULL); #ifdef HAVE_CYRUS_SASL if (js->sasl_maxbuf>0) { const char *out; @@ -2302,14 +2304,10 @@ GList *jabber_attention_types(PurpleAccount *account) { static GList *types = NULL; - PurpleAttentionType *attn; if (!types) { - attn = g_new0(PurpleAttentionType, 1); - attn->name = _("Buzz"); - attn->incoming_description = _("%s has buzzed you!"); - attn->outgoing_description = _("Buzzing %s..."); - types = g_list_append(types, attn); + types = g_list_append(types, purple_attention_type_new("Buzz", _("Buzz"), + _("%s has buzzed you!"), _("Buzzing %s..."))); } return types;
--- a/libpurple/protocols/jabber/si.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/jabber/si.c Wed Jan 30 10:53:22 2008 +0000 @@ -1100,7 +1100,7 @@ } else { /* we've got multiple resources, we need to pick one to send to */ GList *l; - char *msg = g_strdup_printf(_("Please select which resource of %s you would like to send a file to"), xfer->who); + char *msg = g_strdup_printf(_("Please select the resource of %s to which you would like to send a file"), xfer->who); PurpleRequestFields *fields = purple_request_fields_new(); PurpleRequestField *field = purple_request_field_choice_new("resource", _("Resource"), 0); PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
--- a/libpurple/protocols/msn/msn.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/msn/msn.c Wed Jan 30 10:53:22 2008 +0000 @@ -122,15 +122,11 @@ static GList * msn_attention_types(PurpleAccount *account) { - PurpleAttentionType *attn; static GList *list = NULL; if (!list) { - attn = g_new0(PurpleAttentionType, 1); - attn->name = _("Nudge"); - attn->incoming_description = _("%s has nudged you!"); - attn->outgoing_description = _("Nudging %s..."); - list = g_list_append(list, attn); + list = g_list_append(list, purple_attention_type_new("Nudge", _("Nudge"), + _("%s has nudged you!"), _("Nudging %s..."))); } return list; @@ -374,7 +370,7 @@ _("Do you want to allow or disallow people on " "your buddy list to send you MSN Mobile pages " "to your cell phone or other mobile device?"), - -1, + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), NULL, NULL, gc, 3, _("Allow"), G_CALLBACK(enable_msn_pages_cb),
--- a/libpurple/protocols/msn/servconn.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/msn/servconn.c Wed Jan 30 10:53:22 2008 +0000 @@ -391,6 +391,7 @@ session = servconn->session; len = read(servconn->fd, buf, sizeof(buf) - 1); + servconn->session->account->gc->last_received = time(NULL); if (len <= 0) { switch (errno) {
--- a/libpurple/protocols/msnp9/msn.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/msnp9/msn.c Wed Jan 30 10:53:22 2008 +0000 @@ -27,6 +27,7 @@ #include "msn.h" #include "accountopt.h" +#include "eventloop.h" #include "msg.h" #include "page.h" #include "pluginpref.h" @@ -122,15 +123,11 @@ static GList * msn_attention_types(PurpleAccount *account) { - PurpleAttentionType *attn; static GList *list = NULL; if (!list) { - attn = g_new0(PurpleAttentionType, 1); - attn->name = _("Nudge"); - attn->incoming_description = _("%s has nudged you!"); - attn->outgoing_description = _("Nudging %s..."); - list = g_list_append(list, attn); + list = g_list_append(list, purple_attention_type_new("Nudge", _("Nudge"), + _("%s has nudged you!"), _("Nudging %s..."))); } return list; @@ -351,7 +348,7 @@ _("Do you want to allow or disallow people on " "your buddy list to send you MSN Mobile pages " "to your cell phone or other mobile device?"), - -1, + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), NULL, NULL, gc, 3, _("Allow"), G_CALLBACK(enable_msn_pages_cb), @@ -860,7 +857,7 @@ imdata->msg = body_str; imdata->flags = flags; imdata->when = time(NULL); - g_idle_add(msn_send_me_im, imdata); + purple_timeout_add(0, msn_send_me_im, imdata); } msn_message_destroy(msg);
--- a/libpurple/protocols/msnp9/notification.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/msnp9/notification.c Wed Jan 30 10:53:22 2008 +0000 @@ -1012,7 +1012,7 @@ { purple_debug_error("msn", "Error opening temp passport file: %s\n", - strerror(errno)); + g_strerror(errno)); } else { @@ -1061,7 +1061,7 @@ { purple_debug_error("msn", "Error closing temp passport file: %s\n", - strerror(errno)); + g_strerror(errno)); g_unlink(session->passport_info.file); g_free(session->passport_info.file);
--- a/libpurple/protocols/msnp9/servconn.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/msnp9/servconn.c Wed Jan 30 10:53:22 2008 +0000 @@ -387,12 +387,13 @@ session = servconn->session; len = read(servconn->fd, buf, sizeof(buf) - 1); + servconn->session->account->gc->last_received = time(NULL); if (len < 0 && errno == EAGAIN) return; else if (len <= 0) { - purple_debug_error("msn", "servconn read error, len: %d error: %s\n", len, strerror(errno)); + purple_debug_error("msn", "servconn read error, len: %d error: %s\n", len, g_strerror(errno)); msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ); return;
--- a/libpurple/protocols/myspace/myspace.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/myspace/myspace.c Wed Jan 30 10:53:22 2008 +0000 @@ -133,7 +133,7 @@ types = NULL; - /* Statuses are almost all the same. Define a macro to reduce code repetition. */ + /* Statuses are almost all the same. Define a macro to reduce code repetition. */ #define _MSIM_ADD_NEW_STATUS(prim) status = \ purple_status_type_new_with_attrs( \ prim, /* PurpleStatusPrimitive */ \ @@ -552,10 +552,8 @@ * return 1 even if the message could not be sent, since I don't know if * it has failed yet--because the IM is only sent after the userid is * retrieved from the server (which happens after this function returns). + * If an error does occur, it should be logged to the IM window. */ - /* TODO: maybe if message is delayed, don't echo to conv window, - * but do echo it to conv window manually once it is actually - * sent? Would be complicated. */ rc = 1; } else { rc = -1; @@ -563,19 +561,6 @@ g_free(message_msim); - /* - * In MySpace, you login with your email address, but don't talk to other - * users using their email address. So there is currently an asymmetry in the - * IM windows when using this plugin: - * - * you@example.com: hello - * some_other_user: what's going on? - * you@example.com: just coding a prpl - * - * TODO: Make the sent IM's appear as from the user's username, instead of - * their email address. Purple uses the login (in MSIM, the email)--change this. - */ - return rc; } @@ -602,7 +587,7 @@ g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); g_return_val_if_fail(who != NULL, FALSE); g_return_val_if_fail(text != NULL, FALSE); - + from_username = session->account->username; g_return_val_if_fail(from_username != NULL, FALSE); @@ -611,7 +596,7 @@ type, from_username, who, text); msg = msim_msg_new( - "bm", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(type), + "bm", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(type), "sesskey", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(session->sesskey), /* 't' will be inserted here */ "cv", MSIM_TYPE_INTEGER, GUINT_TO_POINTER(MSIM_CLIENT_VERSION), @@ -664,7 +649,7 @@ msim_incoming_bm(MsimSession *session, MsimMessage *msg) { guint bm; - + bm = msim_msg_get_integer(msg, "bm"); msim_incoming_bm_record_cv(session, msg); @@ -745,7 +730,10 @@ /* TODO: dump unknown msgs to file, so user can send them to me * if they wish, to help add support for new messages (inspired - * by Alexandr Shutko, who maintains OSCAR protocol documentation). */ + * by Alexandr Shutko, who maintains OSCAR protocol documentation). + * + * Filed enhancement ticket for libpurple as #4688. + */ purple_debug_info("msim", "Unrecognized data on account for %s\n", (session && session->account && session->account->username) ? @@ -787,9 +775,6 @@ msg_text, username); if (g_str_equal(msg_text, "%typing%")) { - /* TODO: find out if msim repeatedly sends typing messages, so we can - * give it a timeout. Right now, there does seem to be an inordinately - * amount of time between typing stopped-typing notifications. */ serv_got_typing(session->gc, username, 0, PURPLE_TYPING); rc = TRUE; } else if (g_str_equal(msg_text, "%stoptyping%")) { @@ -797,6 +782,25 @@ rc = TRUE; } else if (strstr(msg_text, "!!!ZAP_SEND!!!=RTE_BTN_ZAPS_")) { rc = msim_incoming_zap(session, msg); + } else if (strstr(msg_text, "!!!GroupCount=")) { + /* TODO: support group chats. I think the number in msg_text has + * something to do with the 'gid' field. */ + purple_debug_info("msim", "msim_incoming_action: TODO: implement #4691, group chats: %s\n", msg_text); + + rc = TRUE; + } else if (strstr(msg_text, "!!!Offline=")) { + /* TODO: support group chats. This one might mean a user + * went offline or exited the chat. */ + purple_debug_info("msim", "msim_incoming_action: TODO: implement #4691, group chats: %s\n", msg_text); + + rc = TRUE; + } else if (msim_msg_get_integer(msg, "aid") != 0) { + purple_debug_info("msim", "TODO: implement #4691, group chat from %d on %d: %s\n", + msim_msg_get_integer(msg, "aid"), + msim_msg_get_integer(msg, "f"), + msg_text); + + rc = TRUE; } else { msim_unrecognized(session, msg, "got to msim_incoming_action but unrecognized value for 'msg'"); @@ -959,6 +963,7 @@ if (!user) { /* User isn't on blist, create a temporary user to store info. */ + /* TODO: is this legit, or is it somehow responsible for #3444? */ PurpleBuddy *buddy; user = g_new0(MsimUser, 1); @@ -1052,6 +1057,7 @@ guint status_code; const gchar *message; gchar *stripped; + gchar *unrecognized_msg; session = (MsimSession *)account->gc->proto_data; @@ -1083,6 +1089,12 @@ purple_debug_info("msim", "msim_set_status: unknown " "status interpreting as online"); status_code = MSIM_STATUS_CODE_ONLINE; + + unrecognized_msg = g_strdup_printf("msim_set_status, unrecognized status type: %d\n", + purple_status_type_get_primitive(type)); + msim_unrecognized(session, NULL, unrecognized_msg); + g_free(unrecognized_msg); + break; } @@ -1197,13 +1209,13 @@ /* Special elements name beginning with '_', we'll use internally within the * program (did not come directly from the wire). */ msg = msim_msg_append(msg, "_username", MSIM_TYPE_STRING, username); /* This makes 'msg' the owner of 'username' */ - + /* TODO: attach more useful information, like ImageURL */ msim_process(session, msg); /* TODO: Free copy cloned from msim_preprocess_incoming(). */ - //XXX msim_msg_free(msg); + /* msim_msg_free(msg); */ msim_msg_free(body); } @@ -1211,16 +1223,17 @@ * * @param wanted_uid * - * @return Username of wanted_uid, if on blist, or NULL. Static string. + * @return Username of wanted_uid, if on blist, or NULL. + * This is a static string, so don't free it. Copy it if needed. * */ static const gchar * -msim_uid2username_from_blist(MsimSession *session, guint wanted_uid) +msim_uid2username_from_blist(PurpleAccount *account, guint wanted_uid) { GSList *buddies, *cur; - gchar *ret; - - buddies = purple_find_buddies(session->account, NULL); + const gchar *ret; + + buddies = purple_find_buddies(account, NULL); if (!buddies) { @@ -1244,7 +1257,7 @@ if (uid == wanted_uid) { - ret = g_strdup(name); + ret = name; break; } } @@ -1271,7 +1284,7 @@ /* 'f' = userid message is from, in buddy messages */ uid = msim_msg_get_integer(msg, "f"); - username = msim_uid2username_from_blist(session, uid); + username = msim_uid2username_from_blist(session->account, uid); if (username) { /* Know username already, use it. */ @@ -1313,13 +1326,13 @@ g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); delta = time(NULL) - session->last_comm; - //purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); + /* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */ if (delta >= MSIM_KEEPALIVE_INTERVAL) { errmsg = g_strdup_printf(_("Connection to server lost (no data received within %d seconds)"), (int)delta); purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n", errmsg, MSIM_KEEPALIVE_INTERVAL); - purple_connection_error_reason (session->gc, + purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg); purple_notify_error(session->gc, NULL, errmsg, NULL); @@ -1741,7 +1754,7 @@ static gboolean msim_web_challenge(MsimSession *session, MsimMessage *msg) { - /* TODO: web challenge, store token */ + /* TODO: web challenge, store token. #2659. */ return FALSE; } @@ -1832,13 +1845,14 @@ if (msim_msg_get(msg, "fatal")) { PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; purple_debug_info("msim", "fatal error, closing\n"); + switch (err) { - case 260: /* Incorrect password */ + case MSIM_ERROR_INCORRECT_PASSWORD: /* Incorrect password */ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; if (!purple_account_get_remember_password(session->account)) purple_account_set_password(session->account, NULL); break; - case 6: /* Logged in elsewhere */ + case MSIM_ERROR_LOGGED_IN_ELSEWHERE: /* Logged in elsewhere */ reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; if (!purple_account_get_remember_password(session->account)) purple_account_set_password(session->account, NULL); @@ -1871,6 +1885,7 @@ gchar *status_headline, *status_headline_escaped; gint status_code, purple_status_code; gchar *username; + gchar *unrecognized_msg; g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); g_return_val_if_fail(msg != NULL, FALSE); @@ -1903,7 +1918,8 @@ blist = purple_get_blist(); - /* Add buddy if not found */ + /* Add buddy if not found. + * TODO: Could this be responsible for #3444? */ user = msim_find_user(session, username); if (!user) { PurpleBuddy *buddy; @@ -1915,7 +1931,7 @@ user = msim_get_user_from_buddy(buddy); - /* All buddies on list should have 'uid' integer associated with them. */ + /* All buddies on list should have a UserID integer associated with them. */ purple_blist_node_set_int(&buddy->node, "UserID", msim_msg_get_integer(msg, "f")); msim_store_user_info(session, msg, NULL); @@ -1938,7 +1954,7 @@ /* don't copy; let the MsimUser own the headline, memory-wise */ user->headline = status_headline_escaped; - + /* Set user status */ switch (status_code) { case MSIM_STATUS_CODE_OFFLINE_OR_HIDDEN: @@ -1959,9 +1975,15 @@ break; default: - purple_debug_info("msim", "msim_status for %s, unknown status code %d, treating as available\n", + purple_debug_info("msim", "msim_incoming_status for %s, unknown status code %d, treating as available\n", username, status_code); purple_status_code = PURPLE_STATUS_AVAILABLE; + + unrecognized_msg = g_strdup_printf("msim_incoming_status, unrecognized status code: %d\n", + status_code); + msim_unrecognized(session, NULL, unrecognized_msg); + g_free(unrecognized_msg); + } purple_prpl_got_user_status(session->account, username, purple_primitive_get_id_from_type(purple_status_code), NULL); @@ -2156,10 +2178,15 @@ gchar *msg; msg = g_strdup_printf(_("No such user: %s"), username); - purple_notify_error(NULL, NULL, _("User lookup"), msg); + if (!purple_conv_present_error(username, session->account, msg)) { + purple_notify_error(NULL, NULL, _("User lookup"), msg); + } + g_free(msg); g_free(username); - //msim_msg_free(msg); + /* TODO: free + * msim_msg_free(msg); + */ return; } @@ -2180,7 +2207,9 @@ g_free(uid_field_name); g_free(uid_before); g_free(username); - //msim_msg_free(msg); + /* TODO: free + * msim_msg_free(msg); + */ } /** Postprocess and send a message. @@ -2222,8 +2251,7 @@ uid = 0; } - if (!buddy || !uid) - { + if (!buddy || !uid) { /* Don't have uid offhand - need to ask for it, and wait until hear back before sending. */ purple_debug_info("msim", ">>> msim_postprocess_outgoing: couldn't find username %s in blist\n", username ? username : "(NULL)"); @@ -2244,7 +2272,9 @@ rc = msim_msg_send(session, msg); - //msim_msg_free(msg); + /* TODO: free + * msim_msg_free(msg); + */ return rc; } @@ -2304,7 +2334,7 @@ "blocklist", MSIM_TYPE_BOOLEAN, TRUE, "sesskey", MSIM_TYPE_INTEGER, session->sesskey, /* TODO: MsimMessage lists. Currently <uid> isn't replaced in lists. */ - //"idlist", MSIM_TYPE_STRING, g_strdup("a-|<uid>|b-|<uid>"), + /* "idlist", MSIM_TYPE_STRING, g_strdup("a-|<uid>|b-|<uid>"), */ "idlist", MSIM_TYPE_LIST, blocklist_updates, NULL); @@ -2316,6 +2346,72 @@ msim_msg_free(blocklist_msg); } +/** + * Returns a string of a username in canonical form. Basically removes all the + * spaces, lowercases the string, and looks up user IDs to usernames. + * Normalizing tom, TOM, Tom, and 6221 wil all return 'tom'. + * + * Borrowed this code from oscar_normalize. Added checking for + * "if userid, get name before normalizing" + */ +const char *msim_normalize(const PurpleAccount *account, const char *str) { + static char normalized[BUF_LEN]; + char *tmp1, *tmp2; + int i, j; + guint id; + + g_return_val_if_fail(str != NULL, NULL); + + if (msim_is_userid(str)) { + /* Have user ID, we need to get their username first :) */ + const char *username; + + /* If the account does not exist, we can't look up the user. */ + if (!account) + return str; + + id = atol(str); + username = msim_uid2username_from_blist((PurpleAccount *)account, id); + if (!username) { + /* Not in buddy list... scheisse... TODO: Manual Lookup! Bug #4631 */ + /* Note: manual lookup using msim_lookup_user() is a problem inside + * msim_normalize(), because msim_lookup_user() calls a callback function + * when the user information has been looked up, but msim_normalize() expects + * the result immediately. */ + strncpy(normalized, str, BUF_LEN); + } else { + strncpy(normalized, username, BUF_LEN); + } + } else { + /* Have username. */ + strncpy(normalized, str, BUF_LEN); + } + + /* Strip spaces. */ + for (i=0, j=0; normalized[j]; i++, j++) { + while (normalized[j] == ' ') + j++; + normalized[i] = normalized[j]; + } + normalized[i] = '\0'; + + /* Lowercase and perform UTF-8 normalization. */ + tmp1 = g_utf8_strdown(normalized, -1); + tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT); + g_snprintf(normalized, sizeof(normalized), "%s", tmp2); + g_free(tmp2); + g_free(tmp1); + + /* TODO: re-add caps and spacing back to what the user wanted. + * User can format their own names, for example 'msimprpl' is shown + * as 'MsIm PrPl' in the official client. + * + * TODO: file a ticket to add this enhancement. + */ + + return normalized; +} + /** Return whether the buddy can be messaged while offline. * * The protocol supports offline messages in just the same way as online @@ -2364,31 +2460,30 @@ g_return_if_fail(cond == PURPLE_INPUT_READ); g_return_if_fail(MSIM_SESSION_VALID(session)); - /* Mark down that we got data, so don't timeout. */ + /* Mark down that we got data, so we don't timeout. */ session->last_comm = time(NULL); - /* Only can handle so much data at once... - * If this happens, try recompiling with a higher MSIM_READ_BUF_SIZE. - * Should be large enough to hold the largest protocol message. - */ - if (session->rxoff >= MSIM_READ_BUF_SIZE) { - purple_debug_error("msim", - "msim_input_cb: %d-byte read buffer full! rxoff=%d\n", - MSIM_READ_BUF_SIZE, session->rxoff); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Read buffer full")); + /* If approaching end of buffer, reallocate some more memory. */ + if (session->rxsize < session->rxoff + MSIM_READ_BUF_SIZE) { + purple_debug_info("msim", + "msim_input_cb: %d-byte read buffer full, rxoff=%d, " "growing by %d bytes\n", + session->rxsize, session->rxoff, MSIM_READ_BUF_SIZE); + session->rxsize += MSIM_READ_BUF_SIZE; + session->rxbuf = g_realloc(session->rxbuf, session->rxsize); + return; } - purple_debug_info("msim", "buffer at %d (max %d), reading up to %d\n", - session->rxoff, MSIM_READ_BUF_SIZE, - MSIM_READ_BUF_SIZE - session->rxoff); + purple_debug_info("msim", "dynamic buffer at %d (max %d), reading up to %d\n", + session->rxoff, session->rxsize, + MSIM_READ_BUF_SIZE - session->rxoff - 1); /* Read into buffer. On Win32, need recv() not read(). session->fd also holds * the file descriptor, but it sometimes differs from the 'source' parameter. */ - n = recv(session->fd, session->rxbuf + session->rxoff, MSIM_READ_BUF_SIZE - session->rxoff, 0); + n = recv(session->fd, + session->rxbuf + session->rxoff, + session->rxsize - session->rxoff - 1, 0); if (n < 0 && errno == EAGAIN) { return; @@ -2408,13 +2503,13 @@ return; } - if (n + session->rxoff >= MSIM_READ_BUF_SIZE) { + if (n + session->rxoff > session->rxsize) { purple_debug_info("msim_input_cb", "received %d bytes, pushing rxoff to %d, over buffer size of %d\n", - n, n + session->rxoff, MSIM_READ_BUF_SIZE); - /* TODO: g_realloc like msn, yahoo, irc, jabber? */ + n, n + session->rxoff, session->rxsize); purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Read buffer full")); + _("Read buffer full (2)")); + return; } /* Null terminate */ @@ -2457,6 +2552,7 @@ purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unparseable message")); + break; } else { /* Process message and then free it (processing function should * clone message if it wants to keep it afterwards.) */ @@ -2469,10 +2565,11 @@ /* Move remaining part of buffer to beginning. */ session->rxoff -= strlen(session->rxbuf) + strlen(MSIM_FINAL_STRING); memmove(session->rxbuf, end + strlen(MSIM_FINAL_STRING), - MSIM_READ_BUF_SIZE - (end + strlen(MSIM_FINAL_STRING) - session->rxbuf)); - - /* Clear end of buffer */ - //memset(end, 0, MSIM_READ_BUF_SIZE - (end - session->rxbuf)); + session->rxsize - (end + strlen(MSIM_FINAL_STRING) - session->rxbuf)); + + /* Clear end of buffer + * memset(end, 0, MSIM_READ_BUF_SIZE - (end - session->rxbuf)); + */ } } @@ -2675,7 +2772,7 @@ uid = msim_msg_get_integer(contact_info, "ContactID"); if (!user_lookup_info) { - username = g_strdup(msim_uid2username_from_blist(session, uid)); + username = g_strdup(msim_uid2username_from_blist(session->account, uid)); g_return_if_fail(username != NULL); } else { user_lookup_info_body = msim_msg_get_dictionary(user_lookup_info, "body"); @@ -2724,8 +2821,9 @@ msim_store_user_info(session, contact_info, NULL); /* TODO: other fields, store in 'user' */ - msim_msg_free(contact_info); + + g_free(username); } /** Add first ContactID in contact_info to buddy's list. Used to add @@ -2743,7 +2841,7 @@ g_return_val_if_fail(uid != 0, FALSE); /* Lookup the username, since NickName and IMName is unreliable */ - username = msim_uid2username_from_blist(session, uid); + username = msim_uid2username_from_blist(session->account, uid); if (!username) { gchar *uid_str; @@ -2772,7 +2870,10 @@ msim_msg_dump("msim_got_contact_list: reply=%s", reply); body = msim_msg_get_dictionary(reply, "body"); - g_return_if_fail(body != NULL); + if (!body) { + /* No friends. Not an error. */ + return; + } buddy_count = 0; @@ -2970,7 +3071,7 @@ NULL, /* rename_group */ NULL, /* buddy_free */ NULL, /* convo_closed */ - NULL, /* normalize */ + msim_normalize, /* normalize */ NULL, /* set_buddy_icon */ NULL, /* remove_group */ NULL, /* get_cb_real_name */
--- a/libpurple/protocols/myspace/myspace.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/myspace/myspace.h Wed Jan 30 10:53:22 2008 +0000 @@ -180,7 +180,9 @@ #define MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS 1 #define MSIM_CONTACT_LIST_IMPORT_TOP_FRIENDS 2 -#define MsimAttentionType PurpleAttentionType +/* Error codes */ +#define MSIM_ERROR_INCORRECT_PASSWORD 260 +#define MSIM_ERROR_LOGGED_IN_ELSEWHERE 6 /* Functions */ gboolean msim_load(PurplePlugin *plugin); @@ -201,6 +203,8 @@ void msim_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); void msim_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); +const char *msim_normalize(const PurpleAccount *account, const char *str); + gboolean msim_offline_message(const PurpleBuddy *buddy); void msim_close(PurpleConnection *gc);
--- a/libpurple/protocols/myspace/session.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/myspace/session.c Wed Jan 30 10:53:22 2008 +0000 @@ -59,7 +59,8 @@ session->server_info = NULL; session->rxoff = 0; - session->rxbuf = g_new0(gchar, MSIM_READ_BUF_SIZE); + session->rxsize = MSIM_READ_BUF_SIZE; + session->rxbuf = g_new0(gchar, session->rxsize); session->next_rid = 1; session->last_comm = time(NULL); session->inbox_status = 0;
--- a/libpurple/protocols/myspace/session.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/myspace/session.h Wed Jan 30 10:53:22 2008 +0000 @@ -42,6 +42,7 @@ gchar *rxbuf; /**< Receive buffer */ guint rxoff; /**< Receive buffer offset */ + guint rxsize; /**< Receive buffer size */ guint next_rid; /**< Next request/response ID */ time_t last_comm; /**< Time received last communication */ guint inbox_status; /**< Bit field of inbox notifications */
--- a/libpurple/protocols/myspace/zap.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/myspace/zap.c Wed Jan 30 10:53:22 2008 +0000 @@ -29,15 +29,12 @@ msim_attention_types(PurpleAccount *acct) { static GList *types = NULL; - MsimAttentionType* attn; + PurpleAttentionType* attn; if (!types) { -#define _MSIM_ADD_NEW_ATTENTION(icn, nme, incoming, outgoing) \ - attn = g_new0(MsimAttentionType, 1); \ - attn->icon_name = icn; \ - attn->name = nme; \ - attn->incoming_description = incoming; \ - attn->outgoing_description = outgoing; \ +#define _MSIM_ADD_NEW_ATTENTION(icn, ulname, nme, incoming, outgoing) \ + attn = purple_attention_type_new(ulname, nme, incoming, outgoing); \ + purple_attention_type_set_icon_name(attn, icn); \ types = g_list_append(types, attn); /* TODO: icons for each zap */ @@ -48,37 +45,46 @@ * projectile or weapon." This term often has an electrical * connotation, for example, "he was zapped by electricity when * he put a fork in the toaster." */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Zap"), _("%s has zapped you!"), _("Zapping %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Zap", _("Zap"), _("%s has zapped you!"), + _("Zapping %s...")); /* Whack means "to hit or strike someone with a sharp blow" */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Whack"), _("%s has whacked you!"), _("Whacking %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Whack", _("Whack"), + _("%s has whacked you!"), _("Whacking %s...")); /* Torch means "to set on fire." Don't worry, this doesn't * make a whole lot of sense in English, either. Feel free * to translate it literally. */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Torch"), _("%s has torched you!"), _("Torching %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Torch", _("Torch"), + _("%s has torched you!"), _("Torching %s...")); /* Smooch means "to kiss someone, often enthusiastically" */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Smooch"), _("%s has smooched you!"), _("Smooching %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Smooch", _("Smooch"), + _("%s has smooched you!"), _("Smooching %s...")); /* A hug is a display of affection; wrapping your arms around someone */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Hug"), _("%s has hugged you!"), _("Hugging %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Hug", _("Hug"), _("%s has hugged you!"), + _("Hugging %s...")); /* Slap means "to hit someone with an open/flat hand" */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Slap"), _("%s has slapped you!"), _("Slapping %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Slap", _("Slap"), + _("%s has slapped you!"), _("Slapping %s...")); /* Goose means "to pinch someone on their butt" */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Goose"), _("%s has goosed you!"), _("Goosing %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Goose", _("Goose"), + _("%s has goosed you!"), _("Goosing %s...")); /* A high-five is when two people's hands slap each other * in the air above their heads. It is done to celebrate * something, often a victory, or to congratulate someone. */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("High-five"), _("%s has high-fived you!"), _("High-fiving %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "High-five", _("High-five"), + _("%s has high-fived you!"), _("High-fiving %s...")); /* We're not entirely sure what the MySpace people mean by * this... but we think it's the equivalent of "prank." Or, for * someone to perform a mischievous trick or practical joke. */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Punk"), _("%s has punk'd you!"), _("Punking %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Punk", _("Punk"), + _("%s has punk'd you!"), _("Punking %s...")); /* Raspberry is a slang term for the vibrating sound made * when you stick your tongue out of your mouth with your @@ -87,7 +93,8 @@ * gesture, so it does not carry a harsh negative * connotation. It is generally used in a playful tone * with friends. */ - _MSIM_ADD_NEW_ATTENTION(NULL, _("Raspberry"), _("%s has raspberried you!"), _("Raspberrying %s...")); + _MSIM_ADD_NEW_ATTENTION(NULL, "Raspberry", _("Raspberry"), + _("%s has raspberried you!"), _("Raspberrying %s...")); } return types; @@ -99,14 +106,14 @@ { GList *types; MsimSession *session; - MsimAttentionType *attn; + PurpleAttentionType *attn; PurpleBuddy *buddy; session = (MsimSession *)gc->proto_data; /* Look for this attention type, by the code index given. */ types = msim_attention_types(gc->account); - attn = (MsimAttentionType *)g_list_nth_data(types, code); + attn = (PurpleAttentionType *)g_list_nth_data(types, code); if (!attn) { purple_debug_info("msim_send_attention", "got invalid zap code %d\n", code); @@ -200,12 +207,12 @@ i = 0; do { - MsimAttentionType *attn; + PurpleAttentionType *attn; - attn = (MsimAttentionType *)types->data; + attn = (PurpleAttentionType *)types->data; - act = purple_menu_action_new(attn->name, PURPLE_CALLBACK(msim_send_zap_from_menu), - GUINT_TO_POINTER(i), NULL); + act = purple_menu_action_new(purple_attention_type_get_name(attn), + PURPLE_CALLBACK(msim_send_zap_from_menu), GUINT_TO_POINTER(i), NULL); zap_menu = g_list_append(zap_menu, act); ++i;
--- a/libpurple/protocols/novell/nmfield.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/novell/nmfield.c Wed Jan 30 10:53:22 2008 +0000 @@ -164,9 +164,7 @@ case NMFIELD_TYPE_BINARY: case NMFIELD_TYPE_UTF8: case NMFIELD_TYPE_DN: - if (field->ptr_value != NULL) { - g_free(field->ptr_value); - } + g_free(field->ptr_value); break; case NMFIELD_TYPE_ARRAY:
--- a/libpurple/protocols/novell/novell.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/novell/novell.c Wed Jan 30 10:53:22 2008 +0000 @@ -1920,6 +1920,7 @@ parms = g_slist_append(parms, nm_event_get_conference(event)); /* Prompt the user */ + /* TODO: Would it be better to use serv_got_chat_invite() here? */ gc = purple_account_get_connection(user->client_data); purple_request_action(gc, title, primary, secondary, PURPLE_DEFAULT_ACTION_NONE,
--- a/libpurple/protocols/oscar/family_buddy.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/family_buddy.c Wed Jan 30 10:53:22 2008 +0000 @@ -219,8 +219,9 @@ if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, &userinfo); - if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING) - aim_locate_requestuserinfo(od, userinfo.sn); + if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && userinfo.flags & AIM_FLAG_AWAY) + aim_locate_autofetch_away_message(od, userinfo.sn); + aim_info_free(&userinfo); return ret;
--- a/libpurple/protocols/oscar/family_locate.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/family_locate.c Wed Jan 30 10:53:22 2008 +0000 @@ -386,11 +386,11 @@ } void -aim_locate_requestuserinfo(OscarData *od, const char *sn) +aim_locate_autofetch_away_message(OscarData *od, const char *sn) { struct userinfo_node *cur; - /* Make sure we haven't already requested info for this buddy */ + /* Make sure we haven't already made an info request for this buddy */ for (cur = od->locate.requested; cur != NULL; cur = cur->next) if (aim_sncmp(sn, cur->sn) == 0) return; @@ -401,7 +401,7 @@ cur->next = od->locate.requested; od->locate.requested = cur; - aim_locate_getinfoshort(od, cur->sn, 0x00000003); + aim_locate_getinfoshort(od, cur->sn, 0x00000002); } aim_userinfo_t *aim_locate_finduserinfo(OscarData *od, const char *sn) {
--- a/libpurple/protocols/oscar/flap_connection.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Wed Jan 30 10:53:22 2008 +0000 @@ -360,7 +360,7 @@ conn = data; od = conn->od; - account = (PURPLE_CONNECTION_IS_VALID(od->gc) ? purple_connection_get_account(od->gc) : NULL); + account = purple_connection_get_account(od->gc); purple_debug_info("oscar", "Destroying oscar connection of " "type 0x%04hx. Disconnect reason is %d\n", @@ -375,8 +375,8 @@ * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then * we should try to request one instead of disconnecting. */ - if (account && !account->disconnecting && - ((od->oscar_connections == NULL) || (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE)))) + if (!account->disconnecting && ((od->oscar_connections == NULL) + || (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE)))) { /* No more FLAP connections! Sign off this PurpleConnection! */ gchar *tmp; @@ -817,6 +817,7 @@ OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno)); break; } + conn->od->gc->last_received = time(NULL); /* If we don't even have a complete FLAP header then do nothing */ conn->header_received += read;
--- a/libpurple/protocols/oscar/libaim.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/libaim.c Wed Jan 30 10:53:22 2008 +0000 @@ -92,11 +92,11 @@ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ /* padding */ - NULL, - NULL, - NULL, NULL };
--- a/libpurple/protocols/oscar/libicq.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/libicq.c Wed Jan 30 10:53:22 2008 +0000 @@ -92,11 +92,11 @@ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* get_attention_types */ /* padding */ - NULL, - NULL, - NULL, NULL };
--- a/libpurple/protocols/oscar/oscar.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.c Wed Jan 30 10:53:22 2008 +0000 @@ -197,7 +197,6 @@ static void purple_icons_fetch(PurpleConnection *gc); -static void recent_buddies_cb(const char *name, PurplePrefType type, gconstpointer value, gpointer data); void oscar_set_info(PurpleConnection *gc, const char *info); static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status); static void oscar_set_extendedstatus(PurpleConnection *gc); @@ -1203,6 +1202,48 @@ return 1; } +static void +idle_reporting_pref_cb(const char *name, PurplePrefType type, + gconstpointer value, gpointer data) +{ + PurpleConnection *gc; + OscarData *od; + gboolean report_idle; + guint32 presence; + + gc = data; + od = gc->proto_data; + report_idle = strcmp((const char *)value, "none") != 0; + presence = aim_ssi_getpresence(od->ssi.local); + + if (report_idle) + aim_ssi_setpresence(od, presence | 0x400); + else + aim_ssi_setpresence(od, presence & ~0x400); +} + +/** + * Should probably make a "Use recent buddies group" account preference + * so that this option is surfaced to the user. + */ +static void +recent_buddies_pref_cb(const char *name, PurplePrefType type, + gconstpointer value, gpointer data) +{ + PurpleConnection *gc; + OscarData *od; + guint32 presence; + + gc = data; + od = gc->proto_data; + presence = aim_ssi_getpresence(od->ssi.local); + + if (value) + aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES); + else + aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES); +} + void oscar_login(PurpleAccount *account) { @@ -1293,7 +1334,8 @@ } /* Connect to core Purple signals */ - purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_cb, gc); + purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc); + purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc); newconn = flap_connection_new(od, SNAC_FAMILY_AUTH); newconn->connect_data = purple_proxy_connect(NULL, account, @@ -1538,6 +1580,7 @@ { struct pieceofcrap *pos = data; gchar *buf; + ssize_t result; if (!PURPLE_CONNECTION_IS_VALID(pos->gc)) { @@ -1549,8 +1592,8 @@ pos->fd = source; if (source < 0) { - buf = g_strdup_printf(_("You may be disconnected shortly. You may want to use TOC until " - "this is fixed. Check %s for updates."), PURPLE_WEBSITE); + buf = g_strdup_printf(_("You may be disconnected shortly. " + "Check %s for updates."), PURPLE_WEBSITE); purple_notify_warning(pos->gc, NULL, _("Unable to get a valid AIM login hash."), buf); @@ -1562,7 +1605,18 @@ buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n", pos->offset, pos->len, pos->modname ? pos->modname : ""); - write(pos->fd, buf, strlen(buf)); + result = send(pos->fd, buf, strlen(buf), 0); + if (result != strlen(buf)) { + if (result < 0) + purple_debug_error("oscar", "Error writing %" G_GSIZE_FORMAT + " bytes to fetch AIM hash data: %s\n", + strlen(buf), g_strerror(errno)); + else + purple_debug_error("oscar", "Tried to write %" + G_GSIZE_FORMAT " bytes to fetch AIM hash data but " + "instead wrote %" G_GSIZE_FORMAT " bytes\n", + strlen(buf), result); + } g_free(buf); g_free(pos->modname); pos->inpa = purple_input_add(pos->fd, PURPLE_INPUT_READ, damn_you, pos); @@ -1744,7 +1798,6 @@ int type = 0; gboolean buddy_is_away = FALSE; const char *status_id; - char *itmsurl = NULL; va_list ap; aim_userinfo_t *info; @@ -1792,11 +1845,6 @@ status_id = OSCAR_STATUS_ID_AVAILABLE; } - if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len) - /* Grab the iTunes Music Store URL */ - itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding, - info->itmsurl, info->itmsurl_len); - if (info->flags & AIM_FLAG_WIRELESS) { purple_prpl_got_user_status(account, info->sn, OSCAR_STATUS_ID_MOBILE, NULL); @@ -1804,27 +1852,31 @@ purple_prpl_got_user_status_deactive(account, info->sn, OSCAR_STATUS_ID_MOBILE); } - if (status_id == OSCAR_STATUS_ID_AVAILABLE) + if (strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) == 0) { char *message = NULL; + char *itmsurl = NULL; if (info->status != NULL && info->status[0] != '\0') /* Grab the available message */ message = oscar_encoding_to_utf8(account, info->status_encoding, info->status, info->status_len); + if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len) + /* Grab the iTunes Music Store URL */ + itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding, + info->itmsurl, info->itmsurl_len); + purple_prpl_got_user_status(account, info->sn, status_id, "message", message, "itmsurl", itmsurl, NULL); g_free(message); + g_free(itmsurl); } else { - purple_prpl_got_user_status(account, info->sn, status_id, - "itmsurl", itmsurl, NULL); - } - - g_free(itmsurl); + purple_prpl_got_user_status(account, info->sn, status_id, NULL); + } /* Login time stuff */ if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE) @@ -4471,7 +4523,6 @@ PurplePresence *presence; PurpleStatusType *status_type; PurpleStatusPrimitive primitive; - gboolean invisible; char *htmlinfo; char *info_encoding = NULL; @@ -4486,7 +4537,6 @@ status_type = purple_status_get_type(status); primitive = purple_status_type_get_primitive(status_type); presence = purple_account_get_presence(account); - invisible = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE); if (!setinfo) { @@ -4924,9 +4974,21 @@ } } /* Presence settings (idle time visibility) */ - if ((tmp = aim_ssi_getpresence(od->ssi.local)) != 0xFFFFFFFF) - if (!(tmp & 0x400)) + tmp = aim_ssi_getpresence(od->ssi.local); + if (tmp != 0xFFFFFFFF) { + const char *idle_reporting_pref; + gboolean report_idle; + + idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting"); + report_idle = strcmp(idle_reporting_pref, "none") != 0; + + if (report_idle) aim_ssi_setpresence(od, tmp | 0x400); + else + aim_ssi_setpresence(od, tmp & ~0x400); + } + + } /* end pruning buddies from local list */ /* Add from server list to local list */ @@ -6002,7 +6064,7 @@ _("Because this reveals your IP address, it " "may be considered a security risk. Do you " "wish to continue?"), - 0, + 0, /* Default action is "connect" */ purple_connection_get_account(gc), data->who, NULL, data, 2, _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb), @@ -6565,33 +6627,6 @@ } } -static void -recent_buddies_cb(const char *name, PurplePrefType type, - gconstpointer value, gpointer data) -{ - PurpleConnection *gc = data; - OscarData *od = gc->proto_data; - guint32 presence; - - presence = aim_ssi_getpresence(od->ssi.local); - - if (value) { - presence &= ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES; - aim_ssi_setpresence(od, presence); - } else { - presence |= AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES; - aim_ssi_setpresence(od, presence); - } -} - -#ifdef USE_PRPL_PREFERENCES - ppref = purple_plugin_pref_new_with_name_and_label("/plugins/prpl/oscar/recent_buddies", _("Use recent buddies group")); - purple_plugin_pref_frame_add(frame, ppref); - - ppref = purple_plugin_pref_new_with_name_and_label("/plugins/prpl/oscar/show_idle", _("Show how long you have been idle")); - purple_plugin_pref_frame_add(frame, ppref); -#endif - const char * oscar_normalize(const PurpleAccount *account, const char *str) { @@ -6754,7 +6789,7 @@ /* Preferences */ purple_prefs_add_none("/plugins/prpl/oscar"); purple_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE); - purple_prefs_add_bool("/plugins/prpl/oscar/show_idle", FALSE); + purple_prefs_remove("/plugins/prpl/oscar/show_idle"); purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy"); /* protocol handler */
--- a/libpurple/protocols/oscar/oscar.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.h Wed Jan 30 10:53:22 2008 +0000 @@ -1074,7 +1074,7 @@ /* 0x000f */ int aim_locate_setinterests(OscarData *od, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy); /* 0x0015 */ int aim_locate_getinfoshort(OscarData *od, const char *sn, guint32 flags); -void aim_locate_requestuserinfo(OscarData *od, const char *sn); +void aim_locate_autofetch_away_message(OscarData *od, const char *sn); guint32 aim_locate_getcaps(OscarData *od, ByteStream *bs, int len); guint32 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len); void aim_info_free(aim_userinfo_t *);
--- a/libpurple/protocols/oscar/peer.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/oscar/peer.c Wed Jan 30 10:53:22 2008 +0000 @@ -1041,7 +1041,7 @@ "Images. Because your IP address will be " "revealed, this may be considered a privacy " "risk."), - PURPLE_DEFAULT_ACTION_NONE, + 0, /* Default action is "connect" */ account, sn, NULL, conn, 2, _("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
--- a/libpurple/protocols/qq/file_trans.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/qq/file_trans.c Wed Jan 30 10:53:22 2008 +0000 @@ -97,6 +97,7 @@ guint8 *buffer; PurpleCipher *cipher; PurpleCipherContext *context; + size_t wc; const gint QQ_MAX_FILE_MD5_LENGTH = 10002432; @@ -109,15 +110,20 @@ buffer = g_newa(guint8, filelen); g_return_if_fail(buffer != NULL); - fread(buffer, filelen, 1, fp); + wc = fread(buffer, filelen, 1, fp); + fclose(fp); + if (wc != 1) { + purple_debug_error("qq", "Unable to read file: %s\n", filename); + + /* TODO: XXX: Really, the caller should be modified to deal with this properly. */ + return; + } cipher = purple_ciphers_find_cipher("md5"); context = purple_cipher_context_new(cipher, NULL); purple_cipher_context_append(context, buffer, filelen); purple_cipher_context_digest(context, 16, md5, NULL); purple_cipher_context_destroy(context); - - fclose(fp); } static void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh)
--- a/libpurple/protocols/qq/group_im.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/qq/group_im.c Wed Jan 30 10:53:22 2008 +0000 @@ -135,7 +135,7 @@ purple_request_action(gc, _("QQ Qun Operation"), msg, reason, - 2, + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), nombre, NULL, g, 3, _("Approve"),
--- a/libpurple/protocols/qq/group_opt.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.c Wed Jan 30 10:53:22 2008 +0000 @@ -96,7 +96,8 @@ g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0); qq_send_packet_get_info(g->gc, g->member, TRUE); /* we want to see window */ - purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "", 2, + purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "", + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(g->gc), NULL, NULL, g, 2, _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
--- a/libpurple/protocols/qq/sys_msg.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/qq/sys_msg.c Wed Jan 30 10:53:22 2008 +0000 @@ -84,7 +84,8 @@ nombre = uid_to_purple_name(uid); purple_request_action - (gc, NULL, _("Do you want to approve the request?"), "", 2, + (gc, NULL, _("Do you want to approve the request?"), "", + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), nombre, NULL, g, 2, _("Reject"), G_CALLBACK(qq_reject_add_request_with_gc_and_uid), @@ -107,7 +108,8 @@ qq_send_packet_get_info(gc, uid, TRUE); /* we want to see window */ nombre = uid_to_purple_name(uid); purple_request_action - (gc, NULL, _("Do you want to add this buddy?"), "", 2, + (gc, NULL, _("Do you want to add this buddy?"), "", + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), nombre, NULL, g, 2, _("Cancel"), NULL, @@ -166,7 +168,8 @@ message = g_strdup_printf(_("You have been added by %s"), from); _qq_sys_msg_log_write(gc, message, from); purple_request_action(gc, NULL, message, - _("Would you like to add him?"), 2, + _("Would you like to add him?"), + PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), name, NULL, g, 3, _("Cancel"), NULL, @@ -240,7 +243,7 @@ _qq_sys_msg_log_write(gc, message, from); purple_request_action - (gc, NULL, message, reason, 2, + (gc, NULL, message, reason, PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), name, NULL, g, 3, _("Reject"), @@ -260,7 +263,7 @@ g2->uid = strtol(from, NULL, 10); message = g_strdup_printf(_("%s is not in your buddy list"), from); purple_request_action(gc, NULL, message, - _("Would you like to add him?"), 2, + _("Would you like to add him?"), PURPLE_DEFAULT_ACTION_NONE, purple_connection_get_account(gc), name, NULL, g2, 3, _("Cancel"), NULL,
--- a/libpurple/protocols/sametime/sametime.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/sametime/sametime.c Wed Jan 30 10:53:22 2008 +0000 @@ -1695,7 +1695,9 @@ int len; len = read(sock, buf, BUF_LEN); - if(len > 0) mwSession_recv(session, buf, len); + if(len > 0) { + mwSession_recv(session, buf, len); + } return len; } @@ -2286,6 +2288,7 @@ PurpleXfer *xfer; FILE *fp; + size_t wc; xfer = mwFileTransfer_getClientData(ft); g_return_if_fail(xfer != NULL); @@ -2294,7 +2297,12 @@ g_return_if_fail(fp != NULL); /* we must collect and save our precious data */ - fwrite(data->data, 1, data->len, fp); + wc = fwrite(data->data, 1, data->len, fp); + if (wc != data->len) { + DEBUG_ERROR("failed to write data\n"); + purple_xfer_cancel_local(xfer); + return; + } /* update the progress */ xfer->bytes_sent += data->len;
--- a/libpurple/protocols/simple/simple.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/simple/simple.c Wed Jan 30 10:53:22 2008 +0000 @@ -1356,10 +1356,21 @@ } static gboolean process_publish_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { + + const gchar *etag = NULL; + if(msg->response != 200 && msg->response != 408) { /* never send again */ sip->republish = -1; } + + etag = sipmsg_find_header(msg, "SIP-Etag"); + if (etag) { + /* we must store the etag somewhere. */ + g_free(sip->publish_etag); + sip->publish_etag = g_strdup(etag); + } + return TRUE; } @@ -1368,10 +1379,12 @@ gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); gchar *doc = gen_pidf(sip, TRUE); - add_headers = g_strdup_printf("%s%d%s", - "Expires: ", - PUBLISH_EXPIRATION, - "\r\nEvent: presence\r\n" + add_headers = g_strdup_printf("%s%s%s%s%d\r\n%s", + sip->publish_etag ? "SIP-If-Match: " : "", + sip->publish_etag ? sip->publish_etag : "", + sip->publish_etag ? "\r\n" : "", + "Expires: ", PUBLISH_EXPIRATION, + "Event: presence\r\n" "Content-Type: application/pidf+xml\r\n"); send_sip_request(sip->gc, "PUBLISH", uri, uri, @@ -1384,14 +1397,23 @@ static void send_closed_publish(struct simple_account_data *sip) { gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); - gchar *doc = gen_pidf(sip, FALSE); - send_sip_request(sip->gc, "PUBLISH", uri, uri, - "Expires: 600\r\nEvent: presence\r\n" - "Content-Type: application/pidf+xml\r\n", + gchar *add_headers, *doc; + + add_headers = g_strdup_printf("%s%s%s%s", + sip->publish_etag ? "SIP-If-Match: " : "", + sip->publish_etag ? sip->publish_etag : "", + sip->publish_etag ? "\r\n" : "", + "Expires: 600\r\n" + "Event: presence\r\n" + "Content-Type: application/pidf+xml\r\n"); + + doc = gen_pidf(sip, FALSE); + send_sip_request(sip->gc, "PUBLISH", uri, uri, add_headers, doc, NULL, process_publish_response); /*sip->republish = time(NULL) + 500;*/ g_free(uri); g_free(doc); + g_free(add_headers); } static void process_incoming_subscribe(struct simple_account_data *sip, struct sipmsg *msg) { @@ -1653,7 +1675,7 @@ if(sip->fd == source) sip->fd = -1; return; } - + gc->last_received = time(NULL); conn->inbufused += len; conn->inbuf[conn->inbufused] = '\0'; @@ -1948,6 +1970,7 @@ g_free(sip->proxy.target); g_free(sip->proxy.realm); g_free(sip->proxy.digest_session_key); + g_free(sip->publish_etag); if(sip->txbuf) purple_circ_buffer_destroy(sip->txbuf); g_free(sip->realhostname);
--- a/libpurple/protocols/simple/simple.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/simple/simple.h Wed Jan 30 10:53:22 2008 +0000 @@ -112,6 +112,7 @@ int registerexpire; gchar *realhostname; int realport; /* port and hostname from SRV record */ + gchar *publish_etag; }; struct sip_connection {
--- a/libpurple/protocols/yahoo/util.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/util.c Wed Jan 30 10:53:22 2008 +0000 @@ -32,6 +32,70 @@ #include "util.h" #include <string.h> +/* + * Returns cookies formatted as a null terminated string for the given connection. + * Must g_free return value. + * + * TODO:will work, but must test for strict correctness + */ +gchar* yahoo_get_cookies(PurpleConnection *gc) +{ + gchar *ans = NULL; + gchar *cur; + char firstflag = 1; + gchar *t1,*t2,*t3; + GSList *tmp; + GSList *cookies; + cookies = ((struct yahoo_data*)(gc->proto_data))->cookies; + tmp = cookies; + while(tmp) + { + cur = tmp->data; + t1 = ans; + t2 = g_strrstr(cur, ";expires="); + if(t2 == NULL) + t2 = g_strrstr(cur, "; expires="); + if(t2 == NULL) + { + if(firstflag) + ans = g_strdup_printf("%c=%s", cur[0], cur+2); + else + ans = g_strdup_printf("%s; %c=%s", t1, cur[0], cur+2); + } + else + { + t3 = strstr(t2+1, ";"); + if(t3 != NULL) + { + t2[0] = '\0'; + + if(firstflag) + ans = g_strdup_printf("%c=%s%s", cur[0], cur+2, t3); + else + ans = g_strdup_printf("%s; %c=%s%s", t1, cur[0], cur+2, t3); + + t2[0] = ';'; + } + else + { + t2[0] = '\0'; + + if(firstflag) + ans = g_strdup_printf("%c=%s", cur[0], cur+2); + else + ans = g_strdup_printf("%s; %c=%s", t1, cur[0], cur+2); + + t2[0] = ';'; + } + } + if(firstflag) + firstflag = 0; + else + g_free(t1); + tmp = g_slist_next(tmp); + } + return ans; +} /** * Encode some text to send to the yahoo server. @@ -121,6 +185,25 @@ return g_strdup(""); } +char *yahoo_convert_to_numeric(const char *str) +{ + GString *gstr = NULL; + char *retstr; + const unsigned char *p; + + gstr = g_string_sized_new(strlen(str) * 6 + 1); + + for (p = (unsigned char *)str; *p; p++) { + g_string_append_printf(gstr, "&#%u;", *p); + } + + retstr = gstr->str; + + g_string_free(gstr, FALSE); + + return retstr; +} + /* * I found these on some website but i don't know that they actually * work (or are supposed to work). I didn't implement them yet.
--- a/libpurple/protocols/yahoo/yahoo.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Wed Jan 30 10:53:22 2008 +0000 @@ -392,6 +392,10 @@ case 97: /* Unicode status message */ unicode = !strcmp(pair->value, "1"); break; + case 244: /* client version number. Yahoo Client Detection */ + if(f && strtol(pair->value, NULL, 10)) + f->version_id = strtol(pair->value, NULL, 10); + break; default: purple_debug(PURPLE_DEBUG_ERROR, "yahoo", @@ -493,16 +497,16 @@ static void yahoo_process_cookie(struct yahoo_data *yd, char *c) { if (c[0] == 'Y') { - g_free(yd->cookie_y); + if (yd->cookie_y) + g_free(yd->cookie_y); yd->cookie_y = _getcookie(c); } else if (c[0] == 'T') { - g_free(yd->cookie_t); + if (yd->cookie_t) + g_free(yd->cookie_t); yd->cookie_t = _getcookie(c); - } else if (c[0] == 'C') { - g_free(yd->cookie_c); - yd->cookie_c = _getcookie(c); } else - purple_debug_info("yahoo", "Ignoring unrecognized cookie '%c'\n", c[0]); + purple_debug_info("yahoo", "Unrecognized cookie '%c'\n", c[0]); + yd->cookies = g_slist_prepend(yd->cookies, g_strdup(c)); } static void yahoo_process_list_15(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -510,6 +514,7 @@ GSList *l = pkt->hash; PurpleAccount *account = purple_connection_get_account(gc); + struct yahoo_data *yd = gc->proto_data; GHashTable *ht; char *grp = NULL; char *norm_bud = NULL; @@ -575,6 +580,9 @@ purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol); } break; + case 59: /* somebody told cookies come here too, but im not sure */ + yahoo_process_cookie(yd, pair->value); + break; case 317: /* Stealth Setting */ if (f && (strtol(pair->value, NULL, 10) == 2)) { f->presence = YAHOO_PRESENCE_PERM_OFFLINE; @@ -1513,7 +1521,13 @@ to_y64(result96, digest, 16); pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pack, "ssss", 0, name, 6, result6, 96, result96, 1, name); + yahoo_packet_hash(pack, "ssssss", + 0, name, + 6, result6, + 96, result96, + 1, name, + 244, YAHOO_CLIENT_VERSION_ID, + 135, YAHOO_CLIENT_VERSION); yahoo_packet_send_and_free(pack, yd); g_free(hash_string_p); @@ -1532,11 +1546,11 @@ char *enc_pass; struct yahoo_data *yd = gc->proto_data; - PurpleCipher *md5_cipher; + PurpleCipher *md5_cipher; PurpleCipherContext *md5_ctx; guchar md5_digest[16]; - PurpleCipher *sha1_cipher; + PurpleCipher *sha1_cipher; PurpleCipherContext *sha1_ctx1; PurpleCipherContext *sha1_ctx2; @@ -1548,7 +1562,7 @@ char *delimit_lookup = ",;"; char *password_hash = (char *)g_malloc(25); - char *crypt_hash = (char *)g_malloc(25); + char *crypt_hash = (char *)g_malloc(25); char *crypt_result = NULL; unsigned char pass_hash_xor1[64]; @@ -1602,7 +1616,7 @@ magic_ptr = seed; - while (*magic_ptr != (int)NULL) { + while (*magic_ptr != '\0') { char *loc; /* Ignore parentheses. */ @@ -1655,7 +1669,7 @@ * dust on the values. */ - for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) { + for (magic_cnt = magic_len - 2; magic_cnt >= 0; magic_cnt--) { unsigned char byte1; unsigned char byte2; @@ -1671,7 +1685,7 @@ byte1 ^= byte2; magic[magic_cnt+1] = byte1; - } + } /* * Magic: Phase 3. This computes 20 bytes. The first 4 bytes are used as our magic @@ -1686,8 +1700,8 @@ x = 0; do { - unsigned int bl = 0; - unsigned int cl = magic[magic_cnt++]; + unsigned int bl = 0; + unsigned int cl = magic[magic_cnt++]; if (magic_cnt >= magic_len) break; @@ -1712,17 +1726,18 @@ /* First four bytes are magic key. */ memcpy(&magic_key_char[0], comparison_src, 4); - magic_4 = magic_key_char[0] | (magic_key_char[1]<<8) | (magic_key_char[2]<<16) | (magic_key_char[3]<<24); + magic_4 = magic_key_char[0] | (magic_key_char[1] << 8) | + (magic_key_char[2] << 16) | (magic_key_char[3] << 24); /* * Magic: Phase 4. Determine what function to use later by getting outside/inside * loop values until we match our previous buffer. */ for (x = 0; x < 65535; x++) { - int leave = 0; + int leave = 0; for (y = 0; y < 5; y++) { - unsigned char test[3]; + unsigned char test[3]; /* Calculate buffer. */ test[0] = x; @@ -1958,8 +1973,13 @@ } purple_debug_info("yahoo", "yahoo status: %d\n", yd->current_status); pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->current_status, 0); - yahoo_packet_hash(pack, "sssss", 0, name, 6, resp_6, 96, resp_96, 1, - name, 135, "6,0,0,1710"); + yahoo_packet_hash(pack, "ssssss", + 0, name, + 6, resp_6, + 96, resp_96, + 1, name, + 244, YAHOO_CLIENT_VERSION_ID, + 135, YAHOO_CLIENT_VERSION); if (yd->picture_checksum) yahoo_packet_hash_int(pack, 192, yd->picture_checksum); @@ -2449,12 +2469,16 @@ case YAHOO_SERVICE_AUDIBLE: yahoo_process_audible(gc, pkt); break; - case YAHOO_SERVICE_Y7_FILETRANSFER: - yahoo_process_y7_filetransfer(gc, pkt); + case YAHOO_SERVICE_FILETRANS_15: + yahoo_process_filetrans_15(gc, pkt); break; - case YAHOO_SERVICE_Y7_FILETRANSFER_INFO: - yahoo_process_y7_filetransfer_info(gc, pkt); + case YAHOO_SERVICE_FILETRANS_INFO_15: + yahoo_process_filetrans_info_15(gc, pkt); break; + case YAHOO_SERVICE_FILETRANS_ACC_15: + yahoo_process_filetrans_acc_15(gc, pkt); + break; + default: purple_debug(PURPLE_DEBUG_ERROR, "yahoo", "Unhandled service 0x%02x\n", pkt->service); @@ -2488,7 +2512,7 @@ _("Server closed the connection.")); return; } - + gc->last_received = time(NULL); yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen); memcpy(yd->rxqueue + yd->rxlen, buf, len); yd->rxlen += len; @@ -2676,11 +2700,15 @@ s = g_string_sized_new(len); while ((i = strstr(i, "Set-Cookie: "))) { + i += strlen("Set-Cookie: "); for (;*i != ';' && *i != '\0'; i++) g_string_append_c(s, *i); - + g_string_append(s, "; "); + /* Should these cookies be included too when trying for xfer? + * It seems to work without these + */ } yd->auth = g_string_free(s, FALSE); @@ -2970,6 +2998,7 @@ yd->txbuf = purple_circ_buffer_new(0); yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free); yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); yd->confs = NULL; yd->conf_id = 2; @@ -3024,17 +3053,23 @@ } g_slist_free(yd->confs); + for (l = yd->cookies; l; l = l->next) { + g_free(l->data); + l->data=NULL; + } + g_slist_free(yd->cookies); + yd->chat_online = 0; if (yd->in_chat) yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */ g_hash_table_destroy(yd->friends); g_hash_table_destroy(yd->imvironments); + g_hash_table_destroy(yd->xfer_peer_idstring_map); g_free(yd->chat_name); g_free(yd->cookie_y); g_free(yd->cookie_t); - g_free(yd->cookie_c); if (yd->txhandler) purple_input_remove(yd->txhandler); @@ -4144,17 +4179,13 @@ GList *yahoo_attention_types(PurpleAccount *account) { - PurpleAttentionType *attn; static GList *list = NULL; if (!list) { /* Yahoo only supports one attention command: the 'buzz'. */ /* This is index number YAHOO_BUZZ. */ - attn = g_new0(PurpleAttentionType, 1); - attn->name = _("Buzz"); - attn->incoming_description = _("%s has buzzed you!"); - attn->outgoing_description = _("Buzzing %s..."); - list = g_list_append(list, attn); + list = g_list_append(list, purple_attention_type_new("Buzz", _("Buzz"), + _("%s has buzzed you!"), _("Buzzing %s..."))); } return list;
--- a/libpurple/protocols/yahoo/yahoo.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Wed Jan 30 10:53:22 2008 +0000 @@ -34,6 +34,8 @@ #define YAHOO_MAIL_URL "https://login.yahoo.com/config/login?.src=ym" #define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com" #define YAHOO_XFER_PORT 80 +#define YAHOO_XFER_RELAY_HOST "relay.msg.yahoo.com" +#define YAHOO_XFER_RELAY_PORT 80 #define YAHOO_ROOMLIST_URL "http://insider.msg.yahoo.com/ycontent/" #define YAHOO_ROOMLIST_LOCALE "us" /* really we should get the list of servers from @@ -43,6 +45,11 @@ #define YAHOOJP_MAIL_URL "http://mail.yahoo.co.jp/" #define YAHOOJP_XFER_HOST "filetransfer.msg.yahoo.co.jp" #define YAHOOJP_WEBCAM_HOST "wc.yahoo.co.jp" +/*not sure, must test:*/ +#define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.com" +#define YAHOOJP_XFER_RELAY_PORT 80 +#define YAHOOJP_ROOMLIST_URL "http://insider.msg.yahoo.co.jp/ycontent/" +#define YAHOOJP_ROOMLIST_LOCALE "ja" #define YAHOO_AUDIBLE_URL "http://us.dl1.yimg.com/download.yahoo.com/dl/aud" @@ -67,6 +74,9 @@ #define YAHOO_STATUS_TYPE_INVISIBLE "invisible" #define YAHOO_STATUS_TYPE_MOBILE "mobile" +#define YAHOO_CLIENT_VERSION_ID "2097087" +#define YAHOO_CLIENT_VERSION "8.1.0.421" + /* Index into attention types list. */ #define YAHOO_BUZZ 0 @@ -86,7 +96,8 @@ YAHOO_STATUS_IDLE = 999, YAHOO_STATUS_WEBLOGIN = 0x5a55aa55, YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */ - YAHOO_STATUS_TYPING = 0x16 + YAHOO_STATUS_TYPING = 0x16, + YAHOO_STATUS_DISCONNECTED = 0xffffffff /* in ymsg 15. doesnt mean the normal sense of 'disconnected' */ }; struct yahoo_buddy_icon_upload_data { @@ -134,7 +145,6 @@ gsize auth_written; char *cookie_y; char *cookie_t; - char *cookie_c; int session_id; gboolean jp; gboolean wm; /* connected w/ web messenger method */ @@ -154,6 +164,8 @@ * for when we lookup people profile or photo information. */ GSList *url_datas; + GHashTable *xfer_peer_idstring_map;/*Hey, i dont know, but putting this HashTable next to friends gives a run time fault...*/ + GSList *cookies;/*contains all cookies, including _y and _t*/ }; #define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255) @@ -206,12 +218,20 @@ */ char *yahoo_string_decode(PurpleConnection *gc, const char *str, gboolean utf8); +char *yahoo_convert_to_numeric(const char *str); + /* previously-static functions, now needed for yahoo_profile.c */ void yahoo_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full); /* yahoo_profile.c */ void yahoo_get_info(PurpleConnection *gc, const char *name); +/* needed for xfer, thought theyd be useful for other enhancements later on + Returns list of cookies stored in yahoo_data formatted as a single null terminated string + returned value must be g_freed +*/ +gchar* yahoo_get_cookies(PurpleConnection *gc); + /** * Check to see whether the sender is permitted to send *
--- a/libpurple/protocols/yahoo/yahoo_aliases.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_aliases.c Wed Jan 30 10:53:22 2008 +0000 @@ -37,6 +37,8 @@ /* I hate hardcoding this stuff, but Yahoo never sends us anything to use. Someone in the know may be able to tweak this URL */ #define YAHOO_ALIAS_FETCH_URL "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us&diffs=1&t=0&tags=short&rt=0&prog-ver=8.1.0.249&useutf8=1&legenc=codepage-1252" #define YAHOO_ALIAS_UPDATE_URL "http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us&sync=1&tags=short&noclear=1&useutf8=1&legenc=codepage-1252" +#define YAHOOJP_ALIAS_FETCH_URL "http://address.yahoo.co.jp/yab/jp?v=XM&prog=ymsgr&.intl=jp&diffs=1&t=0&tags=short&rt=0&prog-ver=7.0.0.7" +#define YAHOOJP_ALIAS_UPDATE_URL "http://address.yahoo.co.jp/yab/jp?v=XM&prog=ymsgr&.intl=jp&sync=1&tags=short&noclear=1" void yahoo_update_alias(PurpleConnection *gc, const char *who, const char *alias); @@ -90,7 +92,10 @@ id = xmlnode_get_attrib(item,"id"); /* Yahoo stores first and last names separately, lets put them together into a full name */ - full_name = g_strstrip(g_strdup_printf("%s %s", (fn != NULL ? fn : "") , (ln != NULL ? ln : ""))); + if (yd->jp) + full_name = g_strstrip(g_strdup_printf("%s %s", (ln != NULL ? ln : "") , (fn != NULL ? fn : ""))); + else + full_name = g_strstrip(g_strdup_printf("%s %s", (fn != NULL ? fn : "") , (ln != NULL ? ln : ""))); nick_name = (nn != NULL ? g_strstrip(g_strdup_printf("%s", nn)) : NULL); if (nick_name != NULL) @@ -139,7 +144,7 @@ { struct yahoo_data *yd = gc->proto_data; struct callback_data *cb; - const char *url = YAHOO_ALIAS_FETCH_URL; + const char *url; char *request, *webpage, *webaddress; PurpleUtilFetchUrlData *url_data; @@ -148,6 +153,7 @@ cb->gc = gc; /* Build all the info to make the web request */ + url = yd->jp ? YAHOOJP_ALIAS_FETCH_URL : YAHOO_ALIAS_FETCH_URL; purple_url_parse(url, &webaddress, NULL, &webpage, NULL, NULL); request = g_strdup_printf("GET /%s HTTP/1.1\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n" @@ -221,14 +227,15 @@ struct yahoo_data *yd; struct YahooUser *yu; char *content, *url, *request, *webpage, *webaddress, *strtmp; + char *escaped_alias, *alias_jp, *converted_alias_jp; int inttmp; struct callback_data *cb; PurpleBuddy *buddy; PurpleUtilFetchUrlData *url_data; - g_return_if_fail(alias!= NULL); - g_return_if_fail(who!=NULL); - g_return_if_fail(gc!=NULL); + g_return_if_fail(alias != NULL); + g_return_if_fail(who != NULL); + g_return_if_fail(gc != NULL); purple_debug_info("yahoo", "Sending '%s' as new alias for user '%s'.\n",alias, who); @@ -247,12 +254,25 @@ cb->gc = gc; /* Build all the info to make the web request */ - url = g_strdup(YAHOO_ALIAS_UPDATE_URL); + url = yd->jp? YAHOOJP_ALIAS_UPDATE_URL: YAHOO_ALIAS_UPDATE_URL; purple_url_parse(url, &webaddress, &inttmp, &webpage, &strtmp, &strtmp); - content = g_strdup_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?><ab k=\"%s\" cc=\"1\">\n" - "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", - gc->account->username, who, yu->id, g_markup_escape_text(alias, strlen(alias))); + if (yd->jp) { + alias_jp = g_convert(alias, strlen(alias), "EUC-JP", "UTF-8", NULL, NULL, NULL); + converted_alias_jp = yahoo_convert_to_numeric(alias_jp); + content = g_strdup_printf("<ab k=\"%s\" cc=\"1\">\n" + "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", + gc->account->username, who, yu->id, converted_alias_jp); + free(converted_alias_jp); + g_free(alias_jp); + } + else { + escaped_alias = g_markup_escape_text(alias, strlen(alias)); + content = g_strdup_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?><ab k=\"%s\" cc=\"1\">\n" + "<ct e=\"1\" yi='%s' id='%s' nn='%s' pr='0' />\n</ab>\r\n", + gc->account->username, who, yu->id, escaped_alias); + g_free(escaped_alias); + } request = g_strdup_printf("POST /%s HTTP/1.1\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n" @@ -271,7 +291,6 @@ } g_free(content); - g_free(url); g_free(request); }
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Wed Jan 30 10:53:22 2008 +0000 @@ -21,6 +21,7 @@ */ #include "internal.h" +#include "dnsquery.h" #include "prpl.h" #include "util.h" @@ -32,6 +33,7 @@ #include "yahoo_packet.h" #include "yahoo_filexfer.h" #include "yahoo_doodle.h" +#include "yahoo_friend.h" struct yahoo_xfer_data { gchar *host; @@ -46,81 +48,65 @@ guint tx_handler; gchar *rxqueue; guint rxlen; + gchar *xfer_peer_idstring; + gchar *xfer_idstring_for_relay; + int version; /*0 for old, 15 for Y7(YMSG 15)*/ + int info_val_249; - gboolean y7; /* true for Y7 transfers (receive only for now) */ - gchar *token; - gchar *tid; + enum { + STARTED = 0, + HEAD_REQUESTED, + HEAD_REPLY_RECEIVED, + TRANSFER_PHASE, + ACCEPTED + } status_15; + + /* contains all filenames, in case of multiple transfers, with the first + * one in the list being the current file's name (ymsg15) */ + GSList *filename_list; + GSList *size_list; /*corresponds to filename_list, with size as **STRING** */ + gboolean firstoflist; }; static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd) { + PurpleConnection *gc; + struct yahoo_data *yd; + PurpleXfer *xfer; + GSList *l; + + gc = xd->gc; + yd = gc->proto_data; + + /*remove entry from map*/ + if(xd->xfer_peer_idstring) { + xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xd->xfer_peer_idstring); + if(xfer) + g_hash_table_remove(yd->xfer_peer_idstring_map, xd->xfer_peer_idstring); + } + + /*empty file & filesize list*/ + for (l = xd->filename_list; l; l = l->next) { + g_free(l->data); + l->data=NULL; + } + for (l = xd->size_list; l; l = l->next) { + g_free(l->data); + l->data=NULL; + } + g_slist_free(xd->filename_list); + g_slist_free(xd->size_list); + g_free(xd->host); g_free(xd->path); g_free(xd->txbuf); - g_free(xd->token); - g_free(xd->tid); + g_free(xd->xfer_peer_idstring); + g_free(xd->xfer_idstring_for_relay); if (xd->tx_handler) purple_input_remove(xd->tx_handler); g_free(xd); } - -static void yahoo_xfer_y7_request_next_file(PurpleXfer *xfer) -{ - struct yahoo_packet *pack; - struct yahoo_xfer_data *xd = xfer->data; - PurpleConnection *gc = xd->gc; - struct yahoo_data *yd = gc->proto_data; - - g_return_if_fail(xd->y7); - - pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pack, "sssi", - 1, purple_connection_get_display_name(xd->gc), - 5, xfer->who, - 265, xd->tid, - 271, 1); - yahoo_packet_send_and_free(pack, yd); -} - -static void yahoo_xfer_y7_cancel_receive(PurpleXfer *xfer) -{ - struct yahoo_packet *pack; - struct yahoo_xfer_data *xd = xfer->data; - PurpleConnection *gc = xd->gc; - struct yahoo_data *yd = gc->proto_data; - - g_return_if_fail(xd->y7); - - pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, -1, 0); - yahoo_packet_hash(pack, "sssi", - 1, purple_connection_get_display_name(gc), - 5, xfer->who, - 265, xd->tid, - 66, -1); - yahoo_packet_send_and_free(pack, yd); -} - -static void yahoo_xfer_y7_accept_file(PurpleXfer *xfer) -{ - struct yahoo_packet *pack; - struct yahoo_xfer_data *xd = xfer->data; - PurpleConnection *gc = xd->gc; - struct yahoo_data *yd = gc->proto_data; - - g_return_if_fail(xd->y7); - - pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pack, "ssssis", - 1, purple_connection_get_display_name(gc), - 5, xfer->who, /* XXX this needs an accessor */ - 265, xd->tid, - 27, purple_xfer_get_filename(xfer), /* XXX this might be of incorrect encoding */ - 249, 3, - 251, xd->token); - yahoo_packet_send_and_free(pack, yd); -} - static void yahoo_receivefile_send_cb(gpointer data, gint source, PurpleInputCondition condition) { PurpleXfer *xfer; @@ -160,7 +146,6 @@ { PurpleXfer *xfer; struct yahoo_xfer_data *xd; - struct yahoo_data *yd; purple_debug(PURPLE_DEBUG_INFO, "yahoo", "AAA - in yahoo_receivefile_connected\n"); @@ -176,22 +161,11 @@ } xfer->fd = source; - yd = xd->gc->proto_data; /* The first time we get here, assemble the tx buffer */ if (xd->txbuflen == 0) { - if (!xd->y7) - xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", - xd->path, xd->host); - else - xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\n" - "Connection: close\r\n" - "Accept: */*\r\n" - "Host: %s\r\n" - "Cookie: Y=%s; T=%s\r\n" - "\r\n", - xd->path, xd->host, yd->cookie_y, yd->cookie_t); - purple_debug(PURPLE_DEBUG_INFO, "yahoo_filexfer", "HTTP request: [%s]\n", xd->txbuf); + xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", + xd->path, xd->host); xd->txbuflen = strlen(xd->txbuf); xd->txbuf_written = 0; } @@ -361,9 +335,6 @@ } } } else { - if (xfer_data->y7) - yahoo_xfer_y7_accept_file(xfer); - xfer->fd = -1; if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, yahoo_receivefile_connected, xfer) == NULL) { @@ -374,23 +345,67 @@ } } +static void yahoo_xfer_init_15(PurpleXfer *xfer) +{ + struct yahoo_xfer_data *xfer_data; + PurpleConnection *gc; + PurpleAccount *account; + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + xfer_data = xfer->data; + gc = xfer_data->gc; + yd = gc->proto_data; + account = purple_connection_get_account(gc); + + if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) { + gchar *filename; + filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, + YAHOO_STATUS_AVAILABLE, + yd->session_id); + yahoo_packet_hash(pkt, "sssiiiisiii", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 222, 1, + 266, 1, + 302, 268, + 300, 268, + 27, filename, + 28, xfer->size, + 301, 268, + 303, 268); + g_free(filename); + } else { + if(xfer_data->firstoflist == TRUE) { + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, + YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, "sssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 222, 3); + } else { + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, + YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, "sssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 271, 1); + } + } + yahoo_packet_send_and_free(pkt, yd); +} + static void yahoo_xfer_start(PurpleXfer *xfer) { /* We don't need to do anything here, do we? */ } -static void yahoo_xfer_end(PurpleXfer *xfer) -{ - struct yahoo_xfer_data *xfer_data; - - xfer_data = xfer->data; - - if (xfer_data) - yahoo_xfer_data_free(xfer_data); - xfer->data = NULL; - -} - static guint calculate_length(const gchar *l, size_t len) { int i; @@ -423,8 +438,6 @@ if ((purple_xfer_get_size(xfer) > 0) && (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer))) { purple_xfer_set_completed(xfer, TRUE); - if (xd->y7) - yahoo_xfer_y7_request_next_file(xfer); return 0; } else return -1; @@ -504,6 +517,42 @@ xfer_data = xfer->data; + if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL && xfer_data->version == 15) + { + PurpleConnection *gc; + PurpleAccount *account; + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + gc = xfer_data->gc; + yd = gc->proto_data; + account = purple_connection_get_account(gc); + if(xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */ + { + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, + YAHOO_STATUS_DISCONNECTED, + yd->session_id); + yahoo_packet_hash(pkt, "sssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 66, -1); + } + else + { + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, + YAHOO_STATUS_AVAILABLE, + yd->session_id); + yahoo_packet_hash(pkt, "sssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 222, 2); + } + yahoo_packet_send_and_free(pkt, yd); + } + + if (xfer_data) yahoo_xfer_data_free(xfer_data); xfer->data = NULL; @@ -515,22 +564,145 @@ xfer_data = xfer->data; - if (xfer_data) { - if (xfer_data->y7) - yahoo_xfer_y7_cancel_receive(xfer); + if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL && xfer_data->version == 15) + { + + PurpleConnection *gc; + PurpleAccount *account; + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + gc = xfer_data->gc; + yd = gc->proto_data; + account = purple_connection_get_account(gc); + if(!xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */ + { + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, + YAHOO_STATUS_AVAILABLE, + yd->session_id); + yahoo_packet_hash(pkt, "sssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 222, 4); + } + else + { + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, + YAHOO_STATUS_DISCONNECTED, + yd->session_id); + yahoo_packet_hash(pkt, "sssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 66, -1); + } + yahoo_packet_send_and_free(pkt, yd); + } + + if (xfer_data) yahoo_xfer_data_free(xfer_data); - } xfer->data = NULL; } -static void yahoo_xfer_request_denied(PurpleXfer *xfer) +static void yahoo_xfer_end(PurpleXfer *xfer_old) { struct yahoo_xfer_data *xfer_data; + PurpleXfer *xfer = NULL; + PurpleConnection *gc; + struct yahoo_data *yd; - xfer_data = xfer->data; + xfer_data = xfer_old->data; + if(xfer_data && xfer_data->version == 15 + && purple_xfer_get_type(xfer_old) == PURPLE_XFER_RECEIVE + && xfer_data->filename_list) { + + /* removing top of filename & size list completely */ + g_free( xfer_data->filename_list->data ); + g_free( xfer_data->size_list->data ); + + xfer_data->filename_list->data = NULL; + xfer_data->size_list->data = NULL; + + xfer_data->filename_list = g_slist_delete_link(xfer_data->filename_list, xfer_data->filename_list); + xfer_data->size_list = g_slist_delete_link(xfer_data->size_list, xfer_data->size_list); + + /* if there are still more files */ + if(xfer_data->filename_list) + { + gchar* filename; + long filesize; + + filename = xfer_data->filename_list->data; + filesize = atol( xfer_data->size_list->data ); + + gc = xfer_data->gc; + yd = gc->proto_data; - if (xfer_data->y7) - yahoo_xfer_y7_cancel_receive(xfer); + /* setting up xfer_data for next file's tranfer */ + g_free(xfer_data->host); + g_free(xfer_data->path); + g_free(xfer_data->txbuf); + g_free(xfer_data->rxqueue); + g_free(xfer_data->xfer_idstring_for_relay); + if (xfer_data->tx_handler) + purple_input_remove(xfer_data->tx_handler); + xfer_data->host = NULL; + xfer_data->host = NULL; + xfer_data->port = 0; + xfer_data->expires = 0; + xfer_data->started = FALSE; + xfer_data->txbuf = NULL; + xfer_data->txbuflen = 0; + xfer_data->txbuf_written = 0; + xfer_data->tx_handler = 0; + xfer_data->rxqueue = NULL; + xfer_data->rxlen = 0; + xfer_data->xfer_idstring_for_relay = NULL; + xfer_data->info_val_249 = 0; + xfer_data->status_15 = STARTED; + xfer_data->firstoflist = FALSE; + + /* Dereference xfer_data from old xfer */ + xfer_old->data = NULL; + + /* Build the file transfer handle. */ + xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, xfer_old->who); + + + if (xfer) { + /* Set the info about the incoming file. */ + char *utf8_filename = yahoo_string_decode(gc, filename, TRUE); + purple_xfer_set_filename(xfer, utf8_filename); + g_free(utf8_filename); + purple_xfer_set_size(xfer, filesize); + + xfer->data = xfer_data; + + /* Setup our I/O op functions */ + purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); + purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); + purple_xfer_set_end_fnc(xfer, yahoo_xfer_end); + purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); + purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); + purple_xfer_set_read_fnc(xfer, yahoo_xfer_read); + purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); + purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv); + + /*update map to current xfer*/ + g_hash_table_remove(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring); + g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer); + + /* Now perform the request */ + purple_xfer_request(xfer); + } + return; + } + } + if (xfer_data) + yahoo_xfer_data_free(xfer_data); + xfer_old->data = NULL; + } void yahoo_process_p2pfilexfer(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -726,165 +898,6 @@ } } -void yahoo_process_y7_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - struct yahoo_data *yd = gc->proto_data; - char *who = NULL, *name = NULL; - int ttype = 0; - char *tid = NULL; - GSList *l = pkt->hash; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 4: - /* them */ - who = pair->value; - break; - case 5: - /* us */ - name = pair->value; - break; - case 222: - /* 1=send, 2=cancel, 3=accept, 4=reject */ - if(pair->value) - ttype = atoi(pair->value); - break; - case 265: - /* transfer ID */ - tid = pair->value; - break; - case 266: - /* number of files */ - break; - case 27: - /* filename */ - break; - case 28: - /* filesize */ - break; - } - - l = l->next; - } - if (ttype == 1 && tid) { - /* We auto-accept all offers here, and ask the user about each individual - * file in yahoo_process_y7_filetransfer_info. This works fine for receiving - * a single file; when receiving multiple canceling one in the middle - * will also cancel the rest of them. - * Maybe TODO: UI and API allowing transfer of multiple files as a package. */ - struct yahoo_packet *pack; - pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pack, "sssi", 1, name, 5, who, 265, tid, 222, 3); - yahoo_packet_send_and_free(pack, yd); - } -} - -void yahoo_process_y7_filetransfer_info(PurpleConnection *gc, struct yahoo_packet *pkt) -{ - struct yahoo_data *yd = gc->proto_data; - char *who = NULL, *name = NULL; - int medium = 0; - char *tid = NULL, *server_host = NULL, *server_token = NULL, *filename = NULL; - GSList *l = pkt->hash; - struct yahoo_packet *pack; - PurpleXfer *xfer; - struct yahoo_xfer_data *xfer_data; - - while (l) { - struct yahoo_pair *pair = l->data; - - switch (pair->key) { - case 4: - /* them */ - who = pair->value; - break; - case 5: - /* us */ - name = pair->value; - break; - case 249: - /* 1=p2p, 3=reflection server */ - if(pair->value) - medium = atoi(pair->value); - break; - case 265: - /* transfer ID */ - tid = pair->value; - break; - case 27: - filename = pair->value; - break; - case 250: - server_host = pair->value; - break; - case 251: - server_token = pair->value; - break; - } - - l = l->next; - } - if (medium == 1) { - /* reject P2P transfers */ - pack = yahoo_packet_new(YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pack, "sssi", 1, name, 5, who, 265, tid, 66, -3); - yahoo_packet_send_and_free(pack, yd); - return; - } - - if (medium != 3) { - purple_debug_error("yahoo", "Unexpected medium %d.\n", medium); - /* weird */ - return; - } - - /* Setup the Yahoo-specific file transfer data */ - xfer_data = g_new0(struct yahoo_xfer_data, 1); - xfer_data->gc = gc; - xfer_data->host = g_strdup(server_host); - xfer_data->token = g_strdup(server_token); - xfer_data->tid = g_strdup(tid); - xfer_data->port = 80; - xfer_data->y7 = TRUE; - - /* TODO: full urlencode here */ - server_token = purple_strreplace(server_token, "\002", "%02"); - xfer_data->path = g_strdup_printf("relay?token=%s&sender=%s&recver=%s", - server_token, who, name); - g_free(server_token); - - purple_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s.\n", - xfer_data->host, xfer_data->port, xfer_data->path); - - /* Build the file transfer handle. */ - xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, who); - xfer->data = xfer_data; - - /* Set the info about the incoming file. */ - { - char *utf8_filename = yahoo_string_decode(gc, filename, TRUE); - purple_xfer_set_filename(xfer, utf8_filename); - g_free(utf8_filename); - } - - /* purple_xfer_set_size(xfer, filesize); */ - - /* Setup our I/O op functions */ - purple_xfer_set_init_fnc(xfer, yahoo_xfer_init); - purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); - purple_xfer_set_end_fnc(xfer, yahoo_xfer_end); - purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); - purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); - purple_xfer_set_read_fnc(xfer, yahoo_xfer_read); - purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); - purple_xfer_set_request_denied_fnc(xfer, yahoo_xfer_request_denied); - - /* Now perform the request */ - purple_xfer_request(xfer); -} - PurpleXfer *yahoo_new_xfer(PurpleConnection *gc, const char *who) { PurpleXfer *xfer; @@ -914,15 +927,641 @@ return xfer; } +static gchar* yahoo_xfer_new_xfer_id(void) +{ + gchar *ans; + int i,j; + ans = g_strnfill(24, ' '); + ans[23] = '$'; + ans[22] = '$'; + for(i = 0; i < 22; i++) + { + j = g_random_int_range (0,61); + if(j < 26) + ans[i] = j + 'a'; + else if(j < 52) + ans[i] = j - 26 + 'A'; + else + ans[i] = j - 52 + '0'; + } + return ans; +} + +static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char *error_message) +{ + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + struct sockaddr_in *addr; + struct yahoo_packet *pkt; + long actaddr; + long a,b,c,d; + PurpleConnection *gc; + PurpleAccount *account; + struct yahoo_data *yd; + gchar *url; + gchar *filename; + + if (!(xfer = data)) + return; + if (!(xd = xfer->data)) + return; + gc = xd->gc; + account = purple_connection_get_account(gc); + yd = gc->proto_data; + + if(!hosts) + { + purple_debug_error("yahoo", "Unable to find an IP address for relay.msg.yahoo.com\n"); + purple_xfer_cancel_remote(xfer); + return; + } + + /* Discard the length... */ + hosts = g_slist_remove(hosts, hosts->data); + if(!hosts) + { + purple_debug_error("yahoo", "Unable to find an IP address for relay.msg.yahoo.com\n"); + purple_xfer_cancel_remote(xfer); + return; + } + + /*TODO:actually, u must try with addr no.1 , if its not working addr no.2 .....*/ + addr = hosts->data; + actaddr = addr->sin_addr.s_addr; + d = actaddr % 256; + actaddr = (actaddr - d) / 256; + c = actaddr % 256; + actaddr = (actaddr - c) / 256; + b = actaddr % 256; + actaddr = (actaddr - b) / 256; + a = actaddr; + if(yd->jp) + xd->port = YAHOOJP_XFER_RELAY_PORT; + else + 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); + return; + } + g_free(url); + /* Free the address... */ + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + addr = NULL; + while (hosts != NULL) + { + /* Discard the length... */ + hosts = g_slist_remove(hosts, hosts->data); + /* Free the address... */ + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + } + + 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)); + + yahoo_packet_hash(pkt, "ssssis", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xd->xfer_peer_idstring, + 27, filename, + 249, 3, + 250, xd->host); + + g_free(filename); + yahoo_packet_send_and_free(pkt, yd); +} + + void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file) { + struct yahoo_xfer_data *xfer_data; + struct yahoo_data *yd; + int ver = 0; PurpleXfer *xfer = yahoo_new_xfer(gc, who); + YahooFriend *yf = yahoo_friend_find(gc, who); + /* To determine whether client uses ymsg 15 i.e. client is higher than YM 7 */ + if(yf && yf->version_id > 500000) + ver=15; g_return_if_fail(xfer != NULL); + if(ver == 15) { + yd = gc->proto_data; + xfer_data = xfer->data; + xfer_data->status_15 = STARTED; + purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); + xfer_data->version = 15; + xfer_data->xfer_peer_idstring = yahoo_xfer_new_xfer_id(); + g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer); + } + /* Now perform the request */ if (file) purple_xfer_request_accepted(xfer, file); else purple_xfer_request(xfer); } + +static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/*using this in recv_cb*/ +static void yahoo_xfer_recv_cb_15(gpointer data, gint source, PurpleInputCondition condition) +{ + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + int did; + gchar* buf; + gchar* t; + PurpleAccount *account; + PurpleConnection *gc; + + xfer = data; + xd = xfer->data; + account = purple_connection_get_account(xd->gc); + gc = xd->gc; + + buf=g_strnfill(1000, 0); + while((did = read(source, buf, 998)) > 0) + { + xd->txbuflen += did; + buf[did] = '\0'; + t = xd->txbuf; + xd->txbuf = g_strconcat(t,buf,NULL); + g_free(t); + } + g_free(buf); + + if (did < 0 && errno == EAGAIN) return; + else if (did < 0) { + purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); + purple_xfer_cancel_remote(xfer); + return; + } + + purple_input_remove(xd->tx_handler); + xd->tx_handler = 0; + xd->txbuflen = 0; + + if(xd->status_15 == HEAD_REQUESTED) { + xd->status_15 = HEAD_REPLY_RECEIVED; + close(source);/*Is this required?*/ + g_free(xd->txbuf); + xd->txbuf = NULL; + if (purple_proxy_connect(NULL, account, xd->host, xd->port, yahoo_xfer_connected_15, xfer) == NULL) + { + purple_notify_error(gc, NULL, _("File Transfer Failed"), + _("Unable to establish file descriptor.")); + purple_xfer_cancel_remote(xfer); + } + } else { + purple_debug_error("yahoo","Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", + purple_xfer_get_type(xfer), + xd->status_15); + return; + } +} + +static void yahoo_xfer_send_cb_15(gpointer data, gint source, PurpleInputCondition condition) +{ + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + int remaining, written; + + xfer = data; + xd = xfer->data; + remaining = xd->txbuflen - xd->txbuf_written; + written = write(source, xd->txbuf + xd->txbuf_written, remaining); + + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) { + purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); + purple_xfer_cancel_remote(xfer); + return; + } + + if (written < remaining) { + xd->txbuf_written += written; + return; + } + + purple_input_remove(xd->tx_handler); + xd->tx_handler = 0; + g_free(xd->txbuf); + xd->txbuf = NULL; + xd->txbuflen = 0; + xd->txbuf_written = 0; + + if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED) + { + xd->status_15 = HEAD_REQUESTED; + xd->tx_handler = purple_input_add(source, PURPLE_INPUT_READ, yahoo_xfer_recv_cb_15, xfer); + yahoo_xfer_recv_cb_15(xfer, source, PURPLE_INPUT_READ); + } + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED) + { + xd->status_15 = TRANSFER_PHASE; + xfer->fd = source; + purple_xfer_start(xfer, source, NULL, 0); + } + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED) + { + xd->status_15 = TRANSFER_PHASE; + xfer->fd = source; + purple_xfer_start(xfer, source, NULL, 0); + } + else + { + purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15); + return; + } + +} + + +static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message) +{ + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + PurpleAccount *account; + struct yahoo_data* yd; + + if (!(xfer = data)) + return; + if (!(xd = xfer->data)) + return; + yd = xd->gc->proto_data; + account = purple_connection_get_account(xd->gc); + if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) { + purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer), + xfer->who, _("Unable to connect.")); + purple_xfer_cancel_remote(xfer); + return; + } + /* The first time we get here, assemble the tx buffer */ + if (xd->txbuflen == 0) + { + gchar* cookies; + cookies = yahoo_get_cookies(xd->gc); + if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED) + { + xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n", + purple_url_encode(xd->xfer_idstring_for_relay), + purple_normalize(account, purple_account_get_username(account)), + xfer->who, + cookies, + xd->host, + (long int)xfer->size); + } + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED) + { + xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nAccept:*/*\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n", + purple_url_encode(xd->xfer_idstring_for_relay), + purple_normalize(account, purple_account_get_username(account)), + xfer->who, + cookies, + xd->host); + } + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED) + { + xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nConnection: Keep-Alive\r\n\r\n", + purple_url_encode(xd->xfer_idstring_for_relay), + purple_normalize(account, purple_account_get_username(account)), + xfer->who, + cookies, + xd->host); + } + else + { + purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15); + g_free(cookies); + return; + } + xd->txbuflen = strlen(xd->txbuf); + xd->txbuf_written = 0; + g_free(cookies); + } + + if (!xd->tx_handler) + { + xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, + yahoo_xfer_send_cb_15, xfer); + yahoo_xfer_send_cb_15(xfer, source, PURPLE_INPUT_WRITE); + } +} + +void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + char *from = NULL; + char *to = NULL; + char *imv = NULL; + long val_222 = 0L; + PurpleXfer *xfer; + struct yahoo_data *yd; + struct yahoo_xfer_data *xfer_data; + char *service = NULL; + char *filename = NULL; + char *xfer_peer_idstring = NULL; + unsigned long filesize = 0L; + GSList *l; + GSList *filename_list = NULL; + GSList *size_list = NULL; + int nooffiles = 0; + + yd = gc->proto_data; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 4: + from = pair->value; + break; + case 5: + to = pair->value; + break; + case 265: + xfer_peer_idstring = pair->value; + break; + case 27: + filename_list = g_slist_prepend(filename_list, g_strdup(pair->value)); + nooffiles++; + break; + case 28: + size_list = g_slist_prepend(size_list, g_strdup(pair->value)); + break; + case 222: + val_222 = atol(pair->value); + /* 1=send, 2=cancel, 3=accept, 4=reject */ + break; + + /*check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/ + case 49: + service = pair->value; + break; + case 63: + imv = pair->value; + break; + /*end check*/ + + } + } + if(!xfer_peer_idstring) + return; + + if(val_222 == 2 || val_222 == 4) + { + xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, + xfer_peer_idstring); + if(!xfer) return; + purple_xfer_cancel_remote(xfer); + return; + } + if(val_222 == 3) + { + xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, + xfer_peer_idstring); + if(!xfer) + return; + /* + * In the file trans info packet tht we must reply with , we are supposed to mention the ip address... + * purple connect does not give me a way of finding the ip address... + * so, purple dnsquery is used... but retries, trying with next ip address etc. is not implemented..TODO + */ + if (yd->jp) + { + purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer); + } + else + { + purple_dnsquery_a(YAHOO_XFER_RELAY_HOST, YAHOO_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer); + } + return; + } + + /*processing for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/ + /* + * The remote user has changed their IMVironment. We + * record it for later use. + */ + if (from && imv && service && (strcmp("IMVIRONMENT", service) == 0)) { + g_hash_table_replace(yd->imvironments, g_strdup(from), g_strdup(imv)); + return; + } + + if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) { + if (service && (strcmp("FILEXFER", service) != 0)) { + purple_debug_misc("yahoo", "unhandled service 0x%02x\n", pkt->service); + return; + } + } + /*end processing*/ + + if(!filename_list) + return; + /* have to change list into order in which client at other end sends */ + filename_list = g_slist_reverse(filename_list); + size_list = g_slist_reverse(size_list); + filename = filename_list->data; + filesize = atol(size_list->data); + + if(!from) return; + xfer_data = g_new0(struct yahoo_xfer_data, 1); + xfer_data->version = 15; + xfer_data->firstoflist = TRUE; + xfer_data->gc = gc; + xfer_data->xfer_peer_idstring = g_strdup(xfer_peer_idstring); + xfer_data->filename_list = filename_list; + xfer_data->size_list = size_list; + + /* Build the file transfer handle. */ + xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, from); + xfer->message = NULL; + + if (xfer) + { + /* Set the info about the incoming file. */ + char *utf8_filename = yahoo_string_decode(gc, filename, TRUE); + purple_xfer_set_filename(xfer, utf8_filename); + g_free(utf8_filename); + purple_xfer_set_size(xfer, filesize); + + xfer->data = xfer_data; + + + /* Setup our I/O op functions */ + purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); + purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); + purple_xfer_set_end_fnc(xfer, yahoo_xfer_end); + purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); + purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); + purple_xfer_set_read_fnc(xfer, yahoo_xfer_read); + purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); + purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv); + + g_hash_table_insert(yd->xfer_peer_idstring_map, + xfer_data->xfer_peer_idstring, + xfer); + + if(nooffiles > 1) { + gchar* message; + message = g_strdup_printf(_("%s is trying to send you a group of %d files.\n"), xfer->who, nooffiles); + purple_xfer_conversation_write(xfer, message, FALSE); + g_free(message); + } + /* Now perform the request */ + purple_xfer_request(xfer); + } +} + +void yahoo_process_filetrans_info_15(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + char *from = NULL; + char *to = NULL; + char *url = NULL; + long val_249 = 0; + long val_66 = 0; + PurpleXfer *xfer; + struct yahoo_data *yd; + struct yahoo_xfer_data *xfer_data; + char *filename = NULL; + char *xfer_peer_idstring = NULL; + char *xfer_idstring_for_relay = NULL; + GSList *l; + struct yahoo_packet *pkt_to_send; + PurpleAccount *account; + + yd = gc->proto_data; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 4: + from = pair->value; + break; + case 5: + to = pair->value; + break; + case 265: + xfer_peer_idstring = pair->value; + break; + case 27: + filename = pair->value; + break; + case 66: + val_66 = strtol(pair->value, NULL, 10); + break; + case 249: + val_249 = strtol(pair->value, NULL, 10); /* + * really pissed off with this- i hv seen 2 occurences of this + * being 1(its normally 3) - and in those cases, the url + * format and corresponding processing seems to be different + * (i havent tested - couldnt reproduce a 1), although i + * guess its easier. + */ + break; + case 250: + url = pair->value; + break; + case 251: + xfer_idstring_for_relay = pair->value; + break; + } + } + + if(!xfer_peer_idstring) + return; + + xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_peer_idstring); + + if(!xfer) return; + + if(val_66==-1) + { + purple_xfer_cancel_remote(xfer); + return; + } + + xfer_data = xfer->data; + + xfer_data->info_val_249 = val_249; + xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); + if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) { + purple_xfer_cancel_remote(xfer); + return; + } + + account = purple_connection_get_account(xfer_data->gc); + + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, + YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt_to_send, "ssssisi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 27, xfer->filename, + 249, xfer_data->info_val_249, + 251, xfer_data->xfer_idstring_for_relay, + 222, 3); + + yahoo_packet_send_and_free(pkt_to_send, yd); + if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, + yahoo_xfer_connected_15, xfer) == NULL) { + purple_notify_error(gc, NULL, _("File Transfer Failed"), + _("Unable to establish file descriptor.")); + purple_xfer_cancel_remote(xfer); + } + +} +/*TODO: Check filename etc. No probs till some hacker comes in the way*/ +void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + gchar *xfer_peer_idstring = NULL; + gchar *xfer_idstring_for_relay = NULL; + PurpleXfer *xfer; + struct yahoo_data *yd; + struct yahoo_xfer_data *xfer_data; + GSList *l; + PurpleAccount *account; + long val_66 = 0; + + yd = gc->proto_data; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 251: + xfer_idstring_for_relay = pair->value; + break; + case 265: + xfer_peer_idstring = pair->value; + break; + case 66: + val_66 = atol(pair->value); + } + } + + xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_peer_idstring); + if(!xfer) return; + + if(val_66 == -1 || !(xfer_idstring_for_relay)) + { + purple_xfer_cancel_remote(xfer); + return; + } + + xfer_data = xfer->data; + xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); + xfer_data->status_15 = ACCEPTED; + account = purple_connection_get_account(gc); + + if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, + yahoo_xfer_connected_15, xfer) == NULL) + { + purple_notify_error(gc, NULL, _("File Transfer Failed"),_("Unable to connect")); + purple_xfer_cancel_remote(xfer); + } +}
--- a/libpurple/protocols/yahoo/yahoo_filexfer.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.h Wed Jan 30 10:53:22 2008 +0000 @@ -30,16 +30,6 @@ void yahoo_process_p2pfilexfer( PurpleConnection *gc, struct yahoo_packet *pkt ); /** - * Process ymsg version 7 file receive invites. - */ -void yahoo_process_y7_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt); - -/** - * Process ymsg version 7 file receive connection setups. - */ -void yahoo_process_y7_filetransfer_info(PurpleConnection *gc, struct yahoo_packet *pkt); - -/** * Process ymsg file receive invites. */ void yahoo_process_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt); @@ -61,4 +51,8 @@ */ void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file); +void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt); +void yahoo_process_filetrans_info_15(PurpleConnection *gc, struct yahoo_packet *pkt); +void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *pkt); + #endif
--- a/libpurple/protocols/yahoo/yahoo_friend.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.h Wed Jan 30 10:53:22 2008 +0000 @@ -48,6 +48,7 @@ gboolean bicon_sent_request; YahooPresenceVisibility presence; int protocol; /* 1=LCS, 2=MSN*/ + long int version_id; } YahooFriend; YahooFriend *yahoo_friend_find(PurpleConnection *gc, const char *name);
--- a/libpurple/protocols/yahoo/yahoo_packet.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.h Wed Jan 30 10:53:22 2008 +0000 @@ -99,12 +99,12 @@ YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8, YAHOO_SERVICE_AUDIBLE = 0xd0, YAHOO_SERVICE_AUTH_REQ_15 = 0xd6, - YAHOO_SERVICE_Y7_FILETRANSFER = 0xdc, - YAHOO_SERVICE_Y7_FILETRANSFER_INFO = 0xdd, - YAHOO_SERVICE_Y7_FILETRANSFER_ACCEPT = 0xde, YAHOO_SERVICE_CHGRP_15 = 0xe7, YAHOO_SERVICE_STATUS_15 = 0xf0, YAHOO_SERVICE_LIST_15 = 0xf1, + YAHOO_SERVICE_FILETRANS_15 = 0xdc, + YAHOO_SERVICE_FILETRANS_INFO_15 = 0xdd, + YAHOO_SERVICE_FILETRANS_ACC_15 = 0xde, YAHOO_SERVICE_WEBLOGIN = 0x0226, YAHOO_SERVICE_SMS_MSG = 0x02ea };
--- a/libpurple/protocols/yahoo/yahoochat.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoochat.c Wed Jan 30 10:53:22 2008 +0000 @@ -1461,28 +1461,30 @@ PurpleRoomlist *yahoo_roomlist_get_list(PurpleConnection *gc) { - struct yahoo_roomlist *yrl; + PurpleAccount *account; PurpleRoomlist *rl; - const char *rll; + PurpleRoomlistField *f; + GList *fields = NULL; + struct yahoo_roomlist *yrl; + const char *rll, *rlurl; char *url; - GList *fields = NULL; - PurpleRoomlistField *f; - rll = purple_account_get_string(purple_connection_get_account(gc), - "room_list_locale", YAHOO_ROOMLIST_LOCALE); + account = purple_connection_get_account(gc); - if (rll != NULL && *rll != '\0') { - url = g_strdup_printf("%s?chatcat=0&intl=%s", - purple_account_get_string(purple_connection_get_account(gc), - "room_list", YAHOO_ROOMLIST_URL), rll); - } else { - url = g_strdup_printf("%s?chatcat=0", - purple_account_get_string(purple_connection_get_account(gc), - "room_list", YAHOO_ROOMLIST_URL)); + /* for Yahoo Japan, it appears there is only one valid URL and locale */ + if(purple_account_get_bool(account, "yahoojp", FALSE)) { + rll = YAHOOJP_ROOMLIST_LOCALE; + rlurl = YAHOOJP_ROOMLIST_URL; + } + else { /* but for the rest of the world that isn't the case */ + rll = purple_account_get_string(account, "room_list_locale", YAHOO_ROOMLIST_LOCALE); + rlurl = purple_account_get_string(account, "room_list", YAHOO_ROOMLIST_URL); } + url = g_strdup_printf("%s?chatcat=0&intl=%s", rlurl, rll); + yrl = g_new0(struct yahoo_roomlist, 1); - rl = purple_roomlist_new(purple_connection_get_account(gc)); + rl = purple_roomlist_new(account); yrl->list = rl; purple_url_parse(url, &(yrl->host), NULL, &(yrl->path), NULL, NULL); @@ -1508,7 +1510,7 @@ purple_roomlist_set_fields(rl, fields); - if (purple_proxy_connect(NULL, purple_connection_get_account(gc), yrl->host, 80, + if (purple_proxy_connect(NULL, account, yrl->host, 80, yahoo_roomlist_got_connected, yrl) == NULL) { purple_notify_error(gc, NULL, _("Connection problem"), _("Unable to fetch room list."));
--- a/libpurple/protocols/zephyr/ZSendList.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/zephyr/ZSendList.c Wed Jan 30 10:53:22 2008 +0000 @@ -24,7 +24,7 @@ char *list[]; int nitems; Z_AuthProc cert_routine; - Code_t (*send_routine)(void); + Code_t (*send_routine)(); { Code_t retval; ZNotice_t newnotice;
--- a/libpurple/protocols/zephyr/ZSendNot.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/zephyr/ZSendNot.c Wed Jan 30 10:53:22 2008 +0000 @@ -20,7 +20,7 @@ Code_t ZSrvSendNotice(notice, cert_routine, send_routine) ZNotice_t *notice; Z_AuthProc cert_routine; - Code_t (*send_routine)(void); + Code_t (*send_routine)(); { Code_t retval; ZNotice_t newnotice;
--- a/libpurple/protocols/zephyr/zephyr.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Wed Jan 30 10:53:22 2008 +0000 @@ -156,13 +156,20 @@ #endif static Code_t zephyr_subscribe_to(zephyr_account* zephyr, char* class, char *instance, char *recipient, char* galaxy) { + size_t result; + Code_t ret_val = -1; if (use_tzc(zephyr)) { /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */ gchar *zsubstr = g_strdup_printf("((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",class,instance,recipient); - write(zephyr->totzc[ZEPHYR_FD_WRITE],zsubstr,strlen(zsubstr)); + size_t len = strlen(zsubstr); + result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zsubstr,len); + if (result != len) { + purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno)); + } else { + ret_val = ZERR_NONE; + } g_free(zsubstr); - return ZERR_NONE; } else { if (use_zeph02(zephyr)) { @@ -170,13 +177,10 @@ sub.zsub_class = class; sub.zsub_classinst = instance; sub.zsub_recipient = recipient; - return ZSubscribeTo(&sub,1,0); - } else { - /* This should not happen */ - return -1; + ret_val = ZSubscribeTo(&sub,1,0); } } - return -1; + return ret_val; } char *local_zephyr_normalize(zephyr_account* zephyr,const char *); @@ -1373,7 +1377,11 @@ } else if (use_tzc(zephyr)) { gchar *zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",chk); - write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,strlen(zlocstr)); + size_t len = strlen(zlocstr); + size_t result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,len); + if (result != len) { + purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno)); + } g_free(zlocstr); } } @@ -2193,6 +2201,8 @@ html_buf2 = purple_unescape_html(html_buf); if(use_tzc(zephyr)) { + size_t len; + size_t result; char* zsendstr; /* CMU cclub tzc doesn't grok opcodes for now */ char* tzc_sig = zephyr_tzc_escape_msg(sig); @@ -2200,7 +2210,14 @@ zsendstr = g_strdup_printf("((tzcfodder . send) (class . \"%s\") (auth . t) (recipients (\"%s\" . \"%s\")) (message . (\"%s\" \"%s\")) ) \n", zclass, instance, recipient, tzc_sig, tzc_body); /* fprintf(stderr,"zsendstr = %s\n",zsendstr); */ - write(zephyr->totzc[ZEPHYR_FD_WRITE],zsendstr,strlen(zsendstr)); + len = strlen(zsendstr); + result = write(zephyr->totzc[ZEPHYR_FD_WRITE], zsendstr, len); + if (result != len) { + g_free(zsendstr); + g_free(html_buf2); + g_free(html_buf); + return errno; + } g_free(zsendstr); } else if (use_zeph02(zephyr)) { ZNotice_t notice; @@ -2221,6 +2238,9 @@ purple_debug_info("zephyr","About to send notice\n"); if (! ZSendNotice(¬ice, ZAUTH) == ZERR_NONE) { /* XXX handle errors here */ + g_free(buf); + g_free(html_buf2); + g_free(html_buf); return 0; } purple_debug_info("zephyr","notice sent\n"); @@ -2257,7 +2277,7 @@ ZAsyncLocateData_t ald; zephyr_account *zephyr = gc->proto_data; gchar* normalized_who = local_zephyr_normalize(zephyr,who); - + if (use_zeph02(zephyr)) { if (ZRequestLocations(normalized_who, &ald, UNACKED, ZAUTH) == ZERR_NONE) { zephyr->pending_zloc_names = g_list_append(zephyr->pending_zloc_names, @@ -2266,14 +2286,22 @@ /* XXX deal with errors somehow */ } } else if (use_tzc(zephyr)) { + size_t len; + size_t result; char* zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",normalized_who); zephyr->pending_zloc_names = g_list_append(zephyr->pending_zloc_names, g_strdup(normalized_who)); - write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,strlen(zlocstr)); + len = strlen(zlocstr); + result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,len); + if (result != len) { + purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno)); + } g_free(zlocstr); } } static void zephyr_set_status(PurpleAccount *account, PurpleStatus *status) { + size_t len; + size_t result; zephyr_account *zephyr = purple_account_get_connection(account)->proto_data; PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status)); @@ -2291,7 +2319,11 @@ } else { char *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr->ourhost,zephyr->exposure); - write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,strlen(zexpstr)); + len = strlen(zexpstr); + result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,len); + if (result != len) { + purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno)); + } g_free(zexpstr); } } @@ -2301,7 +2333,11 @@ ZSetLocation(EXPOSE_OPSTAFF); } else { char *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr->ourhost,EXPOSE_OPSTAFF); - write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,strlen(zexpstr)); + len = strlen(zexpstr); + result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,len); + if (result != len) { + purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno)); + } g_free(zexpstr); } }
--- a/libpurple/protocols/zephyr/zephyr.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/protocols/zephyr/zephyr.h Wed Jan 30 10:53:22 2008 +0000 @@ -163,9 +163,9 @@ Code_t ZReadAscii16 ZP((char *, int, unsigned short *)); Code_t ZSendPacket ZP((char*, int, int)); Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc)); -Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)(void))); +Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)())); Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc)); -Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)(void))); +Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)())); Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc)); Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc)); Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems,
--- a/libpurple/prpl.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/prpl.c Wed Jan 30 10:53:22 2008 +0000 @@ -29,6 +29,107 @@ #include "util.h" /**************************************************************************/ +/** @name Attention Type API */ +/**************************************************************************/ +PurpleAttentionType * +purple_attention_type_new(const char *ulname, const char *name, + const char *inc_desc, const char *out_desc) +{ + PurpleAttentionType *attn = g_new0(PurpleAttentionType, 1); + + purple_attention_type_set_name(attn, name); + purple_attention_type_set_incoming_desc(attn, inc_desc); + purple_attention_type_set_outgoing_desc(attn, out_desc); + purple_attention_type_set_unlocalized_name(attn, ulname); + + return attn; +} + + +void +purple_attention_type_set_name(PurpleAttentionType *type, const char *name) +{ + g_return_if_fail(type != NULL); + + type->name = name; +} + +void +purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc) +{ + g_return_if_fail(type != NULL); + + type->incoming_description = desc; +} + +void +purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc) +{ + g_return_if_fail(type != NULL); + + type->outgoing_description = desc; +} + +void +purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name) +{ + g_return_if_fail(type != NULL); + + type->icon_name = name; +} + +void +purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname) +{ + g_return_if_fail(type != NULL); + + type->unlocalized_name = ulname; +} + +const char * +purple_attention_type_get_name(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->name; +} + +const char * +purple_attention_type_get_incoming_desc(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->incoming_description; +} + +const char * +purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->outgoing_description; +} + +const char * +purple_attention_type_get_icon_name(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + if(type->icon_name == NULL || *(type->icon_name) == '\0') + return NULL; + + return type->icon_name; +} + +const char * +purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type) +{ + g_return_val_if_fail(type != NULL, NULL); + + return type->unlocalized_name; +} + +/**************************************************************************/ /** @name Protocol Plugin API */ /**************************************************************************/ void
--- a/libpurple/prpl.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/prpl.h Wed Jan 30 10:53:22 2008 +0000 @@ -99,9 +99,9 @@ const char *incoming_description; /**< Shown when sent */ const char *outgoing_description; /**< Shown when receied */ const char *icon_name; /**< Icon to display (optional) */ + const char *unlocalized_name; /**< Unlocalized name for UIs needing it */ /* Reserved fields for future purposes */ - gpointer _reserved1; gpointer _reserved2; gpointer _reserved3; gpointer _reserved4; @@ -412,6 +412,127 @@ #endif /**************************************************************************/ +/** @name Attention Type API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates a new #PurpleAttentionType object and sets its mandatory parameters. + * + * @param ulname A non-localized string that can be used by UIs in need of such + * non-localized strings. This should be the same as @a name, + * without localization. + * @param name A localized string that the UI may display for the event. This + * should be the same string as @a ulname, with localization. + * @param inc_desc A localized description shown when the event is received. + * @param out_desc A localized description shown when the event is sent. + * @return A pointer to the new object. + * @since 2.4.0 + */ +PurpleAttentionType *purple_attention_type_new(const char *ulname, const char *name, + const char *inc_desc, const char *out_desc); + +/** + * Sets the displayed name of the attention-demanding event. + * + * @param type The attention type. + * @param name The localized name that will be displayed by UIs. This should be + * the same string given as the unlocalized name, but with + * localization. + * @since 2.4.0 + */ +void purple_attention_type_set_name(PurpleAttentionType *type, const char *name); + +/** + * Sets the description of the attention-demanding event shown in conversations + * when the event is received. + * + * @param type The attention type. + * @param desc The localized description for incoming events. + * @since 2.4.0 + */ +void purple_attention_type_set_incoming_desc(PurpleAttentionType *type, const char *desc); + +/** + * Sets the description of the attention-demanding event shown in conversations + * when the event is sent. + * + * @param type The attention type. + * @param desc The localized description for outgoing events. + * @since 2.4.0 + */ +void purple_attention_type_set_outgoing_desc(PurpleAttentionType *type, const char *desc); + +/** + * Sets the name of the icon to display for the attention event; this is optional. + * + * @param type The attention type. + * @param name The icon's name. + * @note Icons are optional for attention events. + * @since 2.4.0 + */ +void purple_attention_type_set_icon_name(PurpleAttentionType *type, const char *name); + +/** + * Sets the unlocalized name of the attention event; some UIs may need this, + * thus it is required. + * + * @param type The attention type. + * @param ulname The unlocalized name. This should be the same string given as + * the localized name, but without localization. + * @since 2.4.0 + */ +void purple_attention_type_set_unlocalized_name(PurpleAttentionType *type, const char *ulname); + +/** + * Get the attention type's name as displayed by the UI. + * + * @param type The attention type. + * @return The name. + * @since 2.4.0 + */ +const char *purple_attention_type_get_name(const PurpleAttentionType *type); + +/** + * Get the attention type's description shown when the event is received. + * + * @param type The attention type. + * @return The description. + * @since 2.4.0 + */ +const char *purple_attention_type_get_incoming_desc(const PurpleAttentionType *type); + +/** + * Get the attention type's description shown when the event is sent. + * + * @param type The attention type. + * @return The description. + * @since 2.4.0 + */ +const char *purple_attention_type_get_outgoing_desc(const PurpleAttentionType *type); + +/** + * Get the attention type's icon name. + * + * @param type The attention type. + * @return The icon name or @c NULL if unset/empty. + * @note Icons are optional for attention events. + * @since 2.4.0 + */ +const char *purple_attention_type_get_icon_name(const PurpleAttentionType *type); + +/** + * Get the attention type's unlocalized name; this is useful for some UIs. + * + * @param type The attention type + * @return The unlocalized name. + * @since 2.4.0 + */ +const char *purple_attention_type_get_unlocalized_name(const PurpleAttentionType *type); + +/*@}*/ + +/**************************************************************************/ /** @name Protocol Plugin API */ /**************************************************************************/ /*@{*/
--- a/libpurple/request.h Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/request.h Wed Jan 30 10:53:22 2008 +0000 @@ -1318,6 +1318,8 @@ * @param secondary Secondary information, or @c NULL if there is none. * @param default_action The default action, zero-indexed; if the third action * supplied should be the default, supply <tt>2</tt>. + * The should be the action that users are most likely + * to select. * @param account The #PurpleAccount associated with this request, or @c * NULL if none is. * @param who The username of the buddy associated with this request, @@ -1356,6 +1358,8 @@ * @param secondary Secondary information, or @c NULL if there is none. * @param default_action The default action, zero-indexed; if the third action * supplied should be the default, supply <tt>2</tt>. + * The should be the action that users are most likely + * to select. * @param account The #PurpleAccount associated with this request, or @c * NULL if none is. * @param who The username of the buddy associated with this request,
--- a/libpurple/stringref.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/stringref.c Wed Jan 30 10:53:22 2008 +0000 @@ -31,6 +31,7 @@ #include <stdarg.h> #include "debug.h" +#include "eventloop.h" #include "stringref.h" /** @@ -87,7 +88,7 @@ newref->ref = 0x80000000; if (gclist == NULL) - g_idle_add(gs_idle_cb, NULL); + purple_timeout_add(0, gs_idle_cb, NULL); gclist = g_list_prepend(gclist, newref); return newref;
--- a/libpurple/tests/test_util.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/tests/test_util.c Wed Jan 30 10:53:22 2008 +0000 @@ -71,6 +71,14 @@ START_TEST(test_util_email_is_valid) { fail_unless(purple_email_is_valid("purple-devel@lists.sf.net")); + fail_if(purple_email_is_valid("purple-devel@@lists.sf.net")); + fail_if(purple_email_is_valid("purple@devel@lists.sf.net")); + fail_if(purple_email_is_valid("purple-devel@list..sf.net")); + fail_if(purple_email_is_valid("purple-devel")); + fail_if(purple_email_is_valid("purple-devel@")); + fail_if(purple_email_is_valid("@lists.sf.net")); + fail_if(purple_email_is_valid("")); + fail_if(purple_email_is_valid("totally bogus")); } END_TEST
--- a/libpurple/util.c Thu Jan 10 05:32:41 2008 +0000 +++ b/libpurple/util.c Wed Jan 30 10:53:22 2008 +0000 @@ -1447,7 +1447,6 @@ ALLOW_TAG("pre"); ALLOW_TAG("q"); ALLOW_TAG("span"); - ALLOW_TAG("strong"); ALLOW_TAG("ul"); @@ -1467,9 +1466,14 @@ plain = g_string_append_c(plain, '\n'); continue; } - if(!g_ascii_strncasecmp(c, "<b>", 3) || !g_ascii_strncasecmp(c, "<bold>", strlen("<bold>"))) { + if(!g_ascii_strncasecmp(c, "<b>", 3) || !g_ascii_strncasecmp(c, "<bold>", strlen("<bold>")) || !g_ascii_strncasecmp(c, "<strong>", strlen("<strong>"))) { struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1); - pt->src_tag = *(c+2) == '>' ? "b" : "bold"; + if (*(c+2) == '>') + pt->src_tag = "b"; + else if (*(c+2) == 'o') + pt->src_tag = "bold"; + else + pt->src_tag = "strong"; pt->dest_tag = "span"; tags = g_list_prepend(tags, pt); c = strchr(c, '>') + 1; @@ -4631,6 +4635,68 @@ #endif /* !_WIN32 */ } +static void +set_status_with_attrs(PurpleStatus *status, ...) +{ + va_list args; + va_start(args, status); + purple_status_set_active_with_attrs(status, TRUE, args); + va_end(args); +} + +void purple_util_set_current_song(const char *title, const char *artist, const char *album) +{ + GList *list = purple_accounts_get_all(); + for (; list; list = list->next) { + PurplePresence *presence; + PurpleStatus *tune; + PurpleAccount *account = list->data; + if (!purple_account_get_enabled(account, purple_core_get_ui())) + continue; + + presence = purple_account_get_presence(account); + tune = purple_presence_get_status(presence, "tune"); + if (!tune) + continue; + if (title) { + set_status_with_attrs(tune, + PURPLE_TUNE_TITLE, title, + PURPLE_TUNE_ARTIST, artist, + PURPLE_TUNE_ALBUM, album, + NULL); + } else { + purple_status_set_active(tune, FALSE); + } + } +} + +char * purple_util_format_song_info(const char *title, const char *artist, const char *album, gpointer unused) +{ + GString *string; + char *esc; + + if (!title || !*title) + return NULL; + + esc = g_markup_escape_text(title, -1); + string = g_string_new(""); + g_string_append_printf(string, "%s", esc); + g_free(esc); + + if (artist && *artist) { + esc = g_markup_escape_text(artist, -1); + g_string_append_printf(string, _(" - %s"), esc); + g_free(esc); + } + + if (album && *album) { + esc = g_markup_escape_text(album, -1); + g_string_append_printf(string, _(" (%s)"), esc); + g_free(esc); + } + + return g_string_free(string, FALSE); +} void botch_ucs(gchar *ucs, gssize len) { @@ -4861,57 +4927,3 @@ *newlen = bytes; return utf; } - -void purple_util_set_current_song(const char *title, const char *artist, const char *album) -{ - GList *list = purple_accounts_get_all(); - for (; list; list = list->next) { - PurplePresence *presence; - PurpleStatus *tune; - PurpleAccount *account = list->data; - if (!purple_account_get_enabled(account, purple_core_get_ui())) - continue; - - presence = purple_account_get_presence(account); - tune = purple_presence_get_status(presence, "tune"); - if (!tune) - continue; - if (title) { - purple_status_set_active(tune, TRUE); - purple_status_set_attr_string(tune, PURPLE_TUNE_TITLE, title); - purple_status_set_attr_string(tune, PURPLE_TUNE_ARTIST, artist); - purple_status_set_attr_string(tune, PURPLE_TUNE_ALBUM, album); - } else { - purple_status_set_active(tune, FALSE); - } - } -} - -char * purple_util_format_song_info(const char *title, const char *artist, const char *album, gpointer unused) -{ - GString *string; - char *esc; - - if (!title) - return NULL; - - esc = g_markup_escape_text(title, -1); - string = g_string_new(""); - g_string_append_printf(string, "%s", esc); - g_free(esc); - - if (artist) { - esc = g_markup_escape_text(artist, -1); - g_string_append_printf(string, _(" - %s"), esc); - g_free(esc); - } - - if (album) { - esc = g_markup_escape_text(album, -1); - g_string_append_printf(string, _(" (%s)"), esc); - g_free(esc); - } - - return g_string_free(string, FALSE); -} -
--- a/pidgin/gtkaccount.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkaccount.c Wed Jan 30 10:53:22 2008 +0000 @@ -2177,6 +2177,7 @@ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); dialog->treeview = treeview; gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + g_object_unref(G_OBJECT(dialog->model)); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
--- a/pidgin/gtkblist.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkblist.c Wed Jan 30 10:53:22 2008 +0000 @@ -937,7 +937,6 @@ pidgin_blist_joinchat_show(void) { GtkWidget *hbox, *vbox; - GtkWidget *rowbox; GtkWidget *label; PidginBuddyList *gtkblist; GtkWidget *img = NULL; @@ -981,7 +980,6 @@ data->account_menu = pidgin_account_option_menu_new(NULL, FALSE, G_CALLBACK(joinchat_select_account_cb), chat_account_filter_func, data); - gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Account:"), data->sg, data->account_menu, TRUE, NULL); @@ -4145,12 +4143,11 @@ static void pidgin_blist_new_list(PurpleBuddyList *blist) { PidginBuddyList *gtkblist; - PidginBuddyListPrivate *priv; gtkblist = g_new0(PidginBuddyList, 1); gtkblist->connection_errors = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); - gtkblist->priv = priv = g_new0(PidginBuddyListPrivate, 1); + gtkblist->priv = g_new0(PidginBuddyListPrivate, 1); blist->ui_data = gtkblist; } @@ -6071,6 +6068,7 @@ gtkblist->timeout = 0; gtkblist->drag_timeout = 0; gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; + g_object_unref(G_OBJECT(gtkblist->treemodel)); gtkblist->treemodel = NULL; g_object_unref(G_OBJECT(gtkblist->ift)); g_object_unref(G_OBJECT(gtkblist->empty_avatar));
--- a/pidgin/gtkcertmgr.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkcertmgr.c Wed Jan 30 10:53:22 2008 +0000 @@ -373,7 +373,7 @@ purple_request_yes_no(tpm_dat, _("Confirm certificate delete"), primary, NULL, /* Can this be NULL? */ - 2, /* NO is default action */ + 1, /* NO is default action */ NULL, NULL, NULL, id, /* id ownership passed to callback */ tls_peers_mgmt_delete_confirm_cb, @@ -422,6 +422,7 @@ tpm_dat->listview = listview = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store))); + g_object_unref(G_OBJECT(store)); { GtkCellRenderer *renderer;
--- a/pidgin/gtkconv.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkconv.c Wed Jan 30 10:53:22 2008 +0000 @@ -173,6 +173,7 @@ static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y, int width, int height); +static gboolean pidgin_conv_xy_to_right_infopane(PidginWindow *win, int x, int y); static GdkColor *get_nick_color(PidginConversation *gtkconv, const char *name) { static GdkColor col; @@ -494,6 +495,13 @@ cmdline = cmd + strlen(prefix); + if (strcmp(cmdline, "xyzzy") == 0) { + purple_conversation_write(conv, "", "Nothing happens", + PURPLE_MESSAGE_NO_LOG, time(NULL)); + g_free(cmd); + return TRUE; + } + gtk_text_iter_forward_chars(&start, g_utf8_strlen(prefix, -1)); gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end); markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end); @@ -3489,9 +3497,14 @@ gtk_text_buffer_delete_mark(buffer, stmark); gtk_text_buffer_delete_mark(buffer, enmark); gtk_text_buffer_delete(buffer, &start, &end); - } else if (message && *message == '\n' && !*(message + 1)) + } else if (message && *message == '\n' && message[1] == ' ' && message[2] == '\0') message = NULL; +#ifdef RESERVE_LINE + if (!message) + message = "\n "; /* The blank space is required to avoid a GTK+/Pango bug */ +#endif + if (message) { GtkTextIter iter; gtk_text_buffer_get_end_iter(buffer, &iter); @@ -3519,7 +3532,11 @@ return; if (purple_conv_im_get_typing_state(im) == PURPLE_NOT_TYPING) { - update_typing_message(gtkconv, "\n"); +#ifdef RESERVE_LINE + update_typing_message(gtkconv, NULL); +#else + update_typing_message(gtkconv, "\n "); +#endif return; } @@ -5070,7 +5087,7 @@ gtk_text_buffer_create_tag(GTK_IMHTML(gtkconv->imhtml)->text_buffer, "TYPING-NOTIFICATION", "foreground", "#888888", "justification", GTK_JUSTIFY_LEFT, /* XXX: RTL'ify */ - "weight", PANGO_WEIGHT_BOLD, + "weight", PANGO_WEIGHT_LIGHT, "scale", PANGO_SCALE_SMALL, NULL); @@ -6988,6 +7005,18 @@ gray_stuff_out(PIDGIN_CONVERSATION(conv)); } +static gboolean +pidgin_conv_xy_to_right_infopane(PidginWindow *win, int x, int y) +{ + gint pane_x, pane_y, x_rel; + PidginConversation *gtkconv; + + gdk_window_get_origin(win->notebook->window, &pane_x, &pane_y); + x_rel = x - pane_x; + gtkconv = pidgin_conv_window_get_active_gtkconv(win); + return (x_rel > gtkconv->infopane->allocation.x + gtkconv->infopane->allocation.width / 2); +} + int pidgin_conv_get_tab_at_xy(PidginWindow *win, int x, int y, gboolean *to_right) { @@ -7023,7 +7052,7 @@ tab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), page); /* Make sure the tab is not hidden beyond an arrow */ - if (!GTK_WIDGET_DRAWABLE(tab)) + if (!GTK_WIDGET_DRAWABLE(tab) && gtk_notebook_get_show_tabs(notebook)) continue; if (horiz) { @@ -8304,7 +8333,6 @@ GtkWidget *tab; gint page_num; gboolean horiz_tabs = FALSE; - PidginConversation *gtkconv; gboolean to_right = FALSE; /* Get the window that the cursor is over. */ @@ -8318,20 +8346,27 @@ dest_notebook = GTK_NOTEBOOK(dest_win->notebook); - page_num = pidgin_conv_get_tab_at_xy(dest_win, - e->x_root, e->y_root, &to_right); - to_right = to_right && (win != dest_win); + if (gtk_notebook_get_show_tabs(dest_notebook)) { + page_num = pidgin_conv_get_tab_at_xy(dest_win, + e->x_root, e->y_root, &to_right); + to_right = to_right && (win != dest_win); + tab = pidgin_conv_window_get_gtkconv_at_index(dest_win, page_num)->tabby; + } else { + page_num = 0; + to_right = pidgin_conv_xy_to_right_infopane(dest_win, e->x_root, e->y_root); + tab = pidgin_conv_window_get_gtkconv_at_index(dest_win, page_num)->infopane; + } if (gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_TOP || gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_BOTTOM) { horiz_tabs = TRUE; } - gtkconv = pidgin_conv_window_get_gtkconv_at_index(dest_win, page_num); - tab = gtkconv->tabby; - if (gtk_notebook_get_show_tabs(dest_notebook) == FALSE) { - dnd_hints_show_relative(HINT_ARROW_DOWN, gtkconv->infopane, HINT_POSITION_CENTER, HINT_POSITION_TOP); - dnd_hints_show_relative(HINT_ARROW_UP, gtkconv->infopane, HINT_POSITION_CENTER, HINT_POSITION_BOTTOM); + if (gtk_notebook_get_show_tabs(dest_notebook) == FALSE && win == dest_win) + { + /* dragging a tab from a single-tabbed window over its own window */ + dnd_hints_hide_all(); + return TRUE; } else if (horiz_tabs) { if (((gpointer)win == (gpointer)dest_win && win->drag_tab < page_num) || to_right) { dnd_hints_show_relative(HINT_ARROW_DOWN, tab, HINT_POSITION_RIGHT, HINT_POSITION_TOP); @@ -8528,6 +8563,7 @@ notebook_release_cb(GtkWidget *widget, GdkEventButton *e, PidginWindow *win) { PidginWindow *dest_win; + GtkNotebook *dest_notebook; PurpleConversation *conv; PidginConversation *gtkconv; gint dest_page_num = 0; @@ -8603,9 +8639,16 @@ "conversation-dragging", win, dest_win); /* Get the destination page number. */ - if (!new_window) - dest_page_num = pidgin_conv_get_tab_at_xy(dest_win, - e->x_root, e->y_root, &to_right); + if (!new_window) { + dest_notebook = GTK_NOTEBOOK(dest_win->notebook); + if (gtk_notebook_get_show_tabs(dest_notebook)) { + dest_page_num = pidgin_conv_get_tab_at_xy(dest_win, + e->x_root, e->y_root, &to_right); + } else { + dest_page_num = 0; + to_right = pidgin_conv_xy_to_right_infopane(dest_win, e->x_root, e->y_root); + } + } gtkconv = pidgin_conv_window_get_gtkconv_at_index(win, win->drag_tab);
--- a/pidgin/gtkimhtml.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkimhtml.c Wed Jan 30 10:53:22 2008 +0000 @@ -1463,6 +1463,9 @@ gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL); +#if GTK_CHECK_VERSION(2,10,10) + gtk_text_buffer_create_tag(imhtml->text_buffer, "comment", "invisible", FALSE, NULL); +#endif /* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */ imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2); @@ -2989,10 +2992,15 @@ gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); +#if GTK_CHECK_VERSION(2,10,10) + wpos = g_snprintf (ws, len, "%s", tag); + gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL); +#else if (imhtml->show_comments && !(options & GTK_IMHTML_NO_COMMENTS)) { wpos = g_snprintf (ws, len, "%s", tag); gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); } +#endif ws[0] = '\0'; wpos = 0; /* NEW_BIT (NEW_COMMENT_BIT); */ @@ -3138,6 +3146,12 @@ void gtk_imhtml_show_comments (GtkIMHtml *imhtml, gboolean show) { +#if GTK_CHECK_VERSION(2,10,10) + GtkTextTag *tag; + tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "comment"); + if (tag) + g_object_set(G_OBJECT(tag), "invisible", !show, NULL); +#endif imhtml->show_comments = show; }
--- a/pidgin/gtkimhtmltoolbar.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Wed Jan 30 10:53:22 2008 +0000 @@ -1312,6 +1312,7 @@ gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0); g_object_set_data(G_OBJECT(hbox), "lean-view", box); + gtk_widget_show(box); purple_prefs_connect_callback(toolbar, PIDGIN_PREFS_ROOT "/conversations/toolbar/wide", imhtmltoolbar_view_pref_changed, toolbar);
--- a/pidgin/gtklog.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtklog.c Wed Jan 30 10:53:22 2008 +0000 @@ -139,7 +139,7 @@ { /* Searching for the same term acts as "Find Next" */ gtk_imhtml_search_find(GTK_IMHTML(lv->imhtml), lv->search); - return; + return; } pidgin_set_cursor(lv->window, GDK_WATCH); @@ -321,7 +321,7 @@ data2[0] = lv->treestore; data2[1] = data[3]; /* iter */ data2[2] = log; - purple_request_action(lv, NULL, "Delete Log?", tmp, 0, + purple_request_action(lv, NULL, "Delete Log?", tmp, 0, NULL, NULL, NULL, data2, 2, _("Delete"), delete_log_cb, @@ -556,8 +556,13 @@ if (!purple_prefs_get_bool("/purple/logging/log_chats")) log_preferences = _("Chats will only be logged if the \"Log all chats\" preference is enabled."); } + g_free(ht->screenname); + g_free(ht); } + if(icon != NULL) + gtk_widget_destroy(icon); + purple_notify_info(NULL, title, _("No logs were found"), log_preferences); return NULL; } @@ -614,6 +619,7 @@ gtk_paned_add1(GTK_PANED(pane), sw); lv->treestore = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_POINTER); lv->treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (lv->treestore)); + g_object_unref(G_OBJECT(lv->treestore)); rend = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new_with_attributes ("time", rend, "markup", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW(lv->treeview), col); @@ -730,18 +736,19 @@ } void pidgin_log_show_contact(PurpleContact *contact) { - struct log_viewer_hash_t *ht = g_new0(struct log_viewer_hash_t, 1); + struct log_viewer_hash_t *ht; PurpleBlistNode *child; PidginLogViewer *lv = NULL; GList *logs = NULL; GdkPixbuf *pixbuf; - GtkWidget *image = gtk_image_new(); + GtkWidget *image; const char *name = NULL; char *title; int total_log_size = 0; g_return_if_fail(contact != NULL); + ht = g_new0(struct log_viewer_hash_t, 1); ht->type = PURPLE_LOG_IM; ht->contact = contact; @@ -763,9 +770,16 @@ } logs = g_list_sort(logs, purple_log_compare); + image = gtk_image_new(); pixbuf = gtk_widget_render_icon(image, PIDGIN_STOCK_STATUS_PERSON, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL), "GtkWindow"); - gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf); + if (pixbuf) { + gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf); + g_object_unref(pixbuf); + } else { + gtk_widget_destroy(image); + image = NULL; + } if (contact->alias != NULL) name = contact->alias;
--- a/pidgin/gtkmain.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkmain.c Wed Jan 30 10:53:22 2008 +0000 @@ -510,6 +510,7 @@ {"session", required_argument, NULL, 's'}, {"version", no_argument, NULL, 'v'}, {"display", required_argument, NULL, 'D'}, + {"sync", no_argument, NULL, 'S'}, {0, 0, 0, 0} }; @@ -519,7 +520,7 @@ debug_enabled = FALSE; #endif - /* This is the first Glib function call. Make sure to initialize GThread bfeore then */ + /* Initialize GThread before calling any Glib or GTK+ functions. */ g_thread_init(NULL); #ifdef ENABLE_NLS @@ -656,6 +657,7 @@ opt_si = FALSE; break; case 'D': /* --display */ + case 'S': /* --sync */ /* handled by gtk_init_check below */ break; case '?': /* show terse help */
--- a/pidgin/gtknotify.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtknotify.c Wed Jan 30 10:53:22 2008 +0000 @@ -379,6 +379,7 @@ mail_dialog->treemodel = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); mail_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(mail_dialog->treemodel)); + g_object_unref(G_OBJECT(mail_dialog->treemodel)); gtk_tree_view_set_search_column(GTK_TREE_VIEW(mail_dialog->treeview), PIDGIN_MAIL_TEXT); gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(mail_dialog->treeview), pidgin_tree_view_search_equal_func, NULL, NULL); @@ -818,6 +819,7 @@ /* Setup the treeview */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + g_object_unref(G_OBJECT(model)); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); gtk_widget_set_size_request(treeview, 500, 400); gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), @@ -1056,7 +1058,12 @@ /* if they are running gnome, use the gnome web browser */ if (purple_running_gnome() == TRUE) { - command = g_strdup_printf("gnome-open %s", escaped); + char *tmp = g_find_program_in_path("xdg-open"); + if (tmp == NULL) + command = g_strdup_printf("gnome-open %s", escaped); + else + command = g_strdup_printf("xdg-open %s", escaped); + g_free(tmp); } else if (purple_running_osx() == TRUE) { @@ -1072,6 +1079,10 @@ else command = g_strdup_printf("%s %s", web_browser, escaped); } + else if (!strcmp(web_browser, "xdg-open")) + { + command = g_strdup_printf("xdg-open %s", escaped); + } else if (!strcmp(web_browser, "gnome-open")) { command = g_strdup_printf("gnome-open %s", escaped);
--- a/pidgin/gtkpounce.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkpounce.c Wed Jan 30 10:53:22 2008 +0000 @@ -1237,6 +1237,7 @@ /* Create the treeview */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model)); + g_object_unref(G_OBJECT(dialog->model)); dialog->treeview = treeview; gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
--- a/pidgin/gtkprefs.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkprefs.c Wed Jan 30 10:53:22 2008 +0000 @@ -478,12 +478,20 @@ { FILE *f; gchar *path; + size_t wc; if ((error_message != NULL) || (len == 0)) return; f = purple_mkstemp(&path, TRUE); - fwrite(themedata, len, 1, f); + wc = fwrite(themedata, len, 1, f); + if (wc != 1) { + purple_debug_warning("theme_got_url", "Unable to write theme data.\n"); + fclose(f); + g_unlink(path); + g_free(path); + return; + } fclose(f); theme_install_theme(path, user_data); @@ -1364,6 +1372,7 @@ {N_("Netscape"), "netscape"}, {N_("Mozilla"), "mozilla"}, {N_("Konqueror"), "kfmclient"}, + {N_("Desktop Default"), "xdg-open"}, {N_("GNOME Default"), "gnome-open"}, {N_("Galeon"), "galeon"}, {N_("Firefox"), "firefox"}, @@ -1386,6 +1395,14 @@ browsers = g_list_prepend(browsers, (gpointer)_(possible_browsers[i].name)); if(browser_setting && !strcmp(possible_browsers[i].command, browser_setting)) browser_setting = NULL; + /* If xdg-open is valid, prefer it over gnome-open and skip forward */ + if(!strcmp(possible_browsers[i].command, "xdg-open")) { + if (browser_setting && !strcmp("gnome-open", browser_setting)) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/browsers/browser", possible_browsers[i].command); + browser_setting = NULL; + } + i++; + } } } @@ -1460,15 +1477,15 @@ } entry = gtk_entry_new(); - if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"), "custom")) - gtk_widget_set_sensitive(hbox, FALSE); - purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/browsers/browser", - browser_changed2_cb, hbox); gtk_entry_set_text(GTK_ENTRY(entry), purple_prefs_get_path(PIDGIN_PREFS_ROOT "/browsers/command")); g_signal_connect(G_OBJECT(entry), "focus-out-event", G_CALLBACK(manual_browser_set), NULL); - pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Manual:\n(%s for URL)"), sg, entry, TRUE, NULL); + hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Manual:\n(%s for URL)"), sg, entry, TRUE, NULL); + if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/browsers/browser"), "custom")) + gtk_widget_set_sensitive(hbox, FALSE); + purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/browsers/browser", + browser_changed2_cb, hbox); gtk_widget_show_all(ret); g_object_unref(sg); @@ -1721,9 +1738,6 @@ int j; const char *file; char *pref; -#if !defined _WIN32 || defined USE_GSTREAMER - GtkWidget *label; -#endif #ifndef _WIN32 GtkWidget *dd; GtkWidget *entry;
--- a/pidgin/gtkprivacy.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkprivacy.c Wed Jan 30 10:53:22 2008 +0000 @@ -474,6 +474,8 @@ if (privacy_dialog == NULL) return; + g_object_unref(G_OBJECT(privacy_dialog->allow_store)); + g_object_unref(G_OBJECT(privacy_dialog->block_store)); g_free(privacy_dialog); privacy_dialog = NULL; }
--- a/pidgin/gtkrequest.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkrequest.c Wed Jan 30 10:53:22 2008 +0000 @@ -663,7 +663,11 @@ gtk_widget_grab_focus(img); gtk_widget_grab_default(img); } else - gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_action); + /* + * Need to invert the default_action number because the + * buttons are added to the dialog in reverse order. + */ + gtk_dialog_set_default_response(GTK_DIALOG(dialog), action_count - 1 - default_action); /* Show everything. */ pidgin_auto_parent_window(dialog); @@ -1007,6 +1011,7 @@ /* Create the tree view */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
--- a/pidgin/gtksavedstatuses.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtksavedstatuses.c Wed Jan 30 10:53:22 2008 +0000 @@ -669,6 +669,7 @@ purple_request_close_with_handle(status_window); purple_notify_close_with_handle(status_window); purple_signals_disconnect_by_handle(status_window); + g_object_unref(G_OBJECT(status_window->model)); g_free(status_window); status_window = NULL; } @@ -723,6 +724,7 @@ status_editor_remove_dialog(dialog); g_free(dialog->original_title); + g_object_unref(G_OBJECT(dialog->model)); g_free(dialog); return FALSE;
--- a/pidgin/gtkstatusbox.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkstatusbox.c Wed Jan 30 10:53:22 2008 +0000 @@ -320,12 +320,20 @@ { FILE *f; gchar *path; + size_t wc; if ((error_message != NULL) || (len == 0)) return; f = purple_mkstemp(&path, TRUE); - fwrite(themedata, len, 1, f); + wc = fwrite(themedata, len, 1, f); + if (wc != 1) { + purple_debug_warning("theme_got_url", "Unable to write theme data.\n"); + fclose(f); + g_unlink(path); + g_free(path); + return; + } fclose(f); icon_choose_cb(path, user_data); @@ -503,20 +511,28 @@ pidgin_status_box_finalize(GObject *obj) { PidginStatusBox *statusbox = PIDGIN_STATUS_BOX(obj); + int i; purple_signals_disconnect_by_handle(statusbox); purple_prefs_disconnect_by_handle(statusbox); - gdk_cursor_unref(statusbox->hand_cursor); - gdk_cursor_unref(statusbox->arrow_cursor); + destroy_icon_box(statusbox); + + if (statusbox->active_row) + gtk_tree_row_reference_free(statusbox->active_row); - purple_imgstore_unref(statusbox->buddy_icon_img); - g_object_unref(G_OBJECT(statusbox->buddy_icon)); - g_object_unref(G_OBJECT(statusbox->buddy_icon_hover)); + for (i = 0; i < G_N_ELEMENTS(statusbox->connecting_pixbufs); i++) { + if (statusbox->connecting_pixbufs[i] != NULL) + gdk_pixbuf_unref(statusbox->connecting_pixbufs[i]); + } - if (statusbox->buddy_icon_sel) - gtk_widget_destroy(statusbox->buddy_icon_sel); + 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->store)); + g_object_unref(G_OBJECT(statusbox->dropdown_store)); G_OBJECT_CLASS(parent_class)->finalize(obj); } @@ -1164,18 +1180,15 @@ cache_pixbufs(PidginStatusBox *status_box) { GtkIconSize icon_size; + int i; g_object_set(G_OBJECT(status_box->icon_rend), "xpad", 3, NULL); icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); - if (status_box->connecting_pixbufs[0] != NULL) - gdk_pixbuf_unref(status_box->connecting_pixbufs[0]); - if (status_box->connecting_pixbufs[1] != NULL) - gdk_pixbuf_unref(status_box->connecting_pixbufs[1]); - if (status_box->connecting_pixbufs[2] != NULL) - gdk_pixbuf_unref(status_box->connecting_pixbufs[2]); - if (status_box->connecting_pixbufs[3] != NULL) - gdk_pixbuf_unref(status_box->connecting_pixbufs[3]); + 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]); + } status_box->connecting_index = 0; status_box->connecting_pixbufs[0] = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_ANIMATION_CONNECT0, @@ -1197,14 +1210,10 @@ status_box->connecting_pixbufs[8] = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_ANIMATION_CONNECT8, icon_size, "PidginStatusBox"); - if (status_box->typing_pixbufs[0] != NULL) - gdk_pixbuf_unref(status_box->typing_pixbufs[0]); - if (status_box->typing_pixbufs[1] != NULL) - gdk_pixbuf_unref(status_box->typing_pixbufs[1]); - if (status_box->typing_pixbufs[2] != NULL) - gdk_pixbuf_unref(status_box->typing_pixbufs[2]); - if (status_box->typing_pixbufs[3] != NULL) - gdk_pixbuf_unref(status_box->typing_pixbufs[3]); + 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]); + } status_box->typing_index = 0; status_box->typing_pixbufs[0] = gtk_widget_render_icon (GTK_WIDGET(status_box->vbox), PIDGIN_STOCK_ANIMATION_TYPING0,
--- a/pidgin/gtkutils.c Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkutils.c Wed Jan 30 10:53:22 2008 +0000 @@ -1595,7 +1595,7 @@ else if (!(im || ft)) purple_request_yes_no(NULL, NULL, _("You have dragged an image"), _("Would you like to set it as the buddy icon for this user?"), - 0, + PURPLE_DEFAULT_ACTION_NONE, account, who, NULL, data, (GCallback)dnd_set_icon_ok_cb, (GCallback)dnd_set_icon_cancel_cb); else
--- a/pidgin/gtkutils.h Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/gtkutils.h Wed Jan 30 10:53:22 2008 +0000 @@ -798,11 +798,11 @@ * Add a labelled widget to a GtkVBox * * @param vbox The GtkVBox to add the widget to. - * @param widget_label The label to give the widget. - * @param sg The GtkSizeGroup to add the label to. - * @param widget The GtkWidget to add + * @param widget_label The label to give the widget, can be @c NULL. + * @param sg The GtkSizeGroup to add the label to, can be @c NULL. + * @param widget The GtkWidget to add. * @param expand Whether to expand the widget horizontally. - * @param p_label Place to store a pointer to the GtkLabel, or NULL if you don't care. + * @param p_label Place to store a pointer to the GtkLabel, or @c NULL if you don't care. * * @return A GtkHBox already added to the GtkVBox containing the GtkLabel and the GtkWidget. * @since 2.4.0
--- a/pidgin/win32/nsis/pidgin-installer.nsi Thu Jan 10 05:32:41 2008 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Jan 30 10:53:22 2008 +0000 @@ -1167,6 +1167,10 @@ MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION $(PIDGIN_IS_RUNNING) /SD IDCANCEL IDRETRY retry_runcheck Abort + ; Close the Handle (If we don't do this, the uninstaller called from within will fail) + ; This is not optimal because there is a (small) window of time when a new process could start + System::Call 'kernel32::CloseHandle(i $R1) i .R1' + Pop $R1 Pop $R0 FunctionEnd
--- a/po/POTFILES.in Thu Jan 10 05:32:41 2008 +0000 +++ b/po/POTFILES.in Wed Jan 30 10:53:22 2008 +0000 @@ -33,6 +33,7 @@ finch/plugins/gntclipboard.c finch/plugins/gntgf.c finch/plugins/gnthistory.c +finch/plugins/grouping.c finch/plugins/lastlog.c libpurple/account.c libpurple/blist.c @@ -72,6 +73,7 @@ libpurple/protocols/bonjour/bonjour.c libpurple/protocols/bonjour/bonjour.h libpurple/protocols/bonjour/jabber.c +libpurple/protocols/bonjour/mdns_win32.c libpurple/protocols/gg/gg.c libpurple/protocols/irc/cmds.c libpurple/protocols/irc/dcc_send.c @@ -213,6 +215,7 @@ pidgin/pidgin.h pidgin/pidgincombobox.c pidgin/pidginstock.c +pidgin/pidgintooltip.c pidgin/pixmaps/emotes/default/24/default.theme.in pidgin/pixmaps/emotes/none/none.theme.in pidgin/plugins/cap/cap.c
--- a/po/de.po Thu Jan 10 05:32:41 2008 +0000 +++ b/po/de.po Wed Jan 30 10:53:22 2008 +0000 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-01-08 11:16+0100\n" -"PO-Revision-Date: 2008-01-08 11:15+0100\n" +"POT-Creation-Date: 2008-01-23 10:20+0100\n" +"PO-Revision-Date: 2008-01-23 10:19+0100\n" "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n" "Language-Team: Deutsch <de@li.org>\n" "MIME-Version: 1.0\n" @@ -155,6 +155,29 @@ msgid "Deny" msgstr "Ablehnen" +#, c-format +msgid "" +"Online: %d\n" +"Total: %d" +msgstr "" +"Online: %d\n" +"Gesamt: %d" + +#, c-format +msgid "Account: %s (%s)" +msgstr "Konto: %s (%s)" + +#, c-format +msgid "" +"\n" +"Last Seen: %s ago" +msgstr "" +"\n" +"Zuletzt gesehen: vor %s" + +msgid "Default" +msgstr "Standard" + msgid "You must provide a screename for the buddy." msgstr "Sie müssen einen Benutzernamen für den Buddy angeben." @@ -303,26 +326,6 @@ msgid "On Mobile" msgstr "Am Handy" -#, c-format -msgid "" -"Online: %d\n" -"Total: %d" -msgstr "" -"Online: %d\n" -"Gesamt: %d" - -#, c-format -msgid "Account: %s (%s)" -msgstr "Konto: %s (%s)" - -#, c-format -msgid "" -"\n" -"Last Seen: %s ago" -msgstr "" -"\n" -"Zuletzt gesehen: vor %s" - msgid "New..." msgstr "Neu..." @@ -389,6 +392,15 @@ msgid "By Log Size" msgstr "Nach Größe der Logs" +msgid "Buddy" +msgstr "Buddy" + +msgid "Chat" +msgstr "Chat" + +msgid "Grouping" +msgstr "Gruppierung" + msgid "Certificate Import" msgstr "Zertifikat-Import" @@ -1321,6 +1333,27 @@ "Wenn eine neue Unterhaltung eröffnet wird, fügt dieses Plugin die letzte " "Unterhaltung in die aktuelle Unterhaltung ein." +msgid "Online" +msgstr "Online" + +msgid "Offline" +msgstr "Offline" + +msgid "Online Buddies" +msgstr "Online-Buddys" + +msgid "Offline Buddies" +msgstr "Offline-Buddys" + +msgid "Online/Offline" +msgstr "Online/Offline" + +msgid "No Grouping" +msgstr "Keine Gruppierung" + +msgid "Provides alternate buddylist grouping options." +msgstr "Bietet alternative Einstellungen für die Kontaktlisten-Gruppierung." + msgid "Lastlog" msgstr "Verlauf" @@ -2688,6 +2721,9 @@ msgid "Could not listen on socket" msgstr "Kann nicht an der Socket hören" +msgid "Error communicating with local mDNSResponder." +msgstr "Fehler bei der Kommunikation mit lokalem mDNSResponder." + msgid "Invalid proxy settings" msgstr "Falsche Proxy-Einstellungen" @@ -2819,9 +2855,6 @@ msgid "Add to chat..." msgstr "Zum Chat hinzufügen..." -msgid "Offline" -msgstr "Offline" - msgid "Available" msgstr "Verfügbar" @@ -4237,6 +4270,8 @@ msgid "Unable to buzz, because the user %s does not support it." msgstr "Kann nicht anklopfen, da der Benutzer %s dies nicht unterstützt." +#. Yahoo only supports one attention command: the 'buzz'. +#. This is index number YAHOO_BUZZ. msgid "Buzz" msgstr "Anklopfen" @@ -5971,9 +6006,6 @@ msgid "AIM Direct IM" msgstr "AIM direkte Nachricht" -msgid "Chat" -msgstr "Chat" - msgid "Get File" msgstr "Datei abrufen" @@ -6043,9 +6075,6 @@ msgid "Invisible" msgstr "Unsichtbar" -msgid "Online" -msgstr "Online" - msgid "IP Address" msgstr "IP-Adresse" @@ -9151,6 +9180,10 @@ msgid "Unable to establish file descriptor." msgstr "Konnte Dateibeschreibung nicht erstellen." +#, c-format +msgid "%s is trying to send you a group of %d files.\n" +msgstr "%s versucht, Ihnen eine Gruppe von %d Dateien zu senden.\n" + msgid "Write Error" msgstr "Schreibfehler" @@ -10072,9 +10105,6 @@ msgid "Total Buddies" msgstr "Buddy-Anzahl" -msgid "Online Buddies" -msgstr "Online-Buddys" - #, c-format msgid "Idle %dd %dh %02dm" msgstr "Untätig %dd %dh %02dm" @@ -11682,9 +11712,6 @@ msgid "Pounce Target" msgstr "Alarm-Ziel" -msgid "Default" -msgstr "Standard" - msgid "Smiley theme failed to unpack." msgstr "Smiley-Thema konnte nicht entpackt werden." @@ -12295,6 +12322,9 @@ msgid "_Open Mail" msgstr "Mail ö_ffnen" +msgid "Pidgin Tooltip" +msgstr "Pidgin-Tooltip" + msgid "Pidgin smileys" msgstr "Pidgin-Smileys"
--- a/po/en_GB.po Thu Jan 10 05:32:41 2008 +0000 +++ b/po/en_GB.po Wed Jan 30 10:53:22 2008 +0000 @@ -3315,7 +3315,7 @@ #: ../libpurple/plugins/psychic.c:72 msgid "You feel a disturbance in the force..." -msgstr "You have a disturbance in the force..." +msgstr "You feel a disturbance in the force..." #: ../libpurple/plugins/psychic.c:91 msgid "Only enable for users on the buddy list"