Mercurial > pidgin.yaz
changeset 17750:3b3b6fd6714e
merge of '8364b50c7693c2d765b0e1f162f25a06a2bfe492'
and 'ef8c6f1f96c5a71b2e9285550d2f13eb576a5072'
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Wed, 06 Jun 2007 00:58:00 +0000 |
parents | fde3255cf28f (diff) 850550c53bbe (current diff) |
children | 33063a3940a8 |
files | libpurple/protocols/bonjour/dns_sd.c libpurple/protocols/bonjour/dns_sd.h pidgin/gtkimhtmltoolbar.c |
diffstat | 57 files changed, 1706 insertions(+), 936 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Wed Jun 06 00:57:17 2007 +0000 +++ b/COPYRIGHT Wed Jun 06 00:58:00 2007 +0000 @@ -8,6 +8,7 @@ Dave Ahlswede Manuel Amador Matt Amato +Elliott Sales de Andrade Geoffrey Antos Daniel Atallah Paul Aurich @@ -88,6 +89,7 @@ Jeramey Crawford Michael Culbertson Steven Danna +Chris Davies Martijn Dekker Vinicius Depizzol Philip Derrin @@ -264,6 +266,7 @@ Ted Percival Eduardo Pérez Matt Perry +Nathan Peterson Celso Pinto Joao Luís Marques Pinto Aleksander Piotrowski
--- a/ChangeLog Wed Jun 06 00:57:17 2007 +0000 +++ b/ChangeLog Wed Jun 06 00:58:00 2007 +0000 @@ -3,12 +3,16 @@ version 2.0.2 (??/??/????): Pidgin: * Added a custom conversation font option to preferences + * Fixed smiley ordering in the insert smiley popup to be more intuitive libpurple: * Moving an ICQ buddy from one group to another no longer re-requests authorization from that person (Rene Hausleitner) * Added nullprpl, an example protocol plugin (Ryan Barrett) * Fixed SOCKS5 bug which caused Jabber file receiving to fail + * Remove MSN's random "Authorization Failed" dialogs + * Fix MSN to correctly detect incorrect passwords and disable the account + * Get User Info on MSN is now more reliable & accurate Finch: * Auto account reconnecting
--- a/ChangeLog.win32 Wed Jun 06 00:57:17 2007 +0000 +++ b/ChangeLog.win32 Wed Jun 06 00:58:00 2007 +0000 @@ -1,4 +1,7 @@ -version 2.0.1 (??/??/????): +version 2.0.2 (??/??/????): + * Add Bonjour protocol support thanks to Chris Davies. This requires + Apple Bonjour for Windows from: + http://www.apple.com/support/downloads/bonjourforwindows.html version 2.0.0 (5/3/2007): * URI Handler support added via `pidgin.exe --protocolhandler=`
--- a/doc/funniest_home_convos.txt Wed Jun 06 00:57:17 2007 +0000 +++ b/doc/funniest_home_convos.txt Wed Jun 06 00:58:00 2007 +0000 @@ -471,4 +471,6 @@ 14:08 <elb> "... yes" 14:08 <elb> I mean, what do you say 14:08 <Robot101> elb: was their nick "idi"? - + +19:23 <-- elb has quit (K-lined) +
--- a/libpurple/conversation.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/conversation.c Wed Jun 06 00:58:00 2007 +0000 @@ -1535,20 +1535,22 @@ PurpleConvChatBuddyFlags flag = GPOINTER_TO_INT(fl->data); const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL); - if (!strcmp(chat->nick, purple_normalize(conv->account, user))) { - const char *alias2 = purple_account_get_alias(conv->account); - if (alias2 != NULL) - alias = alias2; - else - { - const char *display_name = purple_connection_get_display_name(gc); - if (display_name != NULL) - alias = display_name; + if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + if (!strcmp(chat->nick, purple_normalize(conv->account, user))) { + const char *alias2 = purple_account_get_alias(conv->account); + if (alias2 != NULL) + alias = alias2; + else + { + const char *display_name = purple_connection_get_display_name(gc); + if (display_name != NULL) + alias = display_name; + } + } else { + PurpleBuddy *buddy; + if ((buddy = purple_find_buddy(gc->account, user)) != NULL) + alias = purple_buddy_get_contact_alias(buddy); } - } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { - PurpleBuddy *buddy; - if ((buddy = purple_find_buddy(gc->account, user)) != NULL) - alias = purple_buddy_get_contact_alias(buddy); } quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(), @@ -1633,14 +1635,16 @@ /* Note this for later. */ is_me = TRUE; - alias = purple_account_get_alias(conv->account); - if (alias != NULL) - new_alias = alias; - else - { - const char *display_name = purple_connection_get_display_name(gc); - if (display_name != NULL) - alias = display_name; + if(!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { + alias = purple_account_get_alias(conv->account); + if (alias != NULL) + new_alias = alias; + else + { + const char *display_name = purple_connection_get_display_name(gc); + if (display_name != NULL) + alias = display_name; + } } } else if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { PurpleBuddy *buddy; @@ -1960,7 +1964,7 @@ for (l = purple_conv_chat_get_users(chat); l; l = l->next) { cb = l->data; - if (!purple_utf8_strcasecmp(cb->name, name)) + if (!g_utf8_collate(cb->name, name)) return cb; }
--- a/libpurple/idle.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/idle.c Wed Jun 06 00:58:00 2007 +0000 @@ -92,7 +92,7 @@ } -static int no_away = 0; +static gboolean no_away = FALSE; static gint time_until_next_idle_event; /* * This function should be called when you think your idle state @@ -118,15 +118,15 @@ time_t time_idle; gboolean auto_away; const gchar *idle_reporting; - gboolean report_idle; - GList *l; + gboolean report_idle = TRUE; gint away_seconds = 0; - gint idle_recheck_interval; + gint idle_recheck_interval = 0; purple_signal_emit(purple_blist_get_handle(), "update-idle"); idle_reporting = purple_prefs_get_string("/purple/away/idle_reporting"); - report_idle = TRUE; + auto_away = purple_prefs_get_bool("/purple/away/away_when_idle"); + if (!strcmp(idle_reporting, "system") && (idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL)) { @@ -145,15 +145,9 @@ /* Don't report idle time */ time_idle = 0; report_idle = FALSE; - } - /* Auto-away stuff */ - auto_away = purple_prefs_get_bool("/purple/away/away_when_idle"); - - /* 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) - { + /* If we're not reporting idle, we can still do auto-away. + * First try "system" and if that isn't possible, use "purple" */ if (auto_away) { if ((idle_ui_ops != NULL) && (idle_ui_ops->get_time_idle != NULL)) @@ -172,7 +166,7 @@ if (!no_away) { purple_savedstatus_set_idleaway(FALSE); - no_away = 1; + no_away = TRUE; } time_until_next_idle_event = 0; return; @@ -192,11 +186,11 @@ if (auto_away && time_idle > away_seconds) { purple_savedstatus_set_idleaway(TRUE); - no_away = 0; + no_away = FALSE; } else if (!no_away && time_idle < away_seconds) { - no_away = 1; + no_away = TRUE; 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; @@ -205,6 +199,7 @@ /* Idle reporting stuff */ if (report_idle && (time_idle >= IDLEMARK)) { + GList *l; for (l = purple_connections_get_all(); l != NULL; l = l->next) { PurpleConnection *gc = l->data;
--- a/libpurple/plugins/joinpart.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/plugins/joinpart.c Wed Jun 06 00:58:00 2007 +0000 @@ -210,7 +210,7 @@ * we don't have to worry one will be called after this. */ g_hash_table_destroy((GHashTable *)data[0]); - g_source_remove(GPOINTER_TO_UINT(data[1])); + purple_timeout_remove(GPOINTER_TO_UINT(data[1])); g_free(data); return TRUE;
--- a/libpurple/plugins/log_reader.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/plugins/log_reader.c Wed Jun 06 00:58:00 2007 +0000 @@ -1482,46 +1482,66 @@ * "> * Then, replace the next " " (or add this if the end-of-line is reached) with: * </a> + * + * As implemented, this isn't perfect, but it should cover common cases. */ link_temp_line = NULL; - while ((link = g_strstr_len(line, strlen(line), "(Link: "))) { - GString *temp; + while ((link = strstr(line, "(Link: "))) + { + char *tmp = link; - if (!*link) - continue; + link += 7; + if (*link) + { + char *end_paren; + char *space; + GString *temp; - *link = '\0'; - link++; + if (!(end_paren = strstr(link, ")"))) + { + /* Something is not as we expect. Bail out. */ + break; + } - temp = g_string_new(line); - g_string_append(temp, "<a href=\""); + *tmp = '\0'; + temp = g_string_new(line); + + /* Start an <a> tag. */ + g_string_append(temp, "<a href=\""); + + /* Append up to the ) */ + g_string_append_len(temp, link, end_paren - link); - if (strlen(link) >= 6) { - link += (sizeof("(Link: ") - 1); + /* Finish the <a> tag. */ + g_string_append(temp, "\">"); + + /* The \r is a bit of a hack to keep there from being a \r in + * the link text, which may not matter. */ + if ((space = strstr(end_paren, " ")) || (space = strstr(end_paren, "\r"))) + { + g_string_append_len(temp, end_paren + 1, space - end_paren - 1); - while (*link && *link != ')') { - g_string_append_c(temp, *link); - link++; + /* Close the <a> tag. */ + g_string_append(temp, "</a>"); + + space++; + if (*space) + { + g_string_append_c(temp, ' '); + /* Keep the rest of the line. */ + g_string_append(temp, space); + } } - if (link) { - link++; - - g_string_append(temp, "\">"); - while (*link && *link != ' ') { - g_string_append_c(temp, *link); - link++; - } + else + { + /* There is no space before the end of the line. */ + g_string_append(temp, end_paren + 1); + /* Close the <a> tag. */ g_string_append(temp, "</a>"); } - g_string_append(temp, link); - - /* Free the last round's line. */ - if (link_temp_line) - g_free(line); - - line = temp->str; - g_string_free(temp, FALSE); + g_free(link_temp_line); + line = g_string_free(temp, FALSE); /* Save this memory location so we can free it later. */ link_temp_line = line; @@ -1661,8 +1681,7 @@ g_string_append_c(formatted, '\n'); - if (link_temp_line) - g_free(link_temp_line); + g_free(link_temp_line); c++; line = c;
--- a/libpurple/plugins/perl/perl-handlers.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/plugins/perl/perl-handlers.c Wed Jun 06 00:58:00 2007 +0000 @@ -184,7 +184,7 @@ timeout_handlers = g_list_remove(timeout_handlers, handler); if (handler->iotag > 0) - g_source_remove(handler->iotag); + purple_timeout_remove(handler->iotag); if (handler->callback != NULL) SvREFCNT_dec(handler->callback); @@ -405,7 +405,7 @@ timeout_handlers = g_list_append(timeout_handlers, handler); - handler->iotag = g_timeout_add(seconds * 1000, perl_timeout_cb, handler); + handler->iotag = purple_timeout_add(seconds * 1000, perl_timeout_cb, handler); } void
--- a/libpurple/protocols/Makefile.am Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/Makefile.am Wed Jun 06 00:58:00 2007 +0000 @@ -1,4 +1,4 @@ -EXTRA_DIST = Makefile.mingw null/ +EXTRA_DIST = Makefile.mingw DIST_SUBDIRS = bonjour gg irc jabber msn novell null oscar qq sametime silc toc simple yahoo zephyr
--- a/libpurple/protocols/Makefile.mingw Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/Makefile.mingw Wed Jun 06 00:58:00 2007 +0000 @@ -8,7 +8,7 @@ PIDGIN_TREE_TOP := ../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak -SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo +SUBDIRS = gg irc jabber msn novell null oscar qq sametime silc simple yahoo bonjour .PHONY: all install clean
--- a/libpurple/protocols/bonjour/Makefile.am Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/Makefile.am Wed Jun 06 00:58:00 2007 +0000 @@ -1,4 +1,6 @@ EXTRA_DIST = \ + mdns_win32.c \ + mdns_win32.h \ Makefile.mingw pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -8,12 +10,16 @@ bonjour.h \ buddy.c \ buddy.h \ - dns_sd.c \ - dns_sd.h \ + dns_sd_proxy.h \ jabber.c \ - jabber.h + jabber.h \ + mdns_common.c \ + mdns_common.h \ + mdns_howl.c \ + mdns_howl.h \ + mdns_types.h -AM_CFLAGS = $(st) +AM_CFLAGS = $(st) -DUSE_BONJOUR_HOWL libbonjour_la_LDFLAGS = -module -avoid-version
--- a/libpurple/protocols/bonjour/Makefile.mingw Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/Makefile.mingw Wed Jun 06 00:58:00 2007 +0000 @@ -8,7 +8,6 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = libbonjour -NEEDED_DLLS = $(HOWL_TOP)/bin/libhowl-1.dll TYPE = PLUGIN # Static or Plugin... @@ -21,20 +20,22 @@ endif endif +CFLAGS += -DUSE_BONJOUR_APPLE + ## ## INCLUDE PATHS ## -INCLUDE_PATHS += -I$(BONJOUR_ROOT) \ +INCLUDE_PATHS += -I. \ -I$(GTK_TOP)/include \ -I$(GTK_TOP)/include/glib-2.0 \ -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(HOWL_TOP)/include \ + -I$(BONJOUR_TOP)/include \ -I$(PURPLE_TOP) \ -I$(PURPLE_TOP)/win32 \ -I$(PIDGIN_TREE_TOP) LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(HOWL_TOP)/lib \ + -L$(BONJOUR_TOP)/lib \ -L$(PURPLE_TOP) ## @@ -42,7 +43,8 @@ ## C_SRC = bonjour.c \ buddy.c \ - dns_sd.c \ + mdns_common.c \ + mdns_win32.c \ jabber.c OBJECTS = $(C_SRC:%.c=%.o) @@ -54,7 +56,7 @@ -lglib-2.0 \ -lws2_32 \ -lintl \ - -lhowl \ + -ldnssd \ -lpurple include $(PIDGIN_COMMON_RULES) @@ -68,7 +70,6 @@ install: all $(DLL_INSTALL_DIR) cp $(TARGET).dll $(DLL_INSTALL_DIR) - cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR) $(OBJECTS): $(PURPLE_CONFIG_H)
--- a/libpurple/protocols/bonjour/bonjour.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Wed Jun 06 00:58:00 2007 +0000 @@ -37,7 +37,7 @@ #include "version.h" #include "bonjour.h" -#include "dns_sd.h" +#include "mdns_common.h" #include "jabber.h" #include "buddy.h" @@ -120,17 +120,11 @@ /* Connect to the mDNS daemon looking for buddies in the LAN */ bd->dns_sd_data = bonjour_dns_sd_new(); - bd->dns_sd_data->name = (sw_string)purple_account_get_username(account); - bd->dns_sd_data->txtvers = g_strdup("1"); - bd->dns_sd_data->version = g_strdup("1"); bd->dns_sd_data->first = g_strdup(purple_account_get_string(account, "first", default_firstname)); bd->dns_sd_data->last = g_strdup(purple_account_get_string(account, "last", default_lastname)); bd->dns_sd_data->port_p2pj = bd->jabber_data->port; - bd->dns_sd_data->phsh = g_strdup(""); - bd->dns_sd_data->email = g_strdup(purple_account_get_string(account, "email", "")); - bd->dns_sd_data->vc = g_strdup(""); - bd->dns_sd_data->jid = g_strdup(purple_account_get_string(account, "jid", "")); - bd->dns_sd_data->AIM = g_strdup(purple_account_get_string(account, "AIM", "")); + /* Not engaged in AV conference */ + bd->dns_sd_data->vc = g_strdup("!"); status = purple_account_get_active_status(account); presence = purple_account_get_presence(account); @@ -491,8 +485,8 @@ LPUSER_INFO_10 user_info = NULL; LPSERVER_INFO_100 server_info = NULL; wchar_t *servername = NULL; - wchar_t username[UNLEN + 1] = {'\0'}; - DWORD dwLenUsername = sizeof(username); + wchar_t username[UNLEN + 1]; + DWORD dwLenUsername = UNLEN + 1; FARPROC myNetServerEnum = wpurple_find_and_loadproc( "Netapi32.dll", "NetServerEnum"); FARPROC myNetApiBufferFree = wpurple_find_and_loadproc( @@ -517,7 +511,7 @@ } } - if (!GetUserNameW(&username, &dwLenUsername)) { + if (!GetUserNameW((LPWSTR) &username, &dwLenUsername)) { purple_debug_warning("bonjour", "Unable to look up username\n"); } @@ -553,7 +547,7 @@ */ splitpoint = strchr(tmp, ','); if (splitpoint != NULL) - default_lastname = g_strndup(tmp, splitpoint - tmp); + default_lastname = g_strndup(tmp, splitpoint - tmp); else default_lastname = g_strdup(tmp); }
--- a/libpurple/protocols/bonjour/bonjour.h Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/bonjour.h Wed Jun 06 00:58:00 2007 +0000 @@ -26,9 +26,7 @@ #ifndef _BONJOUR_H_ #define _BONJOUR_H_ -#include <howl.h> - -#include "dns_sd.h" +#include "mdns_common.h" #include "internal.h" #include "jabber.h"
--- a/libpurple/protocols/bonjour/buddy.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.c Wed Jun 06 00:58:00 2007 +0000 @@ -27,51 +27,66 @@ * Creates a new buddy. */ BonjourBuddy * -bonjour_buddy_new(const gchar *name, const gchar *first, gint port_p2pj, - const gchar *phsh, const gchar *status, const gchar *email, - const gchar *last, const gchar *jid, const gchar *AIM, - const gchar *vc, const gchar *ip, const gchar *msg) +bonjour_buddy_new(const gchar *name, PurpleAccount* account) { - BonjourBuddy *buddy = malloc(sizeof(BonjourBuddy)); + BonjourBuddy *buddy = g_new0(BonjourBuddy, 1); + buddy->account = account; buddy->name = g_strdup(name); - buddy->first = g_strdup(first); - buddy->port_p2pj = port_p2pj; - buddy->phsh = g_strdup(phsh); - buddy->status = g_strdup(status); - buddy->email = g_strdup(email); - buddy->last = g_strdup(last); - buddy->jid = g_strdup(jid); - buddy->AIM = g_strdup(AIM); - buddy->vc = g_strdup(vc); - buddy->ip = g_strdup(ip); - buddy->msg = g_strdup(msg); - buddy->conversation = NULL; return buddy; } +void +set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, uint32_t len){ + gchar **fld = NULL; + + if (!strcmp(record_key, "1st")) + fld = &buddy->first; + else if(!strcmp(record_key, "email")) + fld = &buddy->email; + else if(!strcmp(record_key, "ext")) + fld = &buddy->ext; + else if(!strcmp(record_key, "jid")) + fld = &buddy->jid; + else if(!strcmp(record_key, "last")) + fld = &buddy->last; + else if(!strcmp(record_key, "msg")) + fld = &buddy->msg; + else if(!strcmp(record_key, "nick")) + fld = &buddy->nick; + else if(!strcmp(record_key, "node")) + fld = &buddy->node; + else if(!strcmp(record_key, "phsh")) + fld = &buddy->phsh; + else if(!strcmp(record_key, "status")) + fld = &buddy->status; + else if(!strcmp(record_key, "vc")) + fld = &buddy->vc; + else if(!strcmp(record_key, "ver")) + fld = &buddy->ver; + else if(!strcmp(record_key, "AIM")) + fld = &buddy->AIM; + + if(fld == NULL) + return; + + g_free(*fld); + *fld = NULL; + *fld = g_strndup(value, len); +} + /** * Check if all the compulsory buddy data is present. */ gboolean bonjour_buddy_check(BonjourBuddy *buddy) { - if (buddy->name == NULL) { + if (buddy->account == NULL) return FALSE; - } - - if (buddy->first == NULL) { - return FALSE; - } - if (buddy->last == NULL) { + if (buddy->name == NULL) return FALSE; - } - - if (buddy->status == NULL) { - return FALSE; - } return TRUE; } @@ -82,12 +97,12 @@ * the buddy. */ void -bonjour_buddy_add_to_purple(PurpleAccount *account, BonjourBuddy *bonjour_buddy) +bonjour_buddy_add_to_purple(BonjourBuddy *bonjour_buddy) { PurpleBuddy *buddy; PurpleGroup *group; const char *status_id, *first, *last; - char *alias; + gchar *alias; /* Translate between the Bonjour status and the Purple status */ if (g_ascii_strcasecmp("dnd", bonjour_buddy->status) == 0) @@ -117,10 +132,11 @@ } /* Make sure the buddy exists in our buddy list */ - buddy = purple_find_buddy(account, bonjour_buddy->name); + buddy = purple_find_buddy(bonjour_buddy->account, bonjour_buddy->name); + if (buddy == NULL) { - buddy = purple_buddy_new(account, bonjour_buddy->name, alias); + buddy = purple_buddy_new(bonjour_buddy->account, bonjour_buddy->name, alias); buddy->proto_data = bonjour_buddy; purple_blist_node_set_flags((PurpleBlistNode *)buddy, PURPLE_BLIST_NODE_FLAG_NO_SAVE); purple_blist_add_buddy(buddy, NULL, group, NULL); @@ -128,13 +144,13 @@ /* Set the user's status */ if (bonjour_buddy->msg != NULL) - purple_prpl_got_user_status(account, buddy->name, status_id, + purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id, "message", bonjour_buddy->msg, NULL); else - purple_prpl_got_user_status(account, buddy->name, status_id, + purple_prpl_got_user_status(bonjour_buddy->account, buddy->name, status_id, NULL); - purple_prpl_got_user_idle(account, buddy->name, FALSE, 0); + purple_prpl_got_user_idle(bonjour_buddy->account, buddy->name, FALSE, 0); g_free(alias); } @@ -163,5 +179,13 @@ g_free(buddy->conversation); } - free(buddy); +#ifdef USE_BONJOUR_APPLE + if (buddy->txt_query != NULL) + { + purple_input_remove(buddy->txt_query_fd); + DNSServiceRefDeallocate(buddy->txt_query); + } +#endif + + g_free(buddy); }
--- a/libpurple/protocols/bonjour/buddy.h Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/buddy.h Wed Jun 06 00:58:00 2007 +0000 @@ -17,17 +17,27 @@ #ifndef _BONJOUR_BUDDY #define _BONJOUR_BUDDY -#include <howl.h> #include <glib.h> +#include "config.h" #include "account.h" #include "jabber.h" +#ifdef USE_BONJOUR_APPLE +#include "dns_sd_proxy.h" +#else /* USE_BONJOUR_HOWL */ +#include <howl.h> +#endif + typedef struct _BonjourBuddy { + PurpleAccount *account; + gchar *name; + gchar *ip; + gint port_p2pj; + gchar *first; - gint port_p2pj; gchar *phsh; gchar *status; gchar *email; @@ -35,18 +45,49 @@ gchar *jid; gchar *AIM; gchar *vc; - gchar *ip; gchar *msg; + gchar *ext; + gchar *nick; + gchar *node; + gchar *ver; + BonjourJabberConversation *conversation; + +#ifdef USE_BONJOUR_APPLE + DNSServiceRef txt_query; + int txt_query_fd; +#endif + } BonjourBuddy; +static const char *const buddy_TXT_records[] = { + "1st", + "email", + "ext", + "jid", + "last", + "msg", + "nick", + "node", + "phsh", +/* "port.p2pj", Deprecated - MUST ignore */ + "status", +/* "txtvers", Deprecated - hardcoded to 1 */ + "vc", + "ver", + "AIM", /* non standard */ + NULL +}; + /** * Creates a new buddy. */ -BonjourBuddy *bonjour_buddy_new(const gchar *name, const gchar *first, - gint port_p2pj, const gchar *phsh, const gchar *status, - const gchar *email, const gchar *last, const gchar *jid, - const gchar *AIM, const gchar *vc, const gchar *ip, const gchar *msg); +BonjourBuddy *bonjour_buddy_new(const gchar *name, PurpleAccount *account); + +/** + * Sets a value in the BonjourBuddy struct, destroying the old value + */ +void set_bonjour_buddy_value(BonjourBuddy* buddy, const char *record_key, const char *value, uint32_t len); /** * Check if all the compulsory buddy data is present. @@ -56,7 +97,7 @@ /** * If the buddy doesn't previoulsy exists, it is created. Else, its data is changed (???) */ -void bonjour_buddy_add_to_purple(PurpleAccount *account, BonjourBuddy *buddy); +void bonjour_buddy_add_to_purple(BonjourBuddy *buddy); /** * Deletes a buddy from memory.
--- a/libpurple/protocols/bonjour/dns_sd.c Wed Jun 06 00:57:17 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,387 +0,0 @@ -/* - * 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 Library 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 <string.h> - -#include "dns_sd.h" -#include "bonjour.h" -#include "buddy.h" -#include "debug.h" - -/* Private functions */ - -static sw_result HOWL_API -_publish_reply(sw_discovery discovery, sw_discovery_oid oid, - sw_discovery_publish_status status, sw_opaque extra) -{ - purple_debug_warning("bonjour", "_publish_reply --> Start\n"); - - /* Check the answer from the mDNS daemon */ - switch (status) - { - case SW_DISCOVERY_PUBLISH_STARTED : - purple_debug_info("bonjour", "_publish_reply --> Service started\n"); - break; - case SW_DISCOVERY_PUBLISH_STOPPED : - purple_debug_info("bonjour", "_publish_reply --> Service stopped\n"); - break; - case SW_DISCOVERY_PUBLISH_NAME_COLLISION : - purple_debug_info("bonjour", "_publish_reply --> Name collision\n"); - break; - case SW_DISCOVERY_PUBLISH_INVALID : - purple_debug_info("bonjour", "_publish_reply --> Service invalid\n"); - break; - } - - return SW_OKAY; -} - -static sw_result HOWL_API -_resolve_reply(sw_discovery discovery, sw_discovery_oid oid, - sw_uint32 interface_index, sw_const_string name, - sw_const_string type, sw_const_string domain, - sw_ipv4_address address, sw_port port, - sw_octets text_record, sw_ulong text_record_len, - sw_opaque extra) -{ - BonjourBuddy *buddy; - PurpleAccount *account = (PurpleAccount*)extra; - gchar *txtvers = NULL; - gchar *version = NULL; - gchar *first = NULL; - gchar *phsh = NULL; - gchar *status = NULL; - gchar *email = NULL; - gchar *last = NULL; - gchar *jid = NULL; - gchar *AIM = NULL; - gchar *vc = NULL; - gchar *msg = NULL; - gint address_length = 16; - gchar *ip = NULL; - sw_text_record_iterator iterator; - char key[SW_TEXT_RECORD_MAX_LEN]; - char value[SW_TEXT_RECORD_MAX_LEN]; - sw_uint32 value_length; - - sw_discovery_cancel(discovery, oid); - - /* Get the ip as a string */ - ip = malloc(address_length); - sw_ipv4_address_name(address, ip, address_length); - - /* Obtain the parameters from the text_record */ - if ((text_record_len > 0) && (text_record) && (*text_record != '\0')) - { - sw_text_record_iterator_init(&iterator, text_record, text_record_len); - while (sw_text_record_iterator_next(iterator, key, (sw_octet *)value, &value_length) == SW_OKAY) - { - /* Compare the keys with the possible ones and save them on */ - /* the appropiate place of the buddy_list */ - if (strcmp(key, "txtvers") == 0) { - txtvers = g_strdup(value); - } else if (strcmp(key, "version") == 0) { - version = g_strdup(value); - } else if (strcmp(key, "1st") == 0) { - first = g_strdup(value); - } else if (strcmp(key, "status") == 0) { - status = g_strdup(value); - } else if (strcmp(key, "email") == 0) { - email = g_strdup(value); - } else if (strcmp(key, "last") == 0) { - last = g_strdup(value); - } else if (strcmp(key, "jid") == 0) { - jid = g_strdup(value); - } else if (strcmp(key, "AIM") == 0) { - AIM = g_strdup(value); - } else if (strcmp(key, "vc") == 0) { - vc = g_strdup(value); - } else if (strcmp(key, "phsh") == 0) { - phsh = g_strdup(value); - } else if (strcmp(key, "msg") == 0) { - msg = g_strdup(value); - } - } - } - - /* Put the parameters of the text_record in a buddy and add the buddy to */ - /* the buddy list */ - buddy = bonjour_buddy_new(name, first, port, phsh, - status, email, last, jid, AIM, vc, ip, msg); - - if (bonjour_buddy_check(buddy) == FALSE) - { - bonjour_buddy_delete(buddy); - return SW_DISCOVERY_E_UNKNOWN; - } - - /* Add or update the buddy in our buddy list */ - bonjour_buddy_add_to_purple(account, buddy); - - /* Free all the temporal strings */ - g_free(txtvers); - g_free(version); - g_free(first); - g_free(last); - g_free(status); - g_free(email); - g_free(jid); - g_free(AIM); - g_free(vc); - g_free(phsh); - g_free(msg); - - return SW_OKAY; -} - -static sw_result HOWL_API -_browser_reply(sw_discovery discovery, sw_discovery_oid oid, - sw_discovery_browse_status status, - sw_uint32 interface_index, sw_const_string name, - sw_const_string type, sw_const_string domain, - sw_opaque_t extra) -{ - sw_discovery_resolve_id rid; - PurpleAccount *account = (PurpleAccount*)extra; - PurpleBuddy *gb = NULL; - - switch (status) - { - case SW_DISCOVERY_BROWSE_INVALID: - purple_debug_warning("bonjour", "_browser_reply --> Invalid\n"); - break; - case SW_DISCOVERY_BROWSE_RELEASE: - purple_debug_warning("bonjour", "_browser_reply --> Release\n"); - break; - case SW_DISCOVERY_BROWSE_ADD_DOMAIN: - purple_debug_warning("bonjour", "_browser_reply --> Add domain\n"); - break; - case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN: - purple_debug_warning("bonjour", "_browser_reply --> Add default domain\n"); - break; - case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN: - purple_debug_warning("bonjour", "_browser_reply --> Remove domain\n"); - break; - case SW_DISCOVERY_BROWSE_ADD_SERVICE: - /* A new peer has joined the network and uses iChat bonjour */ - purple_debug_info("bonjour", "_browser_reply --> Add service\n"); - if (g_ascii_strcasecmp(name, account->username) != 0) - { - if (sw_discovery_resolve(discovery, interface_index, name, type, - domain, _resolve_reply, extra, &rid) != SW_OKAY) - { - purple_debug_warning("bonjour", "_browser_reply --> Cannot send resolve\n"); - } - } - break; - case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: - purple_debug_info("bonjour", "_browser_reply --> Remove service\n"); - gb = purple_find_buddy((PurpleAccount*)extra, name); - if (gb != NULL) - { - bonjour_buddy_delete(gb->proto_data); - purple_blist_remove_buddy(gb); - } - break; - case SW_DISCOVERY_BROWSE_RESOLVED: - purple_debug_info("bonjour", "_browse_reply --> Resolved\n"); - break; - default: - break; - } - - return SW_OKAY; -} - -static int -_dns_sd_publish(BonjourDnsSd *data, PublishType type) -{ - sw_text_record dns_data; - sw_result publish_result = SW_OKAY; - char portstring[6]; - - /* Fill the data for the service */ - if (sw_text_record_init(&dns_data) != SW_OKAY) - { - purple_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n"); - return -1; - } - - /* Convert the port to a string */ - snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj); - - /* Publish standard records */ - sw_text_record_add_key_and_string_value(dns_data, "txtvers", data->txtvers); - sw_text_record_add_key_and_string_value(dns_data, "version", data->version); - sw_text_record_add_key_and_string_value(dns_data, "1st", data->first); - sw_text_record_add_key_and_string_value(dns_data, "last", data->last); - sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", portstring); - sw_text_record_add_key_and_string_value(dns_data, "phsh", data->phsh); - sw_text_record_add_key_and_string_value(dns_data, "status", data->status); - sw_text_record_add_key_and_string_value(dns_data, "vc", data->vc); - - /* Publish extra records */ - if ((data->email != NULL) && (*data->email != '\0')) - sw_text_record_add_key_and_string_value(dns_data, "email", data->email); - - if ((data->jid != NULL) && (*data->jid != '\0')) - sw_text_record_add_key_and_string_value(dns_data, "jid", data->jid); - - if ((data->AIM != NULL) && (*data->AIM != '\0')) - sw_text_record_add_key_and_string_value(dns_data, "AIM", data->AIM); - - if ((data->msg != NULL) && (*data->msg != '\0')) - sw_text_record_add_key_and_string_value(dns_data, "msg", data->msg); - - /* Publish the service */ - switch (type) - { - case PUBLISH_START: - publish_result = sw_discovery_publish(data->session, 0, data->name, ICHAT_SERVICE, NULL, - NULL, data->port_p2pj, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data), - _publish_reply, NULL, &data->session_id); - break; - case PUBLISH_UPDATE: - publish_result = sw_discovery_publish_update(data->session, data->session_id, - sw_text_record_bytes(dns_data), sw_text_record_len(dns_data)); - break; - } - if (publish_result != SW_OKAY) - { - purple_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service.\n"); - return -1; - } - - /* Free the memory used by temp data */ - sw_text_record_fina(dns_data); - - return 0; -} - -static void -_dns_sd_handle_packets(gpointer data, gint source, PurpleInputCondition condition) -{ - sw_discovery_read_socket((sw_discovery)data); -} - -/* End private functions */ - -/** - * Allocate space for the dns-sd data. - */ -BonjourDnsSd * -bonjour_dns_sd_new() -{ - BonjourDnsSd *data = g_new0(BonjourDnsSd, 1); - - return data; -} - -/** - * Deallocate the space of the dns-sd data. - */ -void -bonjour_dns_sd_free(BonjourDnsSd *data) -{ - g_free(data->first); - g_free(data->last); - g_free(data->email); - g_free(data); -} - -/** - * Send a new dns-sd packet updating our status. - */ -void -bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message) -{ - g_free(data->status); - g_free(data->msg); - - data->status = g_strdup(status); - data->msg = g_strdup(status_message); - - /* Update our text record with the new status */ - _dns_sd_publish(data, PUBLISH_UPDATE); /* <--We must control the errors */ -} - -/** - * Advertise our presence within the dns-sd daemon and start browsing - * for other bonjour peers. - */ -gboolean -bonjour_dns_sd_start(BonjourDnsSd *data) -{ - PurpleAccount *account; - PurpleConnection *gc; - gint dns_sd_socket; - sw_discovery_oid session_id; - - account = data->account; - gc = purple_account_get_connection(account); - - /* Initialize the dns-sd data and session */ - if (sw_discovery_init(&data->session) != SW_OKAY) - { - purple_debug_error("bonjour", "Unable to initialize an mDNS session.\n"); - - /* In Avahi, sw_discovery_init frees data->session but doesn't clear it */ - data->session = NULL; - - return FALSE; - } - - /* Publish our bonjour IM client at the mDNS daemon */ - _dns_sd_publish(data, PUBLISH_START); /* <--We must control the errors */ - - /* Advise the daemon that we are waiting for connections */ - if (sw_discovery_browse(data->session, 0, ICHAT_SERVICE, NULL, _browser_reply, - data->account, &session_id) != SW_OKAY) - { - purple_debug_error("bonjour", "Unable to get service."); - return FALSE; - } - - /* Get the socket that communicates with the mDNS daemon and bind it to a */ - /* callback that will handle the dns_sd packets */ - dns_sd_socket = sw_discovery_socket(data->session); - gc->inpa = purple_input_add(dns_sd_socket, PURPLE_INPUT_READ, - _dns_sd_handle_packets, data->session); - - return TRUE; -} - -/** - * Unregister the "_presence._tcp" service at the mDNS daemon. - */ -void -bonjour_dns_sd_stop(BonjourDnsSd *data) -{ - PurpleAccount *account; - PurpleConnection *gc; - - if (data->session == NULL) - return; - - sw_discovery_cancel(data->session, data->session_id); - - account = data->account; - gc = purple_account_get_connection(account); - purple_input_remove(gc->inpa); - - g_free(data->session); - data->session = NULL; -}
--- a/libpurple/protocols/bonjour/dns_sd.h Wed Jun 06 00:57:17 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * 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 Library 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 _BONJOUR_DNS_SD -#define _BONJOUR_DNS_SD - -#include <howl.h> -#include <glib.h> -#include "account.h" - -#define ICHAT_SERVICE "_presence._tcp." - -/** - * Data to be used by the dns-sd connection. - */ -typedef struct _BonjourDnsSd -{ - sw_discovery session; - sw_discovery_oid session_id; - PurpleAccount *account; - gchar *name; - gchar *txtvers; - gchar *version; - gchar *first; - gchar *last; - gint port_p2pj; - gchar *phsh; - gchar *status; - gchar *email; - gchar *vc; - gchar *jid; - gchar *AIM; - gchar *msg; - GHashTable *buddies; -} BonjourDnsSd; - -typedef enum _PublishType { - PUBLISH_START, - PUBLISH_UPDATE -} PublishType; - -/** - * Allocate space for the dns-sd data. - */ -BonjourDnsSd *bonjour_dns_sd_new(void); - -/** - * Deallocate the space of the dns-sd data. - */ -void bonjour_dns_sd_free(BonjourDnsSd *data); - -/** - * Send a new dns-sd packet updating our status. - */ -void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message); - -/** - * Advertise our presence within the dns-sd daemon and start - * browsing for other bonjour peers. - */ -gboolean bonjour_dns_sd_start(BonjourDnsSd *data); - -/** - * Unregister the "_presence._tcp" service at the mDNS daemon. - */ -void bonjour_dns_sd_stop(BonjourDnsSd *data); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/dns_sd_proxy.h Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,19 @@ +#ifndef _DNS_SD_PROXY +#define _DNS_SD_PROXY + +#include <stdint.h> + +/* fixup to make pidgin compile against win32 bonjour */ +#ifdef _WIN32 +#define _MSL_STDINT_H +#undef bzero +#endif + +#include <dns_sd.h> + +/* dns_sd.h defines bzero and we also do in libc_internal.h */ +#ifdef _WIN32 +#undef bzero +#endif + +#endif
--- a/libpurple/protocols/bonjour/issues.txt Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/issues.txt Wed Jun 06 00:58:00 2007 +0000 @@ -6,4 +6,3 @@ * Avatars * File transfers * Typing notifications -* Check if it works on win32
--- a/libpurple/protocols/bonjour/jabber.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Wed Jun 06 00:58:00 2007 +0000 @@ -49,22 +49,32 @@ _connect_to_buddy(PurpleBuddy *gb) { gint socket_fd; - gint retorno = 0; struct sockaddr_in buddy_address; + BonjourBuddy *bb = gb->proto_data; + + purple_debug_info("bonjour", "Connecting to buddy %s at %s:%d.\n", + purple_buddy_get_name(gb), bb->ip ? bb->ip : "(null)", bb->port_p2pj); /* Create a socket and make it non-blocking */ socket_fd = socket(PF_INET, SOCK_STREAM, 0); + if (socket_fd < 0) { + purple_debug_warning("bonjour", "Error opening socket: %s\n", strerror(errno)); + return -1; + } buddy_address.sin_family = PF_INET; - buddy_address.sin_port = htons(((BonjourBuddy*)(gb->proto_data))->port_p2pj); - inet_aton(((BonjourBuddy*)(gb->proto_data))->ip, &(buddy_address.sin_addr)); + buddy_address.sin_port = htons(bb->port_p2pj); + inet_aton(bb->ip, &(buddy_address.sin_addr)); memset(&(buddy_address.sin_zero), '\0', 8); - retorno = connect(socket_fd, (struct sockaddr*)&buddy_address, sizeof(struct sockaddr)); - if (retorno == -1) { - purple_debug_warning("bonjour", "connect error: %s\n", strerror(errno)); + /* TODO: make this nonblocking before connecting */ + if (connect(socket_fd, (struct sockaddr*)&buddy_address, sizeof(struct sockaddr)) == 0) + fcntl(socket_fd, F_SETFL, O_NONBLOCK); + else { + purple_debug_warning("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n", purple_buddy_get_name(gb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, strerror(errno)); + close(socket_fd); + socket_fd = -1; } - fcntl(socket_fd, F_SETFL, O_NONBLOCK); return socket_fd; } @@ -258,8 +268,8 @@ /* Read chunks of 512 bytes till the end of the data */ while ((partial_message_length = recv(socket, partial_data, 512, 0)) > 0) { - g_string_append_len(data, partial_data, partial_message_length); - total_message_length += partial_message_length; + g_string_append_len(data, partial_data, partial_message_length); + total_message_length += partial_message_length; } if (partial_message_length == -1) @@ -440,7 +450,7 @@ /* Open a watcher for the client socket */ bb->conversation->watcher_id = purple_input_add(client_socket, PURPLE_INPUT_READ, - _client_socket_handler, gb); + _client_socket_handler, gb); } else { close(client_socket); } @@ -537,12 +547,29 @@ gint ret; gb = purple_find_buddy(data->account, to); - if (gb == NULL) + if (gb == NULL) { + purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to); /* You can not send a message to an offline buddy */ return -10000; + } bb = (BonjourBuddy *)gb->proto_data; + /* Check if there is a previously open conversation */ + if (bb->conversation == NULL) + { + int socket = _connect_to_buddy(gb); + if (socket < 0) + return -10001; + + bb->conversation = g_new(BonjourJabberConversation, 1); + bb->conversation->socket = socket; + bb->conversation->stream_started = FALSE; + bb->conversation->buddy_name = g_strdup(gb->name); + bb->conversation->watcher_id = purple_input_add(bb->conversation->socket, + PURPLE_INPUT_READ, _client_socket_handler, gb); + } + /* Enclose the message from the UI within a "font" node */ message_body_node = xmlnode_new("body"); stripped_message = purple_markup_strip_html(body); @@ -575,17 +602,6 @@ message = xmlnode_to_str(message_node, &message_length); xmlnode_free(message_node); - /* Check if there is a previously open conversation */ - if (bb->conversation == NULL) - { - bb->conversation = g_new(BonjourJabberConversation, 1); - bb->conversation->socket = _connect_to_buddy(gb); - bb->conversation->stream_started = FALSE; - bb->conversation->buddy_name = g_strdup(gb->name); - bb->conversation->watcher_id = purple_input_add(bb->conversation->socket, - PURPLE_INPUT_READ, _client_socket_handler, gb); - } - /* Check if the stream for the conversation has been started */ if (bb->conversation->stream_started == FALSE) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_common.c Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,175 @@ +/* + * 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 Library 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 <string.h> + +#include "config.h" +#include "mdns_common.h" +#include "bonjour.h" +#include "buddy.h" +#include "debug.h" + + +/** + * Allocate space for the dns-sd data. + */ +BonjourDnsSd * +bonjour_dns_sd_new() +{ + BonjourDnsSd *data = g_new0(BonjourDnsSd, 1); + + return data; +} + +/** + * Deallocate the space of the dns-sd data. + */ +void +bonjour_dns_sd_free(BonjourDnsSd *data) +{ + g_free(data->first); + g_free(data->last); + g_free(data->phsh); + g_free(data->status); + g_free(data->vc); + g_free(data->msg); + g_free(data); +} + +/** + * Send a new dns-sd packet updating our status. + */ +void +bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message) +{ + g_free(data->status); + g_free(data->msg); + + data->status = g_strdup(status); + data->msg = g_strdup(status_message); + + /* Update our text record with the new status */ + _mdns_publish(data, PUBLISH_UPDATE); /* <--We must control the errors */ +} + +/** + * Advertise our presence within the dns-sd daemon and start browsing + * for other bonjour peers. + */ +gboolean +bonjour_dns_sd_start(BonjourDnsSd *data) +{ + PurpleAccount *account; + PurpleConnection *gc; + gint dns_sd_socket; + gpointer opaque_data; + +#ifdef USE_BONJOUR_HOWL + sw_discovery_oid session_id; +#endif + + account = data->account; + gc = purple_account_get_connection(account); + + /* Initialize the dns-sd data and session */ +#ifndef USE_BONJOUR_APPLE + if (sw_discovery_init(&data->session) != SW_OKAY) + { + purple_debug_error("bonjour", "Unable to initialize an mDNS session.\n"); + + /* In Avahi, sw_discovery_init frees data->session but doesn't clear it */ + data->session = NULL; + + return FALSE; + } +#endif + + /* Publish our bonjour IM client at the mDNS daemon */ + + if (0 != _mdns_publish(data, PUBLISH_START)) + { + return FALSE; + } + + /* Advise the daemon that we are waiting for connections */ + +#ifdef USE_BONJOUR_APPLE + if (DNSServiceBrowse(&data->browser, 0, 0, ICHAT_SERVICE, NULL, _mdns_service_browse_callback, account) + != kDNSServiceErr_NoError) +#else /* USE_BONJOUR_HOWL */ + if (sw_discovery_browse(data->session, 0, ICHAT_SERVICE, NULL, _browser_reply, + account, &session_id) != SW_OKAY) +#endif + { + purple_debug_error("bonjour", "Unable to get service."); + return FALSE; + } + + /* Get the socket that communicates with the mDNS daemon and bind it to a */ + /* callback that will handle the dns_sd packets */ + +#ifdef USE_BONJOUR_APPLE + dns_sd_socket = DNSServiceRefSockFD(data->browser); + opaque_data = data->browser; +#else /* USE_BONJOUR_HOWL */ + dns_sd_socket = sw_discovery_socket(data->session); + opaque_data = data->session; +#endif + + gc->inpa = purple_input_add(dns_sd_socket, PURPLE_INPUT_READ, + _mdns_handle_event, opaque_data); + + return TRUE; +} + +/** + * Unregister the "_presence._tcp" service at the mDNS daemon. + */ + +void +bonjour_dns_sd_stop(BonjourDnsSd *data) +{ + PurpleAccount *account; + PurpleConnection *gc; + +#ifdef USE_BONJOUR_APPLE + if (data->advertisement == NULL || data->browser == NULL) +#else /* USE_BONJOUR_HOWL */ + if (data->session == NULL) +#endif + return; + +#ifdef USE_BONJOUR_HOWL + sw_discovery_cancel(data->session, data->session_id); +#endif + + account = data->account; + gc = purple_account_get_connection(account); + purple_input_remove(gc->inpa); + +#ifdef USE_BONJOUR_APPLE + /* hack: for win32, we need to stop listening to the advertisement pipe too */ + purple_input_remove(data->advertisement_fd); + + DNSServiceRefDeallocate(data->advertisement); + DNSServiceRefDeallocate(data->browser); + data->advertisement = NULL; + data->browser = NULL; +#else /* USE_BONJOUR_HOWL */ + g_free(data->session); + data->session = NULL; +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_common.h Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,54 @@ +/* + * 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 Library 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 _BONJOUR_MDNS_COMMON +#define _BONJOUR_MDNS_COMMON + +#include "mdns_types.h" + +#ifdef USE_BONJOUR_APPLE +#include "mdns_win32.h" +#elif defined USE_BONJOUR_HOWL +#include "mdns_howl.h" +#endif + +/** + * Allocate space for the dns-sd data. + */ +BonjourDnsSd *bonjour_dns_sd_new(void); + +/** + * Deallocate the space of the dns-sd data. + */ +void bonjour_dns_sd_free(BonjourDnsSd *data); + +/** + * Send a new dns-sd packet updating our status. + */ +void bonjour_dns_sd_send_status(BonjourDnsSd *data, const char *status, const char *status_message); + +/** + * Advertise our presence within the dns-sd daemon and start + * browsing for other bonjour peers. + */ +gboolean bonjour_dns_sd_start(BonjourDnsSd *data); + +/** + * Unregister the "_presence._tcp" service at the mDNS daemon. + */ +void bonjour_dns_sd_stop(BonjourDnsSd *data); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_howl.c Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,238 @@ +/* + * 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 Library 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 "mdns_howl.h" + +#include "debug.h" +#include "buddy.h" + +sw_result HOWL_API +_publish_reply(sw_discovery discovery, sw_discovery_oid oid, + sw_discovery_publish_status status, sw_opaque extra) +{ + purple_debug_warning("bonjour", "_publish_reply --> Start\n"); + + /* Check the answer from the mDNS daemon */ + switch (status) + { + case SW_DISCOVERY_PUBLISH_STARTED : + purple_debug_info("bonjour", "_publish_reply --> Service started\n"); + break; + case SW_DISCOVERY_PUBLISH_STOPPED : + purple_debug_info("bonjour", "_publish_reply --> Service stopped\n"); + break; + case SW_DISCOVERY_PUBLISH_NAME_COLLISION : + purple_debug_info("bonjour", "_publish_reply --> Name collision\n"); + break; + case SW_DISCOVERY_PUBLISH_INVALID : + purple_debug_info("bonjour", "_publish_reply --> Service invalid\n"); + break; + } + + return SW_OKAY; +} + +sw_result HOWL_API +_resolve_reply(sw_discovery discovery, sw_discovery_oid oid, + sw_uint32 interface_index, sw_const_string name, + sw_const_string type, sw_const_string domain, + sw_ipv4_address address, sw_port port, + sw_octets text_record, sw_ulong text_record_len, + sw_opaque extra) +{ + BonjourBuddy *buddy; + PurpleAccount *account = (PurpleAccount*)extra; + gint address_length = 16; + sw_text_record_iterator iterator; + char key[SW_TEXT_RECORD_MAX_LEN]; + char value[SW_TEXT_RECORD_MAX_LEN]; + sw_uint32 value_length; + + sw_discovery_cancel(discovery, oid); + + /* create a buddy record */ + buddy = bonjour_buddy_new(name, account); + + /* Get the ip as a string */ + buddy->ip = g_malloc(address_length); + sw_ipv4_address_name(address, buddy->ip, address_length); + + buddy->port_p2pj = port; + + /* Obtain the parameters from the text_record */ + if ((text_record_len > 0) && (text_record) && (*text_record != '\0')) + { + sw_text_record_iterator_init(&iterator, text_record, text_record_len); + while (sw_text_record_iterator_next(iterator, key, (sw_octet *)value, &value_length) == SW_OKAY) + set_bonjour_buddy_value(buddy, key, value, value_length); + + sw_text_record_iterator_fina(iterator); + } + + if (!bonjour_buddy_check(buddy)) + { + bonjour_buddy_delete(buddy); + return SW_DISCOVERY_E_UNKNOWN; + } + + /* Add or update the buddy in our buddy list */ + bonjour_buddy_add_to_purple(buddy); + + return SW_OKAY; +} + +sw_result HOWL_API +_browser_reply(sw_discovery discovery, sw_discovery_oid oid, + sw_discovery_browse_status status, + sw_uint32 interface_index, sw_const_string name, + sw_const_string type, sw_const_string domain, + sw_opaque_t extra) +{ + sw_discovery_resolve_id rid; + PurpleAccount *account = (PurpleAccount*)extra; + PurpleBuddy *gb = NULL; + + switch (status) + { + case SW_DISCOVERY_BROWSE_INVALID: + purple_debug_warning("bonjour", "_browser_reply --> Invalid\n"); + break; + case SW_DISCOVERY_BROWSE_RELEASE: + purple_debug_warning("bonjour", "_browser_reply --> Release\n"); + break; + case SW_DISCOVERY_BROWSE_ADD_DOMAIN: + purple_debug_warning("bonjour", "_browser_reply --> Add domain\n"); + break; + case SW_DISCOVERY_BROWSE_ADD_DEFAULT_DOMAIN: + purple_debug_warning("bonjour", "_browser_reply --> Add default domain\n"); + break; + case SW_DISCOVERY_BROWSE_REMOVE_DOMAIN: + purple_debug_warning("bonjour", "_browser_reply --> Remove domain\n"); + break; + case SW_DISCOVERY_BROWSE_ADD_SERVICE: + /* A new peer has joined the network and uses iChat bonjour */ + purple_debug_info("bonjour", "_browser_reply --> Add service\n"); + if (g_ascii_strcasecmp(name, account->username) != 0) + { + if (sw_discovery_resolve(discovery, interface_index, name, type, + domain, _resolve_reply, extra, &rid) != SW_OKAY) + { + purple_debug_warning("bonjour", "_browser_reply --> Cannot send resolve\n"); + } + } + break; + case SW_DISCOVERY_BROWSE_REMOVE_SERVICE: + purple_debug_info("bonjour", "_browser_reply --> Remove service\n"); + gb = purple_find_buddy((PurpleAccount*)extra, name); + if (gb != NULL) + { + bonjour_buddy_delete(gb->proto_data); + purple_blist_remove_buddy(gb); + } + break; + case SW_DISCOVERY_BROWSE_RESOLVED: + purple_debug_info("bonjour", "_browse_reply --> Resolved\n"); + break; + default: + break; + } + + return SW_OKAY; +} + +int +_mdns_publish(BonjourDnsSd *data, PublishType type) +{ + sw_text_record dns_data; + sw_result publish_result = SW_OKAY; + char portstring[6]; + const char *jid, *aim, *email; + + /* Fill the data for the service */ + if (sw_text_record_init(&dns_data) != SW_OKAY) + { + purple_debug_error("bonjour", "Unable to initialize the data for the mDNS.\n"); + return -1; + } + + /* Convert the port to a string */ + snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj); + + jid = purple_account_get_string(data->account, "jid", NULL); + aim = purple_account_get_string(data->account, "AIM", NULL); + email = purple_account_get_string(data->account, "email", NULL); + + /* We should try to follow XEP-0174, but some clients have "issues", so we humor them. + * See http://telepathy.freedesktop.org/wiki/SalutInteroperability + */ + + /* Needed by iChat */ + sw_text_record_add_key_and_string_value(dns_data, "txtvers", "1"); + /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */ + sw_text_record_add_key_and_string_value(dns_data, "1st", data->first); + /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */ + sw_text_record_add_key_and_string_value(dns_data, "last", data->last); + /* Needed by Adium */ + sw_text_record_add_key_and_string_value(dns_data, "port.p2pj", portstring); + /* Needed by iChat, Gaim/Pidgin <= 2.0.1 */ + sw_text_record_add_key_and_string_value(dns_data, "status", data->status); + /* Currently always set to "!" since we don't support AV and wont ever be in a conference */ + sw_text_record_add_key_and_string_value(dns_data, "vc", data->vc); + sw_text_record_add_key_and_string_value(dns_data, "ver", VERSION); + if (email != NULL && *email != '\0') + sw_text_record_add_key_and_string_value(dns_data, "email", email); + if (jid != NULL && *jid != '\0') + sw_text_record_add_key_and_string_value(dns_data, "jid", jid); + /* Nonstandard, but used by iChat */ + if (aim != NULL && *aim != '\0') + sw_text_record_add_key_and_string_value(dns_data, "AIM", aim); + if (data->msg != NULL && *data->msg != '\0') + sw_text_record_add_key_and_string_value(dns_data, "msg", data->msg); + if (data->phsh != NULL && *data->phsh != '\0') + sw_text_record_add_key_and_string_value(dns_data, "phsh", data->phsh); + + /* TODO: ext, nick, node */ + + /* Publish the service */ + switch (type) + { + case PUBLISH_START: + publish_result = sw_discovery_publish(data->session, 0, purple_account_get_username(data->account), ICHAT_SERVICE, NULL, + NULL, data->port_p2pj, sw_text_record_bytes(dns_data), sw_text_record_len(dns_data), + _publish_reply, NULL, &data->session_id); + break; + case PUBLISH_UPDATE: + publish_result = sw_discovery_publish_update(data->session, data->session_id, + sw_text_record_bytes(dns_data), sw_text_record_len(dns_data)); + break; + } + if (publish_result != SW_OKAY) + { + purple_debug_error("bonjour", "Unable to publish or change the status of the _presence._tcp service.\n"); + return -1; + } + + /* Free the memory used by temp data */ + sw_text_record_fina(dns_data); + + return 0; +} + +void +_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) +{ + sw_discovery_read_socket((sw_discovery)data); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_howl.h Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,47 @@ +/* + * 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 Library 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 _BONJOUR_MDNS_HOWL +#define _BONJOUR_MDNS_HOWL + +#include "config.h" + +#ifdef USE_BONJOUR_HOWL + +#include <howl.h> +#include <glib.h> +#include "mdns_types.h" + +/* callback functions */ + +sw_result HOWL_API _publish_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_publish_status status, sw_opaque extra); + +sw_result HOWL_API _resolve_reply(sw_discovery discovery, sw_discovery_oid oid, sw_uint32 interface_index, sw_const_string name, + sw_const_string type, sw_const_string domain, sw_ipv4_address address, sw_port port, sw_octets text_record, + sw_ulong text_record_len, sw_opaque extra); + +sw_result HOWL_API _browser_reply(sw_discovery discovery, sw_discovery_oid oid, sw_discovery_browse_status status, + sw_uint32 interface_index, sw_const_string name, sw_const_string type, sw_const_string domain, sw_opaque_t extra); + + +/* interface functions */ + +int _mdns_publish(BonjourDnsSd *data, PublishType type); +void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition); + +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_types.h Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,63 @@ +/* + * 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 Library 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 _BONJOUR_MDNS_TYPES +#define _BONJOUR_MDNS_TYPES + +#include <glib.h> +#include "account.h" +#include "config.h" + +#ifdef USE_BONJOUR_APPLE +#include "dns_sd_proxy.h" +#else /* USE_BONJOUR_HOWL */ +#include <howl.h> +#endif + +#define ICHAT_SERVICE "_presence._tcp." + +/** + * Data to be used by the dns-sd connection. + */ +typedef struct _BonjourDnsSd +{ +#ifdef USE_BONJOUR_APPLE + DNSServiceRef advertisement; + DNSServiceRef browser; + + int advertisement_fd; /* hack... windows bonjour is broken, so we have to have this */ +#else /* USE_BONJOUR_HOWL */ + sw_discovery session; + sw_discovery_oid session_id; +#endif + + PurpleAccount *account; + gchar *first; + gchar *last; + gint port_p2pj; + gchar *phsh; + gchar *status; + gchar *vc; + gchar *msg; +} BonjourDnsSd; + +typedef enum _PublishType { + PUBLISH_START, + PUBLISH_UPDATE +} PublishType; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,296 @@ +/* + * 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 Library 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 "mdns_win32.h" + +#include "debug.h" + +/* data structure for the resolve callback */ +typedef struct _ResolveCallbackArgs +{ + DNSServiceRef resolver; + int resolver_fd; + + PurpleDnsQueryData *query; + gchar *fqn; + + BonjourBuddy* buddy; +} ResolveCallbackArgs; + +static void +_mdns_parse_text_record(BonjourBuddy* buddy, const char* record, uint16_t record_len) +{ + const char *txt_entry; + uint8_t txt_len; + int i; + + for (i = 0; buddy_TXT_records[i] != NULL; i++) { + txt_entry = TXTRecordGetValuePtr(record_len, record, buddy_TXT_records[i], &txt_len); + if (txt_entry != NULL) + set_bonjour_buddy_value(buddy, buddy_TXT_records[i], txt_entry, txt_len); + } +} + +static void DNSSD_API +_mdns_text_record_query_callback(DNSServiceRef DNSServiceRef, DNSServiceFlags flags, + uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullname, + uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, + uint32_t ttl, void *context) +{ + if (kDNSServiceErr_NoError != errorCode) + purple_debug_error("bonjour", "text record query - callback error.\n"); + else if (flags & kDNSServiceFlagsAdd) + { + BonjourBuddy *buddy = (BonjourBuddy*)context; + _mdns_parse_text_record(buddy, rdata, rdlen); + bonjour_buddy_add_to_purple(buddy); + } +} + +static void +_mdns_resolve_host_callback(GSList *hosts, gpointer data, const char *error_message) +{ + ResolveCallbackArgs* args = (ResolveCallbackArgs*)data; + + if (!hosts || !hosts->data) + purple_debug_error("bonjour", "host resolution - callback error.\n"); + else + { + struct sockaddr_in *addr = (struct sockaddr_in*)g_slist_nth_data(hosts, 1); + BonjourBuddy* buddy = args->buddy; + + buddy->ip = g_strdup(inet_ntoa(addr->sin_addr)); + + /* finally, set up the continuous txt record watcher, and add the buddy to purple */ + + if (kDNSServiceErr_NoError == DNSServiceQueryRecord(&buddy->txt_query, 0, 0, args->fqn, + kDNSServiceType_TXT, kDNSServiceClass_IN, _mdns_text_record_query_callback, buddy)) + { + gint fd = DNSServiceRefSockFD(buddy->txt_query); + buddy->txt_query_fd = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, buddy->txt_query); + + bonjour_buddy_add_to_purple(buddy); + } + else + bonjour_buddy_delete(buddy); + + } + + /* free the hosts list*/ + g_slist_free(hosts); + + /* free the remaining args memory */ + purple_dnsquery_destroy(args->query); + g_free(args->fqn); + g_free(args); +} + +static void DNSSD_API +_mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, + const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) +{ + ResolveCallbackArgs *args = (ResolveCallbackArgs*)context; + + /* remove the input fd and destroy the service ref */ + purple_input_remove(args->resolver_fd); + DNSServiceRefDeallocate(args->resolver); + + if (kDNSServiceErr_NoError != errorCode) + { + purple_debug_error("bonjour", "service resolver - callback error.\n"); + bonjour_buddy_delete(args->buddy); + g_free(args); + } + else + { + args->buddy->port_p2pj = ntohs(port); + + /* parse the text record */ + _mdns_parse_text_record(args->buddy, txtRecord, txtLen); + + /* set more arguments, and start the host resolver */ + args->fqn = g_strdup(fullname); + + if (!(args->query = + purple_dnsquery_a(hosttarget, port, _mdns_resolve_host_callback, args))) + { + purple_debug_error("bonjour", "service resolver - host resolution failed.\n"); + bonjour_buddy_delete(args->buddy); + g_free(args->fqn); + g_free(args); + } + } + +} + +static void DNSSD_API +_mdns_service_register_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, + const char *name, const char *regtype, const char *domain, void *context) +{ + /* we don't actually care about anything said in this callback - this is only here because Bonjour for windows is broken */ + if (kDNSServiceErr_NoError != errorCode) + purple_debug_error("bonjour", "service advertisement - callback error.\n"); + else + purple_debug_info("bonjour", "service advertisement - callback.\n"); +} + +void DNSSD_API +_mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) +{ + PurpleAccount *account = (PurpleAccount*)context; + PurpleBuddy *gb = NULL; + + if (kDNSServiceErr_NoError != errorCode) + purple_debug_error("bonjour", "service browser - callback error"); + else if (flags & kDNSServiceFlagsAdd) + { + /* A presence service instance has been discovered... check it isn't us! */ + if (g_ascii_strcasecmp(serviceName, account->username) != 0) + { + /* OK, lets go ahead and resolve it to add to the buddy list */ + ResolveCallbackArgs *args = g_new0(ResolveCallbackArgs, 1); + args->buddy = bonjour_buddy_new(serviceName, account); + + if (kDNSServiceErr_NoError != DNSServiceResolve(&args->resolver, 0, 0, serviceName, regtype, replyDomain, _mdns_service_resolve_callback, args)) + { + bonjour_buddy_delete(args->buddy); + g_free(args); + purple_debug_error("bonjour", "service browser - failed to resolve service.\n"); + } + else + { + /* get a file descriptor for this service ref, and add it to the input list */ + gint resolver_fd = DNSServiceRefSockFD(args->resolver); + args->resolver_fd = purple_input_add(resolver_fd, PURPLE_INPUT_READ, _mdns_handle_event, args->resolver); + } + } + } + else + { + /* A peer has sent a goodbye packet, remove them from the buddy list */ + purple_debug_info("bonjour", "service browser - remove notification\n"); + gb = purple_find_buddy(account, serviceName); + if (gb != NULL) + { + bonjour_buddy_delete(gb->proto_data); + purple_blist_remove_buddy(gb); + } + } +} + +int +_mdns_publish(BonjourDnsSd *data, PublishType type) +{ + TXTRecordRef dns_data; + char portstring[6]; + int ret = 0; + const char *jid, *aim, *email; + DNSServiceErrorType set_ret; + + TXTRecordCreate(&dns_data, 256, NULL); + + /* Convert the port to a string */ + snprintf(portstring, sizeof(portstring), "%d", data->port_p2pj); + + jid = purple_account_get_string(data->account, "jid", NULL); + aim = purple_account_get_string(data->account, "AIM", NULL); + email = purple_account_get_string(data->account, "email", NULL); + + /* We should try to follow XEP-0174, but some clients have "issues", so we humor them. + * See http://telepathy.freedesktop.org/wiki/SalutInteroperability + */ + + /* Needed by iChat */ + set_ret = TXTRecordSetValue(&dns_data, "txtvers", 1, "1"); + /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */ + if (set_ret == kDNSServiceErr_NoError) + set_ret = TXTRecordSetValue(&dns_data, "1st", strlen(data->first), data->first); + /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */ + if (set_ret == kDNSServiceErr_NoError) + set_ret = TXTRecordSetValue(&dns_data, "last", strlen(data->last), data->last); + /* Needed by Adium */ + if (set_ret == kDNSServiceErr_NoError) + set_ret = TXTRecordSetValue(&dns_data, "port.p2pj", strlen(portstring), portstring); + /* Needed by iChat, Gaim/Pidgin <= 2.0.1 */ + if (set_ret == kDNSServiceErr_NoError) + set_ret = TXTRecordSetValue(&dns_data, "status", strlen(data->status), data->status); + if (set_ret == kDNSServiceErr_NoError) + set_ret = TXTRecordSetValue(&dns_data, "ver", strlen(VERSION), VERSION); + /* Currently always set to "!" since we don't support AV and wont ever be in a conference */ + if (set_ret == kDNSServiceErr_NoError) + set_ret = TXTRecordSetValue(&dns_data, "vc", strlen(data->vc), data->vc); + if (set_ret == kDNSServiceErr_NoError && email != NULL && *email != '\0') + set_ret = TXTRecordSetValue(&dns_data, "email", strlen(email), email); + if (set_ret == kDNSServiceErr_NoError && jid != NULL && *jid != '\0') + set_ret = TXTRecordSetValue(&dns_data, "jid", strlen(jid), jid); + /* Nonstandard, but used by iChat */ + if (set_ret == kDNSServiceErr_NoError && aim != NULL && *aim != '\0') + set_ret = TXTRecordSetValue(&dns_data, "AIM", strlen(aim), aim); + if (set_ret == kDNSServiceErr_NoError && data->msg != NULL && *data->msg != '\0') + set_ret = TXTRecordSetValue(&dns_data, "msg", strlen(data->msg), data->msg); + if (set_ret == kDNSServiceErr_NoError && data->phsh != NULL && *data->phsh != '\0') + set_ret = TXTRecordSetValue(&dns_data, "phsh", strlen(data->phsh), data->phsh); + + /* TODO: ext, nick, node */ + + if (set_ret != kDNSServiceErr_NoError) + { + purple_debug_error("bonjour", "Unable to allocate memory for text record.\n"); + ret = -1; + } + else + { + DNSServiceErrorType err = kDNSServiceErr_NoError; + + /* OK, we're done constructing the text record, (re)publish the service */ + + switch (type) + { + case PUBLISH_START: + err = DNSServiceRegister(&data->advertisement, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE, + NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), + _mdns_service_register_callback, NULL); + break; + + case PUBLISH_UPDATE: + err = DNSServiceUpdateRecord(data->advertisement, NULL, 0, TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data), 0); + break; + } + + if (kDNSServiceErr_NoError != err) + { + purple_debug_error("bonjour", "Failed to publish presence service.\n"); + ret = -1; + } + else if (PUBLISH_START == type) + { + /* hack: Bonjour on windows is broken. We don't care about the callback but we have to listen anyway */ + gint advertisement_fd = DNSServiceRefSockFD(data->advertisement); + data->advertisement_fd = purple_input_add(advertisement_fd, PURPLE_INPUT_READ, _mdns_handle_event, data->advertisement); + } + } + + /* Free the memory used by temp data */ + TXTRecordDeallocate(&dns_data); + return ret; +} + +void +_mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition) +{ + DNSServiceProcessResult((DNSServiceRef)data); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.h Wed Jun 06 00:58:00 2007 +0000 @@ -0,0 +1,40 @@ +/* + * 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 Library 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 _BONJOUR_MDNS_WIN32 +#define _BONJOUR_MDNS_WIN32 + +#ifdef USE_BONJOUR_APPLE + +#include <glib.h> +#include "mdns_types.h" +#include "buddy.h" +#include "dnsquery.h" +#include "dns_sd_proxy.h" + +/* Bonjour async callbacks */ + +void DNSSD_API _mdns_service_browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context); + +/* interface functions */ + +int _mdns_publish(BonjourDnsSd *data, PublishType type); +void _mdns_handle_event(gpointer data, gint source, PurpleInputCondition condition); + +#endif + +#endif
--- a/libpurple/protocols/msn/msn.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/msn/msn.c Wed Jun 06 00:58:00 2007 +0000 @@ -1423,6 +1423,22 @@ if (found) \ sect_info = TRUE; +#define MSN_GOT_INFO_GET_FIELD_NO_SEARCH(a, b) \ + found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \ + "\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, msn_info_strip_search_link); \ + if (found) \ + sect_info = TRUE; + +static char * +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) + return g_strndup(field, len); + return g_strndup(field, c - field); +} + static void msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data, const gchar *url_text, size_t len, const gchar *error_message) @@ -1538,10 +1554,10 @@ /* General */ MSN_GOT_INFO_GET_FIELD("Nickname", _("Nickname")); - MSN_GOT_INFO_GET_FIELD("Age", _("Age")); - MSN_GOT_INFO_GET_FIELD("Gender", _("Gender")); - MSN_GOT_INFO_GET_FIELD("Occupation", _("Occupation")); - MSN_GOT_INFO_GET_FIELD("Location", _("Location")); + MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Age", _("Age")); + MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Gender", _("Gender")); + MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Occupation", _("Occupation")); + MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Location", _("Location")); /* Extract their Interests and put it in */ found = purple_markup_extract_info_field(stripped, stripped_len, user_info, @@ -1802,7 +1818,10 @@ /* This doesn't work with the new spaces profiles - Stu 3/2/06 char *p = strstr(url_buffer, "Unknown Member </TITLE>"); * This might not work for long either ... */ + /* Nope, it failed some time before 5/2/07 :( char *p = strstr(url_buffer, "form id=\"SpacesSearch\" name=\"SpacesSearch\""); + * Let's see how long this one holds out for ... */ + char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http://spaces.live.com/profile.aspx?cid=0\""); PurpleBuddy *b = purple_find_buddy (purple_connection_get_account(info_data->gc), info_data->name); purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"),
--- a/libpurple/protocols/msn/msn.h Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/msn/msn.h Wed Jun 06 00:58:00 2007 +0000 @@ -67,7 +67,7 @@ #define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders" #define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login=" -#define PROFILE_URL "http://spaces.msn.com/profile.aspx?mem=" +#define PROFILE_URL "http://spaces.live.com/profile.aspx?mem=" #define USEROPT_HOTMAIL 0
--- a/libpurple/protocols/msn/nexus.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/msn/nexus.c Wed Jun 06 00:58:00 2007 +0000 @@ -247,6 +247,8 @@ 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'; } }
--- a/libpurple/protocols/msn/session.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/msn/session.c Wed Jun 06 00:58:00 2007 +0000 @@ -316,6 +316,7 @@ "temporarily.")); break; case MSN_ERROR_AUTH: + gc->wants_to_die = TRUE; msg = g_strdup_printf(_("Unable to authenticate: %s"), (info == NULL ) ? _("Unknown error") : info);
--- a/libpurple/protocols/msn/switchboard.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/msn/switchboard.c Wed Jun 06 00:58:00 2007 +0000 @@ -419,7 +419,14 @@ case MSN_SB_ERROR_TOO_FAST: str_reason = _("Message could not be sent " "because we are sending too quickly:"); - break; + break; + case MSN_SB_ERROR_AUTHFAILED: + str_reason = _("Message could not be sent " + "because we wer unable to establish a " + "session with the server. This is " + "likely a server problem, try again in " + "a few minutes:"); + break; default: str_reason = _("Message could not be sent " "because an error with " @@ -963,9 +970,13 @@ * Connect stuff **************************************************************************/ static void +ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error); + +static void connect_cb(MsnServConn *servconn) { MsnSwitchBoard *swboard; + MsnTransaction *trans; MsnCmdProc *cmdproc; PurpleAccount *account; @@ -980,16 +991,44 @@ { swboard->empty = FALSE; - msn_cmdproc_send(cmdproc, "ANS", "%s %s %s", - purple_account_get_username(account), - swboard->auth_key, swboard->session_id); + trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s", + purple_account_get_username(account), + swboard->auth_key, swboard->session_id); } else { - msn_cmdproc_send(cmdproc, "USR", "%s %s", - purple_account_get_username(account), - swboard->auth_key); + trans = msn_transaction_new(cmdproc, "USR", "%s %s", + purple_account_get_username(account), + swboard->auth_key); } + + msn_transaction_set_error_cb(trans, ans_usr_error); + msn_transaction_set_data(trans, swboard); + msn_cmdproc_send_trans(cmdproc, trans); +} + +static void +ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) +{ + MsnSwitchBoard *swboard; + char **params; + char *passport; + int reason = MSN_SB_ERROR_UNKNOWN; + + if (error == 911) + { + reason = MSN_SB_ERROR_AUTHFAILED; + } + + purple_debug_warning("msn", "ans_usr_error: command %s gave error %i\n", trans->command, error); + + params = g_strsplit(trans->params, " ", 0); + passport = params[0]; + swboard = trans->data; + + swboard_error_helper(swboard, reason, passport); + + g_strfreev(params); } static void
--- a/libpurple/protocols/msn/switchboard.h Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/msn/switchboard.h Wed Jun 06 00:58:00 2007 +0000 @@ -46,6 +46,7 @@ MSN_SB_ERROR_USER_OFFLINE, /**< The user to call is offline. */ MSN_SB_ERROR_CONNECTION, /**< There was a connection error. */ MSN_SB_ERROR_TOO_FAST, /**< We are sending too fast */ + MSN_SB_ERROR_AUTHFAILED, /**< Authentication failed joining the switchboard session */ MSN_SB_ERROR_UNKNOWN /**< An unknown error occurred. */ } MsnSBErrorType;
--- a/libpurple/protocols/null/nullprpl.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/null/nullprpl.c Wed Jun 06 00:58:00 2007 +0000 @@ -432,7 +432,7 @@ from_username, who, message); /* is the sender blocked by the recipient's privacy settings? */ - if (!purple_privacy_check(to_acct, gc->account->username)) { + if (to_acct && !purple_privacy_check(to_acct, gc->account->username)) { char *msg = g_strdup_printf( _("Your message was blocked by %s's privacy settings."), who); purple_debug_info("nullprpl",
--- a/libpurple/protocols/oscar/flap_connection.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Wed Jun 06 00:58:00 2007 +0000 @@ -303,7 +303,7 @@ } } - if (conn->fd != -1) + if (conn->fd >= 0) { if (conn->type == SNAC_FAMILY_LOCATE) flap_connection_send_close(od, conn); @@ -792,7 +792,7 @@ } /* If there was an error then close the connection */ - if (read == -1) + if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ @@ -853,7 +853,7 @@ break; } - if (read == -1) + if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ @@ -902,7 +902,7 @@ ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0); if (ret <= 0) { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + if (ret < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) /* No worries */ return; @@ -936,7 +936,7 @@ purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count); /* If we haven't already started writing stuff, then start the cycle */ - if ((conn->watcher_outgoing == 0) && (conn->fd != -1)) + if ((conn->watcher_outgoing == 0) && (conn->fd >= 0)) { conn->watcher_outgoing = purple_input_add(conn->fd, PURPLE_INPUT_WRITE, send_cb, conn);
--- a/libpurple/protocols/oscar/odc.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/oscar/odc.c Wed Jun 06 00:58:00 2007 +0000 @@ -447,7 +447,7 @@ return; } - if (read == -1) + if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */
--- a/libpurple/protocols/oscar/oscar.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/oscar/oscar.c Wed Jun 06 00:58:00 2007 +0000 @@ -1604,8 +1604,7 @@ straight_to_hell, pos) == NULL) { char buf[256]; - if (pos->modname) - g_free(pos->modname); + g_free(pos->modname); g_free(pos); g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. " "Check %s for updates."), PURPLE_WEBSITE);
--- a/libpurple/protocols/oscar/peer.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/oscar/peer.c Wed Jun 06 00:58:00 2007 +0000 @@ -173,12 +173,12 @@ purple_input_remove(conn->watcher_outgoing); conn->watcher_outgoing = 0; } - if (conn->listenerfd != -1) + if (conn->listenerfd >= 0) { close(conn->listenerfd); conn->listenerfd = -1; } - if (conn->fd != -1) + if (conn->fd >= 0) { close(conn->fd); conn->fd = -1; @@ -310,7 +310,7 @@ } /* If there was an error then close the connection */ - if (read == -1) + if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ @@ -360,7 +360,7 @@ return; } - if (read == -1) + if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ @@ -422,7 +422,7 @@ wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0); if (wrotelen <= 0) { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) /* No worries */ return; @@ -462,7 +462,7 @@ purple_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len); /* If we haven't already started writing stuff, then start the cycle */ - if ((conn->watcher_outgoing == 0) && (conn->fd != -1)) + if ((conn->watcher_outgoing == 0) && (conn->fd >= 0)) { conn->watcher_outgoing = purple_input_add(conn->fd, PURPLE_INPUT_WRITE, send_cb, conn); @@ -596,7 +596,7 @@ purple_debug_info("oscar", "Accepting connection on listener socket.\n"); conn->fd = accept(conn->listenerfd, &addr, &addrlen); - if (conn->fd == -1) + if (conn->fd < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No connection yet--no worries */ @@ -640,7 +640,7 @@ conn = data; conn->listen_data = NULL; - if (listenerfd == -1) + if (listenerfd < 0) { /* Could not open listener socket */ peer_connection_trynext(conn);
--- a/libpurple/protocols/oscar/peer_proxy.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/oscar/peer_proxy.c Wed Jun 06 00:58:00 2007 +0000 @@ -224,7 +224,7 @@ } /* If there was an error then close the connection */ - if (read == -1) + if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ @@ -285,7 +285,8 @@ return; } - if (read == -1) + /* If there was an error then close the connection */ + if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */
--- a/libpurple/protocols/silc/silc.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/silc/silc.c Wed Jun 06 00:58:00 2007 +0000 @@ -119,12 +119,12 @@ NULL, 0); } -static int +static gboolean silcpurple_scheduler(gpointer *context) { SilcPurple sg = (SilcPurple)context; silc_client_run_one(sg->client); - return 1; + return TRUE; } static void @@ -361,11 +361,7 @@ } /* Schedule SILC using Glib's event loop */ -#ifndef _WIN32 - sg->scheduler = g_timeout_add(5, (GSourceFunc)silcpurple_scheduler, sg); -#else - sg->scheduler = g_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg); -#endif + sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg); } static int @@ -396,8 +392,8 @@ if (sg->conn) silc_client_close_connection(sg->client, sg->conn); - g_source_remove(sg->scheduler); - g_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg); + purple_timeout_remove(sg->scheduler); + purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg); }
--- a/libpurple/protocols/yahoo/yahoo.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Wed Jun 06 00:58:00 2007 +0000 @@ -2310,7 +2310,7 @@ * are you trying to pull? */ guchar *start; - purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!"); + purple_debug_warning("yahoo", "Error in YMSG stream, got something not a YMSG packet!\n"); start = memchr(yd->rxqueue + 1, 'Y', yd->rxlen - 1); if (start) { @@ -2377,7 +2377,11 @@ } if (source < 0) { - purple_connection_error(gc, _("Unable to connect.")); + gchar *tmp; + tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), + error_message); + purple_connection_error(gc, tmp); + g_free(tmp); return; } @@ -2405,7 +2409,11 @@ } if (source < 0) { - purple_connection_error(gc, _("Unable to connect.")); + gchar *tmp; + tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), + error_message); + purple_connection_error(gc, tmp); + g_free(tmp); return; } @@ -2507,12 +2515,16 @@ if (written < 0 && errno == EAGAIN) written = 0; else if (written <= 0) { + gchar *tmp; g_free(yd->auth); yd->auth = NULL; if (gc->inpa) purple_input_remove(gc->inpa); gc->inpa = 0; - purple_connection_error(gc, _("Unable to connect.")); + tmp = g_strdup_printf(_("Lost connection with %s:\n%s"), + "login.yahoo.com:80", strerror(errno)); + purple_connection_error(gc, tmp); + g_free(tmp); return; } @@ -2533,7 +2545,11 @@ PurpleConnection *gc = data; if (source < 0) { - purple_connection_error(gc, _("Unable to connect.")); + gchar *tmp; + tmp = g_strdup_printf(_("Could not establish a connection with %s:\n%s"), + "login.yahoo.com:80", error_message); + purple_connection_error(gc, tmp); + g_free(tmp); return; } @@ -2616,8 +2632,7 @@ if (error_message != NULL) { - /* TODO: Include error_message in the message below */ - purple_connection_error(gc, _("Unable to connect.")); + purple_connection_error(gc, error_message); return; }
--- a/libpurple/protocols/yahoo/yahoo_packet.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.c Wed Jun 06 00:58:00 2007 +0000 @@ -110,6 +110,29 @@ return len; } +/* + * 'len' is the value given to us by the server that is supposed to + * be the length of 'data'. But apparently there's a time when this + * length is incorrect. Christopher Layne thinks it might be a bug + * in their server code. + * + * The following information is from Christopher: + * + * It sometimes happens when Yahoo! sends a packet continuation within + * chat. Sometimes when joining a large chatroom the initial + * SERVICE_CHATJOIN packet will be so large that it will need to be + * split into multiple packets. That's fine, except that the length + * of the second packet is wrong. The packet has the same length as + * the first packet, and the length given in the header is the same, + * however the actual data in the packet is shorter than this length. + * So half of the packet contains good, valid data, and then the rest + * of the packet is junk. Luckily there is a null terminator after + * the valid data and before the invalid data. + * + * What does all this mean? It means that we parse through the data + * pulling out key/value pairs until we've parsed 'len' bytes, or until + * we run into a null terminator, whichever comes first. + */ void yahoo_packet_read(struct yahoo_packet *pkt, const guchar *data, int len) { int pos = 0; @@ -121,18 +144,8 @@ while (pos + 1 < len) { - /* this is weird, and in one of the chat packets, and causes us to - * think all the values are keys and all the keys are values after - * this point if we don't handle it */ - if (data[pos] == '\0') { - while (pos + 1 < len) { - if (data[pos] == 0xc0 && data[pos + 1] == 0x80) - break; - pos++; - } - pos += 2; - continue; - } + if (data[pos] == '\0') + break; pair = g_new0(struct yahoo_pair, 1);
--- a/libpurple/purple-remote Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/purple-remote Wed Jun 06 00:58:00 2007 +0000 @@ -31,7 +31,7 @@ return result def show_help(): - print """This program uses DBus to communicate with purple. + print """This program uses D-Bus to communicate with purple. Usage: @@ -96,8 +96,6 @@ protocol = match.group(2) if protocol == "xmpp": protocol = "jabber" - if protocol == "aim" or protocol == "icq": - protocol = "oscar" if protocol is not None: protocol = "prpl-" + protocol command = match.group(5)
--- a/libpurple/util.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/util.c Wed Jun 06 00:58:00 2007 +0000 @@ -46,6 +46,7 @@ } website; char *url; + int num_times_redirected; gboolean full; char *user_agent; gboolean http11; @@ -1259,14 +1260,17 @@ pt->dest_tag = y; \ tags = g_list_prepend(tags, pt); \ } \ - xhtml = g_string_append(xhtml, "<" y); \ - c += strlen("<" x ); \ - xhtml = g_string_append(xhtml, innards->str); \ - xhtml = g_string_append_c(xhtml, '>'); \ + if(xhtml) { \ + xhtml = g_string_append(xhtml, "<" y); \ + xhtml = g_string_append(xhtml, innards->str); \ + xhtml = g_string_append_c(xhtml, '>'); \ + } \ c = p + 1; \ } else { \ - xhtml = g_string_append(xhtml, "<"); \ - plain = g_string_append_c(plain, '<'); \ + if(xhtml) \ + xhtml = g_string_append(xhtml, "<"); \ + if(plain) \ + plain = g_string_append_c(plain, '<'); \ c++; \ } \ g_string_free(innards, TRUE); \ @@ -1275,16 +1279,19 @@ if(!g_ascii_strncasecmp(c, "<" x, strlen("<" x)) && \ (*(c+strlen("<" x)) == '>' || \ !g_ascii_strncasecmp(c+strlen("<" x), "/>", 2))) { \ - xhtml = g_string_append(xhtml, "<" y); \ + if(xhtml) \ + xhtml = g_string_append(xhtml, "<" y); \ c += strlen("<" x); \ if(*c != '/') { \ struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1); \ pt->src_tag = x; \ pt->dest_tag = y; \ tags = g_list_prepend(tags, pt); \ - xhtml = g_string_append_c(xhtml, '>'); \ + if(xhtml) \ + xhtml = g_string_append_c(xhtml, '>'); \ } else { \ - xhtml = g_string_append(xhtml, "/>");\ + if(xhtml) \ + xhtml = g_string_append(xhtml, "/>");\ } \ c = strchr(c, '>') + 1; \ continue; \ @@ -1294,11 +1301,18 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out, char **plain_out) { - GString *xhtml = g_string_new(""); - GString *plain = g_string_new(""); + GString *xhtml = NULL; + GString *plain = NULL; GList *tags = NULL, *tag; const char *c = html; + g_return_if_fail(xhtml_out != NULL || plain_out != NULL); + + if(xhtml_out) + xhtml = g_string_new(""); + if(plain_out) + plain = g_string_new(""); + while(c && *c) { if(*c == '<') { if(*(c+1) == '/') { /* closing tag */ @@ -1314,7 +1328,8 @@ if(tag) { while(tags) { struct purple_parse_tag *pt = tags->data; - g_string_append_printf(xhtml, "</%s>", pt->dest_tag); + if(xhtml) + g_string_append_printf(xhtml, "</%s>", pt->dest_tag); if(tags == tag) break; tags = g_list_remove(tags, pt); @@ -1332,8 +1347,10 @@ if(*end == '>') { c = end+1; } else { - xhtml = g_string_append(xhtml, "<"); - plain = g_string_append_c(plain, '<'); + if(xhtml) + xhtml = g_string_append(xhtml, "<"); + if(plain) + plain = g_string_append_c(plain, '<'); c++; } } @@ -1362,7 +1379,8 @@ ALLOW_TAG("span"); ALLOW_TAG("strong"); ALLOW_TAG("ul"); - + ALLOW_TAG("img"); + /* we skip <HR> because it's not legal in XHTML-IM. However, * we still want to send something sensible, so we put a * linebreak in its place. <BR> also needs special handling @@ -1373,8 +1391,9 @@ !g_ascii_strncasecmp(c+3, "/>", 2) || !g_ascii_strncasecmp(c+3, " />", 3))) { c = strchr(c, '>') + 1; - xhtml = g_string_append(xhtml, "<br/>"); - if(*c != '\n') + if(xhtml) + xhtml = g_string_append(xhtml, "<br/>"); + if(plain && *c != '\n') plain = g_string_append_c(plain, '\n'); continue; } @@ -1384,7 +1403,8 @@ pt->dest_tag = "span"; tags = g_list_prepend(tags, pt); c = strchr(c, '>') + 1; - xhtml = g_string_append(xhtml, "<span style='font-weight: bold;'>"); + if(xhtml) + xhtml = g_string_append(xhtml, "<span style='font-weight: bold;'>"); continue; } if(!g_ascii_strncasecmp(c, "<u>", 3) || !g_ascii_strncasecmp(c, "<underline>", strlen("<underline>"))) { @@ -1393,7 +1413,8 @@ pt->dest_tag = "span"; tags = g_list_prepend(tags, pt); c = strchr(c, '>') + 1; - xhtml = g_string_append(xhtml, "<span style='text-decoration: underline;'>"); + if (xhtml) + xhtml = g_string_append(xhtml, "<span style='text-decoration: underline;'>"); continue; } if(!g_ascii_strncasecmp(c, "<s>", 3) || !g_ascii_strncasecmp(c, "<strike>", strlen("<strike>"))) { @@ -1402,7 +1423,8 @@ pt->dest_tag = "span"; tags = g_list_prepend(tags, pt); c = strchr(c, '>') + 1; - xhtml = g_string_append(xhtml, "<span style='text-decoration: line-through;'>"); + if(xhtml) + xhtml = g_string_append(xhtml, "<span style='text-decoration: line-through;'>"); continue; } if(!g_ascii_strncasecmp(c, "<sub>", 5)) { @@ -1411,7 +1433,8 @@ pt->dest_tag = "span"; tags = g_list_prepend(tags, pt); c = strchr(c, '>') + 1; - xhtml = g_string_append(xhtml, "<span style='vertical-align:sub;'>"); + if(xhtml) + xhtml = g_string_append(xhtml, "<span style='vertical-align:sub;'>"); continue; } if(!g_ascii_strncasecmp(c, "<sup>", 5)) { @@ -1420,7 +1443,8 @@ pt->dest_tag = "span"; tags = g_list_prepend(tags, pt); c = strchr(c, '>') + 1; - xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>"); + if(xhtml) + xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>"); continue; } if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) { @@ -1514,7 +1538,10 @@ pt->dest_tag = "span"; tags = g_list_prepend(tags, pt); if(style->len) - g_string_append_printf(xhtml, "<span style='%s'>", g_strstrip(style->str)); + { + if(xhtml) + g_string_append_printf(xhtml, "<span style='%s'>", g_strstrip(style->str)); + } else pt->ignore = TRUE; g_string_free(style, TRUE); @@ -1534,7 +1561,8 @@ color = g_string_append_c(color, *q); q++; } - g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str)); + if(xhtml) + g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str)); g_string_free(color, TRUE); if ((c = strchr(c, '>')) != NULL) c++; @@ -1555,14 +1583,17 @@ if(!g_ascii_strncasecmp(c, "<!--", strlen("<!--"))) { char *p = strstr(c + strlen("<!--"), "-->"); if(p) { - xhtml = g_string_append(xhtml, "<!--"); + if(xhtml) + xhtml = g_string_append(xhtml, "<!--"); c += strlen("<!--"); continue; } } - xhtml = g_string_append(xhtml, "<"); - plain = g_string_append_c(plain, '<'); + if(xhtml) + xhtml = g_string_append(xhtml, "<"); + if(plain) + plain = g_string_append_c(plain, '<'); c++; } } else if(*c == '&') { @@ -1575,29 +1606,31 @@ g_snprintf(buf, sizeof(buf), "%c", *c); pln = buf; } - xhtml = g_string_append_len(xhtml, c, len); - plain = g_string_append(plain, pln); + if(xhtml) + xhtml = g_string_append_len(xhtml, c, len); + if(plain) + plain = g_string_append(plain, pln); c += len; } else { - xhtml = g_string_append_c(xhtml, *c); - plain = g_string_append_c(plain, *c); + if(xhtml) + xhtml = g_string_append_c(xhtml, *c); + if(plain) + plain = g_string_append_c(plain, *c); c++; } } - tag = tags; - while(tag) { - struct purple_parse_tag *pt = tag->data; - if(!pt->ignore) - g_string_append_printf(xhtml, "</%s>", pt->dest_tag); - tag = tag->next; + if(xhtml) { + for (tag = tags; tag ; tag = tag->next) { + struct purple_parse_tag *pt = tag->data; + if(!pt->ignore) + g_string_append_printf(xhtml, "</%s>", pt->dest_tag); + } } g_list_free(tags); if(xhtml_out) - *xhtml_out = g_strdup(xhtml->str); + *xhtml_out = g_string_free(xhtml, FALSE); if(plain_out) - *plain_out = g_strdup(plain->str); - g_string_free(xhtml, TRUE); - g_string_free(plain, TRUE); + *plain_out = g_string_free(plain, FALSE); } /* The following are probably reasonable changes: @@ -2621,14 +2654,17 @@ if (len >= 4) { - if (!strncmp((char *)data, "BM", 2)) - return "bmp"; - else if (!strncmp((char *)data, "GIF8", 4)) + if (!strncmp((char *)data, "GIF8", 4)) return "gif"; - else if (!strncmp((char *)data, "\xff\xd8\xff\xe0", 4)) + else if (!strncmp((char *)data, "\xff\xd8\xff", 3)) /* 4th may be e0 through ef */ return "jpg"; else if (!strncmp((char *)data, "\x89PNG", 4)) return "png"; + else if (!strncmp((char *)data, "MM", 2) || + !strncmp((char *)data, "II", 2)) + return "tif"; + else if (!strncmp((char *)data, "BM", 2)) + return "bmp"; } return "icon"; @@ -3186,6 +3222,17 @@ g_hash_table_destroy(params); } +/* + * TODO: Should probably add a "gboolean *ret_ishttps" parameter that + * is set to TRUE if this URL is https, otherwise it is set to + * FALSE. But that change will break the API. + * + * This is important for Yahoo! web messenger login. They now + * force https login, and if you access the web messenger login + * page via http then it redirects you to the https version, but + * purple_util_fetch_url() ignores the "https" and attempts to + * fetch the URL via http again, which gets redirected again. + */ gboolean purple_url_parse(const char *url, char **ret_host, int *ret_port, char **ret_path, char **ret_user, char **ret_passwd) @@ -3206,12 +3253,16 @@ g_return_val_if_fail(url != NULL, FALSE); - if ((turl = strstr(url, "http://")) != NULL || - (turl = strstr(url, "HTTP://")) != NULL) + if ((turl = purple_strcasestr(url, "http://")) != NULL) { turl += 7; url = turl; } + else if ((turl = purple_strcasestr(url, "https://")) != NULL) + { + turl += 8; + url = turl; + } /* parse out authentication information if supplied */ /* Only care about @ char BEFORE the first / */ @@ -3291,85 +3342,92 @@ PurpleUtilFetchUrlData *gfud) { gchar *s; - - if ((s = g_strstr_len(data, data_len, "Location: ")) != NULL) + gchar *new_url, *temp_url, *end; + gboolean full; + int len; + + if ((s = g_strstr_len(data, data_len, "Location: ")) == NULL) + /* We're not being redirected */ + return FALSE; + + s += strlen("Location: "); + end = strchr(s, '\r'); + + /* Just in case :) */ + if (end == NULL) + end = strchr(s, '\n'); + + if (end == NULL) + return FALSE; + + len = end - s; + + new_url = g_malloc(len + 1); + strncpy(new_url, s, len); + new_url[len] = '\0'; + + full = gfud->full; + + if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL) { - gchar *new_url, *temp_url, *end; - gboolean full; - int len; - - s += strlen("Location: "); - end = strchr(s, '\r'); - - /* Just in case :) */ - if (end == NULL) - end = strchr(s, '\n'); - - if (end == NULL) - return FALSE; - - len = end - s; - - new_url = g_malloc(len + 1); - strncpy(new_url, s, len); - new_url[len] = '\0'; - - full = gfud->full; - - if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL) - { - temp_url = new_url; - - new_url = g_strdup_printf("%s:%d%s", gfud->website.address, - gfud->website.port, temp_url); - - g_free(temp_url); - - full = FALSE; - } - - purple_debug_info("util", "Redirecting to %s\n", new_url); - - /* - * Try again, with this new location. This code is somewhat - * ugly, but we need to reuse the gfud because whoever called - * us is holding a reference to it. - */ - g_free(gfud->url); - gfud->url = new_url; - gfud->full = full; - g_free(gfud->request); - gfud->request = NULL; - - purple_input_remove(gfud->inpa); - gfud->inpa = 0; - close(gfud->fd); - gfud->fd = -1; - gfud->request_written = 0; - gfud->len = 0; - gfud->data_len = 0; - - g_free(gfud->website.user); - g_free(gfud->website.passwd); - g_free(gfud->website.address); - g_free(gfud->website.page); - purple_url_parse(new_url, &gfud->website.address, &gfud->website.port, - &gfud->website.page, &gfud->website.user, &gfud->website.passwd); - - gfud->connect_data = purple_proxy_connect(NULL, NULL, - gfud->website.address, gfud->website.port, - url_fetch_connect_cb, gfud); - - if (gfud->connect_data == NULL) - { - purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), - gfud->website.address); - } - + temp_url = new_url; + + new_url = g_strdup_printf("%s:%d%s", gfud->website.address, + gfud->website.port, temp_url); + + g_free(temp_url); + + full = FALSE; + } + + purple_debug_info("util", "Redirecting to %s\n", new_url); + + gfud->num_times_redirected++; + if (gfud->num_times_redirected >= 5) + { + purple_util_fetch_url_error(gfud, + _("Could not open %s: Redirected too many times"), + gfud->url); return TRUE; } - return FALSE; + /* + * Try again, with this new location. This code is somewhat + * ugly, but we need to reuse the gfud because whoever called + * us is holding a reference to it. + */ + g_free(gfud->url); + gfud->url = new_url; + gfud->full = full; + g_free(gfud->request); + gfud->request = NULL; + + purple_input_remove(gfud->inpa); + gfud->inpa = 0; + close(gfud->fd); + gfud->fd = -1; + gfud->request_written = 0; + gfud->len = 0; + gfud->data_len = 0; + + g_free(gfud->website.user); + g_free(gfud->website.passwd); + g_free(gfud->website.address); + g_free(gfud->website.page); + purple_url_parse(new_url, &gfud->website.address, &gfud->website.port, + &gfud->website.page, &gfud->website.user, &gfud->website.passwd); + + gfud->connect_data = purple_proxy_connect(NULL, NULL, + gfud->website.address, gfud->website.port, + url_fetch_connect_cb, gfud); + + if (gfud->connect_data == NULL) + { + purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), + gfud->website.address); + } + + return TRUE; } static size_t
--- a/libpurple/win32/global.mak Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/win32/global.mak Wed Jun 06 00:58:00 2007 +0000 @@ -14,7 +14,7 @@ GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.6 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0 GTK_BIN ?= $(GTK_TOP)/bin -HOWL_TOP ?= $(WIN32_DEV_TOP)/howl-1.0.0 +BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2 MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2 NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4
--- a/libpurple/xmlnode.c Wed Jun 06 00:57:17 2007 +0000 +++ b/libpurple/xmlnode.c Wed Jun 06 00:58:00 2007 +0000 @@ -519,38 +519,38 @@ } static xmlSAXHandler xmlnode_parser_libxml = { - .internalSubset = NULL, - .isStandalone = NULL, - .hasInternalSubset = NULL, - .hasExternalSubset = NULL, - .resolveEntity = NULL, - .getEntity = NULL, - .entityDecl = NULL, - .notationDecl = NULL, - .attributeDecl = NULL, - .elementDecl = NULL, - .unparsedEntityDecl = NULL, - .setDocumentLocator = NULL, - .startDocument = NULL, - .endDocument = NULL, - .startElement = NULL, - .endElement = NULL, - .reference = NULL, - .characters = xmlnode_parser_element_text_libxml, - .ignorableWhitespace = NULL, - .processingInstruction = NULL, - .comment = NULL, - .warning = NULL, - .error = xmlnode_parser_error_libxml, - .fatalError = NULL, - .getParameterEntity = NULL, - .cdataBlock = NULL, - .externalSubset = NULL, - .initialized = XML_SAX2_MAGIC, - ._private = NULL, - .startElementNs = xmlnode_parser_element_start_libxml, - .endElementNs = xmlnode_parser_element_end_libxml, - .serror = NULL + NULL, /* internalSubset */ + NULL, /* isStandalone */ + NULL, /* hasInternalSubset */ + NULL, /* hasExternalSubset */ + NULL, /* resolveEntity */ + NULL, /* getEntity */ + NULL, /* entityDecl */ + NULL, /* notationDecl */ + NULL, /* attributeDecl */ + NULL, /* elementDecl */ + NULL, /* unparsedEntityDecl */ + NULL, /* setDocumentLocator */ + NULL, /* startDocument */ + NULL, /* endDocument */ + NULL, /* startElement */ + NULL, /* endElement */ + NULL, /* reference */ + xmlnode_parser_element_text_libxml, /* characters */ + NULL, /* ignorableWhitespace */ + NULL, /* processingInstruction */ + NULL, /* comment */ + NULL, /* warning */ + xmlnode_parser_error_libxml, /* error */ + NULL, /* fatalError */ + NULL, /* getParameterEntity */ + NULL, /* cdataBlock */ + NULL, /* externalSubset */ + XML_SAX2_MAGIC, /* initialized */ + NULL, /* _private */ + xmlnode_parser_element_start_libxml, /* startElementNs */ + xmlnode_parser_element_end_libxml, /* endElementNs */ + NULL, /* serror */ }; xmlnode *
--- a/pidgin/gtkaccount.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/gtkaccount.c Wed Jun 06 00:58:00 2007 +0000 @@ -184,6 +184,7 @@ gtk_size_group_add_widget(dialog->sg, label); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), widget); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, PIDGIN_HIG_BORDER); @@ -426,16 +427,18 @@ gtk_widget_ref(dialog->protocol_menu); } - hbox = add_pref_box(dialog, vbox, _("Protocol:"), dialog->protocol_menu); + hbox = add_pref_box(dialog, vbox, _("Pro_tocol:"), dialog->protocol_menu); g_object_set_data(G_OBJECT(dialog->protocol_menu), "container", hbox); gtk_widget_unref(dialog->protocol_menu); /* Screen name */ dialog->screenname_entry = gtk_entry_new(); +#if GTK_CHECK_VERSION(2,10,0) g_object_set(G_OBJECT(dialog->screenname_entry), "truncate-multiline", TRUE, NULL); - - add_pref_box(dialog, vbox, _("Screen name:"), dialog->screenname_entry); +#endif + + add_pref_box(dialog, vbox, _("Screen _name:"), dialog->screenname_entry); g_signal_connect(G_OBJECT(dialog->screenname_entry), "changed", G_CALLBACK(screenname_changed_cb), dialog); @@ -517,16 +520,16 @@ gtk_entry_set_visibility(GTK_ENTRY(dialog->password_entry), FALSE); if (gtk_entry_get_invisible_char(GTK_ENTRY(dialog->password_entry)) == '*') gtk_entry_set_invisible_char(GTK_ENTRY(dialog->password_entry), PIDGIN_INVISIBLE_CHAR); - dialog->password_box = add_pref_box(dialog, vbox, _("Password:"), + dialog->password_box = add_pref_box(dialog, vbox, _("_Password:"), dialog->password_entry); /* Alias */ dialog->alias_entry = gtk_entry_new(); - add_pref_box(dialog, vbox, _("Local alias:"), dialog->alias_entry); + add_pref_box(dialog, vbox, _("Local _alias:"), dialog->alias_entry); /* Remember Password */ dialog->remember_pass_check = - gtk_check_button_new_with_label(_("Remember password")); + gtk_check_button_new_with_mnemonic(_("Remember pass_word")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check), FALSE); gtk_box_pack_start(GTK_BOX(vbox), dialog->remember_pass_check, @@ -597,12 +600,12 @@ /* New mail notifications */ dialog->new_mail_check = - gtk_check_button_new_with_label(_("New mail notifications")); + gtk_check_button_new_with_mnemonic(_("New _mail notifications")); gtk_box_pack_start(GTK_BOX(vbox), dialog->new_mail_check, FALSE, FALSE, 0); gtk_widget_show(dialog->new_mail_check); /* Buddy icon */ - dialog->icon_check = gtk_check_button_new_with_label(_("Use this buddy icon for this account:")); + dialog->icon_check = gtk_check_button_new_with_mnemonic(_("Use this buddy _icon for this account:")); g_signal_connect(G_OBJECT(dialog->icon_check), "toggled", G_CALLBACK(icon_check_cb), dialog); gtk_widget_show(dialog->icon_check); gtk_box_pack_start(GTK_BOX(vbox), dialog->icon_check, FALSE, FALSE, 0); @@ -1021,7 +1024,7 @@ if (dialog->proxy_frame != NULL) gtk_widget_destroy(dialog->proxy_frame); - frame = pidgin_make_frame(parent, _("Proxy Options")); + frame = pidgin_make_frame(parent, _("Pro_xy Options")); dialog->proxy_frame = gtk_widget_get_parent(gtk_widget_get_parent(frame)); gtk_box_reorder_child(GTK_BOX(parent), dialog->proxy_frame, 1);
--- a/pidgin/gtkblist.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/gtkblist.c Wed Jun 06 00:58:00 2007 +0000 @@ -266,8 +266,6 @@ purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/width", event->width); purple_prefs_set_int(PIDGIN_PREFS_ROOT "/blist/height", event->height); - gtk_widget_set_size_request(gtkblist->headline_label, - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/blist/width")-25,-1); /* continue to handle event normally */ return FALSE; } @@ -1124,10 +1122,10 @@ if (((PurpleBlistNode*)buddy)->parent->child->next && !sub && !contact_expanded) { pidgin_separator(menu); pidgin_append_blist_node_privacy_menu(menu, (PurpleBlistNode *)buddy); - pidgin_new_item_from_stock(menu, _("Alias..."), PIDGIN_STOCK_ALIAS, + pidgin_new_item_from_stock(menu, _("_Alias..."), PIDGIN_STOCK_ALIAS, G_CALLBACK(gtk_blist_menu_alias_cb), contact, 0, 0, NULL); - pidgin_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE, + pidgin_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(pidgin_blist_remove_cb), contact, 0, 0, NULL); } else if (!sub || contact_expanded) { @@ -4737,8 +4735,15 @@ NODE_COLUMN, &new_selection, -1); } - /* we set this up as a timeout, otherwise the blist flickers */ - g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); + /* we set this up as a timeout, otherwise the blist flickers ... + * but we don't do it for groups, because it causes total bizarness - + * the previously selected buddy node might rendered at half height. + */ + if ((new_selection != NULL) && PURPLE_BLIST_NODE_IS_GROUP(new_selection)) { + do_selection_changed(new_selection); + } else { + g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); + } } static gboolean insert_node(PurpleBuddyList *list, PurpleBlistNode *node, GtkTreeIter *iter) @@ -5399,12 +5404,25 @@ gtk_container_set_border_width(GTK_CONTAINER(table), 0); gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); - label = gtk_label_new(_("Screen name:")); + /* Set up stuff for the account box */ + label = gtk_label_new_with_mnemonic(_("_Account:")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); + data->account_box = pidgin_account_option_menu_new(account, FALSE, + G_CALLBACK(add_buddy_select_account_cb), NULL, data); + + gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 0, 1); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_box); + pidgin_set_accessible_label (data->account_box, label); + /* End of account box */ + + label = gtk_label_new_with_mnemonic(_("_Screen name:")); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); + data->entry = gtk_entry_new(); - gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 0, 1); + gtk_table_attach_defaults(GTK_TABLE(table), data->entry, 1, 2, 1, 2); gtk_widget_grab_focus(data->entry); if (username != NULL) @@ -5414,19 +5432,20 @@ GTK_RESPONSE_OK, FALSE); gtk_entry_set_activates_default (GTK_ENTRY(data->entry), TRUE); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry); pidgin_set_accessible_label (data->entry, label); g_signal_connect(G_OBJECT(data->entry), "changed", G_CALLBACK(pidgin_set_sensitive_if_input), data->window); - label = gtk_label_new(_("Alias:")); + label = gtk_label_new_with_mnemonic(_("A_lias:")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); data->entry_for_alias = gtk_entry_new(); gtk_table_attach_defaults(GTK_TABLE(table), - data->entry_for_alias, 1, 2, 1, 2); + data->entry_for_alias, 1, 2, 2, 3); if (alias != NULL) gtk_entry_set_text(GTK_ENTRY(data->entry_for_alias), alias); @@ -5435,29 +5454,19 @@ gtk_widget_grab_focus(GTK_WIDGET(data->entry_for_alias)); gtk_entry_set_activates_default (GTK_ENTRY(data->entry_for_alias), TRUE); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->entry_for_alias); pidgin_set_accessible_label (data->entry_for_alias, label); - label = gtk_label_new(_("Group:")); + label = gtk_label_new_with_mnemonic(_("_Group:")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); data->combo = gtk_combo_new(); gtk_combo_set_popdown_strings(GTK_COMBO(data->combo), groups_tree()); - gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 2, 3); + gtk_table_attach_defaults(GTK_TABLE(table), data->combo, 1, 2, 3, 4); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_COMBO(data->combo)->entry); pidgin_set_accessible_label (data->combo, label); - /* Set up stuff for the account box */ - label = gtk_label_new(_("Account:")); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); - gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); - - data->account_box = pidgin_account_option_menu_new(account, FALSE, - G_CALLBACK(add_buddy_select_account_cb), NULL, data); - - gtk_table_attach_defaults(GTK_TABLE(table), data->account_box, 1, 2, 3, 4); - pidgin_set_accessible_label (data->account_box, label); - /* End of account box */ - g_signal_connect(G_OBJECT(data->window), "response", G_CALLBACK(add_buddy_cb), data); @@ -5763,7 +5772,7 @@ rowbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); - label = gtk_label_new(_("Account:")); + label = gtk_label_new_with_mnemonic(_("_Account:")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_size_group_add_widget(data->sg, label); gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); @@ -5772,6 +5781,7 @@ G_CALLBACK(addchat_select_account_cb), chat_account_filter_func, data); gtk_box_pack_start(GTK_BOX(rowbox), data->account_menu, TRUE, TRUE, 0); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->account_menu); pidgin_set_accessible_label (data->account_menu, label); data->entries_box = gtk_vbox_new(FALSE, 5); @@ -5783,7 +5793,7 @@ rowbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); - label = gtk_label_new(_("Alias:")); + label = gtk_label_new_with_mnemonic(_("A_lias:")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_size_group_add_widget(data->sg, label); gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); @@ -5793,6 +5803,7 @@ gtk_entry_set_text(GTK_ENTRY(data->alias_entry), alias); gtk_box_pack_end(GTK_BOX(rowbox), data->alias_entry, TRUE, TRUE, 0); gtk_entry_set_activates_default(GTK_ENTRY(data->alias_entry), TRUE); + gtk_label_set_mnemonic_widget(GTK_LABEL(label), data->alias_entry); pidgin_set_accessible_label (data->alias_entry, label); if (name != NULL) gtk_widget_grab_focus(data->alias_entry); @@ -5800,7 +5811,7 @@ rowbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(vbox), rowbox, FALSE, FALSE, 0); - label = gtk_label_new(_("Group:")); + label = gtk_label_new_with_mnemonic(_("_Group:")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_size_group_add_widget(data->sg, label); gtk_box_pack_start(GTK_BOX(rowbox), label, FALSE, FALSE, 0); @@ -5814,6 +5825,7 @@ gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(data->group_combo)->entry), group->name); } + gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_COMBO(data->group_combo)->entry); pidgin_set_accessible_label (data->group_combo, label); g_signal_connect(G_OBJECT(data->window), "response",
--- a/pidgin/gtkconv.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/gtkconv.c Wed Jun 06 00:58:00 2007 +0000 @@ -2897,14 +2897,25 @@ GList *list; PidginConversation *gtkconv; PurpleConversation *conv; - PurpleBuddy *buddy; + PurpleBlistNode *node = NULL; + PurpleChat *chat = NULL; + PurpleBuddy *buddy = NULL; gtkconv = pidgin_conv_window_get_active_gtkconv(win); conv = gtkconv->active_conv; - buddy = purple_find_buddy(conv->account, conv->name); menu = gtk_item_factory_get_widget(win->menu.item_factory, N_("/Conversation/More")); + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) + chat = purple_blist_find_chat(conv->account, conv->name); + else + buddy = purple_find_buddy(conv->account, conv->name); + + if (chat) + node = (PurpleBlistNode *)chat; + else if (buddy) + node = (PurpleBlistNode *)buddy; + /* Remove the previous entries */ for (list = gtk_container_get_children(GTK_CONTAINER(menu)); list; ) { @@ -2914,12 +2925,11 @@ } /* Now add the stuff */ - if (buddy) + if (node) { if (purple_account_is_connected(conv->account)) - pidgin_append_blist_node_proto_menu(menu, conv->account->gc, - (PurpleBlistNode *)buddy); - pidgin_append_blist_node_extended_menu(menu, (PurpleBlistNode *)buddy); + pidgin_append_blist_node_proto_menu(menu, conv->account->gc, node); + pidgin_append_blist_node_extended_menu(menu, node); } if ((list = gtk_container_get_children(GTK_CONTAINER(menu))) == NULL)
--- a/pidgin/gtkimhtml.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/gtkimhtml.c Wed Jun 06 00:58:00 2007 +0000 @@ -140,6 +140,10 @@ }; static guint signals [LAST_SIGNAL] = { 0 }; +static char *html_clipboard = NULL; +static char *text_clipboard = NULL; +GtkClipboard *clipboard_selection = NULL; + static GtkTargetEntry selection_targets[] = { #ifndef _WIN32 { "text/html", 0, TARGET_HTML }, @@ -871,14 +875,17 @@ static void gtk_imhtml_clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, GtkIMHtml *imhtml) { char *text = NULL; - gboolean primary; + gboolean primary = (clipboard != clipboard_selection); GtkTextIter start, end; - GtkTextMark *sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer); - GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); - - gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel); - gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins); - primary = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY) == clipboard; + GtkTextMark *sel = NULL; + GtkTextMark *ins = NULL; + + if (primary) { + ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer); + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel); + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins); + } if (info == TARGET_HTML) { char *selection; @@ -888,7 +895,7 @@ if (primary) { text = gtk_imhtml_get_markup_range(imhtml, &start, &end); } else - text = imhtml->clipboard_html_string; + text = html_clipboard; /* Mozilla asks that we start our text/html with the Unicode byte order mark */ str = g_string_append_unichar(str, 0xfeff); @@ -906,7 +913,7 @@ if (primary) { text = gtk_imhtml_get_text(imhtml, &start, &end); } else - text = imhtml->clipboard_text_string; + text = text_clipboard; gtk_selection_data_set_text(selection_data, text, strlen(text)); } if (primary) /* This was allocated here */ @@ -929,20 +936,32 @@ &insert); } +static void gtk_imhtml_clipboard_clear (GtkClipboard *clipboard, GtkSelectionData *sel_data, + guint info, gpointer user_data_or_owner) +{ + clipboard_selection = NULL; +} + static void copy_clipboard_cb(GtkIMHtml *imhtml, gpointer unused) { GtkTextIter start, end; if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) { - gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD), + if (!clipboard_selection) + clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_with_owner(clipboard_selection, selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry), (GtkClipboardGetFunc)gtk_imhtml_clipboard_get, - (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml)); + (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, G_OBJECT(imhtml)); g_free(imhtml->clipboard_html_string); g_free(imhtml->clipboard_text_string); imhtml->clipboard_html_string = gtk_imhtml_get_markup_range(imhtml, &start, &end); imhtml->clipboard_text_string = gtk_imhtml_get_text(imhtml, &start, &end); + + text_clipboard = imhtml->clipboard_text_string; + html_clipboard = imhtml->clipboard_html_string; + } g_signal_stop_emission_by_name(imhtml, "copy-clipboard"); @@ -952,10 +971,12 @@ { GtkTextIter start, end; if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) { - gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD), + if (!clipboard_selection) + clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_with_owner(clipboard_selection, selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry), (GtkClipboardGetFunc)gtk_imhtml_clipboard_get, - (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml)); + (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, G_OBJECT(imhtml)); g_free(imhtml->clipboard_html_string); g_free(imhtml->clipboard_text_string); @@ -963,6 +984,9 @@ imhtml->clipboard_html_string = gtk_imhtml_get_markup_range(imhtml, &start, &end); imhtml->clipboard_text_string = gtk_imhtml_get_text(imhtml, &start, &end); + text_clipboard = imhtml->clipboard_text_string; + html_clipboard = imhtml->clipboard_html_string; + if (imhtml->editable) gtk_text_buffer_delete_selection(imhtml->text_buffer, FALSE, FALSE); } @@ -1199,17 +1223,18 @@ g_free(img_data); } - if (imhtml->clipboard_text_string) { - g_free(imhtml->clipboard_text_string); - g_free(imhtml->clipboard_html_string); - } - g_list_free(imhtml->scalables); g_slist_free(imhtml->im_images); g_queue_free(imhtml->animations); g_free(imhtml->protocol_name); g_free(imhtml->search_string); G_OBJECT_CLASS(parent_class)->finalize (object); + if (clipboard_selection) + gtk_clipboard_set_with_owner(clipboard_selection, + selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry), + (GtkClipboardGetFunc)gtk_imhtml_clipboard_get, + (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml)); + } /* Boring GTK+ stuff */
--- a/pidgin/gtkthemes.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/gtkthemes.c Wed Jun 06 00:58:00 2007 +0000 @@ -146,6 +146,9 @@ list->next = child; else theme->list = child; + /* Reverse the Smiley list since it was built in reverse order for efficiency reasons */ + if (list != NULL) + list->smileys = g_slist_reverse(list->smileys); list = child; } else if (!g_ascii_strncasecmp(i, "Name=", strlen("Name="))) { int len; @@ -201,14 +204,16 @@ } - /* Reverse the Smiley list since it was built in reverse order for efficiency reasons */ - list->smileys = g_slist_reverse(list->smileys); if (!have_used_sfile) g_free(sfile); } } + /* Reverse the Smiley list since it was built in reverse order for efficiency reasons */ + if (list != NULL) + list->smileys = g_slist_reverse(list->smileys); + g_free(dirname); fclose(f);
--- a/pidgin/gtkutils.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/gtkutils.c Wed Jun 06 00:58:00 2007 +0000 @@ -612,6 +612,7 @@ g_object_unref(pixbuf); gtalk_name = NULL; + i++; } pixbuf = get_prpl_pixbuf(prpl_info); @@ -3033,20 +3034,20 @@ row = pixels; for (i = 3; i < rowstride; i+=4) { - if (row[i] != 0xff) + if (row[i] < 0xfe) return FALSE; } for (i = 1; i < height - 1; i++) { row = pixels + (i*rowstride); - if (row[3] != 0xff || row[rowstride-1] != 0xff) { + if (row[3] < 0xfe || row[rowstride-1] < 0xfe) { return FALSE; - } + } } row = pixels + ((height-1) * rowstride); for (i = 3; i < rowstride; i+=4) { - if (row[i] != 0xff) + if (row[i] < 0xfe) return FALSE; }
--- a/pidgin/plugins/relnot.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/plugins/relnot.c Wed Jun 06 00:58:00 2007 +0000 @@ -69,20 +69,17 @@ message = g_string_new(""); g_string_append_printf(message, _("You are using %s version %s. The " - "current version is %s.<hr>"), + "current version is %s. You can get it from " + "<a href=\"" PURPLE_WEBSITE "\">" PURPLE_WEBSITE "</a><hr>"), PIDGIN_NAME, purple_core_get_version(), cur_ver); if(*changelog) { formatted = purple_strdup_withhtml(changelog); - g_string_append_printf(message, _("<b>ChangeLog:</b>\n%s<br><br>"), + g_string_append_printf(message, _("<b>ChangeLog:</b><br>%s"), formatted); g_free(formatted); } - g_string_append_printf(message, _("You can get version %s from:<br>" - "<a href=\"http://pidgin.im/\">" - "http://pidgin.im</a>."), cur_ver); - purple_notify_formatted(NULL, _("New Version Available"), _("New Version Available"), NULL, message->str, NULL, NULL);
--- a/pidgin/plugins/xmppconsole.c Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/plugins/xmppconsole.c Wed Jun 06 00:58:00 2007 +0000 @@ -183,16 +183,17 @@ char *text; gc = console->gc; - - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); - + + if (gc) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry)); gtk_text_buffer_get_start_iter(buffer, &start); gtk_text_buffer_get_end_iter(buffer, &end); - + text = gtk_imhtml_get_text(GTK_IMHTML(console->entry), &start, &end); - - if (gc && prpl_info->convo_closed != NULL) + + if (prpl_info && prpl_info->send_raw != NULL) prpl_info->send_raw(gc, text, strlen(text)); g_free(text);
--- a/pidgin/win32/nsis/pidgin-installer.nsi Wed Jun 06 00:57:17 2007 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Jun 06 00:58:00 2007 +0000 @@ -690,6 +690,7 @@ Delete "$INSTDIR\plugins\iconaway.dll" Delete "$INSTDIR\plugins\idle.dll" Delete "$INSTDIR\plugins\libaim.dll" + Delete "$INSTDIR\plugins\libbonjour.dll" Delete "$INSTDIR\plugins\libgg.dll" Delete "$INSTDIR\plugins\libicq.dll" Delete "$INSTDIR\plugins\libirc.dll"