# HG changeset patch # User Yoshiki Yazawa # Date 1236544689 0 # Node ID f44832c6a65ba1b2f79321871907ce5d24c783e3 # Parent 4033fea1709cef430901fe7d4c8c0bdf5a28f6b1# Parent 7114d475a9e75757b49a8091991934dbeeb4d6ff propagate from branch 'im.pidgin.pidgin' (head a840b7a77c7e395db5e7877fab90763d91f70b3a) to branch 'im.pidgin.pidgin.yaz' (head 42d24988b115ba197f27a95547b1445c10f06d2c) diff -r 4033fea1709c -r f44832c6a65b COPYRIGHT --- a/COPYRIGHT Tue Mar 03 08:03:11 2009 +0000 +++ b/COPYRIGHT Sun Mar 08 20:38:09 2009 +0000 @@ -57,6 +57,7 @@ Philip Brown Dan Bruce Norbert Buchmuller +Johannes Buchner Sean Burke Thomas Butter Trevor Caira @@ -261,6 +262,7 @@ Lucio Maciel Brian Macke Paolo Maggi +Sulabh Mahajan Willian T. Mahan Kris Marsh Fidel Martinez diff -r 4033fea1709c -r f44832c6a65b ChangeLog --- a/ChangeLog Tue Mar 03 08:03:11 2009 +0000 +++ b/ChangeLog Sun Mar 08 20:38:09 2009 +0000 @@ -18,9 +18,6 @@ * 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. - * Authentication failures now clear the account password, causing Pidgin - to prompt for the password when clicking the "Re-enable" button on the - minidialog. version 2.5.5 (03/01/2009): libpurple: diff -r 4033fea1709c -r f44832c6a65b doc/funniest_home_convos.txt --- a/doc/funniest_home_convos.txt Tue Mar 03 08:03:11 2009 +0000 +++ b/doc/funniest_home_convos.txt Sun Mar 08 20:38:09 2009 +0000 @@ -565,3 +565,10 @@ for using pidgen 22:36 why do they think this is a bad client? does it have history? +-- + +15:45 We've had a Grand Plugin Database Plan for approximately forever. +15:45 ah, the GPDP +15:46 well, there was a Grand Smiley Theme Database +15:47 the GSTD sounds like a bad acronym +15:47 I realized after typing that diff -r 4033fea1709c -r f44832c6a65b libpurple/ft.c --- a/libpurple/ft.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/ft.c Sun Mar 08 20:38:09 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 4033fea1709c -r f44832c6a65b libpurple/plugins/Makefile.am --- a/libpurple/plugins/Makefile.am Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/plugins/Makefile.am Sun Mar 08 20:38:09 2009 +0000 @@ -36,6 +36,7 @@ newline_la_LDFLAGS = -module -avoid-version notify_example_la_LDFLAGS = -module -avoid-version offlinemsg_la_LDFLAGS = -module -avoid-version +one_time_password_la_LDFLAGS = -module -avoid-version pluginpref_example_la_LDFLAGS = -module -avoid-version psychic_la_LDFLAGS = -module -avoid-version signals_test_la_LDFLAGS = -module -avoid-version @@ -65,6 +66,7 @@ debug_example.la \ helloworld.la \ notify_example.la \ + one_time_password.la \ pluginpref_example.la \ signals_test.la \ simple.la @@ -81,6 +83,7 @@ newline_la_SOURCES = newline.c notify_example_la_SOURCES = notify_example.c offlinemsg_la_SOURCES = offlinemsg.c +one_time_password_la_SOURCES = one_time_password.c pluginpref_example_la_SOURCES = pluginpref_example.c psychic_la_SOURCES = psychic.c signals_test_la_SOURCES = signals-test.c @@ -97,6 +100,7 @@ newline_la_LIBADD = $(GLIB_LIBS) notify_example_la_LIBADD = $(GLIB_LIBS) offlinemsg_la_LIBADD = $(GLIB_LIBS) +one_time_password_la_LIBADD = $(GLIB_LIBS) pluginpref_example_la_LIBADD = $(GLIB_LIBS) psychic_la_LIBADD = $(GLIB_LIBS) signals_test_la_LIBADD = $(GLIB_LIBS) diff -r 4033fea1709c -r f44832c6a65b libpurple/plugins/one_time_password.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/one_time_password.c Sun Mar 08 20:38:09 2009 +0000 @@ -0,0 +1,151 @@ +/* + * One Time Password support plugin for libpurple + * + * Copyright (C) 2009, Daniel Atallah + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02111-1301, USA. + */ +#include "internal.h" +#include "debug.h" +#include "plugin.h" +#include "version.h" +#include "account.h" +#include "accountopt.h" + +#define PLUGIN_ID "core-one_time_password" +#define PREF_NAME PLUGIN_ID "_enabled" + +static void +signed_on_cb(PurpleConnection *conn, void *data) +{ + PurpleAccount *account = purple_connection_get_account(conn); + + if (purple_account_get_bool(account, PREF_NAME, FALSE)) { + if(purple_account_get_remember_password(account)) + purple_debug_error("One Time Password", + "Unable to enforce one time password for account %s (%s).\n" + "Account is set to remember the password.\n", + purple_account_get_username(account), + purple_account_get_protocol_name(account)); + else { + + purple_debug_info("One Time Password", "Clearing password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_name(account)); + + purple_account_set_password(account, NULL); + /* TODO: Do we need to somehow clear conn->password ? */ + } + } +} + +static gboolean +plugin_load(PurplePlugin *plugin) +{ + PurplePlugin *prpl; + PurplePluginProtocolInfo *prpl_info; + PurpleAccountOption *option; + GList *l; + + /* Register protocol preference. */ + for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) { + prpl = (PurplePlugin *)l->data; + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) { + option = purple_account_option_bool_new(_("One Time Password"), + PREF_NAME, FALSE); + prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); + } + } + + /* Register callback. */ + purple_signal_connect(purple_connections_get_handle(), "signed-on", + plugin, PURPLE_CALLBACK(signed_on_cb), NULL); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin) +{ + PurplePlugin *prpl; + PurplePluginProtocolInfo *prpl_info; + PurpleAccountOption *option; + GList *l, *options; + + /* Remove protocol preference. */ + for (l = purple_plugins_get_protocols(); l != NULL; l = l->next) { + prpl = (PurplePlugin *)l->data; + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + if (prpl_info != NULL && !(prpl_info->options & OPT_PROTO_NO_PASSWORD)) { + options = prpl_info->protocol_options; + while (options != NULL) { + option = (PurpleAccountOption *) options->data; + if (strcmp(PREF_NAME, purple_account_option_get_setting(option)) == 0) { + prpl_info->protocol_options = g_list_delete_link(prpl_info->protocol_options, options); + purple_account_option_destroy(option); + break; + } + options = options->next; + } + } + } + + /* Callback will be automagically unregistered */ + + return TRUE; +} + +static PurplePluginInfo info = +{ + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_STANDARD, /**< type */ + NULL, /**< ui_requirement */ + 0, /**< flags */ + NULL, /**< dependencies */ + PURPLE_PRIORITY_DEFAULT, /**< priority */ + PLUGIN_ID, /**< id */ + N_("One Time Password Support"), /**< name */ + DISPLAY_VERSION, /**< version */ + /** summary */ + N_("Enforce that passwords are used only once."), + /** description */ + N_("Allows you to enforce on a per-account basis that passwords not " + "being saved are only used in a single successful connection.\n" + "Note: The account password must not be saved for this to work."), + "Daniel Atallah ", /**< author */ + PURPLE_WEBSITE, /**< homepage */ + plugin_load, /**< load */ + plugin_unload, /**< unload */ + NULL, /**< destroy */ + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + NULL, /**< actions */ + NULL, /**< reserved 1 */ + NULL, /**< reserved 2 */ + NULL, /**< reserved 3 */ + NULL /**< reserved 4 */ +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(one_time_password, init_plugin, info) diff -r 4033fea1709c -r f44832c6a65b libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sun Mar 08 20:38:09 2009 +0000 @@ -2524,7 +2524,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 4033fea1709c -r f44832c6a65b libpurple/protocols/oscar/bstream.c --- a/libpurple/protocols/oscar/bstream.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/oscar/bstream.c Sun Mar 08 20:38:09 2009 +0000 @@ -311,3 +311,37 @@ return byte_stream_putle32(bs, atoi(purple_account_get_username(account))); } + +void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data) +{ + byte_stream_put16(bs, type); + + if (data != NULL && data->len > 0) { + /* Flags. 0x04 means "this asset has data attached to it" */ + byte_stream_put8(bs, 0x04); /* Flags */ + byte_stream_put8(bs, data->len); /* Length */ + byte_stream_rewind(data); + byte_stream_putbs(bs, data, data->len); /* Data */ + } else { + byte_stream_put8(bs, 0x00); /* No flags */ + byte_stream_put8(bs, 0x00); /* Length */ + /* No data */ + } +} + +void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr) +{ + ByteStream data; + size_t len = datastr != NULL ? strlen(datastr) : 0; + + if (len > 0) { + byte_stream_new(&data, 2 + len + 2); + byte_stream_put16(&data, len); /* Length */ + byte_stream_putstr(&data, datastr); /* String */ + byte_stream_put16(&data, 0x0000); /* Unknown */ + byte_stream_put_bart_asset(bs, type, &data); + byte_stream_destroy(&data); + } else { + byte_stream_put_bart_asset(bs, type, NULL); + } +} diff -r 4033fea1709c -r f44832c6a65b libpurple/protocols/oscar/family_oservice.c --- a/libpurple/protocols/oscar/family_oservice.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/oscar/family_oservice.c Sun Mar 08 20:38:09 2009 +0000 @@ -825,7 +825,7 @@ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, - gboolean setavailmsg, const char *availmsg, const char *itmsurl) + gboolean setstatusmsg, const char *statusmsg, const char *itmsurl) { FlapConnection *conn; ByteStream bs; @@ -851,30 +851,17 @@ } #endif - if (setavailmsg) + if (setstatusmsg) { - int availmsglen, itmsurllen; + size_t statusmsglen, itmsurllen; ByteStream tmpbs; - availmsglen = (availmsg != NULL) ? strlen(availmsg) : 0; + statusmsglen = (statusmsg != NULL) ? strlen(statusmsg) : 0; itmsurllen = (itmsurl != NULL) ? strlen(itmsurl) : 0; - byte_stream_new(&tmpbs, availmsglen + 8 + itmsurllen + 8); - byte_stream_put16(&tmpbs, 0x0002); - byte_stream_put8(&tmpbs, 0x04); /* Flags */ - byte_stream_put8(&tmpbs, availmsglen + 4); - byte_stream_put16(&tmpbs, availmsglen); - if (availmsglen > 0) - byte_stream_putstr(&tmpbs, availmsg); - byte_stream_put16(&tmpbs, 0x0000); - - byte_stream_put16(&tmpbs, 0x0009); - byte_stream_put8(&tmpbs, 0x04); /* Flags */ - byte_stream_put8(&tmpbs, itmsurllen + 4); - byte_stream_put16(&tmpbs, itmsurllen); - if (itmsurllen > 0) - byte_stream_putstr(&tmpbs, itmsurl); - byte_stream_put16(&tmpbs, 0x0000); + byte_stream_new(&tmpbs, statusmsglen + 8 + itmsurllen + 8); + byte_stream_put_bart_asset_str(&tmpbs, 0x0002, statusmsg); + byte_stream_put_bart_asset_str(&tmpbs, 0x0009, itmsurl); aim_tlvlist_add_raw(&tlvlist, 0x001d, byte_stream_curpos(&tmpbs), tmpbs.data); diff -r 4033fea1709c -r f44832c6a65b libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.c Sun Mar 08 20:38:09 2009 +0000 @@ -4767,16 +4767,17 @@ PurpleStatusType *status_type; PurpleStatusPrimitive primitive; - char *htmlinfo; char *info_encoding = NULL; char *info = NULL; gsize infolen = 0; - const char *htmlaway; char *away_encoding = NULL; char *away = NULL; gsize awaylen = 0; + char *status_text = NULL; + const char *itmsurl = NULL; + status_type = purple_status_get_type(status); primitive = purple_status_type_get_primitive(status_type); @@ -4794,7 +4795,7 @@ } else if (rawinfo != NULL) { - htmlinfo = purple_strdup_withhtml(rawinfo); + char *htmlinfo = purple_strdup_withhtml(rawinfo); info = purple_prpl_oscar_convert_to_infotext(htmlinfo, &infolen, &info_encoding); g_free(htmlinfo); @@ -4811,16 +4812,54 @@ } } - if (!setstatus) + if (setstatus) { - /* Do nothing! */ - } - else if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE) - { - const char *status_html, *itmsurl; - char *status_text = NULL; + const char *status_html; status_html = purple_status_get_attr_string(status, "message"); + + if (primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE) + { + /* This is needed for us to un-set any previous away message. */ + away = g_strdup(""); + } + else + { + gchar *linkified; + + /* We do this for icq too so that they work for old third party clients */ + linkified = purple_markup_linkify(status_html); + away = purple_prpl_oscar_convert_to_infotext(linkified, &awaylen, &away_encoding); + g_free(linkified); + + if (awaylen > od->rights.maxawaymsglen) + { + gchar *errstr; + + errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte " + "has been exceeded. It has been truncated for you.", + "The maximum away message length of %d bytes " + "has been exceeded. It has been truncated for you.", + od->rights.maxawaymsglen), od->rights.maxawaymsglen); + purple_notify_warning(gc, NULL, _("Away message too long."), errstr); + g_free(errstr); + } + } + } + + aim_locate_setprofile(od, + info_encoding, info, MIN(infolen, od->rights.maxsiglen), + away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen)); + g_free(info); + g_free(away); + + if (setstatus) + { + const char *status_html; + + status_html = purple_status_get_attr_string(status, "message"); + if (od->icq && (status_html == NULL || status_html[0] == '\0')) + status_html = purple_status_type_get_name(status_type); if (status_html != NULL) { status_text = purple_markup_strip_html(status_html); @@ -4831,82 +4870,28 @@ strcpy(tmp, "..."); } } + itmsurl = purple_status_get_attr_string(status, "itmsurl"); + /* TODO: Combine these two calls! */ aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, itmsurl); - g_free(status_text); - - /* This is needed for us to un-set any previous away message. */ - away = g_strdup(""); - } - else - { - char *status_text = NULL; - - htmlaway = purple_status_get_attr_string(status, "message"); - if ((htmlaway == NULL) || (*htmlaway == '\0')) - htmlaway = purple_status_type_get_name(status_type); - - /* ICQ 6.x seems to use an available message for all statuses so set one */ - if (od->icq) - { - status_text = purple_markup_strip_html(htmlaway); - /* If the status_text is longer than 251 characters then truncate it */ - if (strlen(status_text) > MAXAVAILMSGLEN) - { - char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]); - strcpy(tmp, "..."); - } - aim_srv_setextrainfo(od, FALSE, 0, TRUE, status_text, NULL); - } - g_free(status_text); - - /* Set a proper away message for icq too so that they work for old third party clients */ - - away = purple_prpl_oscar_convert_to_infotext(htmlaway, &awaylen, &away_encoding); - - if (awaylen > od->rights.maxawaymsglen) - { - gchar *errstr; - - errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte " - "has been exceeded. It has been truncated for you.", - "The maximum away message length of %d bytes " - "has been exceeded. It has been truncated for you.", - od->rights.maxawaymsglen), od->rights.maxawaymsglen); - purple_notify_warning(gc, NULL, _("Away message too long."), errstr); - g_free(errstr); - } - } - - if (setstatus) oscar_set_extendedstatus(gc); - - aim_locate_setprofile(od, info_encoding, info, MIN(infolen, od->rights.maxsiglen), - away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen)); - g_free(info); - g_free(away); + } } static void -oscar_set_status_icq(PurpleAccount *account, PurpleStatus *status) +oscar_set_status_icq(PurpleAccount *account) { PurpleConnection *gc = purple_account_get_connection(account); - OscarData *od = NULL; - - if (gc) - od = purple_connection_get_protocol_data(gc); - if (!od) - return; - - if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_INVISIBLE) - account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS; - else - account->perm_deny = PURPLE_PRIVACY_DENY_USERS; - - if ((od->ssi.received_data) && (aim_ssi_getpermdeny(od->ssi.local) != account->perm_deny)) - aim_ssi_setpermdeny(od, account->perm_deny, 0xffffffff); - + + /* Our permit/deny setting affects our invisibility */ + oscar_set_permit_deny(gc); + + /* + * TODO: I guess we should probably wait and do this after we get + * confirmation from the above SSI call? Right now other people + * see our status blip to "invisible" before we appear offline. + */ oscar_set_extendedstatus(gc); } @@ -4926,7 +4911,7 @@ /* Set the ICQ status for ICQ accounts only */ if (oscar_util_valid_name_icq(purple_account_get_username(account))) - oscar_set_status_icq(account, status); + oscar_set_status_icq(account); } #ifdef CRAZY_WARN @@ -5082,7 +5067,7 @@ return 1; } - oscar_set_extendedstatus(gc); + oscar_set_status_icq(purple_connection_get_account(gc)); return 1; } @@ -5374,15 +5359,19 @@ } break; case 0x0004: { /* Permit/deny setting */ - if (curitem->data) { - guint8 permdeny; - if ((permdeny = aim_ssi_getpermdeny(od->ssi.local)) && (permdeny != account->perm_deny)) { + /* + * We don't inherit the permit/deny setting from the server + * for ICQ because, for ICQ, this setting controls who can + * see your online status when you are invisible. Thus it is + * a part of your status and not really related to blocking. + */ + if (!od->icq && curitem->data) { + guint8 perm_deny = aim_ssi_getpermdeny(od->ssi.local); + if (perm_deny != 0 && perm_deny != account->perm_deny) + { purple_debug_info("oscar", - "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, permdeny); - account->perm_deny = permdeny; - if (od->icq && account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) { - purple_presence_set_status_active(purple_account_get_presence(account), OSCAR_STATUS_ID_INVISIBLE, TRUE); - } + "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, perm_deny); + account->perm_deny = perm_deny; } } } break; @@ -5393,7 +5382,7 @@ } /* End of switch on curitem->type */ } /* End of for loop */ - oscar_set_extendedstatus(gc); + oscar_set_status_icq(account); /* Activate SSI */ /* Sending the enable causes other people to be able to see you, and you to see them */ @@ -6074,29 +6063,36 @@ void oscar_set_permit_deny(PurpleConnection *gc) { PurpleAccount *account = purple_connection_get_account(gc); OscarData *od = purple_connection_get_protocol_data(gc); - - if (od->ssi.received_data) { - switch (account->perm_deny) { - case PURPLE_PRIVACY_ALLOW_ALL: - aim_ssi_setpermdeny(od, 0x01, 0xffffffff); - break; - case PURPLE_PRIVACY_ALLOW_BUDDYLIST: - aim_ssi_setpermdeny(od, 0x05, 0xffffffff); - break; - case PURPLE_PRIVACY_ALLOW_USERS: - aim_ssi_setpermdeny(od, 0x03, 0xffffffff); - break; - case PURPLE_PRIVACY_DENY_ALL: - aim_ssi_setpermdeny(od, 0x02, 0xffffffff); - break; - case PURPLE_PRIVACY_DENY_USERS: - aim_ssi_setpermdeny(od, 0x04, 0xffffffff); - break; - default: - aim_ssi_setpermdeny(od, 0x01, 0xffffffff); - break; - } - } + PurplePrivacyType perm_deny; + + /* + * For ICQ the permit/deny setting controls who you can see you + * online when you set your status to "invisible." If we're ICQ + * and we're invisible then we need to use one of + * PURPLE_PRIVACY_ALLOW_USERS or PURPLE_PRIVACY_ALLOW_BUDDYLIST or + * PURPLE_PRIVACY_DENY_USERS if we actually want to be invisible + * to anyone. + * + * These three permit/deny settings correspond to: + * 1. Invisible to everyone except the people on my "permit" list + * 2. Invisible to everyone except the people on my buddy list + * 3. Invisible only to the people on my "deny" list + * + * It would be nice to allow cases 2 and 3, but our UI doesn't have + * a nice way to do it. For now we just force case 1. + */ + if (od->icq && purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE)) + perm_deny = PURPLE_PRIVACY_ALLOW_USERS; + else + perm_deny = account->perm_deny; + + if (od->ssi.received_data) + /* + * Conveniently there is a one-to-one mapping between the + * values of libpurple's PurplePrivacyType and the values used + * by the oscar protocol. + */ + aim_ssi_setpermdeny(od, perm_deny, 0xffffffff); } void oscar_add_permit(PurpleConnection *gc, const char *who) { diff -r 4033fea1709c -r f44832c6a65b libpurple/protocols/oscar/oscar.h --- a/libpurple/protocols/oscar/oscar.h Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/oscar/oscar.h Sun Mar 08 20:38:09 2009 +0000 @@ -665,7 +665,7 @@ /* 0x0014 */ void aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32); /* 0x0016 */ void aim_srv_nop(OscarData *od, FlapConnection *conn); /* 0x0017 */ void aim_srv_setversions(OscarData *od, FlapConnection *conn); -/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setavailmsg, const char *availmsg, const char *itmsurl); +/* 0x001e */ int aim_srv_setextrainfo(OscarData *od, gboolean seticqstatus, guint32 icqstatus, gboolean setstatusmsg, const char *statusmsg, const char *itmsurl); void aim_bos_reqrights(OscarData *od, FlapConnection *conn); @@ -1595,6 +1595,18 @@ int byte_stream_putuid(ByteStream *bs, OscarData *od); int byte_stream_putcaps(ByteStream *bs, guint32 caps); +/** + * Inserts a BART asset block into the given byte stream. The flags + * and length are set appropriately based on the value of data. + */ +void byte_stream_put_bart_asset(ByteStream *bs, guint16 type, ByteStream *data); + +/** + * A helper function that calls byte_stream_put_bart_asset with the + * appropriate data ByteStream given the datastr. + */ +void byte_stream_put_bart_asset_str(ByteStream *bs, guint16 type, const char *datastr); + /* * Generic SNAC structure. Rarely if ever used. */ diff -r 4033fea1709c -r f44832c6a65b libpurple/protocols/yahoo/yahoo.c --- a/libpurple/protocols/yahoo/yahoo.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sun Mar 08 20:38:09 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; @@ -2199,11 +2385,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; @@ -2213,24 +2403,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; } @@ -2241,6 +2455,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) @@ -2250,6 +2889,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; @@ -2269,14 +2916,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 */ } @@ -2288,6 +2942,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) { @@ -2300,12 +2956,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); + } } } @@ -2385,12 +3063,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); @@ -2467,7 +3145,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; @@ -2501,6 +3180,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", @@ -3016,6 +3698,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? */ @@ -3023,6 +3706,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); @@ -3088,6 +3774,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); @@ -3636,10 +4333,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; @@ -3648,7 +4464,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) { @@ -3667,9 +4484,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"); @@ -3708,8 +4583,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; @@ -3724,12 +4609,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; } @@ -3975,6 +4883,7 @@ char *group2; YahooFriend *f; const char *bname; + gboolean msn = FALSE; if (!yd->logged_in) return; @@ -3984,6 +4893,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) @@ -3993,20 +4903,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); } @@ -4020,13 +4948,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))) { @@ -4042,8 +4975,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); } @@ -4116,13 +5056,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. @@ -4136,8 +5085,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); @@ -4349,10 +5303,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; } @@ -4366,7 +5320,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(); */ @@ -4436,7 +5390,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 4033fea1709c -r f44832c6a65b libpurple/protocols/yahoo/yahoo.h --- a/libpurple/protocols/yahoo/yahoo.h Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Sun Mar 08 20:38:09 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,3" - /* 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 4033fea1709c -r f44832c6a65b libpurple/protocols/yahoo/yahoo_filexfer.c --- a/libpurple/protocols/yahoo/yahoo_filexfer.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Sun Mar 08 20:38:09 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; @@ -605,6 +614,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; @@ -616,6 +645,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 ); @@ -689,7 +722,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); @@ -985,7 +1018,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; @@ -1035,34 +1068,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; - - /* recent yahoo japan --yaz */ - if(yd->jp && yf && yf->version_id > 500000) - 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) @@ -1071,7 +1094,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; @@ -1111,7 +1136,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) @@ -1160,7 +1185,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); @@ -1171,21 +1196,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; @@ -1212,31 +1249,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 { @@ -1257,6 +1324,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; @@ -1302,14 +1588,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 */ } } @@ -1337,6 +1623,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, @@ -1350,7 +1644,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. @@ -1366,7 +1660,7 @@ return; } } - /*end processing*/ + /* end processing */ if(!filename_list) return; @@ -1441,6 +1735,7 @@ GSList *l; struct yahoo_packet *pkt_to_send; PurpleAccount *account; + struct yahoo_p2p_data *p2p_data; yd = gc->proto_data; @@ -1464,13 +1759,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; @@ -1498,35 +1788,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; @@ -1537,6 +1840,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) { @@ -1551,19 +1856,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 4033fea1709c -r f44832c6a65b libpurple/protocols/yahoo/yahoo_friend.c --- a/libpurple/protocols/yahoo/yahoo_friend.c Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.c Sun Mar 08 20:38:09 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 4033fea1709c -r f44832c6a65b libpurple/protocols/yahoo/yahoo_friend.h --- a/libpurple/protocols/yahoo/yahoo_friend.h Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.h Sun Mar 08 20:38:09 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 4033fea1709c -r f44832c6a65b libpurple/protocols/yahoo/yahoo_packet.h --- a/libpurple/protocols/yahoo/yahoo_packet.h Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.h Sun Mar 08 20:38:09 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 4033fea1709c -r f44832c6a65b libpurple/purple-url-handler --- a/libpurple/purple-url-handler Tue Mar 03 08:03:11 2009 +0000 +++ b/libpurple/purple-url-handler Sun Mar 08 20:38:09 2009 +0000 @@ -207,11 +207,7 @@ def correct_server(account): username = cpurple.PurpleAccountGetUsername(account) - user_split = (username.split("@")) - # Not all accounts have a split, so append an empty string so the - # [1] doesn't throw an IndexError. - user_split.append("") - return (server == user_split[1]) + return ("@" in username) and (server == (username.split("@"))[1]) account = findaccount(protocol, matcher=correct_server) diff -r 4033fea1709c -r f44832c6a65b pidgin/gtkblist.c --- a/pidgin/gtkblist.c Tue Mar 03 08:03:11 2009 +0000 +++ b/pidgin/gtkblist.c Sun Mar 08 20:38:09 2009 +0000 @@ -4002,7 +4002,7 @@ } else if (!purple_presence_is_online(presence)) { if (theme) pair = pidgin_blist_theme_get_offline_text_info(theme); - name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black"; + name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL; name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; if (theme) @@ -4013,7 +4013,7 @@ } else if (purple_presence_is_available(presence)) { if (theme) pair = pidgin_blist_theme_get_online_text_info(theme); - name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black"; + name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL; name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; if (theme) @@ -4024,7 +4024,7 @@ } else { if (theme) pair = pidgin_blist_theme_get_away_text_info(theme); - name_color = (pair != NULL && pair->color != NULL) ? pair->color : "black"; + name_color = (pair != NULL && pair->color != NULL) ? pair->color : NULL; name_font = (pair != NULL && pair->font != NULL) ? pair->font : ""; if (theme) @@ -4034,23 +4034,49 @@ } if (aliased && selected) { - name_color = "black"; - status_color = "black"; + if (theme) { + name_color = "black"; + status_color = "black"; + } else { + name_color = NULL; + status_color = NULL; + } } /* Put it all together */ if (aliased && biglist && (statustext || idletime)) { /* using breaks the status, so it must be seperated into */ - text = g_strdup_printf("%s\n" - "%s%s%s", - name_font, name_color, nametext, status_font, status_color, - idletime != NULL ? idletime : "", - (idletime != NULL && statustext != NULL) ? " - " : "", - statustext != NULL ? statustext : ""); - - } else - text = g_strdup_printf("%s", name_font, name_color, nametext); - + if (name_color) { + text = g_strdup_printf("%s\n" + "%s%s%s", + name_font, name_color, nametext, status_font, status_color, + idletime != NULL ? idletime : "", + (idletime != NULL && statustext != NULL) ? " - " : "", + statustext != NULL ? statustext : ""); + } else if (status_color) { + text = g_strdup_printf("%s\n" + "%s%s%s", + name_font, nametext, status_font, status_color, + idletime != NULL ? idletime : "", + (idletime != NULL && statustext != NULL) ? " - " : "", + statustext != NULL ? statustext : ""); + } else { + text = g_strdup_printf("%s\n" + "%s%s%s", + name_font, nametext, status_font, + idletime != NULL ? idletime : "", + (idletime != NULL && statustext != NULL) ? " - " : "", + statustext != NULL ? statustext : ""); + } + } else { + if (name_color) { + text = g_strdup_printf("%s", + name_font, name_color, nametext); + } else { + text = g_strdup_printf("%s", name_font, + nametext); + } + } g_free(nametext); g_free(statustext); g_free(idletime); @@ -4818,8 +4844,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); } @@ -4852,25 +4879,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, @@ -6165,12 +6176,17 @@ pair = pidgin_blist_theme_get_collapsed_text_info(theme); - text_color = (selected || pair == NULL || pair->color == NULL) ? "black" : pair->color; + text_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color; text_font = (pair == NULL || pair->font == NULL) ? "" : pair->font; esc = g_markup_escape_text(group->name, -1); - mark = g_strdup_printf("%s%s", + if (text_color) { + mark = g_strdup_printf("%s%s", text_color, text_font, esc ? esc : "", group_count); + } else { + mark = g_strdup_printf("%s%s", + text_font, esc ? esc : "", group_count); + } g_free(esc); return mark; @@ -6231,10 +6247,17 @@ if (!selected && theme != NULL && (pair = pidgin_blist_theme_get_idle_text_info(theme)) != NULL && pair->color != NULL) textcolor = pair->color; else - textcolor = "black"; - - idle = g_strdup_printf("%d:%02d", textcolor, - (pair == NULL || pair->font == NULL) ? "" : pair->font, ihrs, imin); + textcolor = NULL; + + if (textcolor) { + idle = g_strdup_printf("%d:%02d", + textcolor, (pair == NULL || pair->font == NULL) ? "" : pair->font, + ihrs, imin); + } else { + idle = g_strdup_printf("%d:%02d", + (pair == NULL || pair->font == NULL) ? "" : pair->font, + ihrs, imin); + } } } @@ -6331,10 +6354,15 @@ } font = (pair == NULL || pair->font == NULL) ? "" : pair->font; - fg_color = (selected || pair == NULL || pair->color == NULL) ? "black" : pair->color; - - tmp = g_strdup_printf("%s", + fg_color = (selected || pair == NULL || pair->color == NULL) ? NULL : pair->color; + + if (fg_color) { + tmp = g_strdup_printf("%s", font, fg_color, mark); + } else { + tmp = g_strdup_printf("%s", font, + mark); + } g_free(mark); mark = tmp; @@ -6464,13 +6492,17 @@ font = (pair == NULL || pair->font == NULL) ? "" : pair->font; if (selected || pair == NULL || pair->color == NULL) /* nick_said color is the same as gtkconv:tab-label-attention */ - color = (nick_said ? "#006aff" : "black"); + color = (nick_said ? "#006aff" : NULL); else color = pair->color; - tmp = g_strdup_printf("%s", - font, color, hidden ? "bold" : "normal", mark); - + if (color) { + tmp = g_strdup_printf("%s", + font, color, hidden ? "bold" : "normal", mark); + } else { + tmp = g_strdup_printf("%s", + font, hidden ? "bold" : "normal", mark); + } g_free(mark); mark = tmp; diff -r 4033fea1709c -r f44832c6a65b pidgin/gtkconn.c --- a/pidgin/gtkconn.c Tue Mar 03 08:03:11 2009 +0000 +++ b/pidgin/gtkconn.c Sun Mar 08 20:38:09 2009 +0000 @@ -163,11 +163,6 @@ g_hash_table_remove(auto_reconns, account); purple_account_set_enabled(account, PIDGIN_UI, FALSE); - - /* clear the saved password if this is an authentication failure */ - if(reason == PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED) - purple_account_set_password(account, NULL); - } /* If we have any open chats, we probably want to rejoin when we get back online. */ diff -r 4033fea1709c -r f44832c6a65b po/ca.po --- a/po/ca.po Tue Mar 03 08:03:11 2009 +0000 +++ b/po/ca.po Sun Mar 08 20:38:09 2009 +0000 @@ -33,8 +33,8 @@ msgstr "" "Project-Id-Version: Pidgin\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-02-22 10:28+0100\n" -"PO-Revision-Date: 2009-02-22 15:18+0100\n" +"POT-Creation-Date: 2009-03-05 22:22+0100\n" +"PO-Revision-Date: 2009-03-05 22:37+0100\n" "Last-Translator: Josep Puigdemont i Casamajó \n" "Language-Team: Catalan \n" "MIME-Version: 1.0\n" @@ -4468,17 +4468,24 @@ msgstr "No s'ha pogut fer ping a l'usuari %s" #, c-format -msgid "Unable to buzz, because there is nothing known about user %s." +msgid "Unable to buzz, because there is nothing known about %s." msgstr "No s'ha pogut botzinar perquè no es coneix res de l'usuari %s." #, c-format -msgid "Unable to buzz, because user %s might be offline." +msgid "Unable to buzz, because %s might be offline." msgstr "" "No s'ha pogut botzinar possiblement perquè l'usuari %s està desconnectat." #, c-format -msgid "Unable to buzz, because the user %s does not support it." -msgstr "No s'ha pogut botzinar perquè l'usuari %s no ho permet." +msgid "" +"Unable to buzz, because %s does not support it or do not wish to receive " +"buzzes now." +msgstr "" +"No s'ha pogut botzinar perquè no és pot, o bé l'usuari %s no ho permet." + +#, c-format +msgid "Buzzing %s..." +msgstr "S'està botzinant a %s..." #. Yahoo only supports one attention command: the 'buzz'. #. This is index number YAHOO_BUZZ. @@ -4489,10 +4496,6 @@ msgid "%s has buzzed you!" msgstr "%s us ha botzinat!" -#, c-format -msgid "Buzzing %s..." -msgstr "S'està botzinant a %s..." - msgid "config: Configure a chat room." msgstr "config: configura la sala de xat." @@ -4646,6 +4649,19 @@ msgid "Error in chat %s" msgstr "S'ha produït un error en el xat %s" +#, fuzzy +msgid "An error occured on the in-band bytestream transfer\n" +msgstr "S'ha produït un error en obrir el fitxer." + +msgid "Transfer was closed." +msgstr "La transferència s'ha tancat." + +msgid "Failed to open the file" +msgstr "No s'ha pogut obrir el fitxer" + +msgid "Failed to open in-band bytestream" +msgstr "" + #, c-format msgid "Unable to send file to %s, user does not support file transfers" msgstr "" @@ -5794,26 +5810,25 @@ msgid "Zap" msgstr "" -#, fuzzy, c-format +#, c-format msgid "%s has zapped you!" -msgstr "%s us ha afegit [%s]" - -#, fuzzy, c-format +msgstr "" + +#, c-format msgid "Zapping %s..." -msgstr "S'està trucant a %s" +msgstr "" #. Whack means "to hit or strike someone with a sharp blow" -#, fuzzy msgid "Whack" msgstr "bufetejar" -#, fuzzy, c-format +#, c-format msgid "%s has whacked you!" -msgstr "%s us ha afegit [%s]" - -#, fuzzy, c-format +msgstr "%s us ha bufetejat [%s]" + +#, c-format msgid "Whacking %s..." -msgstr "Bufetejant" +msgstr "S'està bufetejant %s..." #. Torch means "to set on fire." Don't worry, this doesn't #. * make a whole lot of sense in English, either. Feel free @@ -5897,15 +5912,15 @@ #. * someone to perform a mischievous trick or practical joke. #, fuzzy msgid "Punk" -msgstr "Ping" +msgstr "Enredar" #, fuzzy, c-format msgid "%s has punk'd you!" -msgstr "%s s'ha connectat." +msgstr "%s us ha enredat!" #, fuzzy, c-format msgid "Punking %s..." -msgstr "Ping" +msgstr "S'està enredant %s..." #. Raspberry is a slang term for the vibrating sound made #. * when you stick your tongue out of your mouth with your @@ -6513,7 +6528,7 @@ "començar amb una lletra i contenir només lletres, nombres o espais, o només " "nombres." -#. Unregistered screen name +#. Unregistered username #. uid is not exist msgid "Invalid username." msgstr "El nom d'usuari no és vàlid" @@ -6530,7 +6545,7 @@ msgstr "" "El servei de missatges instantanis d'AOL no està disponible temporalment." -#. screen name connecting too frequently +#. username connecting too frequently #. IP address connecting too frequently msgid "" "You have been connecting and disconnecting too frequently. Wait ten minutes " @@ -6717,7 +6732,7 @@ msgstr[0] "Heu perdut %hu missatge de %s per motius desconeguts." msgstr[1] "Heu perdut %hu missatges de %s per motius desconeguts." -#. Data is assumed to be the destination sn +#. Data is assumed to be the destination bn #, c-format msgid "Unable to send message: %s" msgstr "No s'ha pogut enviar el missatge: %s" @@ -7308,6 +7323,35 @@ msgid "Could not change buddy information." msgstr "No s'ha pogut canviar la informació l'amic." +msgid "Mobile" +msgstr "Mòbil" + +msgid "Note" +msgstr "Nota" + +# FIXME: "memo", el qq té una terminologia molt peculiar +#. callback +msgid "Buddy Memo" +msgstr "Memo de l'amic" + +msgid "Change his/her memo as you like" +msgstr "Canvieu-ne el memo" + +msgid "_Modify" +msgstr "_Modifica" + +msgid "Memo Modify" +msgstr "Modifica el memo" + +msgid "Server says:" +msgstr "El servidor diu:" + +msgid "Your request was accepted." +msgstr "S'ha acceptat la vostra sol·licitud." + +msgid "Your request was rejected." +msgstr "S'ha rebutjat la vostra sol·licitud." + #, c-format msgid "%u requires verification" msgstr "Cal verificació per a %u" @@ -7620,6 +7664,9 @@ msgid "

