# HG changeset patch # User Paul Aurich # Date 1237607343 0 # Node ID e4a060a1e3defe100f1b1a9f49f34f6894fe0cdb # Parent c605e5f2fc993d7c857f732918ad9b2a2a49ff79# Parent b23211876f478a1c92e9c7b89e4d5792ac8eec0e propagate from branch 'im.pidgin.pidgin' (head d70375c084af2336e7fc6646fb778120b05de9a8) to branch 'im.pidgin.cpw.darkrain42.xmpp.bosh' (head 3f04f4a917efd4f6e3468064c845e9ffd6a252b1) diff -r c605e5f2fc99 -r e4a060a1e3de COPYRIGHT --- a/COPYRIGHT Sat Mar 21 03:03:18 2009 +0000 +++ b/COPYRIGHT Sat Mar 21 03:49:03 2009 +0000 @@ -8,6 +8,7 @@ Dave Ahlswede Manuel Amador Matt Amato +Josef Andrysek Geoffrey Antos Daniel Atallah Paul Aurich @@ -262,6 +263,7 @@ Lucio Maciel Brian Macke Paolo Maggi +Sulabh Mahajan Willian T. Mahan Kris Marsh Fidel Martinez @@ -431,6 +433,7 @@ Amir Szekely (kichik) Robert T. Greg Taeger +Rob Taft Peter Tang Brian Tarricone Peter Teichman diff -r c605e5f2fc99 -r e4a060a1e3de ChangeLog --- a/ChangeLog Sat Mar 21 03:03:18 2009 +0000 +++ b/ChangeLog Sat Mar 21 03:49:03 2009 +0000 @@ -6,8 +6,9 @@ project. With some minor additions and clean ups from Paul Aurich. XMPP: - * Add support for in-band bytestreams (XEP-0047). - * Add support for attention (XEP-0224). + * Add support for in-band bytestreams for file transfers (XEP-0047). + * Add support for sending attentions (equivalent to "buzz" and "nudge") + using the command /buzz (XEP-0224). Pidgin: * Added -f command line option to tell Pidgin to ignore NetworkManager @@ -18,6 +19,9 @@ * Pressing the Enter key in the message entry box of the New Status dialog and various other dialogs now causes the cursor to move to the next line. + * Created a unified Buddy Pounce notification window for all pounces + where "Pop up a notification" is selected, which avoids having a + new dialog box every time a pounce is triggered. (Jorge VillaseƱor) version 2.5.5 (03/01/2009): libpurple: diff -r c605e5f2fc99 -r e4a060a1e3de ChangeLog.API --- a/ChangeLog.API Sat Mar 21 03:03:18 2009 +0000 +++ b/ChangeLog.API Sat Mar 21 03:49:03 2009 +0000 @@ -26,6 +26,14 @@ * purple_request_field_set_ui_data * purple_strequal * xmlnode_from_file + * xmlnode_set_attrib_full + + Changed: + * xmlnode_remove_attrib now removes all attributes with the + same name. Previously, it would remove the first one found, + which was completely non-deterministic. If you want to remove + the attribute with no namespace, then use NULL with + xmlnode_remove_with_namespace. Deprecated: * purple_buddy_get_local_alias @@ -40,6 +48,8 @@ * purple_status_set_attr_string * purple_presence_add_status * purple_presence_add_list + * xmlnode_set_attrib_with_namespace + * xmlnode_set_attrib_with_prefix pidgin: Added: @@ -52,6 +62,7 @@ * pidgin_blist_get_theme * pidgin_sound_is_customized * pidgin_utils_init, pidgin_utils_uninit + * pidgin_notify_pounce_add perl: Changed: diff -r c605e5f2fc99 -r e4a060a1e3de finch/gntlog.c --- a/finch/gntlog.c Sat Mar 21 03:03:18 2009 +0000 +++ b/finch/gntlog.c Sat Mar 21 03:49:03 2009 +0000 @@ -66,7 +66,7 @@ g_str_hash(purple_account_get_username(viewer->account)); } - return (guint)viewer; + return g_direct_hash(viewer); } static gboolean log_viewer_equal(gconstpointer y, gconstpointer z) diff -r c605e5f2fc99 -r e4a060a1e3de finch/gntplugin.c --- a/finch/gntplugin.c Sat Mar 21 03:03:18 2009 +0000 +++ b/finch/gntplugin.c Sat Mar 21 03:49:03 2009 +0000 @@ -484,10 +484,10 @@ char *value = NULL; switch(type) { case PURPLE_PREF_BOOLEAN: - value = g_strdup_printf("%d", (int)list->next->data); + value = g_strdup_printf("%d", GPOINTER_TO_INT(list->next->data)); break; case PURPLE_PREF_INT: - value = g_strdup_printf("%d", (int)list->next->data); + value = g_strdup_printf("%d", GPOINTER_TO_INT(list->next->data)); break; case PURPLE_PREF_STRING: value = g_strdup(list->next->data); diff -r c605e5f2fc99 -r e4a060a1e3de finch/gntroomlist.c --- a/finch/gntroomlist.c Sat Mar 21 03:03:18 2009 +0000 +++ b/finch/gntroomlist.c Sat Mar 21 03:49:03 2009 +0000 @@ -190,7 +190,7 @@ label = g_strdup(iter->data ? "True" : "False"); break; case PURPLE_ROOMLIST_FIELD_INT: - label = g_strdup_printf("%d", (int)iter->data); + label = g_strdup_printf("%d", GPOINTER_TO_INT(iter->data)); break; case PURPLE_ROOMLIST_FIELD_STRING: label = g_strdup(iter->data); diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/account.h --- a/libpurple/account.h Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/account.h Sat Mar 21 03:49:03 2009 +0000 @@ -42,6 +42,7 @@ #include "connection.h" #include "log.h" +#include "privacy.h" #include "proxy.h" #include "prpl.h" #include "status.h" @@ -141,7 +142,7 @@ */ GSList *permit; /**< Permit list. */ GSList *deny; /**< Deny list. */ - int perm_deny; /**< The permit/deny setting. */ + PurplePrivacyType perm_deny; /**< The permit/deny setting. */ GList *status_types; /**< Status types. */ diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/blist.h --- a/libpurple/blist.h Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/blist.h Sat Mar 21 03:49:03 2009 +0000 @@ -595,7 +595,7 @@ * @param contact The optional contact to place the buddy in. * @param group The group to add the new buddy to. * @param node The insertion point. Pass in NULL to add the node as - * the last child in the given group. + * the first child in the given group. */ void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node); diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/circbuffer.c --- a/libpurple/circbuffer.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/circbuffer.c Sat Mar 21 03:49:03 2009 +0000 @@ -68,7 +68,8 @@ /* If the fill pointer is wrapped to before the remove * pointer, we need to shift the data */ - if (in_offset < out_offset) { + if (in_offset < out_offset + || (in_offset == out_offset && buf->bufused > 0)) { int shift_n = MIN(buf->buflen - start_buflen, in_offset); memcpy(buf->buffer + start_buflen, buf->buffer, diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/dnssrv.c --- a/libpurple/dnssrv.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/dnssrv.c Sat Mar 21 03:49:03 2009 +0000 @@ -193,10 +193,12 @@ end: size = g_list_length(ret); + /* TODO: Check return value */ write(out, &(query.type), sizeof(query.type)); write(out, &size, sizeof(size)); while (ret != NULL) { + /* TODO: Check return value */ if (query.type == T_SRV) write(out, ret->data, sizeof(PurpleSrvResponse)); if (query.type == T_TXT) write(out, ret->data, sizeof(PurpleTxtResponse)); g_free(ret->data); diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/ft.c --- a/libpurple/ft.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/ft.c Sat Mar 21 03:49:03 2009 +0000 @@ -857,8 +857,12 @@ else s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); - if (xfer->ops.read != NULL) + if (xfer->ops.read != NULL) { r = (xfer->ops.read)(buffer, xfer); + if ((purple_xfer_get_size(xfer) > 0) && + ((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer))) + purple_xfer_set_completed(xfer, TRUE); + } else { *buffer = g_malloc0(s); diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/bonjour/parser.c --- a/libpurple/protocols/bonjour/parser.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/bonjour/parser.c Sat Mar 21 03:49:03 2009 +0000 @@ -91,14 +91,12 @@ xmlnode_set_namespace(node, (const char*) namespace); for(i=0; i < nb_attributes * 5; i+=5) { + const char *name = (const char *)attributes[i]; + const char *prefix = (const char *)attributes[i+1]; + const char *attrib_ns = (const char *)attributes[i+2]; char *txt; int attrib_len = attributes[i+4] - attributes[i+3]; char *attrib = g_malloc(attrib_len + 1); - char *attrib_ns = NULL; - - if (attributes[i+2]) { - attrib_ns = g_strdup((char*)attributes[i+2]); - } memcpy(attrib, attributes[i+3], attrib_len); attrib[attrib_len] = '\0'; @@ -106,9 +104,8 @@ txt = attrib; attrib = purple_unescape_html(txt); g_free(txt); - xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib); + xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib); g_free(attrib); - g_free(attrib_ns); } bconv->current = node; diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sat Mar 21 03:49:03 2009 +0000 @@ -2634,7 +2634,7 @@ return TRUE; } else { *error = g_strdup_printf(_("Unable to buzz, because %s does " - "not support it or do not wish to receive buzzes now."), alias); + "not support it or does not wish to receive buzzes now."), alias); return FALSE; } } diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/jabber/parser.c --- a/libpurple/protocols/jabber/parser.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/jabber/parser.c Sat Mar 21 03:49:03 2009 +0000 @@ -86,6 +86,8 @@ } } for(i=0; i < nb_attributes * 5; i+=5) { + const char *name = (const char *)attributes[i]; + const char *prefix = (const char *)attributes[i+1]; const char *attrib_ns = (const char *)attributes[i+2]; char *txt; int attrib_len = attributes[i+4] - attributes[i+3]; @@ -97,7 +99,7 @@ txt = attrib; attrib = purple_unescape_html(txt); g_free(txt); - xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib); + xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib); g_free(attrib); } diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/jabber/si.c --- a/libpurple/protocols/jabber/si.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/jabber/si.c Sat Mar 21 03:49:03 2009 +0000 @@ -1096,6 +1096,7 @@ "jabber_si_xfer_ibb_send_data: error reading from file\n"); purple_xfer_cancel_local(xfer); } + g_free(data); } static void diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Sat Mar 21 03:49:03 2009 +0000 @@ -4789,7 +4789,7 @@ status_html = purple_status_get_attr_string(status, "message"); - if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE) + if (status_html == NULL || primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE) { /* This is needed for us to un-set any previous away message. */ away = g_strdup(""); @@ -5030,10 +5030,15 @@ purple_debug_error("oscar", "ssi: SNAC error %hu\n", reason); if (reason == 0x0005) { - purple_notify_error(gc, NULL, _("Unable to Retrieve Buddy List"), - _("The AIM servers were temporarily unable to send your buddy list. Your buddy list is not lost, and will probably become available in a few minutes.")); if (od->getblisttimer > 0) purple_timeout_remove(od->getblisttimer); + else + /* We only show this error the first time it happens */ + purple_notify_error(gc, NULL, + _("Unable to Retrieve Buddy List"), + _("The AIM servers were temporarily unable to send " + "your buddy list. Your buddy list is not lost, and " + "will probably become available in a few minutes.")); od->getblisttimer = purple_timeout_add(30000, purple_ssi_rerequestdata, od); return 1; } diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sat Mar 21 03:49:03 2009 +0000 @@ -30,6 +30,7 @@ #include "cmds.h" #include "core.h" #include "debug.h" +#include "network.h" #include "notify.h" #include "privacy.h" #include "prpl.h" @@ -38,6 +39,7 @@ #include "server.h" #include "util.h" #include "version.h" +#include "xmlnode.h" #include "yahoo.h" #include "yahoochat.h" @@ -153,6 +155,7 @@ char *name = NULL; gboolean unicode = FALSE; char *message = NULL; + char *msn_name = NULL; if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { if (!purple_account_get_remember_password(account)) @@ -235,6 +238,8 @@ message = pair->value; break; case 11: /* this is the buddy's session id */ + if (f) + f->session_id = strtol(pair->value, NULL, 10); break; case 17: /* in chat? */ break; @@ -350,7 +355,12 @@ if(f && strtol(pair->value, NULL, 10)) f->version_id = strtol(pair->value, NULL, 10); break; - + case 241: /* protocol buddy belongs to */ + if(strtol(pair->value, NULL, 10) == 2) { + msn_name = g_strconcat("msn/", name, NULL); + name = msn_name; + } + break; default: purple_debug_warning("yahoo", "Unknown status key %d\n", pair->key); @@ -472,10 +482,13 @@ struct yahoo_data *yd = gc->proto_data; GHashTable *ht; char *norm_bud = NULL; + char *temp = NULL; YahooFriend *f = NULL; /* It's your friends. They're going to want you to share your StarBursts. */ /* But what if you had no friends? */ PurpleBuddy *b; PurpleGroup *g; + int protocol = 0; + int stealth = 0; ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_slist_free); @@ -499,6 +512,48 @@ break; case 301: /* This is 319 before all s/n's in a group after the first. It is followed by an identical 300. */ + if(temp != NULL) { + if(protocol == 2) + norm_bud = g_strconcat("msn/", temp, NULL); + else + norm_bud = g_strdup(temp); + + if (yd->current_list15_grp) { + /* This buddy is in a group */ + f = yahoo_friend_find_or_new(gc, norm_bud); + if (!(b = purple_find_buddy(account, norm_bud))) { + if (!(g = purple_find_group(yd->current_list15_grp))) { + g = purple_group_new(yd->current_list15_grp); + purple_blist_add_group(g, NULL); + } + b = purple_buddy_new(account, norm_bud, NULL); + purple_blist_add_buddy(b, NULL, g, NULL); + } + yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp); + if(protocol != 0) { + f->protocol = protocol; + purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol); + } + if(stealth == 2) + f->presence = YAHOO_PRESENCE_PERM_OFFLINE; + + /* set p2p status not connected and no p2p packet sent */ + if(protocol == 0) { + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + f->p2p_packet_sent = 0; + } else + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_DO_NOT_CONNECT); + } else { + /* This buddy is on the ignore list (and therefore in no group) */ + purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n",account->username, norm_bud); + purple_privacy_deny_add(account, norm_bud, 1); + } + + protocol = 0; + stealth = 0; + norm_bud = NULL; + temp = NULL; + } break; case 300: /* This is 318 before a group, 319 before any s/n in a group, and 320 before any ignored s/n. */ break; @@ -507,42 +562,16 @@ yd->current_list15_grp = yahoo_string_decode(gc, pair->value, FALSE); break; case 7: /* buddy's s/n */ - g_free(norm_bud); - norm_bud = g_strdup(purple_normalize(account, pair->value)); - - if (yd->current_list15_grp) { - /* This buddy is in a group */ - f = yahoo_friend_find_or_new(gc, norm_bud); - if (!(b = purple_find_buddy(account, norm_bud))) { - if (!(g = purple_find_group(yd->current_list15_grp))) { - g = purple_group_new(yd->current_list15_grp); - purple_blist_add_group(g, NULL); - } - b = purple_buddy_new(account, norm_bud, NULL); - purple_blist_add_buddy(b, NULL, g, NULL); - } - yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_grp); - - } else { - /* This buddy is on the ignore list (and therefore in no group) */ - purple_debug_info("yahoo", "%s adding %s to the deny list because of the ignore list / no group was found\n", - account->username, norm_bud); - purple_privacy_deny_add(account, norm_bud, 1); - } + temp = g_strdup(purple_normalize(account, pair->value)); break; case 241: /* another protocol user */ - if (f) { - f->protocol = strtol(pair->value, NULL, 10); - purple_debug_info("yahoo", "Setting protocol to %d\n", f->protocol); - } + protocol = strtol(pair->value, NULL, 10); break; case 59: /* somebody told cookies come here too, but im not sure */ yahoo_process_cookie(yd, pair->value); break; case 317: /* Stealth Setting */ - if (f && (strtol(pair->value, NULL, 10) == 2)) { - f->presence = YAHOO_PRESENCE_PERM_OFFLINE; - } + stealth = strtol(pair->value, NULL, 10); break; /* case 242: */ /* this seems related to 241 */ /* break; */ @@ -552,6 +581,7 @@ g_hash_table_foreach(ht, yahoo_do_group_cleanup, NULL); g_hash_table_destroy(ht); g_free(norm_bud); + g_free(temp); } static void yahoo_process_list(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -636,6 +666,10 @@ } yahoo_do_group_check(account, ht, norm_bud, grp); + /* set p2p status not connected and no p2p packet sent */ + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + f->p2p_packet_sent = 0; + g_free(norm_bud); } g_strfreev(buddies); @@ -692,7 +726,8 @@ yahoo_fetch_aliases(gc); } -static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt) +/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */ +static void yahoo_process_notify(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type) { PurpleAccount *account; char *msg = NULL; @@ -701,12 +736,16 @@ char *game = NULL; YahooFriend *f = NULL; GSList *l = pkt->hash; + gint val_11 = 0; + struct yahoo_data *yd = gc->proto_data; + gboolean msn = FALSE; + char *msn_from = NULL; account = purple_connection_get_account(gc); while (l) { struct yahoo_pair *pair = l->data; - if (pair->key == 4) + if (pair->key == 4 || pair->key == 1) from = pair->value; if (pair->key == 49) msg = pair->value; @@ -714,19 +753,43 @@ stat = pair->value; if (pair->key == 14) game = pair->value; + if (pair->key == 11) + val_11 = strtol(pair->value, NULL, 10); + if (pair->key == 241) + if(strtol(pair->value, NULL, 10) == 2) + msn = TRUE; l = l->next; } if (!from || !msg) return; + /* disconnect the peer if connected through p2p and sends wrong value for session id */ + if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) { + purple_debug_warning("yahoo","p2p: %s sent us notify with wrong session id. Disconnecting p2p connection to peer\n", from); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + g_hash_table_remove(yd->peers, from); + return; + } + + if(msn) + msn_from = g_strconcat("msn/", from, NULL); + if (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING")) && (purple_privacy_check(account, from))) { - if (*stat == '1') - serv_got_typing(gc, from, 0, PURPLE_TYPING); - else - serv_got_typing_stopped(gc, from); + if(msn) { + if (*stat == '1') + serv_got_typing(gc, msn_from, 0, PURPLE_TYPING); + else + serv_got_typing_stopped(gc, msn_from); + } + else { + if (*stat == '1') + serv_got_typing(gc, from, 0, PURPLE_TYPING); + else + serv_got_typing_stopped(gc, from); + } } else if (!g_ascii_strncasecmp(msg, "GAME", strlen("GAME"))) { PurpleBuddy *bud = purple_find_buddy(account, from); @@ -754,6 +817,7 @@ g_free(buf); } + g_free(msn_from); } @@ -765,7 +829,69 @@ char *msg; }; -static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt) +static void yahoo_process_sms_message(PurpleConnection *gc, struct yahoo_packet *pkt) +{ + PurpleAccount *account; + GSList *l = pkt->hash; + struct _yahoo_im *sms = NULL; + struct yahoo_data *yd; + char *server_msg = NULL; + char *m; + + yd = gc->proto_data; + account = purple_connection_get_account(gc); + + while (l != NULL) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) { + sms = g_new0(struct _yahoo_im, 1); + sms->from = g_strdup_printf("+%s", pair->value); + sms->time = time(NULL); + sms->utf8 = TRUE; + } + if (pair->key == 14) { + if (sms) + sms->msg = pair->value; + } + if (pair->key == 68) + if(sms) + g_hash_table_insert(yd->sms_carrier, g_strdup(sms->from), g_strdup(pair->value)); + if (pair->key == 16) + server_msg = pair->value; + l = l->next; + } + + if( (pkt->status == -1) || (pkt->status == YAHOO_STATUS_DISCONNECTED) ) { + if (server_msg) { + PurpleConversation *c; + c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms->from, account); + if (c == NULL) + c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, sms->from); + purple_conversation_write(c, NULL, server_msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); + } + else + purple_notify_error(gc, NULL, _("Your SMS was not delivered"), NULL); + + g_free(sms->from); + g_free(sms); + return ; + } + + if (!sms->from || !sms->msg) { + g_free(sms); + return; + } + + m = yahoo_string_decode(gc, sms->msg, sms->utf8); + serv_got_im(gc, sms->from, m, 0, sms->time); + + g_free(m); + g_free(sms->from); + g_free(sms); +} + +/* pkt_type is YAHOO_PKT_TYPE_SERVER if pkt arrives from yahoo server, YAHOO_PKT_TYPE_P2P if pkt arrives through p2p */ +static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type) { PurpleAccount *account; struct yahoo_data *yd = gc->proto_data; @@ -773,13 +899,17 @@ GSList *list = NULL; struct _yahoo_im *im = NULL; const char *imv = NULL; + gint val_11 = 0; + gboolean msn = FALSE; + char *msn_from = NULL; account = purple_connection_get_account(gc); - if (pkt->status <= 1 || pkt->status == 5) { + if (pkt->status <= 1 || pkt->status == 5 || pkt->status == YAHOO_STATUS_OFFLINE) { + /* messages are received with status YAHOO_STATUS_OFFLINE in case of p2p */ while (l != NULL) { struct yahoo_pair *pair = l->data; - if (pair->key == 4) { + if (pair->key == 4 || pair->key == 1) { im = g_new0(struct _yahoo_im, 1); list = g_slist_append(list, im); im->from = pair->value; @@ -799,6 +929,15 @@ if (im) im->msg = pair->value; } + if (pair->key == 241) { + if(strtol(pair->value, NULL, 10) == 2) + msn = TRUE; + } + /* peer session id */ + if (pair->key == 11) { + if (im) + val_11 = strtol(pair->value, NULL, 10); + } /* IMV key */ if (pair->key == 63) { @@ -811,6 +950,17 @@ _("Your Yahoo! message did not get sent."), NULL); } + if(msn) + msn_from = g_strconcat("msn/", im->from, NULL); + + /* disconnect the peer if connected through p2p and sends wrong value for session id */ + if( (pkt_type == YAHOO_PKT_TYPE_P2P) && (val_11 != yd->session_id) ) { + purple_debug_warning("yahoo","p2p: %s sent us message with wrong session id. Disconnecting p2p connection to peer\n", im->from); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + g_hash_table_remove(yd->peers, im->from); + return; + } + /** TODO: It seems that this check should be per IM, not global */ /* Check for the Doodle IMV */ if (im != NULL && imv!= NULL && im->from != NULL) @@ -847,6 +997,7 @@ for (l = list; l; l = l->next) { YahooFriend *f; char *m, *m2; + PurpleConversation *c; im = l->data; if (!im->from || !im->msg) { @@ -869,35 +1020,54 @@ m = m2; purple_util_chrreplace(m, '\r', '\n'); + c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->from, account); + if ((c == NULL) && msn) + c=purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, msn_from, account); + if (!strcmp(m, "")) { - PurpleConversation *c; char *username; - c = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->from, account); - if (c == NULL) - c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from); - - username = g_markup_escape_text(im->from, -1); + if(c == NULL) { + if(msn) + c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, msn_from); + else + c = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, im->from); + } + if(msn) + username = g_markup_escape_text(msn_from, -1); + else + username = g_markup_escape_text(im->from, -1); + purple_prpl_got_attention(gc, username, YAHOO_BUZZ); g_free(username); g_free(m); g_free(im); + g_free(msn_from); continue; } m2 = yahoo_codes_to_html(m); g_free(m); - serv_got_im(gc, im->from, m2, 0, im->time); + + if(msn) + serv_got_im(gc, msn_from, m2, 0, im->time); + else + serv_got_im(gc, im->from, m2, 0, im->time); + g_free(m2); - if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) { - if (yahoo_friend_get_buddy_icon_need_request(f)) { - yahoo_send_picture_request(gc, im->from); - yahoo_friend_set_buddy_icon_need_request(f, FALSE); + /* laters : implement buddy icon for msn friends */ + if(!msn) { + if ((f = yahoo_friend_find(gc, im->from)) && im->buddy_icon == 2) { + if (yahoo_friend_get_buddy_icon_need_request(f)) { + yahoo_send_picture_request(gc, im->from); + yahoo_friend_set_buddy_icon_need_request(f, FALSE); + } } } g_free(im); + g_free(msn_from); } g_slist_free(list); } @@ -1030,12 +1200,14 @@ PurpleAccount *account; GSList *l = pkt->hash; const char *msg = NULL; + int protocol = 0; account = purple_connection_get_account(gc); /* Buddy authorized/declined our addition */ if (pkt->status == 1) { - const char *who = NULL; + char *temp = NULL; + char *who = NULL; int response = 0; while (l) { @@ -1043,7 +1215,7 @@ switch (pair->key) { case 4: - who = pair->value; + temp = pair->value; break; case 13: response = strtol(pair->value, NULL, 10); @@ -1051,10 +1223,18 @@ case 14: msg = pair->value; break; + case 241: + protocol = strtol(pair->value, NULL, 10); + break; } l = l->next; } + if(protocol == 0) + who = g_strdup(temp); + else if(protocol == 2) + who = g_strconcat("msn/", temp, NULL); + if (response == 1) /* Authorized */ purple_debug_info("yahoo", "Received authorization from buddy '%s'.\n", who ? who : "(Unknown Buddy)"); else if (response == 2) { /* Declined */ @@ -1062,12 +1242,13 @@ yahoo_buddy_denied_our_add(gc, who, msg); } else purple_debug_error("yahoo", "Received unknown authorization response of %d from buddy '%s'.\n", response, who ? who : "(Unknown Buddy)"); - + g_free(who); } /* Buddy requested authorization to add us. */ else if (pkt->status == 3) { struct yahoo_add_request *add_req; const char *firstname = NULL, *lastname = NULL; + char *temp = NULL; add_req = g_new0(struct yahoo_add_request, 1); add_req->gc = gc; @@ -1077,6 +1258,7 @@ switch (pair->key) { case 4: + temp = pair->value; add_req->who = g_strdup(pair->value); break; case 5: @@ -1098,6 +1280,10 @@ } l = l->next; } + if(add_req->protocol == 2) + add_req->who = g_strconcat("msn/", temp, NULL); + else + add_req->who = g_strdup(temp); if (add_req->id && add_req->who) { char *alias = NULL, *dec_msg = NULL; @@ -2191,11 +2377,15 @@ { int err = 0; char *who = NULL; + char *temp = NULL; char *group = NULL; char *decoded_group; char *buf; YahooFriend *f; GSList *l = pkt->hash; + struct yahoo_data *yd = gc->proto_data; + int protocol = 0; + gboolean msn = FALSE; while (l) { struct yahoo_pair *pair = l->data; @@ -2205,24 +2395,48 @@ err = strtol(pair->value, NULL, 10); break; case 7: - who = pair->value; + temp = pair->value; break; case 65: group = pair->value; break; + case 241: + protocol = strtol(pair->value, NULL, 10); + if(protocol == 2) + msn = TRUE; + break; } l = l->next; } - if (!who) + if (!temp) return; if (!group) group = ""; + + if(msn) + who = g_strconcat("msn/", temp, NULL); + else + who = g_strdup(temp); if (!err || (err == 2)) { /* 0 = ok, 2 = already on serv list */ f = yahoo_friend_find_or_new(gc, who); yahoo_update_status(gc, who, f); + if(protocol) + f->protocol = protocol; + + if( !g_hash_table_lookup(yd->peers, who) ) { + /* we are not connected as client, so set friend to not connected */ + if(msn) + yahoo_friend_set_p2p_status(f,YAHOO_P2PSTATUS_DO_NOT_CONNECT); + else { + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + f->p2p_packet_sent = 0; + } + } + else /* we are already connected. set friend to YAHOO_P2PSTATUS_WE_ARE_CLIENT */ + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT); return; } @@ -2233,6 +2447,431 @@ purple_notify_error(gc, NULL, _("Could not add buddy to server list"), buf); g_free(buf); g_free(decoded_group); + g_free(who); +} + +/* write pkt to the source */ +static void yahoo_p2p_write_pkt(gint source, struct yahoo_packet *pkt) +{ + size_t pkt_len; + guchar *raw_packet; + + /*build the raw packet and send it to the host*/ + pkt_len = yahoo_packet_build(pkt, 0, 0, 0, &raw_packet); + if(write(source, raw_packet, pkt_len) != pkt_len) + purple_debug_warning("yahoo","p2p: couldn't write to the source\n"); + g_free(raw_packet); +} + +static void yahoo_p2p_keepalive_cb(gpointer key, gpointer value, gpointer user_data) +{ + struct yahoo_p2p_data *p2p_data = value; + PurpleConnection *gc = user_data; + struct yahoo_packet *pkt_to_send; + PurpleAccount *account; + struct yahoo_data *yd = gc->proto_data; + + account = purple_connection_get_account(gc); + + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssisi", + 4, purple_normalize(account, purple_account_get_username(account)), + 5, p2p_data->host_username, + 241, 0, /* Protocol identifier */ + 49, "PEERTOPEER", + 13, 7); + yahoo_p2p_write_pkt(p2p_data->source, pkt_to_send); + + yahoo_packet_free(pkt_to_send); +} + +static gboolean yahoo_p2p_keepalive(gpointer data) +{ + PurpleConnection *gc = data; + struct yahoo_data *yd = gc->proto_data; + + g_hash_table_foreach(yd->peers, yahoo_p2p_keepalive_cb, gc); + + return TRUE; +} + +/* destroy p2p_data associated with a peer and close p2p connection. + * g_hash_table_remove() calls this function to destroy p2p_data associated with the peer, + * call g_hash_table_remove() instead of this fucntion if peer has an entry in the table */ +static void yahoo_p2p_disconnect_destroy_data(gpointer data) +{ + struct yahoo_p2p_data *p2p_data; + YahooFriend *f; + + if(!(p2p_data = data)) + return ; + + /* If friend, set him not connected */ + f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username); + if (f) + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_NOT_CONNECTED); + + if(p2p_data->source >= 0) + close(p2p_data->source); + purple_input_remove(p2p_data->input_event); + g_free(p2p_data->host_ip); + g_free(p2p_data->host_username); + g_free(p2p_data); +} + +/* exchange of initial p2pfilexfer packets, service type YAHOO_SERVICE_P2PFILEXFER */ +static void yahoo_p2p_process_p2pfilexfer(gpointer data, gint source, struct yahoo_packet *pkt) +{ + struct yahoo_p2p_data *p2p_data; + char *who = NULL; + GSList *l = pkt->hash; + struct yahoo_packet *pkt_to_send; + PurpleAccount *account; + int val_13_to_send = 0; + struct yahoo_data *yd; + YahooFriend *f; + + if(!(p2p_data = data)) + return ; + + yd = p2p_data->gc->proto_data; + + /* lets see whats in the packet */ + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 4: + who = pair->value; + if(strncmp(who, p2p_data->host_username, strlen(p2p_data->host_username)) != 0) { + /* from whom are we receiving the packets ?? */ + purple_debug_warning("yahoo","p2p: received data from wrong user\n"); + return; + } + break; + case 13: + p2p_data->val_13 = strtol(pair->value, NULL, 10); /* Value should be 5-7 */ + break; + /* case 5, 49 look laters, no use right now */ + } + l = l->next; + } + + account = purple_connection_get_account(p2p_data->gc); + + /* key_13: sort of a counter. + * WHEN WE ARE CLIENT: yahoo server sends val_13 = 0, we send to peer val_13 = 1, receive back val_13 = 5, + * we send val_13=6, receive val_13=7, we send val_13=7, HALT. Keep sending val_13 = 7 as keep alive. + * WHEN WE ARE SERVER: we send val_13 = 0 to yahoo server, peer sends us val_13 = 1, we send val_13 = 5, + * receive val_13 = 6, send val_13 = 7, receive val_13 = 7. HALT. Keep sending val_13 = 7 as keep alive. */ + + switch(p2p_data->val_13) { + case 1 : val_13_to_send = 5; break; + case 5 : val_13_to_send = 6; break; + case 6 : val_13_to_send = 7; break; + case 7 : if( g_hash_table_lookup(yd->peers, p2p_data->host_username) ) + return; + val_13_to_send = 7; break; + default: purple_debug_warning("yahoo","p2p:Unknown value for key 13\n"); + return; + } + + /* Build the yahoo packet */ + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssisi", + 4, purple_normalize(account, purple_account_get_username(account)), + 5, p2p_data->host_username, + 241, 0, /* Protocol identifier */ + 49, "PEERTOPEER", + 13, val_13_to_send); + + /* build the raw packet and send it to the host */ + yahoo_p2p_write_pkt(source, pkt_to_send); + yahoo_packet_free(pkt_to_send); + + if( val_13_to_send == 7 ) + if( !g_hash_table_lookup(yd->peers, p2p_data->host_username) ) { + g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data); + /* If the peer is a friend, set him connected */ + f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username); + if (f) { + if(p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) { + p2p_data->session_id = f->session_id; + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER); + } + else + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT); + } + } +} + +/* callback function associated with receiving of data, not considering receipt of multiple YMSG packets in a single TCP packet */ +static void yahoo_p2p_read_pkt_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + guchar buf[1024]; /* is it safe to assume a fixed array length of 1024 ?? */ + int len; + int pos = 0; + int pktlen; + struct yahoo_packet *pkt; + guchar *start = NULL; + struct yahoo_p2p_data *p2p_data; + struct yahoo_data *yd; + + if(!(p2p_data = data)) + return ; + yd = p2p_data->gc->proto_data; + + len = read(source, buf, sizeof(buf)); + if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + return ; /* No Worries*/ + else if (len <= 0) + { + purple_debug_warning("yahoo","p2p: Error in connection, or host disconnected\n"); + /* remove from p2p connection lists, also calls yahoo_p2p_disconnect_destroy_data */ + if( g_hash_table_lookup(yd->peers, p2p_data->host_username) ) + g_hash_table_remove(yd->peers,p2p_data->host_username); + else + yahoo_p2p_disconnect_destroy_data(data); + return; + } + + if(len < YAHOO_PACKET_HDRLEN) + return; + + if(strncmp((char *)buf, "YMSG", MIN(4, len)) != 0) { + /* Not a YMSG packet */ + purple_debug_warning("yahoo","p2p: Got something other than YMSG packet\n"); + + start = memchr(buf + 1, 'Y', len - 1); + if(start) { + g_memmove(buf, start, len - (start - buf)); + len -= start - buf; + } else { + g_free(buf); + return; + } + } + + pos += 4; /* YMSG */ + pos += 2; + pos += 2; + + pktlen = yahoo_get16(buf + pos); pos += 2; + purple_debug(PURPLE_DEBUG_MISC, "yahoo", "p2p: %d bytes to read\n", len); + + pkt = yahoo_packet_new(0, 0, 0); + pkt->service = yahoo_get16(buf + pos); pos += 2; + pkt->status = yahoo_get32(buf + pos); pos += 4; + pkt->id = yahoo_get32(buf + pos); pos += 4; + + purple_debug(PURPLE_DEBUG_MISC, "yahoo", "p2p: Yahoo Service: 0x%02x Status: %d\n",pkt->service, pkt->status); + yahoo_packet_read(pkt, buf + pos, pktlen); + + /* packet processing */ + switch(pkt->service) { + case YAHOO_SERVICE_P2PFILEXFER: + yahoo_p2p_process_p2pfilexfer(data, source, pkt); + break; + case YAHOO_SERVICE_MESSAGE: + yahoo_process_message(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P); + break; + case YAHOO_SERVICE_NOTIFY: + yahoo_process_notify(p2p_data->gc, pkt, YAHOO_PKT_TYPE_P2P); + break; + default: + purple_debug_warning("yahoo","p2p: p2p service %d Unhandled\n",pkt->service); + } + + yahoo_packet_free(pkt); +} + +static void yahoo_p2p_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + int acceptfd; + struct yahoo_p2p_data *p2p_data; + struct yahoo_data *yd; + + if(!(p2p_data = data)) + return ; + yd = p2p_data->gc->proto_data; + + acceptfd = accept(source, NULL, 0); + if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return; + else if(acceptfd == -1) { + purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno)); + yahoo_p2p_disconnect_destroy_data(data); + return; + } + + /* remove timeout */ + purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle); + yd->yahoo_p2p_server_timeout_handle = 0; + + /* remove watcher and close p2p server */ + purple_input_remove(yd->yahoo_p2p_server_watcher); + close(yd->yahoo_local_p2p_server_fd); + yd->yahoo_local_p2p_server_fd = -1; + + /* Add an Input Read event to the file descriptor */ + p2p_data->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data); + p2p_data->source = acceptfd; +} + +static gboolean yahoo_cancel_p2p_server_listen_cb(gpointer data) +{ + struct yahoo_p2p_data *p2p_data; + struct yahoo_data *yd; + + if(!(p2p_data = data)) + return FALSE; + + yd = p2p_data->gc->proto_data; + + purple_debug_warning("yahoo","yahoo p2p server timeout, peer failed to connect"); + yahoo_p2p_disconnect_destroy_data(data); + purple_input_remove(yd->yahoo_p2p_server_watcher); + yd->yahoo_p2p_server_watcher = 0; + close(yd->yahoo_local_p2p_server_fd); + yd->yahoo_local_p2p_server_fd = -1; + yd->yahoo_p2p_server_timeout_handle = 0; + + return FALSE; +} + +static void yahoo_p2p_server_listen_cb(int listenfd, gpointer data) +{ + struct yahoo_p2p_data *p2p_data; + struct yahoo_data *yd; + + if(!(p2p_data = data)) + return ; + + if(listenfd == -1) { + purple_debug_warning("yahoo","p2p: error starting p2p server\n"); + yahoo_p2p_disconnect_destroy_data(data); + return; + } + + yd = p2p_data->gc->proto_data; + + /* Add an Input Read event to the file descriptor */ + yd->yahoo_local_p2p_server_fd = listenfd; + yd->yahoo_p2p_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_server_send_connected_cb,data); + + /* add timeout */ + yd->yahoo_p2p_server_timeout_handle = purple_timeout_add_seconds(YAHOO_P2P_SERVER_TIMEOUT, yahoo_cancel_p2p_server_listen_cb, data); +} + +/* send p2p pkt containing our encoded ip, asking peer to connect to us */ +void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13) +{ + const char *public_ip; + guint32 temp[4]; + guint32 ip; + char temp_str[100]; + gchar *base64_ip = NULL; + YahooFriend *f; + struct yahoo_packet *pkt; + PurpleAccount *account; + struct yahoo_data *yd = gc->proto_data; + struct yahoo_p2p_data *p2p_data; + + f = yahoo_friend_find(gc, who); + account = purple_connection_get_account(gc); + + /* Do not send invitation if already listening for other connection */ + if(yd->yahoo_local_p2p_server_fd >= 0) + return; + + /* One shouldn't try to connect to self */ + if( strcmp(purple_normalize(account, purple_account_get_username(account)), who) == 0) + return; + + /* send packet to only those friends who arent p2p connected and to whom we havent already sent. Do not send if this condition doesn't hold good */ + if( !( f && (yahoo_friend_get_p2p_status(f) == YAHOO_P2PSTATUS_NOT_CONNECTED) && (f->p2p_packet_sent == 0)) ) + return; + + /* Dont send p2p packet to buddies of other protocols */ + if(f->protocol) + return; + + /* Finally, don't try to connect to buddies not online or on sms */ + if( (f->status == YAHOO_STATUS_OFFLINE) || f->sms ) + return; + + public_ip = purple_network_get_public_ip(); + if( (sscanf(public_ip, "%u.%u.%u.%u", &temp[0], &temp[1], &temp[2], &temp[3])) !=4 ) + return ; + + ip = (temp[3] << 24) | (temp[2] <<16) | (temp[1] << 8) | temp[0]; + sprintf(temp_str, "%d", ip); + base64_ip = purple_base64_encode( (guchar *)temp_str, strlen(temp_str) ); + + pkt = yahoo_packet_new(YAHOO_SERVICE_PEERTOPEER, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, "sssissis", + 1, purple_normalize(account, purple_account_get_username(account)), + 4, purple_normalize(account, purple_account_get_username(account)), + 12, base64_ip, /* base64 encode ip */ + 61, 0, /* To-do : figure out what is 61 for?? */ + 2, "", + 5, who, + 13, val_13, + 49, "PEERTOPEER"); + yahoo_packet_send_and_free(pkt, yd); + + f->p2p_packet_sent = 1; /* set p2p_packet_sent to sent */ + + p2p_data = g_new0(struct yahoo_p2p_data, 1); + + p2p_data->gc = gc; + p2p_data->host_ip = NULL; + p2p_data->host_username = g_strdup(who); + p2p_data->val_13 = val_13; + p2p_data->connection_type = YAHOO_P2P_WE_ARE_SERVER; + + purple_network_listen(YAHOO_PAGER_PORT_P2P, SOCK_STREAM, yahoo_p2p_server_listen_cb, p2p_data); + + g_free(base64_ip); +} + +/* function called when connection to p2p host is setup */ +static void yahoo_p2p_init_cb(gpointer data, gint source, const gchar *error_message) +{ + struct yahoo_p2p_data *p2p_data; + struct yahoo_packet *pkt_to_send; + PurpleAccount *account; + struct yahoo_data *yd; + + if(!(p2p_data = data)) + return ; + yd = p2p_data->gc->proto_data; + + if(error_message != NULL) { + purple_debug_warning("yahoo","p2p: %s\n",error_message); + yahoo_send_p2p_pkt(p2p_data->gc, p2p_data->host_username, 2);/* send p2p init packet with val_13=2 */ + + yahoo_p2p_disconnect_destroy_data(p2p_data); + return; + } + + /* Add an Input Read event to the file descriptor */ + p2p_data->input_event = purple_input_add(source, PURPLE_INPUT_READ, yahoo_p2p_read_pkt_cb, data); + p2p_data->source = source; + + account = purple_connection_get_account(p2p_data->gc); + + /* Build the yahoo packet */ + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_P2PFILEXFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssisi", + 4, purple_normalize(account, purple_account_get_username(account)), + 5, p2p_data->host_username, + 241, 0, /* Protocol identifier */ + 49, "PEERTOPEER", + 13, 1); /* we receive key13= 0 or 2, we send key13=1 */ + + yahoo_p2p_write_pkt(source, pkt_to_send); /* build raw packet and send */ + yahoo_packet_free(pkt_to_send); } static void yahoo_process_p2p(PurpleConnection *gc, struct yahoo_packet *pkt) @@ -2242,6 +2881,14 @@ char *base64 = NULL; guchar *decoded; gsize len; + gint val_13 = 0; + gint val_11 = 0; + PurpleAccount *account; + YahooFriend *f; + + /* if status is not 1 ie YAHOO_STATUS_BRB, the packet bounced back, so contains our own ip */ + if(!(pkt->status == YAHOO_STATUS_BRB)) + return ; while (l) { struct yahoo_pair *pair = l->data; @@ -2261,14 +2908,21 @@ /* so, this is an ip address. in base64. decoded it's in ascii. after strtol, it's in reversed byte order. Who thought this up?*/ break; + case 13: + val_13 = strtol(pair->value, NULL, 10); + break; + case 11: + val_11 = strtol(pair->value, NULL, 10); /* session id of peer */ + if( (f = yahoo_friend_find(gc, who)) ) + f->session_id = val_11; + break; /* TODO: figure these out yahoo: Key: 61 Value: 0 yahoo: Key: 2 Value: - yahoo: Key: 13 Value: 0 + yahoo: Key: 13 Value: 0 packet count ?? yahoo: Key: 49 Value: PEERTOPEER yahoo: Key: 140 Value: 1 - yahoo: Key: 11 Value: -1786225828 */ } @@ -2280,6 +2934,8 @@ guint32 ip; char *tmp2; YahooFriend *f; + char *host_ip; + struct yahoo_p2p_data *p2p_data = g_new0(struct yahoo_p2p_data, 1); decoded = purple_base64_decode(base64, &len); if (len) { @@ -2292,12 +2948,34 @@ ip = strtol(tmp2, NULL, 10); g_free(tmp2); g_free(decoded); - tmp2 = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, + host_ip = g_strdup_printf("%u.%u.%u.%u", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff); f = yahoo_friend_find(gc, who); if (f) - yahoo_friend_set_ip(f, tmp2); - g_free(tmp2); + yahoo_friend_set_ip(f, host_ip); + purple_debug_info("yahoo", "IP : %s\n", host_ip); + + account = purple_connection_get_account(gc); + + if(val_11==0) { + if(!f) + return; + else + val_11 = f->session_id; + } + + p2p_data->host_username = g_strdup(who); + p2p_data->val_13 = val_13; + p2p_data->session_id = val_11; + p2p_data->host_ip = host_ip; + p2p_data->gc = gc; + p2p_data->connection_type = YAHOO_P2P_WE_ARE_CLIENT; + + /* connect to host */ + if((purple_proxy_connect(NULL, account, host_ip, YAHOO_PAGER_PORT_P2P, yahoo_p2p_init_cb, p2p_data))==NULL) { + yahoo_p2p_disconnect_destroy_data(p2p_data); + purple_debug_info("yahoo","p2p: Connection to %s failed\n", host_ip); + } } } @@ -2377,12 +3055,12 @@ yahoo_process_status(gc, pkt); break; case YAHOO_SERVICE_NOTIFY: - yahoo_process_notify(gc, pkt); + yahoo_process_notify(gc, pkt, YAHOO_PKT_TYPE_SERVER); break; case YAHOO_SERVICE_MESSAGE: case YAHOO_SERVICE_GAMEMSG: case YAHOO_SERVICE_CHATMSG: - yahoo_process_message(gc, pkt); + yahoo_process_message(gc, pkt, YAHOO_PKT_TYPE_SERVER); break; case YAHOO_SERVICE_SYSMESSAGE: yahoo_process_sysmessage(gc, pkt); @@ -2459,7 +3137,8 @@ break; case YAHOO_SERVICE_P2PFILEXFER: /* This case had no break and continued; thus keeping it this way.*/ - yahoo_process_p2pfilexfer(gc, pkt); + yahoo_process_p2p(gc, pkt); /* P2PFILEXFER handled the same way as process_p2p */ + yahoo_process_p2pfilexfer(gc, pkt); /* redundant ??, need to have a break now */ case YAHOO_SERVICE_FILETRANSFER: yahoo_process_filetransfer(gc, pkt); break; @@ -2493,6 +3172,9 @@ case YAHOO_SERVICE_FILETRANS_ACC_15: yahoo_process_filetrans_acc_15(gc, pkt); break; + case YAHOO_SERVICE_SMS_MSG: + yahoo_process_sms_message(gc, pkt); + break; default: purple_debug(PURPLE_DEBUG_ERROR, "yahoo", @@ -3008,6 +3690,7 @@ purple_connection_set_display_name(gc, purple_account_get_username(account)); + yd->yahoo_local_p2p_server_fd = -1; yd->fd = -1; yd->txhandler = 0; /* TODO: Is there a good grow size for the buffer? */ @@ -3015,6 +3698,9 @@ yd->friends = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_friend_free); yd->imvironments = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); yd->xfer_peer_idstring_map = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); + yd->peers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, yahoo_p2p_disconnect_destroy_data); + yd->sms_carrier = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + yd->yahoo_p2p_timer = purple_timeout_add_seconds(YAHOO_P2P_KEEPALIVE_SECS, yahoo_p2p_keepalive, gc); yd->confs = NULL; yd->conf_id = 2; yd->last_keepalive = yd->last_ping = time(NULL); @@ -3080,6 +3766,17 @@ if (yd->in_chat) yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */ + purple_timeout_remove(yd->yahoo_p2p_timer); + if(yd->yahoo_p2p_server_timeout_handle != 0) + purple_timeout_remove(yd->yahoo_p2p_server_timeout_handle); + + /* close p2p server if it is waiting for a peer to connect */ + purple_input_remove(yd->yahoo_p2p_server_watcher); + close(yd->yahoo_local_p2p_server_fd); + yd->yahoo_local_p2p_server_fd = -1; + + g_hash_table_destroy(yd->sms_carrier); + g_hash_table_destroy(yd->peers); g_hash_table_destroy(yd->friends); g_hash_table_destroy(yd->imvironments); g_hash_table_destroy(yd->xfer_peer_idstring_map); @@ -3625,10 +4322,129 @@ return m; } +struct yahoo_sms_carrier_cb_data { + PurpleConnection *gc; + char *who; + char *what; +}; + +static int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags); + +static void yahoo_get_sms_carrier_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, + const gchar *webdata, size_t len, const gchar *error_message) +{ + struct yahoo_sms_carrier_cb_data *sms_cb_data = user_data; + PurpleConnection *gc = sms_cb_data->gc; + struct yahoo_data *yd = gc->proto_data; + char *mobile_no = NULL; + char *status = NULL; + char *carrier = NULL; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account); + + if (error_message != NULL) { + purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL)); + + g_free(sms_cb_data->who); + g_free(sms_cb_data->what); + g_free(sms_cb_data); + return ; + } + else if (len > 0 && webdata && *webdata) { + xmlnode *validate_data_root = xmlnode_from_str(webdata, -1); + xmlnode *validate_data_child = xmlnode_get_child(validate_data_root, "mobile_no"); + mobile_no = (char *)xmlnode_get_attrib(validate_data_child, "msisdn"); + + validate_data_root = xmlnode_copy(validate_data_child); + validate_data_child = xmlnode_get_child(validate_data_root, "status"); + status = xmlnode_get_data(validate_data_child); + + validate_data_child = xmlnode_get_child(validate_data_root, "carrier"); + carrier = xmlnode_get_data(validate_data_child); + + purple_debug_info("yahoo","SMS validate data: Mobile:%s, Status:%s, Carrier:%s\n", mobile_no, status, carrier); + + if( strcmp(status, "Valid") == 0) { + g_hash_table_insert(yd->sms_carrier, g_strdup_printf("+%s", mobile_no), g_strdup(carrier)); + yahoo_send_im(sms_cb_data->gc, sms_cb_data->who, sms_cb_data->what, PURPLE_MESSAGE_SEND); + } + else { + g_hash_table_insert(yd->sms_carrier, g_strdup_printf("+%s", mobile_no), g_strdup("Unknown")); + purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL)); + } + + xmlnode_free(validate_data_child); + xmlnode_free(validate_data_root); + g_free(sms_cb_data->who); + g_free(sms_cb_data->what); + g_free(sms_cb_data); + g_free(mobile_no); + g_free(status); + g_free(carrier); + } +} + +static void yahoo_get_sms_carrier(PurpleConnection *gc, gpointer data) +{ + struct yahoo_data *yd = gc->proto_data; + PurpleUtilFetchUrlData *url_data; + struct yahoo_sms_carrier_cb_data *sms_cb_data; + char *validate_request_str = NULL; + char *request = NULL; + gboolean use_whole_url = FALSE; + xmlnode *validate_request_root = NULL; + xmlnode *validate_request_child = NULL; + + if(!(sms_cb_data = data)) + return; + + validate_request_root = xmlnode_new("validate"); + xmlnode_set_attrib(validate_request_root, "intl", "us"); + xmlnode_set_attrib(validate_request_root, "version", YAHOO_CLIENT_VERSION); + xmlnode_set_attrib(validate_request_root, "qos", "0"); + + validate_request_child = xmlnode_new_child(validate_request_root, "mobile_no"); + xmlnode_set_attrib(validate_request_child, "msisdn", sms_cb_data->who + 1); + + validate_request_str = xmlnode_to_str(validate_request_root, NULL); + + xmlnode_free(validate_request_child); + xmlnode_free(validate_request_root); + + request = g_strdup_printf( + "POST /mobileno?intl=us&version=%s HTTP/1.1\r\n" + "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s; path=/; domain=.yahoo.com;\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n" + "Host: validate.msg.yahoo.com\r\n" + "Content-Length: %d\r\n" + "Cache-Control: no-cache\r\n\r\n%s", + YAHOO_CLIENT_VERSION, yd->cookie_t, yd->cookie_y, strlen(validate_request_str), validate_request_str); + + /* use whole URL if using HTTP Proxy */ + if ((gc->account->proxy_info) && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP)) + use_whole_url = TRUE; + + url_data = purple_util_fetch_url_request(YAHOO_SMS_CARRIER_URL, use_whole_url, + "Mozilla/4.0 (compatible; MSIE 5.5)", TRUE, request, FALSE, + yahoo_get_sms_carrier_cb, data); + + g_free(request); + g_free(validate_request_str); + + if (!url_data) { + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, sms_cb_data->who, account); + purple_conversation_write(conv, NULL, "Cant send SMS, Unable to obtain mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL)); + g_free(sms_cb_data->who); + g_free(sms_cb_data->what); + g_free(sms_cb_data); + } +} + static int yahoo_send_im(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags) { struct yahoo_data *yd = gc->proto_data; - struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0); + struct yahoo_packet *pkt = NULL; char *msg = yahoo_html_to_codes(what); char *msg2; gboolean utf8 = TRUE; @@ -3637,7 +4453,8 @@ YahooFriend *f = NULL; gsize lenb = 0; glong lenc = 0; - + struct yahoo_p2p_data *p2p_data; + gboolean msn = FALSE; msg2 = yahoo_string_encode(gc, msg, &utf8); if(msg2) { @@ -3656,9 +4473,67 @@ } } - yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who); - if ((f = yahoo_friend_find(gc, who)) && f->protocol) - yahoo_packet_hash_int(pkt, 241, f->protocol); + msn = g_str_has_prefix(who, "msn/") || g_str_has_prefix(who, "MSN/"); + + if( strncmp(who, "+", 1) == 0 ) { + /* we have an sms to be sent */ + gchar *carrier = NULL; + const char *alias = NULL; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account); + + carrier = g_hash_table_lookup(yd->sms_carrier, who); + if (!carrier) { + struct yahoo_sms_carrier_cb_data *sms_cb_data; + sms_cb_data = g_malloc(sizeof(struct yahoo_sms_carrier_cb_data)); + sms_cb_data->gc = gc; + sms_cb_data->who = g_malloc(strlen(who)); + sms_cb_data->what = g_malloc(strlen(what)); + strcpy(sms_cb_data->who, who); + strcpy(sms_cb_data->what, what); + + purple_conversation_write(conv, NULL, "Getting mobile carrier to send the sms", PURPLE_MESSAGE_SYSTEM, time(NULL)); + + yahoo_get_sms_carrier(gc, sms_cb_data); + + g_free(msg); + g_free(msg2); + return ret; + } + else if( strcmp(carrier,"Unknown") == 0 ) { + purple_conversation_write(conv, NULL, "Cant send SMS, Unknown mobile carrier", PURPLE_MESSAGE_SYSTEM, time(NULL)); + + g_free(msg); + g_free(msg2); + return -1; + } + + alias = purple_account_get_alias(account); + pkt = yahoo_packet_new(YAHOO_SERVICE_SMS_MSG, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, "sssss", + 1, purple_connection_get_display_name(gc), + 69, alias, + 5, who + 1, + 68, carrier, + 14, msg2); + yahoo_packet_send_and_free(pkt, yd); + + g_free(msg); + g_free(msg2); + + return ret; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0); + if(msn) { + yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who+4); + yahoo_packet_hash_int(pkt, 241, 2); + } + else { + yahoo_packet_hash(pkt, "ss", 1, purple_connection_get_display_name(gc), 5, who); + if ((f = yahoo_friend_find(gc, who)) && f->protocol) + yahoo_packet_hash_int(pkt, 241, f->protocol); + } if (utf8) yahoo_packet_hash_str(pkt, 97, "1"); @@ -3697,8 +4572,18 @@ yahoo_packet_hash_str(pkt, 206, "2"); /* We may need to not send any packets over 2000 bytes, but I'm not sure yet. */ - if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) - yahoo_packet_send(pkt, yd); + if ((YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt)) <= 2000) { + /* if p2p link exists, send through it. To-do: key 15, time value to be sent in case of p2p */ + if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn ) { + yahoo_packet_hash_int(pkt, 11, p2p_data->session_id); + yahoo_p2p_write_pkt(p2p_data->source, pkt); + } + else { + yahoo_packet_send(pkt, yd); + if(!msn) + yahoo_send_p2p_pkt(gc, who, 0); /* send p2p packet, with val_13=0 */ + } + } else ret = -E2BIG; @@ -3713,12 +4598,35 @@ static unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state) { struct yahoo_data *yd = gc->proto_data; - struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, 0); - yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc), + struct yahoo_p2p_data *p2p_data; + gboolean msn = (g_str_has_prefix(who, "msn/") || g_str_has_prefix(who, "MSN/")); + struct yahoo_packet *pkt = NULL; + + /* Don't do anything if sms is being typed */ + if( strncmp(who, "+", 1) == 0 ) + return 0; + + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_TYPING, 0); + + /* check to see if p2p link exists, send through it */ + if( (p2p_data = g_hash_table_lookup(yd->peers, who)) && !msn ) { + yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc), 14, " ", 13, state == PURPLE_TYPING ? "1" : "0", - 5, who, 1002, "1"); - - yahoo_packet_send_and_free(pkt, yd); + 5, who, 11, p2p_data->session_id, 1002, "1"); /* To-do: key 15 to be sent in case of p2p */ + yahoo_p2p_write_pkt(p2p_data->source, pkt); + yahoo_packet_free(pkt); + } + else { /* send through yahoo server */ + if(msn) + yahoo_packet_hash(pkt, "sssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc), + 14, " ", 13, state == PURPLE_TYPING ? "1" : "0", + 5, who+4, 1002, "1", 241, "2"); + else + yahoo_packet_hash(pkt, "ssssss", 49, "TYPING", 1, purple_connection_get_display_name(gc), + 14, " ", 13, state == PURPLE_TYPING ? "1" : "0", + 5, who+4, 1002, "1"); + yahoo_packet_send_and_free(pkt, yd); + } return 0; } @@ -3952,6 +4860,7 @@ char *group2; YahooFriend *f; const char *bname; + gboolean msn = FALSE; if (!yd->logged_in) return; @@ -3961,6 +4870,7 @@ return; f = yahoo_friend_find(gc, bname); + msn = g_str_has_prefix(bname, "msn/") || g_str_has_prefix(bname, "MSN/"); g = purple_buddy_get_group(buddy); if (g) @@ -3970,20 +4880,38 @@ group2 = yahoo_string_encode(gc, group, NULL); pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pkt, "ssssssssss", - 14, "", - 65, group2, - 97, "1", - 1, purple_connection_get_display_name(gc), - 302, "319", - 300, "319", - 7, bname, - 334, "0", - 301, "319", - 303, "319" - ); - if (f && f->protocol) + if(msn) { + yahoo_packet_hash(pkt, "sssssssssss", + 14, "", + 65, group2, + 97, "1", + 1, purple_connection_get_display_name(gc), + 302, "319", + 300, "319", + 7, bname + 4, + 241, "2", + 334, "0", + 301, "319", + 303, "319" + ); + } + else { + yahoo_packet_hash(pkt, "ssssssssss", + 14, "", + 65, group2, + 97, "1", + 1, purple_connection_get_display_name(gc), + 302, "319", + 300, "319", + 7, bname, + 334, "0", + 301, "319", + 303, "319" + ); + } + if (f && f->protocol && !msn) yahoo_packet_hash_int(pkt, 241, f->protocol); + yahoo_packet_send_and_free(pkt, yd); g_free(group2); } @@ -3997,13 +4925,18 @@ gboolean remove = TRUE; char *cg; const char *bname, *gname; + YahooFriend *f = NULL; + gboolean msn = FALSE; bname = purple_buddy_get_name(buddy); - if (!(yahoo_friend_find(gc, bname))) + f = yahoo_friend_find(gc, bname); + if (!f) return; gname = purple_group_get_name(group); buddies = purple_find_buddies(purple_connection_get_account(gc), bname); + if(f->protocol == 2) + msn = TRUE; for (l = buddies; l; l = l->next) { g = purple_buddy_get_group(l->data); if (purple_utf8_strcasecmp(gname, purple_group_get_name(g))) { @@ -4019,8 +4952,15 @@ cg = yahoo_string_encode(gc, gname, NULL); pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), + + if(msn) + yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), + 7, bname+4, 65, cg); + else + yahoo_packet_hash(pkt, "sss", 1, purple_connection_get_display_name(gc), 7, bname, 65, cg); + if(f->protocol) + yahoo_packet_hash_int(pkt, 241, f->protocol); yahoo_packet_send_and_free(pkt, yd); g_free(cg); } @@ -4093,13 +5033,22 @@ struct yahoo_data *yd = gc->proto_data; struct yahoo_packet *pkt; char *gpn, *gpo; + YahooFriend *f = yahoo_friend_find(gc, who); + gboolean msn = FALSE; + const char *temp = NULL; /* Step 0: If they aren't on the server list anyway, * don't bother letting the server know. */ - if (!yahoo_friend_find(gc, who)) + if (!f) return; + if(f->protocol == 2) { + msn = TRUE; + temp = who+4; + } else + temp = who; + /* If old and new are the same, we would probably * end up deleting the buddy, which would be bad. * This might happen because of the charset conversation. @@ -4113,8 +5062,13 @@ } pkt = yahoo_packet_new(YAHOO_SERVICE_CHGRP_15, YAHOO_STATUS_AVAILABLE, 0); - yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc), - 302, "240", 300, "240", 7, who, 224, gpo, 264, gpn, 301, + if(f->protocol) + yahoo_packet_hash(pkt, "ssssissss", 1, purple_connection_get_display_name(gc), + 302, "240", 300, "240", 7, temp, 241, f->protocol, 224, gpo, 264, gpn, 301, + "240", 303, "240"); + else + yahoo_packet_hash(pkt, "ssssssss", 1, purple_connection_get_display_name(gc), + 302, "240", 300, "240", 7, temp, 224, gpo, 264, gpn, 301, "240", 303, "240"); yahoo_packet_send_and_free(pkt, yd); @@ -4326,10 +5280,10 @@ purple_conv_send_confirm(conv, message); } } - /*else + /* else **If pidgindialogs_im() was in the core, we could use it here. * It is all purple_request_* based, but I'm not sure it really belongs in the core - pidgindialogs_im();*/ + pidgindialogs_im(); */ return TRUE; } @@ -4343,7 +5297,7 @@ g_hash_table_insert(params, g_strdup("type"), g_strdup("Chat")); serv_join_chat(purple_account_get_connection(acct), params); } - /*else + /* else ** Same as above (except that this would have to be re-written using purple_request_*) pidgin_blist_joinchat_show(); */ @@ -4413,7 +5367,7 @@ yahoo_add_buddy, NULL, /* add_buddies */ yahoo_remove_buddy, - NULL, /*remove_buddies */ + NULL, /* remove_buddies */ NULL, /* add_permit */ yahoo_add_deny, NULL, /* rem_permit */ diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/yahoo/yahoo.h --- a/libpurple/protocols/yahoo/yahoo.h Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Sat Mar 21 03:49:03 2009 +0000 @@ -30,6 +30,9 @@ #define YAHOO_PAGER_HOST "scs.msg.yahoo.com" #define YAHOO_PAGER_PORT 5050 +#define YAHOO_PAGER_PORT_P2P 5101 +#define YAHOO_P2P_KEEPALIVE_SECS 300 +#define YAHOO_P2P_SERVER_TIMEOUT 10 #define YAHOO_PROFILE_URL "http://profiles.yahoo.com/" #define YAHOO_MAIL_URL "https://login.yahoo.com/config/login?.src=ym" #define YAHOO_XFER_HOST "filetransfer.msg.yahoo.com" @@ -45,7 +48,7 @@ #define YAHOOJP_MAIL_URL "http://mail.yahoo.co.jp/" #define YAHOOJP_XFER_HOST "filetransfer.msg.yahoo.co.jp" #define YAHOOJP_WEBCAM_HOST "wc.yahoo.co.jp" -/*not sure, must test:*/ +/* not sure, must test: */ #define YAHOOJP_XFER_RELAY_HOST "relay.msg.yahoo.co.jp" #define YAHOOJP_XFER_RELAY_PORT 80 #define YAHOOJP_ROOMLIST_URL "http://insider.msg.yahoo.co.jp/ycontent/" @@ -55,6 +58,8 @@ #define WEBMESSENGER_URL "http://login.yahoo.com/config/login?.src=pg" +#define YAHOO_SMS_CARRIER_URL "http://lookup.msg.vip.mud.yahoo.com" + #define YAHOO_PICURL_SETTING "picture_url" #define YAHOO_PICCKSUM_SETTING "picture_checksum" #define YAHOO_PICEXPIRE_SETTING "picture_expire" @@ -80,10 +85,19 @@ #define YAHOOJP_CLIENT_VERSION_ID "524223" #define YAHOOJP_CLIENT_VERSION "7,0,1,1" - /* Index into attention types list. */ #define YAHOO_BUZZ 0 +typedef enum { + YAHOO_PKT_TYPE_SERVER = 0, + YAHOO_PKT_TYPE_P2P +} yahoo_pkt_type; + +typedef enum { + YAHOO_P2P_WE_ARE_CLIENT =0, + YAHOO_P2P_WE_ARE_SERVER +} yahoo_p2p_connection_type; + enum yahoo_status { YAHOO_STATUS_AVAILABLE = 0, YAHOO_STATUS_BRB, @@ -113,6 +127,17 @@ guint watcher; }; +struct yahoo_p2p_data { + PurpleConnection *gc; + char *host_ip; + char *host_username; + int val_13; + guint input_event; + gint source; + int session_id; + yahoo_p2p_connection_type connection_type; +}; + struct _YchtConn; struct yahoo_data { @@ -168,8 +193,8 @@ * for when we lookup people profile or photo information. */ GSList *url_datas; - GHashTable *xfer_peer_idstring_map;/*Hey, i dont know, but putting this HashTable next to friends gives a run time fault...*/ - GSList *cookies;/*contains all cookies, including _y and _t*/ + GHashTable *xfer_peer_idstring_map;/* Hey, i dont know, but putting this HashTable next to friends gives a run time fault... */ + GSList *cookies;/* contains all cookies, including _y and _t */ /** * We may receive a list15 in multiple packets with no prior warning as to how many we'll be getting; @@ -178,6 +203,12 @@ char *current_list15_grp; time_t last_ping; time_t last_keepalive; + GHashTable *peers; /* information about p2p data */ + int yahoo_p2p_timer; + int yahoo_local_p2p_server_fd; + int yahoo_p2p_server_watcher; + GHashTable *sms_carrier; /* sms carrier data */ + guint yahoo_p2p_server_timeout_handle; }; #define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255) @@ -265,4 +296,7 @@ gboolean yahoo_send_attention(PurpleConnection *gc, const char *username, guint type); GList *yahoo_attention_types(PurpleAccount *account); +/* send p2p pkt containing our encoded ip, asking peer to connect to us */ +void yahoo_send_p2p_pkt(PurpleConnection *gc, const char *who, int val_13); + #endif /* _YAHOO_H_ */ diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/yahoo/yahoo_filexfer.c --- a/libpurple/protocols/yahoo/yahoo_filexfer.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Sat Mar 21 03:49:03 2009 +0000 @@ -26,6 +26,7 @@ #include "prpl.h" #include "util.h" #include "debug.h" +#include "network.h" #include "notify.h" #include "proxy.h" #include "ft.h" @@ -50,7 +51,7 @@ guint rxlen; gchar *xfer_peer_idstring; gchar *xfer_idstring_for_relay; - int version; /*0 for old, 15 for Y7(YMSG 15)*/ + int version; /* 0 for old, 15 for Y7(YMSG 15) */ int info_val_249; enum { @@ -58,14 +59,22 @@ HEAD_REQUESTED, HEAD_REPLY_RECEIVED, TRANSFER_PHASE, - ACCEPTED + ACCEPTED, + P2P_HEAD_REQUESTED, + P2P_HEAD_REPLIED, + P2P_GET_REQUESTED } status_15; /* contains all filenames, in case of multiple transfers, with the first * one in the list being the current file's name (ymsg15) */ GSList *filename_list; - GSList *size_list; /*corresponds to filename_list, with size as **STRING** */ + GSList *size_list; /* corresponds to filename_list, with size as **STRING** */ gboolean firstoflist; + gchar *xfer_url; /* url of the file, used when we are p2p server */ + int yahoo_local_p2p_ft_server_fd; + int yahoo_local_p2p_ft_server_port; + int yahoo_p2p_ft_server_watcher; + int input_event; }; static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd) @@ -78,14 +87,14 @@ gc = xd->gc; yd = gc->proto_data; - /*remove entry from map*/ + /* remove entry from map */ if(xd->xfer_peer_idstring) { xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xd->xfer_peer_idstring); if(xfer) g_hash_table_remove(yd->xfer_peer_idstring_map, xd->xfer_peer_idstring); } - /*empty file & filesize list*/ + /* empty file & filesize list */ for (l = xd->filename_list; l; l = l->next) { g_free(l->data); l->data=NULL; @@ -600,6 +609,26 @@ xfer->data = NULL; } +/* Send HTTP OK after receiving file */ +static void yahoo_p2p_ft_server_send_OK(PurpleXfer *xfer) +{ + char *tx = NULL; + int written; + + tx = g_strdup_printf("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nContent-Type: application/octet-stream\r\nConnection: close\r\n\r\n"); + written = write(xfer->fd, tx, strlen(tx)); + + if (written < 0 && errno == EAGAIN) + written = 0; + else if (written <= 0) + purple_debug_info("yahoo", "p2p filetransfer: Unable to write HTTP OK"); + + /* close connection */ + close(xfer->fd); + xfer->fd = -1; + g_free(tx); +} + static void yahoo_xfer_end(PurpleXfer *xfer_old) { struct yahoo_xfer_data *xfer_data; @@ -611,6 +640,10 @@ if(xfer_data && xfer_data->version == 15 && purple_xfer_get_type(xfer_old) == PURPLE_XFER_RECEIVE && xfer_data->filename_list) { + + /* Send HTTP OK in case of p2p transfer, when we act as server */ + if((xfer_data->xfer_url != NULL) && (xfer_old->fd >=0) && (purple_xfer_get_status(xfer_old) == PURPLE_XFER_STATUS_DONE)) + yahoo_p2p_ft_server_send_OK(xfer_old); /* removing top of filename & size list completely */ g_free( xfer_data->filename_list->data ); @@ -684,7 +717,7 @@ purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv); - /*update map to current xfer*/ + /* update map to current xfer */ g_hash_table_remove(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring); g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer); @@ -980,7 +1013,7 @@ return; } - /*TODO:actually, u must try with addr no.1 , if its not working addr no.2 .....*/ + /* TODO:actually, u must try with addr no.1 , if its not working addr no.2 ..... */ addr = hosts->data; actaddr = addr->sin_addr.s_addr; d = actaddr % 256; @@ -1030,30 +1063,24 @@ yahoo_packet_send_and_free(pkt, yd); } - void yahoo_send_file(PurpleConnection *gc, const char *who, const char *file) { struct yahoo_xfer_data *xfer_data; struct yahoo_data *yd = gc->proto_data; - int ver = 0; PurpleXfer *xfer = yahoo_new_xfer(gc, who); - YahooFriend *yf = yahoo_friend_find(gc, who); - - /* To determine if we should use yahoo p15 for transfer. Check other user's - * reported version, but if we're on Yahoo Japan, ignore it. */ - if(yf && yf->version_id > 500000 && !yd->jp) - ver = 15; g_return_if_fail(xfer != NULL); - if(ver == 15) { - xfer_data = xfer->data; - xfer_data->status_15 = STARTED; - purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); - xfer_data->version = 15; - xfer_data->xfer_peer_idstring = yahoo_xfer_new_xfer_id(); - g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer); - } + /* if we don't have a p2p connection, try establishing it now */ + if( !g_hash_table_lookup(yd->peers, who) ) + yahoo_send_p2p_pkt(gc, who, 0); + + xfer_data = xfer->data; + xfer_data->status_15 = STARTED; + purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); + xfer_data->version = 15; + xfer_data->xfer_peer_idstring = yahoo_xfer_new_xfer_id(); + g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer); /* Now perform the request */ if (file) @@ -1062,7 +1089,9 @@ purple_xfer_request(xfer); } -static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/*using this in recv_cb*/ +static void yahoo_p2p_ft_server_listen_cb(int listenfd, gpointer data); /* using this in yahoo_xfer_send_cb_15 */ +static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/* using this in recv_cb */ + static void yahoo_xfer_recv_cb_15(gpointer data, gint source, PurpleInputCondition condition) { PurpleXfer *xfer; @@ -1102,7 +1131,7 @@ if(xd->status_15 == HEAD_REQUESTED) { xd->status_15 = HEAD_REPLY_RECEIVED; - close(source);/*Is this required?*/ + close(source);/* Is this required? */ g_free(xd->txbuf); xd->txbuf = NULL; if (purple_proxy_connect(NULL, account, xd->host, xd->port, yahoo_xfer_connected_15, xfer) == NULL) @@ -1151,7 +1180,7 @@ xd->txbuf_written = 0; if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED) - { + { xd->status_15 = HEAD_REQUESTED; xd->tx_handler = purple_input_add(source, PURPLE_INPUT_READ, yahoo_xfer_recv_cb_15, xfer); yahoo_xfer_recv_cb_15(xfer, source, PURPLE_INPUT_READ); @@ -1162,21 +1191,33 @@ xfer->fd = source; purple_xfer_start(xfer, source, NULL, 0); } - else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED) + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && (xd->status_15 == ACCEPTED || xd->status_15 == P2P_GET_REQUESTED) ) { xd->status_15 = TRANSFER_PHASE; xfer->fd = source; + /* Remove Read event */ + purple_input_remove(xd->input_event); + xd->input_event = 0; purple_xfer_start(xfer, source, NULL, 0); } + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == P2P_HEAD_REQUESTED) + { + xd->status_15 = P2P_HEAD_REPLIED; + /* Remove Read event and close descriptor */ + purple_input_remove(xd->input_event); + xd->input_event = 0; + close(source); + xfer->fd = -1; + /* start local server, listen for connections */ + purple_network_listen(xd->yahoo_local_p2p_ft_server_port, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer); + } else { purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15); return; } - } - static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message) { PurpleXfer *xfer; @@ -1203,31 +1244,61 @@ cookies = yahoo_get_cookies(xd->gc); if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED) { - xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n", + if(xd->info_val_249 == 2) + { + /* sending file via p2p, we are connected as client */ + xd->txbuf = g_strdup_printf("POST /%s HTTP/1.1\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n", + xd->path, + xd->host, + (long int)xfer->size); /* to do, add Referer */ + } + else + { + /* sending file via relaying */ + xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n", purple_url_encode(xd->xfer_idstring_for_relay), purple_normalize(account, purple_account_get_username(account)), xfer->who, cookies, xd->host, (long int)xfer->size); + } } else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED) { - xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nAccept:*/*\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n", + if(xd->info_val_249 == 1) + { + /* receiving file via p2p, connected as client */ + xd->txbuf = g_strdup_printf("HEAD /%s HTTP/1.1\r\nAccept:*/*\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n",xd->path,xd->host); + } + else + { + /* receiving file via relaying */ + xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nAccept:*/*\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n", purple_url_encode(xd->xfer_idstring_for_relay), purple_normalize(account, purple_account_get_username(account)), xfer->who, cookies, xd->host); + } } else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED) { - xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nConnection: Keep-Alive\r\n\r\n", + if(xd->info_val_249 == 1) + { + /* receiving file via p2p, connected as client */ + xd->txbuf = g_strdup_printf("GET /%s HTTP/1.1\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n",xd->path,xd->host); + } + else + { + /* receiving file via relaying */ + xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nConnection: Keep-Alive\r\n\r\n", purple_url_encode(xd->xfer_idstring_for_relay), purple_normalize(account, purple_account_get_username(account)), xfer->who, cookies, xd->host); + } } else { @@ -1248,6 +1319,225 @@ } } +static void yahoo_p2p_ft_POST_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + + xfer = data; + if (!(xd = xfer->data)) { + purple_input_remove(xd->input_event); + purple_xfer_cancel_remote(xfer); + return; + } + + purple_input_remove(xd->input_event); + xd->status_15 = TRANSFER_PHASE; + xfer->fd = source; + purple_xfer_start(xfer, source, NULL, 0); +} + +static void yahoo_p2p_ft_HEAD_GET_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + guchar buf[1024]; + int len; + char *url_head; + char *url_get; + time_t unix_time; + char *time_str; + + xfer = data; + if (!(xd = xfer->data)) { + purple_input_remove(xd->input_event); + purple_xfer_cancel_remote(xfer); + return; + } + + len = read(source, buf, sizeof(buf)); + if ((len < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + return ; /* No Worries*/ + else if (len <= 0) { + purple_debug_warning("yahoo","p2p-ft: Error in connection, or host disconnected\n"); + purple_input_remove(xd->input_event); + purple_xfer_cancel_remote(xfer); + return; + } + + url_head = g_strdup_printf("HEAD %s", xd->xfer_url); + url_get = g_strdup_printf("GET %s", xd->xfer_url); + + if( strncmp(url_head, (char *)buf, strlen(url_head)) == 0 ) + xd->status_15 = P2P_HEAD_REQUESTED; + else if( strncmp(url_get, (char *)buf, strlen(url_get)) == 0 ) + xd->status_15 = P2P_GET_REQUESTED; + else { + purple_debug_warning("yahoo","p2p-ft: Wrong HEAD/GET request from peer, disconnecting host\n"); + purple_input_remove(xd->input_event); + purple_xfer_cancel_remote(xfer); + g_free(url_head); + return; + } + + unix_time = time(NULL); + time_str = ctime(&unix_time); + strcpy(time_str + strlen(time_str) - 1, "\0"); + + if (xd->txbuflen == 0) { + xd->txbuf = g_strdup_printf("HTTP/1.0 200 OK\r\nDate: %s GMT\r\nServer: Y!/1.0\r\nMIME-version: 1.0\r\nLast-modified: %s GMT\r\nContent-length: %d\r\n\r\n", time_str, time_str, xfer->size); + xd->txbuflen = strlen(xd->txbuf); + xd->txbuf_written = 0; + } + + if (!xd->tx_handler) { + xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, yahoo_xfer_send_cb_15, xfer); + yahoo_xfer_send_cb_15(xfer, source, PURPLE_INPUT_WRITE); + } + + g_free(url_head); + g_free(url_get); +} + +static void yahoo_p2p_ft_server_send_connected_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + int acceptfd; + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + + xfer = data; + if (!(xd = xfer->data)) { + purple_xfer_cancel_remote(xfer); + return; + } + + acceptfd = accept(source, NULL, 0); + if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return; + else if(acceptfd == -1) { + purple_debug_warning("yahoo","yahoo_p2p_server_send_connected_cb: accept: %s\n", g_strerror(errno)); + purple_xfer_cancel_remote(xfer); + /* remove watcher and close p2p ft server */ + purple_input_remove(xd->yahoo_p2p_ft_server_watcher); + close(xd->yahoo_local_p2p_ft_server_fd); + return; + } + + /* remove watcher and close p2p ft server */ + purple_input_remove(xd->yahoo_p2p_ft_server_watcher); + close(xd->yahoo_local_p2p_ft_server_fd); + + /* Add an Input Read event to the file descriptor */ + xfer->fd = acceptfd; + if(xfer->type == PURPLE_XFER_RECEIVE) + xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_POST_cb, data); + else + xd->input_event = purple_input_add(acceptfd, PURPLE_INPUT_READ, yahoo_p2p_ft_HEAD_GET_cb, data); +} + +static void yahoo_p2p_ft_server_listen_cb(int listenfd, gpointer data) +{ + PurpleXfer *xfer; + struct yahoo_xfer_data *xd; + struct yahoo_packet *pkt; + PurpleAccount *account; + struct yahoo_data *yd; + gchar *filename; + const char *local_ip; + gchar *url_to_send = NULL; + char *filename_without_spaces = NULL; + + xfer = data; + if ( !( (xd = xfer->data) || (listenfd != -1) ) ) { + purple_debug_warning("yahoo","p2p: error starting server for p2p file transfer\n"); + purple_xfer_cancel_remote(xfer); + return; + } + + if( (xfer->type == PURPLE_XFER_RECEIVE) || (xd->status_15 != P2P_HEAD_REPLIED) ) { + yd = xd->gc->proto_data; + account = purple_connection_get_account(xd->gc); + local_ip = purple_network_get_my_ip(listenfd); + xd->yahoo_local_p2p_ft_server_port = purple_network_get_port_from_fd(listenfd); + + filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); + filename_without_spaces = g_strdup(filename); + purple_util_chrreplace(filename_without_spaces, ' ', '+'); + xd->xfer_url = g_strdup_printf("/Messenger.%s.%d000%s?AppID=Messenger&UserID=%s&K=lc9lu2u89gz1llmplwksajkjx", xfer->who, (int)time(NULL), filename_without_spaces, xfer->who); + url_to_send = g_strdup_printf("http://%s:%d%s", local_ip, xd->yahoo_local_p2p_ft_server_port, xd->xfer_url); + + if(xfer->type == PURPLE_XFER_RECEIVE) { + xd->info_val_249 = 2; /* 249=2: we are p2p server, and receiving file */ + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, + YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, "ssssis", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xd->xfer_peer_idstring, + 27, xfer->filename, + 249, 2, + 250, url_to_send); + } + else { + xd->info_val_249 = 1; /* 249=1: we are p2p server, and sending file */ + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, "ssssis", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xd->xfer_peer_idstring, + 27, filename, + 249, 1, + 250, url_to_send); + } + + yahoo_packet_send_and_free(pkt, yd); + + g_free(filename); + g_free(url_to_send); + g_free(filename_without_spaces); + } + + /* Add an Input Read event to the file descriptor */ + xd->yahoo_local_p2p_ft_server_fd = listenfd; + xd->yahoo_p2p_ft_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_ft_server_send_connected_cb, data); +} + +/* send (p2p) file transfer information */ +static void yahoo_p2p_client_send_ft_info(PurpleConnection *gc, PurpleXfer *xfer) +{ + struct yahoo_xfer_data *xd; + struct yahoo_packet *pkt; + PurpleAccount *account; + struct yahoo_data *yd; + gchar *filename; + struct yahoo_p2p_data *p2p_data; + + if (!(xd = xfer->data)) + return; + + account = purple_connection_get_account(gc); + yd = gc->proto_data; + + p2p_data = g_hash_table_lookup(yd->peers, xfer->who); + if( p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER ) + if(purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer)) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id); + filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); + + yahoo_packet_hash(pkt, "ssssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xd->xfer_peer_idstring, + 27, filename, + 249, 2); /* 249=2: we are p2p client */ + xd->info_val_249 = 2; + yahoo_packet_send_and_free(pkt, yd); + + g_free(filename); +} + void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt) { char *from = NULL; @@ -1293,14 +1583,14 @@ /* 1=send, 2=cancel, 3=accept, 4=reject */ break; - /*check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/ + /* check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it. */ case 49: service = pair->value; break; case 63: imv = pair->value; break; - /*end check*/ + /* end check */ } } @@ -1328,6 +1618,14 @@ * so, purple dnsquery is used... but retries, trying with next ip * address etc. is not implemented..TODO */ + + /* To send through p2p */ + if( g_hash_table_lookup(yd->peers, from) ) { + /* send p2p file transfer information */ + yahoo_p2p_client_send_ft_info(gc, xfer); + return; + } + if (yd->jp) { purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT, @@ -1341,7 +1639,7 @@ return; } - /*processing for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/ + /* processing for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it. */ /* * The remote user has changed their IMVironment. We * record it for later use. @@ -1357,7 +1655,7 @@ return; } } - /*end processing*/ + /* end processing */ if(!filename_list) return; @@ -1432,6 +1730,7 @@ GSList *l; struct yahoo_packet *pkt_to_send; PurpleAccount *account; + struct yahoo_p2p_data *p2p_data; yd = gc->proto_data; @@ -1455,13 +1754,8 @@ val_66 = strtol(pair->value, NULL, 10); break; case 249: - val_249 = strtol(pair->value, NULL, 10); /* - * really pissed off with this- i hv seen 2 occurences of this - * being 1(its normally 3) - and in those cases, the url - * format and corresponding processing seems to be different - * (i havent tested - couldnt reproduce a 1), although i - * guess its easier. - */ + val_249 = strtol(pair->value, NULL, 10); + /* 249 has value 1 or 2 when doing p2p transfer and value 3 when relaying through yahoo server */ break; case 250: url = pair->value; @@ -1489,35 +1783,48 @@ xfer_data->info_val_249 = val_249; xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); - if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) { - purple_xfer_cancel_remote(xfer); - return; - } + if(val_249 == 1 || val_249 == 3) { + if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) { + purple_xfer_cancel_remote(xfer); + return; + } + + account = purple_connection_get_account(xfer_data->gc); - account = purple_connection_get_account(xfer_data->gc); + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, + YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssssisi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 27, xfer->filename, + 249, xfer_data->info_val_249, + 251, xfer_data->xfer_idstring_for_relay, + 222, 3); - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, - YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_send_and_free(pkt_to_send, yd); - yahoo_packet_hash(pkt_to_send, "ssssisi", - 1, purple_normalize(account, purple_account_get_username(account)), - 5, xfer->who, - 265, xfer_data->xfer_peer_idstring, - 27, xfer->filename, - 249, xfer_data->info_val_249, - 251, xfer_data->xfer_idstring_for_relay, - 222, 3); + if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, + yahoo_xfer_connected_15, xfer) == NULL) { + purple_notify_error(gc, NULL, _("File Transfer Failed"), + _("Unable to establish file descriptor.")); + purple_xfer_cancel_remote(xfer); + } + } + else if(val_249 == 2) { + p2p_data = g_hash_table_lookup(yd->peers, xfer->who); + if( !( p2p_data && (p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) ) ) { + purple_xfer_cancel_remote(xfer); + return; + } + if(!purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer)) { + purple_xfer_cancel_remote(xfer); + return; + } + } +} - yahoo_packet_send_and_free(pkt_to_send, yd); - if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, - yahoo_xfer_connected_15, xfer) == NULL) { - purple_notify_error(gc, NULL, _("File Transfer Failed"), - _("Unable to establish file descriptor.")); - purple_xfer_cancel_remote(xfer); - } - -} -/*TODO: Check filename etc. No probs till some hacker comes in the way*/ +/* TODO: Check filename etc. No probs till some hacker comes in the way */ void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *pkt) { gchar *xfer_peer_idstring = NULL; @@ -1528,6 +1835,8 @@ GSList *l; PurpleAccount *account; long val_66 = 0; + gchar *url = NULL; + int val_249 = 0; yd = gc->proto_data; for (l = pkt->hash; l; l = l->next) { @@ -1542,19 +1851,35 @@ break; case 66: val_66 = atol(pair->value); + break; + case 249: + val_249 = atol(pair->value); + break; + case 250: + url = pair->value; /* we get a p2p url here when sending file, connected as client */ + break; } } xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_peer_idstring); if(!xfer) return; - if(val_66 == -1 || !(xfer_idstring_for_relay)) + if(val_66 == -1 || ( (!(xfer_idstring_for_relay)) && (val_249 != 2) )) + { + purple_xfer_cancel_remote(xfer); + return; + } + + if( (val_249 == 2) && (!(url)) ) { purple_xfer_cancel_remote(xfer); return; } xfer_data = xfer->data; + if(url) + purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL); + xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); xfer_data->status_15 = ACCEPTED; account = purple_connection_get_account(gc); diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/yahoo/yahoo_friend.c --- a/libpurple/protocols/yahoo/yahoo_friend.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.c Sat Mar 21 03:49:03 2009 +0000 @@ -147,24 +147,36 @@ { GSList *l = pkt->hash; YahooFriend *f; + char *temp = NULL; char *who = NULL; int value = 0; + int protocol = 0; + gboolean msn = FALSE; while (l) { struct yahoo_pair *pair = l->data; switch (pair->key) { case 7: - who = pair->value; + temp = pair->value; break; case 31: value = strtol(pair->value, NULL, 10); break; + case 241: + protocol = strtol(pair->value, NULL, 10); + msn = TRUE; + break; } l = l->next; } + if(msn) + who = g_strconcat("msn/", temp, NULL); + else + who = g_strdup(temp); + if (value != 1 && value != 2) { purple_debug_error("yahoo", "Received unknown value for presence key: %d\n", value); return; @@ -173,8 +185,10 @@ g_return_if_fail(who != NULL); f = yahoo_friend_find(gc, who); - if (!f) + if (!f) { + g_free(who); return; + } if (pkt->service == YAHOO_SERVICE_PRESENCE_PERM) { purple_debug_info("yahoo", "Setting permanent presence for %s to %d.\n", who, (value == 1)); @@ -194,6 +208,7 @@ else f->presence = YAHOO_PRESENCE_DEFAULT; } + g_free(who); } void yahoo_friend_update_presence(PurpleConnection *gc, const char *name, @@ -204,6 +219,7 @@ YahooFriend *f; const char *thirtyone, *thirteen; int service = -1; + const char *temp = NULL; if (!yd->logged_in) return; @@ -212,6 +228,11 @@ if (!f) return; + if(f->protocol == 2) + temp = name+4; + else + temp = name; + /* No need to change the value if it is already correct */ if (f->presence == presence) { purple_debug_info("yahoo", "Not setting presence because there are no changes.\n"); @@ -236,12 +257,21 @@ if (f->presence == YAHOO_PRESENCE_PERM_OFFLINE) { pkt = yahoo_packet_new(YAHOO_SERVICE_PRESENCE_PERM, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt, "ssssssss", + if(f->protocol) + yahoo_packet_hash(pkt, "ssssssiss", 1, purple_connection_get_display_name(gc), 31, "2", 13, "2", 302, "319", 300, "319", - 7, name, + 7, temp, 241, f->protocol, 301, "319", 303, "319"); + else + yahoo_packet_hash(pkt, "ssssssss", + 1, purple_connection_get_display_name(gc), + 31, "2", 13, "2", + 302, "319", 300, "319", + 7, temp, + 301, "319", 303, "319"); + yahoo_packet_send_and_free(pkt, yd); } @@ -254,13 +284,31 @@ pkt = yahoo_packet_new(service, YAHOO_STATUS_AVAILABLE, yd->session_id); - yahoo_packet_hash(pkt, "ssssssss", + if(f->protocol) + yahoo_packet_hash(pkt, "ssssssiss", 1, purple_connection_get_display_name(gc), 31, thirtyone, 13, thirteen, 302, "319", 300, "319", - 7, name, + 7, temp, 241, f->protocol, + 301, "319", 303, "319"); + else + yahoo_packet_hash(pkt, "ssssssss", + 1, purple_connection_get_display_name(gc), + 31, thirtyone, 13, thirteen, + 302, "319", 300, "319", + 7, temp, 301, "319", 303, "319"); yahoo_packet_send_and_free(pkt, yd); } } + +void yahoo_friend_set_p2p_status(YahooFriend *f, YahooP2PStatus p2p_status) +{ + f->p2p_status = p2p_status; +} + +YahooP2PStatus yahoo_friend_get_p2p_status(YahooFriend *f) +{ + return f->p2p_status; +} diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/yahoo/yahoo_friend.h --- a/libpurple/protocols/yahoo/yahoo_friend.h Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.h Sat Mar 21 03:49:03 2009 +0000 @@ -34,6 +34,13 @@ YAHOO_PRESENCE_PERM_OFFLINE } YahooPresenceVisibility; +typedef enum { + YAHOO_P2PSTATUS_NOT_CONNECTED = 0, + YAHOO_P2PSTATUS_DO_NOT_CONNECT, + YAHOO_P2PSTATUS_WE_ARE_SERVER, + YAHOO_P2PSTATUS_WE_ARE_CLIENT +} YahooP2PStatus; + /* these are called friends instead of buddies mainly so I can use variables * named f and not confuse them with variables named b */ @@ -50,6 +57,9 @@ int protocol; /* 1=LCS, 2=MSN*/ long int version_id; gchar *alias_id; + YahooP2PStatus p2p_status; + gboolean p2p_packet_sent; /* 0:not sent, 1=sent */ + gint session_id; /* session id of friend */ } YahooFriend; YahooFriend *yahoo_friend_find(PurpleConnection *gc, const char *name); @@ -76,4 +86,7 @@ void yahoo_friend_update_presence(PurpleConnection *gc, const char *name, YahooPresenceVisibility presence); +void yahoo_friend_set_p2p_status(YahooFriend *f, YahooP2PStatus p2p_status); +YahooP2PStatus yahoo_friend_get_p2p_status(YahooFriend *f); + #endif /* _YAHOO_FRIEND_H_ */ diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/protocols/yahoo/yahoo_packet.h --- a/libpurple/protocols/yahoo/yahoo_packet.h Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.h Sat Mar 21 03:49:03 2009 +0000 @@ -98,15 +98,18 @@ YAHOO_SERVICE_AVATAR_UPDATE = 0xc7, YAHOO_SERVICE_VERIFY_ID_EXISTS = 0xc8, YAHOO_SERVICE_AUDIBLE = 0xd0, + /* YAHOO_SERVICE_CHAT_SESSION = 0xd4,?? Reports start of chat session, gets an id from server */ YAHOO_SERVICE_AUTH_REQ_15 = 0xd6, + YAHOO_SERVICE_FILETRANS_15 = 0xdc, + YAHOO_SERVICE_FILETRANS_INFO_15 = 0xdd, + YAHOO_SERVICE_FILETRANS_ACC_15 = 0xde, + /* photo sharing services ?? - 0xd2, 0xd7, 0xd8, 0xda */ YAHOO_SERVICE_CHGRP_15 = 0xe7, YAHOO_SERVICE_STATUS_15 = 0xf0, YAHOO_SERVICE_LIST_15 = 0xf1, - YAHOO_SERVICE_FILETRANS_15 = 0xdc, - YAHOO_SERVICE_FILETRANS_INFO_15 = 0xdd, - YAHOO_SERVICE_FILETRANS_ACC_15 = 0xde, YAHOO_SERVICE_WEBLOGIN = 0x0226, YAHOO_SERVICE_SMS_MSG = 0x02ea + /* YAHOO_SERVICE_DISCONNECT = 0x07d1 Server forces us to disconnect. Is sent with TCP FIN flag set */ }; struct yahoo_pair { diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/util.c --- a/libpurple/util.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/util.c Sat Mar 21 03:49:03 2009 +0000 @@ -2850,6 +2850,12 @@ return "icon"; } +/* + * TODO: Consider using something faster than SHA-1, such as MD5, MD4 + * or CRC32. Are there security implications to that? Would + * probably be a good idea to benchmark some algorithms with + * 3KB-10KB chunks of data (typical buddy icon sizes). + */ char * purple_util_get_image_checksum(gconstpointer image_data, size_t image_len) { @@ -4038,6 +4044,13 @@ &gfud->website.page, &gfud->website.user, &gfud->website.passwd); if (purple_strcasestr(url, "https://") != NULL) { + if (!purple_ssl_is_supported()) { + purple_util_fetch_url_error(gfud, + _("Unable to connect to %s: Server requires TLS/SSL, but no TLS/SSL support was found."), + gfud->website.address); + return NULL; + } + gfud->is_ssl = TRUE; gfud->ssl_connection = purple_ssl_connect(NULL, gfud->website.address, gfud->website.port, diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/xmlnode.c --- a/libpurple/xmlnode.c Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/xmlnode.c Sat Mar 21 03:49:03 2009 +0000 @@ -27,6 +27,7 @@ * libxode uses memory pools that we simply have no need for, I decided to * write my own stuff. Also, re-writing this lets me be as lightweight * as I want to be. Thank you libxode for giving me a good starting point */ +#define _PURPLE_XMLNODE_C_ #include "debug.h" #include "internal.h" @@ -126,21 +127,28 @@ g_return_if_fail(node != NULL); g_return_if_fail(attr != NULL); - for(attr_node = node->child; attr_node; attr_node = attr_node->next) - { + attr_node = node->child; + while (attr_node) { if(attr_node->type == XMLNODE_TYPE_ATTRIB && purple_strequal(attr_node->name, attr)) { - if(sibling == NULL) { - node->child = attr_node->next; - } else { - sibling->next = attr_node->next; - } if (node->lastchild == attr_node) { node->lastchild = sibling; } - xmlnode_free(attr_node); - return; + if (sibling == NULL) { + node->child = attr_node->next; + xmlnode_free(attr_node); + attr_node = node->child; + } else { + sibling->next = attr_node->next; + sibling = attr_node->next; + xmlnode_free(attr_node); + attr_node = sibling; + } + } + else + { + attr_node = attr_node->next; } sibling = attr_node; } @@ -178,24 +186,25 @@ void xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) { - xmlnode *attrib_node; - - g_return_if_fail(node != NULL); - g_return_if_fail(attr != NULL); - g_return_if_fail(value != NULL); - xmlnode_remove_attrib(node, attr); - - attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); - - attrib_node->data = g_strdup(value); - - xmlnode_insert_child(node, attrib_node); + xmlnode_set_attrib_full(node, attr, NULL, NULL, value); } void xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value) { + xmlnode_set_attrib_full(node, attr, xmlns, NULL, value); +} + +void +xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value) +{ + xmlnode_set_attrib_full(node, attr, NULL, prefix, value); +} + +void +xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns, const char *prefix, const char *value) +{ xmlnode *attrib_node; g_return_if_fail(node != NULL); @@ -207,22 +216,6 @@ attrib_node->data = g_strdup(value); attrib_node->xmlns = g_strdup(xmlns); - - xmlnode_insert_child(node, attrib_node); -} - -void -xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value) -{ - xmlnode *attrib_node; - - g_return_if_fail(node != NULL); - g_return_if_fail(attr != NULL); - g_return_if_fail(value != NULL); - - attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); - - attrib_node->data = g_strdup(value); attrib_node->prefix = g_strdup(prefix); xmlnode_insert_child(node, attrib_node); @@ -585,7 +578,8 @@ } for(i=0; i < nb_attributes * 5; i+=5) { - const char *prefix = (const char *)attributes[i + 1]; + const char *name = (const char *)attributes[i]; + const char *prefix = (const char *)attributes[i+1]; char *txt; int attrib_len = attributes[i+4] - attributes[i+3]; char *attrib = g_malloc(attrib_len + 1); @@ -594,11 +588,7 @@ txt = attrib; attrib = purple_unescape_html(txt); g_free(txt); - if (prefix && *prefix) { - xmlnode_set_attrib_with_prefix(node, (const char*) attributes[i], prefix, attrib); - } else { - xmlnode_set_attrib(node, (const char*) attributes[i], attrib); - } + xmlnode_set_attrib_full(node, name, NULL, prefix, attrib); g_free(attrib); } diff -r c605e5f2fc99 -r e4a060a1e3de libpurple/xmlnode.h --- a/libpurple/xmlnode.h Sat Mar 21 03:03:18 2009 +0000 +++ b/libpurple/xmlnode.h Sat Mar 21 03:49:03 2009 +0000 @@ -157,6 +157,7 @@ */ void xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value); +#if !(defined PURPLE_DISABLE_DEPRECATED) || (defined _PURPLE_XMLNODE_C_) /** * Sets a prefixed attribute for a node * @@ -164,6 +165,8 @@ * @param attr The name of the attribute to set * @param prefix The prefix of the attribute to ste * @param value The value of the attribute + * + * @deprecated Use xmlnode_set_attrib_full instead. */ void xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value); @@ -174,8 +177,25 @@ * @param attr The name of the attribute to set * @param xmlns The namespace of the attribute to ste * @param value The value of the attribute + * + * @deprecated Use xmlnode_set_attrib_full instead. */ void xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value); +#endif /* PURPLE_DISABLE_DEPRECATED */ + +/** + * Sets a namespaced attribute for a node + * + * @param node The node to set an attribute for. + * @param attr The name of the attribute to set + * @param xmlns The namespace of the attribute to ste + * @param prefix The prefix of the attribute to ste + * @param value The value of the attribute + * + * @since 2.6.0 + */ +void xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns, + const char *prefix, const char *value); /** * Gets an attribute from a node. diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtkblist.c Sat Mar 21 03:49:03 2009 +0000 @@ -4838,8 +4838,9 @@ #define SSL_FAQ_URI "http://d.pidgin.im/wiki/FAQssl" static void -ssl_faq_clicked_cb(GtkButton *button, - PurpleAccount *account) +ssl_faq_clicked_cb(PidginMiniDialog *mini_dialog, + GtkButton *button, + gpointer ignored) { purple_notify_uri(NULL, SSL_FAQ_URI); } @@ -4872,25 +4873,9 @@ g_object_set_data(G_OBJECT(mini_dialog), OBJECT_DATA_KEY_ACCOUNT, account); - if(err->type == PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT) { - GtkWidget *faq_button = gtk_button_new(); - GtkWidget *faq_label = gtk_label_new(NULL); - gtk_label_set_markup(GTK_LABEL(faq_label), - "" SSL_FAQ_URI ""); -#if GTK_CHECK_VERSION(2,6,0) - g_object_set(G_OBJECT(faq_label), "ellipsize", - PANGO_ELLIPSIZE_MIDDLE, NULL); -#endif - gtk_container_add(GTK_CONTAINER(faq_button), faq_label); - gtk_button_set_relief(GTK_BUTTON(faq_button), GTK_RELIEF_NONE); - - g_signal_connect(faq_button, "clicked", - (GCallback)ssl_faq_clicked_cb, account); - - gtk_box_pack_start(PIDGIN_MINI_DIALOG(mini_dialog)->contents, - faq_button, FALSE, FALSE, 0); - } + if(err->type == PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT) + pidgin_mini_dialog_add_button(PIDGIN_MINI_DIALOG(mini_dialog), + _("SSL FAQs"), ssl_faq_clicked_cb, NULL); g_signal_connect_after(mini_dialog, "destroy", (GCallback)generic_error_destroy_cb, diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtkconv.c --- a/pidgin/gtkconv.c Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtkconv.c Sat Mar 21 03:49:03 2009 +0000 @@ -44,6 +44,7 @@ #include "account.h" #include "cmds.h" +#include "core.h" #include "debug.h" #include "idle.h" #include "imgstore.h" @@ -329,7 +330,8 @@ PurpleCmdStatus status; if (!g_ascii_strcasecmp(args[0], "version")) { - tmp = g_strdup_printf("me is using %s v%s.", "Pidgin", DISPLAY_VERSION); + tmp = g_strdup_printf("me is using Pidgin v%s with libpurple v%s.", + DISPLAY_VERSION, purple_core_get_version()); markup = g_markup_escape_text(tmp, -1); status = purple_cmd_do_command(conv, tmp, markup, error); diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtkdialogs.c Sat Mar 21 03:49:03 2009 +0000 @@ -33,6 +33,7 @@ #include "prpl.h" #include "request.h" #include "util.h" +#include "core.h" #include "gtkblist.h" #include "gtkdialogs.h" @@ -188,7 +189,7 @@ {N_("Italian"), "it", "Claudio Satriano", "satriano@na.infn.it"}, {N_("Japanese"), "ja", "Takashi Aihana", "aihana@gnome.gr.jp"}, {N_("Georgian"), "ka", N_("Ubuntu Georgian Translators"), "alexander.didebulidze@stusta.mhn.de"}, - {"Khmer", "km", "Khoem Sokhem", "khoemsokhem@khmeros.info"}, + {N_("Khmer"), "km", "Khoem Sokhem", "khoemsokhem@khmeros.info"}, {N_("Kannada"), "kn", N_("Kannada Translation team"), "translation@sampada.info"}, {N_("Korean"), "ko", "Sushizang", "sushizang@empal.com"}, {N_("Kurdish"), "ku", "Erdal Ronahi", "erdal.ronahi@gmail.com"}, @@ -441,7 +442,7 @@ str = g_string_sized_new(4096); g_string_append_printf(str, - "
%s %s


