# HG changeset patch # User Sean Egan # Date 1179962270 0 # Node ID cb5bd3c045a9684c8e62d244a5215b0a6cb6a789 # Parent 6ec8bae7d98aadeb7a9dcf363d235d073e009d92# Parent d04b1617da3daec016b7c349088a64c70a7562c0 merge of '10181aa0574db4bb368e5eb9c7eb9440c61d930a' and '12efd7c55efaf31eca5fecc6ccdeeb12ee4b7d95' diff -r 6ec8bae7d98a -r cb5bd3c045a9 AUTHORS --- a/AUTHORS Wed May 23 23:13:11 2007 +0000 +++ b/AUTHORS Wed May 23 23:17:50 2007 +0000 @@ -3,8 +3,7 @@ For a complete list of all contributors, see the COPYRIGHT file. -We've got an IRC room now too, #pidgin on irc.freenode.net -(#pidgin-win32 for Windows users). Come check us out. +We've got an IRC room now too, #pidgin on irc.freenode.net. Come check us out. Current Developers: ------------------ diff -r 6ec8bae7d98a -r cb5bd3c045a9 COPYRIGHT --- a/COPYRIGHT Wed May 23 23:13:11 2007 +0000 +++ b/COPYRIGHT Wed May 23 23:17:50 2007 +0000 @@ -87,6 +87,7 @@ Palmer Cox Jeramey Crawford Michael Culbertson +Steven Danna Martijn Dekker Vinicius Depizzol Philip Derrin @@ -172,6 +173,7 @@ Thomas Huriaux Instant Messaging Freedom, Inc. Vitaliy Ischenko +Intel Corporation Scott Jackson Hans Petter Jansson Henry Jen @@ -303,6 +305,7 @@ David Schmitt Mark Schneider Evan Schoenberg +Gabriel Schulhof Federico Schwindt Torrey Searle Peter Seebach @@ -355,11 +358,13 @@ Junichi Uekawa István Váradi Martijn van Beers +Arjan van de Ven Philip Van Hoof Kristof Vansant James Vega David Vermeille Sid Vicious +Jorge Villaseñor (Masca) Bjoern Voigt Wan Hing Wah Philip Walford diff -r 6ec8bae7d98a -r cb5bd3c045a9 ChangeLog --- a/ChangeLog Wed May 23 23:13:11 2007 +0000 +++ b/ChangeLog Wed May 23 23:17:50 2007 +0000 @@ -25,10 +25,32 @@ unencrypted channel * Fix XMPP SASL authentication error when using Cyrus and a connect server * Fix changing tab locations to update properly +<<<<<<< variant A * ALSA added as a possible sound method * Added Google Talk to the accounts dropdown * Google Talk accounts will not import buddies from your Gmail addres book +>>>>>>> variant B + * Turning off "Show formatting on incoming messages" now ignores + formatting in tags too + * File transfer progress for transfers on MSN is now correctly displayed + * You can set/change alias of buddies/chats by double-clicking on the + conversation tabs (Ma Xuan) + * Fix IRC connection bug with dircproxy (xjoe) + * Ctrl+[shift]+tab focuses the next most active tab (William Thompson) + * Fix Open Hotmail Inbox for MSN to work more reliably + * Add a Google Talk item to the protocol list, to help users who think + we don't support Google Talk. The item acts just like "XMPP". + * Remember if the X server supports XScreenSaver, to avoid waking it + every 5 seconds. (Arjan van de Ven with Intel Corporation) + * Change our idle checking to poll only as necessary and raise the + unidle timeout from 5 seconds to 60 when using XScreenSaver. This + and the XScreenSaver change will reduce Pidgin's effect on power + consumption when running with NO_HZ. (Arjan van de Ven with Intel + Corporation) + * Conversation -> Save As will now use aliases. +####### Ancestor +======= end Finch: * Userlist in chat windows, which can be turned on or off using @@ -37,6 +59,11 @@ * Improved tab completion support * Ctrl+c prompts with a dialog before exiting * Filter string in the debug window + * Notify when you leave a chat + * Work around an ncurses bug which appears when half of a multi-cell + character is covered by an upper-level window + * New plugins are shown in bold text in the plugin dialog + * Nicer HTML screendumps version 2.0.0 (5/3/2007): * The project has new names - libpurple for the core, Pidgin for the diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/finch.c --- a/finch/finch.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/finch.c Wed May 23 23:17:50 2007 +0000 @@ -214,11 +214,6 @@ {0, 0, 0, 0} }; -#ifdef PURPLE_FATAL_ASSERTS - /* Make g_return_... functions fatal. */ - g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL); -#endif - #ifdef ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); bind_textdomain_codeset(PACKAGE, "UTF-8"); diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntaccount.c --- a/finch/gntaccount.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntaccount.c Wed May 23 23:17:50 2007 +0000 @@ -930,11 +930,15 @@ static PurpleAccountUiOps ui_ops = { - .notify_added = notify_added, - .status_changed = NULL, - .request_add = request_add, - .request_authorize = finch_request_authorize, - .close_account_request = finch_request_close + notify_added, + NULL, + request_add, + finch_request_authorize, + finch_request_close, + NULL, + NULL, + NULL, + NULL }; PurpleAccountUiOps *finch_accounts_get_ui_ops() diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntblist.c --- a/finch/gntblist.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntblist.c Wed May 23 23:17:50 2007 +0000 @@ -113,6 +113,7 @@ static void update_node_display(PurpleBlistNode *buddy, FinchBlist *ggblist); static void update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist); static void account_signed_on_cb(PurpleConnection *pc, gpointer null); +static void finch_request_add_buddy(PurpleAccount *account, const char *username, const char *grp, const char *alias); /* Sort functions */ static int blist_node_compare_position(PurpleBlistNode *n1, PurpleBlistNode *n2); @@ -285,6 +286,7 @@ if (error) { + finch_request_add_buddy(account, username, group, alias); purple_notify_error(NULL, _("Error"), _("Error adding buddy"), error); return; } @@ -447,9 +449,13 @@ node_remove, NULL, NULL, - .request_add_buddy = finch_request_add_buddy, - .request_add_chat = finch_request_add_chat, - .request_add_group = finch_request_add_group + finch_request_add_buddy, + finch_request_add_chat, + finch_request_add_group, + NULL, + NULL, + NULL, + NULL }; static gpointer diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntconn.c --- a/finch/gntconn.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntconn.c Wed May 23 23:17:50 2007 +0000 @@ -56,11 +56,17 @@ static PurpleConnectionUiOps ops = { - .connect_progress = NULL, - .connected = NULL, - .disconnected = NULL, - .notice = NULL, - .report_disconnect = finch_connection_report_disconnect + NULL, /* connect_progress */ + NULL, /* connected */ + NULL, /* disconnected */ + NULL, /* notice */ + finch_connection_report_disconnect, + NULL, /* network_connected */ + NULL, /* network_disconnected */ + NULL, + NULL, + NULL, + NULL }; PurpleConnectionUiOps *finch_connections_get_ui_ops() diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntconv.c --- a/finch/gntconv.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntconv.c Wed May 23 23:17:50 2007 +0000 @@ -493,7 +493,8 @@ FinchConvChat *fc = ggc->u.chat = g_new0(FinchConvChat, 1); hbox = gnt_hbox_new(FALSE); gnt_box_set_pad(GNT_BOX(hbox), 0); - tree = fc->userlist = gnt_tree_new(); + tree = fc->userlist = gnt_tree_new_with_columns(2); + gnt_tree_set_col_width(GNT_TREE(tree), 0, 1); /* The flag column */ gnt_tree_set_compare_func(GNT_TREE(tree), (GCompareFunc)g_utf8_collate); gnt_tree_set_hash_fns(GNT_TREE(tree), g_str_hash, g_str_equal, g_free); GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER); @@ -675,6 +676,20 @@ finch_write_common(conv, name, message, flags, mtime); } +static const char * +chat_flag_text(PurpleConvChatBuddyFlags flags) +{ + if (flags & PURPLE_CBFLAGS_FOUNDER) + return "~"; + if (flags & PURPLE_CBFLAGS_OP) + return "@"; + if (flags & PURPLE_CBFLAGS_HALFOP) + return "%"; + if (flags & PURPLE_CBFLAGS_VOICE) + return "+"; + return " "; +} + static void finch_chat_add_users(PurpleConversation *conv, GList *users, gboolean new_arrivals) { @@ -709,7 +724,7 @@ gnt_entry_add_suggest(entry, cbuddy->name); gnt_entry_add_suggest(entry, cbuddy->alias); gnt_tree_add_row_after(tree, g_strdup(cbuddy->name), - gnt_tree_create_row(tree, cbuddy->alias), NULL, NULL); + gnt_tree_create_row(tree, chat_flag_text(cbuddy->flags), cbuddy->alias), NULL, NULL); } } @@ -720,16 +735,19 @@ FinchConv *ggc = conv->ui_data; GntEntry *entry = GNT_ENTRY(ggc->entry); GntTree *tree = GNT_TREE(ggc->u.chat->userlist); + PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), new_n); + gnt_entry_remove_suggest(entry, old); + gnt_tree_remove(tree, (gpointer)old); + gnt_entry_add_suggest(entry, new_n); gnt_entry_add_suggest(entry, new_a); - gnt_tree_remove(tree, (gpointer)old); gnt_tree_add_row_after(tree, g_strdup(new_n), - gnt_tree_create_row(tree, new_a), NULL, NULL); + gnt_tree_create_row(tree, chat_flag_text(cb->flags), new_a), NULL, NULL); } static void -finch_chat_remove_user(PurpleConversation *conv, GList *list) +finch_chat_remove_users(PurpleConversation *conv, GList *list) { /* Remove the name from string completion */ FinchConv *ggc = conv->ui_data; @@ -744,24 +762,32 @@ static void finch_chat_update_user(PurpleConversation *conv, const char *user) { + PurpleConvChatBuddy *cb = purple_conv_chat_cb_find(PURPLE_CONV_CHAT(conv), user); + FinchConv *ggc = conv->ui_data; + gnt_tree_change_text(GNT_TREE(ggc->u.chat->userlist), (gpointer)user, 0, chat_flag_text(cb->flags)); } static PurpleConversationUiOps conv_ui_ops = { - .create_conversation = finch_create_conversation, - .destroy_conversation = finch_destroy_conversation, - .write_chat = finch_write_chat, - .write_im = finch_write_im, - .write_conv = finch_write_conv, - .chat_add_users = finch_chat_add_users, - .chat_rename_user = finch_chat_rename_user, - .chat_remove_users = finch_chat_remove_user, - .chat_update_user = finch_chat_update_user, - .present = NULL, - .has_focus = NULL, - .custom_smiley_add = NULL, - .custom_smiley_write = NULL, - .custom_smiley_close = NULL + finch_create_conversation, + finch_destroy_conversation, + finch_write_chat, + finch_write_im, + finch_write_conv, + finch_chat_add_users, + finch_chat_rename_user, + finch_chat_remove_users, + finch_chat_update_user, + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL }; PurpleConversationUiOps *finch_conv_get_ui_ops() diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntnotify.c --- a/finch/gntnotify.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntnotify.c Wed May 23 23:17:50 2007 +0000 @@ -405,17 +405,21 @@ static PurpleNotifyUiOps ops = { - .notify_message = finch_notify_message, - .close_notify = finch_close_notify, /* The rest of the notify-uiops return a GntWidget. - These widgets should be destroyed from here. */ - .notify_formatted = finch_notify_formatted, - .notify_email = finch_notify_email, - .notify_emails = finch_notify_emails, - .notify_userinfo = finch_notify_userinfo, + finch_notify_message, + finch_notify_email, + finch_notify_emails, + finch_notify_formatted, + finch_notify_searchresults, + finch_notify_sr_new_rows, + finch_notify_userinfo, + NULL, /* notify_uri is of low-priority to me. --sadrul */ + finch_close_notify, /* The rest of the notify-uiops return a GntWidget. + These widgets should be destroyed from here. */ + NULL, + NULL, + NULL, + NULL - .notify_searchresults = finch_notify_searchresults, - .notify_searchresults_new_rows = finch_notify_sr_new_rows, - .notify_uri = NULL /* This is of low-priority to me */ }; PurpleNotifyUiOps *finch_notify_get_ui_ops() diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntplugin.c --- a/finch/gntplugin.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntplugin.c Wed May 23 23:17:50 2007 +0000 @@ -67,11 +67,10 @@ static void plugin_toggled_cb(GntWidget *tree, PurplePlugin *plugin, gpointer null) { - /* TODO: Mark these strings for translation after the freeze */ if (gnt_tree_get_choice(GNT_TREE(tree), plugin)) { if (!purple_plugin_load(plugin)) { - purple_notify_error(NULL, "ERROR", "loading plugin failed", NULL); + purple_notify_error(NULL, _("ERROR"), _("loading plugin failed"), NULL); gnt_tree_set_choice(GNT_TREE(tree), plugin, FALSE); } } @@ -80,7 +79,7 @@ GntWidget *win; if (!purple_plugin_unload(plugin)) { - purple_notify_error(NULL, "ERROR", "unloading plugin failed", NULL); + purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), NULL); gnt_tree_set_choice(GNT_TREE(tree), plugin, TRUE); } diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntprefs.c --- a/finch/gntprefs.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntprefs.c Wed May 23 23:17:50 2007 +0000 @@ -76,7 +76,7 @@ get_idle_options() { GList *list = NULL; - list = g_list_append(list, "Based on keyboard use"); /* XXX: string freeze */ + list = g_list_append(list, (char *)_("Based on keyboard use")); list = g_list_append(list, "system"); list = g_list_append(list, (char*)_("From last sent message")); list = g_list_append(list, "purple"); diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/gntrequest.c --- a/finch/gntrequest.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/gntrequest.c Wed May 23 23:17:50 2007 +0000 @@ -612,13 +612,17 @@ static PurpleRequestUiOps uiops = { - .request_input = finch_request_input, - .close_request = finch_close_request, - .request_choice = finch_request_choice, - .request_action = finch_request_action, - .request_fields = finch_request_fields, - .request_file = finch_request_file, - .request_folder = NULL /* No plans for this */ + finch_request_input, + finch_request_choice, + finch_request_action, + finch_request_fields, + finch_request_file, + finch_close_request, + NULL, /* No plans for request_folder */ + NULL, + NULL, + NULL, + NULL }; PurpleRequestUiOps *finch_request_get_ui_ops() diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/libgnt/Makefile.am --- a/finch/libgnt/Makefile.am Wed May 23 23:13:11 2007 +0000 +++ b/finch/libgnt/Makefile.am Wed May 23 23:17:50 2007 +0000 @@ -66,10 +66,10 @@ gntmarshal.c: $(srcdir)/genmarshal gntmarshal.h echo "#include \"gntmarshal.h\"" > $@ - cat $(srcdir)/genmarshal | glib-genmarshal --prefix=gnt_closure_marshal --body >> $@ + glib-genmarshal --prefix=gnt_closure_marshal --body $(srcdir)/genmarshal >> $@ gntmarshal.h: $(srcdir)/genmarshal - cat $(srcdir)/genmarshal | glib-genmarshal --prefix=gnt_closure_marshal --header > $@ + glib-genmarshal --prefix=gnt_closure_marshal --header $(srcdir)/genmarshal > $@ libgnt_laincludedir=$(includedir)/gnt libgnt_lainclude_HEADERS = \ diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/libgnt/gntmain.c --- a/finch/libgnt/gntmain.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/libgnt/gntmain.c Wed May 23 23:17:50 2007 +0000 @@ -144,8 +144,8 @@ event = GNT_MOUSE_UP; } else return FALSE; - - if (gnt_wm_process_click(wm, event, x, y, widget)) + + if (widget && gnt_wm_process_click(wm, event, x, y, widget)) return TRUE; if (event == GNT_LEFT_MOUSE_DOWN && widget && widget != wm->_list.window && @@ -177,7 +177,8 @@ offset = 0; } - gnt_widget_clicked(widget, event, x, y); + if (widget) + gnt_widget_clicked(widget, event, x, y); return TRUE; } @@ -348,6 +349,10 @@ gnt_wm_raise_window(wm, win); } +#ifdef SIGWINCH +static void (*org_winch_handler)(int); +#endif + static void sighandler(int sig) { @@ -357,6 +362,7 @@ werase(stdscr); wrefresh(stdscr); g_idle_add(refresh_screen, NULL); + org_winch_handler(sig); signal(SIGWINCH, sighandler); break; #endif @@ -434,7 +440,7 @@ wrefresh(stdscr); #ifdef SIGWINCH - signal(SIGWINCH, sighandler); + org_winch_handler = signal(SIGWINCH, sighandler); #endif signal(SIGCHLD, sighandler); signal(SIGINT, sighandler); diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/libgnt/gntwm.c --- a/finch/libgnt/gntwm.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/libgnt/gntwm.c Wed May 23 23:17:50 2007 +0000 @@ -48,6 +48,10 @@ static void update_window_in_list(GntWM *wm, GntWidget *wid); static void shift_window(GntWM *wm, GntWidget *widget, int dir); +#ifndef NO_WIDECHAR +static int widestringwidth(wchar_t *wide); +#endif + static gboolean write_already(gpointer data); static int write_timeout; static time_t last_active_time; @@ -136,6 +140,65 @@ copywin(src, dst, node->scroll, 0, 0, 0, getmaxy(dst) - 1, getmaxx(dst) - 1, 0); } +/** + * The following is a workaround for a bug in most versions of ncursesw. + * Read about it in: http://article.gmane.org/gmane.comp.lib.ncurses.bugs/2751 + * + * In short, if a panel hides one cell of a multi-cell character, then the rest + * of the characters in that line get screwed. The workaround here is to erase + * any such character preemptively. + * + * Caveat: If a wide character is erased, and the panel above it is moved enough + * to expose the entire character, it is not always redrawn. + */ +static void +work_around_for_ncurses_bug() +{ +#ifndef NO_WIDECHAR + PANEL *panel = NULL; + while ((panel = panel_below(panel)) != NULL) { + int sx, ex, sy, ey, w, y; + cchar_t ch; + PANEL *below = panel; + + sx = panel->win->_begx; + ex = panel->win->_maxx + sx; + sy = panel->win->_begy; + ey = panel->win->_maxy + sy; + + while ((below = panel_below(below)) != NULL) { + if (sy > below->win->_begy + below->win->_maxy || + ey < below->win->_begy) + continue; + if (sx > below->win->_begx + below->win->_maxx || + ex < below->win->_begx) + continue; + for (y = MAX(sy, below->win->_begy); y <= MIN(ey, below->win->_begy + below->win->_maxy); y++) { + if (mvwin_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch) != OK) + goto right; + w = widestringwidth(ch.chars); + if (w > 1 && (ch.attr & 1)) { + ch.chars[0] = ' '; + ch.attr &= ~ A_CHARTEXT; + mvwadd_wch(below->win, y - below->win->_begy, sx - 1 - below->win->_begx, &ch); + touchline(below->win, y - below->win->_begy, 1); + } +right: + if (mvwin_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch) != OK) + continue; + w = widestringwidth(ch.chars); + if (w > 1 && !(ch.attr & 1)) { + ch.chars[0] = ' '; + ch.attr &= ~ A_CHARTEXT; + mvwadd_wch(below->win, y - below->win->_begy, ex + 1 - below->win->_begx, &ch); + touchline(below->win, y - below->win->_begy, 1); + } + } + } + } +#endif +} + static gboolean update_screen(GntWM *wm) { @@ -148,6 +211,7 @@ top = top->submenu; } } + work_around_for_ncurses_bug(); update_panels(); doupdate(); return TRUE; @@ -564,14 +628,40 @@ int x, y; chtype old = 0, now = 0; FILE *file = fopen("dump.html", "w"); + struct { + char ascii; + char *unicode; + } unis[] = { + {'q', "─"}, + {'t', "├"}, + {'u', "┤"}, + {'x', "│"}, + {'-', "↑"}, + {'.', "↓"}, + {'l', "┌"}, + {'k', "┐"}, + {'m', "└"}, + {'j', "┘"}, + {'a', "▒"}, + {'\0', NULL} + }; + fprintf(file, "\n \n\n\n"); fprintf(file, "
");
 	for (y = 0; y < getmaxy(stdscr); y++) {
 		for (x = 0; x < getmaxx(stdscr); x++) {
-			char ch;
+			char ch[2] = {0, 0}, *print;
+#ifdef NO_WIDECHAR
 			now = mvwinch(curscr, y, x);
-			ch = now & A_CHARTEXT;
-			now ^= ch;
+			ch[0] = now & A_CHARTEXT;
+			now ^= ch[0];
+#else
+			cchar_t wch;
+			char unicode[12];
+			mvwin_wch(curscr, y, x, &wch);
+			now = wch.attr;
+			ch[0] = (char)(wch.chars[0] & 0xff);
+#endif
 
 #define CHECK(attr, start, end) \
 			do \
@@ -607,7 +697,11 @@
 				if (bgp == -1)
 					bgp = COLOR_WHITE;
 				if (now & A_REVERSE)
-					fgp ^= bgp ^= fgp ^= bgp;  /* *wink* */
+				{
+					short tmp = fgp;
+					fgp = bgp;
+					bgp = tmp;
+				}
 				ret = color_content(fgp, &r, &g, &b);
 				fg.r = r; fg.b = b; fg.g = g;
 				ret = color_content(bgp, &r, &g, &b);
@@ -624,48 +718,39 @@
 				fprintf(file, "",
 						bg.r, bg.g, bg.b, fg.r, fg.g, fg.b);
 			}
+			print = ch;
+#ifndef NO_WIDECHAR
+			if (wch.chars[0] > 255) {
+				snprintf(unicode, sizeof(unicode), "&#x%x;", wch.chars[0]);
+				print = unicode;
+			}
+#endif
 			if (now & A_ALTCHARSET)
 			{
-				switch (ch)
-				{
-					case 'q':
-						ch = '-'; break;
-					case 't':
-					case 'u':
-					case 'x':
-						ch = '|'; break;
-					case 'v':
-					case 'w':
-					case 'l':
-					case 'm':
-					case 'k':
-					case 'j':
-					case 'n':
-						ch = '+'; break;
-					case '-':
-						ch = '^'; break;
-					case '.':
-						ch = 'v'; break;
-					case 'a':
-						ch = '#'; break;
-					default:
-						ch = ' '; break;
+				int u;
+				for (u = 0; unis[u].ascii; u++) {
+					if (ch[0] == unis[u].ascii) {
+						print = unis[u].unicode;
+						break;
+					}
 				}
+				if (!unis[u].ascii)
+					print = " ";
 			}
-			if (ch == '&')
+			if (ch[0] == '&')
 				fprintf(file, "&");
-			else if (ch == '<')
+			else if (ch[0] == '<')
 				fprintf(file, "<");
-			else if (ch == '>')
+			else if (ch[0] == '>')
 				fprintf(file, ">");
 			else
-				fprintf(file, "%c", ch);
+				fprintf(file, "%s", print);
 			old = now;
 		}
 		fprintf(file, "\n");
 		old = 0;
 	}
