Mercurial > pidgin
changeset 26135:91a7d0ad2021
propagate from branch 'im.pidgin.pidgin' (head d2e4560619da68f5b4346dcf1247908d88f39eb3)
to branch 'im.pidgin.soc.2008.yahoo' (head 9a753e6b9d2389b3b7a87d60eebbe98228c2fb36)
author | Sulabh Mahajan <sulabh@soc.pidgin.im> |
---|---|
date | Sun, 13 Jul 2008 14:19:59 +0000 |
parents | 4dd866b83e8e (diff) 0e54d1fea7e2 (current diff) |
children | 2666b864dc89 |
files | COPYRIGHT libpurple/protocols/msn/soap2.c libpurple/protocols/msn/soap2.h libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h libpurple/protocols/yahoo/yahoo.c |
diffstat | 7 files changed, 919 insertions(+), 96 deletions(-) [+] |
line wrap: on
line diff
--- a/COPYRIGHT Sun Jul 13 10:09:19 2008 +0000 +++ b/COPYRIGHT Sun Jul 13 14:19:59 2008 +0000 @@ -241,6 +241,7 @@ Lucio Maciel Brian Macke Paolo Maggi +Sulabh Mahajan Willian T. Mahan Kris Marsh Fidel Martinez
--- a/libpurple/protocols/yahoo/yahoo.c Sun Jul 13 10:09:19 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Sun Jul 13 14:19:59 2008 +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" @@ -215,7 +216,7 @@ if (f->status == YAHOO_STATUS_IDLE) { /* Idle may have already been set in a more precise way in case 137 */ if (f->idle == 0) - f->idle = time(NULL); + f->idle = time(NULL) - 60; /* Start idle at 1 min */ } else f->idle = 0; @@ -229,6 +230,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; @@ -246,7 +249,7 @@ if (f->away == 2) { /* Idle may have already been set in a more precise way in case 137 */ if (f->idle == 0) - f->idle = time(NULL); + f->idle = time(NULL) - 60; /* start idle at 1 min */ } break; @@ -515,6 +518,10 @@ purple_blist_add_buddy(b, NULL, g, NULL); } yahoo_do_group_check(account, ht, norm_bud, yd->current_list15_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; } else { /* This buddy is on the ignore list (and therefore in no group) */ @@ -629,6 +636,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); @@ -685,7 +696,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; @@ -694,12 +706,14 @@ char *game = NULL; YahooFriend *f = NULL; GSList *l = pkt->hash; + gint val_11 = 0; + struct yahoo_data *yd = gc->proto_data; 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; @@ -707,12 +721,22 @@ stat = pair->value; if (pair->key == 14) game = pair->value; + if (pair->key == 11) + val_11 = strtol(pair->value, NULL, 10); 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 (!g_ascii_strncasecmp(msg, "TYPING", strlen("TYPING")) && (purple_privacy_check(account, from))) { @@ -749,7 +773,6 @@ } - struct _yahoo_im { char *from; int time; @@ -758,7 +781,8 @@ char *msg; }; -static void yahoo_process_message(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_message(PurpleConnection *gc, struct yahoo_packet *pkt, yahoo_pkt_type pkt_type) { PurpleAccount *account; struct yahoo_data *yd = gc->proto_data; @@ -766,13 +790,15 @@ GSList *list = NULL; struct _yahoo_im *im = NULL; const char *imv = NULL; + gint val_11 = 0; 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 reveived 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; @@ -792,6 +818,11 @@ if (im) im->msg = pair->value; } + /* peer session id */ + if (pair->key == 11) { + if (im) + val_11 = strtol(pair->value, NULL, 10); + } /* IMV key */ if (pair->key == 63) { @@ -804,6 +835,14 @@ _("Your Yahoo! message did not get sent."), 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) @@ -2181,6 +2220,7 @@ char *buf; YahooFriend *f; GSList *l = pkt->hash; + struct yahoo_data *yd = gc->proto_data; while (l) { struct yahoo_pair *pair = l->data; @@ -2208,6 +2248,14 @@ 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( !g_hash_table_lookup(yd->peers, who) ) { + /* we are not connected as client, so set friend to not connected */ + 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; } @@ -2220,6 +2268,348 @@ g_free(decoded_group); } +/* 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); +} + +/* 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); +} + +/* 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; + + 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 : 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); +} + +/* 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 */ + g_hash_table_remove(yd->peers,p2p_data->host_username); + 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; + YahooFriend *f; + 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 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; + + if( (f = yahoo_friend_find(p2p_data->gc, p2p_data->host_username)) ) { + p2p_data->session_id = f->session_id; + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_SERVER); + } + + /* 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; + + g_hash_table_insert(yd->peers, g_strdup(p2p_data->host_username), p2p_data); +} + +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); +} + +/* send p2p pkt containing our encoded ip, asking peer to connect to us */ +static 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 = g_new0(struct yahoo_p2p_data, 1); + + 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) ); + + f = yahoo_friend_find(gc, who); + account = purple_connection_get_account(gc); + + /* 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; + + 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->gc = gc; + p2p_data->host_ip = NULL; + p2p_data->host_username = (char *)g_malloc(strlen(who)); + strcpy(p2p_data->host_username, 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; + YahooFriend *f; + 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; + + 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) + yahoo_friend_set_p2p_status(f, YAHOO_P2PSTATUS_WE_ARE_CLIENT); + + 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) { GSList *l = pkt->hash; @@ -2227,6 +2617,10 @@ char *base64 = NULL; guchar *decoded; gsize len; + gint val_13 = 0; + gint val_11 = 0; + PurpleAccount *account; + YahooFriend *f; while (l) { struct yahoo_pair *pair = l->data; @@ -2246,14 +2640,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 */ } @@ -2265,6 +2666,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) { @@ -2277,12 +2680,35 @@ 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 = (char *)g_malloc(strlen(who)); + strcpy(p2p_data->host_username, 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); + } } } @@ -2362,12 +2788,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); @@ -2444,7 +2870,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; @@ -2999,6 +3426,7 @@ 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->confs = NULL; yd->conf_id = 2; @@ -3063,6 +3491,7 @@ if (yd->in_chat) yahoo_c_leave(gc, 1); /* 1 = YAHOO_CHAT_ID */ + 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); @@ -3486,11 +3915,13 @@ static void yahoo_get_inbox_token_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *token, size_t len, const gchar *error_message) + const gchar *webdata, size_t len, const gchar *error_message) { PurpleConnection *gc = user_data; gboolean set_cookie = FALSE; gchar *url; + gchar *token = NULL; + int token_size; struct yahoo_data *yd = gc->proto_data; g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); @@ -3499,8 +3930,14 @@ if (error_message != NULL) purple_debug_error("yahoo", "Requesting mail login token failed: %s\n", error_message); - else if (len > 0 && token && *token) { - /* Should we not be hardcoding the rd url? */ + else if (len > 0 && webdata && *webdata) { + /* Extract token from the chunked webdata */ + sscanf(webdata,"%x",&token_size); + token = g_malloc(token_size); + strncpy(token, strstr(webdata,"\r\n")+2, token_size); + token[token_size-1]='\0'; + + /* Should we not be hardcoding the rd url? */ url = g_strdup_printf( "http://login.yahoo.com/config/reset_cookies_token?" ".token=%s" @@ -3518,6 +3955,7 @@ purple_notify_uri(gc, url); g_free(url); + g_free(token); } @@ -3532,12 +3970,13 @@ PurpleUtilFetchUrlData *url_data; const char* base_url = "http://login.yahoo.com"; char *request = g_strdup_printf( - "POST /config/cookie_token HTTP/1.0\r\n" - "Cookie: T=%s; path=/; domain=.yahoo.com; Y=%s;\r\n" + "POST /config/cookie_token HTTP/1.1\r\n" + "Cookie: Y=%s; path=/; domain=.yahoo.com; T=%s; path=/; domain=.yahoo.com;\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n" "Host: login.yahoo.com\r\n" - "Content-Length: 0\r\n\r\n", - yd->cookie_t, yd->cookie_y); + "Content-Length: 0\r\n" + "Cache-Control: no-cache\r\n\r\n", + yd->cookie_y, yd->cookie_t); gboolean use_whole_url = FALSE; /* use whole URL if using HTTP Proxy */ @@ -3555,7 +3994,7 @@ else { const char *yahoo_mail_url = (yd->jp ? YAHOOJP_MAIL_URL : YAHOO_MAIL_URL); purple_debug_error("yahoo", - "Unable to request mail login token; forwarding to login screen."); + "Unable to request mail login token; forwarding to login screen.\n"); purple_notify_uri(gc, yahoo_mail_url); } @@ -3614,6 +4053,7 @@ PurpleWhiteboard *wb; int ret = 1; YahooFriend *f = NULL; + struct yahoo_p2p_data *p2p_data; msg2 = yahoo_string_encode(gc, msg, &utf8); @@ -3658,8 +4098,17 @@ 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)) ) { + yahoo_packet_hash_int(pkt, 11, p2p_data->session_id); + yahoo_p2p_write_pkt(p2p_data->source, pkt); + } + else { + yahoo_packet_send(pkt, yd); + yahoo_send_p2p_pkt(gc, who, 0); /* send p2p packet, with val_13=0 */ + } + } else ret = -E2BIG; @@ -3674,12 +4123,24 @@ static unsigned int yahoo_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state) { struct yahoo_data *yd = gc->proto_data; + struct yahoo_p2p_data *p2p_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), + + /* check to see if p2p link exists, send through it */ + if( (p2p_data = g_hash_table_lookup(yd->peers, who)) ) { + yahoo_packet_hash(pkt, "sssssis", 49, "TYPING", 1, purple_connection_get_display_name(gc), + 14, " ", 13, state == PURPLE_TYPING ? "1" : "0", + 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 */ + yahoo_packet_hash(pkt, "ssssss", 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); + yahoo_packet_send_and_free(pkt, yd); + } return 0; } @@ -4269,10 +4730,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; } @@ -4286,7 +4747,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(); */ @@ -4347,7 +4808,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 */
--- a/libpurple/protocols/yahoo/yahoo.h Sun Jul 13 10:09:19 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.h Sun Jul 13 14:19:59 2008 +0000 @@ -30,6 +30,7 @@ #define YAHOO_PAGER_HOST "scs.msg.yahoo.com" #define YAHOO_PAGER_PORT 5050 +#define YAHOO_PAGER_PORT_P2P 5101 #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 +46,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/" @@ -80,10 +81,19 @@ #define YAHOOJP_CLIENT_VERSION_ID "524223" #define YAHOOJP_CLIENT_VERSION "7,0,1,1" - /* Index into attention types list. */ #define YAHOO_BUZZ 0 +typedef enum { + YAHOO_PKT_TYPE_SERVER = 0, + YAHOO_PKT_TYPE_P2P +} yahoo_pkt_type; + +typedef enum { + YAHOO_P2P_WE_ARE_CLIENT =0, + YAHOO_P2P_WE_ARE_SERVER +} yahoo_p2p_connection_type; + enum yahoo_status { YAHOO_STATUS_AVAILABLE = 0, YAHOO_STATUS_BRB, @@ -113,6 +123,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,14 +189,17 @@ * 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; * the server expects us to keep track of the group for which it is sending us contact names. */ char *current_list15_grp; + GHashTable *peers; /* information about p2p data */ + int yahoo_local_p2p_server_fd; + int yahoo_p2p_server_watcher; }; #define YAHOO_MAX_STATUS_MESSAGE_LENGTH (255)
--- a/libpurple/protocols/yahoo/yahoo_filexfer.c Sun Jul 13 10:09:19 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_filexfer.c Sun Jul 13 14:19:59 2008 +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; @@ -684,7 +693,7 @@ purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv); - /*update map to current xfer*/ + /* update map to current xfer */ g_hash_table_remove(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring); g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_peer_idstring, xfer); @@ -980,7 +989,7 @@ return; } - /*TODO:actually, u must try with addr no.1 , if its not working addr no.2 .....*/ + /* TODO:actually, u must try with addr no.1 , if its not working addr no.2 ..... */ addr = hosts->data; actaddr = addr->sin_addr.s_addr; d = actaddr % 256; @@ -1030,7 +1039,6 @@ 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; @@ -1042,7 +1050,10 @@ /* 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; + ver = 15; + /* Default to ver 15 if user isn't a friend */ + if(!yf) + ver = 15; g_return_if_fail(xfer != NULL); @@ -1062,7 +1073,9 @@ purple_xfer_request(xfer); } -static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/*using this in recv_cb*/ +static void yahoo_p2p_ft_server_listen_cb(int listenfd, gpointer data); /* using this in yahoo_xfer_send_cb_15 */ +static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message);/* using this in recv_cb */ + static void yahoo_xfer_recv_cb_15(gpointer data, gint source, PurpleInputCondition condition) { PurpleXfer *xfer; @@ -1102,7 +1115,7 @@ if(xd->status_15 == HEAD_REQUESTED) { xd->status_15 = HEAD_REPLY_RECEIVED; - close(source);/*Is this required?*/ + close(source);/* Is this required? */ g_free(xd->txbuf); xd->txbuf = NULL; if (purple_proxy_connect(NULL, account, xd->host, xd->port, yahoo_xfer_connected_15, xfer) == NULL) @@ -1151,7 +1164,7 @@ xd->txbuf_written = 0; if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED) - { + { xd->status_15 = HEAD_REQUESTED; xd->tx_handler = purple_input_add(source, PURPLE_INPUT_READ, yahoo_xfer_recv_cb_15, xfer); yahoo_xfer_recv_cb_15(xfer, source, PURPLE_INPUT_READ); @@ -1162,21 +1175,33 @@ xfer->fd = source; purple_xfer_start(xfer, source, NULL, 0); } - else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED) + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && (xd->status_15 == ACCEPTED || xd->status_15 == P2P_GET_REQUESTED) ) { xd->status_15 = TRANSFER_PHASE; xfer->fd = source; + /* Remove Read event */ + purple_input_remove(xd->input_event); + xd->input_event = 0; purple_xfer_start(xfer, source, NULL, 0); } + else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == P2P_HEAD_REQUESTED) + { + xd->status_15 = P2P_HEAD_REPLIED; + /* Remove Read event and close descriptor */ + purple_input_remove(xd->input_event); + xd->input_event = 0; + close(source); + xfer->fd = -1; + /* start local server, listen for connections */ + purple_network_listen(xd->yahoo_local_p2p_ft_server_port, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer); + } else { purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15); return; } - } - static void yahoo_xfer_connected_15(gpointer data, gint source, const gchar *error_message) { PurpleXfer *xfer; @@ -1203,31 +1228,61 @@ cookies = yahoo_get_cookies(xd->gc); if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED) { - xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n", + if(xd->info_val_249 == 2) + { + /* sending file via p2p, we are connected as client */ + xd->txbuf = g_strdup_printf("POST /%s HTTP/1.1\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n", + xd->path, + xd->host, + (long int)xfer->size); /* to do, add Referer */ + } + else + { + /* sending file via relaying */ + xd->txbuf = g_strdup_printf("POST /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: %ld\r\nCache-Control: no-cache\r\n\r\n", purple_url_encode(xd->xfer_idstring_for_relay), purple_normalize(account, purple_account_get_username(account)), xfer->who, cookies, xd->host, (long int)xfer->size); + } } else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == STARTED) { - xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nAccept:*/*\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n", + if(xd->info_val_249 == 1) + { + /* receiving file via p2p, connected as client */ + xd->txbuf = g_strdup_printf("HEAD /%s HTTP/1.1\r\nAccept:*/*\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n",xd->path,xd->host); + } + else + { + /* receiving file via relaying */ + xd->txbuf = g_strdup_printf("HEAD /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nAccept:*/*\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nContent-Length: 0\r\nCache-Control: no-cache\r\n\r\n", purple_url_encode(xd->xfer_idstring_for_relay), purple_normalize(account, purple_account_get_username(account)), xfer->who, cookies, xd->host); + } } else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED) { - xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nConnection: Keep-Alive\r\n\r\n", + if(xd->info_val_249 == 1) + { + /* receiving file via p2p, connected as client */ + xd->txbuf = g_strdup_printf("GET /%s HTTP/1.1\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n",xd->path,xd->host); + } + else + { + /* receiving file via relaying */ + xd->txbuf = g_strdup_printf("GET /relay?token=%s&sender=%s&recver=%s HTTP/1.1\r\nCookie:%s\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\nHost:%s\r\nConnection: Keep-Alive\r\n\r\n", purple_url_encode(xd->xfer_idstring_for_relay), purple_normalize(account, purple_account_get_username(account)), xfer->who, cookies, xd->host); + } } else { @@ -1248,6 +1303,227 @@ } } +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 **split; + 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)); + split = g_strsplit(filename, " ", 0); + filename_without_spaces = g_strjoinv("+", split); + 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_strfreev(split); + g_free(filename_without_spaces); + } + + /* Add an Input Read event to the file descriptor */ + xd->yahoo_local_p2p_ft_server_fd = listenfd; + xd->yahoo_p2p_ft_server_watcher = purple_input_add(listenfd, PURPLE_INPUT_READ, yahoo_p2p_ft_server_send_connected_cb, data); +} + +/* send (p2p) file transfer information */ +static void yahoo_p2p_client_send_ft_info(PurpleConnection *gc, PurpleXfer *xfer) +{ + struct yahoo_xfer_data *xd; + struct yahoo_packet *pkt; + PurpleAccount *account; + struct yahoo_data *yd; + gchar *filename; + struct yahoo_p2p_data *p2p_data; + + if (!(xd = xfer->data)) + return; + + account = purple_connection_get_account(gc); + yd = gc->proto_data; + + p2p_data = g_hash_table_lookup(yd->peers, xfer->who); + if( p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER ) + if(purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer)) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_AVAILABLE, yd->session_id); + filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); + + yahoo_packet_hash(pkt, "ssssi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xd->xfer_peer_idstring, + 27, filename, + 249, 2); /* 249=2: we are p2p client */ + xd->info_val_249 = 2; + yahoo_packet_send_and_free(pkt, yd); + + g_free(filename); +} + void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt) { char *from = NULL; @@ -1293,14 +1569,14 @@ /* 1=send, 2=cancel, 3=accept, 4=reject */ break; - /*check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/ + /* check for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it. */ case 49: service = pair->value; break; case 63: imv = pair->value; break; - /*end check*/ + /* end check */ } } @@ -1328,6 +1604,14 @@ * so, purple dnsquery is used... but retries, trying with next ip * address etc. is not implemented..TODO */ + + /* To send through p2p */ + if( g_hash_table_lookup(yd->peers, from) ) { + /* send p2p file transfer information */ + yahoo_p2p_client_send_ft_info(gc, xfer); + return; + } + if (yd->jp) { purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT, @@ -1341,7 +1625,7 @@ return; } - /*processing for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it.*/ + /* processing for p2p and imviron .... not sure it comes by this service packet. Since it was bundled with filexfer in old ymsg version, still keeping it. */ /* * The remote user has changed their IMVironment. We * record it for later use. @@ -1357,7 +1641,7 @@ return; } } - /*end processing*/ + /* end processing */ if(!filename_list) return; @@ -1432,6 +1716,7 @@ GSList *l; struct yahoo_packet *pkt_to_send; PurpleAccount *account; + struct yahoo_p2p_data *p2p_data; yd = gc->proto_data; @@ -1455,13 +1740,8 @@ val_66 = strtol(pair->value, NULL, 10); break; case 249: - val_249 = strtol(pair->value, NULL, 10); /* - * really pissed off with this- i hv seen 2 occurences of this - * being 1(its normally 3) - and in those cases, the url - * format and corresponding processing seems to be different - * (i havent tested - couldnt reproduce a 1), although i - * guess its easier. - */ + val_249 = strtol(pair->value, NULL, 10); + /* 249 has value 1 or 2 when doing p2p transfer and value 3 when relaying through yahoo server */ break; case 250: url = pair->value; @@ -1489,35 +1769,48 @@ xfer_data->info_val_249 = val_249; xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); - if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) { - purple_xfer_cancel_remote(xfer); - return; - } + if(val_249 == 1 || val_249 == 3) { + if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) { + purple_xfer_cancel_remote(xfer); + return; + } + + account = purple_connection_get_account(xfer_data->gc); - account = purple_connection_get_account(xfer_data->gc); + pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, + YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt_to_send, "ssssisi", + 1, purple_normalize(account, purple_account_get_username(account)), + 5, xfer->who, + 265, xfer_data->xfer_peer_idstring, + 27, xfer->filename, + 249, xfer_data->info_val_249, + 251, xfer_data->xfer_idstring_for_relay, + 222, 3); - pkt_to_send = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, - YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_send_and_free(pkt_to_send, yd); - yahoo_packet_hash(pkt_to_send, "ssssisi", - 1, purple_normalize(account, purple_account_get_username(account)), - 5, xfer->who, - 265, xfer_data->xfer_peer_idstring, - 27, xfer->filename, - 249, xfer_data->info_val_249, - 251, xfer_data->xfer_idstring_for_relay, - 222, 3); + if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, + yahoo_xfer_connected_15, xfer) == NULL) { + purple_notify_error(gc, NULL, _("File Transfer Failed"), + _("Unable to establish file descriptor.")); + purple_xfer_cancel_remote(xfer); + } + } + else if(val_249 == 2) { + p2p_data = g_hash_table_lookup(yd->peers, xfer->who); + if( !( p2p_data && (p2p_data->connection_type == YAHOO_P2P_WE_ARE_SERVER) ) ) { + purple_xfer_cancel_remote(xfer); + return; + } + if(!purple_network_listen_range(0, 0, SOCK_STREAM, yahoo_p2p_ft_server_listen_cb, xfer)) { + purple_xfer_cancel_remote(xfer); + return; + } + } +} - yahoo_packet_send_and_free(pkt_to_send, yd); - if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, - yahoo_xfer_connected_15, xfer) == NULL) { - purple_notify_error(gc, NULL, _("File Transfer Failed"), - _("Unable to establish file descriptor.")); - purple_xfer_cancel_remote(xfer); - } - -} -/*TODO: Check filename etc. No probs till some hacker comes in the way*/ +/* TODO: Check filename etc. No probs till some hacker comes in the way */ void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *pkt) { gchar *xfer_peer_idstring = NULL; @@ -1528,6 +1821,8 @@ GSList *l; PurpleAccount *account; long val_66 = 0; + gchar *url = NULL; + int val_249; yd = gc->proto_data; for (l = pkt->hash; l; l = l->next) { @@ -1542,19 +1837,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);
--- a/libpurple/protocols/yahoo/yahoo_friend.c Sun Jul 13 10:09:19 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.c Sun Jul 13 14:19:59 2008 +0000 @@ -264,3 +264,13 @@ 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; +}
--- a/libpurple/protocols/yahoo/yahoo_friend.h Sun Jul 13 10:09:19 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_friend.h Sun Jul 13 14:19:59 2008 +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_ */
--- a/libpurple/protocols/yahoo/yahoo_packet.h Sun Jul 13 10:09:19 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_packet.h Sun Jul 13 14:19:59 2008 +0000 @@ -76,7 +76,7 @@ YAHOO_SERVICE_IGNORECONTACT, /* > 1, 7, 13 < 1, 66, 13, 0*/ YAHOO_SERVICE_REJECTCONTACT, YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */ - /* YAHOO_SERVICE_??? = 0x8A, */ + /* YAHOO_SERVICE_??? = 0x8A,some sort of keep alive sent to the server every 60 secs */ YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/ YAHOO_SERVICE_CHATGOTO, YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */ @@ -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 {