Acknowledgement:
\n" msgstr "

Reconeixement:
\n" +msgid "

Scrupulous Testers:
\n" +msgstr "

Comprovadors del codi:
\n" + # FIXME: ush... traducció lliure... msgid "

And, all the boys in the backroom...
\n" msgstr "

I tothom que ho ha fet possible...
\n" @@ -7646,6 +7693,9 @@ msgid "About OpenQ" msgstr "Quant a l'OpenQ" +msgid "Modify Buddy Memo" +msgstr "Modifica el memo de l'amic" + #. *< type #. *< ui_requirement #. *< flags @@ -8662,9 +8712,6 @@ msgid "Unit" msgstr "Unitat" -msgid "Note" -msgstr "Nota" - msgid "Join Chat" msgstr "Entra a un xat" @@ -10166,9 +10213,6 @@ msgid "Extended away" msgstr "Absent durant una bona estona" -msgid "Mobile" -msgstr "Mòbil" - # És un estat, com "fora de línia", etc. (josep) msgid "Listening to music" msgstr "Escoltant música" @@ -10211,18 +10255,6 @@ msgid "%x %X" msgstr "%x %X" -#, c-format -msgid "Error Reading %s" -msgstr "S'ha produït un error en llegir %s" - -#, c-format -msgid "" -"An error was encountered reading your %s. They have not been loaded, and " -"the old file has been renamed to %s~." -msgstr "" -"S'ha produït un error en llegir el vostre %s. No s'han carregat, i s'ha " -"canviat el nom del fitxer per %s~." - msgid "Calculating..." msgstr "S'està calculant..." @@ -10332,6 +10364,18 @@ msgid "Address already in use." msgstr "Aquesta adreça ja s'està fent servir" +#, c-format +msgid "Error Reading %s" +msgstr "S'ha produït un error en llegir %s" + +#, c-format +msgid "" +"An error was encountered reading your %s. The file has not been loaded, and " +"the old file has been renamed to %s~." +msgstr "" +"S'ha produït un error en llegir el vostre %s. No s'ha carregat el fitxer, i " +"s'ha canviat el nom per %s~." + msgid "Internet Messenger" msgstr "Missatger d'Internet" @@ -10657,6 +10701,9 @@ msgid "/Tools/_Certificates" msgstr "/Eines/C_ertificats" +msgid "/Tools/Custom Smile_ys" +msgstr "/Eines/Em_oticones personalitzades" + msgid "/Tools/Plu_gins" msgstr "/Eines/_Connectors" @@ -10666,9 +10713,6 @@ msgid "/Tools/Pr_ivacy" msgstr "/Eines/_Privadesa" -msgid "/Tools/Smile_y" -msgstr "/Eines/Em_oticona" - msgid "/Tools/_File Transfers" msgstr "/Eines/_Transferència de fitxers" @@ -10786,8 +10830,8 @@ msgid "By status" msgstr "Per estat" -msgid "By log size" -msgstr "Per la mida del registre" +msgid "By recent log activity" +msgstr "Per activitat recent en el registre" #, c-format msgid "%s disconnected" @@ -11902,15 +11946,6 @@ msgid "Enable typing notification" msgstr "Habilita les notificacions de que s'està escrivint" -msgid "_Copy Email Address" -msgstr "_Copia l'adreça de correu" - -msgid "_Open Link in Browser" -msgstr "_Obre l'enllaç en el navegador" - -msgid "_Copy Link Location" -msgstr "_Copia la ubicació de l'enllaç" - msgid "" "Unrecognized file type\n" "\n" @@ -12169,6 +12204,7 @@ "\n" " -c, --config=DIR use DIR for config files\n" " -d, --debug print debugging messages to stdout\n" +" -f, --force-online force online, regardless of network status\n" " -h, --help display this help and exit\n" " -m, --multiple do not ensure single instance\n" " -n, --nologin don't automatically login\n" @@ -12183,8 +12219,11 @@ "\n" " -c, --config=DIR utilitza DIR per als fitxers de configuració\n" " -d, --debug mostra missatges de depuració a la sortida estàndard\n" +" -f, --force-online força que s'estigui en línia, independent de l'estat " +"de\n" +" la xarxa\n" " -h, --help mostra aquesta ajuda i surt\n" -" -m, --multiple permet que hi hagi més d'una instància\n" +" -m, --multiple no controla que només hi hagi una instància\n" " -n, --nologin no entra automàticament\n" " -l, --login[=NOM] habilita el compte especificat (l'argument opcional " "NOM\n" @@ -12201,6 +12240,7 @@ "\n" " -c, --config=DIR use DIR for config files\n" " -d, --debug print debugging messages to stdout\n" +" -f, --force-online force online, regardless of network status\n" " -h, --help display this help and exit\n" " -m, --multiple do not ensure single instance\n" " -n, --nologin don't automatically login\n" @@ -12214,8 +12254,11 @@ "\n" " -c, --config=DIR utilitza DIR per als fitxers de configuració\n" " -d, --debug mostra missatges de depuració a la sortida estàndard\n" +" -f, --force-online força que s'estigui en línia, independent de l'estat " +"de\n" +" la xarxa\n" " -h, --help mostra aquesta ajuda i surt\n" -" -m, --multiple permet que hi hagi més d'una instància\n" +" -m, --multiple no controla que només hi hagi una instància\n" " -n, --nologin no entra automàticament\n" " -l, --login[=NOM] habilita el compte especificat (l'argument opcional " "NOM\n" @@ -12346,6 +12389,9 @@ msgid "Select a file" msgstr "Seleccioneu un fitxer" +msgid "Modify Buddy Pounce" +msgstr "Modifica l'avís per a l'amic" + # FIXME #. Create the "Pounce on Whom" frame. msgid "Pounce on Whom" @@ -12443,6 +12489,11 @@ msgid "Cl_ose conversations with the Escape key" msgstr "Tanca les converses amb la tecla d'_escapament" +#. Buddy List Themes +msgid "Buddy List Theme" +msgstr "Tema de la llista d'amics" + +#. System Tray msgid "System Tray Icon" msgstr "Icona d'estat" @@ -12928,6 +12979,12 @@ msgid "Status for %s" msgstr "Estat per a %s" +#. +#. * TODO: We should enable/disable the add button based on +#. * whether the user has entered all required data. That +#. * would eliminate the need for this check and provide a +#. * better user experience. +#. msgid "Custom Smiley" msgstr "Emoticona personalitzada" @@ -12937,16 +12994,15 @@ msgid "Please provide a shortcut to associate with the smiley." msgstr "Especifiqueu una drecera associada a l'emoticona." +#, c-format +msgid "" +"A custom smiley for '%s' already exists. Please use a different shortcut." +msgstr "" +"Ja hi ha una emoticona personalitzada per a «%s». Indiqueu-ne una de diferent." + msgid "Duplicate Shortcut" msgstr "Drecera duplicada" -msgid "" -"A custom smiley for the selected shortcut already exists. Please specify a " -"different shortcut." -msgstr "" -"Hi ha una emoticona personalitzada per la drecera que heu seleccionat. " -"Indiqueu-ne una de diferent." - msgid "Please select an image for the smiley." msgstr "Seleccioneu una imatge per a l'emoticona." @@ -12956,16 +13012,19 @@ msgid "Add Smiley" msgstr "Afegeix una emoticona" -msgid "Smiley _Image" -msgstr "_Imatge de l'emoticona" - -#. Smiley shortcut -msgid "Smiley S_hortcut" -msgstr "_Dreceres de l'emoticona" +msgid "_Image:" +msgstr "_Imatge:" + +#. Shortcut text +msgid "S_hortcut text:" +msgstr "_Drecera:" msgid "Smiley" msgstr "Emoticona" +msgid "Shortcut Text" +msgstr "Drecera" + msgid "Custom Smiley Manager" msgstr "Gestor d'emoticones personalitzades" @@ -13093,6 +13152,15 @@ "No s'ha pogut carregar la imatge «%s»: no se'n coneix el motiu, possiblement " "la imatge estigui corrompuda" +msgid "_Open Link" +msgstr "_Obre l'enllaç" + +msgid "_Copy Link Location" +msgstr "_Copia l'enllaç" + +msgid "_Copy Email Address" +msgstr "_Copia l'adreça de correu" + msgid "Save File" msgstr "Desa un fitxer" @@ -14126,6 +14194,18 @@ msgid "This plugin is useful for debbuging XMPP servers or clients." msgstr "Aquest connector és útil per a depurar servidors i clients XMPP." +#~ msgid "By log size" +#~ msgstr "Per la mida del registre" + +#~ msgid "_Open Link in Browser" +#~ msgstr "_Obre l'enllaç en el navegador" + +#~ msgid "Smiley _Image" +#~ msgstr "_Imatge de l'emoticona" + +#~ msgid "Smiley S_hortcut" +#~ msgstr "_Dreceres de l'emoticona" + #~ msgid "Unable to retrieve MSN Address Book" #~ msgstr "No s'ha pogut obtenir la llibreta d'adreces MSN"