# HG changeset patch # User Richard Laager # Date 1191013128 0 # Node ID 045e4f2459a9172043f40d08009aec986a4c21b0 # Parent 5c42c7c7780d0458452bd3f9ea49d2de3a972093# Parent 6dbe19208fd320c24682356f5f1fdfe1ebb4fe4e propagate from branch 'im.pidgin.pidgin.2.2.1' (head 179c071e5cda5fe178066a9251e7b473ee8b0b10) to branch 'im.pidgin.pidgin' (head 33b755b1de38ac34dd0c9fa596a3f78bf816ca66) diff -r 5c42c7c7780d -r 045e4f2459a9 COPYRIGHT --- a/COPYRIGHT Fri Sep 28 20:58:27 2007 +0000 +++ b/COPYRIGHT Fri Sep 28 20:58:48 2007 +0000 @@ -246,6 +246,7 @@ Kevin Miller Paul Miller Arkadiusz Miskiewicz +David Mohr Andrew Molloy Michael Monreal Benjamin Moody @@ -278,6 +279,7 @@ Eduardo Pérez Matt Perry Nathan Peterson +Sebastián E. Peyrott Celso Pinto Joao Luís Marques Pinto Aleksander Piotrowski diff -r 5c42c7c7780d -r 045e4f2459a9 ChangeLog --- a/ChangeLog Fri Sep 28 20:58:27 2007 +0000 +++ b/ChangeLog Fri Sep 28 20:58:48 2007 +0000 @@ -1,5 +1,37 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +Version 2.2.2: + http://developer.pidgin.im/query?status=closed&milestone=2.2.2 + NOTE: Due to the backporting that happened for the 2.2.1 + release, it is possible bugs this release fixes bugs + that were marked as fixed in 2.2.1. + + libpurple: + * Real usernames are now shown in the system log. + + Pidgin: + * If you alias a buddy to an alias that is already present within + a particular group, we now offer to merge the buddies into the + same contact. + * A music emblem is now displayed in the buddy list for a buddy if we + know she is listening to some soothing music. + * Added a 'Move to' menu in buddy list context menu for moving buddies + to other groups as an alternative to dragging. + * Group headings are now marked via an underline instead of a + different color background. + * It is now possible to mark a chat on your buddy list as "Persistent" + so you do not leave the chat when the window or tab is closed. + * The auto-join option for chats is now listed in the "Add Chat" + dialog along with the new persistence option. + * Closing an IM no longer immediately closes your conversation. It + will now remain active for a short time so that if the conversation + resumes, the history will be retained. A preference has been added + to toggle this behavior. + * The "Smiley" menu has been moved to the top-level of the toolbar. + * Pidgin's display is now saved with the command line for session + restoration. (David Mohr) + * ICQ Birthday notifications are shown as buddy list emblems + Version 2.2.1: http://developer.pidgin.im/query?status=closed&milestone=2.2.1 NOTE: Due to the backporting that happened for the actual @@ -2264,4 +2296,4 @@ * Fixed WindowMaker Appicon * Version Number in About Box * Gaim Slogan in about box :) - * Created Changelog File :) + * Created Changelog File :) \ No newline at end of file diff -r 5c42c7c7780d -r 045e4f2459a9 configure.ac --- a/configure.ac Fri Sep 28 20:58:27 2007 +0000 +++ b/configure.ac Fri Sep 28 20:58:48 2007 +0000 @@ -46,7 +46,7 @@ m4_define([purple_lt_current], [2]) m4_define([purple_major_version], [2]) m4_define([purple_minor_version], [2]) -m4_define([purple_micro_version], [1]) +m4_define([purple_micro_version], [2]) m4_define([purple_version_suffix], []) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) @@ -55,7 +55,7 @@ m4_define([gnt_lt_current], [2]) m4_define([gnt_major_version], [2]) m4_define([gnt_minor_version], [2]) -m4_define([gnt_micro_version], [1]) +m4_define([gnt_micro_version], [2]) m4_define([gnt_version_suffix], []) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version]) diff -r 5c42c7c7780d -r 045e4f2459a9 doc/funniest_home_convos.txt --- a/doc/funniest_home_convos.txt Fri Sep 28 20:58:27 2007 +0000 +++ b/doc/funniest_home_convos.txt Fri Sep 28 20:58:48 2007 +0000 @@ -487,3 +487,21 @@ established that 19:03 its functioning just fine +-- + +17:12 I think I foundage a bug +17:13 I don't think blocking works correctly for the AIM protocol +17:13 because a girl attempted to block me in pidgin and I can still + talk to her +17:14 [screen name expunged]: I already told you yesterday. You don't + need me to ease your mind, you want me to change MY mind. Well, + you're out of luck. I don't know how you're still messaging me, + because I already blocked you, but I demand you stop immediately. + For now on, I will save every message that you send me as + evidence for the next time I decide to call the police. I did + call them yesterday, but I didn't have evidence. The more you + message me now, the more you're digging you're own grave, so I + advice you to stop. +17:14 see? +17:16 I think blocking in pidgin not working is not your biggest + problem here. diff -r 5c42c7c7780d -r 045e4f2459a9 finch/gntblist.c --- a/finch/gntblist.c Fri Sep 28 20:58:27 2007 +0000 +++ b/finch/gntblist.c Fri Sep 28 20:58:48 2007 +0000 @@ -1510,6 +1510,8 @@ } else if (strcmp(text, GNT_KEY_CTRL_O) == 0) { purple_prefs_set_bool(PREF_ROOT "/showoffline", !purple_prefs_get_bool(PREF_ROOT "/showoffline")); + } else if (strcmp(text, GNT_KEY_INS) == 0) { + purple_blist_request_add_buddy(NULL, NULL, NULL, NULL); } else if (!gnt_tree_is_searching(GNT_TREE(ggblist->tree))) { if (strcmp(text, "t") == 0) { finch_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree))); @@ -2225,9 +2227,27 @@ } static void +menu_add_buddy_cb(GntMenuItem *item, gpointer null) +{ + purple_blist_request_add_buddy(NULL, NULL, NULL, NULL); +} + +static void +menu_add_chat_cb(GntMenuItem *item, gpointer null) +{ + purple_blist_request_add_chat(NULL, NULL, NULL, NULL); +} + +static void +menu_add_group_cb(GntMenuItem *item, gpointer null) +{ + purple_blist_request_add_group(); +} + +static void create_menu() { - GntWidget *menu, *sub; + GntWidget *menu, *sub, *subsub; GntMenuItem *item; GntWindow *window; @@ -2248,29 +2268,57 @@ gnt_menu_add_item(GNT_MENU(sub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), send_im_select, NULL); - item = gnt_menuitem_check_new(_("Show empty groups")); + item = gnt_menuitem_new(_("Show")); + gnt_menu_add_item(GNT_MENU(sub), item); + subsub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); + + item = gnt_menuitem_check_new(_("Empty groups")); gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), purple_prefs_get_bool(PREF_ROOT "/emptygroups")); - gnt_menu_add_item(GNT_MENU(sub), item); + 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(_("Show offline buddies")); + item = gnt_menuitem_check_new(_("Offline buddies")); gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), purple_prefs_get_bool(PREF_ROOT "/showoffline")); - gnt_menu_add_item(GNT_MENU(sub), item); + gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/showoffline"); - item = gnt_menuitem_new(_("Sort by status")); + item = gnt_menuitem_new(_("Sort")); gnt_menu_add_item(GNT_MENU(sub), item); + subsub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); + + item = gnt_menuitem_new(_("By Status")); + gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "status"); - item = gnt_menuitem_new(_("Sort alphabetically")); - gnt_menu_add_item(GNT_MENU(sub), item); + item = gnt_menuitem_new(_("Alphabetically")); + gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "text"); - item = gnt_menuitem_new(_("Sort by log size")); + item = gnt_menuitem_new(_("By Log Size")); + gnt_menu_add_item(GNT_MENU(subsub), item); + gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "log"); + + item = gnt_menuitem_new(_("Add")); gnt_menu_add_item(GNT_MENU(sub), item); - gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "log"); + + subsub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); + + item = gnt_menuitem_new("Buddy"); + gnt_menu_add_item(GNT_MENU(subsub), item); + gnt_menuitem_set_callback(item, menu_add_buddy_cb, NULL); + + item = gnt_menuitem_new("Chat"); + gnt_menu_add_item(GNT_MENU(subsub), item); + gnt_menuitem_set_callback(item, menu_add_chat_cb, NULL); + + item = gnt_menuitem_new("Group"); + gnt_menu_add_item(GNT_MENU(subsub), item); + gnt_menuitem_set_callback(item, menu_add_group_cb, NULL); reconstruct_accounts_menu(); gnt_menu_add_item(GNT_MENU(menu), ggblist->accounts); diff -r 5c42c7c7780d -r 045e4f2459a9 finch/gntconn.c --- a/finch/gntconn.c Fri Sep 28 20:58:27 2007 +0000 +++ b/finch/gntconn.c Fri Sep 28 20:58:48 2007 +0000 @@ -105,10 +105,11 @@ { FinchAutoRecon *info; PurpleAccount *account = purple_connection_get_account(gc); - - info = g_hash_table_lookup(hash, account); + GList *list; if (!gc->wants_to_die) { + info = g_hash_table_lookup(hash, account); + if (info == NULL) { info = g_new0(FinchAutoRecon, 1); g_hash_table_insert(hash, account, info); @@ -141,6 +142,17 @@ g_free(secondary); purple_account_set_enabled(account, FINCH_UI, FALSE); } + + /* If we have any open chats, we probably want to rejoin when we get back online. */ + list = purple_get_chats(); + while (list) { + PurpleConversation *conv = list->data; + list = list->next; + if (conv->account != account || + purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) + continue; + purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE)); + } } static void diff -r 5c42c7c7780d -r 045e4f2459a9 finch/gntconv.c --- a/finch/gntconv.c Fri Sep 28 20:58:27 2007 +0000 +++ b/finch/gntconv.c Fri Sep 28 20:58:48 2007 +0000 @@ -311,13 +311,40 @@ static void account_signed_on_off(PurpleConnection *gc, gpointer null) { - GList *ims = purple_get_ims(); - while (ims) { - PurpleConversation *conv = ims->data; + GList *list = purple_get_ims(); + while (list) { + PurpleConversation *conv = list->data; PurpleConversation *cc = find_conv_with_contact(conv->account, conv->name); if (cc) generate_send_to_menu(cc->ui_data); - ims = ims->next; + list = list->next; + } + + if (PURPLE_CONNECTION_IS_CONNECTED(gc)) { + /* We just signed on. Let's see if there's any chat that we have open, + * and hadn't left before the disconnect. */ + list = purple_get_chats(); + while (list) { + PurpleConversation *conv = list->data; + PurpleChat *chat; + GHashTable *comps = NULL; + + list = list->next; + if (conv->account != gc->account || + !purple_conversation_get_data(conv, "want-to-rejoin")) + continue; + + chat = purple_blist_find_chat(conv->account, conv->name); + 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); + } else { + comps = chat->components; + } + serv_join_chat(gc, comps); + if (chat == NULL && comps != NULL) + g_hash_table_destroy(comps); + } } } @@ -738,7 +765,8 @@ gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), _(" "), GNT_TEXT_FLAG_BOLD); - if (who && *who && (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV))) + if (who && *who && (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) && + !(flags & PURPLE_MESSAGE_NOTIFY)) { char * name = NULL; diff -r 5c42c7c7780d -r 045e4f2459a9 finch/gntdebug.c --- a/finch/gntdebug.c Fri Sep 28 20:58:27 2007 +0000 +++ b/finch/gntdebug.c Fri Sep 28 20:58:48 2007 +0000 @@ -28,12 +28,14 @@ #include #include #include +#include #include #include #include #include "gntdebug.h" #include "finch.h" +#include "notify.h" #include "util.h" #include @@ -220,9 +222,43 @@ (GDestroyNotify)g_source_remove); } +static void +file_save(GntFileSel *fs, const char *path, const char *file, GntTextView *tv) +{ + FILE *fp; + + if ((fp = g_fopen(path, "w+")) == NULL) { + purple_notify_error(NULL, NULL, _("Unable to open file."), NULL); + return; + } + + fprintf(fp, "Finch Debug Log : %s\n", purple_date_format_full(NULL)); + fprintf(fp, tv->string->str); + fclose(fp); + gnt_widget_destroy(GNT_WIDGET(fs)); +} + +static void +file_cancel(GntWidget *w, GntFileSel *fs) +{ + gnt_widget_destroy(GNT_WIDGET(fs)); +} + +static void +save_debug_win(GntWidget *w, GntTextView *tv) +{ + GntWidget *window = gnt_file_sel_new(); + GntFileSel *sel = GNT_FILE_SEL(window); + gnt_file_sel_set_current_location(sel, purple_home_dir()); + gnt_file_sel_set_suggested_filename(sel, "debug.txt"); + g_signal_connect(G_OBJECT(sel), "file_selected", G_CALLBACK(file_save), tv); + g_signal_connect(G_OBJECT(sel->cancel), "activate", G_CALLBACK(file_cancel), sel); + gnt_widget_show(window); +} + void finch_debug_window_show() { - GntWidget *wid, *box; + GntWidget *wid, *box, *label; debug.paused = FALSE; if (debug.window) { @@ -258,8 +294,15 @@ GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y); gnt_box_add_widget(GNT_BOX(box), wid); + wid = gnt_button_new(_("Save")); + g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(save_debug_win), debug.tview); + GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y); + gnt_box_add_widget(GNT_BOX(box), wid); + debug.search = gnt_entry_new(purple_prefs_get_string(PREF_ROOT "/filter")); - gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Filter: "))); + label = gnt_label_new(_("Filter:")); + GNT_WIDGET_UNSET_FLAGS(label, GNT_WIDGET_GROW_X); + gnt_box_add_widget(GNT_BOX(box), label); gnt_box_add_widget(GNT_BOX(box), debug.search); g_signal_connect(G_OBJECT(debug.search), "text_changed", G_CALLBACK(update_filter_string), NULL); diff -r 5c42c7c7780d -r 045e4f2459a9 finch/gntnotify.c --- a/finch/gntnotify.c Fri Sep 28 20:58:27 2007 +0000 +++ b/finch/gntnotify.c Fri Sep 28 20:58:48 2007 +0000 @@ -352,14 +352,17 @@ gnt_box_add_widget(GNT_BOX(window), gnt_label_new_with_format(secondary, GNT_TEXT_FLAG_NORMAL)); - columns = purple_notify_searchresults_get_columns_count(results); + columns = g_list_length(results->columns); tree = gnt_tree_new_with_columns(columns); gnt_tree_set_show_title(GNT_TREE(tree), TRUE); gnt_box_add_widget(GNT_BOX(window), tree); - for (i = 0; i < columns; i++) - gnt_tree_set_column_title(GNT_TREE(tree), i, - purple_notify_searchresults_column_get_title(results, i)); + i = 0; + for (iter = results->columns; iter; iter = iter->next) + { + gnt_tree_set_column_title(GNT_TREE(tree), i, iter->data); + i++; + } box = gnt_hbox_new(TRUE); diff -r 5c42c7c7780d -r 045e4f2459a9 finch/libgnt/gntentry.c --- a/finch/libgnt/gntentry.c Fri Sep 28 20:58:27 2007 +0000 +++ b/finch/libgnt/gntentry.c Fri Sep 28 20:58:48 2007 +0000 @@ -100,6 +100,15 @@ return changed; } +static int +max_common_prefix(const char *s, const char *t) +{ + const char *f = s; + while (*f && *t && *f == *t++) + f++; + return f - s; +} + static gboolean show_suggest_dropdown(GntEntry *entry) { @@ -110,6 +119,7 @@ GList *iter; const char *text = NULL; const char *sgst = NULL; + int max = -1; if (entry->word) { @@ -121,14 +131,13 @@ else suggest = g_strdup(entry->start); len = strlen(suggest); /* Don't need to use the utf8-function here */ - + if (entry->ddown == NULL) { GntWidget *box = gnt_vbox_new(FALSE); entry->ddown = gnt_tree_new(); gnt_tree_set_compare_func(GNT_TREE(entry->ddown), (GCompareFunc)g_utf8_collate); gnt_box_add_widget(GNT_BOX(box), entry->ddown); - /* XXX: Connect to the "activate" signal for the dropdown tree */ GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_TRANSIENT); @@ -151,6 +160,10 @@ gnt_tree_create_row(GNT_TREE(entry->ddown), text), NULL, NULL); count++; + if (max == -1) + max = strlen(text) - len; + else if (max) + max = MIN(max, max_common_prefix(sgst + len, text + len)); sgst = text; } } @@ -163,6 +176,17 @@ destroy_suggest(entry); return complete_suggest(entry, sgst); } else { + if (max > 0) { + GntWidget *ddown = entry->ddown; + char *match = g_strndup(sgst + len, max); + entry->ddown = NULL; + gnt_entry_key_pressed(GNT_WIDGET(entry), match); + g_free(match); + if (entry->ddown) + gnt_widget_destroy(ddown); + else + entry->ddown = ddown; + } gnt_widget_draw(entry->ddown->parent); } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/blist.c --- a/libpurple/blist.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/blist.c Fri Sep 28 20:58:48 2007 +0000 @@ -632,12 +632,13 @@ continue; buddy = (PurpleBuddy*)bnode; - - if (!purple_account_is_connected(buddy->account)) + if (new_priority == NULL) + { + new_priority = buddy; continue; - if (new_priority == NULL) - new_priority = buddy; - else + } + + if (purple_account_is_connected(buddy->account)) { int cmp; @@ -1442,29 +1443,7 @@ void purple_contact_set_alias(PurpleContact *contact, const char *alias) { - PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); - char *old_alias; - - g_return_if_fail(contact != NULL); - - if (!purple_strings_are_different(contact->alias, alias)) - return; - - old_alias = contact->alias; - - if ((alias != NULL) && (*alias != '\0')) - contact->alias = g_strdup(alias); - else - contact->alias = NULL; - - purple_blist_schedule_save(); - - if (ops && ops->update) - ops->update(purplebuddylist, (PurpleBlistNode*)contact); - - purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased", - contact, old_alias); - g_free(old_alias); + purple_blist_alias_contact(contact,alias); } const char *purple_contact_get_alias(PurpleContact* contact) diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/blist.h --- a/libpurple/blist.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/blist.h Fri Sep 28 20:58:48 2007 +0000 @@ -65,11 +65,11 @@ typedef enum { - PURPLE_BLIST_NODE_FLAG_NO_SAVE = 1 /**< node should not be saved with the buddy list */ + PURPLE_BLIST_NODE_FLAG_NO_SAVE = 1 << 0, /**< node should not be saved with the buddy list */ } PurpleBlistNodeFlags; -#define PURPLE_BLIST_NODE_HAS_FLAG(b, f) ((b)->flags & (f)) +#define PURPLE_BLIST_NODE_HAS_FLAG(b, f) (((PurpleBlistNode*)(b))->flags & (f)) #define PURPLE_BLIST_NODE_SHOULD_SAVE(b) (! PURPLE_BLIST_NODE_HAS_FLAG(b, PURPLE_BLIST_NODE_FLAG_NO_SAVE)) #define PURPLE_BLIST_NODE_NAME(n) ((n)->type == PURPLE_BLIST_CHAT_NODE ? purple_chat_get_name((PurpleChat*)n) : \ @@ -488,6 +488,8 @@ * * @param contact The contact * @param alias The alias to set, or NULL to unset + * + * @deprecated Use purple_blist_alias_contact() instead. */ void purple_contact_set_alias(PurpleContact *contact, const char *alias); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/cmds.h --- a/libpurple/cmds.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/cmds.h Fri Sep 28 20:58:48 2007 +0000 @@ -67,10 +67,20 @@ PURPLE_CMD_P_VERY_HIGH = 6000, }; +/** Flags used to set various properties of commands. Every command should + * have at least one of #PURPLE_CMD_FLAG_IM and #PURPLE_CMD_FLAG_CHAT set in + * order to be even slighly useful. + * + * @see purple_cmd_register + */ enum _PurpleCmdFlag { + /** Command is usable in IMs. */ PURPLE_CMD_FLAG_IM = 0x01, + /** Command is usable in multi-user chats. */ PURPLE_CMD_FLAG_CHAT = 0x02, + /** Command is usable only for a particular prpl. */ PURPLE_CMD_FLAG_PRPL_ONLY = 0x04, + /** Incorrect arguments to this command should be accepted anyway. */ PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS = 0x08, }; @@ -92,36 +102,49 @@ * The command will only happen if commands are enabled, * which is a UI pref. UIs don't have to support commands at all. * - * @param cmd The command. This should be a UTF8 (or ASCII) string, with no spaces + * @param cmd The command. This should be a UTF-8 (or ASCII) string, with no spaces * or other white space. - * @param args This tells Purple how to parse the arguments to the command for you. - * If what the user types doesn't match, Purple will keep looking for another - * command, unless the flag @c PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed in f. - * This string contains no whitespace, and uses a single character for each argument. - * The recognized characters are: - * 'w' Matches a single word. - * 'W' Matches a single word, with formatting. - * 's' Matches the rest of the arguments after this point, as a single string. - * 'S' Same as 's' but with formatting. + * @param args A string of characters describing to libpurple how to parse this + * command's arguments. If what the user types doesn't match this + * pattern, libpurple will keep looking for another command, unless + * the flag #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed in @a f. + * This string should contain no whitespace, and use a single + * character for each argument. The recognized characters are: + *
    + *
  • 'w': Matches a single word.
  • + *
  • 'W': Matches a single word, with formatting.
  • + *
  • 's': Matches the rest of the arguments after this + * point, as a single string.
  • + *
  • 'S': Same as 's' but with formatting.
  • + *
* If args is the empty string, then the command accepts no arguments. - * The args passed to callback func will be a @c NULL terminated array of null - * terminated strings, and will always match the number of arguments asked for, - * unless PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed. - * @param p This is the priority. Higher priority commands will be run first, and usually the - * first command will stop any others from being called. - * @param f These are the flags. You need to at least pass one of PURPLE_CMD_FLAG_IM or - * PURPLE_CMD_FLAG_CHAT (can may pass both) in order for the command to ever actually - * be called. - * @param prpl_id This is the prpl's id string. This is only meaningful if the proper flag is set. + * The args passed to the callback @a func will be a @c NULL + * terminated array of @c NULL terminated strings, and will always + * match the number of arguments asked for, unless + * #PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS is passed. + * @param p This is the priority. Higher priority commands will be run first, + * and usually the first command will stop any others from being + * called. + * @param f Flags specifying various options about this command, combined with + * | (bitwise OR). You need to at least pass one of + * #PURPLE_CMD_FLAG_IM or #PURPLE_CMD_FLAG_CHAT (you may pass both) in + * order for the command to ever actually be called. + * @param prpl_id If the #PURPLE_CMD_FLAG_PRPL_ONLY flag is set, this is the id + * of the prpl to which the command applies (such as + * "prpl-msn"). If the flag is not set, this parameter + * is ignored; pass @c NULL (or a humourous string of your + * choice!). * @param func This is the function to call when someone enters this command. - * @param helpstr This is a whitespace sensitive, UTF-8, HTML string describing how to use the command. - * The preferred format of this string shall be the commands name, followed by a space - * and any arguments it accepts (if it takes any arguments, otherwise no space), followed - * by a colon, two spaces, and a description of the command in sentence form. No slash - * before the command name. - * @param data User defined data to pass to the PurpleCmdFunc - * @return A PurpleCmdId. This is only used for calling purple_cmd_unregister. - * Returns 0 on failure. + * @param helpstr a whitespace sensitive, UTF-8, HTML string describing how to + * use the command. The preferred format of this string is the + * command's name, followed by a space and any arguments it + * accepts (if it takes any arguments, otherwise no space), + * followed by a colon, two spaces, and a description of the + * command in sentence form. Do not include a slash before the + * command name. + * @param data User defined data to pass to the #PurpleCmdFunc @a f. + * @return A #PurpleCmdId, which is only used for calling + * #purple_cmd_unregister, or @a 0 on failure. */ PurpleCmdId purple_cmd_register(const gchar *cmd, const gchar *args, PurpleCmdPriority p, PurpleCmdFlag f, const gchar *prpl_id, PurpleCmdFunc func, const gchar *helpstr, void *data); @@ -133,7 +156,7 @@ * or something else that might go away. Normally this is called when the plugin * unloads itself. * - * @param id The PurpleCmdId to unregister. + * @param id The #PurpleCmdId to unregister, as returned by #purple_cmd_register. */ void purple_cmd_unregister(PurpleCmdId id); @@ -153,7 +176,7 @@ * include both the default formatting and any extra manual formatting. * @param errormsg If the command failed errormsg is filled in with the appropriate error * message. It must be freed by the caller with g_free(). - * @return A PurpleCmdStatus indicated if the command succeeded or failed. + * @return A #PurpleCmdStatus indicated if the command succeeded or failed. */ PurpleCmdStatus purple_cmd_do_command(PurpleConversation *conv, const gchar *cmdline, const gchar *markup, gchar **errormsg); @@ -161,13 +184,15 @@ /** * List registered commands. * - * Returns a GList (which must be freed by the caller) of all commands - * that are valid in the context of conv, or all commands, if conv is - * @c NULL. Don't keep this list around past the main loop, or anything else - * that might unregister a command, as the char*'s used get freed then. + * Returns a GList (which must be freed by the caller) of all commands + * that are valid in the context of @a conv, or all commands, if @a conv is @c + * NULL. Don't keep this list around past the main loop, or anything else that + * might unregister a command, as the const char *'s used get freed + * then. * * @param conv The conversation, or @c NULL. - * @return A GList of const char*, which must be freed with g_list_free(). + * @return A @c GList of const char *, which must be freed with + * g_list_free(). */ GList *purple_cmd_list(PurpleConversation *conv); @@ -180,7 +205,7 @@ * @param conv The conversation, or @c NULL for no context. * @param cmd The command. No wildcards accepted, but returns help for all * commands if @c NULL. - * @return A GList of const char*s, which is the help string + * @return A GList of const char *s, which is the help string * for that command. */ GList *purple_cmd_help(PurpleConversation *conv, const gchar *cmd); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/conversation.c --- a/libpurple/conversation.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/conversation.c Fri Sep 28 20:58:48 2007 +0000 @@ -2348,7 +2348,7 @@ purple_signal_register(handle, "chat-invited", purple_marshal_INT__POINTER_POINTER_POINTER_POINTER_POINTER, - NULL, 5, + purple_value_new(PURPLE_TYPE_INT), 5, purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), purple_value_new(PURPLE_TYPE_STRING), diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/internal.h --- a/libpurple/internal.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/internal.h Fri Sep 28 20:58:48 2007 +0000 @@ -80,6 +80,7 @@ #ifndef _WIN32 #include #include +#include #endif #include #include diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/notify.h --- a/libpurple/notify.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/notify.h Fri Sep 28 20:58:48 2007 +0000 @@ -293,7 +293,17 @@ /** * Returns a number of the rows in the search results object. - * + * + * @deprecated This function will be removed in Pidgin 3.0.0 unless + * there is sufficient demand to keep it. Using this + * function encourages looping through the results + * inefficiently. Instead of using this function you + * should iterate through the results using a loop + * similar to this: + * for (l = results->rows; l != NULL; l = l->next) + * If you really need to get the number of rows you + * can use g_list_length(results->rows). + * * @param results The search results object. * * @return Number of the result rows. @@ -303,6 +313,16 @@ /** * Returns a number of the columns in the search results object. * + * @deprecated This function will be removed in Pidgin 3.0.0 unless + * there is sufficient demand to keep it. Using this + * function encourages looping through the columns + * inefficiently. Instead of using this function you + * should iterate through the columns using a loop + * similar to this: + * for (l = results->columns; l != NULL; l = l->next) + * If you really need to get the number of columns you + * can use g_list_length(results->columns). + * * @param results The search results object. * * @return Number of the columns. @@ -312,6 +332,16 @@ /** * Returns a row of the results from the search results object. * + * @deprecated This function will be removed in Pidgin 3.0.0 unless + * there is sufficient demand to keep it. Using this + * function encourages looping through the results + * inefficiently. Instead of using this function you + * should iterate through the results using a loop + * similar to this: + * for (l = results->rows; l != NULL; l = l->next) + * If you really need to get the data for a particular + * row you can use g_list_nth_data(results->rows, row_id). + * * @param results The search results object. * @param row_id Index of the row to be returned. * @@ -322,7 +352,15 @@ /** * Returns a title of the search results object's column. - * + * + * @deprecated This function will be removed in Pidgin 3.0.0 unless + * there is sufficient demand to keep it. Using this + * function encourages looping through the columns + * inefficiently. Instead of using this function you + * should iterate through the name of a particular + * column you can use + * g_list_nth_data(results->columns, row_id). + * * @param results The search results object. * @param column_id Index of the column. * diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/plugins/tcl/tcl.c --- a/libpurple/plugins/tcl/tcl.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/plugins/tcl/tcl.c Fri Sep 28 20:58:48 2007 +0000 @@ -60,6 +60,7 @@ PurpleStringref *PurpleTclRefStatusAttr; PurpleStringref *PurpleTclRefStatusType; PurpleStringref *PurpleTclRefXfer; +PurpleStringref *PurpleTclRefHandle; static GHashTable *tcl_plugins = NULL; @@ -363,6 +364,7 @@ PurpleTclRefStatusAttr = purple_stringref_new("StatusAttr"); PurpleTclRefStatusType = purple_stringref_new("StatusType"); PurpleTclRefXfer = purple_stringref_new("Xfer"); + PurpleTclRefHandle = purple_stringref_new("Handle"); tcl_plugins = g_hash_table_new(g_direct_hash, g_direct_equal); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/plugins/tcl/tcl_cmds.c --- a/libpurple/plugins/tcl/tcl_cmds.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/plugins/tcl/tcl_cmds.c Fri Sep 28 20:58:48 2007 +0000 @@ -55,7 +55,7 @@ return account; } if (interp != NULL) - Tcl_SetStringObj(Tcl_GetObjResult(interp), "invalid account", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid account", -1)); return NULL; } @@ -74,7 +74,7 @@ return convo; } if (interp != NULL) - Tcl_SetStringObj(Tcl_GetObjResult(interp), "invalid conversation", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid conversation", -1)); return NULL; } @@ -97,7 +97,7 @@ int tcl_cmd_account(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *result = Tcl_GetObjResult(interp), *list, *elem; + Tcl_Obj *result, *list, *elem; const char *cmds[] = { "alias", "connect", "connection", "disconnect", "enabled", "find", "handle", "isconnected", "list", "presence", "protocol", "status", @@ -139,7 +139,7 @@ if ((account = tcl_validate_account(objv[2], interp)) == NULL) return TCL_ERROR; alias = purple_account_get_alias(account); - Tcl_SetStringObj(result, alias ? (char *)alias : "", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj(alias ? (char *)alias : "", -1)); break; case CMD_ACCOUNT_CONNECT: if (objc != 3) { @@ -164,7 +164,7 @@ return TCL_ERROR; Tcl_SetObjResult(interp, purple_tcl_ref_new(PurpleTclRefConnection, - purple_account_get_connection(account))); + purple_account_get_connection(account))); break; case CMD_ACCOUNT_DISCONNECT: if (objc != 3) { @@ -183,9 +183,10 @@ if ((account = tcl_validate_account(objv[2], interp)) == NULL) return TCL_ERROR; if (objc == 3) { - Tcl_SetBooleanObj(result, - purple_account_get_enabled(account, - purple_core_get_ui())); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_account_get_enabled(account, + purple_core_get_ui()))); } else { if ((error = Tcl_GetBooleanFromObj(interp, objv[3], &b)) != TCL_OK) return TCL_ERROR; @@ -207,7 +208,9 @@ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } - Tcl_SetIntObj(result, (int)purple_accounts_get_handle()); + Tcl_SetObjResult(interp, + purple_tcl_ref_new(PurpleTclRefHandle, + purple_accounts_get_handle())); break; case CMD_ACCOUNT_ISCONNECTED: if (objc != 3) { @@ -216,7 +219,9 @@ } if ((account = tcl_validate_account(objv[2], interp)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_account_is_connected(account)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_account_is_connected(account))); break; case CMD_ACCOUNT_LIST: listopt = CMD_ACCOUNTLIST_ALL; @@ -255,7 +260,7 @@ } if ((account = tcl_validate_account(objv[2], interp)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, (char *)purple_account_get_protocol_id(account), -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj((char *)purple_account_get_protocol_id(account), -1)); break; case CMD_ACCOUNT_STATUS: if (objc < 3) { @@ -271,19 +276,19 @@ } else { GList *l = NULL; if (objc % 2) { - Tcl_SetStringObj(result, "name without value setting status", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("name without value setting status", -1)); return TCL_ERROR; } status = purple_account_get_status(account, Tcl_GetString(objv[3])); if (status == NULL) { - Tcl_SetStringObj(result, "invalid status for account", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid status for account", -1)); return TCL_ERROR; } for (i = 4; i < objc; i += 2) { attr_id = Tcl_GetString(objv[i]); value = purple_status_get_attr_value(status, attr_id); if (value == NULL) { - Tcl_SetStringObj(result, "invalid attribute for account", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("invalid attribute for account", -1)); return TCL_ERROR; } switch (purple_value_get_type(value)) { @@ -306,7 +311,7 @@ l = g_list_append(l, Tcl_GetString(objv[i + 1])); break; default: - Tcl_SetStringObj(result, "unknown PurpleValue type", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("unknown PurpleValue type", -1)); return TCL_ERROR; } } @@ -327,10 +332,10 @@ } else { PurpleStatusPrimitive primitive; if (strcmp(Tcl_GetString(objv[3]), "-primitive")) { - Tcl_SetStringObj(result, "bad option \"", -1); + result = Tcl_NewStringObj("bad option \"", -1); Tcl_AppendObjToObj(result, objv[3]); - Tcl_AppendToObj(result, - "\": should be -primitive", -1); + Tcl_AppendToObj(result, "\": should be -primitive", -1); + Tcl_SetObjResult(interp,result); return TCL_ERROR; } primitive = purple_primitive_get_type_from_id(Tcl_GetString(objv[4])); @@ -338,7 +343,7 @@ primitive); } if (status_type == NULL) { - Tcl_SetStringObj(result, "status type not found", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("status type not found", -1)); return TCL_ERROR; } Tcl_SetObjResult(interp, @@ -368,7 +373,8 @@ } if ((account = tcl_validate_account(objv[2], interp)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, (char *)purple_account_get_username(account), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj((char *)purple_account_get_username(account), -1)); break; } @@ -383,7 +389,8 @@ char *type; if (count < 3) { - Tcl_SetStringObj(Tcl_GetObjResult(interp), "list too short", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("list too short", -1)); return NULL; } @@ -420,8 +427,6 @@ if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) return error; - result = Tcl_GetObjResult(interp); - switch (cmd) { case CMD_BUDDY_ALIAS: if (objc != 3) { @@ -433,9 +438,11 @@ if ((node = tcl_list_to_buddy(interp, count, elems)) == NULL) return TCL_ERROR; if (node->type == PURPLE_BLIST_CHAT_NODE) - Tcl_SetStringObj(result, ((PurpleChat *)node)->alias, -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(((PurpleChat *)node)->alias, -1)); else if (node->type == PURPLE_BLIST_BUDDY_NODE) - Tcl_SetStringObj(result, (char *)purple_buddy_get_alias((PurpleBuddy *)node), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj((char *)purple_buddy_get_alias((PurpleBuddy *)node), -1)); return TCL_OK; break; case CMD_BUDDY_HANDLE: @@ -443,7 +450,9 @@ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } - Tcl_SetIntObj(result, (int)purple_blist_get_handle()); + Tcl_SetObjResult(interp, + purple_tcl_ref_new(PurpleTclRefHandle, + purple_blist_get_handle())); break; case CMD_BUDDY_INFO: if (objc != 3 && objc != 4) { @@ -454,11 +463,13 @@ if ((error = Tcl_ListObjGetElements(interp, objv[2], &count, &elems)) != TCL_OK) return error; if (count < 3) { - Tcl_SetStringObj(result, "buddy too short", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("buddy too short", -1)); return TCL_ERROR; } if (strcmp("buddy", Tcl_GetString(elems[0]))) { - Tcl_SetStringObj(result, "invalid buddy", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("invalid buddy", -1)); return TCL_ERROR; } if ((account = tcl_validate_account(elems[2], interp)) == NULL) @@ -475,8 +486,9 @@ if (!strcmp("-all", Tcl_GetString(objv[2]))) { all = 1; } else { - Tcl_SetStringObj(result, "", -1); + result = Tcl_NewStringObj("",-1); Tcl_AppendStringsToObj(result, "unknown option: ", Tcl_GetString(objv[2]), NULL); + Tcl_SetObjResult(interp,result); return TCL_ERROR; } } @@ -547,7 +559,7 @@ const char *cmds[] = { "do", "help", "list", "register", "unregister", NULL }; enum { CMD_CMD_DO, CMD_CMD_HELP, CMD_CMD_LIST, CMD_CMD_REGISTER, CMD_CMD_UNREGISTER } cmd; struct tcl_cmd_handler *handler; - Tcl_Obj *list, *elem, *result = Tcl_GetObjResult(interp); + Tcl_Obj *list, *elem; PurpleConversation *convo; PurpleCmdId id; PurpleCmdStatus status; @@ -575,7 +587,8 @@ status = purple_cmd_do_command(convo, Tcl_GetString(objv[3]), escaped, &errstr); g_free(escaped); - Tcl_SetStringObj(result, errstr ? (char *)errstr : "", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(errstr ? (char *)errstr : "", -1)); g_free(errstr); if (status != PURPLE_CMD_STATUS_OK) { return TCL_ERROR; @@ -640,10 +653,10 @@ handler->interp = interp; if ((id = tcl_cmd_register(handler)) == 0) { tcl_cmd_handler_free(handler); - Tcl_SetIntObj(result, 0); + Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); } else { handler->id = id; - Tcl_SetIntObj(result, id); + Tcl_SetObjResult(interp, Tcl_NewIntObj(id)); } break; case CMD_CMD_UNREGISTER: @@ -663,7 +676,7 @@ int tcl_cmd_connection(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *result = Tcl_GetObjResult(interp), *list, *elem; + Tcl_Obj *list, *elem; const char *cmds[] = { "account", "displayname", "handle", "list", NULL }; enum { CMD_CONN_ACCOUNT, CMD_CONN_DISPLAYNAME, CMD_CONN_HANDLE, CMD_CONN_LIST } cmd; int error; @@ -697,14 +710,16 @@ } if ((gc = tcl_validate_gc(objv[2], interp)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, (char *)purple_connection_get_display_name(gc), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_connection_get_display_name(gc), -1)); break; case CMD_CONN_HANDLE: if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } - Tcl_SetIntObj(result, (int)purple_connections_get_handle()); + Tcl_SetObjResult(interp, purple_tcl_ref_new(PurpleTclRefHandle, + purple_connections_get_handle())); break; case CMD_CONN_LIST: if (objc != 2) { @@ -725,7 +740,7 @@ int tcl_cmd_conversation(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *list, *elem, *result = Tcl_GetObjResult(interp); + Tcl_Obj *list, *elem; const char *cmds[] = { "find", "handle", "list", "new", "write", "name", "title", "send", NULL }; enum { CMD_CONV_FIND, CMD_CONV_HANDLE, CMD_CONV_LIST, CMD_CONV_NEW, CMD_CONV_WRITE , CMD_CONV_NAME, CMD_CONV_TITLE, CMD_CONV_SEND } cmd; const char *styles[] = { "send", "recv", "system", NULL }; @@ -766,7 +781,9 @@ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } - Tcl_SetIntObj(result, (int)purple_conversations_get_handle()); + Tcl_SetObjResult(interp, + purple_tcl_ref_new(PurpleTclRefHandle, + purple_conversations_get_handle())); break; case CMD_CONV_LIST: list = Tcl_NewListObj(0, NULL); @@ -847,7 +864,8 @@ if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, (char *)purple_conversation_get_name(convo), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj((char *)purple_conversation_get_name(convo), -1)); break; case CMD_CONV_TITLE: if (objc != 3) { @@ -857,7 +875,8 @@ if ((convo = tcl_validate_conversation(objv[2], interp)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, (char *)purple_conversation_get_title(convo), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj((char *)purple_conversation_get_title(convo), -1)); break; case CMD_CONV_SEND: if (objc != 4) { @@ -879,7 +898,6 @@ int tcl_cmd_core(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *result = Tcl_GetObjResult(interp); const char *cmds[] = { "handle", "quit", NULL }; enum { CMD_CORE_HANDLE, CMD_CORE_QUIT } cmd; int error; @@ -898,7 +916,9 @@ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } - Tcl_SetIntObj(result, (int)purple_get_core()); + Tcl_SetObjResult(interp, + purple_tcl_ref_new(PurpleTclRefHandle, + purple_get_core())); break; case CMD_CORE_QUIT: if (objc != 2) { @@ -970,7 +990,6 @@ int tcl_cmd_plugins(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *result = Tcl_GetObjResult(interp); const char *cmds[] = { "handle", NULL }; enum { CMD_PLUGINS_HANDLE } cmd; int error; @@ -989,7 +1008,9 @@ Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } - Tcl_SetIntObj(result, (int)purple_plugins_get_handle()); + Tcl_SetObjResult(interp, + purple_tcl_ref_new(PurpleTclRefHandle, + purple_plugins_get_handle())); break; } @@ -998,7 +1019,7 @@ int tcl_cmd_prefs(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *result, *list, *elem, **elems; + Tcl_Obj *list, *elem, **elems; const char *cmds[] = { "get", "set", "type", NULL }; enum { CMD_PREFS_GET, CMD_PREFS_SET, CMD_PREFS_TYPE } cmd; /* char *types[] = { "none", "boolean", "int", "string", "stringlist", NULL }; */ @@ -1015,7 +1036,6 @@ if ((error = Tcl_GetIndexFromObj(interp, objv[1], cmds, "subcommand", 0, (int *)&cmd)) != TCL_OK) return error; - result = Tcl_GetObjResult(interp); switch (cmd) { case CMD_PREFS_GET: if (objc != 3) { @@ -1025,17 +1045,21 @@ preftype = purple_prefs_get_type(Tcl_GetString(objv[2])); switch (preftype) { case PURPLE_PREF_NONE: - Tcl_SetStringObj(result, "pref type none", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("pref type none", -1)); return TCL_ERROR; break; case PURPLE_PREF_BOOLEAN: - Tcl_SetBooleanObj(result, purple_prefs_get_bool(Tcl_GetString(objv[2]))); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_prefs_get_bool(Tcl_GetString(objv[2])))); break; case PURPLE_PREF_INT: - Tcl_SetIntObj(result, purple_prefs_get_int(Tcl_GetString(objv[2]))); + Tcl_SetObjResult(interp, Tcl_NewIntObj(purple_prefs_get_int(Tcl_GetString(objv[2])))); break; case PURPLE_PREF_STRING: - Tcl_SetStringObj(result, (char *)purple_prefs_get_string(Tcl_GetString(objv[2])), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj((char *)purple_prefs_get_string(Tcl_GetString(objv[2])), -1)); break; case PURPLE_PREF_STRING_LIST: cur = purple_prefs_get_string_list(Tcl_GetString(objv[2])); @@ -1049,7 +1073,8 @@ break; default: purple_debug(PURPLE_DEBUG_ERROR, "tcl", "tcl does not know about pref type %d\n", preftype); - Tcl_SetStringObj(result, "unknown pref type", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("unknown pref type", -1)); return TCL_ERROR; } break; @@ -1061,7 +1086,8 @@ preftype = purple_prefs_get_type(Tcl_GetString(objv[2])); switch (preftype) { case PURPLE_PREF_NONE: - Tcl_SetStringObj(result, "bad path or pref type none", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("bad path or pref type none", -1)); return TCL_ERROR; break; case PURPLE_PREF_BOOLEAN: @@ -1100,23 +1126,23 @@ preftype = purple_prefs_get_type(Tcl_GetString(objv[2])); switch (preftype) { case PURPLE_PREF_NONE: - Tcl_SetStringObj(result, "none", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("none", -1)); break; case PURPLE_PREF_BOOLEAN: - Tcl_SetStringObj(result, "boolean", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("boolean", -1)); break; case PURPLE_PREF_INT: - Tcl_SetStringObj(result, "int", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("int", -1)); break; case PURPLE_PREF_STRING: - Tcl_SetStringObj(result, "string", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("string", -1)); break; case PURPLE_PREF_STRING_LIST: - Tcl_SetStringObj(result, "stringlist", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("stringlist", -1)); break; default: purple_debug(PURPLE_DEBUG_ERROR, "tcl", "tcl does not know about pref type %d\n", preftype); - Tcl_SetStringObj(result, "unknown", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("unknown", -1)); } break; } @@ -1134,7 +1160,7 @@ CMD_PRESENCE_CONTEXT, CMD_PRESENCE_CONVERSATION, CMD_PRESENCE_IDLE, CMD_PRESENCE_LOGIN, CMD_PRESENCE_ONLINE, CMD_PRESENCE_STATUS, CMD_PRESENCE_STATUSES } cmd; - Tcl_Obj *result = Tcl_GetObjResult(interp); + Tcl_Obj *result; Tcl_Obj *list, *elem; PurplePresence *presence; GList *cur; @@ -1171,25 +1197,30 @@ purple_tcl_ref_new(PurpleTclRefStatus, purple_presence_get_active_status(presence))); } else if (objc == 4) { - Tcl_SetBooleanObj(result, - purple_presence_is_status_active(presence, - Tcl_GetString(objv[3]))); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_presence_is_status_active(presence, + Tcl_GetString(objv[3])))); } else { PurpleStatusPrimitive primitive; if (strcmp(Tcl_GetString(objv[3]), "-primitive")) { - Tcl_SetStringObj(result, "bad option \"", -1); + result = Tcl_NewStringObj("bad option \"", -1); Tcl_AppendObjToObj(result, objv[3]); Tcl_AppendToObj(result, "\": should be -primitive", -1); + Tcl_SetObjResult(interp,result); return TCL_ERROR; } primitive = purple_primitive_get_type_from_id(Tcl_GetString(objv[4])); if (primitive == PURPLE_STATUS_UNSET) { - Tcl_SetStringObj(result, "invalid primitive ", -1); + result = Tcl_NewStringObj("invalid primitive ", -1); Tcl_AppendObjToObj(result, objv[4]); + Tcl_SetObjResult(interp,result); return TCL_ERROR; } - Tcl_SetBooleanObj(result, purple_presence_is_status_primitive_active(presence, primitive)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_presence_is_status_primitive_active(presence, primitive))); break; } break; @@ -1200,7 +1231,8 @@ } if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_presence_is_available(presence)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj(purple_presence_is_available(presence))); break; case CMD_PRESENCE_CHAT_USER: if (objc != 3) { @@ -1209,7 +1241,8 @@ } if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, purple_presence_get_chat_user(presence), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_presence_get_chat_user(presence), -1)); break; case CMD_PRESENCE_CONTEXT: if (objc != 3) { @@ -1220,16 +1253,16 @@ return TCL_ERROR; switch (purple_presence_get_context(presence)) { case PURPLE_PRESENCE_CONTEXT_UNSET: - Tcl_SetStringObj(result, "unset", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("unset", -1)); break; case PURPLE_PRESENCE_CONTEXT_ACCOUNT: - Tcl_SetStringObj(result, "account", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("account", -1)); break; case PURPLE_PRESENCE_CONTEXT_CONV: - Tcl_SetStringObj(result, "conversation", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("conversation", -1)); break; case PURPLE_PRESENCE_CONTEXT_BUDDY: - Tcl_SetStringObj(result, "buddy", -1); + Tcl_SetObjResult(interp, Tcl_NewStringObj("buddy", -1)); break; } break; @@ -1253,7 +1286,7 @@ if (objc == 3) { if (purple_presence_is_idle(presence)) { idle_time = purple_presence_get_idle_time (presence); - Tcl_SetIntObj(result, idle_time); + Tcl_SetObjResult(interp, Tcl_NewIntObj(idle_time)); } else { result = Tcl_NewListObj(0, NULL); Tcl_SetObjResult(interp, result); @@ -1280,7 +1313,7 @@ if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL) return TCL_ERROR; if (objc == 3) { - Tcl_SetIntObj(result, purple_presence_get_login_time(presence)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(purple_presence_get_login_time(presence))); } else { if ((error == Tcl_GetIntFromObj(interp, objv[3], @@ -1296,7 +1329,9 @@ } if ((presence = purple_tcl_ref_get(interp, objv[2], PurpleTclRefPresence)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_presence_is_online(presence)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_presence_is_online(presence))); break; case CMD_PRESENCE_STATUS: if (objc != 4) { @@ -1332,7 +1367,7 @@ int tcl_cmd_savedstatus(ClientData unused, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - Tcl_Obj *result = Tcl_GetObjResult(interp); + Tcl_Obj *result; const char *cmds[] = { "current", "handle", NULL }; enum { CMD_SAVEDSTATUS_CURRENT, CMD_SAVEDSTATUS_HANDLE } cmd; int error; @@ -1354,16 +1389,20 @@ } if ((saved_status = purple_savedstatus_get_current()) == NULL) return TCL_ERROR; + result = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(purple_savedstatus_get_title(saved_status), -1)); Tcl_ListObjAppendElement(interp, result, Tcl_NewIntObj(purple_savedstatus_get_type(saved_status))); Tcl_ListObjAppendElement(interp, result, Tcl_NewStringObj(purple_savedstatus_get_message(saved_status), -1)); + Tcl_SetObjResult(interp,result); break; case CMD_SAVEDSTATUS_HANDLE: if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, ""); return TCL_ERROR; } - Tcl_SetIntObj(result, (int)purple_savedstatuses_get_handle()); + Tcl_SetObjResult(interp, + purple_tcl_ref_new(PurpleTclRefHandle, + purple_savedstatuses_get_handle())); break; } @@ -1396,7 +1435,6 @@ const char *cmds[] = { "connect", "disconnect", NULL }; enum { CMD_SIGNAL_CONNECT, CMD_SIGNAL_DISCONNECT } cmd; struct tcl_signal_handler *handler; - Tcl_Obj *result = Tcl_GetObjResult(interp); void *instance; int error; @@ -1415,7 +1453,7 @@ return TCL_ERROR; } handler = g_new0(struct tcl_signal_handler, 1); - if ((error = Tcl_GetIntFromObj(interp, objv[2], (int *)&handler->instance)) != TCL_OK) { + if ((handler->instance = purple_tcl_ref_get(interp, objv[2],PurpleTclRefHandle)) == NULL) { g_free(handler); return error; } @@ -1426,9 +1464,9 @@ handler->interp = interp; if (!tcl_signal_connect(handler)) { tcl_signal_handler_free(handler); - Tcl_SetIntObj(result, 1); + Tcl_SetObjResult(interp, Tcl_NewIntObj(1)); } else { - Tcl_SetIntObj(result, 0); + Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); } break; case CMD_SIGNAL_DISCONNECT: @@ -1436,7 +1474,7 @@ Tcl_WrongNumArgs(interp, 2, objv, "instance signal"); return TCL_ERROR; } - if ((error = Tcl_GetIntFromObj(interp, objv[2], (int *)&instance)) != TCL_OK) + if ((instance = purple_tcl_ref_get(interp, objv[2],PurpleTclRefHandle)) == NULL) return error; tcl_signal_disconnect(instance, Tcl_GetString(objv[3]), interp); break; @@ -1449,7 +1487,6 @@ { const char *cmds[] = { "attr", "type", NULL }; enum { CMD_STATUS_ATTR, CMD_STATUS_TYPE } cmd; - Tcl_Obj *result = Tcl_GetObjResult(interp); PurpleStatus *status; PurpleStatusType *status_type; PurpleValue *value; @@ -1475,13 +1512,15 @@ attr = Tcl_GetString(objv[3]); value = purple_status_get_attr_value(status, attr); if (value == NULL) { - Tcl_SetStringObj(result, "no such attribute", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("no such attribute", -1)); return TCL_ERROR; } switch (purple_value_get_type(value)) { case PURPLE_TYPE_BOOLEAN: if (objc == 4) { - Tcl_SetBooleanObj(result, purple_value_get_boolean(value)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj(purple_value_get_boolean(value))); } else { if ((error = Tcl_GetBooleanFromObj(interp, objv[4], &v)) != TCL_OK) return error; @@ -1490,7 +1529,7 @@ break; case PURPLE_TYPE_INT: if (objc == 4) { - Tcl_SetIntObj(result, purple_value_get_int(value)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(purple_value_get_int(value))); } else { if ((error = Tcl_GetIntFromObj(interp, objv[4], &v)) != TCL_OK) return error; @@ -1499,12 +1538,14 @@ break; case PURPLE_TYPE_STRING: if (objc == 4) - Tcl_SetStringObj(result, purple_value_get_string(value), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_value_get_string(value), -1)); else purple_status_set_attr_string(status, attr, Tcl_GetString(objv[4])); break; default: - Tcl_SetStringObj(result, "attribute has unknown type", -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("attribute has unknown type", -1)); return TCL_ERROR; } break; @@ -1528,7 +1569,6 @@ { const char *cmds[] = { "id", "name", NULL }; enum { CMD_STATUS_ATTR_ID, CMD_STATUS_ATTR_NAME } cmd; - Tcl_Obj *result = Tcl_GetObjResult(interp); PurpleStatusAttr *attr; int error; @@ -1548,7 +1588,8 @@ } if ((attr = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusAttr)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, purple_status_attr_get_id(attr), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_status_attr_get_id(attr), -1)); break; case CMD_STATUS_ATTR_NAME: if (objc != 3) { @@ -1557,7 +1598,8 @@ } if ((attr = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusAttr)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, purple_status_attr_get_name(attr), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_status_attr_get_name(attr), -1)); break; } @@ -1576,7 +1618,6 @@ CMD_STATUS_TYPE_NAME, CMD_STATUS_TYPE_PRIMARY_ATTR, CMD_STATUS_TYPE_PRIMITIVE, CMD_STATUS_TYPE_SAVEABLE, CMD_STATUS_TYPE_USER_SETTABLE } cmd; - Tcl_Obj *result = Tcl_GetObjResult(interp); PurpleStatusType *status_type; Tcl_Obj *list, *elem; GList *cur; @@ -1598,7 +1639,8 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_status_type_is_available(status_type)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj(purple_status_type_is_available(status_type))); break; case CMD_STATUS_TYPE_ATTR: if (objc != 4) { @@ -1634,7 +1676,8 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_status_type_is_exclusive(status_type)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj(purple_status_type_is_exclusive(status_type))); break; case CMD_STATUS_TYPE_ID: if (objc != 3) { @@ -1643,7 +1686,8 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, purple_status_type_get_id(status_type), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_status_type_get_id(status_type), -1)); break; case CMD_STATUS_TYPE_INDEPENDENT: if (objc != 3) { @@ -1652,7 +1696,8 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_status_type_is_independent(status_type)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj(purple_status_type_is_independent(status_type))); break; case CMD_STATUS_TYPE_NAME: if (objc != 3) { @@ -1661,7 +1706,8 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, purple_status_type_get_name(status_type), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_status_type_get_name(status_type), -1)); break; case CMD_STATUS_TYPE_PRIMITIVE: if (objc != 3) { @@ -1670,7 +1716,9 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, purple_primitive_get_id_from_type(purple_status_type_get_primitive(status_type)), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_primitive_get_id_from_type + (purple_status_type_get_primitive(status_type)), -1)); break; case CMD_STATUS_TYPE_PRIMARY_ATTR: if (objc != 3) { @@ -1679,7 +1727,8 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetStringObj(result, purple_status_type_get_primary_attr(status_type), -1); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(purple_status_type_get_primary_attr(status_type), -1)); break; case CMD_STATUS_TYPE_SAVEABLE: if (objc != 3) { @@ -1688,7 +1737,9 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_status_type_is_saveable(status_type)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_status_type_is_saveable(status_type))); break; case CMD_STATUS_TYPE_USER_SETTABLE: if (objc != 3) { @@ -1697,7 +1748,9 @@ } if ((status_type = purple_tcl_ref_get(interp, objv[2], PurpleTclRefStatusType)) == NULL) return TCL_ERROR; - Tcl_SetBooleanObj(result, purple_status_type_is_user_settable(status_type)); + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj( + purple_status_type_is_user_settable(status_type))); break; } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/plugins/tcl/tcl_purple.h --- a/libpurple/plugins/tcl/tcl_purple.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/plugins/tcl/tcl_purple.h Fri Sep 28 20:58:48 2007 +0000 @@ -76,6 +76,7 @@ extern PurpleStringref *PurpleTclRefStatusAttr; extern PurpleStringref *PurpleTclRefStatusType; extern PurpleStringref *PurpleTclRefXfer; +extern PurpleStringref *PurpleTclRefHandle; PurplePlugin *tcl_interp_get_plugin(Tcl_Interp *interp); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/adhoccommands.c --- a/libpurple/protocols/jabber/adhoccommands.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/adhoccommands.c Fri Sep 28 20:58:48 2007 +0000 @@ -151,8 +151,11 @@ /* display result */ xmlnode *note = xmlnode_get_child(command,"note"); - if(note) - purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), xmlnode_get_data(note), NULL); + if(note) { + char *data = xmlnode_get_data(note); + purple_notify_info(NULL, xmlnode_get_attrib(packet, "from"), data, NULL); + g_free(data); + } if(xdata) jabber_x_data_request(js, xdata, (jabber_x_data_cb)do_adhoc_ignoreme, NULL); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/buddy.c Fri Sep 28 20:58:48 2007 +0000 @@ -1455,10 +1455,13 @@ return; img = purple_base64_decode(b64data, &size); - if(!img) + if(!img) { + g_free(b64data); return; + } purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, img, size, checksum); + g_free(b64data); } void jabber_buddy_avatar_update_metadata(JabberStream *js, const char *from, xmlnode *items) { diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.c Fri Sep 28 20:58:48 2007 +0000 @@ -814,7 +814,7 @@ if(account->registration_cb) (account->registration_cb)(account, FALSE, account->registration_cb_user_data); jabber_connection_schedule_close(cbdata->js); -} + } g_free(cbdata->who); g_free(cbdata); } @@ -883,12 +883,12 @@ if((href = xmlnode_get_data(url))) { purple_notify_uri(NULL, href); g_free(href); - if(js->registration) { - js->gc->wants_to_die = TRUE; - if(account->registration_cb) /* succeeded, but we have no login info */ - (account->registration_cb)(account, TRUE, account->registration_cb_user_data); - jabber_connection_schedule_close(js); - } + if(js->registration) { + js->gc->wants_to_die = TRUE; + if(account->registration_cb) /* succeeded, but we have no login info */ + (account->registration_cb)(account, TRUE, account->registration_cb_user_data); + jabber_connection_schedule_close(js); + } return; } } @@ -988,14 +988,14 @@ purple_request_field_group_add_field(group, field); } - if((y = xmlnode_get_child(query, "instructions"))) - instructions = xmlnode_get_data(y); + if((y = xmlnode_get_child(query, "instructions"))) + instructions = xmlnode_get_data(y); else if(registered) instructions = g_strdup(_("Please fill out the information below " "to change your account registration.")); - else - instructions = g_strdup(_("Please fill out the information below " - "to register your new account.")); + else + instructions = g_strdup(_("Please fill out the information below " + "to register your new account.")); cbdata = g_new0(JabberRegisterCBData, 1); cbdata->js = js; @@ -1020,8 +1020,8 @@ g_free(title); } - g_free(instructions); - } + g_free(instructions); +} void jabber_register_start(JabberStream *js) { @@ -1532,15 +1532,15 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1555,15 +1555,15 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1578,15 +1578,15 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1601,15 +1601,15 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -1624,15 +1624,15 @@ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), "mood", _("Mood"), purple_value_new(PURPLE_TYPE_STRING), "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), - "tune_artist", _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), - "tune_title", _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), - "tune_album", _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), - "tune_genre", _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), - "tune_comment", _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), - "tune_track", _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), - "tune_time", _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), - "tune_year", _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), - "tune_url", _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ARTIST, _("Tune Artist"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TITLE, _("Tune Title"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_ALBUM, _("Tune Album"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_GENRE, _("Tune Genre"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_COMMENT, _("Tune Comment"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TRACK, _("Tune Track"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_TUNE_TIME, _("Tune Time"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), + PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), purple_value_new(PURPLE_TYPE_BOOLEAN), NULL); @@ -2240,6 +2240,44 @@ return PURPLE_CMD_RET_FAILED; } +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); + } + + return types; +} + +gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code) +{ + PurpleConversation *conv; + char *error; + char *args[1]; + PurpleCmdRet ret; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, username, gc->account); + + args[0] = (char *)username; + + ret = jabber_cmd_buzz(conv, "buzz", args, &error, NULL); + + if (ret == PURPLE_CMD_RET_FAILED) { + purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error ? error : "(NULL)"); + return FALSE; + } else { + return TRUE; + } +} + + gboolean jabber_offline_message(const PurpleBuddy *buddy) { return TRUE; diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/jabber.h Fri Sep 28 20:58:48 2007 +0000 @@ -66,6 +66,9 @@ #define CAPS0115_NODE "http://pidgin.im/caps" +/* Index into attention_types list */ +#define JABBER_BUZZ 0 + typedef enum { JABBER_STREAM_OFFLINE, JABBER_STREAM_CONNECTING, @@ -231,6 +234,8 @@ void jabber_register_gateway(JabberStream *js, const char *gateway); void jabber_register_account(PurpleAccount *account); void jabber_unregister_account(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data); +gboolean jabber_send_attention(PurpleConnection *gc, const char *username, guint code); +GList *jabber_attention_types(PurpleAccount *account); void jabber_convo_closed(PurpleConnection *gc, const char *who); PurpleChat *jabber_find_blist_chat(PurpleAccount *account, const char *name); gboolean jabber_offline_message(const PurpleBuddy *buddy); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Fri Sep 28 20:58:48 2007 +0000 @@ -113,10 +113,10 @@ jabber_prpl_send_raw, /* send_raw */ jabber_roomlist_room_serialize, /* roomlist_room_serialize */ jabber_unregister_account, /* unregister_user */ + jabber_send_attention, /* send_attention */ + jabber_attention_types, /* attention_types */ /* padding */ - NULL, - NULL, NULL }; diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/message.c --- a/libpurple/protocols/jabber/message.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/message.c Fri Sep 28 20:58:48 2007 +0000 @@ -327,7 +327,7 @@ if(type) { if(!strcmp(type, "normal")) jm->type = JABBER_MESSAGE_NORMAL; - else if(!strcmp(type, "chat")) + else if(!strcmp(type, "chat")) jm->type = JABBER_MESSAGE_CHAT; else if(!strcmp(type, "groupchat")) jm->type = JABBER_MESSAGE_GROUPCHAT; diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/presence.c Fri Sep 28 20:58:48 2007 +0000 @@ -192,16 +192,11 @@ jabber_tune_set(js->gc, &tuneinfo); /* update old values */ - if(js->old_artist) - g_free(js->old_artist); - if(js->old_title) - g_free(js->old_title); - if(js->old_source) - g_free(js->old_source); - if(js->old_uri) - g_free(js->old_uri); - if(js->old_track) - g_free(js->old_track); + g_free(js->old_artist); + g_free(js->old_title); + g_free(js->old_source); + g_free(js->old_uri); + g_free(js->old_track); js->old_artist = g_strdup(artist); js->old_title = g_strdup(title); js->old_source = g_strdup(source); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/usernick.c --- a/libpurple/protocols/jabber/usernick.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/usernick.c Fri Sep 28 20:58:48 2007 +0000 @@ -33,7 +33,7 @@ xmlnode *item = xmlnode_get_child(items, "item"); JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE); xmlnode *nick; - const char *nickname = NULL; + char *nickname = NULL; /* ignore the tune of people not on our buddy list */ if (!buddy || !item) @@ -43,8 +43,8 @@ if (!nick) return; nickname = xmlnode_get_data(nick); - serv_got_alias(js->gc, from, nickname); + g_free(nickname); } static void do_nick_set(JabberStream *js, const char *nick) { @@ -64,7 +64,7 @@ } static void do_nick_got_own_nick_cb(JabberStream *js, const char *from, xmlnode *items) { - const char *oldnickname = NULL; + char *oldnickname = NULL; xmlnode *item = xmlnode_get_child(items,"item"); if(item) { @@ -77,6 +77,7 @@ _("This information is visible to all contacts on your contact list, so choose something appropriate."), oldnickname, FALSE, FALSE, NULL, _("Set"), PURPLE_CALLBACK(do_nick_set), _("Cancel"), NULL, purple_connection_get_account(js->gc), NULL, NULL, js); + g_free(oldnickname); } static void do_nick_set_nick(PurplePluginAction *action) { diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/jabber/usertune.c --- a/libpurple/protocols/jabber/usertune.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/jabber/usertune.c Fri Sep 28 20:58:48 2007 +0000 @@ -41,48 +41,61 @@ if (!buddy || !item) return; - tuneinfodata.artist = ""; - tuneinfodata.title = ""; - tuneinfodata.album = ""; - tuneinfodata.track = ""; + tuneinfodata.artist = NULL; + tuneinfodata.title = NULL; + tuneinfodata.album = NULL; + tuneinfodata.track = NULL; tuneinfodata.time = -1; - tuneinfodata.url = ""; - + tuneinfodata.url = NULL; + tune = xmlnode_get_child_with_namespace(item, "tune", "http://jabber.org/protocol/tune"); if (!tune) return; + resource = jabber_buddy_find_resource(buddy, NULL); + if(!resource) + return; /* huh? */ for (tuneinfo = tune->child; tuneinfo; tuneinfo = tuneinfo->next) { if (tuneinfo->type == XMLNODE_TYPE_TAG) { if (!strcmp(tuneinfo->name, "artist")) { - if (tuneinfodata.artist[0] == '\0') /* only pick the first one */ + if (tuneinfodata.artist == NULL) /* only pick the first one */ tuneinfodata.artist = xmlnode_get_data(tuneinfo); } else if (!strcmp(tuneinfo->name, "length")) { if (tuneinfodata.time == -1) { char *length = xmlnode_get_data(tuneinfo); if (length) tuneinfodata.time = strtol(length, NULL, 10); + g_free(length); } } else if (!strcmp(tuneinfo->name, "source")) { - if (tuneinfodata.album[0] == '\0') /* only pick the first one */ + if (tuneinfodata.album == NULL) /* only pick the first one */ tuneinfodata.album = xmlnode_get_data(tuneinfo); } else if (!strcmp(tuneinfo->name, "title")) { - if (tuneinfodata.title[0] == '\0') /* only pick the first one */ + if (tuneinfodata.title == NULL) /* only pick the first one */ tuneinfodata.title = xmlnode_get_data(tuneinfo); } else if (!strcmp(tuneinfo->name, "track")) { - if (tuneinfodata.track[0] == '\0') /* only pick the first one */ + if (tuneinfodata.track == NULL) /* only pick the first one */ tuneinfodata.track = xmlnode_get_data(tuneinfo); } else if (!strcmp(tuneinfo->name, "uri")) { - if (tuneinfodata.url[0] == '\0') /* only pick the first one */ + if (tuneinfodata.url == NULL) /* only pick the first one */ tuneinfodata.url = xmlnode_get_data(tuneinfo); } } } - resource = jabber_buddy_find_resource(buddy, NULL); - if(!resource) - return; /* huh? */ status_id = jabber_buddy_state_get_status_id(resource->state); - purple_prpl_got_user_status(js->gc->account, from, status_id, PURPLE_TUNE_ARTIST, tuneinfodata.artist, PURPLE_TUNE_TITLE, tuneinfodata.title, PURPLE_TUNE_ALBUM, tuneinfodata.album, PURPLE_TUNE_TRACK, tuneinfodata.track, PURPLE_TUNE_TIME, tuneinfodata.time, PURPLE_TUNE_URL, tuneinfodata.url, NULL); + purple_prpl_got_user_status(js->gc->account, from, status_id, + PURPLE_TUNE_ARTIST, tuneinfodata.artist, + PURPLE_TUNE_TITLE, tuneinfodata.title, + PURPLE_TUNE_ALBUM, tuneinfodata.album, + PURPLE_TUNE_TRACK, tuneinfodata.track, + PURPLE_TUNE_TIME, tuneinfodata.time, + PURPLE_TUNE_URL, tuneinfodata.url, NULL); + + g_free(tuneinfodata.artist); + g_free(tuneinfodata.title); + g_free(tuneinfodata.album); + g_free(tuneinfodata.track); + g_free(tuneinfodata.url); } void jabber_tune_init(void) { diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/Makefile.am --- a/libpurple/protocols/msn/Makefile.am Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/Makefile.am Fri Sep 28 20:58:48 2007 +0000 @@ -1,4 +1,6 @@ EXTRA_DIST = \ + directconn.c \ + directconn.h \ Makefile.mingw pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -8,10 +10,10 @@ cmdproc.h \ command.c \ command.h \ + contact.c\ + contact.h\ dialog.c \ dialog.h \ - directconn.c \ - directconn.h \ error.c \ error.h \ group.c \ @@ -30,6 +32,8 @@ notification.h \ object.c \ object.h \ + oim.c\ + oim.h\ page.c \ page.h \ servconn.c \ @@ -46,6 +50,8 @@ slpmsg.h \ slpsession.c \ slpsession.h \ + soap.c\ + soap.h\ state.c \ state.h \ switchboard.c \ @@ -60,8 +66,8 @@ user.h \ userlist.c \ userlist.h \ - msn-utils.c \ - msn-utils.h + msnutils.c \ + msnutils.h AM_CFLAGS = $(st) diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/Makefile.mingw --- a/libpurple/protocols/msn/Makefile.mingw Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/Makefile.mingw Fri Sep 28 20:58:48 2007 +0000 @@ -39,6 +39,7 @@ ## C_SRC = cmdproc.c \ command.c \ + contact.c\ dialog.c \ directconn.c \ error.c \ @@ -50,6 +51,7 @@ nexus.c \ notification.c \ object.c \ + oim.c\ page.c \ servconn.c \ session.c \ @@ -58,6 +60,7 @@ slplink.c \ slpmsg.c \ slpsession.c \ + soap.c\ state.c \ switchboard.c \ sync.c \ @@ -65,7 +68,7 @@ transaction.c \ user.c \ userlist.c \ - msn-utils.c + msnutils.c OBJECTS = $(C_SRC:%.c=%.o) diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/README Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,55 @@ +MSNP14 Implementation +by Ma Yuan + +1. Introduction +------------- + +MSNP14 Protocol, proposed by Windows Live Messenger, is new, and there is no available implementation except the official one on Windows Platform. + +It has introduced many new features attractable to many users, such as: +* Offline Instant Message + You can send the offline Message to the offline User, + The message will be posted to that user the next time when he is online. + +* Communicate with Yahoo User + U can chat with the Yahoo User in MSN, That's Fantastic! Till now , + you can send text/Nudge to Yahoo User. + +* Windows Live ID authentition + WLM use the Window Live ID Authentication process,Known as Passport 3.0, + The procedure is totally different to the previous Passport 2.0 + +* Video/Audio Conversation + U can communicate with other's via Video/Audio. +(Though very interesting, not implemented in this version) + +2.New Features Added +----------------- + +Till now, This project has implemented the following Feature: +* Windows Live ID authentication. + +* Offline Instant Message +Now can send and receive the Offline Instant Message to MSN user and Yahoo User. + +*contact management +Can add/delete Contact +Can add/delete Group + +* Communicate with Yahoo User +Can send/receive Message/Nudge to Yahoo User. + +*. Changes to made to fit MSNP14 Protocol + +3. Reference +------------- + +The very useful sites of MSN Protocol: +MSNpiki site: +reverse engineer of MSN Protocol.up to dated. +http://msnpiki.msnfanatic.com/index.php/MSN_Protocol_Version_13 + +hypothetic site: +old MSN Protocol Introduction,but very useful for basic idea of MSN protocol +http://www.hypothetic.org/docs/msn/index.php + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/cmdproc.c --- a/libpurple/protocols/msn/cmdproc.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Fri Sep 28 20:58:48 2007 +0000 @@ -258,8 +258,10 @@ trans = msn_history_find(cmdproc->history, cmd->trId); if (trans != NULL) - if (trans->timer) + if (trans->timer) { purple_timeout_remove(trans->timer); + trans->timer = 0; + } if (g_ascii_isdigit(cmd->command[0])) { diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/command.c --- a/libpurple/protocols/msn/command.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/command.c Fri Sep 28 20:58:48 2007 +0000 @@ -36,6 +36,50 @@ return TRUE; } +/* + * check the command is the command with payload content + * if it is return TRUE + * else return FALSE + */ +static gboolean +msn_check_payload_cmd(char *str) +{ + if( (!strcmp(str,"ADL")) || + (!strcmp(str,"GCF")) || + (!strcmp(str,"SG")) || + (!strcmp(str,"MSG")) || + (!strcmp(str,"RML")) || + (!strcmp(str,"UBX")) || + (!strcmp(str,"UBN")) || + (!strcmp(str,"UUM")) || + (!strcmp(str,"UBM")) || + (!strcmp(str,"FQY")) || + (!strcmp(str,"UUN")) || + (!strcmp(str,"UUX")) || + (is_num(str))){ + return TRUE; + } + + return FALSE; +} + +/* + * set command Payload length + */ +static void +msn_set_payload_len(MsnCommand *cmd) +{ + char *param; + int len = 0; + + if (msn_check_payload_cmd(cmd->command) && (cmd->param_count > 0)){ + param = cmd->params[cmd->param_count - 1]; + len = is_num(param) ? atoi(param) : 0; + } + + cmd->payload_len = len; +} + MsnCommand * msn_command_from_string(const char *string) { @@ -70,7 +114,13 @@ cmd->trId = is_num(param) ? atoi(param) : 0; } else + { cmd->trId = 0; + } + + /*add payload Length checking*/ + msn_set_payload_len(cmd); + purple_debug_info("MSNP14","get payload len:%d\n",cmd->payload_len); msn_command_ref(cmd); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/contact.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/contact.c Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,1897 @@ +/** + * @file contact.c + * get MSN contacts via SOAP request + * created by MaYuan + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "msn.h" +#include "contact.h" +#include "xmlnode.h" +#include "group.h" + +const char *MsnSoapPartnerScenarioText[] = +{ + "Initial", + "ContactSave", + "MessengerPendingList", + "ContactMsgrAPI", + "BlockUnblock" +}; + +const char *MsnMemberRole[] = +{ + "Forward", + "Allow", + "Block", + "Reverse", + "Pending" +}; + +/* new a contact */ +MsnContact * +msn_contact_new(MsnSession *session) +{ + MsnContact *contact; + + contact = g_new0(MsnContact, 1); + contact->session = session; + contact->soapconn = msn_soap_new(session,contact,1); + + return contact; +} + +/* destroy the contact */ +void +msn_contact_destroy(MsnContact *contact) +{ + msn_soap_destroy(contact->soapconn); + g_free(contact); +} + +MsnCallbackState * +msn_callback_state_new(void) +{ + return g_new0(MsnCallbackState, 1); +} + +void +msn_callback_state_free(MsnCallbackState *state) +{ + if (state == NULL) + return; + + g_free(state->who); + g_free(state->uid); + g_free(state->old_group_name); + g_free(state->new_group_name); + g_free(state->guid); + + g_free(state); +} + +void +msn_callback_state_set_who(MsnCallbackState *state, const gchar *who) +{ + gchar *new_str = NULL; + + g_return_if_fail(state != NULL); + + if (who != NULL) + new_str = g_strdup(who); + + g_free(state->who); + state->who = new_str; +} + +void +msn_callback_state_set_uid(MsnCallbackState *state, const gchar *uid) +{ + gchar *new_str = NULL; + + g_return_if_fail(state != NULL); + + if (uid != NULL) + new_str = g_strdup(uid); + + g_free(state->uid); + state->uid = new_str; +} + +void +msn_callback_state_set_old_group_name(MsnCallbackState *state, const gchar *old_group_name) +{ + gchar *new_str = NULL; + + g_return_if_fail(state != NULL); + + if (old_group_name != NULL) + new_str = g_strdup(old_group_name); + + g_free(state->old_group_name); + state->old_group_name = new_str; +} + +void +msn_callback_state_set_new_group_name(MsnCallbackState *state, const gchar *new_group_name) +{ + gchar *new_str = NULL; + + g_return_if_fail(state != NULL); + + if (new_group_name != NULL) + new_str = g_strdup(new_group_name); + + g_free(state->new_group_name); + state->new_group_name = new_str; +} + +void +msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid) +{ + gchar *new_str = NULL; + + g_return_if_fail(state != NULL); + + if (guid != NULL) + new_str = g_strdup(guid); + + g_free(state->guid); + state->guid = new_str; +} + + +void +msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id) +{ + g_return_if_fail(state != NULL); + + state->list_id = list_id; +} + +void +msn_callback_state_set_action(MsnCallbackState *state, MsnCallbackAction action) +{ + g_return_if_fail(state != NULL); + + state->action |= action; +} + +/*contact SOAP server login error*/ +static void +msn_contact_login_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error) +{ + MsnSession *session; + + session = soapconn->session; + g_return_if_fail(session != NULL); + + msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to contact server")); +} + +/*msn contact SOAP server connect process*/ +static gboolean +msn_contact_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc) +{ + MsnSession * session; + MsnContact *contact; + + contact = soapconn->parent; + g_return_val_if_fail(contact != NULL, TRUE); + + session = contact->session; + g_return_val_if_fail(session != NULL, FALSE); + + /*login ok!We can retrieve the contact list*/ +// msn_get_contact_list(contact, MSN_PS_INITIAL, NULL); + return TRUE; +} + +/*get MSN member role utility*/ +static MsnListId +msn_get_memberrole(char *role) +{ + g_return_val_if_fail(role != NULL, 0); + + if (!strcmp(role,"Allow")) { + return MSN_LIST_AL; + } else if (!strcmp(role,"Block")) { + return MSN_LIST_BL; + } else if (!strcmp(role,"Reverse")) { + return MSN_LIST_RL; + } else if (!strcmp(role,"Pending")) { + return MSN_LIST_PL; + } + return 0; +} + +/*get User Type*/ +static int +msn_get_user_type(char *type) +{ + g_return_val_if_fail(type != NULL, 0); + + if (!strcmp(type,"Regular")) { + return MSN_USER_TYPE_PASSPORT; + } + if (!strcmp(type,"Live")) { + return MSN_USER_TYPE_PASSPORT; + } + if (!strcmp(type,"LivePending")) { + return MSN_USER_TYPE_PASSPORT; + } + + return MSN_USER_TYPE_UNKNOWN; +} + +/* Create the AddressBook in the server, if we don't have one */ +static gboolean +msn_create_address_cb(MsnSoapConn *soapconn) +{ + MsnContact *contact; + + if (soapconn->body == NULL) + return TRUE; + + contact = soapconn->parent; + g_return_val_if_fail(contact != NULL, TRUE); + + purple_debug_info("MSN AddressBook", "Address Book successfully created!\n"); + msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL); + +// msn_soap_free_read_buf(soapconn); + return TRUE; +} + +static void +msn_create_address_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSN AddressBook","AddressBookAdd written\n"); + soapconn->read_cb = msn_create_address_cb; + + return; +} + +static void +msn_create_address_book(MsnContact * contact) +{ + MsnSoapReq *soap_request; + gchar *body; + + g_return_if_fail(contact != NULL); + g_return_if_fail(contact->session != NULL); + g_return_if_fail(contact->session->user != NULL); + g_return_if_fail(contact->session->user->passport != NULL); + + purple_debug_info("MSN AddressBook","Creating an Address Book.\n"); + + body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport); + + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL,MSN_ADD_ADDRESSBOOK_SOAP_ACTION, + body, + NULL, + msn_create_address_cb, + msn_create_address_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn, soap_request); + + g_free(body); + + return; +} + +/*parse contact list*/ +static void +msn_parse_contact_list(MsnContact * contact) +{ + MsnSession * session; + MsnListOp list_op = 0; + MsnListId list; + char * passport, *typedata; + xmlnode *fault, *faultstringnode, *faultdetail, *errorcode; + xmlnode *node, *body, *response, *result, *services; + xmlnode *service, *memberships, *info, *handle, *handletype; + xmlnode *membershipnode, *members, *member, *passportNode; + + session = contact->session; + node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len); + + if (node == NULL) { + purple_debug_error("MSNCL","Unable to parse SOAP data!\n"); + return; + } + + purple_debug_misc("MSNCL","Parsing contact list with size %d\n", contact->soapconn->body_len); + + purple_debug_misc("MSNCL","Root node @ %p: Name: '%s', child: '%s', lastchild: '%s'\n", node, + node->name ? node->name : "(null)", + (node->child && node->child->name) ? node->child->name : "(null)", + (node->lastchild && node->lastchild->name) ? node->lastchild->name : "(null)"); + + body = xmlnode_get_child(node, "Body"); + + if (body == NULL) { + purple_debug_warning("MSNCL", "Failed to parse contact list Body node\n"); + xmlnode_free(node); + return; + } + purple_debug_info("MSNCL","Body @ %p: Name: '%s'\n",body,body->name); + + /* Did we receive a ? */ + if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) { + purple_debug_info("MSNCL","Fault received from SOAP server!\n"); + + if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) { + gchar * faultstring = xmlnode_get_data(faultstringnode); + purple_debug_info("MSNCL", "Faultstring: %s\n", faultstring ? faultstring : "(null)"); + g_free(faultstring); + } + if ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) { + purple_debug_info("MSNCL","detail @ %p, name: %s\n",faultdetail, faultdetail->name); + + if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) { + purple_debug_info("MSNCL","errorcode @ %p, name: %s\n", errorcode, errorcode->name); + + if (errorcode->child != NULL) { + gchar *errorcodestring = xmlnode_get_data(errorcode); + purple_debug_info("MSNCL", "Error Code: %s\n", errorcodestring ? errorcodestring : "(null)"); + + if (errorcodestring && !strncmp(errorcodestring, "ABDoesNotExist", 14) ) { + xmlnode_free(node); + g_free(errorcodestring); + msn_create_address_book(contact); + return; + } + g_free(errorcodestring); + } + } + } + xmlnode_free(node); + msn_get_contact_list(contact, MSN_PS_INITIAL, NULL); + return; + } + + response = xmlnode_get_child(body,"FindMembershipResponse"); + + if (response == NULL) { + /* we may get a response if our cache data is too old: + * + * Need to do full sync. Can't sync deltas Client + * has too old a copy for us to do a delta sync + */ + xmlnode_free(node); + msn_get_contact_list(contact, MSN_PS_INITIAL, NULL); + return; + } + purple_debug_info("MSNCL","FindMembershipResponse @ %p: Name: '%s'\n",response,response->name); + + result = xmlnode_get_child(response,"FindMembershipResult"); + if (result == NULL) { + purple_debug_warning("MSNCL","Received No Update!\n"); + xmlnode_free(node); + return; + } + purple_debug_info("MSNCL","Result @ %p: Name: '%s'\n", result, result->name); + + if ( (services = xmlnode_get_child(result,"Services")) == NULL) { + purple_debug_misc("MSNCL","No received.\n"); + xmlnode_free(node); + return; + } + + purple_debug_info("MSNCL","Services @ %p\n",services); + + for (service = xmlnode_get_child(services, "Service"); service; + service = xmlnode_get_next_twin(service)) { + purple_debug_info("MSNCL","Service @ %p\n",service); + + if ( (info = xmlnode_get_child(service,"Info")) == NULL ) { + purple_debug_error("MSNCL","Error getting 'Info' child node\n"); + continue; + } + if ( (handle = xmlnode_get_child(info,"Handle")) == NULL ) { + purple_debug_error("MSNCL","Error getting 'Handle' child node\n"); + continue; + } + if ( (handletype = xmlnode_get_child(handle,"Type")) == NULL ) { + purple_debug_error("MSNCL","Error getting 'Type' child node\n"); + continue; + } + + if ( (typedata = xmlnode_get_data(handletype)) == NULL) { + purple_debug_error("MSNCL","Error retrieving data from 'Type' child node\n"); + continue; + } + + purple_debug_info("MSNCL","processing '%s' Service\n", typedata); + + if ( !g_strcasecmp(typedata, "Profile") ) { + /* Process Windows Live 'Messenger Roaming Identity' */ + g_free(typedata); + continue; + } + + if ( !g_strcasecmp(typedata, "Messenger") ) { + char *LastChangeStr = NULL; + xmlnode *LastChangeNode; + + /*Last Change Node*/ + if ((LastChangeNode = xmlnode_get_child(service, "LastChange"))) + LastChangeStr = xmlnode_get_data(LastChangeNode); + purple_debug_info("MSNCL","LastChangeNode: '%s'\n",LastChangeStr ? LastChangeStr : "(null)"); + purple_account_set_string(session->account, "CLLastChange", LastChangeStr); + g_free(LastChangeStr); + + memberships = xmlnode_get_child(service,"Memberships"); + if (memberships == NULL) { + purple_debug_warning("MSNCL","Memberships = NULL, cleaning up and returning.\n"); + g_free(typedata); + xmlnode_free(node); + return; + } + purple_debug_info("MSNCL","Memberships @ %p: Name: '%s'\n",memberships,memberships->name); + for (membershipnode = xmlnode_get_child(memberships, "Membership"); membershipnode; + membershipnode = xmlnode_get_next_twin(membershipnode)){ + xmlnode *roleNode; + char *role = NULL; + list = 0; + + if ((roleNode = xmlnode_get_child(membershipnode,"MemberRole"))) { + role = xmlnode_get_data(roleNode); + list = msn_get_memberrole(role); + } + list_op = 1 << list; + + purple_debug_info("MSNCL","MemberRole role: %s, list_op: %d\n", role ? role : "(null)", list_op); + + g_free(role); + + members = xmlnode_get_child(membershipnode, "Members"); + for (member = xmlnode_get_child(members, "Member"); member; + member = xmlnode_get_next_twin(member)){ + MsnUser *user = NULL; + xmlnode *typeNode, *membershipIdNode = NULL; + gchar *type, *membershipId = NULL; + const char *member_type = xmlnode_get_attrib(member, "type"); + + if (!member_type) { + purple_debug_error("msn", "No Member Type specified for Member.\n"); + continue; + } + + if(!g_strcasecmp(member_type, "PassportMember") ) { + passport = type = NULL; + if ((passportNode = xmlnode_get_child(member, "PassportName"))) + passport = xmlnode_get_data(passportNode); + if ((typeNode = xmlnode_get_child(member, "Type"))) + type = xmlnode_get_data(typeNode); + purple_debug_info("MSNCL","Passport name: '%s', Type: %s\n", passport ? passport : "(null)", type ? type : "(null)"); + /* Why do we even bother parsing it just to free it??? */ + g_free(type); + + user = msn_userlist_find_add_user(session->userlist,passport,NULL); + g_free(passport); + + membershipIdNode = xmlnode_get_child(member,"MembershipId"); + if (membershipIdNode != NULL) { + membershipId = xmlnode_get_data(membershipIdNode); + if (membershipId != NULL) { + user->membership_id[list] = atoi(membershipId); + g_free(membershipId); + } + } + + msn_got_lst_user(session, user, list_op, NULL); + } + else if (!g_strcasecmp(member_type, "PhoneMember")) { + purple_debug_info("msn", "Recieved Phone Member; ignoring.\n"); + } + else if (!g_strcasecmp(member_type, "EmailMember")) { + xmlnode *emailNode; + passport = NULL; + + if ((emailNode = xmlnode_get_child(member, "Email"))) + passport = xmlnode_get_data(emailNode); + purple_debug_info("MSNCL","Email Member: Name: '%s', list_op: %d\n", passport ? passport : "(null)", list_op); + + user = msn_userlist_find_add_user(session->userlist, passport, NULL); + g_free(passport); + + membershipIdNode = xmlnode_get_child(member,"MembershipId"); + if (membershipIdNode != NULL) { + membershipId = xmlnode_get_data(membershipIdNode); + if (membershipId != NULL) { + user->membership_id[list] = atoi(membershipId); + g_free(membershipId); + } + } + + msn_got_lst_user(session, user, list_op, NULL); + } else { + purple_debug_info("msn", "Unknown Member type: %s\n", member_type); + } + } + } + g_free(typedata); /* Free 'Type' node data after processing 'Messenger' Service */ + } + } + + xmlnode_free(node); /* Free the whole XML tree */ +} + +static gboolean +msn_get_contact_list_cb(MsnSoapConn *soapconn) +{ + MsnContact *contact; + MsnSession *session; + const char *abLastChange; + const char *dynamicItemLastChange; + gchar *partner_scenario; + + if (soapconn->body == NULL) + return TRUE; + + purple_debug_misc("MSNCL","Got the contact list!\n"); + + contact = soapconn->parent; + g_return_val_if_fail(contact != NULL, TRUE); + session = soapconn->session; + g_return_val_if_fail(session != NULL, FALSE); + g_return_val_if_fail(soapconn->data_cb != NULL, TRUE); + + partner_scenario = soapconn->data_cb; + + msn_parse_contact_list(contact); + /*free the read buffer*/ + msn_soap_free_read_buf(soapconn); + + abLastChange = purple_account_get_string(session->account, "ablastChange", NULL); + dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL); + + if (!strcmp(partner_scenario, MsnSoapPartnerScenarioText[MSN_PS_INITIAL])) { + +#ifdef MSN_PARTIAL_LISTS + /* XXX: this should be enabled when we can correctly do partial + syncs with the server. Currently we need to retrieve the whole + list to detect sync issues */ + msn_get_address_book(contact, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange); +#else + msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL); +#endif + } else { + msn_soap_free_read_buf(soapconn); + } + + return TRUE; +} + +static void +msn_get_contact_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_misc("MSNCL","Sent SOAP request for the contact list.\n"); + soapconn->read_cb = msn_get_contact_list_cb; +} + +/* SOAP get contact list*/ +void +msn_get_contact_list(MsnContact * contact, const MsnSoapPartnerScenario partner_scenario, const char *update_time) +{ + MsnSoapReq *soap_request; + gchar *body; + gchar *update_str = NULL; + const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario]; + + purple_debug_misc("MSNCL","Getting Contact List.\n"); + + if ( update_time != NULL ) { + purple_debug_info("MSNCL","Last update time: %s\n",update_time); + update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML,update_time); + } + + body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, update_str ? update_str : ""); + g_free(update_str); + + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_GET_CONTACT_POST_URL, + MSN_GET_CONTACT_SOAP_ACTION, + body, + (gpointer) partner_scenario_str, + msn_get_contact_list_cb, + msn_get_contact_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn,soap_request); + g_free(body); +} + +static void +msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node) +{ + MsnSession *session = contact->session; + xmlnode *group; + + purple_debug_info("MsnAb","msn_parse_addressbook_groups()\n"); + + for(group = xmlnode_get_child(node, "Group"); group; + group = xmlnode_get_next_twin(group)){ + xmlnode *groupId, *groupInfo, *groupname; + char *group_id = NULL, *group_name = NULL; + + if ((groupId = xmlnode_get_child(group, "groupId"))) + group_id = xmlnode_get_data(groupId); + if ((groupInfo = xmlnode_get_child(group, "groupInfo")) && (groupname = xmlnode_get_child(groupInfo, "name"))) + group_name = xmlnode_get_data(groupname); + + msn_group_new(session->userlist, group_id, group_name); + + if (group_id == NULL){ + /* Group of ungroupped buddies */ + g_free(group_name); + continue; + } + + purple_debug_info("MsnAB","group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)"); + if ((purple_find_group(group_name)) == NULL){ + PurpleGroup *g = purple_group_new(group_name); + purple_blist_add_group(g, NULL); + } + g_free(group_id); + g_free(group_name); + } +} + +static void +msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node) +{ + MsnSession *session = contact->session; + xmlnode *contactNode; + char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL; + + for(contactNode = xmlnode_get_child(node, "Contact"); contactNode; + contactNode = xmlnode_get_next_twin(contactNode)) { + xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds; + MsnUser *user; + MsnUserType usertype; + + if (!(contactId = xmlnode_get_child(contactNode,"contactId")) + || !(contactInfo = xmlnode_get_child(contactNode, "contactInfo")) + || !(contactType = xmlnode_get_child(contactInfo, "contactType"))) + continue; + + g_free(passport); + g_free(Name); + g_free(uid); + g_free(type); + passport = Name = uid = type = NULL; + + uid = xmlnode_get_data(contactId); + type = xmlnode_get_data(contactType); + + /*setup the Display Name*/ + if (type && !strcmp(type, "Me")){ + char *friendly = NULL; + if ((displayName = xmlnode_get_child(contactInfo, "displayName"))) + friendly = xmlnode_get_data(displayName); + purple_connection_set_display_name(session->account->gc, friendly ? purple_url_decode(friendly) : NULL); + g_free(friendly); + continue; /* Not adding own account as buddy to buddylist */ + } + + usertype = msn_get_user_type(type); + passportName = xmlnode_get_child(contactInfo, "passportName"); + if (passportName == NULL) { + xmlnode *emailsNode, *contactEmailNode, *emailNode; + xmlnode *messengerEnabledNode; + char *msnEnabled; + + /*TODO: add it to the none-instant Messenger group and recognize as email Membership*/ + /*Yahoo User?*/ + emailsNode = xmlnode_get_child(contactInfo, "emails"); + if (emailsNode == NULL) { + /*TODO: need to support the Mobile type*/ + continue; + } + for(contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode; + contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){ + if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) { + /* XXX: Should this be a continue instead of a break? It seems like it'd cause unpredictable results otherwise. */ + break; + } + + msnEnabled = xmlnode_get_data(messengerEnabledNode); + + if ((emailNode = xmlnode_get_child(contactEmailNode, "email"))) { + g_free(passport); + passport = xmlnode_get_data(emailNode); + } + + if(msnEnabled && !strcmp(msnEnabled, "true")) { + /*Messenger enabled, Get the Passport*/ + purple_debug_info("MsnAB", "Yahoo User %s\n", passport ? passport : "(null)"); + usertype = MSN_USER_TYPE_YAHOO; + g_free(msnEnabled); + break; + } else { + /*TODO maybe we can just ignore it in Purple?*/ + purple_debug_info("MSNAB", "Other type user\n"); + } + + g_free(msnEnabled); + } + } else { + passport = xmlnode_get_data(passportName); + } + + if (passport == NULL) + continue; + + if ((displayName = xmlnode_get_child(contactInfo, "displayName"))) + Name = xmlnode_get_data(displayName); + else + Name = g_strdup(passport); + + purple_debug_misc("MsnAB","passport:{%s} uid:{%s} display:{%s}\n", + passport, uid ? uid : "(null)", Name ? Name : "(null)"); + + user = msn_userlist_find_add_user(session->userlist, passport, Name); + msn_user_set_uid(user, uid); + msn_user_set_type(user, usertype); + + groupIds = xmlnode_get_child(contactInfo, "groupIds"); + if (groupIds) { + for (guid = xmlnode_get_child(groupIds, "guid"); guid; + guid = xmlnode_get_next_twin(guid)){ + char *group_id = xmlnode_get_data(guid); + msn_user_add_group_id(user, group_id); + purple_debug_misc("MsnAB", "guid:%s\n", group_id ? group_id : "(null)"); + g_free(group_id); + } + } else { + purple_debug_info("msn", "User not in any groups, adding to default group.\n"); + /*not in any group,Then set default group*/ + msn_user_add_group_id(user, MSN_INDIVIDUALS_GROUP_ID); + } + + msn_got_lst_user(session, user, MSN_LIST_FL_OP, NULL); + } + + g_free(passport); + g_free(Name); + g_free(uid); + g_free(type); +} + +static gboolean +msn_parse_addressbook(MsnContact * contact) +{ + MsnSession *session; + xmlnode * node,*body,*response,*result; + xmlnode *groups; + xmlnode *contacts; + xmlnode *abNode; + xmlnode *fault, *faultstringnode, *faultdetail, *errorcode; + + session = contact->session; + + node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len); + if ( node == NULL ) { + purple_debug_error("MSN AddressBook","Error parsing Address Book with size %d\n", contact->soapconn->body_len); + return FALSE; + } + + purple_debug_misc("MSN AddressBook", "Parsing Address Book with size %d\n", contact->soapconn->body_len); + + purple_debug_misc("MSN AddressBook","node{%p},name:%s,child:%s,last:%s\n", node, + node->name ? node->name : "(null)", + (node->child && node->child->name) ? node->child->name : "(null)", + (node->lastchild && node->lastchild->name) ? node->lastchild->name : "(null)"); + + body = xmlnode_get_child(node,"Body"); + purple_debug_misc("MSN AddressBook","body{%p},name:%s\n",body,body->name); + + /* TODO: This appears to be used in a number of places and should be de-duplicated */ + if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) { + purple_debug_info("MSN AddressBook","Fault received from SOAP server!\n"); + + if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) { + gchar *faultstring = xmlnode_get_data(faultstringnode); + purple_debug_info("MSN AddressBook","Faultstring: %s\n", faultstring ? faultstring : "(null)"); + g_free(faultstring); + } + if ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) { + purple_debug_info("MSN AddressBook","detail @ %p, name: %s\n",faultdetail, faultdetail->name); + + if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) { + gchar *errorcodestring; + purple_debug_info("MSN AddressBook","errorcode @ %p, name: %s\n",errorcode, errorcode->name); + + errorcodestring = xmlnode_get_data(errorcode); + purple_debug_info("MSN AddressBook", "Error Code: %s\n", errorcodestring ? errorcodestring : "(null)"); + + if (errorcodestring && !strncmp(errorcodestring, "ABDoesNotExist", 14) ) { + g_free(errorcodestring); + xmlnode_free(node); + return TRUE; + } + g_free(errorcodestring); + } + } + xmlnode_free(node); + return FALSE; + } + + + response = xmlnode_get_child(body,"ABFindAllResponse"); + + if (response == NULL) { + xmlnode_free(node); + return FALSE; + } + + purple_debug_misc("MSN SOAP","response{%p},name:%s\n",response,response->name); + result = xmlnode_get_child(response,"ABFindAllResult"); + if(result == NULL){ + purple_debug_misc("MSNAB","receive no address book update\n"); + xmlnode_free(node); + return TRUE; + } + purple_debug_info("MSN SOAP","result{%p},name:%s\n",result,result->name); + + /*Process Group List*/ + groups = xmlnode_get_child(result,"groups"); + if (groups != NULL) { + msn_parse_addressbook_groups(contact, groups); + } + + /*add a default No group to set up the no group Membership*/ + msn_group_new(session->userlist, MSN_INDIVIDUALS_GROUP_ID, + MSN_INDIVIDUALS_GROUP_NAME); + purple_debug_misc("MsnAB","group_id:%s name:%s\n", + MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME); + if ((purple_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){ + PurpleGroup *g = purple_group_new(MSN_INDIVIDUALS_GROUP_NAME); + purple_blist_add_group(g, NULL); + } + + /*add a default No group to set up the no group Membership*/ + msn_group_new(session->userlist, MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME); + purple_debug_misc("MsnAB","group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME); + if ((purple_find_group(MSN_NON_IM_GROUP_NAME)) == NULL){ + PurpleGroup *g = purple_group_new(MSN_NON_IM_GROUP_NAME); + purple_blist_add_group(g, NULL); + } + + /*Process contact List*/ + purple_debug_info("MSNAB","process contact list...\n"); + contacts =xmlnode_get_child(result,"contacts"); + if (contacts != NULL) { + msn_parse_addressbook_contacts(contact, contacts); + } + + abNode =xmlnode_get_child(result,"ab"); + if(abNode != NULL){ + xmlnode *node2; + char *tmp = NULL; + + if ((node2 = xmlnode_get_child(abNode, "lastChange"))) + tmp = xmlnode_get_data(node2); + purple_debug_info("MsnAB"," lastchanged Time:{%s}\n", tmp ? tmp : "(null)"); + purple_account_set_string(session->account, "ablastChange", tmp); + + g_free(tmp); tmp = NULL; + if ((node2 = xmlnode_get_child(abNode, "DynamicItemLastChanged"))) + tmp = xmlnode_get_data(node2); + purple_debug_info("MsnAB"," DynamicItemLastChanged :{%s}\n", tmp ? tmp : "(null)"); + purple_account_set_string(session->account, "DynamicItemLastChanged", tmp); + g_free(tmp); + } + + xmlnode_free(node); + msn_soap_free_read_buf(contact->soapconn); + return TRUE; +} + +static gboolean +msn_get_address_cb(MsnSoapConn *soapconn) +{ + MsnContact *contact; + MsnSession *session; + + if (soapconn->body == NULL) + return TRUE; + + contact = soapconn->parent; + g_return_val_if_fail(contact != NULL, TRUE); + session = soapconn->session; + g_return_val_if_fail(session != NULL, FALSE); + + purple_debug_misc("MSN AddressBook", "Got the Address Book!\n"); + + if ( msn_parse_addressbook(contact) ) { + //msn_soap_free_read_buf(soapconn); + + if (!session->logged_in) { + msn_send_privacy(session->account->gc); + msn_notification_dump_contact(session); + } + + /*free the read buffer*/ + msn_soap_free_read_buf(soapconn); + return TRUE; + } else { + /* This is making us loop infinitely when we fail to parse the address book, + disable for now (we should re-enable when we send timestamps) + */ + /* + msn_get_address_book(contact, NULL, NULL); + */ + msn_session_disconnect(session); + purple_connection_error(session->account->gc, _("Unable to retrieve MSN Address Book")); + return FALSE; + } +} + +/**/ +static void +msn_address_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_misc("MSN AddressBook","Sent SOAP request for the Address Book.\n"); + soapconn->read_cb = msn_get_address_cb; +} + +/*get the address book*/ +void +msn_get_address_book(MsnContact *contact, const MsnSoapPartnerScenario partner_scenario, const char *LastChanged, const char *dynamicItemLastChange) +{ + MsnSoapReq *soap_request; + char *body; + char *update_str = NULL; + + purple_debug_misc("MSN AddressBook","Getting Address Book\n"); + + /*build SOAP and POST it*/ + if (dynamicItemLastChange != NULL) + update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, dynamicItemLastChange); + else if (LastChanged != NULL) + update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, LastChanged); + + + body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], update_str ? update_str : ""); + g_free(update_str); + + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL,MSN_GET_ADDRESS_SOAP_ACTION, + body, + NULL, + msn_get_address_cb, + msn_address_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn,soap_request); + g_free(body); +} + +static gboolean +msn_add_contact_read_cb(MsnSoapConn *soapconn) +{ + MsnCallbackState *state = NULL; + MsnUserList *userlist; + MsnUser *user; + + g_return_val_if_fail(soapconn->data_cb != NULL, TRUE); + g_return_val_if_fail(soapconn->session != NULL, FALSE); + g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE); + + state = (MsnCallbackState *) soapconn->data_cb; + + if (soapconn->body == NULL) { + msn_callback_state_free(state); + return TRUE; + } + + userlist = soapconn->session->userlist; + + purple_debug_info("MSNCL","Contact added successfully\n"); + + // the code this block is replacing didn't send ADL for yahoo contacts, + // but i haven't confirmed this is WLM's behaviour wrt yahoo contacts + + if ( !msn_user_is_yahoo(soapconn->session->account, state->who) ) { + + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); + } + msn_notification_send_fqy(soapconn->session, state->who); + + user = msn_userlist_find_add_user(userlist, state->who, state->who); + msn_user_add_group_id(user, state->guid); + + if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) { + msn_del_contact_from_list(soapconn->session->contact, NULL, state->who, MSN_LIST_PL); + } else { + msn_soap_free_read_buf(soapconn); + } + + msn_callback_state_free(state); + + return TRUE; +} + +static void +msn_add_contact_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSNCL","Add contact request written\n"); + soapconn->read_cb = msn_add_contact_read_cb; +} + +/* add a Contact in MSN_INDIVIDUALS_GROUP */ +void +msn_add_contact(MsnContact *contact, MsnCallbackState *state, const char *passport) +{ + MsnSoapReq *soap_request; + gchar *body = NULL; + gchar *contact_xml = NULL; + + g_return_if_fail(passport != NULL); +/* gchar *escaped_displayname; + + + if (displayname != NULL) { + escaped_displayname = g_markup_decode_text(displayname, -1); + } else { + escaped_displayname = passport; + } + contact_xml = g_strdup_printf(MSN_XML_ADD_CONTACT, escaped_displayname, passport); +*/ + purple_debug_info("MSNCL","Adding contact %s to contact list\n", passport); + +// if ( !strcmp(state->guid, MSN_INDIVIDUALS_GROUP_ID) ) { + contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport); +// } + body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml); + + g_free(contact_xml); + + /*build SOAP and POST it*/ + + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_CONTACT_ADD_SOAP_ACTION, + body, + state, + msn_add_contact_read_cb, + msn_add_contact_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn,soap_request); + + g_free(body); +} + +static gboolean +msn_add_contact_to_group_read_cb(MsnSoapConn *soapconn) +{ + MsnCallbackState *state; + MsnUserList *userlist; + + g_return_val_if_fail(soapconn->data_cb != NULL, TRUE); + g_return_val_if_fail(soapconn->session != NULL, FALSE); + g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE); + + userlist = soapconn->session->userlist; + + state = (MsnCallbackState *) soapconn->data_cb; + + if (soapconn->body == NULL) { + msn_callback_state_free(state); + return TRUE; + } + + if (msn_userlist_add_buddy_to_group(userlist, state->who, state->new_group_name) == TRUE) { + purple_debug_info("MSNCL", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name); + } else { + purple_debug_info("MSNCL","Contact %s added to group %s successfully on server, but failed in the local list\n", state->who, state->new_group_name); + } + + if (state->action & MSN_ADD_BUDDY) { + MsnUser *user = msn_userlist_find_user(userlist, state->who); + + if ( !msn_user_is_yahoo(soapconn->session->account, state->who) ) { + + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); + } + msn_notification_send_fqy(soapconn->session, state->who); + + if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) { + msn_del_contact_from_list(soapconn->session->contact, NULL, state->who, MSN_LIST_PL); + msn_callback_state_free(state); + return TRUE; + } + } + + if (state->action & MSN_MOVE_BUDDY) { + msn_del_contact_from_group(soapconn->session->contact, state->who, state->old_group_name); + } else { + msn_callback_state_free(state); + msn_soap_free_read_buf(soapconn); + } + return TRUE; +} + +static void +msn_add_contact_to_group_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSNCL","Add contact to group request sent!\n"); + soapconn->read_cb = msn_add_contact_to_group_read_cb; +} + +void +msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, + const char *passport, const char *groupId) +{ + MsnSoapReq *soap_request; + MsnUserList *userlist; + MsnUser *user; + gchar *body = NULL, *contact_xml; + + g_return_if_fail(passport != NULL); + g_return_if_fail(groupId != NULL); + + g_return_if_fail(contact != NULL); + g_return_if_fail(contact->session != NULL); + g_return_if_fail(contact->session->userlist != NULL); + + userlist = contact->session->userlist; + + if (!strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) { + + user = msn_userlist_find_add_user(userlist, passport, passport); + + if (state->action & MSN_ADD_BUDDY) { + msn_add_contact(contact, state, passport); + return; + } + + if (state->action & MSN_MOVE_BUDDY) { + msn_user_add_group_id(user, groupId); + msn_del_contact_from_group(contact, passport, state->old_group_name); + } else { + msn_callback_state_free(state); + } + + return; + } + + + purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport, + msn_userlist_find_group_name(userlist, groupId)); + + user = msn_userlist_find_user(userlist, passport); + if (user == NULL) { + purple_debug_warning("MSN CL", "Unable to retrieve user %s from the userlist!\n", passport); + } + + if (user->uid != NULL) { + contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid); + } else { + contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport); + } + + body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml); + g_free(contact_xml); + + /*build SOAP and POST it*/ + + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_ADD_CONTACT_GROUP_SOAP_ACTION, + body, + state, + msn_add_contact_to_group_read_cb, + msn_add_contact_to_group_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn,soap_request); + + g_free(body); +} + + + +static gboolean +msn_delete_contact_read_cb(MsnSoapConn *soapconn) +{ + MsnUser *user; + MsnCallbackState *state = (MsnCallbackState *) soapconn->data_cb; + MsnUserList *userlist; + + g_return_val_if_fail(soapconn->session != NULL, FALSE); + g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE); + + userlist = soapconn->session->userlist; + + if (soapconn->body == NULL) { + msn_callback_state_free(state); + return TRUE; + } + + purple_debug_info("MSNCL","Delete contact successful\n"); + + user = msn_userlist_find_user_with_id(userlist, state->uid); + if (user != NULL) { + msn_userlist_remove_user(userlist, user); + } + + msn_callback_state_free(state); + msn_soap_free_read_buf(soapconn); + + return TRUE; +} + +static void +msn_delete_contact_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSNCL","Delete contact request written\n"); + soapconn->read_cb = msn_delete_contact_read_cb; +} + +/*delete a Contact*/ +void +msn_delete_contact(MsnContact *contact, const char *contactId) +{ + gchar *body = NULL; + gchar *contact_id_xml = NULL ; + MsnSoapReq *soap_request; + MsnCallbackState *state; + + g_return_if_fail(contactId != NULL); + contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, contactId); + + state = msn_callback_state_new(); + msn_callback_state_set_uid(state, contactId); + + /* build SOAP request */ + purple_debug_info("MSNCL","Deleting contact with contactId: %s\n", contactId); + body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml); + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_CONTACT_DEL_SOAP_ACTION, + body, + state, + msn_delete_contact_read_cb, + msn_delete_contact_written_cb, + msn_contact_connect_init); + + g_free(contact_id_xml); + + /* POST the SOAP request */ + msn_soap_post(contact->soapconn, soap_request); + + g_free(body); +} + +static gboolean +msn_del_contact_from_group_read_cb(MsnSoapConn *soapconn) +{ + MsnCallbackState *state = (MsnCallbackState *) soapconn->data_cb; + + if (soapconn->body == NULL) { + msn_callback_state_free(state); + return TRUE; + } + + if (msn_userlist_rem_buddy_from_group(soapconn->session->userlist, state->who, state->old_group_name)) { + purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name); + } else { + purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name); + } + + msn_callback_state_free(state); + msn_soap_free_read_buf(soapconn); + + return TRUE; +} + +static void +msn_del_contact_from_group_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSN CL","Del contact from group request sent!\n"); + soapconn->read_cb = msn_del_contact_from_group_read_cb; +} + +void +msn_del_contact_from_group(MsnContact *contact, const char *passport, const char *group_name) +{ + MsnSoapReq *soap_request; + MsnUserList * userlist; + MsnUser *user; + MsnCallbackState *state; + gchar *body = NULL, *contact_id_xml; + const gchar *groupId; + + g_return_if_fail(passport != NULL); + g_return_if_fail(group_name != NULL); + g_return_if_fail(contact != NULL); + g_return_if_fail(contact->session != NULL); + g_return_if_fail(contact->session->userlist != NULL); + + userlist = contact->session->userlist; + + groupId = msn_userlist_find_group_id(userlist, group_name); + if (groupId != NULL) { + purple_debug_info("MSN CL", "Deleting user %s from group %s\n", passport, group_name); + } else { + purple_debug_warning("MSN CL", "Unable to retrieve group id from group %s !\n", group_name); + return; + } + + user = msn_userlist_find_user(userlist, passport); + + if (user == NULL) { + purple_debug_warning("MSN CL", "Unable to retrieve user from passport %s!\n", passport); + return; + } + + if ( !strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) { + msn_user_remove_group_id(user, groupId); + return; + } + + state = msn_callback_state_new(); + msn_callback_state_set_who(state, passport); + msn_callback_state_set_guid(state, groupId); + msn_callback_state_set_old_group_name(state, group_name); + + contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid); + body = g_strdup_printf(MSN_CONTACT_DEL_GROUP_TEMPLATE, contact_id_xml, groupId); + g_free(contact_id_xml); + + /*build SOAP and POST it*/ + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_CONTACT_DEL_GROUP_SOAP_ACTION, + body, + state, + msn_del_contact_from_group_read_cb, + msn_del_contact_from_group_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn,soap_request); + + g_free(body); +} + + +static gboolean +msn_update_contact_read_cb(MsnSoapConn *soapconn) +{ + if (soapconn->body == NULL) + return TRUE; + + purple_debug_info("MSN CL","Contact updated successfully\n"); + + return TRUE; +} + +static void +msn_update_contact_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSN CL","Update contact information request sent\n"); + soapconn->read_cb = msn_update_contact_read_cb; +} + +/* Update a contact's nickname */ + +void +msn_update_contact(MsnContact *contact, const char* nickname) +{ + MsnSoapReq *soap_request; + gchar *body, *escaped_nickname; + + /* I'm not sure this is right, but if it isn't, the rest of this function will need to be fixed */ + g_return_if_fail(nickname != NULL); + + purple_debug_info("MSN CL","Update contact information with new friendly name: %s\n", nickname); + + escaped_nickname = g_markup_escape_text(nickname, -1); + + body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname); + + g_free(escaped_nickname); + /*build SOAP and POST it*/ + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_CONTACT_UPDATE_SOAP_ACTION, + body, + NULL, + msn_update_contact_read_cb, + msn_update_contact_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn, soap_request); + + g_free(body); +} + + +static gboolean +msn_del_contact_from_list_read_cb(MsnSoapConn *soapconn) +{ + MsnCallbackState *state = NULL; + + g_return_val_if_fail(soapconn->data_cb != NULL, TRUE); + g_return_val_if_fail(soapconn->session != NULL, FALSE); + g_return_val_if_fail(soapconn->session->contact != NULL, FALSE); + + state = (MsnCallbackState *) soapconn->data_cb; + + if (soapconn->body == NULL) { + msn_callback_state_free(state); + return TRUE; + } + + purple_debug_info("MSN CL", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]); + + if (state->list_id == MSN_LIST_PL) { + msn_add_contact_to_list(soapconn->session->contact, state, state->who, MSN_LIST_RL); + return TRUE; + } + + if (state->list_id == MSN_LIST_AL) { + purple_privacy_permit_remove(soapconn->session->account, state->who, TRUE); + msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL); + msn_callback_state_free(state); + return TRUE; + } + + if (state->list_id == MSN_LIST_BL) { + purple_privacy_deny_remove(soapconn->session->account, state->who, TRUE); + msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_AL); + msn_callback_state_free(state); + return TRUE; + } + + msn_callback_state_free(state); + msn_soap_free_read_buf(soapconn); + + return TRUE; +} + +static void +msn_del_contact_from_list_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSN CL","Delete contact from list SOAP request sent!\n"); + soapconn->read_cb = msn_del_contact_from_list_read_cb; +} + +void +msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state, + const gchar *passport, const MsnListId list) +{ + MsnSoapReq *soap_request; + gchar *body = NULL, *member = NULL; + MsnSoapPartnerScenario partner_scenario; + MsnUser *user; + + g_return_if_fail(contact != NULL); + g_return_if_fail(passport != NULL); + g_return_if_fail(list < 5); + + purple_debug_info("MSN CL", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]); + + if (state == NULL) { + state = msn_callback_state_new(); + } + msn_callback_state_set_list_id(state, list); + msn_callback_state_set_who(state, passport); + + if (list == MSN_LIST_PL) { + g_return_if_fail(contact->session != NULL); + g_return_if_fail(contact->session->userlist != NULL); + + user = msn_userlist_find_user(contact->session->userlist, passport); + + partner_scenario = MSN_PS_CONTACT_API; + member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]); + } else { + /* list == MSN_LIST_AL || list == MSN_LIST_BL */ + partner_scenario = MSN_PS_BLOCK_UNBLOCK; + + member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport); + } + + body = g_strdup_printf( MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE, + MsnSoapPartnerScenarioText[partner_scenario], + MsnMemberRole[list], + member); + g_free(member); + + soap_request = msn_soap_request_new( MSN_CONTACT_SERVER, + MSN_SHARE_POST_URL, + MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION, + body, + state, + msn_del_contact_from_list_read_cb, + msn_del_contact_from_list_written_cb, + msn_contact_connect_init); + + msn_soap_post(contact->soapconn,soap_request); + + g_free(body); +} + +static gboolean +msn_add_contact_to_list_read_cb(MsnSoapConn *soapconn) +{ + MsnCallbackState *state = NULL; + + g_return_val_if_fail(soapconn->data_cb != NULL, TRUE); + + state = (MsnCallbackState *) soapconn->data_cb; + + if (soapconn->body == NULL) { + msn_callback_state_free(state); + return TRUE; + } + + purple_debug_info("MSN CL", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]); + + if (state->list_id == MSN_LIST_RL && (state->action & MSN_DENIED_BUDDY) ) { + g_return_val_if_fail(soapconn->session != NULL, FALSE); + g_return_val_if_fail(soapconn->session->contact != NULL, FALSE); + + msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL); + return TRUE; + } + + if (state->list_id == MSN_LIST_AL) { + purple_privacy_permit_add(soapconn->session->account, state->who, TRUE); + } else if (state->list_id == MSN_LIST_BL) { + purple_privacy_deny_add(soapconn->session->account, state->who, TRUE); + } + + msn_callback_state_free(state); + msn_soap_free_read_buf(soapconn); + return TRUE; +} + + +static void +msn_add_contact_to_list_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSN CL","Add contact to list SOAP request sent!\n"); + soapconn->read_cb = msn_add_contact_to_list_read_cb; +} + +void +msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state, + const gchar *passport, const MsnListId list) +{ + MsnSoapReq *soap_request; + gchar *body = NULL, *member = NULL; + MsnSoapPartnerScenario partner_scenario; + + g_return_if_fail(contact != NULL); + g_return_if_fail(passport != NULL); + g_return_if_fail(list < 5); + + purple_debug_info("MSN CL", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]); + + if (state == NULL) { + state = msn_callback_state_new(); + } + msn_callback_state_set_list_id(state, list); + msn_callback_state_set_who(state, passport); + + partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK; + + member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport); + + body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE, + MsnSoapPartnerScenarioText[partner_scenario], + MsnMemberRole[list], + member); + + g_free(member); + + soap_request = msn_soap_request_new( MSN_CONTACT_SERVER, + MSN_SHARE_POST_URL, + MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION, + body, + state, + msn_add_contact_to_list_read_cb, + msn_add_contact_to_list_written_cb, + msn_contact_connect_init); + + msn_soap_post(contact->soapconn, soap_request); + + g_free(body); +} + + +#if 0 +static gboolean +msn_gleams_read_cb(MsnSoapConn * soapconn) +{ + purple_debug_info("MSN CL","Gleams read done\n"); + return TRUE; +} + +static void +msn_gleams_written_cb(MsnSoapConn * soapconn) +{ + purple_debug_info("MSNP14","finish Group written\n"); + soapconn->read_cb = msn_gleams_read_cb; +// msn_soap_read_cb(data,source,cond); +} + +/*get the gleams info*/ +void +msn_get_gleams(MsnContact *contact) +{ + MsnSoapReq *soap_request; + + purple_debug_info("MSNP14","msn get gleams info...\n"); + /*build SOAP and POST it*/ + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_GET_GLEAMS_SOAP_ACTION, + MSN_GLEAMS_TEMPLATE, + NULL, + msn_gleams_read_cb, + msn_gleams_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn,soap_request); +} +#endif + + +/*************************************************************** + * Group Operations + ***************************************************************/ + +static gboolean +msn_group_read_cb(MsnSoapConn *soapconn) +{ + MsnUserList *userlist; + MsnCallbackState *state = NULL; + + purple_debug_info("MSN CL", "Group request successful.\n"); + + g_return_val_if_fail(soapconn->session != NULL, FALSE); + g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE); + g_return_val_if_fail(soapconn->session->contact != NULL, FALSE); + + state = (MsnCallbackState *) soapconn->data_cb; + + if (soapconn->body == NULL) { + msn_callback_state_free(state); + return TRUE; + } + + if (state) { + userlist = soapconn->session->userlist; + + if (state->action & MSN_RENAME_GROUP) { + msn_userlist_rename_group_id(soapconn->session->userlist, + state->guid, + state->new_group_name); + } + + if (state->action & MSN_ADD_GROUP) { + gchar *guid, *endguid; + + guid = g_strstr_len(soapconn->read_buf, soapconn->read_len, ""); + guid += 6; + endguid = g_strstr_len(soapconn->read_buf, soapconn->read_len, ""); + *endguid = '\0'; + /* create and add the new group to the userlist */ + purple_debug_info("MSN CL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid); + msn_group_new(soapconn->session->userlist, guid, state->new_group_name); + + if (state->action & MSN_ADD_BUDDY) { + msn_userlist_add_buddy(soapconn->session->userlist, + state->who, + state->new_group_name); + msn_callback_state_free(state); + return TRUE; + } + + if (state->action & MSN_MOVE_BUDDY) { + msn_add_contact_to_group(soapconn->session->contact, state, state->who, guid); + return TRUE; + } + } + + if (state->action & MSN_DEL_GROUP) { + GList *l; + + msn_userlist_remove_group_id(soapconn->session->userlist, state->guid); + for (l = userlist->users; l != NULL; l = l->next) { + msn_user_remove_group_id( (MsnUser *)l->data, state->guid); + } + + } + + msn_callback_state_free(state); + } + + msn_soap_free_read_buf(soapconn); + return TRUE; +} + +static void +msn_group_written_cb(MsnSoapConn *soapconn) +{ + purple_debug_info("MSN CL","Sent group request.\n"); + soapconn->read_cb = msn_group_read_cb; +} + +/* add group */ +void +msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_name) +{ + MsnSoapReq *soap_request; + MsnContact *contact; + char *body = NULL; + gchar *escaped_group_name; + + g_return_if_fail(session != NULL); + g_return_if_fail(group_name != NULL); + + contact = session->contact; + purple_debug_info("MSN CL","Adding group %s to contact list.\n", group_name); + + if (state == NULL) { + state = msn_callback_state_new(); + } + + msn_callback_state_set_action(state, MSN_ADD_GROUP); + msn_callback_state_set_new_group_name(state, group_name); + + /* escape group name's html special chars so it can safely be sent + * in a XML SOAP request + */ + escaped_group_name = g_markup_escape_text(group_name, -1); + body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE, escaped_group_name); + g_free(escaped_group_name); + + /*build SOAP and POST it*/ + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_GROUP_ADD_SOAP_ACTION, + body, + state, + msn_group_read_cb, + msn_group_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn,soap_request); + + g_free(body); +} + +/* delete group */ +void +msn_del_group(MsnSession *session, const gchar *group_name) +{ + MsnSoapReq *soap_request; + MsnContact *contact; + MsnCallbackState *state; + char *body = NULL; + const gchar *guid; + + g_return_if_fail(session != NULL); + + g_return_if_fail(group_name != NULL); + contact = session->contact; + purple_debug_info("MSN CL","Deleting group %s from contact list\n", group_name); + + guid = msn_userlist_find_group_id(session->userlist, group_name); + + /* if group uid we need to del is NULL, + * we need to delete nothing + */ + if (guid == NULL) { + purple_debug_info("MSN CL", "Group %s guid not found, returning.\n", group_name); + return; + } + + if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) { + // XXX add back PurpleGroup since it isn't really removed in the server? + return; + } + + state = msn_callback_state_new(); + msn_callback_state_set_action(state, MSN_DEL_GROUP); + msn_callback_state_set_guid(state, guid); + + body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid); + /*build SOAP and POST it*/ + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_GROUP_DEL_SOAP_ACTION, + body, + state, + msn_group_read_cb, + msn_group_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn, soap_request); + + g_free(body); +} + +/* rename group */ +void +msn_contact_rename_group(MsnSession *session, const char *old_group_name, const char *new_group_name) +{ + MsnSoapReq *soap_request; + MsnContact *contact; + gchar * escaped_group_name, *body = NULL; + const gchar * guid; + MsnCallbackState *state = msn_callback_state_new(); + + g_return_if_fail(session != NULL); + g_return_if_fail(session->userlist != NULL); + g_return_if_fail(old_group_name != NULL); + g_return_if_fail(new_group_name != NULL); + + contact = session->contact; + purple_debug_info("MSN CL", "Renaming group %s to %s.\n", old_group_name, new_group_name); + + guid = msn_userlist_find_group_id(session->userlist, old_group_name); + if (guid == NULL) + return; + + msn_callback_state_set_guid(state, guid); + msn_callback_state_set_new_group_name(state, new_group_name); + + if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) { + msn_add_group(session, state, new_group_name); + // XXX move every buddy there (we probably need to fix concurrent SOAP reqs first) + } + + msn_callback_state_set_action(state, MSN_RENAME_GROUP); + + /* escape group name's html special chars so it can safely be sent + * in a XML SOAP request + */ + escaped_group_name = g_markup_escape_text(new_group_name, -1); + + body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name); + + soap_request = msn_soap_request_new(MSN_CONTACT_SERVER, + MSN_ADDRESS_BOOK_POST_URL, + MSN_GROUP_RENAME_SOAP_ACTION, + body, + state, + msn_group_read_cb, + msn_group_written_cb, + msn_contact_connect_init); + msn_soap_post(contact->soapconn, soap_request); + + g_free(escaped_group_name); + g_free(body); +} + +void +msn_contact_connect_init(MsnSoapConn *soapconn) +{ + msn_soap_init(soapconn, MSN_CONTACT_SERVER, 1, + msn_contact_login_connect_cb, + msn_contact_login_error_cb); +} diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/contact.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/contact.h Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,448 @@ +/** + * @file contact.h Header file for contact.c + * Author + * MaYuan + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_CONTACT_H_ +#define _MSN_CONTACT_H_ + +#define MSN_CONTACT_SERVER "contacts.msn.com" + +/* Get Contact List */ + +#define MSN_GET_CONTACT_POST_URL "/abservice/SharingService.asmx" +#define MSN_GET_CONTACT_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership" +#define MSN_GET_CONTACT_UPDATE_XML "Full"\ + "true"\ + "%s" +#define MSN_GET_CONTACT_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "%s"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "Messenger"\ + "Invitation"\ + "SocialNetwork"\ + "Space"\ + "Profile"\ + ""\ + ""\ + "%s"\ + ""\ + ""\ +"" + +/************************************************ + * Address Book SOAP + * *********************************************/ + +#define MSN_ADDRESS_BOOK_POST_URL "/abservice/abservice.asmx" + +/* Create AddressBook template */ +#define MSN_ADD_ADDRESSBOOK_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABAdd" + +#define MSN_ADD_ADDRESSBOOK_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "Initial"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "0"\ + "%s"\ + "true"\ + ""\ + ""\ + ""\ +"" + +/* Get AddressBook */ +#define MSN_GET_ADDRESS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll" +#define MSN_GET_ADDRESS_FULL_TIME "0001-01-01T00:00:00.0000000-08:00" +#define MSN_GET_ADDRESS_UPDATE_XML "true"\ + "%s" + +#define MSN_GET_GLEAM_UPDATE_XML \ + "%s"\ + "Gleam"\ + "%s" + +#define MSN_GET_ADDRESS_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "%s"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + "00000000-0000-0000-0000-000000000000"\ + "Full"\ + "%s"\ + ""\ + ""\ +"" + + +/*Gleams SOAP request template*/ +#define MSN_GET_GLEAMS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll" +#define MSN_GLEAMS_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "Initial"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + "00000000-0000-0000-0000-000000000000"\ + "Full"\ + "Gleam"\ + "0001-01-01T00:00:00.0000000-08:00"\ + ""\ + ""\ +"" + + +/******************************************************* + * Contact Management SOAP actions + *******************************************************/ + +/* Add a new contact t*/ +#define MSN_CONTACT_ADD_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactAdd" +#define MSN_CONTACT_LIVE_PENDING_XML "LivePending%strue" + +#define MSN_CONTACT_XML ""\ + ""\ + "%s"\ + "false"\ + "true"\ + ""\ + "" + +#define MSN_CONTACT_DISPLAYNAME_XML "%s%strue" + +#define MSN_ADD_CONTACT_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseContactSavefalse00000000-0000-0000-0000-000000000000%strue" + +/* Add a contact to a group */ +#define MSN_ADD_CONTACT_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactAdd" +#define MSN_ADD_CONTACT_GROUP_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "ContactSave"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + "00000000-0000-0000-0000-000000000000"\ + ""\ + ""\ + "%s"\ + ""\ + ""\ + "%s"\ + ""\ + "true"\ + "true"\ + ""\ + ""\ + ""\ +"" + +/* Delete a contact from the Contact List */ +#define MSN_CONTACT_DEL_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactDelete" +#define MSN_CONTACT_ID_XML "%s" +#define MSN_DEL_CONTACT_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseTimerfalse00000000-0000-0000-0000-000000000000%s" + +/* Remove a contact from a group */ +#define MSN_CONTACT_DEL_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactDelete" +#define MSN_CONTACT_DEL_GROUP_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseTimerfalse00000000-0000-0000-0000-000000000000%s%s" + + +/* Update Contact Nickname */ +#define MSN_CONTACT_UPDATE_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate" +#define MSN_CONTACT_UPDATE_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "Timer"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + "00000000-0000-0000-0000-000000000000"\ + ""\ + ""\ + ""\ + "Me"\ + "%s"\ + ""\ + "DisplayName"\ + ""\ + ""\ + ""\ + ""\ +"" + + +/******************************************************* + * Add/Delete contact from lists SOAP actions + *******************************************************/ + +/* block means delete from allow list and add contact to block list */ +#define MSN_SHARE_POST_URL "/abservice/SharingService.asmx" + +#define MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/AddMember" +#define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/DeleteMember" + +#define MSN_MEMBER_PASSPORT_XML ""\ + "Passport"\ + "Accepted"\ + "%s"\ + "" + +#define MSN_MEMBER_MEMBERSHIPID_XML ""\ + "Passport"\ + "%u"\ + "Accepted"\ + "" + +/* first delete contact from allow list */ + +#define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "%s"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + ""\ + "0"\ + "Messenger"\ + ""\ + ""\ + ""\ + ""\ + "%s"\ + ""\ + "%s"\ + ""\ + ""\ + ""\ + ""\ + ""\ +"" + +#define MSN_CONTACT_ADD_TO_LIST_TEMPLATE ""\ +""\ + ""\ + ""\ + "09607671-1C32-421F-A6A6-CBFAA51AB5F4"\ + "false"\ + "%s"\ + ""\ + ""\ + "false"\ + ""\ + ""\ + ""\ + ""\ + ""\ + "0"\ + "Messenger"\ + ""\ + ""\ + ""\ + ""\ + "%s"\ + ""\ + "%s"\ + ""\ + ""\ + ""\ + ""\ + ""\ +"" + + + +/******************************************************* + * Group management SOAP actions + *******************************************************/ + +/* add a group */ +#define MSN_GROUP_ADD_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupAdd" +#define MSN_GROUP_ADD_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseGroupSavefalse00000000-0000-0000-0000-000000000000false%sC8529CE2-6EAD-434d-881F-341E17DB3FF8falseMSN.IM.Display1" + +/* delete a group */ +#define MSN_GROUP_DEL_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupDelete" +#define MSN_GROUP_DEL_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseTimerfalse00000000-0000-0000-0000-000000000000%s" + +/* change a group's name */ +#define MSN_GROUP_RENAME_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupUpdate" +#define MSN_GROUP_RENAME_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseTimerfalse00000000-0000-0000-0000-000000000000%s%sGroupName " + +typedef enum +{ + MSN_ADD_BUDDY = 0x01, + MSN_MOVE_BUDDY = 0x02, + MSN_ACCEPTED_BUDDY = 0x04, + MSN_DENIED_BUDDY = 0x08, + MSN_ADD_GROUP = 0x10, + MSN_DEL_GROUP = 0x20, + MSN_RENAME_GROUP = 0x40, +} MsnCallbackAction; + +typedef struct _MsnContact MsnContact; + +struct _MsnContact +{ + MsnSession *session; + + MsnSoapConn *soapconn; +}; + +typedef struct _MsnCallbackState MsnCallbackState; + +struct _MsnCallbackState +{ + gchar * who; + gchar * uid; + gchar * old_group_name; + gchar * new_group_name; + gchar * guid; + MsnListId list_id; + MsnCallbackAction action; +}; + +typedef enum +{ + MSN_PS_INITIAL, + MSN_PS_SAVE_CONTACT, + MSN_PS_PENDING_LIST, + MSN_PS_CONTACT_API, + MSN_PS_BLOCK_UNBLOCK +} MsnSoapPartnerScenario; + +/************************************************ + * function prototype + ************************************************/ +MsnContact * msn_contact_new(MsnSession *session); +void msn_contact_destroy(MsnContact *contact); + +MsnCallbackState * msn_callback_state_new(void); +void msn_callback_state_free(MsnCallbackState *state); +void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who); +void msn_callback_state_set_uid(MsnCallbackState *state, const gchar *uid); +void msn_callback_state_set_old_group_name(MsnCallbackState *state, + const gchar *old_group_name); +void msn_callback_state_set_new_group_name(MsnCallbackState *state, + const gchar *new_group_name); +void msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid); +void msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id); +void msn_callback_state_set_action(MsnCallbackState *state, + MsnCallbackAction action); + +void msn_contact_connect(MsnContact *contact); +void msn_get_contact_list(MsnContact * contact, + const MsnSoapPartnerScenario partner_scenario, + const char *update); +void msn_get_address_book(MsnContact *contact, + const MsnSoapPartnerScenario partner_scenario, + const char * update, const char * gupdate); + +/* contact SOAP operations */ +void msn_update_contact(MsnContact *contact, const char* nickname); + +void msn_add_contact(MsnContact *contact, MsnCallbackState *state, + const char *passport); +void msn_delete_contact(MsnContact *contact, const char *contactId); + +void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, + const char *passport, const char *groupId); +void msn_del_contact_from_group(MsnContact *contact, const char *passport, + const char *group_name); +/* group operations */ +void msn_add_group(MsnSession *session, MsnCallbackState *state, + const char* group_name); +void msn_del_group(MsnSession *session, const gchar *group_name); +void msn_contact_rename_group(MsnSession *session, const char *old_group_name, + const char *new_group_name); + +/* lists operations */ +void msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state, + const gchar *passport, const MsnListId list); +void msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state, + const gchar *passport, const MsnListId list); + +void msn_contact_connect_init(MsnSoapConn *soapconn); + +#endif /* _MSN_CONTACT_H_ */ + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/dialog.c --- a/libpurple/protocols/msn/dialog.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/dialog.c Fri Sep 28 20:58:48 2007 +0000 @@ -36,6 +36,7 @@ /* Remove the buddy referenced by the MsnAddRemData before the serverside list is changed. * If the buddy will be added, he'll be added back; if he will be removed, he won't be. */ +/* Actually with our MSNP14 code that isn't true yet, he won't be added back :( */ static void msn_complete_sync_issue(MsnAddRemData *data) { @@ -44,28 +45,32 @@ if (data->group != NULL) group = purple_find_group(data->group); - + if (group != NULL) buddy = purple_find_buddy_in_group(purple_connection_get_account(data->gc), data->who, group); else buddy = purple_find_buddy(purple_connection_get_account(data->gc), data->who); - + if (buddy != NULL) purple_blist_remove_buddy(buddy); } + static void msn_add_cb(MsnAddRemData *data) { - MsnSession *session; - MsnUserList *userlist; - +#if 0 + /* this *should* be necessary !! */ msn_complete_sync_issue(data); +#endif - session = data->gc->proto_data; - userlist = session->userlist; + if (g_list_find(purple_connections_get_all(), data->gc) != NULL) + { + MsnSession *session = data->gc->proto_data; + MsnUserList *userlist = session->userlist; - msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, data->group); + msn_userlist_add_buddy(userlist, data->who, data->group); + } g_free(data->group); g_free(data->who); @@ -75,17 +80,20 @@ static void msn_rem_cb(MsnAddRemData *data) { - MsnSession *session; - MsnUserList *userlist; - msn_complete_sync_issue(data); - session = data->gc->proto_data; - userlist = session->userlist; + if (g_list_find(purple_connections_get_all(), data->gc) != NULL) + { + MsnSession *session = data->gc->proto_data; + MsnUserList *userlist = session->userlist; - msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group); + if (data->group == NULL) { + msn_userlist_rem_buddy_from_list(userlist, data->who, MSN_LIST_FL); + } else { + g_free(data->group); + } + } - g_free(data->group); g_free(data->who); g_free(data); } @@ -104,7 +112,7 @@ data = g_new0(MsnAddRemData, 1); data->who = g_strdup(passport); - data->group = g_strdup(group_name); + data->group = group_name != NULL ? g_strdup(group_name) : NULL; data->gc = gc; msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"), diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/directconn.c --- a/libpurple/protocols/msn/directconn.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/directconn.c Fri Sep 28 20:58:48 2007 +0000 @@ -76,7 +76,6 @@ * Connection Functions **************************************************************************/ -#if 0 static int create_listener(int port) { @@ -160,7 +159,6 @@ return fd; } -#endif static size_t msn_directconn_write(MsnDirectConn *directconn, @@ -288,6 +286,11 @@ /* ERROR */ purple_debug_error("msn", "error reading\n"); + if (directconn->inpa) + purple_input_remove(directconn->inpa); + + close(directconn->fd); + msn_directconn_destroy(directconn); return; @@ -302,6 +305,11 @@ /* ERROR */ purple_debug_error("msn", "error reading\n"); + if (directconn->inpa) + purple_input_remove(directconn->inpa); + + close(directconn->fd); + msn_directconn_destroy(directconn); return; @@ -348,17 +356,22 @@ /* ERROR */ purple_debug_error("msn", "error reading\n"); + if (directconn->inpa) + purple_input_remove(directconn->inpa); + + close(directconn->fd); + msn_directconn_destroy(directconn); } } static void -connect_cb(gpointer data, gint source, const gchar *error_message) +connect_cb(gpointer data, gint source, PurpleInputCondition cond) { MsnDirectConn* directconn; int fd; - purple_debug_misc("msn", "directconn: connect_cb: %d\n", source); + purple_debug_misc("msn", "directconn: connect_cb: %d, %d.\n", source, cond); directconn = data; directconn->connect_data = NULL; @@ -438,7 +451,6 @@ return (directconn->connect_data != NULL); } -#if 0 void msn_directconn_listen(MsnDirectConn *directconn) { @@ -458,7 +470,6 @@ directconn->port = port; directconn->c = 0; } -#endif MsnDirectConn* msn_directconn_new(MsnSlpLink *slplink) diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/directconn.h --- a/libpurple/protocols/msn/directconn.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/directconn.h Fri Sep 28 20:58:48 2007 +0000 @@ -52,9 +52,7 @@ MsnDirectConn *msn_directconn_new(MsnSlpLink *slplink); gboolean msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port); -#if 0 void msn_directconn_listen(MsnDirectConn *directconn); -#endif void msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg); void msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce); void msn_directconn_destroy(MsnDirectConn *directconn); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/group.c --- a/libpurple/protocols/msn/group.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/group.c Fri Sep 28 20:58:48 2007 +0000 @@ -25,18 +25,18 @@ #include "group.h" MsnGroup * -msn_group_new(MsnUserList *userlist, int id, const char *name) +msn_group_new(MsnUserList *userlist, const char *id, const char *name) { MsnGroup *group; - g_return_val_if_fail(id >= 0, NULL); + g_return_val_if_fail(id != NULL, NULL); g_return_val_if_fail(name != NULL, NULL); group = g_new0(MsnGroup, 1); msn_userlist_add_group(userlist, group); - group->id = id; + group->id = g_strdup(id); group->name = g_strdup(name); return group; @@ -47,17 +47,18 @@ { g_return_if_fail(group != NULL); + g_free(group->id); g_free(group->name); g_free(group); } void -msn_group_set_id(MsnGroup *group, int id) +msn_group_set_id(MsnGroup *group, const char *id) { g_return_if_fail(group != NULL); - g_return_if_fail(id >= 0); + g_return_if_fail(id != NULL); - group->id = id; + group->id = g_strdup(id); } void @@ -72,10 +73,10 @@ group->name = g_strdup(name); } -int +char* msn_group_get_id(const MsnGroup *group) { - g_return_val_if_fail(group != NULL, -1); + g_return_val_if_fail(group != NULL, NULL); return group->id; } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/group.h --- a/libpurple/protocols/msn/group.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/group.h Fri Sep 28 20:58:48 2007 +0000 @@ -30,8 +30,21 @@ #include "session.h" #include "user.h" +#include "soap.h" +#include "userlist.h" -#include "userlist.h" +#define MSN_ADD_GROUPS "test111C8529CE2-6EAD-434d-881F-341E17DB3FF8falseMSN.IM.Display1" + +#define MSN_ADD_GROUP_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseGroupSavefalse00000000-0000-0000-0000-000000000000false%s" + +#define MSN_GROUP_IDS "9e57e654-59f0-44d1-aedc-0a7500b7e51f" +#define MSN_DELETE_GROUP_TEMPLATE "09607671-1C32-421F-A6A6-CBFAA51AB5F4falseTimerfalse00000000-0000-0000-0000-000000000000%s" + +#define MSN_INDIVIDUALS_GROUP_ID "1983" +#define MSN_INDIVIDUALS_GROUP_NAME "Other Contacts" + +#define MSN_NON_IM_GROUP_ID "email" +#define MSN_NON_IM_GROUP_NAME "Non-IM Contacts" /** * A group. @@ -39,8 +52,9 @@ struct _MsnGroup { MsnSession *session; /**< The MSN session. */ + MsnSoapConn *soapconn; - int id; /**< The group ID. */ + char *id; /**< The group ID. */ char *name; /**< The name of the group. */ }; @@ -58,7 +72,7 @@ * * @return A new group structure. */ -MsnGroup *msn_group_new(MsnUserList *userlist, int id, const char *name); +MsnGroup *msn_group_new(MsnUserList *userlist, const char *id, const char *name); /** * Destroys a group structure. @@ -73,7 +87,7 @@ * @param group The group. * @param id The ID. */ -void msn_group_set_id(MsnGroup *group, int id); +void msn_group_set_id(MsnGroup *group, const char *id); /** * Sets the name for a group. @@ -90,7 +104,7 @@ * * @return The ID. */ -int msn_group_get_id(const MsnGroup *group); +char* msn_group_get_id(const MsnGroup *group); /** * Returns the name for a group. diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/history.c --- a/libpurple/protocols/msn/history.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/history.c Fri Sep 28 20:58:48 2007 +0000 @@ -84,3 +84,4 @@ msn_transaction_destroy(trans); } } + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msg.c --- a/libpurple/protocols/msn/msg.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/msg.c Fri Sep 28 20:58:48 2007 +0000 @@ -126,7 +126,7 @@ msn_message_set_charset(msg, "UTF-8"); msn_message_set_flag(msg, 'A'); msn_message_set_attr(msg, "X-MMS-IM-Format", - "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0"); + "FN=MS%20Sans%20Serif; EF=; CO=0; CS=86;PF=0"); message_cr = purple_str_add_cr(message); msn_message_set_bin_data(msg, message_cr, strlen(message_cr)); @@ -206,7 +206,8 @@ void msn_message_parse_payload(MsnMessage *msg, - const char *payload, size_t payload_len) + const char *payload, size_t payload_len, + const char *line_dem,const char *body_dem) { char *tmp_base, *tmp; const char *content_type; @@ -214,12 +215,11 @@ char **elems, **cur, **tokens; g_return_if_fail(payload != NULL); - tmp_base = tmp = g_malloc0(payload_len + 1); memcpy(tmp_base, payload, payload_len); /* Parse the attributes. */ - end = strstr(tmp, "\r\n\r\n"); + end = strstr(tmp, body_dem); /* TODO? some clients use \r delimiters instead of \r\n, the official client * doesn't send such messages, but does handle receiving them. We'll just * avoid crashing for now */ @@ -229,7 +229,7 @@ } *end = '\0'; - elems = g_strsplit(tmp, "\r\n", 0); + elems = g_strsplit(tmp, line_dem, 0); for (cur = elems; *cur != NULL; cur++) { @@ -240,6 +240,7 @@ key = tokens[0]; value = tokens[1]; + /*if not MIME content ,then return*/ if (!strcmp(key, "MIME-Version")) { g_strfreev(tokens); @@ -274,7 +275,7 @@ g_strfreev(elems); /* Proceed to the end of the "\r\n\r\n" */ - tmp = end + 4; + tmp = end + strlen(body_dem); /* Now we *should* be at the body. */ content_type = msn_message_get_content_type(msg); @@ -480,6 +481,7 @@ { memcpy(n, body, body_len); n += body_len; + *n = '\0'; } } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msg.h --- a/libpurple/protocols/msn/msg.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/msg.h Fri Sep 28 20:58:48 2007 +0000 @@ -34,6 +34,12 @@ typedef void (*MsnMsgCb)(MsnMessage *, void *data); +#define MSG_BODY_DEM "\r\n\r\n" +#define MSG_LINE_DEM "\r\n" + +#define MSG_OIM_BODY_DEM "\n\n" +#define MSG_OIM_LINE_DEM "\n" + /* typedef enum { @@ -180,7 +186,8 @@ * @param payload_len The length of the payload. */ void msn_message_parse_payload(MsnMessage *msg, const char *payload, - size_t payload_len); + size_t payload_len, + const char *line_dem,const char *body_dem); /** * Destroys a message. diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msn-utils.c --- a/libpurple/protocols/msn/msn-utils.c Fri Sep 28 20:58:27 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,440 +0,0 @@ -/** - * @file msn-utils.c Utility functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "msn-utils.h" - -void -msn_parse_format(const char *mime, char **pre_ret, char **post_ret) -{ - char *cur; - GString *pre = g_string_new(NULL); - GString *post = g_string_new(NULL); - unsigned int colors[3]; - - if (pre_ret != NULL) *pre_ret = NULL; - if (post_ret != NULL) *post_ret = NULL; - - cur = strstr(mime, "FN="); - - if (cur && (*(cur = cur + 3) != ';')) - { - pre = g_string_append(pre, ""); - post = g_string_prepend(post, ""); - } - - cur = strstr(mime, "EF="); - - if (cur && (*(cur = cur + 3) != ';')) - { - while (*cur && *cur != ';') - { - pre = g_string_append_c(pre, '<'); - pre = g_string_append_c(pre, *cur); - pre = g_string_append_c(pre, '>'); - post = g_string_prepend_c(post, '>'); - post = g_string_prepend_c(post, *cur); - post = g_string_prepend_c(post, '/'); - post = g_string_prepend_c(post, '<'); - cur++; - } - } - - cur = strstr(mime, "CO="); - - if (cur && (*(cur = cur + 3) != ';')) - { - int i; - - i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]); - - if (i > 0) - { - char tag[64]; - - if (i == 1) - { - colors[1] = 0; - colors[2] = 0; - } - else if (i == 2) - { - unsigned int temp = colors[0]; - - colors[0] = colors[1]; - colors[1] = temp; - colors[2] = 0; - } - else if (i == 3) - { - unsigned int temp = colors[2]; - - colors[2] = colors[0]; - colors[0] = temp; - } - - g_snprintf(tag, sizeof(tag), - "", - colors[0], colors[1], colors[2]); - - pre = g_string_append(pre, tag); - post = g_string_prepend(post, ""); - } - } - - cur = strstr(mime, "RL="); - - if (cur && (*(cur = cur + 3) != ';')) - { - if (*cur == '1') - { - /* RTL text was received */ - pre = g_string_append(pre, ""); - post = g_string_prepend(post, ""); - } - } - - cur = g_strdup(purple_url_decode(pre->str)); - g_string_free(pre, TRUE); - - if (pre_ret != NULL) - *pre_ret = cur; - else - g_free(cur); - - cur = g_strdup(purple_url_decode(post->str)); - g_string_free(post, TRUE); - - if (post_ret != NULL) - *post_ret = cur; - else - g_free(cur); -} - -/* - * We need this because we're only supposed to encode spaces in the font - * names. purple_url_encode() isn't acceptable. - */ -static const char * -encode_spaces(const char *str) -{ - static char buf[BUF_LEN]; - const char *c; - char *d; - - g_return_val_if_fail(str != NULL, NULL); - - for (c = str, d = buf; *c != '\0'; c++) - { - if (*c == ' ') - { - *d++ = '%'; - *d++ = '2'; - *d++ = '0'; - } - else - *d++ = *c; - } - - return buf; -} - -/* - * Taken from the zephyr plugin. - * This parses HTML formatting (put out by one of the gtkimhtml widgets - * and converts it to msn formatting. It doesn't deal with the tag closing, - * but gtkimhtml widgets give valid html. - * It currently deals properly with , , , , - * , , . - * It ignores and - */ -void -msn_import_html(const char *html, char **attributes, char **message) -{ - int len, retcount = 0; - const char *c; - char *msg; - char *fontface = NULL; - char fonteffect[4]; - char fontcolor[7]; - char direction = '0'; - - gboolean has_bold = FALSE; - gboolean has_italic = FALSE; - gboolean has_underline = FALSE; - gboolean has_strikethrough = FALSE; - - g_return_if_fail(html != NULL); - g_return_if_fail(attributes != NULL); - g_return_if_fail(message != NULL); - - len = strlen(html); - msg = g_malloc0(len + 1); - - memset(fontcolor, 0, sizeof(fontcolor)); - strcat(fontcolor, "0"); - memset(fonteffect, 0, sizeof(fonteffect)); - - for (c = html; *c != '\0';) - { - if (*c == '<') - { - if (!g_ascii_strncasecmp(c + 1, "br>", 3)) - { - msg[retcount++] = '\r'; - msg[retcount++] = '\n'; - c += 4; - } - else if (!g_ascii_strncasecmp(c + 1, "i>", 2)) - { - if (!has_italic) - { - strcat(fonteffect, "I"); - has_italic = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "b>", 2)) - { - if (!has_bold) - { - strcat(fonteffect, "B"); - has_bold = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "u>", 2)) - { - if (!has_underline) - { - strcat(fonteffect, "U"); - has_underline = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "s>", 2)) - { - if (!has_strikethrough) - { - strcat(fonteffect, "S"); - has_strikethrough = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8)) - { - c += 9; - - if (!g_ascii_strncasecmp(c, "mailto:", 7)) - c += 7; - - while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) - msg[retcount++] = *c++; - - if (*c != '\0') - c += 2; - - /* ignore descriptive string */ - while ((*c != '\0') && g_ascii_strncasecmp(c, "", 4)) - c++; - - if (*c != '\0') - c += 4; - } - else if (!g_ascii_strncasecmp(c + 1, "span", 4)) - { - /* Bi-directional text support using CSS properties in span tags */ - c += 5; - - while (*c != '\0' && *c != '>') - { - while (*c == ' ') - c++; - if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9)) - { - c += 9; - direction = '1'; - } - else if (!g_ascii_strncasecmp(c, "style=\"", 7)) - { - /* Parse inline CSS attributes */ - char *attributes; - int attr_len = 0; - c += 7; - while (*(c + attr_len) != '\0' && *(c + attr_len) != '"') - attr_len++; - if (*(c + attr_len) == '"') - { - char *attr_dir; - attributes = g_strndup(c, attr_len); - attr_dir = purple_markup_get_css_property(attributes, "direction"); - if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3))) - direction = '1'; - g_free(attr_dir); - g_free(attributes); - } - - } - else - { - c++; - } - } - if (*c == '>') - c++; - } - else if (!g_ascii_strncasecmp(c + 1, "font", 4)) - { - c += 5; - - while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1)) - c++; - - if (!g_ascii_strncasecmp(c, "color=\"#", 7)) - { - c += 8; - - fontcolor[0] = *(c + 4); - fontcolor[1] = *(c + 5); - fontcolor[2] = *(c + 2); - fontcolor[3] = *(c + 3); - fontcolor[4] = *c; - fontcolor[5] = *(c + 1); - - c += 8; - } - else if (!g_ascii_strncasecmp(c, "face=\"", 6)) - { - const char *end = NULL; - const char *comma = NULL; - unsigned int namelen = 0; - - c += 6; - end = strchr(c, '\"'); - comma = strchr(c, ','); - - if (comma == NULL || comma > end) - namelen = (unsigned int)(end - c); - else - namelen = (unsigned int)(comma - c); - - fontface = g_strndup(c, namelen); - c = end + 2; - } - else - { - /* Drop all unrecognized/misparsed font tags */ - while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) - c++; - - if (*c != '\0') - c += 2; - } - } - else - { - while ((*c != '\0') && (*c != '>')) - c++; - if (*c != '\0') - c++; - } - } - else if (*c == '&') - { - if (!g_ascii_strncasecmp(c, "<", 4)) - { - msg[retcount++] = '<'; - c += 4; - } - else if (!g_ascii_strncasecmp(c, ">", 4)) - { - msg[retcount++] = '>'; - c += 4; - } - else if (!g_ascii_strncasecmp(c, " ", 6)) - { - msg[retcount++] = ' '; - c += 6; - } - else if (!g_ascii_strncasecmp(c, """, 6)) - { - msg[retcount++] = '"'; - c += 6; - } - else if (!g_ascii_strncasecmp(c, "&", 5)) - { - msg[retcount++] = '&'; - c += 5; - } - else if (!g_ascii_strncasecmp(c, "'", 6)) - { - msg[retcount++] = '\''; - c += 6; - } - else - msg[retcount++] = *c++; - } - else - msg[retcount++] = *c++; - } - - if (fontface == NULL) - fontface = g_strdup("MS Sans Serif"); - - *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c", - encode_spaces(fontface), - fonteffect, fontcolor, direction); - *message = g_strdup(msg); - - g_free(fontface); - g_free(msg); -} - -void -msn_parse_socket(const char *str, char **ret_host, int *ret_port) -{ - char *host; - char *c; - int port; - - host = g_strdup(str); - - if ((c = strchr(host, ':')) != NULL) - { - *c = '\0'; - port = atoi(c + 1); - } - else - port = 1863; - - *ret_host = host; - *ret_port = port; -} diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msn-utils.h --- a/libpurple/protocols/msn/msn-utils.h Fri Sep 28 20:58:27 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/** - * @file msn-utils.h Utility functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_UTILS_H_ -#define _MSN_UTILS_H_ - -/** - * Parses the MSN message formatting into a format compatible with Purple. - * - * @param mime The mime header with the formatting. - * @param pre_ret The returned prefix string. - * @param post_ret The returned postfix string. - * - * @return The new message. - */ -void msn_parse_format(const char *mime, char **pre_ret, char **post_ret); - -/** - * Parses the Purple message formatting (html) into the MSN format. - * - * @param html The html message to format. - * @param attributes The returned attributes string. - * @param message The returned message string. - * - * @return The new message. - */ -void msn_import_html(const char *html, char **attributes, char **message); - -void msn_parse_socket(const char *str, char **ret_host, int *ret_port); - -#endif /* _MSN_UTILS_H_ */ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Fri Sep 28 20:58:48 2007 +0000 @@ -37,7 +37,7 @@ #include "cmds.h" #include "core.h" #include "prpl.h" -#include "msn-utils.h" +#include "msnutils.h" #include "version.h" #include "switchboard.h" @@ -151,7 +151,7 @@ return PURPLE_CMD_RET_OK; } -static void +void msn_act_id(PurpleConnection *gc, const char *entry) { MsnCmdProc *cmdproc; @@ -175,9 +175,12 @@ return; } - msn_cmdproc_send(cmdproc, "REA", "%s %s", - purple_account_get_username(account), - alias); + if (*alias == '\0') { + alias = purple_url_encode(purple_account_get_username(account)); + } + + msn_cmdproc_send(cmdproc, "PRP", "MFN %s", alias); + } static void @@ -414,6 +417,28 @@ return user && user->mobile; } +void +msn_send_privacy(PurpleConnection *gc) +{ + PurpleAccount *account; + MsnSession *session; + MsnCmdProc *cmdproc; + + account = purple_connection_get_account(gc); + session = gc->proto_data; + cmdproc = session->notification->cmdproc; + + if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL || + account->perm_deny == PURPLE_PRIVACY_DENY_USERS) + { + msn_cmdproc_send(cmdproc, "BLP", "%s", "AL"); + } + else + { + msn_cmdproc_send(cmdproc, "BLP", "%s", "BL"); + } +} + static void initiate_chat_cb(PurpleBlistNode *node, gpointer data) { @@ -460,6 +485,7 @@ session = gc->proto_data; xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who); + if (xfer) { slplink = msn_session_get_slplink(session, who); @@ -511,20 +537,27 @@ return "msn"; } +/* + * Set the User status text + * Add the PSM String Using "Name - PSM String" format + */ static char * msn_status_text(PurpleBuddy *buddy) { PurplePresence *presence; PurpleStatus *status; + const char *msg, *cmedia; presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); - if (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence)) - { - return g_strdup(purple_status_get_name(status)); - } + msg = purple_status_get_attr_string(status, "message"); + cmedia = purple_status_get_attr_string(status, "currentmedia"); + if (cmedia) + return g_markup_escape_text(cmedia, -1); + else if (msg) + return g_markup_escape_text(msg, -1); return NULL; } @@ -540,14 +573,43 @@ if (purple_presence_is_online(presence)) { - purple_notify_user_info_add_pair(user_info, _("Status"), - (purple_presence_is_idle(presence) ? _("Idle") : purple_status_get_name(status))); - } + const char *psm, *currentmedia, *name; + char *tmp; + + psm = purple_status_get_attr_string(status, "message"); + currentmedia = purple_status_get_attr_string(status, "currentmedia"); + + if (!purple_presence_is_available(presence)) { + name = purple_status_get_name(status); + } else { + name = NULL; + } + + if (name != NULL && *name) { + char *tmp2 = g_markup_escape_text(name, -1); - if (full && user) - { - purple_notify_user_info_add_pair(user_info, _("Has you"), - ((user->list_op & (1 << MSN_LIST_RL)) ? _("Yes") : _("No"))); + if (psm != NULL && *psm) { + tmp = g_markup_escape_text(psm, -1); + purple_notify_user_info_add_pair(user_info, tmp2, tmp); + g_free(tmp); + } else { + purple_notify_user_info_add_pair(user_info, _("Status"), tmp2); + } + + g_free(tmp2); + } else { + if (psm != NULL && *psm) { + tmp = g_markup_escape_text(psm, -1); + purple_notify_user_info_add_pair(user_info, _("Status"), tmp); + g_free(tmp); + } + } + + if (currentmedia) { + tmp = g_markup_escape_text(currentmedia, -1); + purple_notify_user_info_add_pair(user_info, _("Current media"), tmp); + g_free(tmp); + } } /* XXX: This is being shown in non-full tooltips because the @@ -567,28 +629,44 @@ PurpleStatusType *status; GList *types = NULL; - status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, - NULL, NULL, FALSE, TRUE, FALSE); + status = purple_status_type_new_with_attrs( + PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE, + "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), + "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), + NULL); types = g_list_append(types, status); - status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - NULL, NULL, FALSE, TRUE, FALSE); + status = purple_status_type_new_with_attrs( + PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE, + "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), + "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), + NULL); types = g_list_append(types, status); - status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "brb", _("Be Right Back"), FALSE, TRUE, FALSE); + status = purple_status_type_new_with_attrs( + PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE, + "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), + "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), + NULL); types = g_list_append(types, status); - status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, - "busy", _("Busy"), FALSE, TRUE, FALSE); + status = purple_status_type_new_with_attrs( + PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE, + "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), + "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), + NULL); types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, - "phone", _("On the Phone"), FALSE, TRUE, FALSE); + status = purple_status_type_new_with_attrs( + PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE, + "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), + "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), + NULL); types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "lunch", _("Out to Lunch"), FALSE, TRUE, FALSE); + status = purple_status_type_new_with_attrs( + PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE, + "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), + "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING), + NULL); types = g_list_append(types, status); status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, @@ -731,7 +809,7 @@ http_method = purple_account_get_bool(account, "http_method", FALSE); if (http_method) - host = purple_account_get_string(account, "http_method_server", MSN_SERVER); + host = purple_account_get_string(account, "http_method_server", MSN_HTTPCONN_SERVER); else host = purple_account_get_string(account, "server", MSN_SERVER); port = purple_account_get_int(account, "port", MSN_PORT); @@ -788,6 +866,7 @@ char *msgformat; char *msgtext; + purple_debug_info("MSNP14","send IM {%s} to %s\n",message,who); account = purple_connection_get_account(gc); if (buddy) { @@ -801,62 +880,91 @@ } msn_import_html(message, &msgformat, &msgtext); + if(msn_user_is_online(account, who)|| + msn_user_is_yahoo(account, who)){ + /*User online,then send Online Instant Message*/ - if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564) - { + if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564) + { + g_free(msgformat); + g_free(msgtext); + + return -E2BIG; + } + + msg = msn_message_new_plain(msgtext); + msg->remote_user = g_strdup(who); + msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat); + g_free(msgformat); g_free(msgtext); - return -E2BIG; - } + purple_debug_info("MSNP14","prepare to send online Message\n"); + if (g_ascii_strcasecmp(who, purple_account_get_username(account))) + { + MsnSession *session; + MsnSwitchBoard *swboard; - msg = msn_message_new_plain(msgtext); - msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat); - - g_free(msgformat); - g_free(msgtext); + session = gc->proto_data; + if(msn_user_is_yahoo(account,who)){ + /*we send the online and offline Message to Yahoo User via UBM*/ + purple_debug_info("MSNP14","send to Yahoo User\n"); + uum_send_msg(session,msg); + }else{ + purple_debug_info("MSNP14","send via switchboard\n"); + swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM); + msn_switchboard_send_msg(swboard, msg, TRUE); + } + } + else + { + char *body_str, *body_enc, *pre, *post; + const char *format; + MsnIMData *imdata = g_new0(MsnIMData, 1); + /* + * In MSN, you can't send messages to yourself, so + * we'll fake like we received it ;) + */ + body_str = msn_message_to_string(msg); + body_enc = g_markup_escape_text(body_str, -1); + g_free(body_str); - if (g_ascii_strcasecmp(who, purple_account_get_username(account))) - { - MsnSession *session; - MsnSwitchBoard *swboard; + format = msn_message_get_attr(msg, "X-MMS-IM-Format"); + msn_parse_format(format, &pre, &post); + body_str = g_strdup_printf("%s%s%s", pre ? pre : "", + body_enc ? body_enc : "", post ? post : ""); + g_free(body_enc); + g_free(pre); + g_free(post); + + serv_got_typing_stopped(gc, who); + imdata->gc = gc; + imdata->who = who; + imdata->msg = body_str; + imdata->flags = flags; + imdata->when = time(NULL); + g_idle_add(msn_send_me_im, imdata); + } + msn_message_destroy(msg); + }else { + /*send Offline Instant Message,only to MSN Passport User*/ + MsnSession *session; + MsnOim *oim; + char *friendname; + + purple_debug_info("MSNP14","prepare to send offline Message\n"); session = gc->proto_data; - swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM); + /* XXX/khc: hack */ + if (!session->oim) + session->oim = msn_oim_new(session); - msn_switchboard_send_msg(swboard, msg, TRUE); + oim = session->oim; + friendname = msn_encode_mime(account->username); + msn_oim_prep_send_msg_info(oim, purple_account_get_username(account), + friendname, who, message); + msn_oim_send_msg(oim); } - else - { - char *body_str, *body_enc, *pre, *post; - const char *format; - MsnIMData *imdata = g_new0(MsnIMData, 1); - /* - * In MSN, you can't send messages to yourself, so - * we'll fake like we received it ;) - */ - body_str = msn_message_to_string(msg); - body_enc = g_markup_escape_text(body_str, -1); - g_free(body_str); - - format = msn_message_get_attr(msg, "X-MMS-IM-Format"); - msn_parse_format(format, &pre, &post); - body_str = g_strdup_printf("%s%s%s", pre ? pre : "", - body_enc ? body_enc : "", post ? post : ""); - g_free(body_enc); - g_free(pre); - g_free(post); - - serv_got_typing_stopped(gc, who); - imdata->gc = gc; - imdata->who = who; - imdata->msg = body_str; - imdata->flags = flags; - imdata->when = time(NULL); - g_idle_add(msn_send_me_im, imdata); - } - - msn_message_destroy(msg); return 1; } @@ -995,6 +1103,7 @@ userlist = session->userlist; who = msn_normalize(gc->account, buddy->name); + purple_debug_info("MSN","Add user:%s to group:%s\n", who, group->name); if (!session->logged_in) { #if 0 @@ -1028,8 +1137,7 @@ /* XXX - Would group ever be NULL here? I don't think so... * shx: Yes it should; MSN handles non-grouped buddies, and this is only * internal. */ - msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, - group ? group->name : NULL); + msn_userlist_add_buddy(userlist, who, group ? group->name : NULL); } static void @@ -1045,7 +1153,7 @@ return; /* XXX - Does buddy->name need to be msn_normalize'd here? --KingAnt */ - msn_userlist_rem_buddy(userlist, buddy->name, MSN_LIST_FL, group->name); + msn_userlist_rem_buddy(userlist, buddy->name); } static void @@ -1062,10 +1170,18 @@ if (!session->logged_in) return; - if (user != NULL && user->list_op & MSN_LIST_BL_OP) - msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL); + if (user != NULL && user->list_op & MSN_LIST_BL_OP) { + msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL); - msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL); + /* delete contact from Block list and add it to Allow in the callback */ + msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL); + } else { + /* just add the contact to Allow list */ + msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_AL); + } + + + msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL); } static void @@ -1082,10 +1198,17 @@ if (!session->logged_in) return; - if (user != NULL && user->list_op & MSN_LIST_AL_OP) - msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL); + if (user != NULL && user->list_op & MSN_LIST_AL_OP) { + msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL); - msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL); + /* delete contact from Allow list and add it to Block in the callback */ + msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL); + } else { + /* just add the contact to Block list */ + msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_BL); + } + + msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL); } static void @@ -1103,10 +1226,12 @@ user = msn_userlist_find_user(userlist, who); - msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL); + msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL); + + msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL); if (user != NULL && user->list_op & MSN_LIST_RL_OP) - msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL); + msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL); } static void @@ -1124,32 +1249,18 @@ user = msn_userlist_find_user(userlist, who); - msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL); + msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL); + + msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL); if (user != NULL && user->list_op & MSN_LIST_RL_OP) - msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL); + msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL); } static void msn_set_permit_deny(PurpleConnection *gc) { - PurpleAccount *account; - MsnSession *session; - MsnCmdProc *cmdproc; - - account = purple_connection_get_account(gc); - session = gc->proto_data; - cmdproc = session->notification->cmdproc; - - if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL || - account->perm_deny == PURPLE_PRIVACY_DENY_USERS) - { - msn_cmdproc_send(cmdproc, "BLP", "%s", "AL"); - } - else - { - msn_cmdproc_send(cmdproc, "BLP", "%s", "BL"); - } + msn_send_privacy(gc); } static void @@ -1286,24 +1397,20 @@ PurpleGroup *group, GList *moved_buddies) { MsnSession *session; - MsnCmdProc *cmdproc; - int old_gid; - const char *enc_new_group_name; session = gc->proto_data; - cmdproc = session->notification->cmdproc; - enc_new_group_name = purple_url_encode(group->name); - - old_gid = msn_userlist_find_group_id(session->userlist, old_name); - - if (old_gid >= 0) + + g_return_if_fail(session != NULL); + g_return_if_fail(session->userlist != NULL); + + if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL) { - msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid, - enc_new_group_name); + msn_contact_rename_group(session, old_name, group->name); } else { - msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name); + /* not found */ + msn_add_group(session, NULL, group->name); } } @@ -1363,15 +1470,20 @@ { MsnSession *session; MsnCmdProc *cmdproc; - int group_id; session = gc->proto_data; cmdproc = session->notification->cmdproc; - if ((group_id = msn_userlist_find_group_id(session->userlist, group->name)) >= 0) + purple_debug_info("MSN", "Remove group %s\n", group->name); + /*we can't delete the default group*/ + if(!strcmp(group->name, MSN_INDIVIDUALS_GROUP_NAME)|| + !strcmp(group->name, MSN_NON_IM_GROUP_NAME)) { - msn_cmdproc_send(cmdproc, "RMG", "%d", group_id); + purple_debug_info("MSN", "This group can't be removed, returning.\n"); + return ; } + + msn_del_group(session, group->name); } /** @@ -1421,12 +1533,11 @@ { char *p, *q; - if ((p = strstr(url_text, " contactparams:photopreauthurl=\"")) != NULL) + if ((p = strstr(url_text, PHOTO_URL)) != NULL) { - p += strlen(" contactparams:photopreauthurl=\""); + p += strlen(PHOTO_URL); } - - if (p && (strncmp(p, "http://", 8) == 0) && ((q = strchr(p, '"')) != NULL)) + if (p && (strncmp(p, "http://",strlen("http://")) == 0) && ((q = strchr(p, '"')) != NULL)) return g_strndup(p, q - p); return NULL; @@ -1464,8 +1575,7 @@ msn_info_strip_search_link(const char *field, size_t len) { const char *c; - if ((c = strstr(field, " (http://spaces.live.com/default.aspx?page=searchresults")) == NULL && - (c = strstr(field, " (http://spaces.msn.com/default.aspx?page=searchresults")) == NULL) + if ((c = strstr(field, " (http://")) == NULL) return g_strndup(field, len); return g_strndup(field, c - field); } @@ -1491,7 +1601,7 @@ MsnGetInfoStepTwoData *info2_data = NULL; #endif - purple_debug_info("msn", "In msn_got_info\n"); + purple_debug_info("msn", "In msn_got_info,url_text:{%s}\n",url_text); /* Make sure the connection is still valid */ if (g_list_find(purple_connections_get_all(), info_data->gc) == NULL) @@ -1875,6 +1985,7 @@ #if PHOTO_SUPPORT /* Find the URL to the photo; must be before the marshalling [Bug 994207] */ photo_url_text = msn_get_photo_url(url_text); + purple_debug_info("MSNP14","photo url:{%s}\n",photo_url_text); /* Marshall the existing state */ info2_data = g_malloc0(sizeof(MsnGetInfoStepTwoData)); @@ -2099,7 +2210,7 @@ msn_add_deny, /* add_deny */ msn_rem_permit, /* rem_permit */ msn_rem_deny, /* rem_deny */ - msn_set_permit_deny, /* set_permit_deny */ + msn_set_permit_deny, /* set_permit_deny */ NULL, /* join_chat */ NULL, /* reject chat invite */ NULL, /* get_chat_name */ @@ -2155,10 +2266,11 @@ "MSN", /**< name */ VERSION, /**< version */ /** summary */ - N_("MSN Protocol Plugin"), + N_("Windows Live Messenger Protocol Plugin"), /** description */ - N_("MSN Protocol Plugin"), - "Christian Hammond ", /**< author */ + N_("Windows Live Messenger Protocol Plugin"), + "Christian Hammond , " + "MaYuan ", /**< author */ PURPLE_WEBSITE, /**< homepage */ msn_load, /**< load */ @@ -2183,11 +2295,11 @@ PurpleAccountOption *option; option = purple_account_option_string_new(_("Server"), "server", - MSN_SERVER); + WLM_SERVER); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_int_new(_("Port"), "port", 1863); + option = purple_account_option_int_new(_("Port"), "port", WLM_PORT); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msn.h --- a/libpurple/protocols/msn/msn.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/msn.h Fri Sep 28 20:58:48 2007 +0000 @@ -63,12 +63,23 @@ #define USEROPT_MSNPORT 4 #define MSN_PORT 1863 +/* Windows Live Messenger Server*/ +#define WLM_SERVER "muser.messenger.hotmail.com" +#define WLM_PORT 1863 +#define WLM_PROT_VER 13 +/*This MSNP14 Support chat with Yahoo Messenger*/ +#define WLM_YAHOO_PROT_VER 14 + +#define WLM_MAX_PROTOCOL 14 +#define WLM_MIN_PROTOCOL 13 + #define MSN_TYPING_RECV_TIMEOUT 6 #define MSN_TYPING_SEND_TIMEOUT 4 -#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders" +#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"w3 #define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login=" #define PROFILE_URL "http://spaces.live.com/profile.aspx?mem=" +#define PHOTO_URL " contactparams:photopreauthurl=\"" #define USEROPT_HOTMAIL 0 @@ -88,9 +99,11 @@ MSN_LIST_FL_OP = 0x01, MSN_LIST_AL_OP = 0x02, MSN_LIST_BL_OP = 0x04, - MSN_LIST_RL_OP = 0x08 + MSN_LIST_RL_OP = 0x08, + MSN_LIST_PL_OP = 0x10 } MsnListOp; +#define MSN_LIST_OP_MASK 0x07 typedef enum { @@ -131,4 +144,7 @@ (MSN_CLIENT_ID_RESERVED_2 << 8) | \ (MSN_CLIENT_ID_CAPABILITIES)) +void msn_act_id(PurpleConnection *gc, const char *entry); +void msn_send_privacy(PurpleConnection *gc); + #endif /* _MSN_H_ */ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msnutils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/msnutils.c Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,591 @@ +/** + * @file msnutils.c Utility functions + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#include "msn.h" +#include "msnutils.h" +#include "time.h" +//#include + +char *rand_guid(void); + +/************************************************************************** + * Util + **************************************************************************/ +char * +rand_guid() +{ + return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X", + rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111, + rand() % 0xAAFF + 0x1111); +} + +void +msn_parse_format(const char *mime, char **pre_ret, char **post_ret) +{ + char *cur; + GString *pre = g_string_new(NULL); + GString *post = g_string_new(NULL); + unsigned int colors[3]; + + if (pre_ret != NULL) *pre_ret = NULL; + if (post_ret != NULL) *post_ret = NULL; + + cur = strstr(mime, "FN="); + + if (cur && (*(cur = cur + 3) != ';')) + { + pre = g_string_append(pre, ""); + post = g_string_prepend(post, ""); + } + + cur = strstr(mime, "EF="); + + if (cur && (*(cur = cur + 3) != ';')) + { + while (*cur && *cur != ';') + { + pre = g_string_append_c(pre, '<'); + pre = g_string_append_c(pre, *cur); + pre = g_string_append_c(pre, '>'); + post = g_string_prepend_c(post, '>'); + post = g_string_prepend_c(post, *cur); + post = g_string_prepend_c(post, '/'); + post = g_string_prepend_c(post, '<'); + cur++; + } + } + + cur = strstr(mime, "CO="); + + if (cur && (*(cur = cur + 3) != ';')) + { + int i; + + i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]); + + if (i > 0) + { + char tag[64]; + + if (i == 1) + { + colors[1] = 0; + colors[2] = 0; + } + else if (i == 2) + { + unsigned int temp = colors[0]; + + colors[0] = colors[1]; + colors[1] = temp; + colors[2] = 0; + } + else if (i == 3) + { + unsigned int temp = colors[2]; + + colors[2] = colors[0]; + colors[0] = temp; + } + + g_snprintf(tag, sizeof(tag), + "", + colors[0], colors[1], colors[2]); + + pre = g_string_append(pre, tag); + post = g_string_prepend(post, ""); + } + } + + cur = strstr(mime, "RL="); + + if (cur && (*(cur = cur + 3) != ';')) + { + if (*cur == '1') + { + /* RTL text was received */ + pre = g_string_append(pre, ""); + post = g_string_prepend(post, ""); + } + } + + cur = g_strdup(purple_url_decode(pre->str)); + g_string_free(pre, TRUE); + + if (pre_ret != NULL) + *pre_ret = cur; + else + g_free(cur); + + cur = g_strdup(purple_url_decode(post->str)); + g_string_free(post, TRUE); + + if (post_ret != NULL) + *post_ret = cur; + else + g_free(cur); +} + +/*encode the str to RFC2047 style + * Currently only support the UTF-8 and base64 encode + */ +char * +msn_encode_mime(const char *str) +{ + char *base64; + + base64 = purple_base64_encode((guchar *)str, strlen(str)); + return g_strdup_printf("=?utf-8?B?%s?=", base64); +} + +/* + * We need this because we're only supposed to encode spaces in the font + * names. purple_url_encode() isn't acceptable. + */ +static const char * +encode_spaces(const char *str) +{ + static char buf[BUF_LEN]; + const char *c; + char *d; + + g_return_val_if_fail(str != NULL, NULL); + + for (c = str, d = buf; *c != '\0'; c++) + { + if (*c == ' ') + { + *d++ = '%'; + *d++ = '2'; + *d++ = '0'; + } + else + *d++ = *c; + } + + return buf; +} + +/* + * Taken from the zephyr plugin. + * This parses HTML formatting (put out by one of the gtkimhtml widgets + * and converts it to msn formatting. It doesn't deal with the tag closing, + * but gtkimhtml widgets give valid html. + * It currently deals properly with , , , , + * , , . + * It ignores and + */ +void +msn_import_html(const char *html, char **attributes, char **message) +{ + int len, retcount = 0; + const char *c; + char *msg; + char *fontface = NULL; + char fonteffect[4]; + char fontcolor[7]; + char direction = '0'; + + gboolean has_bold = FALSE; + gboolean has_italic = FALSE; + gboolean has_underline = FALSE; + gboolean has_strikethrough = FALSE; + + g_return_if_fail(html != NULL); + g_return_if_fail(attributes != NULL); + g_return_if_fail(message != NULL); + + len = strlen(html); + msg = g_malloc0(len + 1); + + memset(fontcolor, 0, sizeof(fontcolor)); + strcat(fontcolor, "0"); + memset(fonteffect, 0, sizeof(fonteffect)); + + for (c = html; *c != '\0';) + { + if (*c == '<') + { + if (!g_ascii_strncasecmp(c + 1, "br>", 3)) + { + msg[retcount++] = '\r'; + msg[retcount++] = '\n'; + c += 4; + } + else if (!g_ascii_strncasecmp(c + 1, "i>", 2)) + { + if (!has_italic) + { + strcat(fonteffect, "I"); + has_italic = TRUE; + } + c += 3; + } + else if (!g_ascii_strncasecmp(c + 1, "b>", 2)) + { + if (!has_bold) + { + strcat(fonteffect, "B"); + has_bold = TRUE; + } + c += 3; + } + else if (!g_ascii_strncasecmp(c + 1, "u>", 2)) + { + if (!has_underline) + { + strcat(fonteffect, "U"); + has_underline = TRUE; + } + c += 3; + } + else if (!g_ascii_strncasecmp(c + 1, "s>", 2)) + { + if (!has_strikethrough) + { + strcat(fonteffect, "S"); + has_strikethrough = TRUE; + } + c += 3; + } + else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8)) + { + c += 9; + + if (!g_ascii_strncasecmp(c, "mailto:", 7)) + c += 7; + + while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) + msg[retcount++] = *c++; + + if (*c != '\0') + c += 2; + + /* ignore descriptive string */ + while ((*c != '\0') && g_ascii_strncasecmp(c, "", 4)) + c++; + + if (*c != '\0') + c += 4; + } + else if (!g_ascii_strncasecmp(c + 1, "span", 4)) + { + /* Bi-directional text support using CSS properties in span tags */ + c += 5; + + while (*c != '\0' && *c != '>') + { + while (*c == ' ') + c++; + if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9)) + { + c += 9; + direction = '1'; + } + else if (!g_ascii_strncasecmp(c, "style=\"", 7)) + { + /* Parse inline CSS attributes */ + char *attributes; + int attr_len = 0; + c += 7; + while (*(c + attr_len) != '\0' && *(c + attr_len) != '"') + attr_len++; + if (*(c + attr_len) == '"') + { + char *attr_dir; + attributes = g_strndup(c, attr_len); + attr_dir = purple_markup_get_css_property(attributes, "direction"); + if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3))) + direction = '1'; + g_free(attr_dir); + g_free(attributes); + } + + } + else + { + c++; + } + } + if (*c == '>') + c++; + } + else if (!g_ascii_strncasecmp(c + 1, "font", 4)) + { + c += 5; + + while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1)) + c++; + + if (!g_ascii_strncasecmp(c, "color=\"#", 7)) + { + c += 8; + + fontcolor[0] = *(c + 4); + fontcolor[1] = *(c + 5); + fontcolor[2] = *(c + 2); + fontcolor[3] = *(c + 3); + fontcolor[4] = *c; + fontcolor[5] = *(c + 1); + + c += 8; + } + else if (!g_ascii_strncasecmp(c, "face=\"", 6)) + { + const char *end = NULL; + const char *comma = NULL; + unsigned int namelen = 0; + + c += 6; + end = strchr(c, '\"'); + comma = strchr(c, ','); + + if (comma == NULL || comma > end) + namelen = (unsigned int)(end - c); + else + namelen = (unsigned int)(comma - c); + + fontface = g_strndup(c, namelen); + c = end + 2; + } + else + { + /* Drop all unrecognized/misparsed font tags */ + while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) + c++; + + if (*c != '\0') + c += 2; + } + } + else + { + while ((*c != '\0') && (*c != '>')) + c++; + if (*c != '\0') + c++; + } + } + else if (*c == '&') + { + if (!g_ascii_strncasecmp(c, "<", 4)) + { + msg[retcount++] = '<'; + c += 4; + } + else if (!g_ascii_strncasecmp(c, ">", 4)) + { + msg[retcount++] = '>'; + c += 4; + } + else if (!g_ascii_strncasecmp(c, " ", 6)) + { + msg[retcount++] = ' '; + c += 6; + } + else if (!g_ascii_strncasecmp(c, """, 6)) + { + msg[retcount++] = '"'; + c += 6; + } + else if (!g_ascii_strncasecmp(c, "&", 5)) + { + msg[retcount++] = '&'; + c += 5; + } + else if (!g_ascii_strncasecmp(c, "'", 6)) + { + msg[retcount++] = '\''; + c += 6; + } + else + msg[retcount++] = *c++; + } + else + msg[retcount++] = *c++; + } + + if (fontface == NULL) + fontface = g_strdup("MS Sans Serif"); + + *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c", + encode_spaces(fontface), + fonteffect, fontcolor, direction); + *message = g_strdup(msg); + + g_free(fontface); + g_free(msg); +} + +void +msn_parse_socket(const char *str, char **ret_host, int *ret_port) +{ + char *host; + char *c; + int port; + + host = g_strdup(str); + + if ((c = strchr(host, ':')) != NULL){ + *c = '\0'; + port = atoi(c + 1); + }else{ + port = 1863; + } + + *ret_host = host; + *ret_port = port; +} +/*************************************************************************** + * MSN Time Related Funciton + ***************************************************************************/ +#if 0 +int +msn_convert_iso8601(const char *timestr,struct tm tm_time) +{ + char temp[64]; + struct tm ctime; + time_t ts; + + purple_debug_info("MSNP14","convert string is{%s}\n",timestr); + tzset(); + /*copy string first*/ + memset(temp, 0, sizeof(temp)); + strncpy(temp, timestr, strlen(timestr)); + + /*convert via strptime()*/ + memset(&ctime, 0, sizeof(struct tm)); + strptime(temp, "%d %b %Y %T %Z", &ctime); + ts = mktime(&ctime) - timezone; + localtime_r(&ts, tm_time); +} +#endif + +/*************************************************************************** + * MSN Challenge Computing Function + ***************************************************************************/ + +/* + * Handle MSN Chanllege computation + *This algorithm reference with http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges + */ +#define BUFSIZE 256 +void +msn_handle_chl(char *input, char *output) +{ + PurpleCipher *cipher; + PurpleCipherContext *context; + char *productKey = MSNP13_WLM_PRODUCT_KEY, + *productID = MSNP13_WLM_PRODUCT_ID, + *hexChars = "0123456789abcdef", + buf[BUFSIZE]; + unsigned char md5Hash[16], *newHash; + unsigned int *md5Parts, *chlStringParts, newHashParts[5]; + + long long nHigh=0, nLow=0; + + int i; + + /* Create the MD5 hash by using Purple MD5 algorithm*/ + cipher = purple_ciphers_find_cipher("md5"); + context = purple_cipher_context_new(cipher, NULL); + + purple_cipher_context_append(context, (const guchar *)input, + strlen(input)); + purple_cipher_context_append(context, (const guchar *)productKey, + strlen(productKey)); + purple_cipher_context_digest(context, sizeof(md5Hash), md5Hash, NULL); + purple_cipher_context_destroy(context); + + /* Split it into four integers */ + md5Parts = (unsigned int *)md5Hash; + for(i=0; i<4; i++){ + /* adjust endianess */ + md5Parts[i] = GUINT_TO_LE(md5Parts[i]); + + /* & each integer with 0x7FFFFFFF */ + /* and save one unmodified array for later */ + newHashParts[i] = md5Parts[i]; + md5Parts[i] &= 0x7FFFFFFF; + } + + /* make a new string and pad with '0' */ + snprintf(buf, BUFSIZE-5, "%s%s", input, productID); + i = strlen(buf); + memset(&buf[i], '0', 8 - (i % 8)); + buf[i + (8 - (i % 8))]='\0'; + + /* split into integers */ + chlStringParts = (unsigned int *)buf; + + /* this is magic */ + for (i=0; i<(strlen(buf)/4)-1; i+=2){ + long long temp; + + chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]); + chlStringParts[i+1] = GUINT_TO_LE(chlStringParts[i+1]); + + temp=(md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF; + nHigh=(md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF; + nLow=nLow + nHigh + temp; + } + nHigh=(nHigh+md5Parts[1]) % 0x7FFFFFFF; + nLow=(nLow+md5Parts[3]) % 0x7FFFFFFF; + + newHashParts[0]^=nHigh; + newHashParts[1]^=nLow; + newHashParts[2]^=nHigh; + newHashParts[3]^=nLow; + + /* adjust endianness */ + for(i=0; i<4; i++) + newHashParts[i] = GUINT_TO_LE(newHashParts[i]); + + /* make a string of the parts */ + newHash = (unsigned char *)newHashParts; + + /* convert to hexadecimal */ + for (i=0; i<16; i++) + { + output[i*2]=hexChars[(newHash[i]>>4)&0xF]; + output[(i*2)+1]=hexChars[newHash[i]&0xF]; + } + + output[32]='\0'; + +// purple_debug_info("MSNP14","chl output{%s}\n",output); +} diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/msnutils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/msnutils.h Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,60 @@ +/** + * @file msnutils.h Utility functions + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef _MSN_UTILS_H_ +#define _MSN_UTILS_H_ + +/*encode the str to RFC2047 style*/ +char * msn_encode_mime(const char *str); + +/** + * Generate the Random GUID + */ +char * rand_guid(void); + +/** + * Parses the MSN message formatting into a format compatible with Purple. + * + * @param mime The mime header with the formatting. + * @param pre_ret The returned prefix string. + * @param post_ret The returned postfix string. + * + * @return The new message. + */ +void msn_parse_format(const char *mime, char **pre_ret, char **post_ret); + +/** + * Parses the Purple message formatting (html) into the MSN format. + * + * @param html The html message to format. + * @param attributes The returned attributes string. + * @param message The returned message string. + * + * @return The new message. + */ +void msn_import_html(const char *html, char **attributes, char **message); + +void msn_parse_socket(const char *str, char **ret_host, int *ret_port); +void msn_handle_chl(char *input, char *output); + +#endif /* _MSN_UTILS_H_ */ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/nexus.c --- a/libpurple/protocols/msn/nexus.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/nexus.c Fri Sep 28 20:58:48 2007 +0000 @@ -22,9 +22,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" +#include "soap.h" #include "nexus.h" #include "notification.h" +#undef NEXUS_LOGIN_TWN + +/*Local Function Prototype*/ +static gboolean nexus_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc); + /************************************************************************** * Main **************************************************************************/ @@ -36,6 +42,9 @@ nexus = g_new0(MsnNexus, 1); nexus->session = session; + /*we must use SSL connection to do Windows Live ID authentication*/ + nexus->soapconn = msn_soap_new(session,nexus,1); + nexus->challenge_data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); @@ -45,24 +54,14 @@ void msn_nexus_destroy(MsnNexus *nexus) { - if (nexus->gsc) - purple_ssl_close(nexus->gsc); - - g_free(nexus->login_host); - - g_free(nexus->login_path); - if (nexus->challenge_data != NULL) g_hash_table_destroy(nexus->challenge_data); - if (nexus->input_handler > 0) - purple_input_remove(nexus->input_handler); - g_free(nexus->write_buf); - g_free(nexus->read_buf); - + msn_soap_destroy(nexus->soapconn); g_free(nexus); } +#if 0 /* khc */ /************************************************************************** * Util **************************************************************************/ @@ -121,285 +120,237 @@ nexus->written_cb(nexus, source, 0); } +#endif /************************************************************************** * Login **************************************************************************/ - static void -login_connect_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond); - -static void -login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data) +nexus_login_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error) { - MsnNexus *nexus; MsnSession *session; - nexus = data; - g_return_if_fail(nexus != NULL); - - nexus->gsc = NULL; - - session = nexus->session; + session = soapconn->session; g_return_if_fail(session != NULL); - msn_session_set_error(session, MSN_ERROR_AUTH, _("Unable to connect")); + soapconn->gsc = NULL; + + msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication:Unable to connect")); /* the above line will result in nexus being destroyed, so we don't want * to destroy it here, or we'd crash */ } -static void -nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnNexus *nexus = data; - MsnSession *session; - int len; - - session = nexus->session; - g_return_if_fail(session != NULL); - - if (nexus->input_handler == 0) - /* TODO: Use purple_ssl_input_add()? */ - nexus->input_handler = purple_input_add(nexus->gsc->fd, - PURPLE_INPUT_READ, nexus_login_written_cb, nexus); - - - len = msn_ssl_read(nexus); - - if (len < 0 && errno == EAGAIN) - return; - else if (len < 0) { - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - /* TODO: error handling */ - return; - } - - if (g_strstr_len(nexus->read_buf, nexus->read_len, - "\r\n\r\n") == NULL) - return; - - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - - purple_ssl_close(nexus->gsc); - nexus->gsc = NULL; - - purple_debug_misc("msn", "ssl buffer: {%s}\n", nexus->read_buf); - - if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL) - { - /* Redirect. */ - char *location, *c; - - location = strstr(nexus->read_buf, "Location: "); - if (location == NULL) - { - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - - return; - } - location = strchr(location, ' ') + 1; - - if ((c = strchr(location, '\r')) != NULL) - *c = '\0'; - - /* Skip the http:// */ - if ((c = strchr(location, '/')) != NULL) - location = c + 2; - - if ((c = strchr(location, '/')) != NULL) - { - g_free(nexus->login_path); - nexus->login_path = g_strdup(c); - - *c = '\0'; - } - - g_free(nexus->login_host); - nexus->login_host = g_strdup(location); - - nexus->gsc = purple_ssl_connect(session->account, - nexus->login_host, PURPLE_SSL_DEFAULT_PORT, - login_connect_cb, login_error_cb, nexus); - } - else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL) - { - const char *error; - - if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL) - { - if ((error = strstr(error, "cbtxt=")) != NULL) - { - const char *c; - char *temp; - - error += strlen("cbtxt="); - - if ((c = strchr(error, '\n')) == NULL) - c = error + strlen(error); - - temp = g_strndup(error, c - error); - error = purple_url_decode(temp); - g_free(temp); - if ((temp = strstr(error, " Do one of the following or try again:")) != NULL) - *temp = '\0'; - } - } - - msn_session_set_error(session, MSN_ERROR_AUTH, error); - } - else if (strstr(nexus->read_buf, "HTTP/1.1 503 Service Unavailable")) - { - msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL); - } - else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK")) - { - char *base, *c; - char *login_params; - -#if 0 - /* All your base are belong to us. */ - base = buffer; - - /* For great cookie! */ - while ((base = strstr(base, "Set-Cookie: ")) != NULL) - { - base += strlen("Set-Cookie: "); - - c = strchr(base, ';'); - - session->login_cookies = - g_list_append(session->login_cookies, - g_strndup(base, c - base)); - } -#endif - - base = strstr(nexus->read_buf, "Authentication-Info: "); - - g_return_if_fail(base != NULL); - - base = strstr(base, "from-PP='"); - base += strlen("from-PP='"); - c = strchr(base, '\''); - - login_params = g_strndup(base, c - base); - - msn_got_login_params(session, login_params); - - g_free(login_params); - - msn_nexus_destroy(nexus); - session->nexus = NULL; - return; - } - - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - -} - -/* this guards against missing hash entries */ -static char * -nexus_challenge_data_lookup(GHashTable *challenge_data, const char *key) -{ - char *entry; - - return (entry = (char *)g_hash_table_lookup(challenge_data, key)) ? - entry : "(null)"; -} - -void -login_connect_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond) +/*process the SOAP reply, get the Authentication Info*/ +static gboolean +nexus_login_read_cb(MsnSoapConn *soapconn) { MsnNexus *nexus; MsnSession *session; + + char *base, *c; + char *msn_twn_t,*msn_twn_p; + char *login_params; + char **elems, **cur, **tokens; + char * cert_str; + + nexus = soapconn->parent; + g_return_val_if_fail(nexus != NULL, TRUE); + session = nexus->session; + g_return_val_if_fail(session != NULL, FALSE); + + /*reply OK, we should process the SOAP body*/ + purple_debug_info("MSN Nexus","TWN Server Windows Live ID Reply OK!\n"); + + //TODO: we should parse it using XML +#ifdef NEXUS_LOGIN_TWN + base = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_START_TOKEN); + base += strlen(TWN_START_TOKEN); + c = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_END_TOKEN); +#else + base = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_START_TOKEN); + base += strlen(TWN_LIVE_START_TOKEN); + c = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_END_TOKEN); +#endif + login_params = g_strndup(base, c - base); + + // purple_debug_info("msn", "TWN Cert: {%s}\n", login_params); + + /* Parse the challenge data. */ + elems = g_strsplit(login_params, "&", 0); + + for (cur = elems; *cur != NULL; cur++){ + tokens = g_strsplit(*cur, "=", 2); + g_hash_table_insert(nexus->challenge_data, tokens[0], tokens[1]); + /* Don't free each of the tokens, only the array. */ + g_free(tokens); + } + + g_strfreev(elems); + + msn_twn_t = (char *)g_hash_table_lookup(nexus->challenge_data, "t"); + msn_twn_p = (char *)g_hash_table_lookup(nexus->challenge_data, "p"); + + /*setup the t and p parameter for session*/ + if (session->passport_info.t != NULL){ + g_free(session->passport_info.t); + } + session->passport_info.t = g_strdup(msn_twn_t); + + if (session->passport_info.p != NULL) + g_free(session->passport_info.p); + session->passport_info.p = g_strdup(msn_twn_p); + + cert_str = g_strdup_printf("t=%s&p=%s",msn_twn_t,msn_twn_p); + msn_got_login_params(session, cert_str); + + purple_debug_info("MSN Nexus","Close nexus connection!\n"); + g_free(cert_str); + g_free(login_params); + msn_nexus_destroy(nexus); + session->nexus = NULL; + + return FALSE; +} + +static void +nexus_login_written_cb(MsnSoapConn *soapconn) +{ + soapconn->read_cb = nexus_login_read_cb; +// msn_soap_read_cb(data,source,cond); +} + + +/*when connect, do the SOAP Style windows Live ID authentication */ +gboolean +nexus_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc) +{ + MsnNexus * nexus; + MsnSession *session; + char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf; + char *fs0,*fs; char *username, *password; char *request_str, *head, *tail; - char *buffer = NULL; - guint32 ctint; +#ifdef NEXUS_LOGIN_TWN + char *challenge_str; +#else + char *rst1_str,*rst2_str,*rst3_str; +#endif + + purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n"); - nexus = data; - g_return_if_fail(nexus != NULL); + g_return_val_if_fail(soapconn != NULL, FALSE); + + nexus = soapconn->parent; + g_return_val_if_fail(nexus != NULL, TRUE); - session = nexus->session; - g_return_if_fail(session != NULL); + session = soapconn->session; + g_return_val_if_fail(session != NULL, FALSE); + + msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING); msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE); - username = - g_strdup(purple_url_encode(purple_account_get_username(session->account))); + /*prepare the Windows Live ID authentication token*/ + username = g_strdup(purple_account_get_username(session->account)); + password = g_strdup(purple_connection_get_password(session->account->gc)); - password = - g_strdup(purple_url_encode(purple_connection_get_password(session->account->gc))); + lc = (char *)g_hash_table_lookup(nexus->challenge_data, "lc"); + id = (char *)g_hash_table_lookup(nexus->challenge_data, "id"); + tw = (char *)g_hash_table_lookup(nexus->challenge_data, "tw"); + fs0= (char *)g_hash_table_lookup(nexus->challenge_data, "fs"); + ru = (char *)g_hash_table_lookup(nexus->challenge_data, "ru"); + ct = (char *)g_hash_table_lookup(nexus->challenge_data, "ct"); + kpp= (char *)g_hash_table_lookup(nexus->challenge_data, "kpp"); + kv = (char *)g_hash_table_lookup(nexus->challenge_data, "kv"); + ver= (char *)g_hash_table_lookup(nexus->challenge_data, "ver"); + rn = (char *)g_hash_table_lookup(nexus->challenge_data, "rn"); + tpf= (char *)g_hash_table_lookup(nexus->challenge_data, "tpf"); - ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200; + /* + * add some fail-safe code to avoid windows Purple Crash bug #1540454 + * If any of these string is NULL, will return Authentication Fail! + * for when windows g_strdup_printf() implementation get NULL point,It crashed! + */ + if(!(lc && id && tw && ru && ct && kpp && kv && ver && tpf)){ + purple_debug_error("MSN Nexus","WLM Authenticate Key Error!\n"); + msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed")); + g_free(username); + g_free(password); + purple_ssl_close(gsc); + msn_nexus_destroy(nexus); + session->nexus = NULL; + return FALSE; + } - head = g_strdup_printf( - "GET %s HTTP/1.1\r\n" - "Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s", - nexus->login_path, - (char *)g_hash_table_lookup(nexus->challenge_data, "ru"), - username); + /* + * in old MSN NS server's "USR TWN S" return,didn't include fs string + * so we use a default "1" for fs. + */ + if(fs0){ + fs = g_strdup(fs0); + }else{ + fs = g_strdup("1"); + } - tail = g_strdup_printf( - "lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%" G_GUINT32_FORMAT ",kpp=%s,kv=%s,ver=%s,tpf=%s\r\n" - "User-Agent: MSMSGS\r\n" - "Host: %s\r\n" - "Connection: Keep-Alive\r\n" - "Cache-Control: no-cache\r\n", - nexus_challenge_data_lookup(nexus->challenge_data, "lc"), - nexus_challenge_data_lookup(nexus->challenge_data, "id"), - nexus_challenge_data_lookup(nexus->challenge_data, "tw"), - nexus_challenge_data_lookup(nexus->challenge_data, "fs"), - nexus_challenge_data_lookup(nexus->challenge_data, "ru"), - ctint, - nexus_challenge_data_lookup(nexus->challenge_data, "kpp"), - nexus_challenge_data_lookup(nexus->challenge_data, "kv"), - nexus_challenge_data_lookup(nexus->challenge_data, "ver"), - nexus_challenge_data_lookup(nexus->challenge_data, "tpf"), - nexus->login_host); +#ifdef NEXUS_LOGIN_TWN + challenge_str = g_strdup_printf( + "lc=%s&id=%s&tw=%s&fs=%s&ru=%s&ct=%s&kpp=%s&kv=%s&ver=%s&rn=%s&tpf=%s\r\n", + lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf + ); - buffer = g_strdup_printf("%s,pwd=XXXXXXXX,%s\r\n", head, tail); - request_str = g_strdup_printf("%s,pwd=%s,%s\r\n", head, password, tail); + /*build the SOAP windows Live ID XML body */ + tail = g_strdup_printf(TWN_ENVELOP_TEMPLATE,username,password,challenge_str ); + g_free(challenge_str); +#else + rst1_str = g_strdup_printf( + "id=%s&tw=%s&fs=%s&kpp=%s&kv=%s&ver=%s&rn=%s", + id,tw,fs,kpp,kv,ver,rn + ); + rst2_str = g_strdup_printf( + "fs=%s&id=%s&kv=%s&rn=%s&tw=%s&ver=%s", + fs,id,kv,rn,tw,ver + ); + rst3_str = g_strdup_printf("id=%s",id); + tail = g_strdup_printf(TWN_LIVE_ENVELOP_TEMPLATE,username,password,rst1_str,rst2_str,rst3_str); + g_free(rst1_str); + g_free(rst2_str); + g_free(rst3_str); +#endif + g_free(fs); - purple_debug_misc("msn", "Sending: {%s}\n", buffer); + soapconn->login_path = g_strdup(TWN_POST_URL); + head = g_strdup_printf( + "POST %s HTTP/1.1\r\n" + "Accept: text/*\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" + "Host: %s\r\n" + "Content-Length: %" G_GSIZE_FORMAT "\r\n" + "Connection: Keep-Alive\r\n" + "Cache-Control: no-cache\r\n\r\n", + soapconn->login_path, soapconn->login_host, strlen(tail)); - g_free(buffer); + request_str = g_strdup_printf("%s%s", head,tail); + +#ifdef MSN_SOAP_DEBUG + purple_debug_misc("MSN Nexus", "TWN Sending:\n%s\n", request_str); +#endif g_free(head); g_free(tail); g_free(username); g_free(password); - nexus->write_buf = request_str; - nexus->written_len = 0; - - nexus->read_len = 0; - - nexus->written_cb = nexus_login_written_cb; + /*prepare to send the SOAP request*/ + msn_soap_write(soapconn, request_str, nexus_login_written_cb); - nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE, - nexus_write_cb, nexus); - - nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE); - - return; - - + return TRUE; } +#if 0 /* khc */ static void nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond) { MsnNexus *nexus = data; int len; + char *da_login; char *base, *c; @@ -408,6 +359,7 @@ nexus->input_handler = purple_input_add(nexus->gsc->fd, PURPLE_INPUT_READ, nexus_connect_written_cb, nexus); + /* Get the PassportURLs line. */ len = msn_ssl_read(nexus); @@ -470,10 +422,13 @@ } +#endif + /************************************************************************** * Connect **************************************************************************/ +#if 0 /* khc */ static void nexus_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond) @@ -502,10 +457,12 @@ nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE); } +#endif + void msn_nexus_connect(MsnNexus *nexus) { - nexus->gsc = purple_ssl_connect(nexus->session->account, - "nexus.passport.com", PURPLE_SSL_DEFAULT_PORT, - nexus_connect_cb, login_error_cb, nexus); + /* Authenticate via Windows Live ID. */ + msn_soap_init(nexus->soapconn, MSN_TWN_SERVER, 1, nexus_login_connect_cb, nexus_login_error_cb); + msn_soap_connect(nexus->soapconn); } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/nexus.h --- a/libpurple/protocols/msn/nexus.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/nexus.h Fri Sep 28 20:58:48 2007 +0000 @@ -24,25 +24,124 @@ #ifndef _MSN_NEXUS_H_ #define _MSN_NEXUS_H_ +#include "soap.h" + +/*#define MSN_TWN_SERVER "loginnet.passport.com"*/ +#define MSN_TWN_SERVER "login.live.com" + +#define TWN_START_TOKEN "" +#define TWN_END_TOKEN "" + +#define TWN_POST_URL "/RST.srf" +#define TWN_ENVELOP_TEMPLATE ""\ + ""\ + "
"\ + ""\ + "{3:B}"\ + "4"\ + "1"\ + ""\ + "AQAAAAIAAABsYwQAAAAzMDg0"\ + ""\ + ""\ + ""\ + "%s"\ + "%s"\ + ""\ + ""\ + "
"\ + ""\ + ""\ + ""\ + "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"\ + ""\ + ""\ + "http://Passport.NET/tb"\ + ""\ + ""\ + ""\ + ""\ + "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"\ + ""\ + ""\ + "messenger.msn.com"\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "
" + +#define TWN_LIVE_START_TOKEN "" +#define TWN_LIVE_END_TOKEN "" +#define TWN_LIVE_ENVELOP_TEMPLATE ""\ +""\ + "
"\ + ""\ + "{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}"\ + "4"\ + "1"\ + ""\ + "AQAAAAIAAABsYwQAAAAyMDUy"\ + ""\ + ""\ + ""\ + "%s"\ + "%s"\ + ""\ + ""\ + "
"\ + ""\ + ""\ + ""\ + "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"\ + ""\ + ""\ + "http://Passport.NET/tb"\ + ""\ + ""\ + ""\ + ""\ + "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"\ + ""\ + ""\ + "messenger.msn.com"\ + ""\ + ""\ + ""\ + ""\ + ""\ + "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"\ + ""\ + ""\ + "contacts.msn.com"\ + ""\ + ""\ + ""\ + " "\ + ""\ + "http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue"\ + ""\ + ""\ + "voice.messenger.msn.com"\ + ""\ + " "\ + ""\ + ""\ + ""\ + ""\ +"
" + typedef struct _MsnNexus MsnNexus; struct _MsnNexus { MsnSession *session; - - char *login_host; - char *login_path; + MsnSoapConn *soapconn; + char * challenge_data_str; GHashTable *challenge_data; - PurpleSslConnection *gsc; - - guint input_handler; - - char *write_buf; - gsize written_len; - PurpleInputFunction written_cb; - - char *read_buf; - gsize read_len; }; void msn_nexus_connect(MsnNexus *nexus); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/notification.c Fri Sep 28 20:58:48 2007 +0000 @@ -25,7 +25,7 @@ #include "notification.h" #include "state.h" #include "error.h" -#include "msn-utils.h" +#include "msnutils.h" #include "page.h" #include "userlist.h" @@ -34,6 +34,15 @@ static MsnTable *cbs_table; +/**************************************************************************** + * Local Function Prototype + ****************************************************************************/ + +static void msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len); +static void +msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, + MsnListOp list_op, MsnUserType type); + /************************************************************************** * Main **************************************************************************/ @@ -92,7 +101,8 @@ MsnCmdProc *cmdproc; MsnSession *session; PurpleAccount *account; - char **a, **c, *vers; + GString *vers; + const char *ver_str; int i; g_return_if_fail(servconn != NULL); @@ -101,25 +111,24 @@ session = servconn->session; account = session->account; - /* Allocate an array for CVR0, NULL, and all the versions */ - a = c = g_new0(char *, session->protocol_ver - 8 + 3); + vers = g_string_new(""); - for (i = session->protocol_ver; i >= 8; i--) - *c++ = g_strdup_printf("MSNP%d", i); +/* for (i = session->protocol_ver; i >= WLM_MIN_PROTOCOL; i--) */ + for (i = WLM_MAX_PROTOCOL; i >= WLM_MIN_PROTOCOL; i--) + g_string_append_printf(vers, " MSNP%d", i); - *c++ = g_strdup("CVR0"); - - vers = g_strjoinv(" ", a); + g_string_append(vers, " CVR0"); if (session->login_step == MSN_LOGIN_STEP_START) msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE); else msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE2); - msn_cmdproc_send(cmdproc, "VER", "%s", vers); + /* Skip the initial space */ + ver_str = (vers->str + 1); + msn_cmdproc_send(cmdproc, "VER", "%s", ver_str); - g_strfreev(a); - g_free(vers); + g_string_free(vers, TRUE); } gboolean @@ -153,7 +162,7 @@ **************************************************************************/ static void -group_error_helper(MsnSession *session, const char *msg, int group_id, int error) +group_error_helper(MsnSession *session, const char *msg, const char *group_id, int error) { PurpleAccount *account; PurpleConnection *gc; @@ -172,11 +181,9 @@ else { const char *group_name; - group_name = - msn_userlist_find_group_name(session->userlist, - group_id); + group_name = msn_userlist_find_group_name(session->userlist,group_id); reason = g_strdup_printf(_("%s is not a valid group."), - group_name); + group_name ? group_name : ""); } } else @@ -214,7 +221,6 @@ PurpleAccount *account; account = cmdproc->session->account; - msn_cmdproc_send(cmdproc, "USR", "TWN I %s", purple_account_get_username(account)); } @@ -232,14 +238,17 @@ if (!g_ascii_strcasecmp(cmd->params[1], "OK")) { - /* OK */ + /* authenticate OK */ + /* friendly name part no longer true in msnp11 */ +#if 0 const char *friendly = purple_url_decode(cmd->params[3]); purple_connection_set_display_name(gc, friendly); - +#endif msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN); - msn_cmdproc_send(cmdproc, "SYN", "%s", "0"); +// msn_cmdproc_send(cmdproc, "SYN", "%s", "0"); + //TODO we should use SOAP contact to fetch contact list } else if (!g_ascii_strcasecmp(cmd->params[1], "TWN")) { @@ -249,15 +258,20 @@ session->nexus = msn_nexus_new(session); /* Parse the challenge data. */ - + session->nexus->challenge_data_str = g_strdup(cmd->params[3]); elems = g_strsplit(cmd->params[3], ",", 0); for (cur = elems; *cur != NULL; cur++) { - tokens = g_strsplit(*cur, "=", 2); + tokens = g_strsplit(*cur, "=", 2); + if(tokens[0]&&tokens[1]) + { + purple_debug_info("MSNP14","challenge %p,key:%s,value:%s\n", + session->nexus->challenge_data,tokens[0],tokens[1]); g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]); - /* Don't free each of the tokens, only the array. */ - g_free(tokens); + } + /* Don't free each of the tokens, only the array. */ + g_free(tokens); } g_strfreev(elems); @@ -322,8 +336,15 @@ return; } + /* + * Windows Live Messenger 8.0 + * Notice :CVR String discriminate! + * reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx + * to see the Local ID + */ msn_cmdproc_send(cmdproc, "CVR", - "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s", +// "0x0409 winnt 5.1 i386 MSG80BETA 8.0.0689 msmsgs %s", + "0x0804 winnt 5.1 i386 MSNMSGR 8.0.0792 msmsgs %s", purple_account_get_username(account)); } @@ -366,7 +387,7 @@ msg = msn_message_new_from_cmd(cmdproc->session, cmd); - msn_message_parse_payload(msg, payload, len); + msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM); #ifdef MSN_DEBUG_NS msn_message_show_readable(msg, "Notification", TRUE); #endif @@ -379,9 +400,12 @@ static void msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { + purple_debug_info("MSNP14","Processing MSG... \n"); + if(cmd->payload_len == 0){ + return; + } /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued * command and we are processing it */ - if (cmd->payload == NULL) { cmdproc->last_cmd->payload_cb = msg_cmd_post; @@ -391,32 +415,145 @@ { g_return_if_fail(cmd->payload_cb != NULL); + purple_debug_info("MSNP14","MSG payload:{%s}\n",cmd->payload); cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len); } } +/*send Message to Yahoo Messenger*/ +void +uum_send_msg(MsnSession *session,MsnMessage *msg) +{ + MsnCmdProc *cmdproc; + MsnTransaction *trans; + char *payload; + gsize payload_len; + int type; + + cmdproc = session->notification->cmdproc; + g_return_if_fail(msg != NULL); + payload = msn_message_gen_payload(msg, &payload_len); + purple_debug_info("MSNP14","send UUM,payload{%s},strlen:%d,len:%d\n", + payload,strlen(payload),payload_len); + type = msg->type; + trans = msn_transaction_new(cmdproc, "UUM","%s 32 %d %d",msg->remote_user,type,payload_len); + msn_transaction_set_payload(trans, payload, strlen(payload)); + msn_cmdproc_send_trans(cmdproc, trans); +} + +static void +ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + MsnMessage *msg; + PurpleConnection *gc; + const char *passport; + const char *content_type; + + purple_debug_info("MSNP14","Process UBM payload:%s\n",payload); + msg = msn_message_new_from_cmd(cmdproc->session, cmd); + + msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM); +#ifdef MSN_DEBUG_NS + msn_message_show_readable(msg, "Notification", TRUE); +#endif + + gc = cmdproc->session->account->gc; + passport = msg->remote_user; + + content_type = msn_message_get_content_type(msg); + purple_debug_info("MSNP14","type:%d\n",content_type); + if(!strcmp(content_type,"text/plain")){ + const char *value; + const char *body; + char *body_str; + char *body_enc; + char *body_final = NULL; + size_t body_len; + + body = msn_message_get_bin_data(msg, &body_len); + body_str = g_strndup(body, body_len); + body_enc = g_markup_escape_text(body_str, -1); + g_free(body_str); + + if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) { + char *pre, *post; + + msn_parse_format(value, &pre, &post); + body_final = g_strdup_printf("%s%s%s", pre ? pre : "", + body_enc ? body_enc : "", post ? post : ""); + g_free(pre); + g_free(post); + } + g_free(body_enc); + serv_got_im(gc, passport, body_final, 0, time(NULL)); + } + if(!strcmp(content_type,"text/x-msmsgscontrol")){ + if(msn_message_get_attr(msg, "TypingUser") != NULL){ + serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT, + PURPLE_TYPING); + } + } + if(!strcmp(content_type,"text/x-msnmsgr-datacast")){ + char *username, *str; + PurpleAccount *account; + PurpleBuddy *buddy; + const char *user; + + account = cmdproc->session->account; + user = msg->remote_user; + + if ((buddy = purple_find_buddy(account, user)) != NULL){ + username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1); + }else{ + username = g_markup_escape_text(user, -1); + } + + str = g_strdup_printf(_("%s just sent you a Nudge!"), username); + g_free(username); + msn_session_report_user(cmdproc->session,user,str,PURPLE_MESSAGE_SYSTEM); + g_free(str); + } + msn_message_destroy(msg); +} + +/*Yahoo msg process*/ +static void +ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_info("MSNP14","Processing UBM... \n"); + if(cmd->payload_len == 0){ + return; + } + /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued + * command and we are processing it */ + if (cmd->payload == NULL){ + cmdproc->last_cmd->payload_cb = ubm_cmd_post; + cmdproc->servconn->payload_len = atoi(cmd->params[2]); + }else{ + g_return_if_fail(cmd->payload_cb != NULL); + + purple_debug_info("MSNP14","UBM payload:{%s}\n",cmd->payload); + ubm_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len); + } +} + /************************************************************************** * Challenges + * we use MD5 to caculate the Challenges **************************************************************************/ - static void chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnTransaction *trans; char buf[33]; - const char *challenge_resp; - PurpleCipher *cipher; - PurpleCipherContext *context; - guchar digest[16]; - int i; +#if 0 cipher = purple_ciphers_find_cipher("md5"); context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, (const guchar *)cmd->params[1], strlen(cmd->params[1])); - - challenge_resp = "VT6PX?UQTM4WM%YR"; + challenge_resp = MSNP13_WLM_PRODUCT_KEY; purple_cipher_context_append(context, (const guchar *)challenge_resp, strlen(challenge_resp)); @@ -424,9 +561,14 @@ purple_cipher_context_destroy(context); for (i = 0; i < 16; i++) + { g_snprintf(buf + (i*2), 3, "%02x", digest[i]); - - trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9"); + } +#else + msn_handle_chl(cmd->params[1], buf); +#endif +// purple_debug_info("MSNP14","<params[1],buf); + trans = msn_transaction_new(cmdproc, "QRY", "%s 32", MSNP13_WLM_PRODUCT_ID); msn_transaction_set_payload(trans, buf, 32); @@ -436,43 +578,303 @@ /************************************************************************** * Buddy Lists **************************************************************************/ +/* add contact to xmlnode */ +static void +msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnUserType type) +{ + xmlnode *d_node,*c_node; + char **tokens; + const char *email,*domain; + char fmt_str[3]; + + g_return_if_fail(passport != NULL); + + purple_debug_info("MSNP14","Passport: %s, type: %d\n", passport, type); + tokens = g_strsplit(passport, "@", 2); + email = tokens[0]; + domain = tokens[1]; + + if (email == NULL || domain == NULL) { + purple_debug_error("msn", "Invalid passport (%s) specified to add to contact xml.\n", passport); + g_strfreev(tokens); + g_return_if_reached(); + } + + /*find a domain Node*/ + for(d_node = xmlnode_get_child(mlNode,"d"); d_node; d_node = xmlnode_get_next_twin(d_node)) + { + const char *attr = xmlnode_get_attrib(d_node,"n"); + if (attr == NULL) + continue; + if (!strcmp(attr,domain)) + break; + } + + if(d_node == NULL) + { + /*domain not found, create a new domain Node*/ + purple_debug_info("msn", "Didn't find existing domain node, adding one.\n"); + d_node = xmlnode_new("d"); + xmlnode_set_attrib(d_node, "n", domain); + xmlnode_insert_child(mlNode, d_node); + } + + /*create contact node*/ + c_node = xmlnode_new("c"); + xmlnode_set_attrib(c_node, "n", email); + + purple_debug_info("MSNP14", "list_op: %d\n", list_op); + g_snprintf(fmt_str, sizeof(fmt_str), "%d", list_op); + xmlnode_set_attrib(c_node, "l", fmt_str); + + if (type != MSN_USER_TYPE_UNKNOWN) + g_snprintf(fmt_str, sizeof(fmt_str), "%d", type); + else if (msn_user_is_yahoo(session->account, passport)) + g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_USER_TYPE_YAHOO); + else + g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_USER_TYPE_PASSPORT); + + /*mobile*/ + //type_str = g_strdup_printf("4"); + xmlnode_set_attrib(c_node, "t", fmt_str); + + xmlnode_insert_child(d_node, c_node); + + g_strfreev(tokens); +} static void -add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len) +{ + MsnTransaction *trans; + purple_debug_info("MSN Notification","Sending ADL with payload: %s\n", payload); + trans = msn_transaction_new(cmdproc, "ADL","%d", strlen(payload)); + msn_transaction_set_payload(trans, payload, strlen(payload)); + msn_cmdproc_send_trans(cmdproc, trans); +} + +/*dump contact info to NS*/ +void +msn_notification_dump_contact(MsnSession *session) +{ + MsnUser *user; + GList *l; + xmlnode *adl_node; + char *payload; + int payload_len; + int adl_count = 0; + const char *display_name; + + adl_node = xmlnode_new("ml"); + adl_node->child = NULL; + xmlnode_set_attrib(adl_node, "l", "1"); + + /*get the userlist*/ + for (l = session->userlist->users; l != NULL; l = l->next){ + user = l->data; + + /* skip RL & PL during initial dump */ + if (!(user->list_op & MSN_LIST_OP_MASK)) + continue; + + msn_add_contact_xml(session, adl_node, user->passport, + user->list_op & MSN_LIST_OP_MASK, user->type); + + /* each ADL command may contain up to 150 contacts */ + if (++adl_count % 150 == 0 || l->next == NULL) { + payload = xmlnode_to_str(adl_node,&payload_len); + + msn_notification_post_adl(session->notification->cmdproc, + payload, payload_len); + + g_free(payload); + xmlnode_free(adl_node); + + if (l->next) { + adl_node = xmlnode_new("ml"); + adl_node->child = NULL; + xmlnode_set_attrib(adl_node, "l", "1"); + } + } + } + + if (adl_count == 0) { + payload = xmlnode_to_str(adl_node,&payload_len); + + msn_notification_post_adl(session->notification->cmdproc, payload, payload_len); + + g_free(payload); + xmlnode_free(adl_node); + } + + display_name = purple_connection_get_display_name(session->account->gc); + if (display_name + && strcmp(display_name, + purple_account_get_username(session->account))) { + msn_act_id(session->account->gc, display_name); + } + +} + +/*Post FQY to NS,Inform add a Yahoo User*/ +void +msn_notification_send_fqy(MsnSession *session, const char *passport) +{ + MsnTransaction *trans; + MsnCmdProc *cmdproc; + char* email,*domain,*payload; + char **tokens; + + cmdproc = session->notification->cmdproc; + + tokens = g_strsplit(passport, "@", 2); + email = tokens[0]; + domain = tokens[1]; + + payload = g_strdup_printf("", domain, email); + trans = msn_transaction_new(cmdproc, "FQY","%d", strlen(payload)); + msn_transaction_set_payload(trans, payload, strlen(payload)); + msn_cmdproc_send_trans(cmdproc, trans); + + g_free(payload); + g_free(tokens); +} + +static void +blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ +} + +static void +adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + xmlnode *root, *domain_node; + + purple_debug_misc("MSN Notification", "Parsing received ADL XML data\n"); + + g_return_if_fail(payload != NULL); + + root = xmlnode_from_str(payload, (gssize) len); + + if (root == NULL) { + purple_debug_info("MSN Notification", "Invalid XML!\n"); + return; + } + for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) { + const gchar * domain = NULL; + xmlnode *contact_node = NULL; + + domain = xmlnode_get_attrib(domain_node, "n"); + + for (contact_node = xmlnode_get_child(domain_node, "c"); contact_node; contact_node = xmlnode_get_next_twin(contact_node)) { +// gchar *name = NULL, *friendlyname = NULL, *passport= NULL; + const gchar *list; + gint list_op = 0; + +// name = xmlnode_get_attrib(contact_node, "n"); + list = xmlnode_get_attrib(contact_node, "l"); + if (list != NULL) { + list_op = atoi(list); + } +// friendlyname = xmlnode_get_attrib(contact_node, "f"); + +// passport = g_strdup_printf("%s@%s", name, domain); + +// if (friendlyname != NULL) { +// decoded_friendlyname = g_strdup(purple_url_decode(friendlyname)); +// } else { +// decoded_friendlyname = g_strdup(passport); +// } + + if (list_op & MSN_LIST_RL_OP) { + /* someone is adding us */ +// got_new_entry(cmdproc->session->account->gc, passport, decoded_friendly_name); + msn_get_contact_list(cmdproc->session->contact, MSN_PS_PENDING_LIST, NULL); + } + +// g_free(decoded_friendly_name); +// g_free(passport); + } + } + + xmlnode_free(root); +} + +static void +adl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; - MsnUser *user; - const char *list; - const char *passport; - const char *friendly; - MsnListId list_id; - int group_id; - list = cmd->params[1]; - passport = cmd->params[3]; - friendly = purple_url_decode(cmd->params[4]); + g_return_if_fail(cmdproc != NULL); + g_return_if_fail(cmdproc->session != NULL); + g_return_if_fail(cmdproc->last_cmd != NULL); + g_return_if_fail(cmd != NULL); session = cmdproc->session; - user = msn_userlist_find_user(session->userlist, passport); + if ( !strcmp(cmd->params[1], "OK")) { + /* ADL ack */ + msn_session_finish_login(session); + } else { + cmdproc->last_cmd->payload_cb = adl_cmd_parse; + } + + return; +} - if (user == NULL) - { - user = msn_user_new(session->userlist, passport, friendly); - msn_userlist_add_user(session->userlist, user); - } - else - msn_user_set_friendly_name(user, friendly); +static void +adl_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) +{ + MsnSession *session; + PurpleAccount *account; + PurpleConnection *gc; + char *reason = NULL; + + session = cmdproc->session; + account = session->account; + gc = purple_account_get_connection(account); + + purple_debug_error("msn","ADL error\n"); + reason = g_strdup_printf(_("Unknown error (%d)"), error); + purple_notify_error(gc, NULL, _("Unable to add user"), reason); + g_free(reason); +} - list_id = msn_get_list_id(list); +static void +fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + purple_debug_info("MSN Notification","FQY payload:\n%s\n", payload); + g_return_if_fail(cmdproc->session != NULL); + g_return_if_fail(cmdproc->session->contact != NULL); +// msn_notification_post_adl(cmdproc, payload, len); +// msn_get_address_book(cmdproc->session->contact, MSN_AB_SAVE_CONTACT, NULL, NULL); +} - if (cmd->param_count >= 6) - group_id = atoi(cmd->params[5]); - else - group_id = -1; +static void +fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_info("MSNP14","Process FQY\n"); + cmdproc->last_cmd->payload_cb = fqy_cmd_post; +} - msn_got_add_user(session, user, list_id, group_id); - msn_user_update(user); +static void +rml_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ +#if 0 + MsnTransaction *trans; + char * payload; +#endif + + purple_debug_info("MSNP14","Process RML\n"); +#if 0 + trans = msn_transaction_new(cmdproc, "RML",""); + + msn_transaction_set_payload(trans, payload, strlen(payload)); + + msn_cmdproc_send_trans(cmdproc, trans); +#endif } static void @@ -567,24 +969,22 @@ group_name = purple_url_decode(cmd->params[2]); - msn_group_new(session->userlist, group_id, group_name); + msn_group_new(session->userlist, cmd->params[3], group_name); - /* There is a user that must me moved to this group */ + /* There is a user that must be moved to this group */ if (cmd->trans->data) { /* msn_userlist_move_buddy(); */ MsnUserList *userlist = cmdproc->session->userlist; - MsnMoveBuddy *data = cmd->trans->data; + MsnCallbackState *data = cmd->trans->data; if (data->old_group_name != NULL) { - msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->old_group_name); + msn_userlist_move_buddy(userlist, data->who, data->old_group_name, group_name); g_free(data->old_group_name); + } else { + // msn_add_contact_to_group(userlist, data, data->who, group_name); } - - msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, group_name); - g_free(data->who); - } } @@ -642,6 +1042,7 @@ PurpleConnection *gc; MsnUser *user; MsnObject *msnobj; + int wlmclient; const char *state, *passport, *friendly; session = cmdproc->session; @@ -650,7 +1051,9 @@ state = cmd->params[1]; passport = cmd->params[2]; - friendly = purple_url_decode(cmd->params[3]); + /*if a contact is actually on the WLM part or the yahoo part*/ + wlmclient = atoi(cmd->params[3]); + friendly = purple_url_decode(cmd->params[4]); user = msn_userlist_find_user(session->userlist, passport); @@ -658,9 +1061,9 @@ msn_user_set_friendly_name(user, friendly); - if (session->protocol_ver >= 9 && cmd->param_count == 6) + if (session->protocol_ver >= 9 && cmd->param_count == 8) { - msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5])); + msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6])); msn_user_set_object(user, msnobj); } @@ -692,6 +1095,7 @@ MsnUser *user; MsnObject *msnobj; int clientid; + int wlmclient; const char *state, *passport, *friendly, *old_friendly; session = cmdproc->session; @@ -700,7 +1104,8 @@ state = cmd->params[0]; passport = cmd->params[1]; - friendly = purple_url_decode(cmd->params[2]); + wlmclient = atoi(cmd->params[2]); + friendly = purple_url_decode(cmd->params[3]); user = msn_userlist_find_user(session->userlist, passport); @@ -713,10 +1118,9 @@ if (session->protocol_ver >= 9) { - if (cmd->param_count == 5) + if (cmd->param_count == 7) { - msnobj = - msn_object_new_from_string(purple_url_decode(cmd->params[4])); + msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5])); msn_user_set_object(user, msnobj); } else @@ -725,7 +1129,7 @@ } } - clientid = atoi(cmd->params[3]); + clientid = atoi(cmd->params[4]); user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE); msn_user_set_state(user, state); @@ -797,7 +1201,9 @@ prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session = cmdproc->session; - const char *type, *value; + const char *type, *value, *friendlyname; + + purple_debug_info("MSN Notification", "prp_cmd()\n"); g_return_if_fail(cmd->param_count >= 3); @@ -821,6 +1227,18 @@ msn_user_set_work_phone(session->user, NULL); else if (!strcmp(type, "PHM")) msn_user_set_mobile_phone(session->user, NULL); + else { + type = cmd->params[1]; + if (!strcmp(type, "MFN")) { + friendlyname = purple_url_decode(cmd->params[2]); + + msn_update_contact(session->contact, friendlyname); + + purple_connection_set_display_name( + purple_account_get_connection(session->account), + friendlyname); + } + } } } @@ -828,11 +1246,10 @@ reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; - int group_id; - const char *group_name; + const char *group_id, *group_name; session = cmdproc->session; - group_id = atoi(cmd->params[2]); + group_id = cmd->params[2]; group_name = purple_url_decode(cmd->params[3]); msn_userlist_rename_group_id(session->userlist, group_id, group_name); @@ -841,27 +1258,26 @@ static void reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) { - int group_id; + const char * group_id; char **params; params = g_strsplit(trans->params, " ", 0); - group_id = atoi(params[0]); + group_id = params[0]; group_error_helper(cmdproc->session, _("Unable to rename group"), group_id, error); g_strfreev(params); } +#if 0 static void rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; MsnUser *user; - const char *list; - const char *passport; + const char *group_id, *list, *passport; MsnListId list_id; - int group_id; session = cmdproc->session; list = cmd->params[1]; @@ -873,22 +1289,23 @@ list_id = msn_get_list_id(list); if (cmd->param_count == 5) - group_id = atoi(cmd->params[4]); + group_id = cmd->params[4]; else - group_id = -1; + group_id = NULL; msn_got_rem_user(session, user, list_id, group_id); msn_user_update(user); } +#endif static void rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; - int group_id; + const char *group_id; session = cmdproc->session; - group_id = atoi(cmd->params[2]); + group_id = cmd->params[2]; msn_userlist_remove_group_id(session->userlist, group_id); } @@ -896,18 +1313,19 @@ static void rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) { - int group_id; + const char *group_id; char **params; params = g_strsplit(trans->params, " ", 0); - group_id = atoi(params[0]); + group_id = params[0]; group_error_helper(cmdproc->session, _("Unable to delete group"), group_id, error); g_strfreev(params); } +#if 0 static void syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { @@ -938,6 +1356,7 @@ session->sync = sync; cmdproc->cbs_table = sync->cbs_table; } +#endif /************************************************************************** * Misc commands @@ -1146,6 +1565,103 @@ g_free(host); } +static void +gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + xmlnode * root; + gchar * buf; + + g_return_if_fail(cmd->payload != NULL); + + if ( (root = xmlnode_from_str(cmd->payload, cmd->payload_len)) == NULL) + { + purple_debug_error("MSN","Unable to parse GCF payload into a XML tree"); + return; + } + + buf = xmlnode_to_formatted_str(root, NULL); + + /* get the payload content */ + purple_debug_info("MSNP14","GCF command payload:\n%s\n",buf); + + g_free(buf); + xmlnode_free(root); +} + +static void +gcf_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_info("MSNP14","Processing GCF command\n"); + cmdproc->last_cmd->payload_cb = gcf_cmd_post; + return; +} + +static void +sbs_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_info("MSNP14","Processing SBS... \n"); + if(cmd->payload_len == 0){ + return; + } + /*get the payload content*/ +} + +/* + * Get the UBX's PSM info + * Post it to the User status + * Thanks for Chris 's code + */ +static void +ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + MsnSession *session; + PurpleAccount *account; + PurpleConnection *gc; + MsnUser *user; + const char *passport; + char *psm_str, *currentmedia_str, *str; + + /*get the payload content*/ +// purple_debug_info("MSNP14","UBX {%s} payload{%s}\n",cmd->params[0], cmd->payload); + + session = cmdproc->session; + account = session->account; + gc = purple_account_get_connection(account); + + passport = cmd->params[0]; + user = msn_userlist_find_user(session->userlist, passport); + + psm_str = msn_get_psm(cmd->payload,len); + currentmedia_str = msn_parse_currentmedia( + str = msn_get_currentmedia(cmd->payload, len)); + g_free(str); + + msn_user_set_statusline(user, psm_str); + msn_user_set_currentmedia(user, currentmedia_str); + msn_user_update(user); + + g_free(psm_str); + g_free(currentmedia_str); +} + +static void +ubx_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_misc("MSNP14","UBX received.\n"); + if(cmd->payload_len == 0){ + return; + } + cmdproc->last_cmd->payload_cb = ubx_cmd_post; +} + +static void +uux_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_misc("MSNP14","UUX received.\n"); +} + /************************************************************************** * Message Types **************************************************************************/ @@ -1155,6 +1671,7 @@ { MsnSession *session; const char *value; + const char *clLastChange; session = cmdproc->session; @@ -1187,10 +1704,27 @@ } if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL) + { session->passport_info.client_port = ntohs(atoi(value)); + } if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL) session->passport_info.sl = atol(value); + + /*starting retrieve the contact list*/ + clLastChange = purple_account_get_string(session->account, "CLLastChange", NULL); + session->contact = msn_contact_new(session); +#ifdef MSN_PARTIAL_LISTS + /* msn_userlist_load defeats all attempts at trying to detect blist sync issues */ + msn_userlist_load(session); + msn_get_contact_list(session->contact, MSN_PS_INITIAL, clLastChange); +#else + /* always get the full list? */ + msn_get_contact_list(session->contact, MSN_PS_INITIAL, NULL); +#endif +#if 0 + msn_contact_connect(session->contact); +#endif } static void @@ -1246,6 +1780,86 @@ g_hash_table_destroy(table); } +/*offline Message notification process*/ +static void +initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + MsnSession *session; + PurpleConnection *gc; + GHashTable *table; + const char *mdata, *unread; + + session = cmdproc->session; + gc = session->account->gc; + + if (strcmp(msg->remote_user, "Hotmail")) + /* This isn't an official message. */ + return; + + /*new a oim session*/ + session->oim = msn_oim_new(session); +// msn_oim_connect(session->oim); + + table = msn_message_get_hashtable_from_body(msg); + + mdata = g_hash_table_lookup(table, "Mail-Data"); + + if (mdata != NULL) + msn_parse_oim_msg(session->oim, mdata); + + if (g_hash_table_lookup(table, "Inbox-URL") == NULL) + { + g_hash_table_destroy(table); + return; + } + + if (session->passport_info.file == NULL) + { + MsnTransaction *trans; + trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX"); + msn_transaction_queue_cmd(trans, msg->cmd); + + msn_cmdproc_send_trans(cmdproc, trans); + + g_hash_table_destroy(table); + return; + } + + if (!purple_account_get_check_mail(session->account)) + { + g_hash_table_destroy(table); + return; + } + + unread = g_hash_table_lookup(table, "Inbox-Unread"); + + if (unread != NULL) + { + int count = atoi(unread); + + if (count > 0) + { + const char *passport; + const char *url; + + passport = msn_user_get_passport(session->user); + url = session->passport_info.file; + + purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL, + &passport, &url, NULL, NULL); + } + } + + g_hash_table_destroy(table); +} + +/*offline Message Notification*/ +static void +delete_oim_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + purple_debug_misc("MSN Notification","Delete OIM message.\n"); +} + static void email_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { @@ -1347,48 +1961,62 @@ } void -msn_notification_add_buddy(MsnNotification *notification, const char *list, - const char *who, const char *store_name, - int group_id) +msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list_id, + const char *who) { MsnCmdProc *cmdproc; + MsnListOp list_op = 1 << list_id; + xmlnode *adl_node; + char *payload; + int payload_len; + cmdproc = notification->servconn->cmdproc; - if (group_id < 0 && !strcmp(list, "FL")) - group_id = 0; + adl_node = xmlnode_new("ml"); + adl_node->child = NULL; + + msn_add_contact_xml(notification->session, adl_node, who, list_op, + MSN_USER_TYPE_PASSPORT); - if (group_id >= 0) - { - msn_cmdproc_send(cmdproc, "ADD", "%s %s %s %d", - list, who, store_name, group_id); - } - else - { - msn_cmdproc_send(cmdproc, "ADD", "%s %s %s", list, who, store_name); - } + payload = xmlnode_to_str(adl_node,&payload_len); + xmlnode_free(adl_node); + + msn_notification_post_adl(notification->servconn->cmdproc, + payload,payload_len); + g_free(payload); } void -msn_notification_rem_buddy(MsnNotification *notification, const char *list, - const char *who, int group_id) +msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId list_id, + const char *who) { MsnCmdProc *cmdproc; + MsnTransaction *trans; + MsnListOp list_op = 1 << list_id; + xmlnode *rml_node; + char *payload; + int payload_len; + cmdproc = notification->servconn->cmdproc; - if (group_id >= 0) - { - msn_cmdproc_send(cmdproc, "REM", "%s %s %d", list, who, group_id); - } - else - { - msn_cmdproc_send(cmdproc, "REM", "%s %s", list, who); - } + rml_node = xmlnode_new("ml"); + rml_node->child = NULL; + + msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_USER_TYPE_PASSPORT); + + payload = xmlnode_to_str(rml_node, &payload_len); + xmlnode_free(rml_node); + + purple_debug_info("MSN Notification","Send RML with payload:\n%s\n", payload); + trans = msn_transaction_new(cmdproc, "RML","%d", strlen(payload)); + msn_transaction_set_payload(trans, payload, strlen(payload)); + msn_cmdproc_send_trans(cmdproc, trans); + g_free(payload); } /************************************************************************** * Init **************************************************************************/ - void msn_notification_init(void) { @@ -1399,18 +2027,18 @@ /* Synchronous */ msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL); msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd); - msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd); - msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd); - msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd); + msn_table_add_cmd(cbs_table, "ADL", "ILN", iln_cmd); +// msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd); /* Removed as of MSNP13 */ msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd); msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd); - msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd); + msn_table_add_cmd(cbs_table, "USR", "GCF", gcf_cmd); +// msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd); /* Removed as of MSNP13 */ msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd); msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd); msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd); msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd); - /* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */ - msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL); + msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); +// msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL); msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd); msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd); msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd); @@ -1419,11 +2047,15 @@ /* Asynchronous */ msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd); msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd); + msn_table_add_cmd(cbs_table, NULL, "UBM", ubm_cmd); + msn_table_add_cmd(cbs_table, NULL, "GCF", gcf_cmd); + msn_table_add_cmd(cbs_table, NULL, "SBS", sbs_cmd); msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd); msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd); - msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd); - msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd); + msn_table_add_cmd(cbs_table, NULL, "RML", rml_cmd); + msn_table_add_cmd(cbs_table, NULL, "ADL", adl_cmd); + msn_table_add_cmd(cbs_table, NULL, "FQY", fqy_cmd); msn_table_add_cmd(cbs_table, NULL, "QRY", NULL); msn_table_add_cmd(cbs_table, NULL, "QNG", qng_cmd); @@ -1433,11 +2065,15 @@ msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd); msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd); + msn_table_add_cmd(cbs_table, NULL, "UBX", ubx_cmd); + msn_table_add_cmd(cbs_table, NULL, "UUX", uux_cmd); + msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd); msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd); msn_table_add_error(cbs_table, "ADD", add_error); + msn_table_add_error(cbs_table, "ADL", adl_error); msn_table_add_error(cbs_table, "REG", reg_error); msn_table_add_error(cbs_table, "RMG", rmg_error); /* msn_table_add_error(cbs_table, "REA", rea_error); */ @@ -1446,12 +2082,24 @@ msn_table_add_msg_type(cbs_table, "text/x-msmsgsprofile", profile_msg); + /*initial OIM notification*/ + msn_table_add_msg_type(cbs_table, + "text/x-msmsgsinitialmdatanotification", + initial_mdata_msg); + /*OIM notification when user online*/ + msn_table_add_msg_type(cbs_table, + "text/x-msmsgsoimnotification", + initial_mdata_msg); msn_table_add_msg_type(cbs_table, "text/x-msmsgsinitialemailnotification", initial_email_msg); msn_table_add_msg_type(cbs_table, "text/x-msmsgsemailnotification", email_msg); + /*delete an offline Message notification*/ + msn_table_add_msg_type(cbs_table, + "text/x-msmsgsactivemailnotification", + delete_oim_msg); msn_table_add_msg_type(cbs_table, "application/x-msmsgssystemmessage", system_msg); @@ -1462,3 +2110,4 @@ { msn_table_destroy(cbs_table); } + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/notification.h --- a/libpurple/protocols/msn/notification.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/notification.h Fri Sep 28 20:58:48 2007 +0000 @@ -24,6 +24,14 @@ #ifndef _MSN_NOTIFICATION_H_ #define _MSN_NOTIFICATION_H_ +/*MSN protocol challenge info*/ +/*MSNP13 challenge*/ +#define MSNP13_WLM_PRODUCT_KEY "O4BG@C7BWLYQX?5G" +#define MSNP13_WLM_PRODUCT_ID "PROD01065C%ZFN6F" + +#define MSNP10_PRODUCT_KEY "VT6PX?UQTM4WM%YR" +#define MSNP10_PRODUCT_ID "PROD0038W!61ZTF9" + typedef struct _MsnNotification MsnNotification; #include "session.h" @@ -40,21 +48,24 @@ }; #include "state.h" +void uum_send_msg(MsnSession *session,MsnMessage *msg); void msn_notification_end(void); void msn_notification_init(void); -void msn_notification_add_buddy(MsnNotification *notification, - const char *list, const char *who, - const char *store_name, int group_id); -void msn_notification_rem_buddy(MsnNotification *notification, - const char *list, const char *who, - int group_id); +void msn_notification_add_buddy_to_list(MsnNotification *notification, + MsnListId list_id, const char *who); +void msn_notification_rem_buddy_from_list(MsnNotification *notification, + MsnListId list_id, const char *who); + +void msn_notification_send_fqy(MsnSession *session, const char *passport); + MsnNotification *msn_notification_new(MsnSession *session); void msn_notification_destroy(MsnNotification *notification); gboolean msn_notification_connect(MsnNotification *notification, - const char *host, int port); + const char *host, int port); void msn_notification_disconnect(MsnNotification *notification); +void msn_notification_dump_contact(MsnSession *session); /** * Closes a notification. diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/oim.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/oim.c Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,712 @@ +/** + * @file oim.c + * get and send MSN offline Instant Message via SOAP request + * Author + * MaYuan + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "soap.h" +#include "oim.h" +#include "msnutils.h" + +/*Local Function Prototype*/ +static void msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid); +static MsnOimSendReq *msn_oim_new_send_req(const char *from_member, + const char *friendname, + const char* to_member, + gint send_seq, + const char *msg); +static void msn_oim_retrieve_connect_init(MsnSoapConn *soapconn); +static void msn_oim_send_connect_init(MsnSoapConn *soapconn); +static void msn_oim_free_send_req(MsnOimSendReq *req); +static void msn_oim_report_to_user(MsnOim *oim, const char *msg_str); +static void msn_oim_get_process(MsnOim *oim, const char *oim_msg); +static char *msn_oim_msg_to_str(MsnOim *oim, const char *body); +static void msn_oim_send_process(MsnOim *oim, const char *body, int len); + +/*new a OIM object*/ +MsnOim * +msn_oim_new(MsnSession *session) +{ + MsnOim *oim; + + oim = g_new0(MsnOim, 1); + oim->session = session; + oim->retrieveconn = msn_soap_new(session,oim,1); + + oim->oim_list = NULL; + oim->sendconn = msn_soap_new(session,oim,1); + oim->run_id = rand_guid(); + oim->challenge = NULL; + oim->send_queue = g_queue_new(); + oim->send_seq = 1; + return oim; +} + +/*destroy the oim object*/ +void +msn_oim_destroy(MsnOim *oim) +{ + MsnOimSendReq *request; + + purple_debug_info("OIM","destroy the OIM \n"); + msn_soap_destroy(oim->retrieveconn); + msn_soap_destroy(oim->sendconn); + g_free(oim->run_id); + g_free(oim->challenge); + + while((request = g_queue_pop_head(oim->send_queue)) != NULL){ + msn_oim_free_send_req(request); + } + g_queue_free(oim->send_queue); + + g_free(oim); +} + +static MsnOimSendReq * +msn_oim_new_send_req(const char *from_member, const char*friendname, + const char* to_member, gint send_seq, + const char *msg) +{ + MsnOimSendReq *request; + + request = g_new0(MsnOimSendReq, 1); + request->from_member =g_strdup(from_member); + request->friendname = g_strdup(friendname); + request->to_member = g_strdup(to_member); + request->send_seq = send_seq; + request->oim_msg = g_strdup(msg); + return request; +} + +static void +msn_oim_free_send_req(MsnOimSendReq *req) +{ + g_return_if_fail(req != NULL); + + g_free(req->from_member); + g_free(req->friendname); + g_free(req->to_member); + g_free(req->oim_msg); + + g_free(req); +} + +/**************************************** + * OIM send SOAP request + * **************************************/ +/*encode the message to OIM Message Format*/ +static char * +msn_oim_msg_to_str(MsnOim *oim, const char *body) +{ + char *oim_body,*oim_base64; + + purple_debug_info("MSN OIM","encode OIM Message...\n"); + oim_base64 = purple_base64_encode((const guchar *)body, strlen(body)); + purple_debug_info("MSN OIM","encoded base64 body:{%s}\n",oim_base64); + oim_body = g_strdup_printf(MSN_OIM_MSG_TEMPLATE, + oim->run_id,oim->send_seq,oim_base64); + + return oim_body; +} + +/*oim SOAP server login error*/ +static void +msn_oim_send_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error) +{ + MsnSession *session; + + session = soapconn->session; + g_return_if_fail(session != NULL); + + msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server")); +} + +/*msn oim SOAP server connect process*/ +static gboolean +msn_oim_send_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc) +{ + MsnSession * session; + MsnOim *oim; + + oim = soapconn->parent; + g_return_val_if_fail(oim != NULL, TRUE); + + session = oim->session; + g_return_val_if_fail(session != NULL, FALSE); + + return TRUE; +} + +/* + * Process the send return SOAP string + * If got SOAP Fault,get the lock key,and resend it. + */ +static void +msn_oim_send_process(MsnOim *oim, const char *body, int len) +{ + xmlnode *responseNode, *bodyNode; + xmlnode *faultNode, *faultCodeNode, *faultstringNode; + xmlnode *detailNode, *challengeNode; + char *faultCodeStr = NULL, *faultstring = NULL; + + responseNode = xmlnode_from_str(body,len); + g_return_if_fail(responseNode != NULL); + bodyNode = xmlnode_get_child(responseNode,"Body"); + faultNode = xmlnode_get_child(bodyNode,"Fault"); + if(faultNode == NULL){ + /*Send OK! return*/ + MsnOimSendReq *request; + + purple_debug_info("MSN OIM","send OIM OK!"); + xmlnode_free(responseNode); + request = g_queue_pop_head(oim->send_queue); + msn_oim_free_send_req(request); + /*send next buffered Offline Message*/ + msn_soap_post(oim->sendconn, NULL); + return; + } + /*get the challenge,and repost it*/ + faultCodeNode = xmlnode_get_child(faultNode,"faultcode"); + if(faultCodeNode == NULL){ + purple_debug_info("MSN OIM","faultcode Node is NULL\n"); + goto oim_send_process_fail; + } + faultCodeStr = xmlnode_get_data(faultCodeNode); + purple_debug_info("MSN OIM","fault code:{%s}\n",faultCodeStr); +#if 0 + if(!strcmp(faultCodeStr,"q0:AuthenticationFailed")){ + /*other Fault Reason?*/ + goto oim_send_process_fail; + } +#endif + + faultstringNode = xmlnode_get_child(faultNode,"faultstring"); + faultstring = xmlnode_get_data(faultstringNode); + purple_debug_info("MSN OIM","fault string :{%s}\n",faultstring); + + /* lock key fault reason, + * compute the challenge and resend it + */ + detailNode = xmlnode_get_child(faultNode, "detail"); + if(detailNode == NULL){ + goto oim_send_process_fail; + } + challengeNode = xmlnode_get_child(detailNode,"LockKeyChallenge"); + if (challengeNode == NULL) { + goto oim_send_process_fail; + } + + g_free(oim->challenge); + oim->challenge = xmlnode_get_data(challengeNode); + purple_debug_info("MSN OIM","lockkey:{%s}\n",oim->challenge); + + /*repost the send*/ + purple_debug_info("MSN OIM","prepare to repost the send...\n"); + msn_oim_send_msg(oim); + +oim_send_process_fail: + g_free(faultstring); + g_free(faultCodeStr); + xmlnode_free(responseNode); + return ; +} + +static gboolean +msn_oim_send_read_cb(MsnSoapConn *soapconn) +{ + MsnSession *session = soapconn->session; + MsnOim * oim; + + if (soapconn->body == NULL) + return TRUE; + + g_return_val_if_fail(session != NULL, FALSE); + oim = soapconn->session->oim; + g_return_val_if_fail(oim != NULL, TRUE); + + purple_debug_info("MSN OIM","read buffer:{%s}\n", soapconn->body); + msn_oim_send_process(oim,soapconn->body,soapconn->body_len); + + return TRUE; +} + +static void +msn_oim_send_written_cb(MsnSoapConn *soapconn) +{ + soapconn->read_cb = msn_oim_send_read_cb; +// msn_soap_read_cb(data,source,cond); +} + +void +msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername, + const char* friendname, const char *tomember, + const char * msg) +{ + MsnOimSendReq *request; + + g_return_if_fail(oim != NULL); + + request = msn_oim_new_send_req(membername,friendname,tomember,oim->send_seq,msg); + g_queue_push_tail(oim->send_queue,request); +} + +/*post send single message request to oim server*/ +void +msn_oim_send_msg(MsnOim *oim) +{ + MsnSoapReq *soap_request; + MsnOimSendReq *oim_request; + char *soap_body,*mspauth; + char *msg_body; + char buf[33]; + + g_return_if_fail(oim != NULL); + oim_request = g_queue_pop_head(oim->send_queue); + g_return_if_fail(oim_request != NULL); + + purple_debug_info("MSN OIM","send single OIM Message\n"); + mspauth = g_strdup_printf("t=%s&p=%s", + oim->session->passport_info.t, + oim->session->passport_info.p + ); + g_queue_push_head(oim->send_queue,oim_request); + + /* if we got the challenge lock key, we compute it + * else we go for the SOAP fault and resend it. + */ + if(oim->challenge != NULL){ + msn_handle_chl(oim->challenge, buf); + }else{ + purple_debug_info("MSN OIM","no lock key challenge,wait for SOAP Fault and Resend\n"); + buf[0]='\0'; + } + purple_debug_info("MSN OIM","get the lock key challenge {%s}\n",buf); + + msg_body = msn_oim_msg_to_str(oim, oim_request->oim_msg); + soap_body = g_strdup_printf(MSN_OIM_SEND_TEMPLATE, + oim_request->from_member, + oim_request->friendname, + oim_request->to_member, + mspauth, + MSNP13_WLM_PRODUCT_ID, + buf, + oim_request->send_seq, + msg_body + ); + soap_request = msn_soap_request_new(MSN_OIM_SEND_HOST, + MSN_OIM_SEND_URL, + MSN_OIM_SEND_SOAP_ACTION, + soap_body, + NULL, + msn_oim_send_read_cb, + msn_oim_send_written_cb, + msn_oim_send_connect_init); + g_free(mspauth); + g_free(msg_body); + g_free(soap_body); + + /*increase the offline Sequence control*/ + if(oim->challenge != NULL){ + oim->send_seq++; + } + msn_soap_post(oim->sendconn,soap_request); +} + +/**************************************** + * OIM delete SOAP request + * **************************************/ +static gboolean +msn_oim_delete_read_cb(MsnSoapConn *soapconn) +{ + if (soapconn->body == NULL) + return TRUE; + purple_debug_info("MSN OIM","OIM delete read buffer:{%s}\n",soapconn->body); + + msn_soap_free_read_buf(soapconn); + /*get next single Offline Message*/ +// msn_soap_post(soapconn,NULL); /* we already do this in soap.c */ + return TRUE; +} + +static void +msn_oim_delete_written_cb(MsnSoapConn *soapconn) +{ + soapconn->read_cb = msn_oim_delete_read_cb; +} + +/*Post to get the Offline Instant Message*/ +static void +msn_oim_post_delete_msg(MsnOim *oim,const char *msgid) +{ + MsnSoapReq *soap_request; + const char *soap_body,*t,*p; + + g_return_if_fail(oim != NULL); + g_return_if_fail(msgid != NULL); + + purple_debug_info("MSN OIM","Delete single OIM Message {%s}\n",msgid); + t = oim->session->passport_info.t; + p = oim->session->passport_info.p; + + soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE, + t, + p, + msgid + ); + soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST, + MSN_OIM_RETRIEVE_URL, + MSN_OIM_DEL_SOAP_ACTION, + soap_body, + NULL, + msn_oim_delete_read_cb, + msn_oim_delete_written_cb, + msn_oim_retrieve_connect_init); + msn_soap_post(oim->retrieveconn,soap_request); +} + +/**************************************** + * OIM get SOAP request + * **************************************/ +/*oim SOAP server login error*/ +static void +msn_oim_get_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error) +{ + MsnSession *session; + + session = soapconn->session; + g_return_if_fail(session != NULL); + msn_soap_clean_unhandled_requests(soapconn); + +// msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server")); +} + +/*msn oim SOAP server connect process*/ +static gboolean +msn_oim_get_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc) +{ + MsnSession * session; + MsnOim *oim; + + oim = soapconn->parent; + g_return_val_if_fail(oim != NULL, TRUE); + + session = oim->session; + g_return_val_if_fail(session != NULL, FALSE); + + purple_debug_info("MSN OIM","Connected and ready to get OIM!\n"); + + return TRUE; +} + +/* like purple_str_to_time, but different. The format of the timestamp + * is like this: 5 Sep 2007 21:42:12 -0700 */ +static time_t +msn_oim_parse_timestamp(const char *timestamp) +{ + char month_str[4], tz_str[6]; + char *tz_ptr = tz_str; + static const char *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL + }; + struct tm t; + memset(&t, 0, sizeof(t)); + + if (sscanf(timestamp, "%02d %03s %04d %02d:%02d:%02d %05s", + &t.tm_mday, month_str, &t.tm_year, + &t.tm_hour, &t.tm_min, &t.tm_sec, tz_str) == 7) { + gboolean offset_positive = TRUE; + int tzhrs; + int tzmins; + + for (t.tm_mon = 0; + months[t.tm_mon] != NULL && + strcmp(months[t.tm_mon], month_str) != 0; t.tm_mon++); + if (months[t.tm_mon] != NULL) { + if (*tz_str == '-') { + offset_positive = FALSE; + tz_ptr++; + } else if (*tz_str == '+') { + tz_ptr++; + } + + if (sscanf(tz_ptr, "%02d%02d", &tzhrs, &tzmins) == 2) { + time_t tzoff = tzhrs * 60 * 60 + tzmins * 60; +#ifdef _WIN32 + long sys_tzoff; +#endif + + if (!offset_positive) + tzoff *= -1; + + t.tm_year -= 1900; + t.tm_isdst = 0; + +#ifdef _WIN32 + if ((sys_tzoff = wpurple_get_tz_offset()) != -1) + tzoff += sys_tzoff; +#else +#ifdef HAVE_TM_GMTOFF + tzoff += t.tm_gmtoff; +#else +# ifdef HAVE_TIMEZONE + tzset(); /* making sure */ + tzoff -= timezone; +# endif +#endif +#endif /* _WIN32 */ + + return mktime(&t) + tzoff; + } + } + } + + purple_debug_info("MSN OIM:OIM", "Can't parse timestamp %s\n", timestamp); + return time(NULL); +} + +/*Post the Offline Instant Message to User Conversation*/ +static void +msn_oim_report_to_user(MsnOim *oim, const char *msg_str) +{ + MsnMessage *message; + char *date,*from,*decode_msg; + gsize body_len; + char **tokens; + char *start,*end; + int has_nick = 0; + char *passport_str, *passport; + char *msg_id; + time_t stamp; + + message = msn_message_new(MSN_MSG_UNKNOWN); + + msn_message_parse_payload(message, msg_str, strlen(msg_str), + MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM); + purple_debug_info("MSN OIM","oim body:{%s}\n",message->body); + decode_msg = (char *)purple_base64_decode(message->body,&body_len); + date = (char *)g_hash_table_lookup(message->attr_table, "Date"); + from = (char *)g_hash_table_lookup(message->attr_table, "From"); + if(strstr(from," ")){ + has_nick = 1; + } + if(has_nick){ + tokens = g_strsplit(from , " " , 2); + passport_str = g_strdup(tokens[1]); + purple_debug_info("MSN OIM","oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n", + date,tokens[0],tokens[1],passport_str); + g_strfreev(tokens); + }else{ + passport_str = g_strdup(from); + purple_debug_info("MSN OIM","oim Date:{%s},passport{%s}\n", + date,passport_str); + } + start = strstr(passport_str,"<"); + start += 1; + end = strstr(passport_str,">"); + passport = g_strndup(start,end - start); + g_free(passport_str); + purple_debug_info("MSN OIM","oim Date:{%s},passport{%s}\n",date,passport); + + stamp = msn_oim_parse_timestamp(date); + + serv_got_im(oim->session->account->gc, passport, decode_msg, 0, stamp); + + /*Now get the oim message ID from the oim_list. + * and append to read list to prepare for deleting the Offline Message when sign out + */ + if(oim->oim_list != NULL){ + msg_id = oim->oim_list->data; + msn_oim_post_delete_msg(oim,msg_id); + oim->oim_list = g_list_remove(oim->oim_list, oim->oim_list->data); + g_free(msg_id); + } + + g_free(passport); +} + +/* Parse the XML data, + * prepare to report the OIM to user + */ +static void +msn_oim_get_process(MsnOim *oim, const char *oim_msg) +{ + xmlnode *oim_node,*bodyNode,*responseNode,*msgNode; + char *msg_str; + + oim_node = xmlnode_from_str(oim_msg, strlen(oim_msg)); + bodyNode = xmlnode_get_child(oim_node,"Body"); + responseNode = xmlnode_get_child(bodyNode,"GetMessageResponse"); + msgNode = xmlnode_get_child(responseNode,"GetMessageResult"); + msg_str = xmlnode_get_data(msgNode); + purple_debug_info("OIM","msg:{%s}\n",msg_str); + msn_oim_report_to_user(oim,msg_str); + + g_free(msg_str); + xmlnode_free(oim_node); +} + +static gboolean +msn_oim_get_read_cb(MsnSoapConn *soapconn) +{ + MsnOim * oim = soapconn->session->oim; + + if (soapconn->body == NULL) + return TRUE; + + purple_debug_info("MSN OIM","OIM get read buffer:{%s}\n",soapconn->body); + + /*we need to process the read message!*/ + msn_oim_get_process(oim,soapconn->body); + msn_soap_free_read_buf(soapconn); + + /*get next single Offline Message*/ +// msn_soap_post(soapconn,NULL); /* we already do this in soap.c */ + return TRUE; +} + +static void +msn_oim_get_written_cb(MsnSoapConn *soapconn) +{ + soapconn->read_cb = msn_oim_get_read_cb; +// msn_soap_read_cb(data,source,cond); +} + +/* parse the oim XML data + * and post it to the soap server to get the Offline Message + * */ +void +msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg) +{ + xmlnode *node, *mNode,*ENode,*INode,*rtNode,*nNode; + char *passport,*msgid,*nickname, *unread, *rTime = NULL; + MsnSession *session = oim->session; + + purple_debug_info("MSN OIM:OIM", "%s", xmlmsg); + + node = xmlnode_from_str(xmlmsg, strlen(xmlmsg)); + if (strcmp(node->name, "MD") != 0) { + xmlnode_free(node); + return; + } + + ENode = xmlnode_get_child(node, "E"); + INode = xmlnode_get_child(ENode, "IU"); + unread = xmlnode_get_data(INode); + + if (unread != NULL && purple_account_get_check_mail(session->account)) + { + int count = atoi(unread); + + if (count > 0) + { + const char *passport; + const char *url; + + passport = msn_user_get_passport(session->user); + url = session->passport_info.file; + + purple_notify_emails(session->account->gc, atoi(unread), FALSE, NULL, NULL, + &passport, &url, NULL, NULL); + } + } + + for(mNode = xmlnode_get_child(node, "M"); mNode; + mNode = xmlnode_get_next_twin(mNode)){ + /*email Node*/ + ENode = xmlnode_get_child(mNode,"E"); + passport = xmlnode_get_data(ENode); + /*Index */ + INode = xmlnode_get_child(mNode,"I"); + msgid = xmlnode_get_data(INode); + /*Nickname*/ + nNode = xmlnode_get_child(mNode,"N"); + nickname = xmlnode_get_data(nNode); + /*receive time*/ + rtNode = xmlnode_get_child(mNode,"RT"); + if(rtNode != NULL) { + rTime = xmlnode_get_data(rtNode); + rtNode = NULL; + } +/* purple_debug_info("MSN OIM","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */ + + oim->oim_list = g_list_append(oim->oim_list,strdup(msgid)); + msn_oim_post_single_get_msg(oim,msgid); + g_free(passport); + g_free(msgid); + g_free(rTime); + rTime = NULL; + g_free(nickname); + } + g_free(unread); + xmlnode_free(node); +} + +/*Post to get the Offline Instant Message*/ +static void +msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid) +{ + MsnSoapReq *soap_request; + const char *soap_body,*t,*p; + + purple_debug_info("MSN OIM","Get single OIM Message\n"); + t = oim->session->passport_info.t; + p = oim->session->passport_info.p; + + soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE, + t, + p, + msgid + ); + soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST, + MSN_OIM_RETRIEVE_URL, + MSN_OIM_GET_SOAP_ACTION, + soap_body, + NULL, + msn_oim_get_read_cb, + msn_oim_get_written_cb, + msn_oim_retrieve_connect_init); + msn_soap_post(oim->retrieveconn,soap_request); +} + +/*msn oim retrieve server connect init */ +static void +msn_oim_retrieve_connect_init(MsnSoapConn *soapconn) +{ + purple_debug_info("MSN OIM","Initializing OIM retrieve connection\n"); + msn_soap_init(soapconn, MSN_OIM_RETRIEVE_HOST, 1, + msn_oim_get_connect_cb, + msn_oim_get_error_cb); +} + +/*Msn OIM Send Server Connect Init Function*/ +static void +msn_oim_send_connect_init(MsnSoapConn *sendconn) +{ + purple_debug_info("MSN OIM","Initializing OIM send connection\n"); + msn_soap_init(sendconn, MSN_OIM_SEND_HOST, 1, + msn_oim_send_connect_cb, + msn_oim_send_error_cb); +} + +/* EOF oim.c*/ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/oim.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/oim.h Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,148 @@ +/** + * @file oim.h Header file for oim.c + * Author + * MaYuan + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_OIM_H_ +#define _MSN_OIM_H_ + +/*OIM Retrieve SOAP Template*/ +#define MSN_OIM_RETRIEVE_HOST "rsi.hotmail.com" +#define MSN_OIM_RETRIEVE_URL "/rsi/rsi.asmx" +#define MSN_OIM_GET_SOAP_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage" + +#define MSN_OIM_GET_TEMPLATE ""\ +""\ + ""\ + ""\ + "%s"\ + "

%s

"\ + "
"\ + "
"\ + ""\ + ""\ + "%s"\ + "false"\ + ""\ + ""\ +"
" + +/*OIM Delete SOAP Template*/ +#define MSN_OIM_DEL_SOAP_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages" + +#define MSN_OIM_DEL_TEMPLATE ""\ +""\ + ""\ + ""\ + "%s"\ + "

%s

"\ + "
"\ + "
"\ + ""\ + ""\ + ""\ + "%s"\ + ""\ + ""\ + ""\ +"
" + +/*OIM Send SOAP Template*/ +#define MSN_OIM_MSG_TEMPLATE "MIME-Version: 1.0\n"\ + "Content-Type: text/plain; charset=UTF-8\n"\ + "Content-Transfer-Encoding: base64\n"\ + "X-OIM-Message-Type: OfflineMessage\n"\ + "X-OIM-Run-Id: {%s}\n"\ + "X-OIM-Sequence-Num: %d\n\n"\ + "%s" + +#define MSN_OIM_SEND_HOST "ows.messenger.msn.com" +#define MSN_OIM_SEND_URL "/OimWS/oim.asmx" +#define MSN_OIM_SEND_SOAP_ACTION "http://messenger.msn.com/ws/2004/09/oim/Store" +#define MSN_OIM_SEND_TEMPLATE ""\ +""\ + ""\ + ""\ + ""\ + ""\ + ""\ + "http://messenger.msn.com"\ + "%d"\ + ""\ + ""\ + ""\ + "text"\ + "%s"\ + ""\ +"" + +typedef struct _MsnOimSendReq MsnOimSendReq; + +struct _MsnOimSendReq +{ + char *from_member; + char *friendname; + char *to_member; + char *oim_msg; + gint send_seq; +}; + +typedef struct _MsnOim MsnOim; + +struct _MsnOim +{ + MsnSession *session; + + MsnSoapConn *retrieveconn; + GList * oim_list; + + MsnSoapConn *sendconn; + char *challenge; + char *run_id; + gint send_seq; + GQueue *send_queue; +}; + +/**************************************************** + * function prototype + * **************************************************/ +MsnOim * msn_oim_new(MsnSession *session); +void msn_oim_destroy(MsnOim *oim); +void msn_oim_connect(MsnOim *oim); + +void msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg); + +/*Send OIM Message*/ +void msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername, + const char *friendname, const char *tomember, + const char * msg); + +void msn_oim_send_msg(MsnOim *oim); + +/*get the OIM message*/ +void msn_oim_get_msg(MsnOim *oim); + +/*report the oim message to the conversation*/ +void msn_oim_report_user(MsnOim *oim,const char *passport,char *msg); + +#endif/* _MSN_OIM_H_*/ +/*endof oim.h*/ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/servconn.c --- a/libpurple/protocols/msn/servconn.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/servconn.c Fri Sep 28 20:58:48 2007 +0000 @@ -168,7 +168,7 @@ **************************************************************************/ static void -connect_cb(gpointer data, gint source, const gchar *error_message) +connect_cb(gpointer data, gint source, const char *error_message) { MsnServConn *servconn; @@ -245,7 +245,9 @@ return TRUE; } else + { return FALSE; + } } void @@ -390,14 +392,21 @@ len = read(servconn->fd, buf, sizeof(buf) - 1); - 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)); - msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ); + if (len <= 0) { + switch (errno) { + + case 0: - return; + case EBADF: + case EAGAIN: return; + + default: purple_debug_error("msn", "servconn read error," + "len: %d, errno: %d, error: %s\n", + len, errno, strerror(errno)); + msn_servconn_got_error(servconn, + MSN_SERVCONN_ERROR_READ); + return; + } } buf[len] = '\0'; @@ -446,6 +455,7 @@ else { msn_cmdproc_process_cmd_text(servconn->cmdproc, cur); + servconn->payload_len = servconn->cmdproc->last_cmd->payload_len; } } while (servconn->connected && !servconn->wasted && servconn->rx_len > 0); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/session.c Fri Sep 28 20:58:48 2007 +0000 @@ -43,7 +43,9 @@ session->user = msn_user_new(session->userlist, purple_account_get_username(account), NULL); - session->protocol_ver = 9; + /*if you want to chat with Yahoo Messenger*/ + //session->protocol_ver = WLM_YAHOO_PROT_VER; + session->protocol_ver = WLM_PROT_VER; session->conv_seq = 1; return session; @@ -70,6 +72,8 @@ msn_userlist_destroy(session->userlist); + g_free(session->passport_info.t); + g_free(session->passport_info.p); g_free(session->passport_info.kv); g_free(session->passport_info.sid); g_free(session->passport_info.mspauth); @@ -87,6 +91,11 @@ if (session->nexus != NULL) msn_nexus_destroy(session->nexus); + if (session->contact != NULL) + msn_contact_destroy(session->contact); + if (session->oim != NULL) + msn_oim_destroy(session->oim); + if (session->user != NULL) msn_user_destroy(session->user); @@ -154,6 +163,37 @@ return NULL; } +static PurpleConversation * +msn_session_get_conv(MsnSession *session,const char *passport) +{ + PurpleAccount *account; + PurpleConversation * conv; + + g_return_val_if_fail(session != NULL, NULL); + account = session->account; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + passport, account); + if(conv == NULL){ + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, passport); + } + return conv; +} + +/* put Message to User Conversation + * + * passport - the one want to talk to you + */ +void +msn_session_report_user(MsnSession *session,const char *passport,char *msg,PurpleMessageFlags flags) +{ + PurpleConversation * conv; + + if ((conv = msn_session_get_conv(session,passport)) != NULL){ + purple_conversation_write(conv, NULL, msg, flags, time(NULL)); + } +} + MsnSwitchBoard * msn_session_find_swboard_with_conv(MsnSession *session, PurpleConversation *conv) { @@ -229,13 +269,14 @@ /* The core used to use msn_add_buddy to add all buddies before * being logged in. This no longer happens, so we manually iterate - * over the whole buddy list to identify sync issues. */ - - for (gnode = purple_blist_get_root(); gnode; gnode = gnode->next) { + * over the whole buddy list to identify sync issues. + */ + for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) { PurpleGroup *group = (PurpleGroup *)gnode; - const char *group_name = group->name; + const char *group_name; if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; + group_name = group->name; for(cnode = gnode->child; cnode; cnode = cnode->next) { if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue; @@ -252,21 +293,17 @@ if ((remote_user != NULL) && (remote_user->list_op & MSN_LIST_FL_OP)) { - int group_id; GList *l; - group_id = msn_userlist_find_group_id(remote_user->userlist, - group_name); - for (l = remote_user->group_ids; l != NULL; l = l->next) { - if (group_id == GPOINTER_TO_INT(l->data)) + const char *name = msn_userlist_find_group_name(remote_user->userlist, l->data); + if (name && !g_strcasecmp(group_name, name)) { found = TRUE; break; } } - } if (!found) @@ -305,6 +342,8 @@ case MSN_ERROR_SIGN_OTHER: gc->wants_to_die = TRUE; msg = g_strdup(_("You have signed on from another location.")); + if (!purple_account_get_remember_password(session->account)) + purple_account_set_password(session->account, NULL); break; case MSN_ERROR_SERV_UNAVAILABLE: msg = g_strdup(_("The MSN servers are temporarily " @@ -419,3 +458,4 @@ msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX"); } } + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/session.h --- a/libpurple/protocols/msn/session.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/session.h Fri Sep 28 20:58:48 2007 +0000 @@ -38,6 +38,8 @@ #include "cmdproc.h" #include "nexus.h" #include "httpconn.h" +#include "contact.h" +#include "oim.h" #include "userlist.h" #include "sync.h" @@ -94,6 +96,8 @@ MsnNotification *notification; MsnNexus *nexus; + MsnContact *contact; + MsnOim *oim; MsnSync *sync; MsnUserList *userlist; @@ -105,8 +109,15 @@ int conv_seq; /**< The current conversation sequence number. */ + /*psm info*/ + char *psm; + struct { + /*t and p, get via USR TWN*/ + char *t; + char *p; + char *kv; char *sid; char *mspauth; @@ -114,7 +125,6 @@ char *file; char *client_ip; int client_port; - } passport_info; }; @@ -224,4 +234,8 @@ */ void msn_session_finish_login(MsnSession *session); +/*post message to User*/ +void msn_session_report_user(MsnSession *session,const char *passport, + char *msg,PurpleMessageFlags flags); + #endif /* _MSN_SESSION_H_ */ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/slp.c --- a/libpurple/protocols/msn/slp.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/slp.c Fri Sep 28 20:58:48 2007 +0000 @@ -33,6 +33,8 @@ /* ms to delay between sending buddy icon requests to the server. */ #define BUDDY_ICON_DELAY 20000 +/*debug SLP*/ +#define MSN_DEBUG_UD static void send_ok(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content); @@ -777,11 +779,11 @@ if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) { /* FIXME: it would be better if we wrote the data as we received it - instead of all at once, calling write multiple times and - close once at the very end - */ + instead of all at once, calling write multiple times and + close once at the very end + */ purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size); - purple_conv_custom_smiley_close(conv, slpcall->data_info); + purple_conv_custom_smiley_close(conv, slpcall->data_info ); } #ifdef MSN_DEBUG_UD purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/slpcall.c --- a/libpurple/protocols/msn/slpcall.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/slpcall.c Fri Sep 28 20:58:48 2007 +0000 @@ -22,6 +22,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" +#include "msnutils.h" #include "slpcall.h" #include "slpsession.h" @@ -30,24 +31,6 @@ /* #define MSN_DEBUG_SLPCALL */ /************************************************************************** - * Util - **************************************************************************/ - -static char * -rand_guid() -{ - return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X", - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111); -} - -/************************************************************************** * Main **************************************************************************/ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/slplink.c --- a/libpurple/protocols/msn/slplink.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/slplink.c Fri Sep 28 20:58:48 2007 +0000 @@ -112,8 +112,10 @@ if (slplink->remote_user != NULL) g_free(slplink->remote_user); +#if 0 if (slplink->directconn != NULL) msn_directconn_destroy(slplink->directconn); +#endif while (slplink->slp_calls != NULL) msn_slp_call_destroy(slplink->slp_calls->data); @@ -244,11 +246,13 @@ void msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg) { +#if 0 if (slplink->directconn != NULL) { msn_directconn_send_msg(slplink->directconn, msg); } else +#endif { if (slplink->swboard == NULL) { @@ -634,9 +638,10 @@ MsnDirectConn *directconn; directconn = slplink->directconn; - +#if 0 if (!directconn->acked) msn_directconn_send_handshake(directconn); +#endif } else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/soap.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/soap.c Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,870 @@ +/** + * @file soap.c + * SOAP connection related process + * Author + * MaYuan + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "msn.h" +#include "soap.h" + + +/*local function prototype*/ +void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step); + +/*setup the soap process step*/ +void +msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step) +{ +#ifdef MSN_SOAP_DEBUG + const char *MsnSoapStepText[] = + { + "Unconnected", + "Connecting", + "Connected", + "Processing", + "Connected Idle" + }; + + purple_debug_info("MSN SOAP", "Setting SOAP process step to %s\n", MsnSoapStepText[step]); +#endif + soapconn->step = step; +} + +//msn_soap_new(MsnSession *session,gpointer data,int sslconn) +/*new a soap connection*/ +MsnSoapConn * +msn_soap_new(MsnSession *session,gpointer data,int sslconn) +{ + MsnSoapConn *soapconn; + + soapconn = g_new0(MsnSoapConn, 1); + soapconn->session = session; + soapconn->parent = data; + soapconn->ssl_conn = sslconn; + + soapconn->gsc = NULL; + soapconn->input_handler = 0; + soapconn->output_handler = 0; + + msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); + soapconn->soap_queue = g_queue_new(); + + return soapconn; +} + +/*ssl soap connect callback*/ +void +msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, + PurpleInputCondition cond) +{ + MsnSoapConn * soapconn; + MsnSession *session; + gboolean soapconn_is_valid = FALSE; + + purple_debug_misc("MSN SOAP","SOAP server connection established!\n"); + + soapconn = data; + g_return_if_fail(soapconn != NULL); + + session = soapconn->session; + g_return_if_fail(session != NULL); + + soapconn->gsc = gsc; + + msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED); + + /*connection callback*/ + if (soapconn->connect_cb != NULL) { + soapconn_is_valid = soapconn->connect_cb(soapconn, gsc); + } + + if (!soapconn_is_valid) { + return; + } + + /*we do the SOAP request here*/ + msn_soap_post_head_request(soapconn); +} + +/*ssl soap error callback*/ +static void +msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data) +{ + MsnSoapConn * soapconn = data; + + g_return_if_fail(data != NULL); + + purple_debug_warning("MSN SOAP","Soap connection error!\n"); + + msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); + + /*error callback*/ + if (soapconn->error_cb != NULL) { + soapconn->error_cb(soapconn, gsc, error); + } else { + msn_soap_post(soapconn, NULL); + } +} + +/*init the soap connection*/ +void +msn_soap_init(MsnSoapConn *soapconn,char * host,int ssl, + MsnSoapSslConnectCbFunction connect_cb, + MsnSoapSslErrorCbFunction error_cb) +{ + purple_debug_misc("MSN SOAP","Initializing SOAP connection\n"); + soapconn->login_host = g_strdup(host); + soapconn->ssl_conn = ssl; + soapconn->connect_cb = connect_cb; + soapconn->error_cb = error_cb; +} + +/*connect the soap connection*/ +void +msn_soap_connect(MsnSoapConn *soapconn) +{ + if (soapconn->ssl_conn) { + purple_ssl_connect(soapconn->session->account, soapconn->login_host, + PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb, + soapconn); + } else { + } + + msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTING); +} + + +static void +msn_soap_close_handler(guint *handler) +{ + if (*handler > 0) { + purple_input_remove(*handler); + *handler = 0; + } +#ifdef MSN_SOAP_DEBUG + else { + purple_debug_misc("MSN SOAP", "Handler inactive, not removing\n"); + } +#endif + +} + + +/*close the soap connection*/ +void +msn_soap_close(MsnSoapConn *soapconn) +{ + if (soapconn->ssl_conn) { + if (soapconn->gsc != NULL) { + purple_ssl_close(soapconn->gsc); + soapconn->gsc = NULL; + } + } else { + } + msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); +} + +/*clean the unhandled SOAP request*/ +void +msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn) +{ + MsnSoapReq *request; + + g_return_if_fail(soapconn != NULL); + + soapconn->body = NULL; + + while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){ + if (soapconn->read_cb) { + soapconn->read_cb(soapconn); + } + msn_soap_request_free(request); + } +} + +/*destroy the soap connection*/ +void +msn_soap_destroy(MsnSoapConn *soapconn) +{ + if(soapconn->login_host) + g_free(soapconn->login_host); + + if(soapconn->login_path) + g_free(soapconn->login_path); + + /*remove the write handler*/ + if (soapconn->output_handler > 0){ + purple_input_remove(soapconn->output_handler); + soapconn->output_handler = 0; + } + /*remove the read handler*/ + if (soapconn->input_handler > 0){ + purple_input_remove(soapconn->input_handler); + soapconn->input_handler = 0; + } + msn_soap_free_read_buf(soapconn); + msn_soap_free_write_buf(soapconn); + + /*close ssl connection*/ + msn_soap_close(soapconn); + + /*process the unhandled soap request*/ + msn_soap_clean_unhandled_requests(soapconn); + + g_queue_free(soapconn->soap_queue); + g_free(soapconn); +} + +/*check the soap is connected? + * if connected return 1 + */ +int +msn_soap_connected(MsnSoapConn *soapconn) +{ + if (soapconn->ssl_conn) { + return (soapconn->gsc == NULL ? 0 : 1); + } + return (soapconn->fd > 0 ? 1 : 0); +} + +/*read and append the content to the buffer*/ +static gssize +msn_soap_read(MsnSoapConn *soapconn) +{ + gssize len, requested_len; + char temp_buf[MSN_SOAP_READ_BUFF_SIZE]; + + if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) { + requested_len = MSN_SOAP_READ_BUFF_SIZE; + } + else { + requested_len = soapconn->need_to_read; + } + + if ( soapconn->ssl_conn ) { + len = purple_ssl_read(soapconn->gsc, temp_buf, requested_len); + } else { + len = read(soapconn->fd, temp_buf, requested_len); + } + + + if ( len <= 0 ) { + switch (errno) { + + case 0: + case EBADF: /* we are sometimes getting this in Windows */ + case EAGAIN: return len; + + default : purple_debug_error("MSN SOAP", "Read error!" + "read len: %d, error = %s\n", + len, strerror(errno)); + purple_input_remove(soapconn->input_handler); + //soapconn->input_handler = 0; + g_free(soapconn->read_buf); + soapconn->read_buf = NULL; + soapconn->read_len = 0; + /* TODO: error handling */ + return len; + } + } + else { + soapconn->read_buf = g_realloc(soapconn->read_buf, + soapconn->read_len + len + 1); + if ( soapconn->read_buf != NULL ) { + memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len); + soapconn->read_len += len; + soapconn->read_buf[soapconn->read_len] = '\0'; + } + else { + purple_debug_error("MSN SOAP", "Failure re-allocating %d bytes of memory!\n", soapconn->read_len + len + 1); + exit(EXIT_FAILURE); + } + + } + +#if defined(MSN_SOAP_DEBUG) + if (len > 0) + purple_debug_info("MSN SOAP","Read %d bytes from SOAP server:\n%s\n", len, soapconn->read_buf + soapconn->read_len - len); +#endif + + return len; +} + +/*read the whole SOAP server response*/ +void +msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + MsnSoapConn *soapconn = data; + MsnSession *session; + int len; + char * body_start,*body_len; + char *length_start,*length_end; +#ifdef MSN_SOAP_DEBUG +#if !defined(_WIN32) + gchar * formattedxml = NULL; + gchar * http_headers = NULL; + xmlnode * node = NULL; +#endif + purple_debug_misc("MSN SOAP", "msn_soap_read_cb()\n"); +#endif + session = soapconn->session; + g_return_if_fail(session != NULL); + + + /*read the request header*/ + len = msn_soap_read(soapconn); + + if ( len < 0 ) + return; + + if (soapconn->read_buf == NULL) { + return; + } + + if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL) + || ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) ) + { + /* Redirect. */ + char *location, *c; + + purple_debug_info("MSN SOAP", "HTTP Redirect\n"); + location = strstr(soapconn->read_buf, "Location: "); + if (location == NULL) + { + c = (char *) g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n"); + if (c != NULL) { + /* we have read the whole HTTP headers and found no Location: */ + msn_soap_free_read_buf(soapconn); + msn_soap_post(soapconn, NULL); + } + + return; + } + location = strchr(location, ' ') + 1; + + if ((c = strchr(location, '\r')) != NULL) + *c = '\0'; + else + return; + + /* Skip the http:// */ + if ((c = strchr(location, '/')) != NULL) + location = c + 2; + + if ((c = strchr(location, '/')) != NULL) + { + g_free(soapconn->login_path); + soapconn->login_path = g_strdup(c); + + *c = '\0'; + } + + g_free(soapconn->login_host); + soapconn->login_host = g_strdup(location); + + msn_soap_close_handler( &(soapconn->input_handler) ); + msn_soap_close(soapconn); + + if (purple_ssl_connect(session->account, soapconn->login_host, + PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, + msn_soap_error_cb, soapconn) == NULL) { + + purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host); + // dispatch next request + msn_soap_post(soapconn, NULL); + } + } + /* Another case of redirection, active on May, 2007 + See http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener#Redirect + */ + else if (strstr(soapconn->read_buf, + "psf:Redirect") != NULL) + { + char *location, *c; + + if ( (location = strstr(soapconn->read_buf, "") ) == NULL) + return; + + /* Omit the tag preceding the URL */ + location += strlen(""); + if (location > soapconn->read_buf + soapconn->read_len) + return; + if ( (location = strstr(location, "://")) == NULL) + return; + + location += strlen("://"); /* Skip http:// or https:// */ + + if ( (c = strstr(location, "")) != NULL ) + *c = '\0'; + else + return; + + if ( (c = strstr(location, "/")) != NULL ) + { + g_free(soapconn->login_path); + soapconn->login_path = g_strdup(c); + *c = '\0'; + } + + g_free(soapconn->login_host); + soapconn->login_host = g_strdup(location); + + msn_soap_close_handler( &(soapconn->input_handler) ); + msn_soap_close(soapconn); + + if (purple_ssl_connect(session->account, soapconn->login_host, + PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, + msn_soap_error_cb, soapconn) == NULL) { + + purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host); + // dispatch next request + msn_soap_post(soapconn, NULL); + } + } + else if (strstr(soapconn->read_buf, "HTTP/1.1 401 Unauthorized") != NULL) + { + const char *error; + + purple_debug_error("MSN SOAP", "Received HTTP error 401 Unauthorized\n"); + if ((error = strstr(soapconn->read_buf, "WWW-Authenticate")) != NULL) + { + if ((error = strstr(error, "cbtxt=")) != NULL) + { + const char *c; + char *temp; + + error += strlen("cbtxt="); + + if ((c = strchr(error, '\n')) == NULL) + c = error + strlen(error); + + temp = g_strndup(error, c - error); + error = purple_url_decode(temp); + g_free(temp); + } + } + + msn_session_set_error(session, MSN_ERROR_AUTH, error); + } + /* Handle Passport 3.0 authentication failures. + * Further info: http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener + */ + else if (strstr(soapconn->read_buf, + "wsse:FailedAuthentication") != NULL) + { + gchar *faultstring; + + faultstring = strstr(soapconn->read_buf, ""); + + if (faultstring != NULL) + { + gchar *c; + faultstring += strlen(""); + if (faultstring < soapconn->read_buf + soapconn->read_len) { + c = strstr(soapconn->read_buf, ""); + if (c != NULL) { + *c = '\0'; + msn_session_set_error(session, MSN_ERROR_AUTH, faultstring); + } + } + } + + } + else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable")) + { + msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL); + } + else if ((strstr(soapconn->read_buf, "HTTP/1.1 200 OK")) + ||(strstr(soapconn->read_buf, "HTTP/1.1 500"))) + { + gboolean soapconn_is_valid = FALSE; + + /*OK! process the SOAP body*/ + body_start = (char *)g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n"); + if (!body_start) { + return; + } + body_start += 4; + + if (body_start > soapconn->read_buf + soapconn->read_len) + return; + + /* we read the content-length*/ + if ( (length_start = g_strstr_len(soapconn->read_buf, soapconn->read_len, "Content-Length: ")) != NULL) + length_start += strlen("Content-Length: "); + + if (length_start > soapconn->read_buf + soapconn->read_len) + return; + + if ( (length_end = strstr(length_start, "\r\n")) == NULL ) + return; + + body_len = g_strndup(length_start, length_end - length_start); + + /*setup the conn body */ + soapconn->body = body_start; + soapconn->body_len = atoi(body_len); + g_free(body_len); +#ifdef MSN_SOAP_DEBUG + purple_debug_misc("MSN SOAP","SOAP bytes read so far: %d, Content-Length: %d\n", soapconn->read_len, soapconn->body_len); +#endif + soapconn->need_to_read = (body_start - soapconn->read_buf + soapconn->body_len) - soapconn->read_len; + if ( soapconn->need_to_read > 0 ) { + return; + } + +#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32) + + node = xmlnode_from_str(soapconn->body, soapconn->body_len); + + if (node != NULL) { + formattedxml = xmlnode_to_formatted_str(node, NULL); + http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf); + + purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml); + g_free(http_headers); + g_free(formattedxml); + xmlnode_free(node); + } + else + purple_debug_info("MSN SOAP","Data received from the SOAP server:\n%s\n", soapconn->read_buf); +#endif + + /*remove the read handler*/ + msn_soap_close_handler( &(soapconn->input_handler) ); +// purple_input_remove(soapconn->input_handler); +// soapconn->input_handler = 0; + /* + * close the soap connection,if more soap request came, + * Just reconnect to do it, + * + * To solve the problem described below: + * When I post the soap request in one socket one after the other, + * The first read is ok, But the second soap read always got 0 bytes, + * Weird! + * */ + msn_soap_close(soapconn); + + /*call the read callback*/ + if ( soapconn->read_cb != NULL ) { + soapconn_is_valid = soapconn->read_cb(soapconn); + } + + if (!soapconn_is_valid) { + return; + } + + /* dispatch next request in queue */ + msn_soap_post(soapconn, NULL); + } + return; +} + +void +msn_soap_free_read_buf(MsnSoapConn *soapconn) +{ + g_return_if_fail(soapconn != NULL); + + if (soapconn->read_buf) { + g_free(soapconn->read_buf); + } + soapconn->read_buf = NULL; + soapconn->read_len = 0; + soapconn->need_to_read = 0; +} + +void +msn_soap_free_write_buf(MsnSoapConn *soapconn) +{ + g_return_if_fail(soapconn != NULL); + + if (soapconn->write_buf) { + g_free(soapconn->write_buf); + } + soapconn->write_buf = NULL; + soapconn->written_len = 0; +} + +/*Soap write process func*/ +static void +msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + MsnSoapConn *soapconn = data; + int len, total_len; + + g_return_if_fail(soapconn != NULL); + if ( soapconn->write_buf == NULL ) { + purple_debug_error("MSN SOAP","SOAP write buffer is NULL\n"); + // msn_soap_check_conn_errors(soapconn); + // purple_input_remove(soapconn->output_handler); + // soapconn->output_handler = 0; + msn_soap_close_handler( &(soapconn->output_handler) ); + return; + } + total_len = strlen(soapconn->write_buf); + + /* + * write the content to SSL server, + */ + len = purple_ssl_write(soapconn->gsc, + soapconn->write_buf + soapconn->written_len, + total_len - soapconn->written_len); + + if (len < 0 && errno == EAGAIN) + return; + else if (len <= 0){ + /*SSL write error!*/ +// msn_soap_check_conn_errors(soapconn); + + msn_soap_close_handler( &(soapconn->output_handler) ); +// purple_input_remove(soapconn->output_handler); +// soapconn->output_handler = 0; + + msn_soap_close(soapconn); + + /* TODO: notify of the error */ + purple_debug_error("MSN SOAP", "Error writing to SSL connection!\n"); + msn_soap_post(soapconn, NULL); + return; + } + soapconn->written_len += len; + + if (soapconn->written_len < total_len) + return; + + msn_soap_close_handler( &(soapconn->output_handler) ); +// purple_input_remove(soapconn->output_handler); +// soapconn->output_handler = 0; + + /*clear the write buff*/ + msn_soap_free_write_buf(soapconn); + + /* Write finish! + * callback for write done + */ + if(soapconn->written_cb != NULL){ + soapconn->written_cb(soapconn); + } + /*maybe we need to read the input?*/ + if ( soapconn->input_handler == 0 ) { + soapconn->input_handler = purple_input_add(soapconn->gsc->fd, + PURPLE_INPUT_READ, msn_soap_read_cb, soapconn); + } +} + +/*write the buffer to SOAP connection*/ +void +msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb) +{ + if (soapconn == NULL) { + return; + } + + msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING); + + soapconn->write_buf = write_buf; + soapconn->written_len = 0; + soapconn->written_cb = written_cb; + + msn_soap_free_read_buf(soapconn); + + /*clear the read buffer first*/ + /*start the write*/ + soapconn->output_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_WRITE, + msn_soap_write_cb, soapconn); + msn_soap_write_cb(soapconn, soapconn->gsc->fd, PURPLE_INPUT_WRITE); +} + +/* New a soap request*/ +MsnSoapReq * +msn_soap_request_new(const char *host,const char *post_url,const char *soap_action, + const char *body, const gpointer data_cb, + MsnSoapReadCbFunction read_cb, + MsnSoapWrittenCbFunction written_cb, + MsnSoapConnectInitFunction connect_init) +{ + MsnSoapReq *request; + + request = g_new0(MsnSoapReq, 1); + request->id = 0; + + request->login_host = g_strdup(host); + request->login_path = g_strdup(post_url); + request->soap_action = g_strdup(soap_action); + request->body = g_strdup(body); + request->data_cb = data_cb; + request->read_cb = read_cb; + request->written_cb = written_cb; + request->connect_init = connect_init; + + return request; +} + +/*free a soap request*/ +void +msn_soap_request_free(MsnSoapReq *request) +{ + g_return_if_fail(request != NULL); + + g_free(request->login_host); + g_free(request->login_path); + g_free(request->soap_action); + g_free(request->body); + request->read_cb = NULL; + request->written_cb = NULL; + request->connect_init = NULL; + + g_free(request); +} + +/*post the soap request queue's head request*/ +void +msn_soap_post_head_request(MsnSoapConn *soapconn) +{ + g_return_if_fail(soapconn != NULL); + g_return_if_fail(soapconn->soap_queue != NULL); + + if (soapconn->step == MSN_SOAP_CONNECTED || + soapconn->step == MSN_SOAP_CONNECTED_IDLE) { + + purple_debug_info("MSN SOAP", "Posting new request from head of the queue\n"); + + if ( !g_queue_is_empty(soapconn->soap_queue) ) { + MsnSoapReq *request; + + if ( (request = g_queue_pop_head(soapconn->soap_queue)) != NULL ) { + msn_soap_post_request(soapconn,request); + } + } else { + purple_debug_info("MSN SOAP", "No requests to process found.\n"); + msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED_IDLE); + } + } +} + +/*post the soap request , + * if not connected, Connected first. + */ +void +msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request) +{ + MsnSoapReq *head_request; + + if (soapconn == NULL) + return; + + if (request != NULL) { +#ifdef MSN_SOAP_DEBUG + purple_debug_misc("MSN SOAP", "Request added to the queue\n"); +#endif + g_queue_push_tail(soapconn->soap_queue, request); + } + + if ( !g_queue_is_empty(soapconn->soap_queue)) { + + /* we may have to reinitialize the soap connection, so avoid + * reusing the connection for now */ + + if (soapconn->step == MSN_SOAP_CONNECTED_IDLE) { + purple_debug_misc("MSN SOAP","Already connected to SOAP server, re-initializing\n"); + msn_soap_close_handler( &(soapconn->input_handler) ); + msn_soap_close_handler( &(soapconn->output_handler) ); + msn_soap_close(soapconn); + } + + if (!msn_soap_connected(soapconn) && (soapconn->step == MSN_SOAP_UNCONNECTED)) { + + /*not connected?and we have something to process connect it first*/ + purple_debug_misc("MSN SOAP","No connection to SOAP server. Connecting...\n"); + head_request = g_queue_peek_head(soapconn->soap_queue); + + if (head_request == NULL) { + purple_debug_error("MSN SOAP", "Queue is not empty, but failed to peek the head request!\n"); + return; + } + + if (head_request->connect_init != NULL) { + head_request->connect_init(soapconn); + } + msn_soap_connect(soapconn); + return; + } + + purple_debug_info("MSN SOAP", "Currently processing another SOAP request\n"); + } else { + purple_debug_info("MSN SOAP", "No requests left to dispatch\n"); + } +} + +/*Post the soap request action*/ +void +msn_soap_post_request(MsnSoapConn *soapconn, MsnSoapReq *request) +{ + char * soap_head = NULL; + char * request_str = NULL; +#ifdef MSN_SOAP_DEBUG +#if !defined(_WIN32) + xmlnode * node; +#endif + purple_debug_misc("MSN SOAP","msn_soap_post_request()\n"); +#endif + + msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING); + soap_head = g_strdup_printf( + "POST %s HTTP/1.1\r\n" + "SOAPAction: %s\r\n" + "Content-Type:text/xml; charset=utf-8\r\n" + "Cookie: MSPAuth=%s\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" + "Accept: */*\r\n" + "Host: %s\r\n" + "Content-Length: %" G_GSIZE_FORMAT "\r\n" + "Connection: Keep-Alive\r\n" + "Cache-Control: no-cache\r\n\r\n", + request->login_path, + request->soap_action, + soapconn->session->passport_info.mspauth, + request->login_host, + strlen(request->body) + ); + request_str = g_strdup_printf("%s%s", soap_head, request->body); + +#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32) + node = xmlnode_from_str(request->body, -1); + if (node != NULL) { + char *formattedstr = xmlnode_to_formatted_str(node, NULL); + purple_debug_info("MSN SOAP","Posting request to SOAP server:\n%s%s\n",soap_head, formattedstr); + g_free(formattedstr); + xmlnode_free(node); + } + else + purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str); +#endif + + g_free(soap_head); + /*free read buffer*/ + // msn_soap_free_read_buf(soapconn); + /*post it to server*/ + soapconn->data_cb = request->data_cb; + msn_soap_write(soapconn, request_str, request->written_cb); +} + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/soap.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/soap.h Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,167 @@ +/** + * @file soap.h + * header file for SOAP connection related process + * Author + * MaYuan + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _MSN_SOAP_H_ +#define _MSN_SOAP_H_ + +#define MSN_SOAP_READ_BUFF_SIZE 8192 + +/* define this to debug the communications with the SOAP server */ +/* #define MSN_SOAP_DEBUG */ + +#define MSN_SOAP_READ 1 +#define MSN_SOAP_WRITE 2 + +typedef enum +{ + MSN_SOAP_UNCONNECTED, + MSN_SOAP_CONNECTING, + MSN_SOAP_CONNECTED, + MSN_SOAP_PROCESSING, + MSN_SOAP_CONNECTED_IDLE +}MsnSoapStep; + +/* MSN SoapRequest structure*/ +typedef struct _MsnSoapReq MsnSoapReq; + +/* MSN Https connection structure*/ +typedef struct _MsnSoapConn MsnSoapConn; + +typedef void (*MsnSoapConnectInitFunction)(MsnSoapConn *); +typedef gboolean (*MsnSoapReadCbFunction)(MsnSoapConn *); +typedef void (*MsnSoapWrittenCbFunction)(MsnSoapConn *); + +typedef gboolean (*MsnSoapSslConnectCbFunction)(MsnSoapConn *, PurpleSslConnection *); +typedef void (*MsnSoapSslErrorCbFunction)(MsnSoapConn *, PurpleSslConnection *, PurpleSslErrorType); + + +struct _MsnSoapReq{ + /*request sequence*/ + int id; + + char *login_host; + char *login_path; + char *soap_action; + + char *body; + + gpointer data_cb; + MsnSoapReadCbFunction read_cb; + MsnSoapWrittenCbFunction written_cb; + MsnSoapConnectInitFunction connect_init; +}; + +struct _MsnSoapConn{ + MsnSession *session; + gpointer parent; + + char *login_host; + char *login_path; + char *soap_action; + + MsnSoapStep step; + /*ssl connection?*/ + guint ssl_conn; + /*normal connection*/ + guint fd; + /*SSL connection*/ + PurpleSslConnection *gsc; + /*ssl connection callback*/ + MsnSoapSslConnectCbFunction connect_cb; + /*ssl error callback*/ + MsnSoapSslErrorCbFunction error_cb; + + /*read handler*/ + guint input_handler; + /*write handler*/ + guint output_handler; + + /*Queue of SOAP request to send*/ + int soap_id; + GQueue *soap_queue; + + /*write buffer*/ + char *write_buf; + gsize written_len; + MsnSoapWrittenCbFunction written_cb; + + /*read buffer*/ + char *read_buf; + gsize read_len; + gsize need_to_read; + MsnSoapReadCbFunction read_cb; + + gpointer data_cb; + + /*HTTP reply body part*/ + char *body; + int body_len; +}; + + +/*Function Prototype*/ +/*Soap Request Function */ +MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url, + const char *soap_action, const char *body, + const gpointer data_cb, + MsnSoapReadCbFunction read_cb, + MsnSoapWrittenCbFunction written_cb, + MsnSoapConnectInitFunction connect_init); + +void msn_soap_request_free(MsnSoapReq *request); +void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request); +void msn_soap_post_head_request(MsnSoapConn *soapconn); + +/*new a soap conneciton */ +MsnSoapConn *msn_soap_new(MsnSession *session,gpointer data,int sslconn); + +/*destroy */ +void msn_soap_destroy(MsnSoapConn *soapconn); + +/*init a soap conneciton */ +void msn_soap_init(MsnSoapConn *soapconn, char * host, int ssl, + MsnSoapSslConnectCbFunction connect_cb, + MsnSoapSslErrorCbFunction error_cb); +void msn_soap_connect(MsnSoapConn *soapconn); +void msn_soap_close(MsnSoapConn *soapconn); + +/*write to soap*/ +void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb); +void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request); + +void msn_soap_free_read_buf(MsnSoapConn *soapconn); +void msn_soap_free_write_buf(MsnSoapConn *soapconn); +void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); +void msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond); + +/*clean the unhandled requests*/ +void msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn); + +/*check if the soap connection is connected*/ +int msn_soap_connected(MsnSoapConn *soapconn); +void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step); + +#endif/*_MSN_SOAP_H_*/ + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/state.c --- a/libpurple/protocols/msn/state.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/state.c Fri Sep 28 20:58:48 2007 +0000 @@ -38,6 +38,197 @@ N_("Available") }; +/* Local Function Prototype*/ +static char *msn_build_psm(const char *psmstr,const char *mediastr, + const char *guidstr); + +/* + * WLM media PSM info build prcedure + * + * Result can like: + * \0Music\01\0{0} - {1}\0Song Title\0Song Artist\0Song Album\0\0\ + * \0Games\01\0Playing {0}\0Game Name\0\ + * \0Office\01\0Office Message\0Office App Name\0" + */ +static char * +msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr) +{ + xmlnode *dataNode,*psmNode,*mediaNode,*guidNode; + char *result; + int length; + + dataNode = xmlnode_new("Data"); + + psmNode = xmlnode_new("PSM"); + if(psmstr != NULL){ + xmlnode_insert_data(psmNode,psmstr,strlen(psmstr)); + } + xmlnode_insert_child(dataNode,psmNode); + + mediaNode = xmlnode_new("CurrentMedia"); + if(mediastr != NULL){ + xmlnode_insert_data(mediaNode,mediastr,strlen(mediastr)); + } + xmlnode_insert_child(dataNode,mediaNode); + + guidNode = xmlnode_new("MachineGuid"); + if(guidstr != NULL){ + xmlnode_insert_data(guidNode,guidstr,strlen(guidstr)); + } + xmlnode_insert_child(dataNode,guidNode); + + result = xmlnode_to_str(dataNode,&length); + xmlnode_free(dataNode); + return result; +} + +/* parse CurrentMedia string */ +char * +msn_parse_currentmedia(const char *cmedia) +{ + char **cmedia_array; + GString *buffer = NULL; + int strings; + + if ((cmedia == NULL) || (*cmedia == '\0')) { + purple_debug_info("msn", "No currentmedia string\n"); + return NULL; + } + + purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia); + + cmedia_array = g_strsplit(cmedia, "\\0", 0); + + strings = 0; + /* Yes, we want to skip the first element here, as it is empty due to + * the cmedia string starting with \0 -- see the examples below. */ + while (cmedia_array[++strings] != NULL); + + /* The cmedia_array[2] field contains a 1 if enabled. */ + if ((strings > 3) && (!strcmp(cmedia_array[2], "1"))) { + char *inptr = cmedia_array[3]; + + buffer = g_string_new(NULL); + + while (*inptr != '\0') { + if ((*inptr == '{') && ((*(inptr + 1) != '\0') && (*(inptr+2) == '}'))) { + char *tmpptr; + int tmp; + + errno = 0; + tmp = strtol(inptr + 1, &tmpptr, 10); + + if (errno == 0 && tmpptr != inptr + 1 && + tmp + 4 < strings) { + /* Replace {?} tag with appropriate text only when successful. + * Skip otherwise. */ + buffer = g_string_append(buffer, cmedia_array[tmp + 4]); + } + inptr += 3; /* Skip to the next char after '}' */ + } else { + buffer = g_string_append_c(buffer, *inptr++); + } + } + purple_debug_info("msn", "Parsed currentmedia string, result: \"%s\"\n", + buffer->str); + } else { + purple_debug_info("msn", "Current media marked disabled, not parsing.\n"); + } + + g_strfreev(cmedia_array); + return buffer ? g_string_free(buffer, FALSE) : NULL; +} + +/* get the CurrentMedia info from the XML string */ +char * +msn_get_currentmedia(char *xml_str, gsize len) +{ + xmlnode *payloadNode, *currentmediaNode; + char *currentmedia; + + purple_debug_info("msn","msn get CurrentMedia\n"); + payloadNode = xmlnode_from_str(xml_str, len); + if (!payloadNode){ + purple_debug_error("msn","PSM XML parse Error!\n"); + return NULL; + } + currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia"); + if (currentmediaNode == NULL){ + purple_debug_info("msn","No CurrentMedia Node"); + xmlnode_free(payloadNode); + return NULL; + } + currentmedia = xmlnode_get_data(currentmediaNode); + + xmlnode_free(payloadNode); + + return currentmedia; +} + +/*get the PSM info from the XML string*/ +char * +msn_get_psm(char *xml_str, gsize len) +{ + xmlnode *payloadNode, *psmNode; + char *psm; + + purple_debug_info("MSNP14","msn get PSM\n"); + payloadNode = xmlnode_from_str(xml_str, len); + if (!payloadNode){ + purple_debug_error("MSNP14","PSM XML parse Error!\n"); + return NULL; + } + psmNode = xmlnode_get_child(payloadNode, "PSM"); + if (psmNode == NULL){ + purple_debug_info("MSNP14","No PSM status Node"); + xmlnode_free(payloadNode); + return NULL; + } + psm = xmlnode_get_data(psmNode); + + xmlnode_free(payloadNode); + + return psm; +} + +/* set the MSN's PSM info,Currently Read from the status Line + * Thanks for Cris Code + */ +void +msn_set_psm(MsnSession *session) +{ + PurpleAccount *account = session->account; + PurplePresence *presence; + PurpleStatus *status; + MsnCmdProc *cmdproc; + MsnTransaction *trans; + char *payload; + const char *statusline; + gchar *unescapedstatusline; + + g_return_if_fail(session != NULL); + g_return_if_fail(session->notification != NULL); + + cmdproc = session->notification->cmdproc; + /*prepare PSM info*/ + if(session->psm){ + g_free(session->psm); + } + /*Get the PSM string from Purple's Status Line*/ + presence = purple_account_get_presence(account); + status = purple_presence_get_active_status(presence); + statusline = purple_status_get_attr_string(status, "message"); + unescapedstatusline = purple_unescape_html(statusline); + session->psm = msn_build_psm(unescapedstatusline, NULL, NULL); + g_free(unescapedstatusline); + payload = session->psm; + + purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload); + trans = msn_transaction_new(cmdproc, "UUX","%d",strlen(payload)); + msn_transaction_set_payload(trans, payload, strlen(payload)); + msn_cmdproc_send_trans(cmdproc, trans); +} + void msn_change_status(MsnSession *session) { @@ -79,6 +270,7 @@ g_free(msnobj_str); } + msn_set_psm(session); } const char * diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/state.h --- a/libpurple/protocols/msn/state.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/state.h Fri Sep 28 20:58:48 2007 +0000 @@ -59,6 +59,17 @@ const char *msn_state_get_text(MsnAwayType state); +void msn_set_psm(MsnSession *session); + +/* Parse CurrentMedia string */ +char * msn_parse_currentmedia(const char *cmedia); + +/* Get the CurrentMedia info from the XML string */ +char * msn_get_currentmedia(char *xml_str,gsize len); + +/*get the PSM info from the XML string*/ +char * msn_get_psm(char *xml_str,gsize len); + MsnAwayType msn_state_from_account(PurpleAccount *account); #endif /* _MSN_STATE_H_ */ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/switchboard.c --- a/libpurple/protocols/msn/switchboard.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/switchboard.c Fri Sep 28 20:58:48 2007 +0000 @@ -25,7 +25,7 @@ #include "prefs.h" #include "switchboard.h" #include "notification.h" -#include "msn-utils.h" +#include "msnutils.h" #include "error.h" @@ -534,6 +534,7 @@ payload = msn_message_gen_payload(msg, &payload_len); #ifdef MSN_DEBUG_SB + purple_debug_info("MSNP14","SB length:{%d}",payload_len); msn_message_show_readable(msg, "SB SEND", FALSE); #endif @@ -621,6 +622,7 @@ g_return_if_fail(swboard != NULL); g_return_if_fail(msg != NULL); + purple_debug_info("MSNP14","switchboard send msg..\n"); if (msn_switchboard_can_send(swboard)) release_msg(swboard, msg); else if (queue) @@ -727,7 +729,8 @@ msg = msn_message_new_from_cmd(cmdproc->session, cmd); - msn_message_parse_payload(msg, payload, len); + msn_message_parse_payload(msg, payload, len, + MSG_LINE_DEM,MSG_BODY_DEM); #ifdef MSN_DEBUG_SB msn_message_show_readable(msg, "SB RECV", FALSE); #endif @@ -749,6 +752,14 @@ } static void +ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_misc("MSNP14","get UBM...\n"); + cmdproc->servconn->payload_len = atoi(cmd->params[4]); + cmdproc->last_cmd->payload_cb = msg_cmd_post; +} + +static void nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnMessage *msg; @@ -1084,6 +1095,8 @@ cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) { int reason = MSN_SB_ERROR_UNKNOWN; + MsnMessage *msg; + MsnSwitchBoard *swboard = trans->data; if (error == 215) { @@ -1096,7 +1109,19 @@ } purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error); + purple_debug_warning("msn", "Will Use Offline Message to sendit\n"); +// cal_error_helper(trans, reason); + /*offline Message send Process*/ + + while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL){ + purple_debug_warning("MSNP14","offline msg to send:{%s}\n",msg->body); + /* The messages could not be sent due to a switchboard error */ + swboard->error = MSN_SB_ERROR_USER_OFFLINE; + msg_error_helper(swboard->cmdproc, msg, + MSN_MSG_ERROR_SB); + msn_message_unref(msg); + } cal_error_helper(trans, reason); } @@ -1140,6 +1165,7 @@ /* The conversation window was closed. */ return; + purple_debug_info("MSNP14","Switchboard:auth:{%s} socket:{%s}\n",cmd->params[4],cmd->params[2]); msn_switchboard_set_auth_key(swboard, cmd->params[4]); msn_parse_socket(cmd->params[2], &host, &port); @@ -1252,6 +1278,7 @@ msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd); msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd); + msn_table_add_cmd(cbs_table, NULL, "UBM", ubm_cmd); msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd); msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd); msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/sync.c --- a/libpurple/protocols/msn/sync.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/sync.c Fri Sep 28 20:58:48 2007 +0000 @@ -90,9 +90,9 @@ { MsnSession *session = cmdproc->session; const char *name; - int group_id; + const char *group_id; - group_id = atoi(cmd->params[0]); + group_id = cmd->params[0]; name = purple_url_decode(cmd->params[1]); msn_group_new(session->userlist, group_id, name); @@ -156,10 +156,10 @@ for (c = tokens; *c != NULL; c++) { - int id; + char *id; - id = atoi(*c); - group_ids = g_slist_append(group_ids, GINT_TO_POINTER(id)); + id = *c; + group_ids = g_slist_append(group_ids, g_strdup(id)); } g_strfreev(tokens); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/user.c --- a/libpurple/protocols/msn/user.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/user.c Fri Sep 28 20:58:48 2007 +0000 @@ -25,6 +25,7 @@ #include "user.h" #include "slp.h" +/*new a user object*/ MsnUser * msn_user_new(MsnUserList *userlist, const char *passport, const char *store_name) @@ -50,6 +51,7 @@ return user; } +/*destroy a user object*/ void msn_user_destroy(MsnUser *user) { @@ -59,7 +61,14 @@ g_hash_table_destroy(user->clientcaps); if (user->group_ids != NULL) + { + GList *l; + for (l = user->group_ids; l != NULL; l = l->next) + { + g_free(l->data); + } g_list_free(user->group_ids); + } if (user->msnobj != NULL) msn_object_destroy(user->msnobj); @@ -67,6 +76,7 @@ g_free(user->passport); g_free(user->friendly_name); g_free(user->store_name); + g_free(user->uid); g_free(user->phone.home); g_free(user->phone.work); g_free(user->phone.mobile); @@ -81,7 +91,18 @@ account = user->userlist->session->account; - if (user->status != NULL) { + if (user->statusline != NULL && user->currentmedia != NULL) { + purple_prpl_got_user_status(account, user->passport, user->status, + "message", user->statusline, + "currentmedia", user->currentmedia, NULL); + } else if (user->currentmedia != NULL) { + purple_prpl_got_user_status(account, user->passport, user->status, "currentmedia", + user->currentmedia, NULL); + } else if (user->statusline != NULL) { + //char *status = g_strdup_printf("%s - %s", user->status, user->statusline); + purple_prpl_got_user_status(account, user->passport, user->status, + "message", user->statusline, NULL); + } else if (user->status != NULL) { if (!strcmp(user->status, "offline") && user->mobile) { purple_prpl_got_user_status(account, user->passport, "offline", NULL); purple_prpl_got_user_status(account, user->passport, "mobile", NULL); @@ -142,12 +163,66 @@ } void +msn_user_set_statusline(MsnUser *user, const char *statusline) +{ + g_return_if_fail(user != NULL); + + g_free(user->statusline); + user->statusline = g_strdup(statusline); +} + +void +msn_user_set_currentmedia(MsnUser *user, const char *currentmedia) +{ + g_return_if_fail(user != NULL); + + g_free(user->currentmedia); + user->currentmedia = g_strdup(currentmedia); +} + +void msn_user_set_store_name(MsnUser *user, const char *name) { g_return_if_fail(user != NULL); - g_free(user->store_name); - user->store_name = g_strdup(name); + if (name != NULL) + { + g_free(user->store_name); + user->store_name = g_strdup(name); + } +} + +void +msn_user_set_uid(MsnUser *user, const char *uid) +{ + g_return_if_fail(user != NULL); + + g_free(user->uid); + user->uid = g_strdup(uid); +} + +void +msn_user_set_type(MsnUser *user, MsnUserType type) +{ + g_return_if_fail(user != NULL); + + user->type = type; +} + +void +msn_user_set_op(MsnUser *user, int list_op) +{ + g_return_if_fail(user != NULL); + + user->list_op |= list_op; +} + +void +msn_user_unset_op(MsnUser *user, int list_op) +{ + g_return_if_fail(user != NULL); + + user->list_op &= ~list_op; } void @@ -218,54 +293,97 @@ } } +/*add group id to User object*/ void -msn_user_add_group_id(MsnUser *user, int id) +msn_user_add_group_id(MsnUser *user, const char* id) { MsnUserList *userlist; PurpleAccount *account; PurpleBuddy *b; PurpleGroup *g; const char *passport; + char *group_id; const char *group_name; g_return_if_fail(user != NULL); - g_return_if_fail(id >= 0); + g_return_if_fail(id != NULL); - user->group_ids = g_list_append(user->group_ids, GINT_TO_POINTER(id)); + group_id = g_strdup(id); + user->group_ids = g_list_append(user->group_ids, group_id); userlist = user->userlist; account = userlist->session->account; passport = msn_user_get_passport(user); - group_name = msn_userlist_find_group_name(userlist, id); + group_name = msn_userlist_find_group_name(userlist, group_id); + + purple_debug_info("User","group id:%s,name:%s,user:%s\n", group_id, group_name, passport); g = purple_find_group(group_name); - if ((id == 0) && (g == NULL)) + if ((id == NULL) && (g == NULL)) { g = purple_group_new(group_name); purple_blist_add_group(g, NULL); } b = purple_find_buddy_in_group(account, passport, g); - if (b == NULL) { b = purple_buddy_new(account, passport, NULL); - purple_blist_add_buddy(b, NULL, g, NULL); } + b->proto_data = user; + /*Update the blist Node info*/ +// purple_blist_node_set_string(&(b->node), "", ""); +} - b->proto_data = user; +/*check if the msn user is online*/ +gboolean +msn_user_is_online(PurpleAccount *account, const char *name) +{ + PurpleBuddy *buddy; + + buddy =purple_find_buddy(account,name); + return PURPLE_BUDDY_IS_ONLINE(buddy); +} + +gboolean +msn_user_is_yahoo(PurpleAccount *account, const char *name) +{ + MsnSession *session = NULL; + MsnUser *user; + PurpleConnection *gc; + + gc = purple_account_get_connection(account); + if (gc != NULL) + session = gc->proto_data; + + if ((session != NULL) && (session->protocol_ver == WLM_PROT_VER)) + return FALSE; + + if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL) + { + return (user->type == MSN_USER_TYPE_YAHOO); + } + return (strstr(name,"@yahoo.") != NULL); } void -msn_user_remove_group_id(MsnUser *user, int id) +msn_user_remove_group_id(MsnUser *user, const char *id) { + GList *l; + g_return_if_fail(user != NULL); - g_return_if_fail(id >= 0); + g_return_if_fail(id != NULL); + + l = g_list_find_custom(user->group_ids, id, (GCompareFunc)strcmp); - user->group_ids = g_list_remove(user->group_ids, GINT_TO_POINTER(id)); + if (l == NULL) + return; + + g_free(l->data); + user->group_ids = g_list_remove_link(user->group_ids, l); } void diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/user.h --- a/libpurple/protocols/msn/user.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/user.h Fri Sep 28 20:58:48 2007 +0000 @@ -31,6 +31,17 @@ #include "userlist.h" +typedef enum +{ + MSN_USER_TYPE_UNKNOWN = 0x00, + MSN_USER_TYPE_PASSPORT = 0x01, + MSN_USER_TYPE_UNKNOWN1 = 0x02, + MSN_USER_TYPE_MOBILE = 0x04, + MSN_USER_TYPE_UNKNOWN2 = 0x08, + MSN_USER_TYPE_UNKNOWN3 = 0x10, + MSN_USER_TYPE_YAHOO = 0x20 +} MsnUserType; + /** * A user. */ @@ -45,7 +56,12 @@ char *store_name; /**< The name stored in the server. */ char *friendly_name; /**< The friendly name. */ + char * uid; /*< User Id */ + const char *status; /**< The state of the user. */ + char *statusline; /**< The state of the user. */ + char *currentmedia; /**< The current media of the user. */ + gboolean idle; /**< The idle state of the user. */ struct @@ -65,7 +81,12 @@ GHashTable *clientcaps; /**< The client's capabilities. */ - int list_op; + MsnUserType type; /**< The user type */ + + int list_op; /**< Which lists the user is in */ + + guint membership_id[5]; /**< The membershipId sent by the contacts server, + indexed by the list it belongs to */ }; /**************************************************************************/ @@ -102,6 +123,22 @@ */ void msn_user_update(MsnUser *user); + /** + * Sets the new statusline of user. + * + * @param user The user. + * @param state The statusline string. + */ +void msn_user_set_statusline(MsnUser *user, const char *statusline); + + /** + * Sets the current media of user. + * + * @param user The user. + * @param state The statusline string. + */ +void msn_user_set_currentmedia(MsnUser *user, const char *currentmedia); + /** * Sets the new state of user. * @@ -156,7 +193,7 @@ * @param user The user. * @param id The group ID. */ -void msn_user_add_group_id(MsnUser *user, int id); +void msn_user_add_group_id(MsnUser *user, const char * id); /** * Removes the group ID from a user. @@ -164,7 +201,7 @@ * @param user The user. * @param id The group ID. */ -void msn_user_remove_group_id(MsnUser *user, int id); +void msn_user_remove_group_id(MsnUser *user, const char * id); /** * Sets the home phone number for a user. @@ -182,6 +219,9 @@ */ void msn_user_set_work_phone(MsnUser *user, const char *number); +void msn_user_set_uid(MsnUser *user, const char *uid); +void msn_user_set_type(MsnUser *user, MsnUserType type); + /** * Sets the mobile phone number for a user. * @@ -279,6 +319,22 @@ */ GHashTable *msn_user_get_client_caps(const MsnUser *user); +/** + * check to see if user is online + */ +gboolean +msn_user_is_online(PurpleAccount *account, const char *name); + +/** + * check to see if user is Yahoo User + */ +gboolean +msn_user_is_yahoo(PurpleAccount *account ,const char *name); + +void msn_user_set_op(MsnUser *user, int list_op); +void msn_user_unset_op(MsnUser *user, int list_op); + /*@}*/ + #endif /* _MSN_USER_H_ */ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/userlist.c --- a/libpurple/protocols/msn/userlist.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/userlist.c Fri Sep 28 20:58:48 2007 +0000 @@ -41,10 +41,21 @@ msn_accept_add_cb(gpointer data) { MsnPermitAdd *pa = data; - MsnSession *session = pa->gc->proto_data; - MsnUserList *userlist = session->userlist; + + purple_debug_misc("MSN Userlist", "Accepted the new buddy: %s\n", pa->who); - msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_AL, NULL); + if (PURPLE_CONNECTION_IS_VALID(pa->gc)) + { + MsnSession *session = pa->gc->proto_data; + MsnUserList *userlist = session->userlist; + MsnUser *user = msn_userlist_find_add_user(userlist, pa->who, pa->who); + + + msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL); + + if (msn_userlist_user_is_in_list(user, MSN_LIST_FL)) + msn_del_contact_from_list(session->contact, NULL, pa->who, MSN_LIST_PL); + } g_free(pa->who); g_free(pa->friendly); @@ -55,10 +66,20 @@ msn_cancel_add_cb(gpointer data) { MsnPermitAdd *pa = data; - MsnSession *session = pa->gc->proto_data; - MsnUserList *userlist = session->userlist; + + purple_debug_misc("MSN Userlist", "Denied the new buddy: %s\n", pa->who); - msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL); + if (PURPLE_CONNECTION_IS_VALID(pa->gc)) + { + MsnSession *session = pa->gc->proto_data; + MsnUserList *userlist = session->userlist; + MsnCallbackState *state = msn_callback_state_new(); + + msn_callback_state_set_action(state, MSN_DENIED_BUDDY); + + msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL); + msn_del_contact_from_list(session->contact, state, pa->who, MSN_LIST_PL); + } g_free(pa->who); g_free(pa->friendly); @@ -68,59 +89,57 @@ static void got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly) { + PurpleAccount *acct; MsnPermitAdd *pa; pa = g_new0(MsnPermitAdd, 1); pa->who = g_strdup(passport); pa->friendly = g_strdup(friendly); pa->gc = gc; - - purple_account_request_authorization(purple_connection_get_account(gc), passport, NULL, friendly, NULL, - purple_find_buddy(purple_connection_get_account(gc), passport) != NULL, - msn_accept_add_cb, msn_cancel_add_cb, pa); + + acct = purple_connection_get_account(gc); + purple_account_request_authorization(acct, passport, NULL, friendly, NULL, + purple_find_buddy(acct, passport) != NULL, + msn_accept_add_cb, msn_cancel_add_cb, pa); + } /************************************************************************** * Utility functions **************************************************************************/ -static gboolean -user_is_in_group(MsnUser *user, int group_id) +gboolean +msn_userlist_user_is_in_group(MsnUser *user, const char * group_id) { if (user == NULL) return FALSE; - if (group_id < 0) + if (group_id == NULL) return FALSE; - if (g_list_find(user->group_ids, GINT_TO_POINTER(group_id))) + if (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp)) return TRUE; return FALSE; } -static gboolean -user_is_there(MsnUser *user, int list_id, int group_id) +gboolean +msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id) { int list_op; if (user == NULL) return FALSE; - + list_op = 1 << list_id; - if (!(user->list_op & list_op)) + if (user->list_op & list_op) + return TRUE; + else return FALSE; - - if (list_id == MSN_LIST_FL) - { - if (group_id >= 0) - return user_is_in_group(user, group_id); - } - - return TRUE; } +#if 0 static const char* get_store_name(MsnUser *user) { @@ -148,31 +167,7 @@ return store_name; } - -static void -msn_request_add_group(MsnUserList *userlist, const char *who, - const char *old_group_name, const char *new_group_name) -{ - MsnCmdProc *cmdproc; - MsnTransaction *trans; - MsnMoveBuddy *data; - - cmdproc = userlist->session->notification->cmdproc; - data = g_new0(MsnMoveBuddy, 1); - - data->who = g_strdup(who); - - if (old_group_name) - data->old_group_name = g_strdup(old_group_name); - - trans = msn_transaction_new(cmdproc, "ADG", "%s %d", - purple_url_encode(new_group_name), - 0); - - msn_transaction_set_data(trans, data); - - msn_cmdproc_send_trans(cmdproc, trans); -} +#endif /************************************************************************** * Server functions @@ -193,14 +188,16 @@ return -1; } +/* this function msn_got_add_user isn't called anywhere */ void msn_got_add_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id) + MsnListId list_id, const char * group_id) { PurpleAccount *account; const char *passport; const char *friendly; + purple_debug_info("MSNP14","got add user...\n"); account = session->account; passport = msn_user_get_passport(user); @@ -214,7 +211,7 @@ serv_got_alias(gc, passport, friendly); - if (group_id >= 0) + if (group_id != NULL) { msn_user_add_group_id(user, group_id); } @@ -263,7 +260,7 @@ * looked at this. Maybe we should use the store * name instead? --KingAnt */ - got_new_entry(gc, passport, friendly); +// got_new_entry(gc, passport, friendly); } } @@ -273,7 +270,7 @@ void msn_got_rem_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id) + MsnListId list_id, const char * group_id) { PurpleAccount *account; const char *passport; @@ -285,7 +282,7 @@ if (list_id == MSN_LIST_FL) { /* TODO: When is the user totally removed? */ - if (group_id >= 0) + if (group_id != NULL) { msn_user_remove_group_id(user, group_id); return; @@ -333,7 +330,6 @@ { purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n", passport); - } } @@ -351,14 +347,16 @@ passport = msn_user_get_passport(user); store = msn_user_get_store_name(user); + + msn_user_set_op(user, list_op); if (list_op & MSN_LIST_FL_OP) { GSList *c; for (c = group_ids; c != NULL; c = g_slist_next(c)) { - int group_id; - group_id = GPOINTER_TO_INT(c->data); + char *group_id; + group_id = c->data; msn_user_add_group_id(user, group_id); } @@ -393,11 +391,14 @@ if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP))) { - got_new_entry(gc, passport, store); +// got_new_entry(gc, passport, store); } } - user->list_op = list_op; + if (list_op & MSN_LIST_PL_OP) + { + got_new_entry(gc, passport, store); + } } /************************************************************************** @@ -427,18 +428,18 @@ { GList *l; + /*destroy userlist*/ for (l = userlist->users; l != NULL; l = l->next) { msn_user_destroy(l->data); } - g_list_free(userlist->users); + /*destroy group list*/ for (l = userlist->groups; l != NULL; l = l->next) { msn_group_destroy(l->data); } - g_list_free(userlist->groups); g_queue_free(userlist->buddy_icon_requests); @@ -449,6 +450,22 @@ g_free(userlist); } +MsnUser * +msn_userlist_find_add_user(MsnUserList *userlist,const char *passport,const char *userName) +{ + MsnUser *user; + + user = msn_userlist_find_user(userlist, passport); + if (user == NULL) + { + user = msn_user_new(userlist, passport, userName); + msn_userlist_add_user(userlist, user); + } else { + msn_user_set_store_name(user, userName); + } + return user; +} + void msn_userlist_add_user(MsnUserList *userlist, MsnUser *user) { @@ -472,10 +489,36 @@ { MsnUser *user = (MsnUser *)l->data; +// purple_debug_info("MsnUserList","user passport:%s,passport:%s\n",user->passport,passport); g_return_val_if_fail(user->passport != NULL, NULL); - if (!strcmp(passport, user->passport)) + if (!g_strcasecmp(passport, user->passport)){ +// purple_debug_info("MsnUserList","return:%p\n",user); return user; + } + } + + return NULL; +} + +MsnUser * +msn_userlist_find_user_with_id(MsnUserList *userlist, const char *uid) +{ + GList *l; + + g_return_val_if_fail(uid != NULL, NULL); + + for (l = userlist->users; l != NULL; l = l->next) + { + MsnUser *user = (MsnUser *)l->data; + + if (user->uid == NULL) { + continue; + } + + if ( !g_strcasecmp(uid, user->uid) ) { + return user; + } } return NULL; @@ -494,18 +537,18 @@ } MsnGroup * -msn_userlist_find_group_with_id(MsnUserList *userlist, int id) +msn_userlist_find_group_with_id(MsnUserList *userlist, const char * id) { GList *l; g_return_val_if_fail(userlist != NULL, NULL); - g_return_val_if_fail(id >= 0, NULL); + g_return_val_if_fail(id != NULL, NULL); for (l = userlist->groups; l != NULL; l = l->next) { MsnGroup *group = l->data; - if (group->id == id) + if (!g_strcasecmp(group->id,id)) return group; } @@ -524,14 +567,14 @@ { MsnGroup *group = l->data; - if ((group->name != NULL) && !g_ascii_strcasecmp(name, group->name)) + if ((group->name != NULL) && !g_strcasecmp(name, group->name)) return group; } return NULL; } -int +const char * msn_userlist_find_group_id(MsnUserList *userlist, const char *group_name) { MsnGroup *group; @@ -541,11 +584,11 @@ if (group != NULL) return msn_group_get_id(group); else - return -1; + return NULL; } const char * -msn_userlist_find_group_name(MsnUserList *userlist, int group_id) +msn_userlist_find_group_name(MsnUserList *userlist, const char * group_id) { MsnGroup *group; @@ -558,7 +601,7 @@ } void -msn_userlist_rename_group_id(MsnUserList *userlist, int group_id, +msn_userlist_rename_group_id(MsnUserList *userlist, const char * group_id, const char *new_name) { MsnGroup *group; @@ -570,7 +613,7 @@ } void -msn_userlist_remove_group_id(MsnUserList *userlist, int group_id) +msn_userlist_remove_group_id(MsnUserList *userlist, const char * group_id) { MsnGroup *group; @@ -584,116 +627,293 @@ } void -msn_userlist_rem_buddy(MsnUserList *userlist, - const char *who, int list_id, const char *group_name) +msn_userlist_rem_buddy(MsnUserList *userlist, const char *who) +{ + MsnUser *user = NULL; + + g_return_if_fail(userlist != NULL); + g_return_if_fail(userlist->session != NULL); + g_return_if_fail(userlist->session->contact != NULL); + g_return_if_fail(who != NULL); + + user = msn_userlist_find_user(userlist, who); + + msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_FL); + + /* delete the contact from address book via soap action */ + if (user != NULL) { + msn_delete_contact(userlist->session->contact, user->uid); + } +} + +void +msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who, + MsnListId list_id) { MsnUser *user; - int group_id; - const char *list; + const gchar *list; + MsnListOp list_op = 1 << list_id; user = msn_userlist_find_user(userlist, who); - group_id = -1; - - if (group_name != NULL) - { - group_id = msn_userlist_find_group_id(userlist, group_name); - - if (group_id < 0) - { - /* Whoa, there is no such group. */ - purple_debug_error("msn", "Group doesn't exist: %s\n", group_name); - return; - } - } - - /* First we're going to check if not there. */ - if (!(user_is_there(user, list_id, group_id))) - { + + g_return_if_fail(user != NULL); + + if ( !msn_userlist_user_is_in_list(user, list_id)) { list = lists[list_id]; - purple_debug_error("msn", "User '%s' is not there: %s\n", - who, list); + purple_debug_info("MSN Userlist", "User %s is not in list %s, not removing.\n", who, list); return; } - /* Then request the rem to the server. */ - list = lists[list_id]; + msn_user_unset_op(user, list_op); - msn_notification_rem_buddy(userlist->session->notification, list, who, group_id); + msn_notification_rem_buddy_from_list(userlist->session->notification, list_id, who); } +/*add buddy*/ void -msn_userlist_add_buddy(MsnUserList *userlist, - const char *who, int list_id, - const char *group_name) +msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group_name) { MsnUser *user; - int group_id; - const char *list; - const char *store_name; + MsnCallbackState *state = NULL; + const char *group_id = NULL, *new_group_name; + + new_group_name = group_name == NULL ? MSN_INDIVIDUALS_GROUP_NAME : group_name; - group_id = -1; + + g_return_if_fail(userlist != NULL); + g_return_if_fail(userlist->session != NULL); + + + purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name); + + state = msn_callback_state_new(); + msn_callback_state_set_who(state, who); + msn_callback_state_set_new_group_name(state, new_group_name); if (!purple_email_is_valid(who)) { /* only notify the user about problems adding to the friends list * maybe we should do something else for other lists, but it probably * won't cause too many problems if we just ignore it */ - if (list_id == MSN_LIST_FL) - { - char *str = g_strdup_printf(_("Unable to add \"%s\"."), who); - purple_notify_error(NULL, NULL, str, - _("The screen name specified is invalid.")); - g_free(str); - } + + char *str = g_strdup_printf(_("Unable to add \"%s\"."), who); + + purple_notify_error(NULL, NULL, str, + _("The screen name specified is invalid.")); + g_free(str); return; } - if (group_name != NULL) + group_id = msn_userlist_find_group_id(userlist, new_group_name); + + if (group_id == NULL) { - group_id = msn_userlist_find_group_id(userlist, group_name); + /* Whoa, we must add that group first. */ + purple_debug_info("MSN Userlist", "Adding user %s to a new group, creating group %s first\n", who, new_group_name); + + msn_callback_state_set_action(state, MSN_ADD_BUDDY); - if (group_id < 0) - { - /* Whoa, we must add that group first. */ - msn_request_add_group(userlist, who, NULL, group_name); + msn_add_group(userlist->session, state, new_group_name); + return; + } else { + msn_callback_state_set_guid(state, group_id); + } + + /* XXX: adding user here may not be correct (should add them in the + * ACK to the ADL command), but for now we need to make sure they exist + * early enough that the ILN command doesn't screw us up */ + + user = msn_userlist_find_add_user(userlist, who, who); + + if ( msn_userlist_user_is_in_list(user, MSN_LIST_FL) ) { + + purple_debug_info("MSN Userlist", "User %s already exists\n", who); + + msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL); + + if (msn_userlist_user_is_in_group(user, group_id)) { + purple_debug_info("MSN Userlist", "User %s is already in group %s, returning\n", who, new_group_name); return; } } + + purple_debug_info("MSN Userlist", "Adding user: %s to group id: %s\n", who, group_id); - user = msn_userlist_find_user(userlist, who); + msn_callback_state_set_action(state, MSN_ADD_BUDDY); + + /* Add contact in the Contact server with a SOAP request and if + successful, send ADL with MSN_LIST_AL and MSN_LIST_FL and a FQY */ + msn_add_contact_to_group(userlist->session->contact, state, who, group_id); +} +void +msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, + MsnListId list_id) +{ + MsnUser *user = NULL; + const gchar *list; + MsnListOp list_op = 1 << list_id; + + g_return_if_fail(userlist != NULL); + + user = msn_userlist_find_add_user(userlist, who, who); + /* First we're going to check if it's already there. */ - if (user_is_there(user, list_id, group_id)) + if (msn_userlist_user_is_in_list(user, list_id)) { list = lists[list_id]; - purple_debug_error("msn", "User '%s' is already there: %s\n", who, list); + purple_debug_info("MSN Userlist", "User '%s' is already in list: %s\n", who, list); return; } + + //store_name = (user != NULL) ? get_store_name(user) : who; + + //purple_debug_info("MSN Userlist", "store_name = %s\n", store_name); + + /* XXX: see XXX above, this should really be done when we get the response from + the server */ + + msn_user_set_op(user, list_op); + + msn_notification_add_buddy_to_list(userlist->session->notification, list_id, who); +} + +gboolean +msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who, + const char *group_name) +{ + MsnUser *user; + gchar * group_id; + + g_return_val_if_fail(userlist != NULL, FALSE); + g_return_val_if_fail(group_name != NULL, FALSE); + g_return_val_if_fail(who != NULL, FALSE); + + purple_debug_info("MSN Userlist","Adding buddy with passport %s to group %s\n", who, group_name); + + if ( (group_id = (gchar *)msn_userlist_find_group_id(userlist, group_name)) == NULL) { + purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name); + return FALSE; + } - store_name = (user != NULL) ? get_store_name(user) : who; + if ( (user = msn_userlist_find_user(userlist, who)) == NULL) { + purple_debug_error("MSN Userlist", "User %s not found!", who); + return FALSE; + } + + msn_user_add_group_id(user, group_id); + + return TRUE; +} + + +gboolean +msn_userlist_rem_buddy_from_group(MsnUserList *userlist, const char *who, + const char *group_name) +{ + const gchar * group_id; + MsnUser *user; - /* Then request the add to the server. */ - list = lists[list_id]; + g_return_val_if_fail(userlist != NULL, FALSE); + g_return_val_if_fail(group_name != NULL, FALSE); + g_return_val_if_fail(who != NULL, FALSE); + + purple_debug_info("MSN Userlist","Removing buddy with passport %s from group %s\n", who, group_name); - msn_notification_add_buddy(userlist->session->notification, list, who, - store_name, group_id); + if ( (group_id = msn_userlist_find_group_id(userlist, group_name)) == NULL) { + purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name); + return FALSE; + } + + if ( (user = msn_userlist_find_user(userlist, who)) == NULL) { + purple_debug_error("MSN Userlist", "User %s not found!", who); + return FALSE; + } + + msn_user_remove_group_id(user, group_id); + + return TRUE; } void msn_userlist_move_buddy(MsnUserList *userlist, const char *who, - const char *old_group_name, const char *new_group_name) + const char *old_group_name, const char *new_group_name) { - int new_group_id; + const char *new_group_id; + MsnCallbackState *state; + + g_return_if_fail(userlist != NULL); + g_return_if_fail(userlist->session != NULL); + g_return_if_fail(userlist->session->contact != NULL); + + state = msn_callback_state_new(); + msn_callback_state_set_who(state, who); + msn_callback_state_set_action(state, MSN_MOVE_BUDDY); + msn_callback_state_set_old_group_name(state, old_group_name); + msn_callback_state_set_new_group_name(state, new_group_name); new_group_id = msn_userlist_find_group_id(userlist, new_group_name); - if (new_group_id < 0) - { - msn_request_add_group(userlist, who, old_group_name, new_group_name); + if (new_group_id == NULL) + { + msn_add_group(userlist->session, state, new_group_name); return; } + + /* add the contact to the new group, and remove it from the old one in + * the callback + */ + msn_add_contact_to_group(userlist->session->contact, state, who, new_group_id); +} - msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, new_group_name); - msn_userlist_rem_buddy(userlist, who, MSN_LIST_FL, old_group_name); +/*load userlist from the Blist file cache*/ +void +msn_userlist_load(MsnSession *session) +{ + PurpleBlistNode *gnode, *cnode, *bnode; + PurpleConnection *gc = purple_account_get_connection(session->account); + GSList *l; + MsnUser * user; + + g_return_if_fail(gc != NULL); + + for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) + { + if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) + continue; + for (cnode = gnode->child; cnode; cnode = cnode->next) + { + if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) + continue; + for (bnode = cnode->child; bnode; bnode = bnode->next) + { + PurpleBuddy *b; + if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) + continue; + b = (PurpleBuddy *)bnode; + if (b->account == gc->account) + { + user = msn_userlist_find_add_user(session->userlist, + b->name,NULL); + b->proto_data = user; + msn_user_set_op(user, MSN_LIST_FL_OP); + } + } + } + } + for (l = session->account->permit; l != NULL; l = l->next) + { + user = msn_userlist_find_add_user(session->userlist, + (char *)l->data,NULL); + msn_user_set_op(user, MSN_LIST_AL_OP); + } + for (l = session->account->deny; l != NULL; l = l->next) + { + user = msn_userlist_find_add_user(session->userlist, + (char *)l->data,NULL); + msn_user_set_op(user, MSN_LIST_BL_OP); + } + } + diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/msn/userlist.h --- a/libpurple/protocols/msn/userlist.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/msn/userlist.h Fri Sep 28 20:58:48 2007 +0000 @@ -35,16 +35,11 @@ MSN_LIST_FL, MSN_LIST_AL, MSN_LIST_BL, - MSN_LIST_RL + MSN_LIST_RL, + MSN_LIST_PL } MsnListId; -typedef struct -{ - char *who; - char *old_group_name; - -} MsnMoveBuddy; struct _MsnUserList { @@ -64,40 +59,57 @@ }; +gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id); +gboolean msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id); MsnListId msn_get_list_id(const char *list); void msn_got_add_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id); + MsnListId list_id, const char *group_id); void msn_got_rem_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id); + MsnListId list_id, const char *group_id); void msn_got_lst_user(MsnSession *session, MsnUser *user, int list_op, GSList *group_ids); MsnUserList *msn_userlist_new(MsnSession *session); void msn_userlist_destroy(MsnUserList *userlist); + void msn_userlist_add_user(MsnUserList *userlist, MsnUser *user); void msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user); -MsnUser *msn_userlist_find_user(MsnUserList *userlist, - const char *passport); + +MsnUser * msn_userlist_find_user(MsnUserList *userlist, const char *passport); +MsnUser * msn_userlist_find_add_user(MsnUserList *userlist, + const char *passport, const char *userName); +MsnUser * msn_userlist_find_user_with_id(MsnUserList *userlist, const char *uid); + void msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group); void msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group); -MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, int id); -MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist, - const char *name); -int msn_userlist_find_group_id(MsnUserList *userlist, - const char *group_name); -const char *msn_userlist_find_group_name(MsnUserList *userlist, - int group_id); -void msn_userlist_rename_group_id(MsnUserList *userlist, int group_id, - const char *new_name); -void msn_userlist_remove_group_id(MsnUserList *userlist, int group_id); +MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, const char *id); +MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name); +const char * msn_userlist_find_group_id(MsnUserList *userlist, + const char *group_name); +const char *msn_userlist_find_group_name(MsnUserList *userlist, const char *group_id); +void msn_userlist_rename_group_id(MsnUserList *userlist, const char *group_id, + const char *new_name); +void msn_userlist_remove_group_id(MsnUserList *userlist, const char *group_id); -void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who, - int list_id, const char *group_name); -void msn_userlist_add_buddy(MsnUserList *userlist, const char *who, - int list_id, const char *group_name); +void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who); +void msn_userlist_add_buddy(MsnUserList *userlist, + const char *who, const char *group_name); void msn_userlist_move_buddy(MsnUserList *userlist, const char *who, - const char *old_group_name, - const char *new_group_name); + const char *old_group_name, + const char *new_group_name); + +gboolean msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who, + const char *group_name); +gboolean msn_userlist_rem_buddy_from_group(MsnUserList *userlist, + const char *who, + const char *group_name); + +void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, + MsnListId list_id); +void msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who, + MsnListId list_id); + +void msn_userlist_load(MsnSession *session); #endif /* _MSN_USERLIST_H_ */ diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/myspace/markup.c --- a/libpurple/protocols/myspace/markup.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/myspace/markup.c Fri Sep 28 20:58:48 2007 +0000 @@ -426,13 +426,14 @@ html_tag_to_msim_markup(MsimSession *session, xmlnode *root, gchar **begin, gchar **end) { + if (!purple_utf8_strcasecmp(root->name, "root") || + !purple_utf8_strcasecmp(root->name, "html")) { + *begin = g_strdup(""); + *end = g_strdup(""); /* TODO: Coalesce nested tags into one tag! * Currently, the 's' value will be overwritten when b/i/u is nested * within another one, and only the inner-most formatting will be * applied to the text. */ - if (!purple_utf8_strcasecmp(root->name, "root")) { - *begin = g_strdup(""); - *end = g_strdup(""); } else if (!purple_utf8_strcasecmp(root->name, "b")) { *begin = g_strdup_printf("", MSIM_TEXT_BOLD); *end = g_strdup(""); @@ -503,8 +504,21 @@ /* TODO: color (bg uses ), emoticons */ } else { + gchar *err; + +#ifdef MSIM_MARKUP_SHOW_UNKNOWN_TAGS *begin = g_strdup_printf("[%s]", root->name); *end = g_strdup_printf("[/%s]", root->name); +#else + *begin = g_strdup(""); + *end = g_strdup(""); +#endif + + err = g_strdup_printf("html_tag_to_msim_markup: unrecognized " + "HTML tag %s was sent by the IM client; ignoring", + root->name ? root->name : "(NULL)"); + msim_unrecognized(NULL, NULL, err); + g_free(err); } } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/myspace/myspace.c --- a/libpurple/protocols/myspace/myspace.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.c Fri Sep 28 20:58:48 2007 +0000 @@ -291,9 +291,10 @@ /* Notify an error message also, because this is important! */ purple_notify_error(acct, g_strdup(_("MySpaceIM Error")), str, NULL); + gc->wants_to_die = TRUE; purple_connection_error(gc, str); - g_free(str); + return; } #endif @@ -458,6 +459,7 @@ purple_cipher_context_append(key_context, hash_pw, HASH_SIZE); purple_cipher_context_append(key_context, (guchar *)(nonce + NONCE_SIZE), NONCE_SIZE); purple_cipher_context_digest(key_context, sizeof(key), key, NULL); + purple_cipher_context_destroy(key_context); #ifdef MSIM_DEBUG_LOGIN_CHALLENGE purple_debug_info("msim", "key = "); @@ -1039,7 +1041,7 @@ PurpleStatusType *type; MsimSession *session; guint status_code; - const gchar *statstring; + gchar *statstring; session = (MsimSession *)account->gc->proto_data; @@ -1073,7 +1075,7 @@ break; } - statstring = purple_status_get_attr_string(status, "message"); + statstring = (gchar *)purple_status_get_attr_string(status, "message"); if (!statstring) { statstring = ""; @@ -1082,7 +1084,7 @@ /* Status strings are plain text. */ statstring = purple_markup_strip_html(statstring); - msim_set_status_code(session, status_code, g_strdup(statstring)); + msim_set_status_code(session, status_code, statstring); } /** Go idle. */ @@ -1213,7 +1215,7 @@ if (uid == wanted_uid) { ret = g_strdup(name); - break; + break; } } @@ -1790,11 +1792,13 @@ /* Destroy session if fatal. */ if (msim_msg_get(msg, "fatal")) { purple_debug_info("msim", "fatal error, closing\n"); - if (err == 260) { - /* Incorrect password */ - session->gc->wants_to_die = TRUE; - if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + switch (err) { + case 260: /* Incorrect password */ + case 6: /* Logged in elsewhere */ + session->gc->wants_to_die = TRUE; + if (!purple_account_get_remember_password(session->account)) + purple_account_set_password(session->account, NULL); + break; } purple_connection_error(session->gc, full_errmsg); } else { @@ -1867,6 +1871,7 @@ purple_blist_add_buddy(buddy, NULL, NULL, NULL); user = msim_get_user_from_buddy(buddy); + /* TODO: free user. memory leak? */ /* All buddies on list should have 'uid' integer associated with them. */ purple_blist_node_set_int(&buddy->node, "UserID", msim_msg_get_integer(msg, "f")); @@ -2863,7 +2868,7 @@ } /** Callbacks called by Purple, to access this plugin. */ -PurplePluginProtocolInfo prpl_info = { +static PurplePluginProtocolInfo prpl_info = { /* options */ OPT_PROTO_USE_POINTSIZE /* specify font size in sane point size */ | OPT_PROTO_MAIL_CHECK, diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/myspace/myspace.h --- a/libpurple/protocols/myspace/myspace.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.h Fri Sep 28 20:58:48 2007 +0000 @@ -67,6 +67,10 @@ /*#define MSIM_DEBUG_LOGIN_CHALLENGE*/ /*#define MSIM_DEBUG_RXBUF */ +/* Encode unknown HTML tags from IM clients in messages as [tag], instead of + * ignoring. Useful for debugging */ +/*#define MSIM_MARKUP_SHOW_UNKNOWN_TAGS */ + /* Define to cause init_plugin() to run some tests and print * the results to the Purple debug log, then exit. Useful to * run with 'pidgin -d' to see the output. Don't define if diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/myspace/user.c --- a/libpurple/protocols/myspace/user.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/myspace/user.c Fri Sep 28 20:58:48 2007 +0000 @@ -154,7 +154,13 @@ } } -/** Store a field of information about a buddy. */ +/** Store a field of information about a buddy. + * + * @param key_str Key to store. + * @param value_str Value string, either user takes ownership of this string + * or it is freed if MsimUser doesn't store the string. + * @param user User to store data in. Existing data will be replaced. + * */ void msim_store_user_info_each(const gchar *key_str, gchar *value_str, MsimUser *user) { @@ -168,25 +174,33 @@ /* Need to store in MsimUser, too? What if not on blist? */ } else if (g_str_equal(key_str, "Age")) { user->age = atol(value_str); + g_free(value_str); } else if (g_str_equal(key_str, "Gender")) { - user->gender = g_strdup(value_str); + g_free(user->gender); + user->gender = value_str; } else if (g_str_equal(key_str, "Location")) { - user->location = g_strdup(value_str); + g_free(user->location); + user->location = value_str; } else if (g_str_equal(key_str, "TotalFriends")) { user->total_friends = atol(value_str); } else if (g_str_equal(key_str, "DisplayName")) { - user->display_name = g_strdup(value_str); + g_free(user->display_name); + user->display_name = value_str; } else if (g_str_equal(key_str, "BandName")) { - user->band_name = g_strdup(value_str); + g_free(user->band_name); + user->band_name = value_str; } else if (g_str_equal(key_str, "SongName")) { - user->song_name = g_strdup(value_str); + g_free(user->song_name); + user->song_name = value_str; } else if (g_str_equal(key_str, "UserName") || g_str_equal(key_str, "IMName") || g_str_equal(key_str, "NickName")) { /* Ignore because PurpleBuddy knows this already */ - ; + g_free(value_str); } else if (g_str_equal(key_str, "ImageURL") || g_str_equal(key_str, "AvatarURL")) { const gchar *previous_url; - user->image_url = g_strdup(value_str); + g_free(user->image_url); + + user->image_url = value_str; /* Instead of showing 'no photo' picture, show nothing. */ if (g_str_equal(user->image_url, "http://x.myspace.com/images/no_pic.gif")) @@ -207,14 +221,17 @@ } else if (g_str_equal(key_str, "LastImageUpdated")) { /* TODO: use somewhere */ user->last_image_updated = atol(value_str); + g_free(value_str); } else if (g_str_equal(key_str, "Headline")) { - user->headline = g_strdup(value_str); + g_free(user->headline); + user->headline = value_str; } else { /* TODO: other fields in MsimUser */ gchar *msg; msg = g_strdup_printf("msim_store_user_info_each: unknown field %s=%s", key_str, value_str); + g_free(value_str); msim_unrecognized(NULL, NULL, msg); @@ -284,7 +301,6 @@ value_str = msim_msg_get_string_from_element(elem); msim_store_user_info_each(key_str, value_str, user); - g_free(value_str); } if (msim_msg_get_integer(msg, "dsn") == MG_OWN_IM_INFO_DSN && @@ -301,6 +317,7 @@ } msim_msg_free(body); + g_free(username); return TRUE; } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/novell/novell.c --- a/libpurple/protocols/novell/novell.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/novell/novell.c Fri Sep 28 20:58:48 2007 +0000 @@ -130,6 +130,8 @@ if (ret_code == NMERR_AUTHENTICATION_FAILED || ret_code == NMERR_CREDENTIALS_MISSING || ret_code == NMERR_PASSWORD_INVALID) { + if (!purple_account_get_remember_password(gc->account)) + purple_account_set_password(gc->account, NULL); gc->wants_to_die = TRUE; } purple_connection_error(gc, err); @@ -2004,11 +2006,14 @@ _evt_user_disconnect(NMUser * user, NMEvent * event) { PurpleConnection *gc; - - gc = purple_account_get_connection((PurpleAccount *) user->client_data); + PurpleAccount *account = user->client_data; + + gc = purple_account_get_connection(account); if (gc) { gc->wants_to_die = TRUE; /* we don't want to reconnect in this case */ + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL); purple_connection_error(gc, _("You have been logged out because you" " logged in at another workstation.")); } @@ -2799,7 +2804,7 @@ const char *text = NULL; if (buddy == NULL) - return; + return; gc = purple_account_get_connection(buddy->account); if (gc == NULL || (user = gc->proto_data) == NULL) diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/oscar/flap_connection.c --- a/libpurple/protocols/oscar/flap_connection.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Fri Sep 28 20:58:48 2007 +0000 @@ -382,6 +382,8 @@ gchar *tmp; if (conn->disconnect_code == 0x0001) { tmp = g_strdup(_("You have signed on from another location.")); + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL); od->gc->wants_to_die = TRUE; } else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) tmp = g_strdup(_("Server closed the connection.")); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Fri Sep 28 20:58:48 2007 +0000 @@ -5523,6 +5523,8 @@ return "hiptop"; if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM) return "secure"; + if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY) + return "birthday"; } return NULL; } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/qq/login_logout.c --- a/libpurple/protocols/qq/login_logout.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/qq/login_logout.c Fri Sep 28 20:58:48 2007 +0000 @@ -480,6 +480,8 @@ switch (ret) { case QQ_LOGIN_REPLY_PWD_ERROR: gc->wants_to_die = TRUE; + if (!purple_account_get_remember_password(gc->account)) + purple_account_set_password(gc->account, NULL); purple_connection_error(gc, _("Incorrect password.")); break; case QQ_LOGIN_REPLY_MISC_ERROR: diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/simple/simple.c --- a/libpurple/protocols/simple/simple.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/simple/simple.c Fri Sep 28 20:58:48 2007 +0000 @@ -695,7 +695,7 @@ static void do_register_exp(struct simple_account_data *sip, int expire) { char *uri, *to, *contact, *hdr; - /* Set our default expiration to 900, + /* Set our default expiration to 900, * as done in the initialization of the simple_account_data * structure. */ @@ -1042,6 +1042,8 @@ if(sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) { purple_debug_info("simple", "Setting wants_to_die to true.\n"); sip->gc->wants_to_die = TRUE; + if (!purple_account_get_remember_password(sip->gc->account)) + purple_account_set_password(sip->gc->account, NULL); purple_connection_error(sip->gc, _("Incorrect password.")); return TRUE; } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Fri Sep 28 20:58:48 2007 +0000 @@ -202,6 +202,8 @@ if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { gc->wants_to_die = TRUE; + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL); purple_connection_error(gc, _("You have signed on from another location.")); return; } @@ -4090,12 +4092,12 @@ { return TRUE; } - + gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type) { PurpleConversation *c; - c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, username, gc->account); g_return_val_if_fail(c != NULL, FALSE); @@ -4120,7 +4122,7 @@ attn->incoming_description = _("%s has buzzed you!"); attn->outgoing_description = _("Buzzing %s..."); list = g_list_append(list, attn); - } + } return list; } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/server.c --- a/libpurple/server.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/server.c Fri Sep 28 20:58:48 2007 +0000 @@ -543,7 +543,7 @@ PurpleMessageFlags flags, time_t mtime) { PurpleAccount *account; - PurpleConversation *cnv; + PurpleConversation *conv; char *message, *name; char *angel, *buffy; int plugin_return; @@ -562,22 +562,19 @@ * We should update the conversation window buttons and menu, * if it exists. */ - cnv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, gc->account); + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, gc->account); /* - * Plugin stuff. we pass a char ** but we don't want to pass what's - * been given us by the prpls. So we create temp holders and pass - * those instead. It's basically just to avoid segfaults. + * Make copies of the message and the sender in case plugins want + * to free these strings and replace them with a modifed version. */ - /* TODO: MAX(message, BUF_LONG) is pretty ugly. */ - buffy = g_malloc(MAX(strlen(msg) + 1, BUF_LONG)); - strcpy(buffy, msg); + buffy = g_strdup(msg); angel = g_strdup(who); plugin_return = GPOINTER_TO_INT( purple_signal_emit_return_1(purple_conversations_get_handle(), "receiving-im-msg", gc->account, - &angel, &buffy, cnv, &flags)); + &angel, &buffy, conv, &flags)); if (!buffy || !angel || plugin_return) { g_free(buffy); @@ -589,21 +586,21 @@ message = buffy; purple_signal_emit(purple_conversations_get_handle(), "received-im-msg", gc->account, - name, message, cnv, flags); + name, message, conv, flags); /* search for conversation again in case it was created by received-im-msg handler */ - if (cnv == NULL) - cnv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account); + if (conv == NULL) + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, gc->account); /* * XXX: Should we be setting this here, or relying on prpls to set it? */ flags |= PURPLE_MESSAGE_RECV; - if (cnv == NULL) - cnv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name); + if (conv == NULL) + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name); - purple_conv_im_write(PURPLE_CONV_IM(cnv), NULL, message, flags, mtime); + purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, message, flags, mtime); g_free(message); /* @@ -670,7 +667,7 @@ { serv_send_im(gc, name, away_msg, PURPLE_MESSAGE_AUTO_RESP); - purple_conv_im_write(PURPLE_CONV_IM(cnv), NULL, away_msg, + purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, away_msg, PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_AUTO_RESP, mtime); } @@ -892,15 +889,10 @@ return; /* - * Plugin stuff. We pass a char ** but we don't want to pass what's - * been given us by the prpls. so we create temp holders and pass those - * instead. It's basically just to avoid segfaults. Of course, if the - * data is binary, plugins don't see it. Bitch all you want; i really - * don't want you to be dealing with it. + * Make copies of the message and the sender in case plugins want + * to free these strings and replace them with a modifed version. */ - /* TODO: MAX(message, BUF_LONG) is pretty ugly. */ - buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG)); - strcpy(buffy, message); + buffy = g_strdup(message); angel = g_strdup(who); plugin_return = GPOINTER_TO_INT( @@ -913,6 +905,7 @@ g_free(angel); return; } + who = angel; message = buffy; diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/sslconn.h --- a/libpurple/sslconn.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/sslconn.h Fri Sep 28 20:58:48 2007 +0000 @@ -31,6 +31,7 @@ #define PURPLE_SSL_DEFAULT_PORT 443 +/** Possible SSL errors. */ typedef enum { PURPLE_SSL_HANDSHAKE_FAILED = 1, @@ -86,39 +87,48 @@ typedef struct { /** Initializes the SSL system provided. - * @return TRUE if initialization succeeded - */ + * @return @a TRUE if initialization succeeded + * @see purple_ssl_init + */ gboolean (*init)(void); - /** Unloads the SSL system. Inverse of init. */ + /** Unloads the SSL system. Inverse of PurpleSslOps::init. + * @see purple_ssl_uninit + */ void (*uninit)(void); - /** Sets up the SSL connection for a PurpleSslConnection once - * the TCP connection has been established */ + /** Sets up the SSL connection for a #PurpleSslConnection once + * the TCP connection has been established + * @see purple_ssl_connect + */ void (*connectfunc)(PurpleSslConnection *gsc); /** Destroys the internal data of the SSL connection provided. * Freeing gsc itself is left to purple_ssl_close() - * + * @see purple_ssl_close */ void (*close)(PurpleSslConnection *gsc); /** Reads data from a connection (like POSIX read()) - * @param gsc Connection context - * @param data Pointer to buffer to drop data into - * @param len Maximum number of bytes to read - * @return Number of bytes actually written into the buffer, or <0 on error + * @param gsc Connection context + * @param data Pointer to buffer to drop data into + * @param len Maximum number of bytes to read + * @return Number of bytes actually written into @a data (which may be + * less than @a len), or <0 on error + * @see purple_ssl_read */ size_t (*read)(PurpleSslConnection *gsc, void *data, size_t len); /** Writes data to a connection (like POSIX send()) - * @param gsc Connection context - * @param data Data buffer to send data from - * @param len Number of bytes to send from buffer - * @return The number of bytes written (may be less than len) or <0 on error + * @param gsc Connection context + * @param data Data buffer to send data from + * @param len Number of bytes to send from buffer + * @return The number of bytes written to @a data (may be less than + * @a len) or <0 on error + * @see purple_ssl_write */ size_t (*write)(PurpleSslConnection *gsc, const void *data, size_t len); /** Obtains the certificate chain provided by the peer * * @param gsc Connection context - * @return A newly allocated list containing the certificates - * the peer provided. - * @see PurpleCertificate + * @return A newly allocated list of #PurpleCertificate containing the + * certificates the peer provided. + * @see purple_ssl_get_peer_certificates * @todo Decide whether the ordering of certificates in this * list can be guaranteed. */ @@ -141,12 +151,12 @@ /** * Returns whether or not SSL is currently supported. * - * @return TRUE if SSL is supported, or FALSE otherwise. + * @return @a TRUE if SSL is supported, or @a FALSE otherwise. */ gboolean purple_ssl_is_supported(void); /** - * Returns a human-readable string for an SSL error + * Returns a human-readable string for an SSL error. * * @param error Error code * @return Human-readable error explanation @@ -163,8 +173,8 @@ * @param port The destination port. * @param func The SSL input handler function. * @param error_func The SSL error handler function. This function - * should NOT call purple_ssl_close(). In the event - * of an error the PurpleSslConnection will be + * should NOT call purple_ssl_close(). In + * the event of an error the #PurpleSslConnection will be * destroyed for you. * @param data User-defined data. * @@ -177,7 +187,8 @@ /** * Makes a SSL connection using an already open file descriptor. - * DEPRECATED. Use purple_ssl_connect_with_host_fd instead. + * + * @deprecated Use purple_ssl_connect_with_host_fd() instead. * * @param account The account making the connection. * @param fd The file descriptor. @@ -256,7 +267,7 @@ * @param gsc The SSL connection handle * * @return The peer certificate chain, in the order of certificate, issuer, - * issuer's issuer, etc. NULL if no certificates have been provided, + * issuer's issuer, etc. @a NULL if no certificates have been provided, */ GList * purple_ssl_get_peer_certificates(PurpleSslConnection *gsc); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/status.c --- a/libpurple/status.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/status.c Fri Sep 28 20:58:48 2007 +0000 @@ -601,7 +601,7 @@ { time_t current_time = time(NULL); const char *buddy_alias = purple_buddy_get_alias(buddy); - char *tmp; + char *tmp, *logtmp; PurpleLog *log; if (old_status != NULL) @@ -609,6 +609,10 @@ tmp = g_strdup_printf(_("%s changed status from %s to %s"), buddy_alias, purple_status_get_name(old_status), purple_status_get_name(new_status)); + logtmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias, buddy->name, + purple_status_get_name(old_status), + purple_status_get_name(new_status)); + } else { @@ -618,11 +622,16 @@ { tmp = g_strdup_printf(_("%s is now %s"), buddy_alias, purple_status_get_name(new_status)); + logtmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias, buddy->name, + purple_status_get_name(new_status)); + } else { tmp = g_strdup_printf(_("%s is no longer %s"), buddy_alias, purple_status_get_name(new_status)); + logtmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias, buddy->name, + purple_status_get_name(new_status)); } } @@ -630,10 +639,11 @@ if (log != NULL) { purple_log_write(log, PURPLE_MESSAGE_SYSTEM, buddy_alias, - current_time, tmp); + current_time, logtmp); } g_free(tmp); + g_free(logtmp); } } diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/util.c --- a/libpurple/util.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/util.c Fri Sep 28 20:58:48 2007 +0000 @@ -68,7 +68,7 @@ }; static char *custom_user_dir = NULL; -static char *home_dir = NULL; +static char *user_dir = NULL; PurpleMenuAction * purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data, @@ -515,23 +515,6 @@ * Date/Time Functions **************************************************************************/ -#ifdef _WIN32 -static long win32_get_tz_offset() { - TIME_ZONE_INFORMATION tzi; - DWORD ret; - long off = -1; - - if ((ret = GetTimeZoneInformation(&tzi)) != TIME_ZONE_ID_INVALID) - { - off = -(tzi.Bias * 60); - if (ret == TIME_ZONE_ID_DAYLIGHT) - off -= tzi.DaylightBias * 60; - } - - return off; -} -#endif - const char *purple_get_tzoff_str(const struct tm *tm, gboolean iso) { static char buf[7]; @@ -546,7 +529,7 @@ g_return_val_if_reached(""); #ifdef _WIN32 - if ((off = win32_get_tz_offset()) == -1) + if ((off = wpurple_get_tz_offset()) == -1) return ""; #else # ifdef HAVE_TM_GMTOFF @@ -854,7 +837,7 @@ #endif #ifdef _WIN32 - if ((sys_tzoff = win32_get_tz_offset()) == -1) + if ((sys_tzoff = wpurple_get_tz_offset()) == -1) tzoff = PURPLE_NO_TZ_OFF; else tzoff += sys_tzoff; @@ -2465,10 +2448,10 @@ { if (custom_user_dir != NULL) return custom_user_dir; - else if (!home_dir) - home_dir = g_build_filename(purple_home_dir(), ".purple", NULL); - - return home_dir; + else if (!user_dir) + user_dir = g_build_filename(purple_home_dir(), ".purple", NULL); + + return user_dir; } void purple_util_set_user_dir(const char *dir) diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/win32/win32dep.c --- a/libpurple/win32/win32dep.c Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/win32/win32dep.c Fri Sep 28 20:58:48 2007 +0000 @@ -575,6 +575,22 @@ libpurpledll_hInstance = NULL; } +long +wpurple_get_tz_offset() { + TIME_ZONE_INFORMATION tzi; + DWORD ret; + long off = -1; + + if ((ret = GetTimeZoneInformation(&tzi)) != TIME_ZONE_ID_INVALID) + { + off = -(tzi.Bias * 60); + if (ret == TIME_ZONE_ID_DAYLIGHT) + off -= tzi.DaylightBias * 60; + } + + return off; +} + /* DLL initializer */ /* suppress gcc "no previous prototype" warning */ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/win32/win32dep.h --- a/libpurple/win32/win32dep.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/win32/win32dep.h Fri Sep 28 20:58:48 2007 +0000 @@ -60,6 +60,7 @@ void wpurple_init(void); void wpurple_cleanup(void); +long wpurple_get_tz_offset(void); /* * MACROS diff -r 5c42c7c7780d -r 045e4f2459a9 libpurple/xmlnode.h --- a/libpurple/xmlnode.h Fri Sep 28 20:58:27 2007 +0000 +++ b/libpurple/xmlnode.h Fri Sep 28 20:58:48 2007 +0000 @@ -129,7 +129,7 @@ * * @param node The node to get data from. * - * @return The data from the node. This data is in raw escaped format. + * @return The data from the node or NULL. This data is in raw escaped format. * You must g_free this string when finished using it. */ char *xmlnode_get_data(xmlnode *node); diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkblist.c Fri Sep 28 20:58:48 2007 +0000 @@ -88,6 +88,8 @@ GtkWidget *group_combo; GtkWidget *entries_box; GtkSizeGroup *sg; + GtkWidget *autojoin; + GtkWidget *persistent; GList *entries; @@ -138,13 +140,23 @@ static void redo_buddy_list(PurpleBuddyList *list, gboolean remove, gboolean rerender); static void pidgin_blist_collapse_contact_cb(GtkWidget *w, PurpleBlistNode *node); static char *pidgin_get_group_title(PurpleBlistNode *gnode, gboolean expanded); - -struct _pidgin_blist_node { +static void pidgin_blist_expand_contact_cb(GtkWidget *w, PurpleBlistNode *node); + +typedef enum { + PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE = 1 << 0, /* Whether there's pending message in a conversation */ +} PidginBlistNodeFlags; + +typedef struct _pidgin_blist_node { GtkTreeRowReference *row; gboolean contact_expanded; gboolean recent_signonoff; gint recent_signonoff_timer; -}; + struct { + PurpleConversation *conv; + time_t last_message; /* timestamp for last displayed message */ + PidginBlistNodeFlags flags; + } conv; +} PidginBlistNode; static char dim_grey_string[8] = ""; static char *dim_grey() @@ -306,12 +318,36 @@ serv_send_file(b->account->gc, b->name, NULL); } +static void gtk_blist_menu_move_to_cb(GtkWidget *w, PurpleBlistNode *node) +{ + PurpleGroup *group = g_object_get_data(G_OBJECT(w), "groupnode"); + purple_blist_add_contact((PurpleContact *)node, group, NULL); + +} + static void gtk_blist_menu_autojoin_cb(GtkWidget *w, PurpleChat *chat) { purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin", gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))); } +static void gtk_blist_menu_persistent_cb(GtkWidget *w, PurpleChat *chat) +{ + purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent", + gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))); +} + +static PurpleConversation * +find_conversation_with_buddy(PurpleBuddy *buddy) +{ + PidginBlistNode *ui = buddy->node.ui_data; + if (ui) + return ui->conv.conv; + return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + purple_buddy_get_name(buddy), + purple_buddy_get_account(buddy)); +} + static void gtk_blist_join_chat(PurpleChat *chat) { PurpleConversation *conv; @@ -395,6 +431,105 @@ } #endif +static void +gtk_blist_do_personize(GList *merges) +{ + PurpleBlistNode *contact = NULL; + int max = 0; + GList *tmp; + + /* First, we find the contact to merge the rest of the buddies into. + * This will be the contact with the most buddies in it; ties are broken + * by which contact is higher in the list + */ + for (tmp = merges; tmp; tmp = tmp->next) { + PurpleBlistNode *node = tmp->data; + PurpleBlistNode *b; + int i = 0; + + if (node->type == PURPLE_BLIST_BUDDY_NODE) + node = node->parent; + + if (node->type != PURPLE_BLIST_CONTACT_NODE) + continue; + + + for (b = node->child; b; b = b->next) + i++; + if (i > max) { + contact = node; + max = i; + } + } + + if (contact == NULL) + return; + + /* Merge all those buddies into this contact */ + for (tmp = merges; tmp; tmp = tmp->next) { + PurpleBlistNode *node = tmp->data; + if (node->type == PURPLE_BLIST_BUDDY_NODE) + node = node->parent; + + if (node == contact) + continue; + + purple_blist_merge_contact((PurpleContact *)node, contact); + } + + /* And show the expanded contact, so the people know what's going on */ + pidgin_blist_expand_contact_cb(NULL, contact); + g_list_free(merges); +} + +static void +gtk_blist_auto_personize(PurpleBlistNode *group, const char *alias) +{ + PurpleBlistNode *contact; + PurpleBlistNode *buddy; + GList *merges = NULL; + int i = 0; + char *a = g_utf8_casefold(alias, -1); + + for (contact = group->child; contact; contact = contact->next) { + char *node_alias; + if (contact->type != PURPLE_BLIST_CONTACT_NODE) + continue; + + node_alias = g_utf8_casefold(purple_contact_get_alias((PurpleContact *)contact), -1); + if (node_alias && !g_utf8_collate(node_alias, a)) { + merges = g_list_append(merges, contact); + i++; + g_free(node_alias); + continue; + } + g_free(node_alias); + + for (buddy = contact->child; buddy; buddy = buddy->next) { + if (buddy->type != PURPLE_BLIST_BUDDY_NODE) + continue; + + node_alias = g_utf8_casefold(purple_buddy_get_alias((PurpleBuddy *)buddy), -1); + if (node_alias && !g_utf8_collate(node_alias, a)) { + merges = g_list_append(merges, buddy); + i++; + } + g_free(node_alias); + } + } + g_free(a); + + if (i > 1) + { + char *msg = g_strdup_printf(ngettext("You have %d contact named %s. Would you like to merge them?", "You currently have %d contacts named %s. Would you like to merge them?", i), i, alias); + purple_request_action(NULL, NULL, msg, _("Merging these contacts will cause them to share a single entry on the buddy list and use a single conversation window. " + "You can separate them again by choosing 'Expand' from the contact's context menu"), 0, NULL, NULL, NULL, + merges, 2, _("_Merge"), PURPLE_CALLBACK(gtk_blist_do_personize), _("_Cancel"), PURPLE_CALLBACK(g_list_free)); + g_free(msg); + } else + g_list_free(merges); +} + static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *arg1, char *arg2, PurpleBuddyList *list) { @@ -421,13 +556,14 @@ PurpleContact *contact = (PurpleContact *)node; struct _pidgin_blist_node *gtknode = (struct _pidgin_blist_node *)node->ui_data; - if (contact->alias || gtknode->contact_expanded) + if (contact->alias || gtknode->contact_expanded) { purple_blist_alias_contact(contact, arg2); - else - { + gtk_blist_auto_personize(node->parent, arg2); + } else { PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact); purple_blist_alias_buddy(buddy, arg2); serv_alias_buddy(buddy); + gtk_blist_auto_personize(node->parent, arg2); } } break; @@ -435,6 +571,7 @@ case PURPLE_BLIST_BUDDY_NODE: purple_blist_alias_buddy((PurpleBuddy*)node, arg2); serv_alias_buddy((PurpleBuddy *)node); + gtk_blist_auto_personize(node->parent->parent, arg2); break; case PURPLE_BLIST_GROUP_NODE: dest = purple_find_group(arg2); @@ -525,6 +662,26 @@ } } +static void gtk_blist_menu_showoffline_cb(GtkWidget *w, PurpleBlistNode *node) +{ + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) + { + purple_blist_node_set_bool(node, "show_offline", + !purple_blist_node_get_bool(node, "show_offline")); + } + else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) + { + PurpleBlistNode *bnode; + gboolean setting = !purple_blist_node_get_bool(node, "show_offline"); + + purple_blist_node_set_bool(node, "show_offline", setting); + for (bnode = node->child; bnode != NULL; bnode = bnode->next) { + purple_blist_node_set_bool(bnode, "show_offline", setting); + } + } + pidgin_blist_update(purple_get_blist(), node); +} + static void gtk_blist_show_systemlog_cb() { pidgin_syslog_show(); @@ -1119,17 +1276,49 @@ g_list_free(ll); } + + +static void +pidgin_append_blist_node_move_to_menu(GtkWidget *menu, PurpleBlistNode *node) +{ + GtkWidget *submenu; + GtkWidget *menuitem; + PurpleBlistNode *group; + + menuitem = gtk_menu_item_new_with_label(_("Move to")); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + gtk_widget_show(menuitem); + + submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + + for (group = purple_blist_get_root(); group; group = group->next) { + if (group->type != PURPLE_BLIST_GROUP_NODE) + continue; + if (group == node->parent) + continue; + menuitem = pidgin_new_item_from_stock(submenu, purple_group_get_name((PurpleGroup *)group), NULL, + G_CALLBACK(gtk_blist_menu_move_to_cb), node, 0, 0, NULL); + g_object_set_data(G_OBJECT(menuitem), "groupnode", group); + } + gtk_widget_show_all(submenu); +} + void pidgin_blist_make_buddy_menu(GtkWidget *menu, PurpleBuddy *buddy, gboolean sub) { PurplePluginProtocolInfo *prpl_info; PurpleContact *contact; + PurpleBlistNode *node; gboolean contact_expanded = FALSE; + gboolean show_offline = FALSE; g_return_if_fail(menu); g_return_if_fail(buddy); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(buddy->account->gc->prpl); + node = (PurpleBlistNode*)buddy; + contact = purple_buddy_get_contact(buddy); if (contact) { contact_expanded = ((struct _pidgin_blist_node *)(((PurpleBlistNode*)contact)->ui_data))->contact_expanded; @@ -1145,17 +1334,17 @@ if (!prpl_info->can_receive_file || prpl_info->can_receive_file(buddy->account->gc, buddy->name)) { - pidgin_new_item_from_stock(menu, _("_Send File"), + pidgin_new_item_from_stock(menu, _("_Send File..."), PIDGIN_STOCK_TOOLBAR_SEND_FILE, G_CALLBACK(gtk_blist_menu_send_file_cb), buddy, 0, 0, NULL); } } - pidgin_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, + pidgin_new_item_from_stock(menu, _("Add Buddy _Pounce..."), NULL, G_CALLBACK(gtk_blist_menu_bp_cb), buddy, 0, 0, NULL); - if (((PurpleBlistNode*)buddy)->parent && ((PurpleBlistNode*)buddy)->parent->child->next && + if (node->parent && node->parent->child->next && !sub && !contact_expanded) { pidgin_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), @@ -1165,15 +1354,22 @@ G_CALLBACK(gtk_blist_menu_showlog_cb), buddy, 0, 0, NULL); } - - pidgin_append_blist_node_proto_menu(menu, buddy->account->gc, - (PurpleBlistNode *)buddy); - pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy); - - if (((PurpleBlistNode*)buddy)->parent && ((PurpleBlistNode*)buddy)->parent->child->next && + if (!(purple_blist_node_get_flags(node) & PURPLE_BLIST_NODE_FLAG_NO_SAVE)) { + show_offline = purple_blist_node_get_bool(node, "show_offline"); + pidgin_new_item_from_stock(menu, show_offline ? _("Hide when offline") : _("Show when offline"), + NULL, G_CALLBACK(gtk_blist_menu_showoffline_cb), node, 0, 0, NULL); + } + + pidgin_append_blist_node_proto_menu(menu, buddy->account->gc, node); + pidgin_append_blist_node_extended_menu(menu, node); + + if (!contact_expanded) + pidgin_append_blist_node_move_to_menu(menu, (PurpleBlistNode *)contact); + + if (node->parent && node->parent->child->next && !sub && !contact_expanded) { pidgin_separator(menu); - pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy); + pidgin_append_blist_node_privacy_menu(menu, node); pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS, G_CALLBACK(gtk_blist_menu_alias_cb), contact, 0, 0, NULL); @@ -1182,7 +1378,7 @@ contact, 0, 0, NULL); } else if (!sub || contact_expanded) { pidgin_separator(menu); - pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy); + pidgin_append_blist_node_privacy_menu(menu, node); pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS, G_CALLBACK(gtk_blist_menu_alias_cb), buddy, 0, 0, NULL); pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, @@ -1235,10 +1431,10 @@ GtkWidget *item; menu = gtk_menu_new(); - item = pidgin_new_item_from_stock(menu, _("Add a _Buddy"), GTK_STOCK_ADD, + item = pidgin_new_item_from_stock(menu, _("Add _Buddy..."), GTK_STOCK_ADD, G_CALLBACK(pidgin_blist_add_buddy_cb), node, 0, 0, NULL); gtk_widget_set_sensitive(item, purple_connections_get_all() != NULL); - item = pidgin_new_item_from_stock(menu, _("Add a C_hat"), GTK_STOCK_ADD, + item = pidgin_new_item_from_stock(menu, _("Add C_hat..."), GTK_STOCK_ADD, G_CALLBACK(pidgin_blist_add_chat_cb), node, 0, 0, NULL); gtk_widget_set_sensitive(item, pidgin_blist_joinchat_is_showable()); pidgin_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, @@ -1256,16 +1452,19 @@ create_chat_menu(PurpleBlistNode *node, PurpleChat *c) { GtkWidget *menu; - gboolean autojoin; + gboolean autojoin, persistent; menu = gtk_menu_new(); autojoin = (purple_blist_node_get_bool(node, "gtk-autojoin") || (purple_blist_node_get_string(node, "gtk-autojoin") != NULL)); + persistent = purple_blist_node_get_bool(node, "gtk-persistent"); pidgin_new_item_from_stock(menu, _("_Join"), PIDGIN_STOCK_CHAT, G_CALLBACK(gtk_blist_menu_join_cb), node, 0, 0, NULL); pidgin_new_check_item(menu, _("Auto-Join"), G_CALLBACK(gtk_blist_menu_autojoin_cb), node, autojoin); + pidgin_new_check_item(menu, _("Persistent"), + G_CALLBACK(gtk_blist_menu_persistent_cb), node, persistent); pidgin_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL); @@ -1307,7 +1506,6 @@ node, 0, 0, NULL); pidgin_append_blist_node_extended_menu(menu, node); - return menu; } @@ -2888,7 +3086,7 @@ /* Accounts menu */ { N_("/_Accounts"), NULL, NULL, 0, "", NULL }, - { N_("/Accounts/Add\\/Edit"), "A", pidgin_accounts_window_show, 0, "", NULL }, + { N_("/Accounts/Manage"), "A", pidgin_accounts_window_show, 0, "", NULL }, /* Tools */ { N_("/_Tools"), NULL, NULL, 0, "", NULL }, @@ -3145,8 +3343,6 @@ GdkPixbuf *ret; PurplePresence *p; - - if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { if(!gtknode->contact_expanded) { buddy = purple_contact_get_priority_buddy((PurpleContact*)node); @@ -3188,6 +3384,13 @@ return ret; } + if (purple_status_get_attr_string(purple_presence_get_active_status(p), PURPLE_TUNE_TITLE)) { + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL); + ret = gdk_pixbuf_new_from_file(path, NULL); + g_free(path); + return ret; + } + prpl = purple_find_prpl(purple_account_get_protocol_id(buddy->account)); if (!prpl) return NULL; @@ -3261,17 +3464,17 @@ } if(buddy) { - PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - purple_buddy_get_name(buddy), - purple_buddy_get_account(buddy)); + PurpleConversation *conv = find_conversation_with_buddy(buddy); PurplePresence *p; gboolean trans; if(conv != NULL) { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if((gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) && size == PIDGIN_STATUS_ICON_SMALL) { - return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), PIDGIN_STOCK_STATUS_MESSAGE, - icon_size, "GtkTreeView"); + if (gtkconv == NULL && size == PIDGIN_STATUS_ICON_SMALL) { + PidginBlistNode *ui = buddy->node.ui_data; + if (ui == NULL || (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)) + return gtk_widget_render_icon (GTK_WIDGET(gtkblist->treeview), + PIDGIN_STOCK_STATUS_MESSAGE, icon_size, "GtkTreeView"); } } @@ -3327,16 +3530,17 @@ struct _pidgin_blist_node *gtkcontactnode = NULL; char *idletime = NULL, *statustext = NULL; time_t t; - PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - purple_buddy_get_name(b), - purple_buddy_get_account(b)); - PidginConversation *gtkconv; + PurpleConversation *conv = find_conversation_with_buddy(b); gboolean hidden_conv = FALSE; - if(conv != NULL) { - gtkconv = PIDGIN_CONVERSATION(conv); - if(gtkconv == NULL || pidgin_conv_is_hidden(gtkconv)) { - hidden_conv = TRUE; + if (conv != NULL) { + PidginBlistNode *ui = b->node.ui_data; + if (ui) { + if (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE) + hidden_conv = TRUE; + } else { + if (PIDGIN_CONVERSATION(conv) == NULL) + hidden_conv = TRUE; } } @@ -3671,7 +3875,7 @@ menu = NULL; } - convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0); + convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0); if (!convs) /* no conversations added, don't show the menu */ return; @@ -3727,7 +3931,7 @@ gtkblist->menutrayicon = NULL; } - convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_IM, PIDGIN_UNSEEN_TEXT, TRUE, 0); + convs = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_ANY, PIDGIN_UNSEEN_TEXT, TRUE, 0); if (convs) { GtkWidget *img = NULL; GString *tooltip_text = NULL; @@ -3735,14 +3939,10 @@ tooltip_text = g_string_new(""); l = convs; while (l != NULL) { - if (PIDGIN_IS_PIDGIN_CONVERSATION(l->data)) { - PidginConversation *gtkconv = PIDGIN_CONVERSATION((PurpleConversation *)l->data); - - g_string_append_printf(tooltip_text, - ngettext("%d unread message from %s\n", "%d unread messages from %s\n", gtkconv->unseen_count), - gtkconv->unseen_count, - gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); - } + int count = GPOINTER_TO_INT(purple_conversation_get_data(l->data, "unseen-count")); + g_string_append_printf(tooltip_text, + ngettext("%d unread message from %s\n", "%d unread messages from %s\n", count), + count, purple_conversation_get_name(l->data)); l = l->next; } if(tooltip_text->len > 0) { @@ -3770,6 +3970,88 @@ conversation_updated_cb(conv, PURPLE_CONV_UPDATE_UNSEEN, gtkblist); } +static void +conversation_deleted_update_ui_cb(PurpleConversation *conv, struct _pidgin_blist_node *ui) +{ + if (ui->conv.conv != conv) + return; + ui->conv.conv = NULL; + ui->conv.flags = 0; + ui->conv.last_message = 0; +} + +static void +written_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message, + PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node) +{ + PidginBlistNode *ui = node->ui_data; + if (ui->conv.conv != conv || PIDGIN_CONVERSATION(conv) || + !(flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV))) + return; + ui->conv.flags |= PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE; + ui->conv.last_message = time(NULL); /* XXX: for lack of better data */ + pidgin_blist_update(purple_get_blist(), node); +} + +static void +displayed_msg_update_ui_cb(PurpleAccount *account, const char *who, const char *message, + PurpleConversation *conv, PurpleMessageFlags flag, PurpleBlistNode *node) +{ + PidginBlistNode *ui = node->ui_data; + if (ui->conv.conv != conv) + return; + ui->conv.flags &= ~PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE; + pidgin_blist_update(purple_get_blist(), node); +} + +static void +conversation_created_cb(PurpleConversation *conv, PidginBuddyList *gtkblist) +{ + switch (conv->type) { + case PURPLE_CONV_TYPE_IM: + { + GSList *buddies = purple_find_buddies(conv->account, conv->name); + while (buddies) { + PurpleBlistNode *buddy = buddies->data; + struct _pidgin_blist_node *ui = buddy->ui_data; + buddies = g_slist_delete_link(buddies, buddies); + if (!ui) + continue; + ui->conv.conv = conv; + ui->conv.flags = 0; + ui->conv.last_message = 0; + purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", + ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui); + purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", + ui, PURPLE_CALLBACK(written_msg_update_ui_cb), buddy); + purple_signal_connect(pidgin_conversations_get_handle(), "displayed-im-msg", + ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), buddy); + } + } + case PURPLE_CONV_TYPE_CHAT: + { + PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name); + struct _pidgin_blist_node *ui; + if (!chat) + break; + ui = chat->node.ui_data; + if (!ui) + break; + ui->conv.conv = conv; + ui->conv.flags = 0; + ui->conv.last_message = 0; + purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", + ui, PURPLE_CALLBACK(conversation_deleted_update_ui_cb), ui); + purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", + ui, PURPLE_CALLBACK(written_msg_update_ui_cb), chat); + purple_signal_connect(pidgin_conversations_get_handle(), "displayed-chat-msg", + ui, PURPLE_CALLBACK(displayed_msg_update_ui_cb), chat); + } + default: + break; + } +} + /********************************************************************************** * Public API Functions * **********************************************************************************/ @@ -4334,7 +4616,7 @@ tmp = g_strdup_printf(_("Welcome to %s!\n\n" "You have no accounts enabled. Enable your IM accounts from the " - "Accounts window at Accounts->Add/Edit. Once you " + "Accounts window at Accounts->Manage. Once you " "enable accounts, you'll be able to sign on, set your status, " "and talk to your friends."), PIDGIN_NAME); pretty = pidgin_make_pretty_arrows(tmp); @@ -4683,6 +4965,9 @@ purple_signal_connect(purple_conversations_get_handle(), "deleting-conversation", gtkblist, PURPLE_CALLBACK(conversation_deleting_cb), gtkblist); + purple_signal_connect(purple_conversations_get_handle(), "conversation-created", + gtkblist, PURPLE_CALLBACK(conversation_created_cb), + gtkblist); gtk_widget_hide(gtkblist->headline_hbox); gtk_widget_hide(gtkblist->error_buttons); @@ -4789,6 +5074,7 @@ if(gtknode->recent_signonoff_timer > 0) purple_timeout_remove(gtknode->recent_signonoff_timer); + purple_signals_disconnect_by_handle(node->ui_data); g_free(node->ui_data); node->ui_data = NULL; } @@ -4953,7 +5239,7 @@ STATUS_ICON_COLUMN, NULL, NAME_COLUMN, title, NODE_COLUMN, gnode, - BGCOLOR_COLUMN, &bgcolor, + /* BGCOLOR_COLUMN, &bgcolor, */ GROUP_EXPANDER_COLUMN, TRUE, GROUP_EXPANDER_VISIBLE_COLUMN, TRUE, CONTACT_EXPANDER_VISIBLE_COLUMN, FALSE, @@ -5213,14 +5499,17 @@ GdkPixbuf *emblem; char *mark; gboolean showicons = purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_buddy_icons"); - const char *name = purple_chat_get_name(chat); - PurpleConversation *conv = - purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, chat->account); - gboolean hidden = (conv && !PIDGIN_CONVERSATION(conv)); - - if(!insert_node(list, node, &iter)) + PidginBlistNode *ui; + PurpleConversation *conv; + gboolean hidden; + + if (!insert_node(list, node, &iter)) return; + ui = node->ui_data; + conv = ui->conv.conv; + hidden = (conv && (ui->conv.flags & PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE)); + status = pidgin_blist_get_status_icon(node, PIDGIN_STATUS_ICON_SMALL); emblem = pidgin_blist_get_emblem(node); @@ -5527,7 +5816,7 @@ gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); /* Set up stuff for the account box */ - label = gtk_label_new_with_mnemonic(_("_Account:")); + label = gtk_label_new_with_mnemonic(_("A_ccount:")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); @@ -5644,6 +5933,12 @@ purple_blist_add_chat(chat, group, NULL); } + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->autojoin))) + purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-autojoin", TRUE); + + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->persistent))) + purple_blist_node_set_bool((PurpleBlistNode*)chat, "gtk-persistent", TRUE); + gtk_widget_destroy(data->window); g_free(data->default_chat_name); g_list_free(data->entries); @@ -5938,6 +6233,11 @@ gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_BIN(data->group_combo)->child); pidgin_set_accessible_label (data->group_combo, label); gtk_box_pack_end(GTK_BOX(rowbox), data->group_combo, TRUE, TRUE, 0); + + data->autojoin = gtk_check_button_new_with_mnemonic(_("Autojoin when account becomes online.")); + data->persistent = gtk_check_button_new_with_mnemonic(_("Hide chat when the window is closed.")); + gtk_box_pack_start(GTK_BOX(vbox), data->autojoin, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), data->persistent, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(data->window), "response", G_CALLBACK(add_chat_resp_cb), data); @@ -6586,7 +6886,7 @@ for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) { menuitem = l->data; - if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Add\\/Edit"))) + if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Manage"))) gtk_widget_destroy(menuitem); } diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkcellrendererexpander.c --- a/pidgin/gtkcellrendererexpander.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkcellrendererexpander.c Fri Sep 28 20:58:48 2007 +0000 @@ -270,6 +270,9 @@ cell_area->x + cell->xpad + (width / 2), cell_area->y + cell->ypad + (height / 2), cell->is_expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED); + if (cell->is_expanded) + gtk_paint_hline (widget->style, window, state, NULL, widget, NULL, 0, + widget->allocation.width, cell_area->y + cell_area->height); } static gboolean pidgin_cell_renderer_expander_activate(GtkCellRenderer *r, diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkconn.c --- a/pidgin/gtkconn.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkconn.c Fri Sep 28 20:58:48 2007 +0000 @@ -53,7 +53,7 @@ * The key is a pointer to the PurpleAccount and the * value is a pointer to a PidginAutoRecon. */ -static GHashTable *hash = NULL; +static GHashTable *auto_reconns = NULL; static void pidgin_connection_connect_progress(PurpleConnection *gc, @@ -80,7 +80,7 @@ pidgin_status_box_set_connecting(PIDGIN_STATUS_BOX(gtkblist->statusbox), (purple_connections_get_connecting() != NULL)); - g_hash_table_remove(hash, account); + g_hash_table_remove(auto_reconns, account); pidgin_blist_update_account_error_state(account, NULL); } @@ -120,7 +120,7 @@ purple_debug_info("autorecon", "do_signon called\n"); g_return_val_if_fail(account != NULL, FALSE); - info = g_hash_table_lookup(hash, account); + info = g_hash_table_lookup(auto_reconns, account); if (info) info->timeout = 0; @@ -141,15 +141,16 @@ { PurpleAccount *account = NULL; PidginAutoRecon *info; + GList *list; account = purple_connection_get_account(gc); - info = g_hash_table_lookup(hash, account); + info = g_hash_table_lookup(auto_reconns, account); pidgin_blist_update_account_error_state(account, text); if (!gc->wants_to_die) { if (info == NULL) { info = g_new0(PidginAutoRecon, 1); - g_hash_table_insert(hash, account, info); + g_hash_table_insert(auto_reconns, account, info); info->delay = g_random_int_range(INITIAL_RECON_DELAY_MIN, INITIAL_RECON_DELAY_MAX); } else { info->delay = MIN(2 * info->delay, MAX_RECON_DELAY); @@ -160,7 +161,7 @@ } else { char *p, *s, *n=NULL ; if (info != NULL) - g_hash_table_remove(hash, account); + g_hash_table_remove(auto_reconns, account); if (purple_account_get_alias(account)) { @@ -192,6 +193,17 @@ */ purple_account_set_enabled(account, PIDGIN_UI, FALSE); } + + /* If we have any open chats, we probably want to rejoin when we get back online. */ + list = purple_get_chats(); + while (list) { + PurpleConversation *conv = list->data; + list = list->next; + if (conv->account != account || + purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv))) + continue; + purple_conversation_set_data(conv, "want-to-rejoin", GINT_TO_POINTER(TRUE)); + } } static void pidgin_connection_network_connected () @@ -204,7 +216,7 @@ while (list) { PurpleAccount *account = (PurpleAccount*)list->data; - g_hash_table_remove(hash, account); + g_hash_table_remove(auto_reconns, account); if (purple_account_is_disconnected(account)) do_signon(account); list = list->next; @@ -265,7 +277,7 @@ static void account_removed_cb(PurpleAccount *account, gpointer user_data) { - g_hash_table_remove(hash, account); + g_hash_table_remove(auto_reconns, account); pidgin_blist_update_account_error_state(account, NULL); } @@ -286,7 +298,7 @@ void pidgin_connection_init(void) { - hash = g_hash_table_new_full( + auto_reconns = g_hash_table_new_full( g_direct_hash, g_direct_equal, NULL, free_auto_recon); @@ -300,5 +312,5 @@ { purple_signals_disconnect_by_handle(pidgin_connection_get_handle()); - g_hash_table_destroy(hash); + g_hash_table_destroy(auto_reconns); } diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkconv.c --- a/pidgin/gtkconv.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkconv.c Fri Sep 28 20:58:48 2007 +0000 @@ -70,6 +70,8 @@ #include "gtknickcolors.h" +#define CLOSE_CONV_TIMEOUT_SECS (10 * 60) + #define AUTO_RESPONSE "<AUTO-REPLY> : " typedef enum @@ -123,7 +125,6 @@ static GtkWidget *invite_dialog = NULL; static GtkWidget *warn_close_dialog = NULL; -static PidginWindow *hidden_convwin = NULL; static GList *window_list = NULL; /* Lists of status icons at all available sizes for use as window icons */ @@ -161,6 +162,7 @@ static gboolean infopane_press_cb(GtkWidget *widget, GdkEventButton *e, PidginConversation *conv); static gboolean pidgin_userlist_motion_cb (GtkWidget *w, GdkEventMotion *event, PidginConversation *gtkconv); static void pidgin_conv_leave_cb (GtkWidget *w, GdkEventCrossing *e, PidginConversation *gtkconv); +static void hide_conv(PidginConversation *gtkconv, gboolean closetimer); static void pidgin_conv_set_position_size(PidginWindow *win, int x, int y, int width, int height); @@ -208,12 +210,49 @@ **************************************************************************/ static gboolean -close_conv_cb(GtkWidget *w, GdkEventButton *event, PidginConversation *gtkconv) -{ +close_this_sucker(gpointer data) +{ + PidginConversation *gtkconv = data; GList *list = g_list_copy(gtkconv->convs); - g_list_foreach(list, (GFunc)purple_conversation_destroy, NULL); g_list_free(list); + return FALSE; +} + +static gboolean +close_conv_cb(GtkWidget *w, GdkEventButton *dontuse, PidginConversation *gtkconv) +{ + /* We are going to destroy the conversations immediately only if the 'close immediately' + * preference is selected. Otherwise, close the conversation after a reasonable timeout + * (I am going to consider 10 minutes as a 'reasonable timeout' here. + * For chats, close immediately if the chat is not in the buddylist, or if the chat is + * not marked 'Persistent' */ + PurpleConversation *conv = gtkconv->active_conv; + PurpleAccount *account = purple_conversation_get_account(conv); + const char *name = purple_conversation_get_name(conv); + + switch (purple_conversation_get_type(conv)) { + case PURPLE_CONV_TYPE_IM: + { + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately")) + close_this_sucker(gtkconv); + else + hide_conv(gtkconv, TRUE); + break; + } + case PURPLE_CONV_TYPE_CHAT: + { + PurpleChat *chat = purple_blist_find_chat(account, name); + if (!chat || + !purple_blist_node_get_bool(&chat->node, "gtk-persistent")) + close_this_sucker(gtkconv); + else + hide_conv(gtkconv, FALSE); + break; + } + default: + ; + } return TRUE; } @@ -337,10 +376,13 @@ static void clear_conversation_scrollback(PurpleConversation *conv) { PidginConversation *gtkconv = NULL; + GList *iter; gtkconv = PIDGIN_CONVERSATION(conv); gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml)); + for (iter = gtkconv->convs; iter; iter = iter->next) + purple_conversation_clear_message_history(iter->data); } static PurpleCmdRet @@ -348,7 +390,6 @@ const char *cmd, char **args, char **error, void *data) { clear_conversation_scrollback(conv); - purple_conversation_clear_message_history(conv); return PURPLE_CMD_STATUS_OK; } @@ -1060,12 +1101,9 @@ { PidginWindow *win = data; PurpleConversation *conv; - PidginConversation *gtkconv; conv = pidgin_conv_window_get_active_conversation(win); - gtkconv = PIDGIN_CONVERSATION(conv); - - gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml)); + clear_conversation_scrollback(conv); } struct _search { @@ -1315,18 +1353,33 @@ add_remove_cb(NULL, PIDGIN_CONVERSATION(conv)); } -#if 0 -static void -menu_hide_conv_cb(gpointer data, guint action, GtkWidget *widget) -{ - PidginWindow *win = data; - PidginConversation *gtkconv = pidgin_conv_window_get_active_gtkconv(win); - PurpleConversation *conv = pidgin_conv_window_get_active_conversation(win); +static gboolean +close_already(gpointer data) +{ + purple_conversation_destroy(data); + return FALSE; +} + +static void +hide_conv(PidginConversation *gtkconv, gboolean closetimer) +{ + GList *list; + purple_signal_emit(pidgin_conversations_get_handle(), "conversation-hiding", gtkconv); - purple_conversation_set_ui_ops(conv, NULL); -} -#endif + + for (list = g_list_copy(gtkconv->convs); list; list = g_list_delete_link(list, list)) { + PurpleConversation *conv = list->data; + if (closetimer) { + guint timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer")); + if (timer) + purple_timeout_remove(timer); + timer = purple_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv); + purple_conversation_set_data(conv, "close-timer", GINT_TO_POINTER(timer)); + } + purple_conversation_set_ui_ops(conv, NULL); + } +} static void menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget) @@ -2343,63 +2396,69 @@ return get_prpl_icon_list(account); } -GdkPixbuf * -pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon) -{ - PurpleAccount *account = NULL; - const char *name = NULL; - GdkPixbuf *status = NULL; - PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); - const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL; - g_return_val_if_fail(conv != NULL, NULL); - - account = purple_conversation_get_account(conv); - name = purple_conversation_get_name(conv); - - g_return_val_if_fail(account != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - /* Use the buddy icon, if possible */ - if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - PurpleBuddy *b = purple_find_buddy(account, name); - if (b != NULL) { +static GdkPixbuf * +pidgin_conv_get_icon(PurpleConversation *conv, GtkWidget *parent, const char *icon_size) +{ + PurpleAccount *account = NULL; + const char *name = NULL; + GdkPixbuf *status = NULL; + PurpleBlistUiOps *ops = purple_blist_get_ui_ops(); + g_return_val_if_fail(conv != NULL, NULL); + + account = purple_conversation_get_account(conv); + name = purple_conversation_get_name(conv); + + g_return_val_if_fail(account != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + /* Use the buddy icon, if possible */ + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + PurpleBuddy *b = purple_find_buddy(account, name); + if (b != NULL) { PurplePresence *p = purple_buddy_get_presence(b); - /* I hate this hack. It fixes a bug where the pending message icon - * displays in the conv tab even though it shouldn't. - * A better solution would be great. */ - if (ops && ops->update) - ops->update(NULL, (PurpleBlistNode*)b); + /* I hate this hack. It fixes a bug where the pending message icon + * displays in the conv tab even though it shouldn't. + * A better solution would be great. */ + if (ops && ops->update) + ops->update(NULL, (PurpleBlistNode*)b); /* XXX Seanegan: We really need a util function to return a pixbuf for a Presence to avoid all this switching */ if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AWAY)) - status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size); + status = pidgin_create_status_icon(PURPLE_STATUS_AWAY, parent, icon_size); else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_EXTENDED_AWAY)) - status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE)) - status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE)) - status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, PIDGIN_CONVERSATION(conv)->icon, icon_size); - } - } - - /* If they don't have a buddy icon, then use the PRPL icon */ - if (status == NULL) { + status = pidgin_create_status_icon(PURPLE_STATUS_EXTENDED_AWAY, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_OFFLINE)) + status = pidgin_create_status_icon(PURPLE_STATUS_OFFLINE, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_AVAILABLE)) + status = pidgin_create_status_icon(PURPLE_STATUS_AVAILABLE, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_INVISIBLE)) + status = pidgin_create_status_icon(PURPLE_STATUS_INVISIBLE, parent, icon_size); + else if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_UNAVAILABLE)) + status = pidgin_create_status_icon(PURPLE_STATUS_UNAVAILABLE, parent, icon_size); + } + } + + /* If they don't have a buddy icon, then use the PRPL icon */ + if (status == NULL) { GtkIconSize size = gtk_icon_size_from_name(icon_size); if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { - status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_PERSON, - size, "GtkWidget"); + status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_PERSON, + size, "GtkWidget"); } else { - status = gtk_widget_render_icon (PIDGIN_CONVERSATION(conv)->icon, PIDGIN_STOCK_STATUS_CHAT, - size, "GtkWidget"); + status = gtk_widget_render_icon (parent, PIDGIN_STOCK_STATUS_CHAT, + size, "GtkWidget"); } } return status; } +GdkPixbuf * +pidgin_conv_get_tab_icon(PurpleConversation *conv, gboolean small_icon) +{ + const char *icon_size = small_icon ? PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC : PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL; + return pidgin_conv_get_icon(conv, PIDGIN_CONVERSATION(conv)->icon, icon_size); +} + static void update_tab_icon(PurpleConversation *conv) @@ -2751,9 +2810,9 @@ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); GdkModifierType state; - if(gtkconv->win==hidden_convwin) { - pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); - pidgin_conv_placement_place(gtkconv); + if (gtkconv == NULL) { + pidgin_conv_attach_to_conversation(conv); + gtkconv = PIDGIN_CONVERSATION(conv); } pidgin_conv_switch_active_conversation(conv); @@ -2786,15 +2845,19 @@ PurpleConversation *conv = (PurpleConversation*)l->data; PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); - if(gtkconv == NULL || gtkconv->active_conv != conv) + if (gtkconv != NULL && gtkconv->active_conv != conv) continue; - - if (gtkconv->unseen_state >= min_state - && (!hidden_only || - (hidden_only && gtkconv->win == hidden_convwin))) { - + if (gtkconv == NULL) { + if (!hidden_only || + !purple_conversation_get_data(conv, "unseen-count")) + continue; r = g_list_prepend(r, conv); c++; + } else { + if (gtkconv->unseen_state >= min_state && !hidden_only) { + r = g_list_prepend(r, conv); + c++; + } } } @@ -2834,11 +2897,11 @@ PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); GtkWidget *icon = gtk_image_new(); - GdkPixbuf *pbuf = pidgin_conv_get_tab_icon(conv, TRUE); + GdkPixbuf *pbuf = pidgin_conv_get_icon(conv, icon, PIDGIN_ICON_SIZE_TANGO_MICROSCOPIC); GtkWidget *item; gchar *text = g_strdup_printf("%s (%d)", - gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)), - gtkconv->unseen_count); + gtkconv ? gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)) : purple_conversation_get_name(conv), + gtkconv ? gtkconv->unseen_count : GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count"))); gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf); g_object_unref(pbuf); @@ -3096,7 +3159,7 @@ PurpleConversation *conv; GtkWidget *item; - if (win->window == NULL || win == hidden_convwin) + if (win->window == NULL) return; gtkconv = pidgin_conv_window_get_active_gtkconv(win); @@ -3571,7 +3634,7 @@ gtk_widget_destroy(win->menu.send_to); /* Build the Send To menu */ - win->menu.send_to = gtk_menu_item_new_with_mnemonic(_("_Send To")); + win->menu.send_to = gtk_menu_item_new_with_mnemonic(_("S_end To")); gtk_widget_show(win->menu.send_to); menu = gtk_menu_new(); @@ -4943,6 +5006,9 @@ GtkWidget *tab_cont; PurpleBlistNode *convnode; + if (hidden) + return; + if (conv_type == PURPLE_CONV_TYPE_IM && (gtkconv = pidgin_conv_find_gtkconv(conv))) { conv->ui_data = gtkconv; if (!g_list_find(gtkconv->convs, conv)) @@ -5042,10 +5108,7 @@ G_CALLBACK(gtk_widget_grab_focus), gtkconv->entry); - if (hidden) - pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv); - else - pidgin_conv_placement_place(gtkconv); + pidgin_conv_placement_place(gtkconv); if (nick_colors == NULL) { nbr_nick_colors = NUM_NICK_COLORS; @@ -5053,11 +5116,13 @@ } } +#if 0 static void pidgin_conv_new_hidden(PurpleConversation *conv) { private_gtkconv_new(conv, TRUE); } +#endif void pidgin_conv_new(PurpleConversation *conv) @@ -5070,26 +5135,22 @@ PurpleConversation *conv, PurpleMessageFlags flags) { PurpleConversationUiOps *ui_ops = pidgin_conversations_get_conv_ui_ops(); - if (conv != NULL) - return; /* create hidden conv if hide_new pref is always */ - if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) - { - ui_ops->create_conversation = pidgin_conv_new_hidden; - purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); - ui_ops->create_conversation = pidgin_conv_new; - return; - } - - /* create hidden conv if hide_new pref is away and account is away */ - if (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && - !purple_status_is_available(purple_account_get_active_status(account))) - { - ui_ops->create_conversation = pidgin_conv_new_hidden; - purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); - ui_ops->create_conversation = pidgin_conv_new; - return; + /* or if hide_new pref is away and account is away */ + if ((strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always") == 0) || + (strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away") == 0 && + !purple_status_is_available(purple_account_get_active_status(account)))) { + if (!conv) { + ui_ops->create_conversation = NULL; + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sender); + purple_conversation_set_ui_ops(conv, NULL); + ui_ops->create_conversation = pidgin_conv_new; + } + } else { + /* new message for an IM */ + if (conv && conv->type == PURPLE_CONV_TYPE_IM) + pidgin_conv_attach_to_conversation(conv); } } @@ -5098,6 +5159,9 @@ { PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + if (!gtkconv) + return; + gtkconv->convs = g_list_remove(gtkconv->convs, conv); /* Don't destroy ourselves until all our convos are gone */ if (gtkconv->convs) { @@ -6575,6 +6639,19 @@ pidgin_conv_update_fields(conv, flags); } +static void +wrote_msg_update_unseen_cb(PurpleAccount *account, const char *who, const char *message, + PurpleConversation *conv, PurpleMessageFlags flag, gpointer null) +{ + if (conv == NULL || PIDGIN_IS_PIDGIN_CONVERSATION(conv)) + return; + if (flag & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)) { + purple_conversation_set_data(conv, "unseen-count", + GINT_TO_POINTER(GPOINTER_TO_INT(purple_conversation_get_data(conv, "unseen-count")) + 1)); + purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN); + } +} + static PurpleConversationUiOps conversation_ui_ops = { pidgin_conv_new, @@ -7072,6 +7149,7 @@ account_status_changed_cb(PurpleAccount *account, PurpleStatus *oldstatus, PurpleStatus *newstatus) { +#if 0 GList *l; PurpleConversation *conv = NULL; PidginConversation *gtkconv; @@ -7081,27 +7159,7 @@ if(purple_status_is_available(oldstatus) || !purple_status_is_available(newstatus)) return; - - while ((l = hidden_convwin->gtkconvs) != NULL) - { - gtkconv = l->data; - - conv = gtkconv->active_conv; - - while(l && !purple_status_is_available( - purple_account_get_active_status( - purple_conversation_get_account(conv)))) - l = l->next; - if (!l) - break; - - pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); - pidgin_conv_placement_place(gtkconv); - - /* TODO: do we need to do anything for any other conversations that are in the same gtkconv here? - * I'm a little concerned that not doing so will cause the "pending" indicator in the gtkblist not to be cleared. -DAA*/ - purple_conversation_update(conv, PURPLE_CONV_UPDATE_UNSEEN); - } +#endif } static void @@ -7109,32 +7167,25 @@ gconstpointer value, gpointer data) { GList *l; - PurpleConversation *conv = NULL; - PidginConversation *gtkconv; gboolean when_away = FALSE; - if(!hidden_convwin) - return; - if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "always")==0) return; if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new"), "away")==0) when_away = TRUE; - while ((l = hidden_convwin->gtkconvs) != NULL) + for (l = purple_get_conversations(); l; l = l->next) { - gtkconv = l->data; - - conv = gtkconv->active_conv; - + PurpleConversation *conv = l->data; + PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv); + if (gtkconv) + continue; if(when_away && !purple_status_is_available( purple_account_get_active_status( purple_conversation_get_account(conv)))) continue; - - pidgin_conv_window_remove_gtkconv(hidden_convwin, gtkconv); - pidgin_conv_placement_place(gtkconv); + pidgin_conv_attach_to_conversation(conv); } } @@ -7190,6 +7241,23 @@ /* if (purple_conversation_get_account(conv) == account) */ pidgin_conv_update_fields(conv, PIDGIN_CONV_TAB_ICON | PIDGIN_CONV_MENU | PIDGIN_CONV_COLORIZE_TITLE); + + if (PURPLE_CONNECTION_IS_CONNECTED(gc) && + conv->type == PURPLE_CONV_TYPE_CHAT && + conv->account == gc->account && + purple_conversation_get_data(conv, "want-to-rejoin")) { + GHashTable *comps = NULL; + PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name); + 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); + } else { + comps = chat->components; + } + serv_join_chat(gc, comps); + if (chat == NULL && comps != NULL) + g_hash_table_destroy(comps); + } } } @@ -7317,9 +7385,15 @@ PidginConversation *gtkconv = data; int count = 0; int timer = gtkconv->attach.timer; + time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->entry), "attach-start-time")); + gtkconv->attach.timer = 0; while (gtkconv->attach.current && count < 100) { /* XXX: 100 is a random value here */ PurpleConvMessage *msg = gtkconv->attach.current->data; + if (when && when < msg->when) { + gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "

", 0); + g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); + } pidgin_conv_write_conv(gtkconv->active_conv, msg->who, msg->who, msg->what, msg->flags, msg->when); gtkconv->attach.current = gtkconv->attach.current->prev; count++; @@ -7328,6 +7402,7 @@ if (gtkconv->attach.current) return TRUE; + g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL); purple_signal_emit(pidgin_conversations_get_handle(), "conversation-displayed", gtkconv); g_source_remove(gtkconv->attach.timer); @@ -7339,18 +7414,21 @@ { GList *list; PidginConversation *gtkconv; + int timer; if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) return FALSE; + purple_conversation_set_data(conv, "unseen-count", NULL); purple_conversation_set_ui_ops(conv, pidgin_conversations_get_conv_ui_ops()); private_gtkconv_new(conv, FALSE); gtkconv = PIDGIN_CONVERSATION(conv); list = purple_conversation_get_message_history(conv); if (list) { - list = g_list_last(list); - gtkconv->attach.current = list; + g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", + GINT_TO_POINTER(((PurpleConvMessage*)(list->data))->when)); + gtkconv->attach.current = g_list_last(list); gtkconv->attach.timer = g_idle_add(add_message_history_to_gtkconv, gtkconv); } else { purple_signal_emit(pidgin_conversations_get_handle(), @@ -7362,6 +7440,9 @@ pidgin_conv_chat_add_users(conv, PURPLE_CONV_CHAT(conv)->in_room, TRUE); } + timer = GPOINTER_TO_INT(purple_conversation_get_data(conv, "close-timer")); + if (timer) + purple_timeout_remove(timer); return TRUE; } @@ -7427,6 +7508,7 @@ purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", TRUE); purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/im/hide_new", "never"); + purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", FALSE); #ifdef _WIN32 purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/win32/minimize_new_convs", FALSE); @@ -7590,9 +7672,6 @@ purple_conversations_set_ui_ops(&conversation_ui_ops); - hidden_convwin = pidgin_conv_window_new(); - window_list = g_list_remove(window_list, hidden_convwin); - purple_signal_connect(purple_accounts_get_handle(), "account-status-changed", handle, PURPLE_CALLBACK(account_status_changed_cb), NULL); @@ -7628,6 +7707,10 @@ purple_signal_connect_priority(purple_conversations_get_handle(), "conversation-updated", handle, PURPLE_CALLBACK(pidgin_conv_updated), NULL, PURPLE_SIGNAL_PRIORITY_LOWEST); + purple_signal_connect(purple_conversations_get_handle(), "wrote-im-msg", handle, + PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); + purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle, + PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL); } void @@ -7636,8 +7719,6 @@ purple_prefs_disconnect_by_handle(pidgin_conversations_get_handle()); purple_signals_disconnect_by_handle(pidgin_conversations_get_handle()); purple_signals_unregister_by_instance(pidgin_conversations_get_handle()); - pidgin_conv_window_destroy(hidden_convwin); - hidden_convwin=NULL; } @@ -8019,7 +8100,7 @@ sub = gtk_menu_item_get_submenu(GTK_MENU_ITEM(gtkconv->win->menu.send_to)); if (sub && GTK_WIDGET_IS_SENSITIVE(gtkconv->win->menu.send_to)) { - GtkWidget *item = gtk_menu_item_new_with_mnemonic(_("_Send To")); + GtkWidget *item = gtk_menu_item_new_with_mnemonic(_("S_end To")); if (populated) pidgin_separator(menu); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); @@ -8769,15 +8850,10 @@ if (win->gtkconvs) { while (win->gtkconvs) { - GList *nextgtk = win->gtkconvs->next; - PidginConversation *gtkconv = win->gtkconvs->data; - GList *nextcore = gtkconv->convs->next; - PurpleConversation *conv = gtkconv->convs->data; - purple_conversation_destroy(conv); - if (!nextgtk && !nextcore) - /* we'll end up invoking ourselves when we destroy our last child */ - /* so don't destroy ourselves right now */ - return; + gboolean last = (win->gtkconvs->next == NULL); + close_conv_cb(NULL, NULL, win->gtkconvs->data); + if (last) + break; } return; } @@ -9054,7 +9130,7 @@ if (win->gtkconvs && win->gtkconvs->next == NULL) pidgin_conv_tab_pack(win, win->gtkconvs->data); - if (!win->gtkconvs && win != hidden_convwin) + if (!win->gtkconvs) pidgin_conv_window_destroy(win); } @@ -9593,9 +9669,7 @@ gboolean pidgin_conv_is_hidden(PidginConversation *gtkconv) { - g_return_val_if_fail(gtkconv != NULL, FALSE); - - return (gtkconv->win == hidden_convwin); + return (gtkconv == NULL); } @@ -9696,3 +9770,4 @@ return colors; } + diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkdocklet.c --- a/pidgin/gtkdocklet.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkdocklet.c Fri Sep 28 20:58:48 2007 +0000 @@ -532,7 +532,7 @@ g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_mute), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - menuitem = gtk_check_menu_item_new_with_label(_("Blink on new message")); + menuitem = gtk_check_menu_item_new_with_label(_("Blink on New Message")); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")); g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(docklet_toggle_blink), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkimhtmltoolbar.c --- a/pidgin/gtkimhtmltoolbar.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkimhtmltoolbar.c Fri Sep 28 20:58:48 2007 +0000 @@ -1166,6 +1166,7 @@ GtkWidget *label; GtkWidget *insert_button; GtkWidget *font_button; + GtkWidget *smiley_button; GtkWidget *font_menu; GtkWidget *insert_menu; GtkWidget *menuitem; @@ -1267,12 +1268,6 @@ insert_menu = gtk_menu_new(); g_object_set_data(G_OBJECT(toolbar), "insert_menu", insert_menu); - menuitem = gtk_menu_item_new_with_mnemonic(_("_Smiley")); - g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->smiley); - gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem); - g_signal_connect(G_OBJECT(toolbar->smiley), "notify::sensitive", - G_CALLBACK(button_sensitiveness_changed), menuitem); - menuitem = gtk_menu_item_new_with_mnemonic(_("_Image")); g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->image); gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem); @@ -1294,6 +1289,24 @@ g_signal_connect(G_OBJECT(insert_button), "activate", G_CALLBACK(pidgin_menu_clicked), insert_menu); g_signal_connect(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate), insert_button); toolbar->sml = NULL; + + /* Sep */ + sep = gtk_vseparator_new(); + gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0); + gtk_widget_show_all(sep); + + /* Smiley */ + smiley_button = gtk_button_new(); + gtk_button_set_relief(GTK_BUTTON(smiley_button), GTK_RELIEF_NONE); + bbox = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(smiley_button), bbox); + image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL)); + gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0); + label = gtk_label_new_with_mnemonic(_("_Smile!")); + gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(box), smiley_button, FALSE, FALSE, 0); + g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley); + gtk_widget_show_all(smiley_button); gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0); g_object_set_data(G_OBJECT(hbox), "lean-view", box); diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkmain.c Fri Sep 28 20:58:48 2007 +0000 @@ -394,6 +394,9 @@ " -n, --nologin don't automatically login\n" " -l, --login[=NAME] automatically login (optional argument NAME specifies\n" " account(s) to use, separated by commas)\n" +#ifndef WIN32 + " --display=DISPLAY X display to use\n" +#endif " -v, --version display the current version and exit\n"), PIDGIN_NAME, VERSION, name); } @@ -481,6 +484,7 @@ {"nologin", no_argument, NULL, 'n'}, {"session", required_argument, NULL, 's'}, {"version", no_argument, NULL, 'v'}, + {"display", required_argument, NULL, 'D'}, {0, 0, 0, 0} }; @@ -509,7 +513,7 @@ #ifndef DEBUG /* We translate this here in case the crash breaks gettext. */ segfault_message_tmp = g_strdup_printf(_( - "%s has segfaulted and attempted to dump a core file.\n" + "%s %s has segfaulted and attempted to dump a core file.\n" "This is a bug in the software and has happened through\n" "no fault of your own.\n\n" "If you can reproduce the crash, please notify the developers\n" @@ -523,7 +527,7 @@ "LSchiere (via AIM). Contact information for Sean and Luke \n" "on other protocols is at\n" "%swiki/DeveloperPages\n"), - PIDGIN_NAME, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE + PIDGIN_NAME, VERSION, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE ); /* we have to convert the message (UTF-8 to console @@ -626,6 +630,9 @@ case 'm': /* do not ensure single instance. */ opt_si = FALSE; break; + case 'D': /* --display */ + /* handled by gtk_init_check below */ + break; case '?': /* show terse help */ default: show_usage(argv[0], TRUE); diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtknotify.c --- a/pidgin/gtknotify.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtknotify.c Fri Sep 28 20:58:48 2007 +0000 @@ -561,8 +561,8 @@ if (!GTK_WIDGET_VISIBLE(dialog)) { GdkPixbuf *pixbuf = gtk_widget_render_icon(dialog, PIDGIN_STOCK_DIALOG_MAIL, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL), NULL); - char *label_text = g_strdup_printf(ngettext("You have %d new e-mail.", - "You have %d new e-mails.", + char *label_text = g_strdup_printf(ngettext("%d new e-mail.", + "%d new e-mails.", mail_dialog->total_count), mail_dialog->total_count); mail_dialog->in_use = TRUE; /* So that _set_headline doesn't accidentally remove the notifications when replacing an @@ -692,30 +692,30 @@ GtkTreeIter iter; GdkPixbuf *pixbuf; guint col_num; - guint i; - guint j; + GList *row, *column; + guint n; gtk_list_store_clear(data->model); pixbuf = pidgin_create_prpl_icon(purple_connection_get_account(gc), 0.5); /* +1 is for the automagically created Status column. */ - col_num = purple_notify_searchresults_get_columns_count(results) + 1; + col_num = g_list_length(results->columns) + 1; - for (i = 0; i < purple_notify_searchresults_get_rows_count(results); i++) { - GList *row = purple_notify_searchresults_row_get(results, i); + for (row = results->rows; row != NULL; row = row->next) { gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, 0, pixbuf, -1); - for (j = 1; j < col_num; j++) { + n = 1; + for (column = row->data; column != NULL; column = column->next) { GValue v; - char *data = g_list_nth_data(row, j - 1); v.g_type = 0; g_value_init(&v, G_TYPE_STRING); - g_value_set_string(&v, data); - gtk_list_store_set_value(model, &iter, j, &v); + g_value_set_string(&v, column->data); + gtk_list_store_set_value(model, &iter, n, &v); + n++; } } @@ -735,6 +735,7 @@ GtkListStore *model; GtkCellRenderer *renderer; guint col_num; + GList *column; guint i; GtkWidget *vbox; @@ -782,7 +783,7 @@ g_free(label_text); /* +1 is for the automagically created Status column. */ - col_num = purple_notify_searchresults_get_columns_count(results) + 1; + col_num = g_list_length(results->columns) + 1; /* Setup the list model */ col_types = g_new0(GType, col_num); @@ -817,12 +818,13 @@ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, "", renderer, "pixbuf", 0, NULL); - for (i = 1; i < col_num; i++) { + i = 1; + for (column = results->columns; column != NULL; column = column->next) { renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, - purple_notify_searchresults_column_get_title(results, i-1), - renderer, "text", i, NULL); + column->data, renderer, "text", i, NULL); + i++; } for (i = 0; i < g_list_length(results->buttons); i++) { diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtkprefs.c Fri Sep 28 20:58:48 2007 +0000 @@ -994,6 +994,8 @@ pidgin_prefs_checkbox(_("Show _formatting on incoming messages"), PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox); + pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"), + PIDGIN_PREFS_ROOT "/conversations/im/close_immediately", vbox); iconpref1 = pidgin_prefs_checkbox(_("Show _detailed information"), PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons", vbox); diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/gtksession.c --- a/pidgin/gtksession.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/gtksession.c Fri Sep 28 20:58:48 2007 +0000 @@ -36,6 +36,7 @@ #include #include #include +#include #define ERROR_LENGTH 512 @@ -141,7 +142,7 @@ /* my magic utility function */ static gchar **session_make_command(gchar *client_id, gchar *config_dir) { - gint i = 2; + gint i = 4; gint j = 0; gchar **ret; @@ -161,6 +162,9 @@ ret[j++] = g_strdup(config_dir); } + ret[j++] = g_strdup("--display"); + ret[j++] = g_strdup((gchar *)gdk_display_get_name(gdk_display_get_default())); + ret[j++] = NULL; return ret; diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/cool.png Binary file pidgin/pixmaps/dialogs/64/cool.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/error.png Binary file pidgin/pixmaps/dialogs/64/error.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/info.png Binary file pidgin/pixmaps/dialogs/64/info.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/mail.png Binary file pidgin/pixmaps/dialogs/64/mail.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/question.png Binary file pidgin/pixmaps/dialogs/64/question.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/scalable/cool.svg --- a/pidgin/pixmaps/dialogs/64/scalable/cool.svg Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/dialogs/64/scalable/cool.svg Fri Sep 28 20:58:48 2007 +0000 @@ -7,101 +7,25 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="64" - height="64" + width="48" + height="48" id="svg2" sodipodi:version="0.32" - inkscape:version="0.43" + inkscape:version="0.45" version="1.0" - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/dialogs" + sodipodi:docbase="/home/hbons/Desktop/2.1.1/dialogs" sodipodi:docname="dialog-cool.svg" - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/dialogs/dialog-cool.png" + inkscape:export-filename="/home/hbons/Desktop/newstyle.png" inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fill="#fce94f" + inkscape:window-width="1440" + inkscape:window-height="847" + inkscape:window-x="3" + inkscape:window-y="25" + width="48px" + height="48px" /> @@ -167,25 +458,25 @@ inkscape:groupmode="layer" id="layer1"> + style="opacity:0.64044949;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3405)" + id="path3140" + sodipodi:cx="10.748654" + sodipodi:cy="10.457643" + sodipodi:rx="6.6449099" + sodipodi:ry="2.3675451" + d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z" + transform="matrix(2.2723916,0,0,1.6905173,-0.1758194,23.510748)" /> + transform="matrix(1.8043406,0,0,1.8043406,3.1976696,7.6828846)" /> + transform="matrix(1.6958412,0,0,1.6958412,4.3182736,9.0348912)" /> + transform="matrix(1.7042169,0,0,1.7042169,4.3797455,8.7825453)" /> + + + + + + + + + + + + transform="matrix(1.712345,0,0,2.754643,-71.507978,-24.75469)" /> + transform="matrix(1.712345,0,0,2.754643,-63.507982,-24.75469)" /> - + d="M 44.125,10.5 C 35.308909,10.855387 28.5,16.639515 28.5,23.40625 C 28.5,27.397567 30.147439,31.550115 33.625,33.875 C 33.835747,34.023343 33.981239,34.247178 34.03125,34.5 C 34.490803,36.524599 34.051533,38.324325 33.46875,39.65625 C 33.930328,39.493414 34.045022,39.492531 34.5625,39.28125 C 36.305088,38.56977 38.200088,37.553862 39.0625,36.59375 C 39.244281,36.384758 39.504456,36.260327 39.78125,36.25 C 42.026592,36.154804 43.292249,36.3125 45,36.3125 C 54.22098,36.312499 61.500002,30.39071 61.5,23.40625 C 61.5,16.421791 54.220979,10.5 45,10.5 C 44.671757,10.5 44.368389,10.490188 44.125,10.5 z " + transform="translate(-72.994465,-39.55632)" /> + d="M 44.125,10.5 C 35.31263,10.855237 28.5,16.645389 28.5,23.40625 C 28.5,27.395135 30.150413,31.552103 33.625,33.875 C 33.846858,34.018204 34.003884,34.242526 34.0625,34.5 C 34.519684,36.514161 34.077673,38.294264 33.5,39.625 C 33.948389,39.466107 34.062909,39.485228 34.5625,39.28125 C 36.304559,38.569986 38.202041,37.520438 39.0625,36.5625 C 39.25032,36.365929 39.509394,36.253288 39.78125,36.25 C 42.027154,36.154781 43.29277,36.3125 45,36.3125 C 54.217148,36.312499 61.500002,30.384642 61.5,23.40625 C 61.5,16.427859 54.217147,10.5 45,10.5 C 44.671521,10.5 44.367905,10.490207 44.125,10.5 z " + transform="translate(-72.994465,-39.55632)" /> + transform="matrix(0.856174,0,0,0.688661,-39.751235,-16.60591)" /> + transform="matrix(0.856172,0,0,0.688662,-35.751215,-16.60592)" /> + transform="matrix(0.856173,0,0,0.688662,-31.751225,-16.60593)" /> + + diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/scalable/error.svg --- a/pidgin/pixmaps/dialogs/64/scalable/error.svg Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/dialogs/64/scalable/error.svg Fri Sep 28 20:58:48 2007 +0000 @@ -7,101 +7,25 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="64" - height="64" + width="48" + height="48" id="svg2" sodipodi:version="0.32" - inkscape:version="0.43" + inkscape:version="0.45" version="1.0" - sodipodi:docbase="/home/hbons/Desktop" - sodipodi:docname="dialog-error.svg" - inkscape:export-filename="/home/hbons/Desktop/dialog-warning.png" + sodipodi:docbase="/home/hbons/Desktop/2.1.1/dialogs" + sodipodi:docname="dialog--error.svg" + inkscape:export-filename="/home/hbons/Desktop/newstyle.png" inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + id="radialGradient3444" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + + + + + + + + + + + + + fill="#fce94f" + inkscape:window-width="1440" + inkscape:window-height="847" + inkscape:window-x="3" + inkscape:window-y="25" + width="48px" + height="48px" /> @@ -167,25 +540,25 @@ inkscape:groupmode="layer" id="layer1"> + style="opacity:0.64044949;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3405)" + id="path3140" + sodipodi:cx="10.748654" + sodipodi:cy="10.457643" + sodipodi:rx="6.6449099" + sodipodi:ry="2.3675451" + d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z" + transform="matrix(2.2723916,0,0,1.6905173,-0.1758194,23.510748)" /> + transform="matrix(1.8043406,0,0,1.8043406,3.1976696,7.6828846)" /> + transform="matrix(1.6958412,0,0,1.6958412,4.3182736,9.0348912)" /> - - - + transform="matrix(1.7042169,0,0,1.7042169,4.3797455,8.7825453)" /> + style="opacity:0.64044949;fill:none;fill-opacity:1;stroke:#f57900;stroke-width:0.3774938;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2259" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(3.5277688,0.705265,-0.6665314,1.855957,7.2901885,-4.1630498)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> + style="opacity:0.64044949;fill:none;fill-opacity:1;stroke:#f57900;stroke-width:0.3774938;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2261" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(-3.5277688,0.705265,0.6665314,1.855957,41.208475,-4.1630498)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> - + d="M 17,21 L 17,22 L 18,22 L 18,21 L 17,21 z M 18,22 L 18,23 L 19,23 L 19,22 L 18,22 z M 19,23 L 19,24 L 20,24 L 20,23 L 19,23 z M 20,23 L 21,23 L 21,22 L 20,22 L 20,23 z M 21,22 L 22,22 L 22,21 L 21,21 L 21,22 z M 20,24 L 20,25 L 21,25 L 21,24 L 20,24 z M 21,25 L 21,26 L 22,26 L 22,25 L 21,25 z M 19,24 L 18,24 L 18,25 L 19,25 L 19,24 z M 18,25 L 17,25 L 17,26 L 18,26 L 18,25 z " + id="rect3307" /> + style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 27,21 L 27,22 L 28,22 L 28,21 L 27,21 z M 28,22 L 28,23 L 29,23 L 29,22 L 28,22 z M 29,23 L 29,24 L 30,24 L 30,23 L 29,23 z M 30,23 L 31,23 L 31,22 L 30,22 L 30,23 z M 31,22 L 32,22 L 32,21 L 31,21 L 31,22 z M 30,24 L 30,25 L 31,25 L 31,24 L 30,24 z M 31,25 L 31,26 L 32,26 L 32,25 L 31,25 z M 29,24 L 28,24 L 28,25 L 29,25 L 29,24 z M 28,25 L 27,25 L 27,26 L 28,26 L 28,25 z " + id="path3326" /> - + style="fill:#ce5c00;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 34,39 C 32.885686,35.003412 29.067255,32 24.5,32 C 19.932746,32 16.114315,35.003412 15,39 C 17.00212,36.412082 20.518441,33.850868 24.5,33.850868 C 28.481556,33.850866 31.99788,36.412082 34,39 z " + id="path3492" + sodipodi:nodetypes="cscsc" /> diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/scalable/info.svg --- a/pidgin/pixmaps/dialogs/64/scalable/info.svg Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/dialogs/64/scalable/info.svg Fri Sep 28 20:58:48 2007 +0000 @@ -7,147 +7,25 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="64" - height="64" + width="48" + height="48" id="svg2" sodipodi:version="0.32" - inkscape:version="0.43" + inkscape:version="0.45" version="1.0" sodipodi:docbase="/home/hbons/Desktop" - sodipodi:docname="dialog-info.svg" - inkscape:export-filename="/home/hbons/Desktop/dialog-info.png" + sodipodi:docname="dialog.svg" + inkscape:export-filename="/home/hbons/Desktop/newstyle.png" inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + id="radialGradient2259" + xlink:href="#linearGradient2382" + inkscape:collect="always" /> + + + + + + + + + + + + + + + + + + + + fill="#fce94f" + inkscape:window-width="1440" + inkscape:window-height="845" + inkscape:window-x="3" + inkscape:window-y="25" + width="48px" + height="48px" /> @@ -213,25 +337,25 @@ inkscape:groupmode="layer" id="layer1"> + style="opacity:0.64044949;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3405)" + id="path3140" + sodipodi:cx="10.748654" + sodipodi:cy="10.457643" + sodipodi:rx="6.6449099" + sodipodi:ry="2.3675451" + d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z" + transform="matrix(2.2723916,0,0,1.6905173,-0.1758194,23.510748)" /> + transform="matrix(1.8043406,0,0,1.8043406,3.1976696,7.6828846)" /> + transform="matrix(1.6958412,0,0,1.6958412,4.3182736,9.0348912)" /> + transform="matrix(1.7042169,0,0,1.7042169,4.3797455,8.7825453)" /> + + + + + style="opacity:0.64044949;fill:none;fill-opacity:1;stroke:#f57900;stroke-width:0.3774938;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2259" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(3.5277688,0.705265,-0.6665314,1.855957,7.2901885,-4.1630498)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> - - - - - - - - - - - - - - - + style="opacity:0.64044949;fill:none;fill-opacity:1;stroke:#f57900;stroke-width:0.3774938;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2261" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(-3.5277688,0.705265,0.6665314,1.855957,41.208475,-4.1630498)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/scalable/question.svg --- a/pidgin/pixmaps/dialogs/64/scalable/question.svg Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/dialogs/64/scalable/question.svg Fri Sep 28 20:58:48 2007 +0000 @@ -7,57 +7,25 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="64" - height="64" + width="48" + height="48" id="svg2" sodipodi:version="0.32" - inkscape:version="0.43" + inkscape:version="0.45" version="1.0" - sodipodi:docbase="/home/hbons/Desktop" + sodipodi:docbase="/home/hbons/Desktop/2.1.1/dialogs/scalable" sodipodi:docname="dialog-question.svg" - inkscape:export-filename="/home/hbons/Desktop/dialog-question.png" + inkscape:export-filename="/home/hbons/Desktop/newstyle.png" inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + id="radialGradient2259" + xlink:href="#linearGradient2382" + inkscape:collect="always" /> + + + + + + + + + + + + + + + + + + + + fill="#fce94f" + inkscape:window-width="1440" + inkscape:window-height="847" + inkscape:window-x="3" + inkscape:window-y="25" + width="48px" + height="48px" /> @@ -123,25 +337,25 @@ inkscape:groupmode="layer" id="layer1"> + style="opacity:0.64044949;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3405)" + id="path3140" + sodipodi:cx="10.748654" + sodipodi:cy="10.457643" + sodipodi:rx="6.6449099" + sodipodi:ry="2.3675451" + d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z" + transform="matrix(2.2723916,0,0,1.6905173,-4.1758193,23.510748)" /> + transform="matrix(1.8043406,0,0,1.8043406,-0.8023303,7.6828845)" /> + transform="matrix(1.6958412,0,0,1.6958412,0.3182737,9.0348911)" /> + transform="matrix(1.7042169,0,0,1.7042169,0.3797456,8.7825452)" /> + + style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.98640186;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2172" + sodipodi:cx="9.7069349" + sodipodi:cy="9.6526775" + sodipodi:rx="1.0259361" + sodipodi:ry="1.9413869" + d="M 10.732871 9.6526775 A 1.0259361 1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361 1.9413869 0 1 1 10.732871 9.6526775 z" + transform="matrix(1.9494387,0,0,1.2877392,-1.9230749,13.069869)" /> + + style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.98640186;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3148" + sodipodi:cx="9.7069349" + sodipodi:cy="9.6526775" + sodipodi:rx="1.0259361" + sodipodi:ry="1.9413869" + d="M 10.732871 9.6526775 A 1.0259361 1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361 1.9413869 0 1 1 10.732871 9.6526775 z" + transform="matrix(1.9494393,0,0,1.2877392,9.0769201,13.069869)" /> - + style="opacity:1;fill:#ce5c00;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="M 30,32 C 28.885686,35.996588 25.067255,39 20.5,39 C 15.932746,39 12.114315,35.996588 11,32 C 16.460075,38.82871 24.447577,38.177252 30,32 z " + id="path2186" + sodipodi:nodetypes="cscc" /> - ? + sodipodi:type="arc" + style="opacity:0.64044949;fill:none;fill-opacity:1;stroke:#f57900;stroke-width:0.3774938;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2259" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(3.5277688,0.705265,-0.6665314,1.855957,3.2901886,-4.1630499)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> + + + diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/scalable/warning.svg --- a/pidgin/pixmaps/dialogs/64/scalable/warning.svg Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/dialogs/64/scalable/warning.svg Fri Sep 28 20:58:48 2007 +0000 @@ -7,55 +7,35 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="64" - height="64" + width="48" + height="48" id="svg2" sodipodi:version="0.32" - inkscape:version="0.43" + inkscape:version="0.45" version="1.0" - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/dialogs" + sodipodi:docbase="/home/hbons/Desktop/2.1.1/dialogs" sodipodi:docname="dialog-warning.svg" - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/dialogs/dialog-warning.png" + inkscape:export-filename="/home/hbons/Desktop/newstyle.png" inkscape:export-xdpi="90" - inkscape:export-ydpi="90"> + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> - + id="linearGradient3179"> - - - + id="stop3181" /> + id="stop3183" /> - @@ -79,15 +59,268 @@ r="9.975256" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" /> + + + + + + + + + + + + + + + + + + + + + id="radialGradient2259" + xlink:href="#linearGradient2382" + inkscape:collect="always" /> + + + + + + + + + + + + + + + + + + + + + fill="#fce94f" + inkscape:window-width="1440" + inkscape:window-height="845" + inkscape:window-x="3" + inkscape:window-y="25" + width="48px" + height="48px" /> @@ -123,25 +358,25 @@ inkscape:groupmode="layer" id="layer1"> + style="opacity:0.64044949;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3405)" + id="path3140" + sodipodi:cx="10.748654" + sodipodi:cy="10.457643" + sodipodi:rx="6.6449099" + sodipodi:ry="2.3675451" + d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z" + transform="matrix(2.2723916,0,0,1.6905173,-0.1758194,23.510748)" /> + transform="matrix(1.8043406,0,0,1.8043406,3.1976696,7.6828846)" /> + transform="matrix(1.6958412,0,0,1.6958412,4.3182736,9.0348912)" /> + transform="matrix(1.7042169,0,0,1.7042169,4.3797455,8.7825453)" /> + style="fill:#eeeeec;fill-opacity:1;stroke:#fea523;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" + d="M 23.500001,23.5 C 23.500001,26.26 21.260002,28.500001 18.500001,28.500001 C 15.740001,28.500001 13.5,26.26 13.5,23.5 C 13.5,20.74 15.740001,18.5 18.500001,18.5 C 21.260002,18.5 23.500001,20.74 23.500001,23.5 z " + id="path3154" /> + sodipodi:type="arc" + style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.98640186;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2172" + sodipodi:cx="9.7069349" + sodipodi:cy="9.6526775" + sodipodi:rx="1.0259361" + sodipodi:ry="1.9413869" + d="M 10.732871 9.6526775 A 1.0259361 1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361 1.9413869 0 1 1 10.732871 9.6526775 z" + transform="matrix(1.9494387,0,0,1.2877392,1.0769249,12.069869)" /> + - ! + + style="opacity:0.64044949;fill:none;fill-opacity:1;stroke:#f57900;stroke-width:0.3774938;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2259" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(3.5277688,0.705265,-0.6665314,1.855957,7.2901885,-4.1630498)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> + + id="path3228" + sodipodi:cx="21.5625" + sodipodi:cy="33.892857" + sodipodi:rx="2.4553571" + sodipodi:ry="1.9642857" + d="M 24.017857 33.892857 A 2.4553571 1.9642857 0 1 1 19.107143,33.892857 A 2.4553571 1.9642857 0 1 1 24.017857 33.892857 z" + transform="matrix(3.0545454,0,0,2.2909093,-41.363636,-42.145461)" /> + style="opacity:1;fill:url(#linearGradient3185);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2206" + sodipodi:cx="21.5625" + sodipodi:cy="33.892857" + sodipodi:rx="2.4553571" + sodipodi:ry="1.9642857" + d="M 24.017857 33.892857 A 2.4553571 1.9642857 0 1 1 19.107143,33.892857 A 2.4553571 1.9642857 0 1 1 24.017857 33.892857 z" + transform="matrix(-3.0545454,0,0,2.2909093,90.363634,-42.145461)" /> diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/dialogs/64/warning.png Binary file pidgin/pixmaps/dialogs/64/warning.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/emblems/16/Makefile.am --- a/pidgin/pixmaps/emblems/16/Makefile.am Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/emblems/16/Makefile.am Fri Sep 28 20:58:48 2007 +0000 @@ -1,6 +1,7 @@ SUBDIRS = scalable EXTRA_DIST = aol-client.png \ + birthday.png \ blocked.png \ bot.png \ external.png \ @@ -12,6 +13,7 @@ hiptop.png \ male.png \ mobile.png \ + music.png \ not-authorized.png \ operator.png \ qq-member.png \ diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/emblems/16/birthday.png Binary file pidgin/pixmaps/emblems/16/birthday.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/emblems/16/music.png Binary file pidgin/pixmaps/emblems/16/music.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/emblems/16/scalable/Makefile.am --- a/pidgin/pixmaps/emblems/16/scalable/Makefile.am Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/emblems/16/scalable/Makefile.am Fri Sep 28 20:58:48 2007 +0000 @@ -7,6 +7,7 @@ game.svg \ male.svg \ mobile.svg \ + music.svg \ not-authorized.svg \ qq-member.svg \ secure.svg \ diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/emblems/16/scalable/music.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pixmaps/emblems/16/scalable/music.svg Fri Sep 28 20:58:48 2007 +0000 @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/protocols/16/myspace.png Binary file pidgin/pixmaps/protocols/16/myspace.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/protocols/48/myspace.png Binary file pidgin/pixmaps/protocols/48/myspace.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/status/16/available.png Binary file pidgin/pixmaps/status/16/available.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/status/16/away.png Binary file pidgin/pixmaps/status/16/away.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/status/16/busy.png Binary file pidgin/pixmaps/status/16/busy.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/status/16/extended-away.png Binary file pidgin/pixmaps/status/16/extended-away.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/status/16/offline.png Binary file pidgin/pixmaps/status/16/offline.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/status/16/rtl/extended-away.png Binary file pidgin/pixmaps/status/16/rtl/extended-away.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/status/16/scalable/available.svg --- a/pidgin/pixmaps/status/16/scalable/available.svg Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/pixmaps/status/16/scalable/available.svg Fri Sep 28 20:58:48 2007 +0000 @@ -13,13 +13,15 @@ height="16" id="svg2" sodipodi:version="0.32" - inkscape:version="0.44.1" + inkscape:version="0.45" version="1.0" inkscape:export-filename="/home/hbons/GUI/Tango/Gaim Refresh/status/16/available.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" - sodipodi:docbase="/home/hbons/Desktop/experiment/status/16/scalable" - sodipodi:docname="available.svg"> + sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable" + sodipodi:docname="available.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> + inkscape:window-y="25" /> @@ -263,16 +265,6 @@ inkscape:groupmode="layer" id="layer1"> - + sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable" + sodipodi:docname="away.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> - + sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable" + sodipodi:docname="busy.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> + inkscape:window-x="3" + inkscape:window-y="25" /> @@ -210,16 +212,6 @@ inkscape:groupmode="layer" id="layer1"> - + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> + inkscape:window-width="1440" + inkscape:window-height="847" + inkscape:window-x="0" + inkscape:window-y="22" /> @@ -79,27 +81,17 @@ inkscape:label="Layer 1" inkscape:groupmode="layer"> - + sodipodi:nodetypes="ccccccc" + transform="matrix(-1.708738,0,0,1.647118,-1.878642,-0.867731)" /> + sodipodi:nodetypes="cccccc" + transform="matrix(-1.398059,0,0,1.342047,-8.253506e-2,0.574281)" /> + sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable" + sodipodi:docname="offline.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + sodipodi:modified="true"> + inkscape:window-x="3" + inkscape:window-y="25" /> @@ -186,16 +188,6 @@ inkscape:groupmode="layer" id="layer1"> - diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/toolbar/16/emote-select.png Binary file pidgin/pixmaps/toolbar/16/emote-select.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/toolbar/16/insert.png Binary file pidgin/pixmaps/toolbar/16/insert.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/pixmaps/toolbar/16/send-file.png Binary file pidgin/pixmaps/toolbar/16/send-file.png has changed diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/plugins/history.c --- a/pidgin/plugins/history.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/plugins/history.c Fri Sep 28 20:58:48 2007 +0000 @@ -45,6 +45,9 @@ convtype = purple_conversation_get_type(c); gtkconv = PIDGIN_CONVERSATION(c); + if (gtkconv == NULL) + return; + if (convtype == PURPLE_CONV_TYPE_IM && g_list_length(gtkconv->convs) < 2) { GSList *buddies; @@ -163,6 +166,7 @@ purple_signal_connect(purple_conversations_get_handle(), "conversation-created", plugin, PURPLE_CALLBACK(historize), NULL); + /* XXX: Do we want to listen to pidgin's "conversation-displayed" signal? */ purple_prefs_connect_callback(plugin, "/purple/logging/log_ims", history_prefs_cb, plugin); diff -r 5c42c7c7780d -r 045e4f2459a9 pidgin/plugins/pidginrc.c --- a/pidgin/plugins/pidginrc.c Fri Sep 28 20:58:27 2007 +0000 +++ b/pidgin/plugins/pidginrc.c Fri Sep 28 20:58:48 2007 +0000 @@ -57,22 +57,16 @@ static const gchar *font_prefs[] = { "/plugins/gtk/purplerc/font/*pidgin_conv_entry", - "/plugins/gtk/purplerc/font/*pidgin_conv_imhtml", - "/plugins/gtk/purplerc/font/*pidgin_log_imhtml", "/plugins/gtk/purplerc/font/*pidgin_request_imhtml", "/plugins/gtk/purplerc/font/*pidgin_notify_imhtml", }; static const gchar *font_prefs_set[] = { "/plugins/gtk/purplerc/set/font/*pidgin_conv_entry", - "/plugins/gtk/purplerc/set/font/*pidgin_conv_imhtml", - "/plugins/gtk/purplerc/set/font/*pidgin_log_imhtml", "/plugins/gtk/purplerc/set/font/*pidgin_request_imhtml", "/plugins/gtk/purplerc/set/font/*pidgin_notify_imhtml", }; static const gchar *font_names[] = { N_("Conversation Entry"), - N_("Conversation History"), - N_("Log Viewer"), N_("Request Dialog"), N_("Notify Dialog") }; diff -r 5c42c7c7780d -r 045e4f2459a9 po/ChangeLog --- a/po/ChangeLog Fri Sep 28 20:58:27 2007 +0000 +++ b/po/ChangeLog Fri Sep 28 20:58:48 2007 +0000 @@ -4,6 +4,7 @@ * Arabic translation updated (Khaled Hosny) * Italian translation updated (Claudio Satriano) * Lao translation added (Anousak Souphavah) + * Norwegian Bokmål translation updated (Espen Stefansen) * Slovenian translation updated (Martin Srebotnjak) * Serbian translation updated (Miloš Popović) diff -r 5c42c7c7780d -r 045e4f2459a9 po/POTFILES.in --- a/po/POTFILES.in Fri Sep 28 20:58:27 2007 +0000 +++ b/po/POTFILES.in Fri Sep 28 20:58:48 2007 +0000 @@ -91,11 +91,13 @@ libpurple/protocols/jabber/usermood.c libpurple/protocols/jabber/usernick.c libpurple/protocols/jabber/xdata.c +libpurple/protocols/msn/contact.c libpurple/protocols/msn/dialog.c libpurple/protocols/msn/error.c libpurple/protocols/msn/msn.c libpurple/protocols/msn/nexus.c libpurple/protocols/msn/notification.c +libpurple/protocols/msn/oim.c libpurple/protocols/msn/servconn.c libpurple/protocols/msn/session.c libpurple/protocols/msn/state.c