", PIDGIN_NAME, DISPLAY_VERSION); + "
%s %s

(libpurple %s)

", PIDGIN_NAME, DISPLAY_VERSION, purple_core_get_version()); g_string_append_printf(str, _("%s is a graphical modular messaging client based on " diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtkmain.c --- a/pidgin/gtkmain.c Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtkmain.c Sat Mar 21 03:49:03 2009 +0000 @@ -669,7 +669,8 @@ } /* show version message */ if (opt_version) { - printf("%s %s\n", PIDGIN_NAME, DISPLAY_VERSION); + printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION, + purple_core_get_version()); #ifdef HAVE_SIGNAL_H g_free(segfault_message); #endif diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtknotify.c --- a/pidgin/gtknotify.c Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtknotify.c Sat Mar 21 03:49:03 2009 +0000 @@ -28,6 +28,7 @@ #include +#include "account.h" #include "connection.h" #include "debug.h" #include "prefs.h" @@ -37,6 +38,7 @@ #include "gtkblist.h" #include "gtkimhtml.h" #include "gtknotify.h" +#include "gtkpounce.h" #include "gtkutils.h" typedef struct @@ -57,6 +59,13 @@ typedef struct { PurpleAccount *account; + PurplePounce *pounce; +} PidginNotifyPounceData; + + +typedef struct +{ + PurpleAccount *account; GtkListStore *model; GtkWidget *treeview; GtkWidget *window; @@ -80,21 +89,44 @@ COLUMNS_PIDGIN_MAIL }; -typedef struct _PidginMailDialog PidginMailDialog; +enum +{ + PIDGIN_POUNCE_ICON, + PIDGIN_POUNCE_ALIAS, + PIDGIN_POUNCE_EVENT, + PIDGIN_POUNCE_TEXT, + PIDGIN_POUNCE_DATE, + PIDGIN_POUNCE_DATA, + PIDGIN_POUNCE_COLUMNS +}; -struct _PidginMailDialog +typedef struct _PidginNotifyDialog PidginNotifyDialog; +typedef PidginNotifyDialog PidginMailDialog; + +struct _PidginNotifyDialog { GtkWidget *dialog; GtkWidget *treeview; GtkTreeStore *treemodel; GtkLabel *label; GtkWidget *open_button; + GtkWidget *dismiss_button; + GtkWidget *edit_button; int total_count; gboolean in_use; }; -static PidginMailDialog *mail_dialog = NULL; +typedef enum +{ + PIDGIN_NOTIFY_MAIL, + PIDGIN_NOTIFY_POUNCE, + PIDGIN_NOTIFY_TYPES +} PidginNotifyType; +static PidginNotifyDialog *mail_dialog = NULL; +static PidginNotifyDialog *pounce_dialog = NULL; + +static GtkWidget *pidgin_get_notification_dialog(PidginNotifyType type); static void *pidgin_notify_emails(PurpleConnection *gc, size_t count, gboolean detailed, const char **subjects, const char **froms, const char **tos, @@ -109,6 +141,159 @@ } static void +pounce_response_close(PidginNotifyDialog *dialog) +{ + GtkTreeIter iter; + PidginNotifyPounceData *pounce_data; + + while (gtk_tree_model_get_iter_first( + GTK_TREE_MODEL(pounce_dialog->treemodel), &iter)) { + gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter, + PIDGIN_POUNCE_DATA, &pounce_data, + -1); + gtk_tree_store_remove(dialog->treemodel, &iter); + + g_free(pounce_data); + } + + gtk_widget_destroy(pounce_dialog->dialog); + g_free(pounce_dialog); + pounce_dialog = NULL; +} + +static void +delete_foreach(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + PidginNotifyPounceData *pounce_data; + + gtk_tree_model_get(model, iter, + PIDGIN_POUNCE_DATA, &pounce_data, + -1); + + if (pounce_data != NULL) + g_free(pounce_data); +} + +static void +append_to_list(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + GList **list = data; + *list = g_list_prepend(*list, gtk_tree_path_copy(path)); +} +static void +pounce_response_dismiss() +{ + GtkTreeSelection *selection; + GList *list = NULL; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog->treeview)); + gtk_tree_selection_selected_foreach(selection, delete_foreach, pounce_dialog); + gtk_tree_selection_selected_foreach(selection, append_to_list, &list); + + while (list) { + GtkTreeIter iter; + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter, + list->data)) { + gtk_tree_store_remove(GTK_TREE_STORE(pounce_dialog->treemodel), &iter); + } + gtk_tree_path_free(list->data); + list = g_list_delete_link(list, list); + } +} + +static void +pounce_response_edit_cb(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + PidginNotifyPounceData *pounce_data; + PidginNotifyDialog *dialog = (PidginNotifyDialog*)data; + PurplePounce *pounce; + GList *list; + + list = purple_pounces_get_all(); + + gtk_tree_model_get(GTK_TREE_MODEL(dialog->treemodel), iter, + PIDGIN_POUNCE_DATA, &pounce_data, + -1); + + for (; list != NULL; list = list->next) { + pounce = list->data; + if (pounce == pounce_data->pounce) { + pidgin_pounce_editor_show(pounce_data->account, NULL, pounce_data->pounce); + return; + } + } + + purple_debug_warning("gtknotify", "Pounce was destroyed.\n"); +} + +static void +pounce_response_cb(GtkDialog *dlg, gint id, PidginNotifyDialog *dialog) +{ + GtkTreeSelection *selection = NULL; + + switch (id) { + case GTK_RESPONSE_CLOSE: + case GTK_RESPONSE_DELETE_EVENT: + pounce_response_close(dialog); + break; + case GTK_RESPONSE_NO: + pounce_response_dismiss(); + break; + case GTK_RESPONSE_APPLY: + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->treeview)); + gtk_tree_selection_selected_foreach(selection, pounce_response_edit_cb, + dialog); + break; + } +} + +static void +pounce_row_selected_cb(GtkTreeView *tv, GtkTreePath *path, + GtkTreeViewColumn *col, gpointer data) +{ + GtkTreeIter iter; + GtkTreeSelection *selection; + gboolean selected; + GList *list; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pounce_dialog->treeview)); + + selected = gtk_tree_selection_get_selected(selection, + NULL, &iter); + + if (selected) { + PurplePounce *pounce; + PidginNotifyPounceData *pounce_data; + + list = purple_pounces_get_all(); + + gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter, + PIDGIN_POUNCE_DATA, &pounce_data, + -1); + + gtk_widget_set_sensitive(pounce_dialog->edit_button, FALSE); + + for (; list != NULL; list = list->next) { + pounce = list->data; + if (pounce == pounce_data->pounce) { + gtk_widget_set_sensitive(pounce_dialog->edit_button, TRUE); + break; + } + } + + gtk_widget_set_sensitive(pounce_dialog->dismiss_button, TRUE); + } else { + gtk_widget_set_sensitive(pounce_dialog->edit_button, FALSE); + gtk_widget_set_sensitive(pounce_dialog->dismiss_button, FALSE); + } + + +} + +static void email_response_cb(GtkDialog *dlg, gint id, PidginMailDialog *dialog) { PidginNotifyMailData *data = NULL; @@ -342,89 +527,7 @@ static GtkWidget * pidgin_get_mail_dialog(void) { - if (mail_dialog == NULL) { - GtkWidget *dialog = NULL; - GtkWidget *label; - GtkWidget *sw; - GtkCellRenderer *rend; - GtkTreeViewColumn *column; - GtkWidget *button = NULL; - GtkWidget *vbox = NULL; - - dialog = gtk_dialog_new_with_buttons(_("New Mail"), NULL, 0, - GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, - NULL); - gtk_window_set_role(GTK_WINDOW(dialog), "new_mail_detailed"); - g_signal_connect(G_OBJECT(dialog), "focus-in-event", - G_CALLBACK(mail_window_focus_cb), NULL); - - gtk_dialog_add_button(GTK_DIALOG(dialog), - _("Open All Messages"), GTK_RESPONSE_ACCEPT); - - button = gtk_dialog_add_button(GTK_DIALOG(dialog), - PIDGIN_STOCK_OPEN_MAIL, GTK_RESPONSE_YES); - - /* make "Open All Messages" the default response */ - gtk_dialog_set_default_response(GTK_DIALOG(dialog), - GTK_RESPONSE_ACCEPT); - - /* Setup the dialog */ - gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE); - gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE); - gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); - gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER); - - /* Vertical box */ - vbox = GTK_DIALOG(dialog)->vbox; - - /* Golden ratio it up! */ - gtk_widget_set_size_request(dialog, 550, 400); - - sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); - - mail_dialog = g_new0(PidginMailDialog, 1); - mail_dialog->dialog = dialog; - mail_dialog->open_button = button; - - mail_dialog->treemodel = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL, - GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); - mail_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(mail_dialog->treemodel)); - g_object_unref(G_OBJECT(mail_dialog->treemodel)); - gtk_tree_view_set_search_column(GTK_TREE_VIEW(mail_dialog->treeview), PIDGIN_MAIL_TEXT); - gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(mail_dialog->treeview), - pidgin_tree_view_search_equal_func, NULL, NULL); - - g_signal_connect(G_OBJECT(dialog), "response", - G_CALLBACK(email_response_cb), mail_dialog); - g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(mail_dialog->treeview))), - "changed", G_CALLBACK(selection_changed_cb), mail_dialog); - g_signal_connect(G_OBJECT(mail_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL); - - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(mail_dialog->treeview), FALSE); - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(mail_dialog->treeview), TRUE); - gtk_container_add(GTK_CONTAINER(sw), mail_dialog->treeview); - - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_resizable(column, TRUE); - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, rend, FALSE); - gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_MAIL_ICON, NULL); - rend = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, rend, TRUE); - gtk_tree_view_column_set_attributes(column, rend, "markup", PIDGIN_MAIL_TEXT, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(mail_dialog->treeview), column); - - label = gtk_label_new(NULL); - gtk_label_set_markup(GTK_LABEL(label), _("You have mail!")); - gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); - gtk_misc_set_alignment(GTK_MISC(label), 0, 0); - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); - } - - return mail_dialog->dialog; + return pidgin_get_notification_dialog(PIDGIN_NOTIFY_MAIL); } /* count == 0 means this is a detailed mail notification. @@ -1001,8 +1104,10 @@ { PidginNotifyMailData *data = (PidginNotifyMailData *)ui_handle; - g_free(data->url); - g_free(data); + if (data) { + g_free(data->url); + g_free(data); + } } else if (type == PURPLE_NOTIFY_SEARCHRESULTS) { @@ -1234,6 +1339,228 @@ return NULL; } +static GtkWidget * +pidgin_get_dialog(PidginNotifyType type, GtkTreeStore *treemodel) +{ + GtkWidget *dialog = NULL; + GtkWidget *label = NULL; + GtkWidget *sw; + GtkCellRenderer *rend; + GtkTreeViewColumn *column; + GtkWidget *button = NULL; + GtkWidget *vbox = NULL; + GtkTreeSelection *sel; + PidginNotifyDialog *spec_dialog = NULL; + + g_return_val_if_fail(type < PIDGIN_NOTIFY_TYPES, NULL); + + dialog = gtk_dialog_new_with_buttons(NULL, NULL, 0, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + /* Setup the dialog */ + gtk_container_set_border_width(GTK_CONTAINER(dialog), PIDGIN_HIG_BOX_SPACE); + gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BOX_SPACE); + gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), PIDGIN_HIG_BORDER); + + /* Vertical box */ + vbox = GTK_DIALOG(dialog)->vbox; + + /* Golden ratio it up! */ + gtk_widget_set_size_request(dialog, 550, 400); + + sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + + spec_dialog = g_new0(PidginNotifyDialog, 1); + spec_dialog->dialog = dialog; + spec_dialog->open_button = button; + + spec_dialog->treemodel = treemodel; + spec_dialog->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(spec_dialog->treemodel)); + g_object_unref(G_OBJECT(spec_dialog->treemodel)); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(spec_dialog->treeview), TRUE); + gtk_container_add(GTK_CONTAINER(sw), spec_dialog->treeview); + + if (type == PIDGIN_NOTIFY_MAIL) { + gtk_window_set_title(GTK_WINDOW(dialog), _("New Mail")); + gtk_window_set_role(GTK_WINDOW(dialog), "new_mail_detailed"); + g_signal_connect(G_OBJECT(dialog), "focus-in-event", + G_CALLBACK(mail_window_focus_cb), NULL); + + gtk_dialog_add_button(GTK_DIALOG(dialog), + _("Open All Messages"), GTK_RESPONSE_ACCEPT); + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), + PIDGIN_STOCK_OPEN_MAIL, GTK_RESPONSE_YES); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(spec_dialog->treeview), FALSE); + + gtk_tree_view_set_search_column(GTK_TREE_VIEW(spec_dialog->treeview), PIDGIN_MAIL_TEXT); + gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(spec_dialog->treeview), + pidgin_tree_view_search_equal_func, NULL, NULL); + + g_signal_connect(G_OBJECT(dialog), "response", + G_CALLBACK(email_response_cb), spec_dialog); + g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview))), + "changed", G_CALLBACK(selection_changed_cb), spec_dialog); + g_signal_connect(G_OBJECT(spec_dialog->treeview), "row-activated", G_CALLBACK(email_row_activated_cb), NULL); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(column, TRUE); + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_MAIL_ICON, NULL); + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, TRUE); + gtk_tree_view_column_set_attributes(column, rend, "markup", PIDGIN_MAIL_TEXT, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column); + + label = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(label), _("You have mail!")); + + } else if (type == PIDGIN_NOTIFY_POUNCE) { + gtk_window_set_title(GTK_WINDOW(dialog), _("New Pounces")); + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), + _("Dismiss"), GTK_RESPONSE_NO); + gtk_widget_set_sensitive(button, FALSE); + spec_dialog->dismiss_button = button; + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), + PIDGIN_STOCK_EDIT, GTK_RESPONSE_APPLY); + gtk_widget_set_sensitive(button, FALSE); + spec_dialog->edit_button = button; + + g_signal_connect(G_OBJECT(dialog), "response", + G_CALLBACK(pounce_response_cb), spec_dialog); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Buddy")); + gtk_tree_view_column_set_resizable(column, TRUE); + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + + gtk_tree_view_column_set_attributes(column, rend, "pixbuf", PIDGIN_POUNCE_ICON, NULL); + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_ALIAS); + gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Event")); + gtk_tree_view_column_set_resizable(column, TRUE); + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_EVENT); + gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Message")); + gtk_tree_view_column_set_resizable(column, TRUE); + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_TEXT); + gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Date")); + gtk_tree_view_column_set_resizable(column, TRUE); + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, rend, FALSE); + gtk_tree_view_column_add_attribute(column, rend, "text", PIDGIN_POUNCE_DATE); + gtk_tree_view_append_column(GTK_TREE_VIEW(spec_dialog->treeview), column); + + label = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(label), _("You have pounced!")); + + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(spec_dialog->treeview)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); + g_signal_connect(G_OBJECT(sel), "changed", + G_CALLBACK(pounce_row_selected_cb), NULL); + } + + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 2); + + if (type == PIDGIN_NOTIFY_MAIL) + mail_dialog = spec_dialog; + else if (type == PIDGIN_NOTIFY_POUNCE) { + pounce_dialog = spec_dialog; + } + + return spec_dialog->dialog; + +} + +void +pidgin_notify_pounce_add(PurpleAccount *account, PurplePounce *pounce, + const char *alias, const char *event, const char *message, const char *date) +{ + GtkWidget *dialog; + GdkPixbuf *icon; + GtkTreeIter iter; + PidginNotifyPounceData *pounce_data; + + dialog = pidgin_get_notification_dialog(PIDGIN_NOTIFY_POUNCE); + + icon = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL); + + pounce_data = g_new(PidginNotifyPounceData, 1); + + pounce_data->account = account; + pounce_data->pounce = pounce; + + gtk_tree_store_append(pounce_dialog->treemodel, &iter, NULL); + + gtk_tree_store_set(pounce_dialog->treemodel, &iter, + PIDGIN_POUNCE_ICON, icon, + PIDGIN_POUNCE_ALIAS, alias, + PIDGIN_POUNCE_EVENT, event, + PIDGIN_POUNCE_TEXT, (message != NULL)? message : _("No message"), + PIDGIN_POUNCE_DATE, date, + PIDGIN_POUNCE_DATA, pounce_data, + -1); + + if (icon) + g_object_unref(icon); + + gtk_widget_show_all(dialog); + + return; +} + +static GtkWidget * +pidgin_get_notification_dialog(PidginNotifyType type) +{ + GtkTreeStore *model = NULL; + + if (type == PIDGIN_NOTIFY_MAIL) { + if (mail_dialog != NULL) + return mail_dialog->dialog; + + model = gtk_tree_store_new(COLUMNS_PIDGIN_MAIL, + GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); + + } else if (type == PIDGIN_NOTIFY_POUNCE) { + + if (pounce_dialog != NULL) + return pounce_dialog->dialog; + + model = gtk_tree_store_new(PIDGIN_POUNCE_COLUMNS, + GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_POINTER); + } + + return pidgin_get_dialog(type, model); +} + static PurpleNotifyUiOps ops = { pidgin_notify_message, diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtknotify.h --- a/pidgin/gtknotify.h Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtknotify.h Sat Mar 21 03:49:03 2009 +0000 @@ -27,6 +27,18 @@ #define _PIDGINNOTIFY_H_ #include "notify.h" +#include "pounce.h" + +/** + * Adds a buddy pounce to the buddy pounce dialog + * + * @param alias The buddy alias + * @param event Event description + * @param message Pounce message + * @param date Pounce date + */ +void pidgin_notify_pounce_add(PurpleAccount *account, PurplePounce *pounce, + const char *alias, const char *event, const char *message, const char *date); /** * Returns the UI operations structure for GTK+ notification functions. diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtkpounce.c --- a/pidgin/gtkpounce.c Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtkpounce.c Sat Mar 21 03:49:03 2009 +0000 @@ -30,7 +30,6 @@ #include "account.h" #include "conversation.h" #include "debug.h" -#include "notify.h" #include "prpl.h" #include "request.h" #include "server.h" @@ -41,6 +40,7 @@ #include "gtkdialogs.h" #include "gtkimhtml.h" #include "gtkpounce.h" +#include "gtknotify.h" #include "pidginstock.h" #include "gtkutils.h" @@ -1275,7 +1275,6 @@ /* Handle double-clicking */ g_signal_connect(G_OBJECT(treeview), "button_press_event", G_CALLBACK(pounce_double_click_cb), dialog); - gtk_container_add(GTK_CONTAINER(sw), treeview); gtk_widget_show(treeview); @@ -1458,27 +1457,27 @@ */ tmp = g_strdup_printf( (events & PURPLE_POUNCE_TYPING) ? - _("%s has started typing to you (%s)") : + _("Started typing") : (events & PURPLE_POUNCE_TYPED) ? - _("%s has paused while typing to you (%s)") : + _("Paused while typing") : (events & PURPLE_POUNCE_SIGNON) ? - _("%s has signed on (%s)") : + _("Signed on") : (events & PURPLE_POUNCE_IDLE_RETURN) ? - _("%s has returned from being idle (%s)") : + _("Returned from being idle") : (events & PURPLE_POUNCE_AWAY_RETURN) ? - _("%s has returned from being away (%s)") : + _("Returned from being away") : (events & PURPLE_POUNCE_TYPING_STOPPED) ? - _("%s has stopped typing to you (%s)") : + _("Stopped typing") : (events & PURPLE_POUNCE_SIGNOFF) ? - _("%s has signed off (%s)") : + _("Signed off") : (events & PURPLE_POUNCE_IDLE) ? - _("%s has become idle (%s)") : + _("Became idle") : (events & PURPLE_POUNCE_AWAY) ? - _("%s has gone away. (%s)") : + _("Went away") : (events & PURPLE_POUNCE_MESSAGE_RECEIVED) ? - _("%s has sent you a message. (%s)") : - _("Unknown pounce event. Please report this!"), - alias, purple_account_get_protocol_name(account)); + _("Sent a message") : + _("Unknown.... Please report this!") + ); /* * Ok here is where I change the second argument, title, from @@ -1488,16 +1487,9 @@ if ((name_shown = purple_account_get_alias(account)) == NULL) name_shown = purple_account_get_username(account); - if (reason == NULL) - { - purple_notify_info(NULL, name_shown, tmp, purple_date_format_full(NULL)); - } - else - { - char *tmp2 = g_strdup_printf("%s\n\n%s", reason, purple_date_format_full(NULL)); - purple_notify_info(NULL, name_shown, tmp, tmp2); - g_free(tmp2); - } + pidgin_notify_pounce_add(account, pounce, alias, tmp, reason, + purple_date_format_full(NULL)); + g_free(tmp); } diff -r c605e5f2fc99 -r e4a060a1e3de pidgin/gtkprefs.c --- a/pidgin/gtkprefs.c Sat Mar 21 03:03:18 2009 +0000 +++ b/pidgin/gtkprefs.c Sat Mar 21 03:49:03 2009 +0000 @@ -2385,14 +2385,12 @@ /* Auto-away stuff */ vbox = pidgin_make_frame(ret, _("Auto-away")); - button = pidgin_prefs_checkbox(_("Change status when _idle"), - "/purple/away/away_when_idle", vbox); - select = pidgin_prefs_labeled_spin_button(vbox, _("_Minutes before becoming idle:"), "/purple/away/mins_before_away", 1, 24 * 60, sg); - g_signal_connect(G_OBJECT(button), "clicked", - G_CALLBACK(pidgin_toggle_sensitive), select); + + button = pidgin_prefs_checkbox(_("Change status when _idle"), + "/purple/away/away_when_idle", vbox); /* TODO: Show something useful if we don't have any saved statuses. */ menu = pidgin_status_menu(purple_savedstatus_get_idleaway(), G_CALLBACK(set_idle_away)); @@ -2404,7 +2402,6 @@ if (!purple_prefs_get_bool("/purple/away/away_when_idle")) { gtk_widget_set_sensitive(GTK_WIDGET(menu), FALSE); - gtk_widget_set_sensitive(GTK_WIDGET(select), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE); }