-	fprintf(file, "
"); + fprintf(file, "\n"); fclose(file); return TRUE; } diff -r 6ec8bae7d98a -r cb5bd3c045a9 finch/plugins/gntgf.c --- a/finch/plugins/gntgf.c Wed May 23 23:13:11 2007 +0000 +++ b/finch/plugins/gntgf.c Wed May 23 23:17:50 2007 +0000 @@ -56,7 +56,8 @@ #include #include -#include +#include "gntplugin.h" +#include "gntconv.h" typedef struct { @@ -154,7 +155,7 @@ #endif static void -notify(const char *fmt, ...) +notify(PurpleConversation *conv, const char *fmt, ...) { GntWidget *window; GntToast *toast; @@ -164,6 +165,13 @@ if (purple_prefs_get_bool(PREFS_BEEP)) beep(); + + if (conv != NULL) { + FinchConv *fc = conv->ui_data; + if (gnt_widget_has_focus(fc->window)) + return; + } + #ifdef HAVE_X11 if (purple_prefs_get_bool(PREFS_URGENT)) urgent(); @@ -220,14 +228,14 @@ buddy_signed_on(PurpleBuddy *buddy, gpointer null) { if (purple_prefs_get_bool(PREFS_EVENT_SIGNONF)) - notify(_("%s just signed on"), purple_buddy_get_alias(buddy)); + notify(NULL, _("%s just signed on"), purple_buddy_get_alias(buddy)); } static void buddy_signed_off(PurpleBuddy *buddy, gpointer null) { if (purple_prefs_get_bool(PREFS_EVENT_SIGNONF)) - notify(_("%s just signed off"), purple_buddy_get_alias(buddy)); + notify(NULL, _("%s just signed off"), purple_buddy_get_alias(buddy)); } static void @@ -235,7 +243,7 @@ PurpleConversation *conv, PurpleMessageFlags flags, gpointer null) { if (purple_prefs_get_bool(PREFS_EVENT_IM_MSG)) - notify(_("%s sent you a message"), sender); + notify(conv, _("%s sent you a message"), sender); } static void @@ -254,9 +262,9 @@ if (purple_prefs_get_bool(PREFS_EVENT_CHAT_NICK) && (purple_utf8_has_word(msg, nick))) - notify(_("%s said your nick in %s"), sender, purple_conversation_get_name(conv)); + notify(conv, _("%s said your nick in %s"), sender, purple_conversation_get_name(conv)); else if (purple_prefs_get_bool(PREFS_EVENT_CHAT_MSG)) - notify(_("%s sent a message in %s"), sender, purple_conversation_get_name(conv)); + notify(conv, _("%s sent a message in %s"), sender, purple_conversation_get_name(conv)); } static gboolean diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/Makefile.am --- a/libpurple/Makefile.am Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/Makefile.am Wed May 23 23:17:50 2007 +0000 @@ -151,7 +151,7 @@ dbus_headers = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \ - connection.h conversation.h core.h log.h prefs.h roomlist.h \ + connection.h conversation.h core.h log.h notify.h prefs.h roomlist.h \ savedstatuses.h status.h server.h util.h xmlnode.h purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/account.c --- a/libpurple/account.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/account.c Wed May 23 23:17:50 2007 +0000 @@ -548,9 +548,9 @@ data = xmlnode_get_attrib(node, "active"); if (data == NULL) return; - if (strcasecmp(data, "true") == 0) + if (g_ascii_strcasecmp(data, "true") == 0) active = TRUE; - else if (strcasecmp(data, "false") == 0) + else if (g_ascii_strcasecmp(data, "false") == 0) active = FALSE; else return; diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/cipher.c --- a/libpurple/cipher.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/cipher.c Wed May 23 23:17:50 2007 +0000 @@ -2000,8 +2000,8 @@ /* Check for a supported algorithm. */ g_return_val_if_fail(algorithm == NULL || *algorithm == '\0' || - strcasecmp(algorithm, "MD5") || - strcasecmp(algorithm, "MD5-sess"), NULL); + g_ascii_strcasecmp(algorithm, "MD5") || + g_ascii_strcasecmp(algorithm, "MD5-sess"), NULL); cipher = purple_ciphers_find_cipher("md5"); g_return_val_if_fail(cipher != NULL, NULL); @@ -2014,7 +2014,7 @@ purple_cipher_context_append(context, (guchar *)":", 1); purple_cipher_context_append(context, (guchar *)password, strlen(password)); - if (algorithm != NULL && !strcasecmp(algorithm, "MD5-sess")) + if (algorithm != NULL && !g_ascii_strcasecmp(algorithm, "MD5-sess")) { guchar digest[16]; @@ -2065,14 +2065,14 @@ /* Check for a supported algorithm. */ g_return_val_if_fail(algorithm == NULL || *algorithm == '\0' || - strcasecmp(algorithm, "MD5") || - strcasecmp(algorithm, "MD5-sess"), NULL); + g_ascii_strcasecmp(algorithm, "MD5") || + g_ascii_strcasecmp(algorithm, "MD5-sess"), NULL); /* Check for a supported "quality of protection". */ g_return_val_if_fail(qop == NULL || *qop == '\0' || - strcasecmp(qop, "auth") || - strcasecmp(qop, "auth-int"), NULL); + g_ascii_strcasecmp(qop, "auth") || + g_ascii_strcasecmp(qop, "auth-int"), NULL); cipher = purple_ciphers_find_cipher("md5"); g_return_val_if_fail(cipher != NULL, NULL); @@ -2083,7 +2083,7 @@ purple_cipher_context_append(context, (guchar *)":", 1); purple_cipher_context_append(context, (guchar *)digest_uri, strlen(digest_uri)); - if (qop != NULL && !strcasecmp(qop, "auth-int")) + if (qop != NULL && !g_ascii_strcasecmp(qop, "auth-int")) { PurpleCipherContext *context2; gchar entity_hash[33]; diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/conversation.c --- a/libpurple/conversation.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/conversation.c Wed May 23 23:17:50 2007 +0000 @@ -970,20 +970,20 @@ { im->typing_state = state; - if (state == PURPLE_TYPING) - { - purple_signal_emit(purple_conversations_get_handle(), - "buddy-typing", im->conv->account, im->conv->name); - } - else if (state == PURPLE_TYPED) + switch (state) { - purple_signal_emit(purple_conversations_get_handle(), - "buddy-typed", im->conv->account, im->conv->name); - } - else if (state == PURPLE_NOT_TYPING) - { - purple_signal_emit(purple_conversations_get_handle(), - "buddy-typing-stopped", im->conv->account, im->conv->name); + case PURPLE_TYPING: + purple_signal_emit(purple_conversations_get_handle(), + "buddy-typing", im->conv->account, im->conv->name); + break; + case PURPLE_TYPED: + purple_signal_emit(purple_conversations_get_handle(), + "buddy-typed", im->conv->account, im->conv->name); + break; + case PURPLE_NOT_TYPING: + purple_signal_emit(purple_conversations_get_handle(), + "buddy-typing-stopped", im->conv->account, im->conv->name); + break; } } } @@ -1497,7 +1497,7 @@ } else if (a->buddy != b->buddy) { ret = a->buddy ? -1 : 1; } else { - ret = strcasecmp(user1, user2); + ret = purple_utf8_strcasecmp(user1, user2); } return ret; diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/dbus-analyze-functions.py --- a/libpurple/dbus-analyze-functions.py Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/dbus-analyze-functions.py Wed May 23 23:17:50 2007 +0000 @@ -96,7 +96,7 @@ if len(type) == 1: # simple types (int, gboolean, etc.) and enums - if (type[0] in simpletypes) or (type[0].startswith("Purple")): + if (type[0] in simpletypes) or ((type[0].startswith("Purple") and not type[0].endswith("Callback"))): return self.inputsimple(type, name) # pointers ... @@ -118,7 +118,6 @@ # unknown pointers are always replaced with NULL else: return self.inputpointer(type, name) - return raise myexception diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/example/nullclient.c --- a/libpurple/example/nullclient.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/example/nullclient.c Wed May 23 23:17:50 2007 +0000 @@ -137,7 +137,25 @@ static PurpleConversationUiOps null_conv_uiops = { - .write_conv = null_write_conv + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + null_write_conv, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL }; static void diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/ft.c --- a/libpurple/ft.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/ft.c Wed May 23 23:17:50 2007 +0000 @@ -891,7 +891,7 @@ r = purple_xfer_read(xfer, &buffer); if (r > 0) { fwrite(buffer, 1, r, xfer->dest_fp); - } else if(r <= 0) { + } else if(r < 0) { purple_xfer_cancel_remote(xfer); return; } @@ -972,7 +972,8 @@ fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET); - xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer); + if (xfer->fd) + xfer->watcher = purple_input_add(xfer->fd, cond, transfer_cb, xfer); xfer->start_time = time(NULL); diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/idle.c --- a/libpurple/idle.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/idle.c Wed May 23 23:17:50 2007 +0000 @@ -31,7 +31,6 @@ #include "signals.h" #define IDLEMARK 600 /* 10 minutes! */ -#define IDLE_CHECK_INTERVAL 5 /* 5 seconds */ typedef enum { @@ -92,6 +91,9 @@ purple_presence_set_idle(presence, FALSE, 0); } + +static int no_away = 0; +static gint time_until_next_idle_event; /* * This function should be called when you think your idle state * may have changed. Maybe you're over the 10-minute mark and @@ -110,14 +112,17 @@ * 2. Set or unset your auto-away message. * 3. Report your current idle time to the IM server. */ -static gint -check_idleness() + +static void +check_idleness(void) { time_t time_idle; gboolean auto_away; const gchar *idle_reporting; gboolean report_idle; GList *l; + gint away_seconds = 0; + gint idle_recheck_interval; purple_signal_emit(purple_blist_get_handle(), "update-idle"); @@ -128,11 +133,13 @@ { /* Use system idle time (mouse or keyboard movement, etc.) */ time_idle = idle_ui_ops->get_time_idle(); + idle_recheck_interval = 60; } else if (!strcmp(idle_reporting, "purple")) { /* Use 'Purple idle' */ time_idle = time(NULL) - last_active_time; + idle_recheck_interval = 0; } else { @@ -146,21 +153,54 @@ /* If we're not reporting idle, we can still do auto-away. * First try "system" and if that isn't possible, use "purple" */ - if (!report_idle && auto_away) { - if ((idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL)) - time_idle = idle_ui_ops->get_time_idle(); + if (!report_idle) + { + if (auto_away) + { + if ((idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL)) + { + time_idle = idle_ui_ops->get_time_idle(); + idle_recheck_interval = 60; + } + else + { + time_idle = time(NULL) - last_active_time; + idle_recheck_interval = 0; + } + } else - time_idle = time(NULL) - last_active_time; + { + if (!no_away) + { + purple_savedstatus_set_idleaway(FALSE); + no_away = 1; + } + time_until_next_idle_event = 0; + return; + } } - if (auto_away && - (time_idle > (60 * purple_prefs_get_int("/purple/away/mins_before_away")))) + time_until_next_idle_event = IDLEMARK - time_idle; + if (time_until_next_idle_event < 0) + { + /* If we're already idle, check again as appropriate. */ + time_until_next_idle_event = idle_recheck_interval; + } + + if (auto_away || !no_away) + away_seconds = 60 * purple_prefs_get_int("/purple/away/mins_before_away"); + + if (auto_away && time_idle > away_seconds) { purple_savedstatus_set_idleaway(TRUE); + no_away = 0; } - else if (time_idle < 60 * purple_prefs_get_int("/purple/away/mins_before_away")) + else if (!no_away && time_idle < away_seconds) { + no_away = 1; purple_savedstatus_set_idleaway(FALSE); + if (time_until_next_idle_event == 0 || (away_seconds - time_idle) < time_until_next_idle_event) + time_until_next_idle_event = away_seconds - time_idle; } /* Idle reporting stuff */ @@ -177,8 +217,21 @@ while (idled_accts != NULL) set_account_unidle(idled_accts->data); } +} - return TRUE; + +/* + * Check idle and set the timer to fire at the next idle-worth event + */ +static gint +check_idleness_timer() +{ + check_idleness(); + if (time_until_next_idle_event == 0) + idle_timer = 0; + else + idle_timer = purple_timeout_add(1000 * (time_until_next_idle_event + 1), check_idleness_timer, NULL); + return FALSE; } static void @@ -205,10 +258,26 @@ set_account_unidle(account); } +static void +idle_reporting_cb(const char *name, PurplePrefType type, gconstpointer val, gpointer data) +{ + if (idle_timer) + purple_timeout_remove(idle_timer); + idle_timer = 0; + check_idleness_timer(); +} + void purple_idle_touch() { time(&last_active_time); + if (!no_away) + { + if (idle_timer) + purple_timeout_remove(idle_timer); + idle_timer = 0; + check_idleness_timer(); + } } void @@ -241,7 +310,7 @@ purple_idle_init() { /* Add the timer to check if we're idle */ - idle_timer = purple_timeout_add(IDLE_CHECK_INTERVAL * 1000, check_idleness, NULL); + idle_timer = purple_timeout_add(1000 * (IDLEMARK + 1), check_idleness_timer, NULL); purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg", purple_idle_get_handle(), @@ -253,6 +322,9 @@ purple_idle_get_handle(), PURPLE_CALLBACK(signing_off_cb), NULL); + purple_prefs_connect_callback(purple_idle_get_handle(), "/purple/away/idle_reporting", + idle_reporting_cb, NULL); + purple_idle_touch(); } @@ -260,6 +332,7 @@ purple_idle_uninit() { purple_signals_disconnect_by_handle(purple_idle_get_handle()); + purple_prefs_disconnect_by_handle(purple_idle_get_handle()); /* Remove the idle timer */ if (idle_timer > 0) diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/notify.c --- a/libpurple/notify.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/notify.c Wed May 23 23:17:50 2007 +0000 @@ -22,6 +22,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "internal.h" +#include "dbus-maybe.h" #include "notify.h" static PurpleNotifyUiOps *notify_ui_ops = NULL; @@ -481,6 +483,7 @@ PurpleNotifyUserInfoEntry *user_info_entry; user_info_entry = g_new0(PurpleNotifyUserInfoEntry, 1); + PURPLE_DBUS_REGISTER_POINTER(user_info_entry, PurpleNotifyUserInfoEntry); user_info_entry->label = g_strdup(label); user_info_entry->value = g_strdup(value); user_info_entry->type = PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR; @@ -495,6 +498,7 @@ g_free(user_info_entry->label); g_free(user_info_entry->value); + PURPLE_DBUS_UNREGISTER_POINTER(user_info_entry); g_free(user_info_entry); } @@ -504,6 +508,7 @@ PurpleNotifyUserInfo *user_info; user_info = g_new0(PurpleNotifyUserInfo, 1); + PURPLE_DBUS_REGISTER_POINTER(user_info, PurpleNotifyUserInfo); user_info->user_info_entries = NULL; return user_info; @@ -521,6 +526,7 @@ } g_list_free(user_info->user_info_entries); + PURPLE_DBUS_UNREGISTER_POINTER(user_info); g_free(user_info); } diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/plugins/joinpart.c --- a/libpurple/plugins/joinpart.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/plugins/joinpart.c Wed May 23 23:17:50 2007 +0000 @@ -156,7 +156,7 @@ static gboolean check_expire_time(struct joinpart_key *key, time_t *last_said, time_t *limit) { - purple_debug_info("joinpart", "Removing key for %s/%s\n", key->conv->name, key->user); + purple_debug_info("joinpart", "Removing key for %s\n", key->user); return (*last_said < *limit); } diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/plugins/perl/Makefile.am --- a/libpurple/plugins/perl/Makefile.am Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/plugins/perl/Makefile.am Wed May 23 23:17:50 2007 +0000 @@ -78,7 +78,8 @@ # common/fallback/const-xs.inc perl_scripts = \ - scripts/function_list.pl + scripts/function_list.pl \ + scripts/signals-test.pl EXTRA_DIST = \ Makefile.mingw \ diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/plugins/perl/common/SavedStatuses.xs --- a/libpurple/plugins/perl/common/SavedStatuses.xs Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/plugins/perl/common/SavedStatuses.xs Wed May 23 23:17:50 2007 +0000 @@ -1,41 +1,129 @@ #include "module.h" +/* I can't get this to work, both with and without the const on the return + * type I get errors from gcc. One way about ignoring types in a cast, and the + * other about assigning to read-only variables. +const Purple::StatusType +purple_savedstatus_substatus_get_type(substatus) + const Purple::SavedStatus::Sub substatus +*/ + MODULE = Purple::SavedStatus PACKAGE = Purple::SavedStatus PREFIX = purple_savedstatus_ PROTOTYPES: ENABLE -gboolean -purple_savedstatus_delete(title) - const char *title - -Purple::SavedStatus -purple_savedstatus_find(title) - const char *title - -const char * -purple_savedstatus_get_message(saved_status) - Purple::SavedStatus saved_status - -const char * -purple_savedstatus_get_title(saved_status) - Purple::SavedStatus saved_status - -Purple::StatusPrimitive -purple_savedstatus_get_type(saved_status) - Purple::SavedStatus saved_status - Purple::SavedStatus purple_savedstatus_new(title, type) const char *title Purple::StatusPrimitive type void +purple_savedstatus_set_title(status, title) + Purple::SavedStatus status + const char *title + +void +purple_savedstatus_set_type(status, type) + Purple::SavedStatus status + Purple::StatusPrimitive type + +void purple_savedstatus_set_message(status, message) Purple::SavedStatus status const char *message +void +purple_savedstatus_set_substatus(status, account, type, message) + Purple::SavedStatus status + Purple::Account account + Purple::StatusType type + const char *message + +void +purple_savedstatus_unset_substatus(status, account) + Purple::SavedStatus status + Purple::Account account + +gboolean +purple_savedstatus_delete(title) + const char *title + Purple::SavedStatus purple_savedstatus_get_current() +Purple::SavedStatus +purple_savedstatus_get_default() + +Purple::SavedStatus +purple_savedstatus_get_idleaway() + +gboolean +purple_savedstatus_is_idleaway() + +void +purple_savedstatus_set_idleaway(idleaway) + gboolean idleaway + +Purple::SavedStatus +purple_savedstatus_get_startup() + +Purple::SavedStatus +purple_savedstatus_find(title) + const char *title + +Purple::SavedStatus +purple_savedstatus_find_by_creation_time(creation_time) + time_t creation_time + +Purple::SavedStatus +purple_savedstatus_find_transient_by_type_and_message(type, message) + Purple::StatusPrimitive type + const char *message + +gboolean +purple_savedstatus_is_transient(saved_status) + const Purple::SavedStatus saved_status + +const char * +purple_savedstatus_get_title(saved_status) + const Purple::SavedStatus saved_status + +Purple::StatusPrimitive +purple_savedstatus_get_type(saved_status) + const Purple::SavedStatus saved_status + +const char * +purple_savedstatus_get_message(saved_status) + const Purple::SavedStatus saved_status + +time_t +purple_savedstatus_get_creation_time(saved_status) + const Purple::SavedStatus saved_status + +gboolean +purple_savedstatus_has_substatuses(saved_status) + const Purple::SavedStatus saved_status + +Purple::SavedStatus::Sub +purple_savedstatus_get_substatus(saved_status, account) + Purple::SavedStatus saved_status + Purple::Account account + +void +purple_savedstatus_activate(saved_status) + Purple::SavedStatus saved_status + +void +purple_savedstatus_activate_for_account(saved_status, account) + const Purple::SavedStatus saved_status + Purple::Account account + +MODULE = Purple::SavedStatus::Sub PACKAGE = Purple::SavedStatus::Sub PREFIX = purple_savedstatus_substatus_ +PROTOTYPES: ENABLE + +const char * +purple_savedstatus_substatus_get_message(substatus) + const Purple::SavedStatus::Sub substatus + MODULE = Purple::SavedStatus PACKAGE = Purple::SavedStatuses PREFIX = purple_savedstatuses_ PROTOTYPES: ENABLE @@ -48,6 +136,16 @@ XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::SavedStatus"))); } +void +purple_savedstatuses_get_popular(how_many) + unsigned int how_many +PREINIT: + const GList *l; +PPCODE: + for (l = purple_savedstatuses_get_popular(how_many); l != NULL; l = l->next) { + XPUSHs(sv_2mortal(purple_perl_bless_object(l->data, "Purple::SavedStatus"))); + } + Purple::Handle purple_savedstatuses_get_handle() diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/plugins/perl/common/module.h --- a/libpurple/plugins/perl/common/module.h Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/plugins/perl/common/module.h Wed May 23 23:17:50 2007 +0000 @@ -236,7 +236,7 @@ /* savedstatuses.h */ typedef PurpleSavedStatus * Purple__SavedStatus; -typedef PurpleSavedStatusSub * Purple__SavedStatusSub; +typedef PurpleSavedStatusSub * Purple__SavedStatus__Sub; /* sound.h */ typedef PurpleSoundEventID Purple__SoundEventID; diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/plugins/perl/common/typemap --- a/libpurple/plugins/perl/common/typemap Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/plugins/perl/common/typemap Wed May 23 23:17:50 2007 +0000 @@ -137,7 +137,9 @@ Purple::RoomlistRoomType T_IV Purple::SavedStatus T_PurpleObj -Purple::SavedStatusSub T_PurpleObj +const Purple::SavedStatus T_PurpleObj +Purple::SavedStatus::Sub T_PurpleObj +const Purple::SavedStatus::Sub T_PurpleObj Purple::SoundEventID T_IV Purple::Sound::UiOps T_PurpleObj diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/plugins/perl/perl-common.c --- a/libpurple/plugins/perl/perl-common.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/plugins/perl/perl-common.c Wed May 23 23:17:50 2007 +0000 @@ -80,7 +80,7 @@ stash = gv_stashpv(stash_name, 1); hv = newHV(); - hv_store(hv, "_purple", 5, create_sv_ptr(object), 0); + hv_store(hv, "_purple", 7, create_sv_ptr(object), 0); return sv_bless(newRV_noinc((SV *)hv), stash); } @@ -94,7 +94,7 @@ hv = hvref(o); if (hv != NULL) { - sv = hv_fetch(hv, "_purple", 5, 0); + sv = hv_fetch(hv, "_purple", 7, 0); if (sv != NULL) return TRUE; @@ -118,7 +118,7 @@ if (hv == NULL) return NULL; - sv = hv_fetch(hv, "_purple", 5, 0); + sv = hv_fetch(hv, "_purple", 7, 0); if (sv == NULL) croak("variable is damaged"); diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/plugins/perl/scripts/signals-test.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/perl/scripts/signals-test.pl Wed May 23 23:17:50 2007 +0000 @@ -0,0 +1,80 @@ +$MODULE_NAME = "Signals Test Script in Perl"; + +use Purple; + +%PLUGIN_INFO = ( + perl_api_version => 2, + name => "Perl: $MODULE_NAME", + version => "0.1", + summary => "Signals Test plugin for the Perl interpreter.", + description => "Demonstrate the user of purple signals from " . + "a perl plugin.", + author => "Sadrul Habib Chowdhury ", + url => "http://developer.pidgin.im/wiki/sadrul/", + + load => "plugin_load", + unload => "plugin_unload" +); + +# Accounts +sub account_connecting_cb +{ + my $account = shift; + Purple::Debug::misc("signals test in perl", "account-connecting (" . $account->get_username() . ")\n"); +} + +# Buddylist +sub buddy_signed_on +{ + my $buddy = shift; + Purple::Debug::misc("signals test in perl", "buddy-signed-on (" . $buddy->get_name() . ")\n"); +} + +# Connections +sub signed_on +{ + my $conn = shift; + Purple::Debug::misc("signals test in perl", "signed-on (" . $conn->get_account()->get_username() . ")\n"); +} + +# Conversations +sub conv_received_msg +{ + my ($account, $sender, $message, $conv, $flags, $data) = @_; + Purple::Debug::misc("signals test in perl", "$data (" . $account->get_username() . ", $sender, $message, $flags)\n"); +} + +sub plugin_load +{ + my $plugin = shift; + + # Hook to the signals + + # Accounts + $act_handle = Purple::Accounts::get_handle(); + Purple::Signal::connect($act_handle, "account-connecting", $plugin, + \&account_connecting_cb, 0); + + # Buddy List + $blist = Purple::BuddyList::get_handle(); + Purple::Signal::connect($blist, "buddy-signed-on", $plugin, + \&buddy_signed_on, 0); + + # Connections + $conn = Purple::Connections::get_handle(); + Purple::Signal::connect($conn, "signed-on", $plugin, + \&signed_on, 0); + + # Conversations + $conv = Purple::Conversations::get_handle(); + Purple::Signal::connect($conv, "received-im-msg", $plugin, + \&conv_received_msg, "received im message"); + Purple::Signal::connect($conv, "received-chat-msg", $plugin, + \&conv_received_msg, "received chat message"); +} + +sub plugin_unload +{ + # Nothing to do here for this plugin. +} + diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/protocols/bonjour/jabber.c --- a/libpurple/protocols/bonjour/jabber.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Wed May 23 23:17:50 2007 +0000 @@ -242,7 +242,7 @@ if (cbba->bj->account == gb->account) { bb = gb->proto_data; - if ((bb != NULL) && (g_strcasecmp(bb->ip, cbba->address) == 0)) + if ((bb != NULL) && (g_ascii_strcasecmp(bb->ip, cbba->address) == 0)) *(cbba->gb) = gb; } } diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/protocols/irc/msgs.c --- a/libpurple/protocols/irc/msgs.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/protocols/irc/msgs.c Wed May 23 23:17:50 2007 +0000 @@ -35,6 +35,7 @@ static char *irc_mask_userhost(const char *mask); static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2]); static void irc_buddy_status(char *name, struct irc_buddy *ib, struct irc_conn *irc); +static void irc_connected(struct irc_conn *irc, const char *nick); static void irc_msg_handle_privmsg(struct irc_conn *irc, const char *name, const char *from, const char *to, @@ -70,6 +71,52 @@ g_free(message); } +static void irc_connected(struct irc_conn *irc, const char *nick) +{ + PurpleConnection *gc; + PurpleStatus *status; + PurpleBlistNode *gnode, *cnode, *bnode; + + if ((gc = purple_account_get_connection(irc->account)) == NULL + || PURPLE_CONNECTION_IS_CONNECTED(gc)) + return; + + purple_connection_set_display_name(gc, nick); + purple_connection_set_state(gc, PURPLE_CONNECTED); + + /* If we're away then set our away message */ + status = purple_account_get_active_status(irc->account); + if (!purple_status_get_type(status) != PURPLE_STATUS_AVAILABLE) { + PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + prpl_info->set_status(irc->account, status); + } + + /* this used to be in the core, but it's not now */ + 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) { + struct irc_buddy *ib = g_new0(struct irc_buddy, 1); + ib->name = g_strdup(b->name); + g_hash_table_insert(irc->buddies, ib->name, ib); + } + } + } + } + + irc_blist_timeout(irc); + if (!irc->timer) + irc->timer = purple_timeout_add(45000, (GSourceFunc)irc_blist_timeout, (gpointer)irc); +} + void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args) { purple_debug(PURPLE_DEBUG_INFO, "irc", "Unrecognized message: %s\n", args[0]); @@ -95,56 +142,16 @@ void irc_msg_luser(struct irc_conn *irc, const char *name, const char *from, char **args) { - PurpleConnection *gc; - PurpleStatus *status; - PurpleBlistNode *gnode, *cnode, *bnode; - - if (!args || !args[0] || !args[1]) - return; - - gc = purple_account_get_connection(irc->account); - if (!gc) + if (!args || !args[0]) return; if (!strcmp(name, "251")) { - /* 251 is required, so we pluck our nick from here */ - purple_connection_set_display_name(gc, args[0]); + /* 251 is required, so we pluck our nick from here and + * finalize connection */ + irc_connected(irc, args[0]); /* Some IRC servers seem to not send a 255 numeric, so * I guess we can't require it; 251 will do. */ /* } else if (!strcmp(name, "255")) { */ - purple_connection_set_state(gc, PURPLE_CONNECTED); - - /* If we're away then set our away message */ - status = purple_account_get_active_status(irc->account); - if (!purple_status_get_type(status) != PURPLE_STATUS_AVAILABLE) { - PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); - prpl_info->set_status(irc->account, status); - } - - /* this used to be in the core, but it's not now */ - 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) { - struct irc_buddy *ib = g_new0(struct irc_buddy, 1); - ib->name = g_strdup(b->name); - g_hash_table_insert(irc->buddies, ib->name, ib); - } - } - } - } - - irc_blist_timeout(irc); - if (!irc->timer) - irc->timer = purple_timeout_add(45000, (GSourceFunc)irc_blist_timeout, (gpointer)irc); } } @@ -523,6 +530,9 @@ { char *escaped; + if (!args || !args[0]) + return; + if (!irc->motd) irc->motd = g_string_new(""); @@ -532,7 +542,9 @@ irc->motd = g_string_new(""); return; } else if (!strcmp(name, "376")) { - /* We no longer have to do anything for ENDMOTD */ + /* dircproxy 1.0.5 does not send 251 on reconnection, so + * finalize the connection here if it is not already done. */ + irc_connected(irc, args[0]); return; } @@ -541,6 +553,9 @@ return; } + if (!args[1]) + return; + escaped = g_markup_escape_text(args[1], -1); g_string_append_printf(irc->motd, "%s
", escaped); g_free(escaped); @@ -1074,11 +1089,21 @@ void irc_msg_regonly(struct irc_conn *irc, const char *name, const char *from, char **args) { PurpleConnection *gc = purple_account_get_connection(irc->account); + PurpleConversation *convo; char *msg; if (!args || !args[1] || !args[2] || !gc) return; + convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account); + if (convo) { + /* This is a channel we're already in; for some reason, + * freenode feels the need to notify us that in some + * hypothetical other situation this might not have + * succeeded. Suppress that. */ + return; + } + msg = g_strdup_printf(_("Cannot join %s: Registration is required."), args[1]); purple_notify_error(gc, _("Cannot join channel"), msg, args[2]); g_free(msg); diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/protocols/irc/parse.c --- a/libpurple/protocols/irc/parse.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/protocols/irc/parse.c Wed May 23 23:17:50 2007 +0000 @@ -227,7 +227,7 @@ enclist = purple_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); encodings = g_strsplit(enclist, ",", 2); - if (encodings[0] == NULL || !strcasecmp("UTF-8", encodings[0])) { + if (encodings[0] == NULL || !g_ascii_strcasecmp("UTF-8", encodings[0])) { g_strfreev(encodings); return g_strdup(string); } @@ -264,7 +264,7 @@ while (*charset == ' ') charset++; - if (!strcasecmp("UTF-8", charset)) { + if (!g_ascii_strcasecmp("UTF-8", charset)) { if (g_utf8_validate(string, -1, NULL)) utf8 = g_strdup(string); } else { diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/protocols/jabber/auth.c --- a/libpurple/protocols/jabber/auth.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/protocols/jabber/auth.c Wed May 23 23:17:50 2007 +0000 @@ -612,28 +612,62 @@ jabber_iq_send(iq); } +/* Parts of this algorithm are inspired by stuff in libgsasl */ static GHashTable* parse_challenge(const char *challenge) { + const char *token_start, *val_start, *val_end, *cur; GHashTable *ret = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - char **pairs; - int i; - pairs = g_strsplit(challenge, ",", -1); + cur = challenge; + while(*cur != '\0') { + /* Find the end of the token */ + gboolean in_quotes = FALSE; + char *name, *value = NULL; + token_start = cur; + while(*cur != '\0' && (in_quotes || (!in_quotes && *cur != ','))) { + if (*cur == '"') + in_quotes = !in_quotes; + cur++; + } + + /* Find start of value. */ + val_start = strchr(token_start, '='); + if (val_start == NULL || val_start > cur) + val_start = cur; + + if (token_start != val_start) { + name = g_strndup(token_start, val_start - token_start); - for(i=0; pairs[i]; i++) { - char **keyval = g_strsplit(pairs[i], "=", 2); - if(keyval[0] && keyval[1]) { - if(keyval[1][0] == '"' && keyval[1][strlen(keyval[1])-1] == '"') - g_hash_table_replace(ret, g_strdup(keyval[0]), g_strndup(keyval[1]+1, strlen(keyval[1])-2)); - else - g_hash_table_replace(ret, g_strdup(keyval[0]), g_strdup(keyval[1])); + if (val_start != cur) { + val_start++; + while (val_start != cur && (*val_start == ' ' || *val_start == '\t' + || *val_start == '\r' || *val_start == '\n' + || *val_start == '"')) + val_start++; + + val_end = cur; + while (val_end != val_start && (*val_end == ' ' || *val_end == ',' || *val_end == '\t' + || *val_end == '\r' || *val_start == '\n' + || *val_end == '"')) + val_end--; + + if (val_start != val_end) + value = g_strndup(val_start, val_end - val_start + 1); + } + + g_hash_table_replace(ret, name, value); } - g_strfreev(keyval); + + /* Find the start of the next token, if there is one */ + if (*cur != '\0') { + cur++; + while (*cur == ' ' || *cur == ',' || *cur == '\t' + || *cur == '\r' || *cur == '\n') + cur++; + } } - g_strfreev(pairs); - return ret; } @@ -738,14 +772,14 @@ } else { /* assemble a response, and send it */ /* see RFC 2831 */ - GString *response = g_string_new(""); - char *a2; - char *auth_resp; - char *buf; - char *cnonce; char *realm; char *nonce; + /* Make sure the auth string contains everything that should be there. + This isn't everything in RFC2831, but it is what we need. */ + + nonce = g_hash_table_lookup(parts, "nonce"); + /* we're actually supposed to prompt the user for a realm if * the server doesn't send one, but that really complicates things, * so i'm not gonna worry about it until is poses a problem to @@ -754,48 +788,55 @@ if(!realm) realm = js->user->domain; - cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), - g_random_int()); - nonce = g_hash_table_lookup(parts, "nonce"); - + if (nonce == NULL || realm == NULL) + purple_connection_error(js->gc, _("Invalid challenge from server")); + else { + GString *response = g_string_new(""); + char *a2; + char *auth_resp; + char *buf; + char *cnonce; - a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); - auth_resp = generate_response_value(js->user, - purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); - g_free(a2); + cnonce = g_strdup_printf("%x%u%x", g_random_int(), (int)time(NULL), + g_random_int()); - a2 = g_strdup_printf(":xmpp/%s", realm); - js->expected_rspauth = generate_response_value(js->user, - purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); - g_free(a2); + a2 = g_strdup_printf("AUTHENTICATE:xmpp/%s", realm); + auth_resp = generate_response_value(js->user, + purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); + g_free(a2); + a2 = g_strdup_printf(":xmpp/%s", realm); + js->expected_rspauth = generate_response_value(js->user, + purple_connection_get_password(js->gc), nonce, cnonce, a2, realm); + g_free(a2); - g_string_append_printf(response, "username=\"%s\"", js->user->node); - g_string_append_printf(response, ",realm=\"%s\"", realm); - g_string_append_printf(response, ",nonce=\"%s\"", nonce); - g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); - g_string_append_printf(response, ",nc=00000001"); - g_string_append_printf(response, ",qop=auth"); - g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); - g_string_append_printf(response, ",response=%s", auth_resp); - g_string_append_printf(response, ",charset=utf-8"); + g_string_append_printf(response, "username=\"%s\"", js->user->node); + g_string_append_printf(response, ",realm=\"%s\"", realm); + g_string_append_printf(response, ",nonce=\"%s\"", nonce); + g_string_append_printf(response, ",cnonce=\"%s\"", cnonce); + g_string_append_printf(response, ",nc=00000001"); + g_string_append_printf(response, ",qop=auth"); + g_string_append_printf(response, ",digest-uri=\"xmpp/%s\"", realm); + g_string_append_printf(response, ",response=%s", auth_resp); + g_string_append_printf(response, ",charset=utf-8"); - g_free(auth_resp); - g_free(cnonce); + g_free(auth_resp); + g_free(cnonce); - enc_out = purple_base64_encode((guchar *)response->str, response->len); + enc_out = purple_base64_encode((guchar *)response->str, response->len); - purple_debug(PURPLE_DEBUG_MISC, "jabber", "decoded response (%d): %s\n", response->len, response->str); + purple_debug(PURPLE_DEBUG_MISC, "jabber", "decoded response (%d): %s\n", response->len, response->str); - buf = g_strdup_printf("%s", enc_out); + buf = g_strdup_printf("%s", enc_out); - jabber_send_raw(js, buf, -1); + jabber_send_raw(js, buf, -1); - g_free(buf); + g_free(buf); - g_free(enc_out); + g_free(enc_out); - g_string_free(response, TRUE); + g_string_free(response, TRUE); + } } g_free(enc_in); diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Wed May 23 23:17:50 2007 +0000 @@ -73,6 +73,15 @@ } MsnGetInfoStepTwoData; +typedef struct +{ + PurpleConnection *gc; + const char *who; + char *msg; + PurpleMessageFlags flags; + time_t when; +} MsnIMData; + static const char * msn_normalize(const PurpleAccount *account, const char *str) { @@ -409,33 +418,7 @@ static void t_msn_xfer_init(PurpleXfer *xfer) { - MsnSlpLink *slplink; - const char *filename; - FILE *fp; - - filename = purple_xfer_get_local_filename(xfer); - - slplink = xfer->data; - - if ((fp = g_fopen(filename, "rb")) == NULL) - { - PurpleAccount *account; - const char *who; - char *msg; - - account = slplink->session->account; - who = slplink->remote_user; - - msg = g_strdup_printf(_("Error reading %s: \n%s.\n"), - filename, strerror(errno)); - purple_xfer_error(purple_xfer_get_type(xfer), account, xfer->who, msg); - purple_xfer_cancel_local(xfer); - g_free(msg); - - return; - } - fclose(fp); - + MsnSlpLink *slplink = xfer->data; msn_slplink_request_ft(slplink, xfer); } @@ -636,7 +619,8 @@ account = purple_connection_get_account(gc); user = msn_normalize(account, purple_account_get_username(account)); - if (strstr(user, "@hotmail.com") != NULL) + if ((strstr(user, "@hotmail.") != NULL) || + (strstr(user, "@msn.com") != NULL)) { m = g_list_append(m, NULL); act = purple_plugin_action_new(_("Open Hotmail Inbox"), @@ -753,6 +737,16 @@ gc->proto_data = NULL; } +static gboolean +msn_send_me_im(gpointer data) +{ + MsnIMData *imdata = data; + serv_got_im(imdata->gc, imdata->who, imdata->msg, imdata->flags, imdata->when); + g_free(imdata->msg); + g_free(imdata); + return FALSE; +} + static int msn_send_im(PurpleConnection *gc, const char *who, const char *message, PurpleMessageFlags flags) @@ -805,6 +799,7 @@ { 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 ;) @@ -822,8 +817,12 @@ g_free(post); serv_got_typing_stopped(gc, who); - serv_got_im(gc, who, body_str, flags, time(NULL)); - g_free(body_str); + 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); @@ -1298,7 +1297,12 @@ conv = swboard->conv; - msn_switchboard_release(swboard, MSN_SB_FLAG_IM); + /* If we release the switchboard here, it may still have messages + pending ACK which would result in incorrect unsent message errors. + Just let it timeout... This is *so* going to screw with people who + use dumb clients that report "User has closed the conversation window" */ + /* msn_switchboard_release(swboard, MSN_SB_FLAG_IM); */ + swboard->conv = NULL; /* If other switchboards managed to associate themselves with this * conv, make sure they know it's gone! */ diff -r 6ec8bae7d98a -r cb5bd3c045a9 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Wed May 23 23:13:11 2007 +0000 +++ b/libpurple/protocols/msn/notification.c Wed May 23 23:17:50 2007 +0000 @@ -589,6 +589,23 @@ } static void +qng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + static int count = 0; + MsnSession *session = cmdproc->session; + + if (session->passport_info.file == NULL) + return; + + if (count++ < 26) + return; + + count = 0; + msn_cmdproc_send(cmdproc, "URL", "%s", "INBOX"); +} + + +static void fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSlpLink *slplink; @@ -982,7 +999,8 @@ } else { - fputs("\n" + fputs("\n" + "\n" "\n" "