Mercurial > pidgin
view libpurple/protocols/yahoo/yahoo_filexfer.c @ 22018:118046227581
This patch from Thanumalayan S. implements Yahoo protocol version 15 file
transfer support. Currently sending multiple files from an official Yahoo
client is treated as multiple individual transfers that must be accepted
separately. This also changes things so that we identify as Yahoo Messenger
version 8.0 (previously we were identifying as 6.0 or not at all).
committer: John Bailey <rekkanoryo@rekkanoryo.org>
author | Thanumalayan <madthanu@gmail.com> |
---|---|
date | Sun, 06 Jan 2008 19:45:03 +0000 |
parents | 97d530d11cc0 |
children | 4163dcf643ae |
line wrap: on
line source
/* * @file yahoo_filexfer.c Yahoo Filetransfer * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * 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 "dnsquery.h" #include "prpl.h" #include "util.h" #include "debug.h" #include "notify.h" #include "proxy.h" #include "ft.h" #include "yahoo.h" #include "yahoo_packet.h" #include "yahoo_filexfer.h" #include "yahoo_doodle.h" #include "yahoo_friend.h" struct yahoo_xfer_data { gchar *host; gchar *path; int port; PurpleConnection *gc; long expires; gboolean started; gchar *txbuf; gsize txbuflen; gsize txbuf_written; guint tx_handler; gchar *rxqueue; guint rxlen; gchar *xfer_idstring_between_peers; gchar *xfer_idstring_for_relay; int version; /*0 for old, 15 for Y7(YMSG 15)*/ int info_val_249; enum {STARTED = 0,HEAD_REQUESTED,HEAD_REPLY_RECEIVED,TRANSFER_PHASE,ACCEPTED} status_15; GSList *filename_list;/*contains all filenames, in case of multiple transfers, with the first one in the list being the current(ymsg15)*/ GSList *size_list;/*corresponds to filename_list, with size as **STRING** */ gboolean firstoflist; }; static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd) { PurpleConnection *gc; struct yahoo_data *yd; PurpleXfer *xfer; GSList *l; gc = xd->gc; yd = gc->proto_data; /*remove entry from map*/ if(xd->xfer_idstring_between_peers) { xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xd->xfer_idstring_between_peers); if(xfer) g_hash_table_remove(yd->xfer_peer_idstring_map, xd->xfer_idstring_between_peers); } /*empty file & filesize list*/ for (l = xd->filename_list; l; l = l->next) { g_free(l->data); l->data=NULL; } for (l = xd->size_list; l; l = l->next) { g_free(l->data); l->data=NULL; } g_slist_free(xd->filename_list); g_slist_free(xd->size_list); g_free(xd->host); g_free(xd->path); g_free(xd->txbuf); g_free(xd->xfer_idstring_between_peers); g_free(xd->xfer_idstring_for_relay); if (xd->tx_handler) purple_input_remove(xd->tx_handler); g_free(xd); } static void yahoo_receivefile_send_cb(gpointer data, gint source, PurpleInputCondition condition) { PurpleXfer *xfer; struct yahoo_xfer_data *xd; int remaining, written; xfer = data; xd = xfer->data; remaining = xd->txbuflen - xd->txbuf_written; written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining); if (written < 0 && errno == EAGAIN) written = 0; else if (written <= 0) { purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); purple_xfer_cancel_remote(xfer); return; } if (written < remaining) { xd->txbuf_written += written; return; } purple_input_remove(xd->tx_handler); xd->tx_handler = 0; g_free(xd->txbuf); xd->txbuf = NULL; xd->txbuflen = 0; purple_xfer_start(xfer, source, NULL, 0); } static void yahoo_receivefile_connected(gpointer data, gint source, const gchar *error_message) { PurpleXfer *xfer; struct yahoo_xfer_data *xd; purple_debug(PURPLE_DEBUG_INFO, "yahoo", "AAA - in yahoo_receivefile_connected\n"); if (!(xfer = data)) return; if (!(xd = xfer->data)) return; if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) { purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer), xfer->who, _("Unable to connect.")); purple_xfer_cancel_remote(xfer); return; } xfer->fd = source; /* The first time we get here, assemble the tx buffer */ if (xd->txbuflen == 0) { xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", xd->path, xd->host); xd->txbuflen = strlen(xd->txbuf); xd->txbuf_written = 0; } if (!xd->tx_handler) { xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, yahoo_receivefile_send_cb, xfer); yahoo_receivefile_send_cb(xfer, source, PURPLE_INPUT_WRITE); } } static void yahoo_sendfile_send_cb(gpointer data, gint source, PurpleInputCondition condition) { PurpleXfer *xfer; struct yahoo_xfer_data *xd; int written, remaining; xfer = data; xd = xfer->data; remaining = xd->txbuflen - xd->txbuf_written; written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining); if (written < 0 && errno == EAGAIN) written = 0; else if (written <= 0) { purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); purple_xfer_cancel_remote(xfer); return; } if (written < remaining) { xd->txbuf_written += written; return; } purple_input_remove(xd->tx_handler); xd->tx_handler = 0; g_free(xd->txbuf); xd->txbuf = NULL; xd->txbuflen = 0; purple_xfer_start(xfer, source, NULL, 0); } static void yahoo_sendfile_connected(gpointer data, gint source, const gchar *error_message) { PurpleXfer *xfer; struct yahoo_xfer_data *xd; struct yahoo_packet *pkt; gchar *size, *filename, *encoded_filename, *header; guchar *pkt_buf; const char *host; int port; size_t content_length, header_len, pkt_buf_len; PurpleConnection *gc; PurpleAccount *account; struct yahoo_data *yd; purple_debug(PURPLE_DEBUG_INFO, "yahoo", "AAA - in yahoo_sendfile_connected\n"); if (!(xfer = data)) return; if (!(xd = xfer->data)) return; if (source < 0) { purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer), xfer->who, _("Unable to connect.")); purple_xfer_cancel_remote(xfer); return; } xfer->fd = source; /* Assemble the tx buffer */ gc = xd->gc; account = purple_connection_get_account(gc); yd = gc->proto_data; pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id); size = g_strdup_printf("%" G_GSIZE_FORMAT, purple_xfer_get_size(xfer)); filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); encoded_filename = yahoo_string_encode(gc, filename, NULL); yahoo_packet_hash(pkt, "sssss", 0, purple_connection_get_display_name(gc), 5, xfer->who, 14, "", 27, encoded_filename, 28, size); g_free(size); g_free(encoded_filename); g_free(filename); content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, yd->jp, &pkt_buf); yahoo_packet_free(pkt); host = purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST); port = purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT); header = g_strdup_printf( "POST http://%s:%d/notifyft HTTP/1.0\r\n" "Content-length: %" G_GSIZE_FORMAT "\r\n" "Host: %s:%d\r\n" "Cookie: Y=%s; T=%s\r\n" "\r\n", host, port, content_length + 4 + purple_xfer_get_size(xfer), host, port, yd->cookie_y, yd->cookie_t); header_len = strlen(header); xd->txbuflen = header_len + pkt_buf_len + 4; xd->txbuf = g_malloc(xd->txbuflen); memcpy(xd->txbuf, header, header_len); g_free(header); memcpy(xd->txbuf + header_len, pkt_buf, pkt_buf_len); g_free(pkt_buf); memcpy(xd->txbuf + header_len + pkt_buf_len, "29\xc0\x80", 4); xd->txbuf_written = 0; if (xd->tx_handler == 0) { xd->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE, yahoo_sendfile_send_cb, xfer); yahoo_sendfile_send_cb(xfer, source, PURPLE_INPUT_WRITE); } } static void yahoo_xfer_init(PurpleXfer *xfer) { struct yahoo_xfer_data *xfer_data; PurpleConnection *gc; PurpleAccount *account; struct yahoo_data *yd; xfer_data = xfer->data; gc = xfer_data->gc; yd = gc->proto_data; account = purple_connection_get_account(gc); if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) { if (yd->jp) { if (purple_proxy_connect(NULL, account, purple_account_get_string(account, "xferjp_host", YAHOOJP_XFER_HOST), purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT), yahoo_sendfile_connected, xfer) == NULL) { purple_notify_error(gc, NULL, _("File Transfer Failed"), _("Unable to establish file descriptor.")); purple_xfer_cancel_remote(xfer); } } else { if (purple_proxy_connect(NULL, account, purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST), purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT), yahoo_sendfile_connected, xfer) == NULL) { purple_notify_error(gc, NULL, _("File Transfer Failed"), _("Unable to establish file descriptor.")); purple_xfer_cancel_remote(xfer); } } } else { xfer->fd = -1; if (purple_proxy_connect(NULL, account, xfer_data->host, xfer_data->port, yahoo_receivefile_connected, xfer) == NULL) { purple_notify_error(gc, NULL, _("File Transfer Failed"), _("Unable to establish file descriptor.")); purple_xfer_cancel_remote(xfer); } } } static void yahoo_xfer_init_15(PurpleXfer *xfer) { struct yahoo_xfer_data *xfer_data; PurpleConnection *gc; PurpleAccount *account; struct yahoo_data *yd; struct yahoo_packet *pkt; xfer_data = xfer->data; gc = xfer_data->gc; yd = gc->proto_data; account = purple_connection_get_account(gc); if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) { gchar *filename; filename = g_path_get_basename(purple_xfer_get_local_filename(xfer)); pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, "sssiiiisiii", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_idstring_between_peers, 222, 1, 266, 1, 302, 268, 300, 268, 27, filename, 28, xfer->size, 301, 268, 303, 268); g_free(filename); } else { if(xfer_data->firstoflist == TRUE) { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_idstring_between_peers, 222, 3); } else { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_ACC_15, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_idstring_between_peers, 271, 1); } } yahoo_packet_send_and_free(pkt, yd); } static void yahoo_xfer_start(PurpleXfer *xfer) { /* We don't need to do anything here, do we? */ } static guint calculate_length(const gchar *l, size_t len) { int i; for (i = 0; i < len; i++) { if (!g_ascii_isdigit(l[i])) continue; return strtol(l + i, NULL, 10); } return 0; } static gssize yahoo_xfer_read(guchar **buffer, PurpleXfer *xfer) { gchar buf[4096]; gssize len; gchar *start = NULL; gchar *length; gchar *end; int filelen; struct yahoo_xfer_data *xd = xfer->data; if (purple_xfer_get_type(xfer) != PURPLE_XFER_RECEIVE) { return 0; } len = read(xfer->fd, buf, sizeof(buf)); if (len <= 0) { if ((purple_xfer_get_size(xfer) > 0) && (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer))) { purple_xfer_set_completed(xfer, TRUE); return 0; } else return -1; } if (!xd->started) { xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen); memcpy(xd->rxqueue + xd->rxlen, buf, len); xd->rxlen += len; length = g_strstr_len(xd->rxqueue, len, "Content-length:"); /* some proxies re-write this header, changing the capitalization :( * technically that's allowed since headers are case-insensitive * [RFC 2616, section 4.2] */ if (length == NULL) length = g_strstr_len(xd->rxqueue, len, "Content-Length:"); if (length) { end = g_strstr_len(length, length - xd->rxqueue, "\r\n"); if (!end) return 0; if ((filelen = calculate_length(length, len - (length - xd->rxqueue)))) purple_xfer_set_size(xfer, filelen); } start = g_strstr_len(xd->rxqueue, len, "\r\n\r\n"); if (start) start += 4; if (!start || start > (xd->rxqueue + len)) return 0; xd->started = TRUE; len -= (start - xd->rxqueue); *buffer = g_malloc(len); memcpy(*buffer, start, len); g_free(xd->rxqueue); xd->rxqueue = NULL; xd->rxlen = 0; } else { *buffer = g_malloc(len); memcpy(*buffer, buf, len); } return len; } static gssize yahoo_xfer_write(const guchar *buffer, size_t size, PurpleXfer *xfer) { gssize len; struct yahoo_xfer_data *xd = xfer->data; if (!xd) return -1; if (purple_xfer_get_type(xfer) != PURPLE_XFER_SEND) { return -1; } len = write(xfer->fd, buffer, size); if (len == -1) { if (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer)) purple_xfer_set_completed(xfer, TRUE); if ((errno != EAGAIN) && (errno != EINTR)) return -1; return 0; } if ((purple_xfer_get_bytes_sent(xfer) + len) >= purple_xfer_get_size(xfer)) purple_xfer_set_completed(xfer, TRUE); return len; } static void yahoo_xfer_cancel_send(PurpleXfer *xfer) { struct yahoo_xfer_data *xfer_data; xfer_data = xfer->data; if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL && xfer_data->version == 15) { PurpleConnection *gc; PurpleAccount *account; struct yahoo_data *yd; struct yahoo_packet *pkt; gc = xfer_data->gc; yd = gc->proto_data; account = purple_connection_get_account(gc); if(xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */ { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_INFO_15, YAHOO_STATUS_DISCONNECTED, yd->session_id); yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_idstring_between_peers, 66, -1); } else { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_idstring_between_peers, 222, 2); } yahoo_packet_send_and_free(pkt, yd); } if (xfer_data) yahoo_xfer_data_free(xfer_data); xfer->data = NULL; } static void yahoo_xfer_cancel_recv(PurpleXfer *xfer) { struct yahoo_xfer_data *xfer_data; xfer_data = xfer->data; if(purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL && xfer_data->version == 15) { PurpleConnection *gc; PurpleAccount *account; struct yahoo_data *yd; struct yahoo_packet *pkt; gc = xfer_data->gc; yd = gc->proto_data; account = purple_connection_get_account(gc); if(!xfer_data->xfer_idstring_for_relay) /* hack to see if file trans acc/info packet has been received */ { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, YAHOO_STATUS_AVAILABLE, yd->session_id); yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_idstring_between_peers, 222, 4); } else { pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANS_15, YAHOO_STATUS_DISCONNECTED, yd->session_id); yahoo_packet_hash(pkt, "sssi", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xfer_data->xfer_idstring_between_peers, 66, -1); } yahoo_packet_send_and_free(pkt, yd); } if (xfer_data) yahoo_xfer_data_free(xfer_data); xfer->data = NULL; } static void yahoo_xfer_end(PurpleXfer *xfer_old) { struct yahoo_xfer_data *xfer_data; PurpleXfer *xfer = NULL; PurpleConnection *gc; struct yahoo_data *yd; xfer_data = xfer_old->data; if(xfer_data && xfer_data->version == 15 && purple_xfer_get_type(xfer_old) == PURPLE_XFER_RECEIVE && xfer_data->filename_list) { /* removing top of filename & size list completely */ g_free( xfer_data->filename_list->data ); g_free( xfer_data->size_list->data ); xfer_data->filename_list->data = NULL; xfer_data->size_list->data = NULL; xfer_data->filename_list = g_slist_delete_link(xfer_data->filename_list, xfer_data->filename_list); xfer_data->size_list = g_slist_delete_link(xfer_data->size_list, xfer_data->size_list); /* if there are still more files */ if(xfer_data->filename_list) { gchar* filename; long filesize; filename = xfer_data->filename_list->data; filesize = atol( xfer_data->size_list->data ); gc = xfer_data->gc; yd = gc->proto_data; /* setting up xfer_data for next file's tranfer */ g_free(xfer_data->host); g_free(xfer_data->path); g_free(xfer_data->txbuf); g_free(xfer_data->rxqueue); g_free(xfer_data->xfer_idstring_for_relay); if (xfer_data->tx_handler) purple_input_remove(xfer_data->tx_handler); xfer_data->host = NULL; xfer_data->host = NULL; xfer_data->port = 0; xfer_data->expires = 0; xfer_data->started = FALSE; xfer_data->txbuf = NULL; xfer_data->txbuflen = 0; xfer_data->txbuf_written = 0; xfer_data->tx_handler = (int) NULL; xfer_data->rxqueue = NULL; xfer_data->rxlen = 0; xfer_data->xfer_idstring_for_relay = NULL; xfer_data->info_val_249 = 0; xfer_data->status_15 = STARTED; xfer_data->firstoflist = FALSE; /* Dereference xfer_data from old xfer */ xfer_old->data = NULL; /* Build the file transfer handle. */ xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, xfer_old->who); if (xfer) { /* Set the info about the incoming file. */ char *utf8_filename = yahoo_string_decode(gc, filename, TRUE); purple_xfer_set_filename(xfer, utf8_filename); g_free(utf8_filename); purple_xfer_set_size(xfer, filesize); xfer->data = xfer_data; /* Setup our I/O op functions */ purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); purple_xfer_set_end_fnc(xfer, yahoo_xfer_end); purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); purple_xfer_set_read_fnc(xfer, yahoo_xfer_read); 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*/ g_hash_table_remove(yd->xfer_peer_idstring_map, xfer_data->xfer_idstring_between_peers); g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_idstring_between_peers, xfer); /* Now perform the request */ purple_xfer_request(xfer); } return; } } if (xfer_data) yahoo_xfer_data_free(xfer_data); xfer_old->data = NULL; } void yahoo_process_p2pfilexfer(PurpleConnection *gc, struct yahoo_packet *pkt) { GSList *l = pkt->hash; char *me = NULL; char *from = NULL; char *service = NULL; char *message = NULL; char *command = NULL; char *imv = NULL; char *unknown = NULL; /* Get all the necessary values from this new packet */ while(l != NULL) { struct yahoo_pair *pair = l->data; switch(pair->key) { case 5: /* Get who the packet is for */ me = pair->value; break; case 4: /* Get who the packet is from */ from = pair->value; break; case 49: /* Get the type of service */ service = pair->value; break; case 14: /* Get the 'message' of the packet */ message = pair->value; break; case 13: /* Get the command associated with this packet */ command = pair->value; break; case 63: /* IMVironment name and version */ imv = pair->value; break; case 64: /* Not sure, but it does vary with initialization of Doodle */ unknown = pair->value; /* So, I'll keep it (for a little while atleast) */ break; } l = l->next; } /* If this packet is an IMVIRONMENT, handle it accordingly */ if(service != NULL && imv != NULL && !strcmp(service, "IMVIRONMENT")) { /* Check for a Doodle packet and handle it accordingly */ if(strstr(imv, "doodle;") != NULL) yahoo_doodle_process(gc, me, from, command, message, imv); /* If an IMVIRONMENT packet comes without a specific imviroment name */ if(!strcmp(imv, ";0")) { /* It is unfortunately time to close all IMVironments with the remote client */ yahoo_doodle_command_got_shutdown(gc, from); } } } void yahoo_process_filetransfer(PurpleConnection *gc, struct yahoo_packet *pkt) { char *from = NULL; char *to = NULL; char *msg = NULL; char *url = NULL; char *imv = NULL; long expires = 0; PurpleXfer *xfer; struct yahoo_data *yd; struct yahoo_xfer_data *xfer_data; char *service = NULL; char *filename = NULL; unsigned long filesize = 0L; GSList *l; yd = gc->proto_data; for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; switch (pair->key) { case 4: from = pair->value; break; case 5: to = pair->value; break; case 14: msg = pair->value; break; case 20: url = pair->value; break; case 38: expires = strtol(pair->value, NULL, 10); break; case 27: filename = pair->value; break; case 28: filesize = atol(pair->value); break; case 49: service = pair->value; break; case 63: imv = pair->value; break; } } /* * The remote user has changed their IMVironment. We * record it for later use. */ if (from && imv && service && (strcmp("IMVIRONMENT", service) == 0)) { g_hash_table_replace(yd->imvironments, g_strdup(from), g_strdup(imv)); return; } if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) { if (service && (strcmp("FILEXFER", service) != 0)) { purple_debug_misc("yahoo", "unhandled service 0x%02x\n", pkt->service); return; } } if (msg) { char *tmp; tmp = strchr(msg, '\006'); if (tmp) *tmp = '\0'; } if (!url || !from) return; /* Setup the Yahoo-specific file transfer data */ xfer_data = g_new0(struct yahoo_xfer_data, 1); xfer_data->gc = gc; if (!purple_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) { g_free(xfer_data); return; } purple_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s, and the full url was %s.\n", xfer_data->host, xfer_data->port, xfer_data->path, url); /* Build the file transfer handle. */ xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, from); if (xfer) { xfer->data = xfer_data; /* Set the info about the incoming file. */ if (filename) { char *utf8_filename = yahoo_string_decode(gc, filename, TRUE); purple_xfer_set_filename(xfer, utf8_filename); g_free(utf8_filename); } else { gchar *start, *end; start = g_strrstr(xfer_data->path, "/"); if (start) start++; end = g_strrstr(xfer_data->path, "?"); if (start && *start && end) { char *utf8_filename; filename = g_strndup(start, end - start); utf8_filename = yahoo_string_decode(gc, filename, TRUE); g_free(filename); purple_xfer_set_filename(xfer, utf8_filename); g_free(utf8_filename); filename = NULL; } } purple_xfer_set_size(xfer, filesize); /* Setup our I/O op functions */ purple_xfer_set_init_fnc(xfer, yahoo_xfer_init); purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); purple_xfer_set_end_fnc(xfer, yahoo_xfer_end); purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); purple_xfer_set_read_fnc(xfer, yahoo_xfer_read); purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); /* Now perform the request */ purple_xfer_request(xfer); } } PurpleXfer *yahoo_new_xfer(PurpleConnection *gc, const char *who) { PurpleXfer *xfer; struct yahoo_xfer_data *xfer_data; g_return_val_if_fail(who != NULL, NULL); xfer_data = g_new0(struct yahoo_xfer_data, 1); xfer_data->gc = gc; /* Build the file transfer handle. */ xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who); if (xfer) { xfer->data = xfer_data; /* Setup our I/O op functions */ purple_xfer_set_init_fnc(xfer, yahoo_xfer_init); purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); purple_xfer_set_end_fnc(xfer, yahoo_xfer_end); purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); purple_xfer_set_read_fnc(xfer, yahoo_xfer_read); purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); } return xfer; } static gchar* yahoo_xfer_new_xfer_id() { gchar *ans; int i,j; ans = g_strnfill(24, ' '); ans[23] = '$'; ans[22] = '$'; for(i = 0; i < 22; i++) { j = g_random_int_range (0,61); if(j < 26) ans[i] = j + 'a'; else if(j < 52) ans[i] = j - 26 + 'A'; else ans[i] = j - 52 + '0'; } return ans; } static void yahoo_xfer_dns_connected_15(GSList *hosts, gpointer data, const char *error_message) { PurpleXfer *xfer; struct yahoo_xfer_data *xd; struct sockaddr_in *addr; struct yahoo_packet *pkt; long actaddr; long a,b,c,d; PurpleConnection *gc; PurpleAccount *account; struct yahoo_data *yd; gchar *url; gchar *filename; if (!(xfer = data)) return; if (!(xd = xfer->data)) return; gc = xd->gc; account = purple_connection_get_account(gc); yd = gc->proto_data; if(!hosts) { purple_debug_error("yahoo", "Unable to find an IP address for relay.msg.yahoo.com\n"); purple_xfer_cancel_remote(xfer); return; } /* Discard the length... */ hosts = g_slist_remove(hosts, hosts->data); if(!hosts) { purple_debug_error("yahoo", "Unable to find an IP address for relay.msg.yahoo.com\n"); purple_xfer_cancel_remote(xfer); return; } /*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; actaddr = (actaddr - d) / 256; c = actaddr % 256; actaddr = (actaddr - c) / 256; b = actaddr % 256; actaddr = (actaddr - b) / 256; a = actaddr; if(yd->jp) xd->port = YAHOOJP_XFER_RELAY_PORT; else xd->port = YAHOO_XFER_RELAY_PORT; url = g_strdup_printf("%ld.%ld.%ld.%ld", d, c, b, a); if (!purple_url_parse(url, &(xd->host), &(xd->port), &(xd->path), NULL, NULL)) { purple_xfer_cancel_remote(xfer); return; } g_free(url); /* Free the address... */ g_free(hosts->data); hosts = g_slist_remove(hosts, hosts->data); addr = NULL; while (hosts != NULL) { /* Discard the length... */ hosts = g_slist_remove(hosts, hosts->data); /* Free the address... */ g_free(hosts->data); hosts = g_slist_remove(hosts, hosts->data); } 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, "ssssis", 1, purple_normalize(account, purple_account_get_username(account)), 5, xfer->who, 265, xd->xfer_idstring_between_peers, 27, filename, 249, 3, 250, xd->host); g_free(filename); 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; int ver = 0; PurpleXfer *xfer = yahoo_new_xfer(gc, who); YahooFriend *yf = yahoo_friend_find(gc, who); /* To determine whether client uses ymsg 15 i.e. client is higher than YM 7 */ if(yf && yf->version_id > 500000) ver=15; g_return_if_fail(xfer != NULL); if(ver == 15) { yd = gc->proto_data; 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_idstring_between_peers = yahoo_xfer_new_xfer_id(); g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_idstring_between_peers, xfer); } /* Now perform the request */ if (file) purple_xfer_request_accepted(xfer, file); else 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_xfer_recv_cb_15(gpointer data, gint source, PurpleInputCondition condition) { PurpleXfer *xfer; struct yahoo_xfer_data *xd; int did; gchar* buf; gchar* t; PurpleAccount *account; PurpleConnection *gc; xfer = data; xd = xfer->data; account = purple_connection_get_account(xd->gc); gc = xd->gc; buf=g_strnfill(1000, 0); while((did = read(source, buf, 998)) > 0) { xd->txbuflen += did; buf[did] = '\0'; t = xd->txbuf; xd->txbuf = g_strconcat(t,buf,NULL); g_free(t); } g_free(buf); if (did < 0 && errno == EAGAIN) return; else if (did < 0) { purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); purple_xfer_cancel_remote(xfer); return; } purple_input_remove(xd->tx_handler); xd->tx_handler = 0; xd->txbuflen = 0; if(xd->status_15 == HEAD_REQUESTED) { xd->status_15 = HEAD_REPLY_RECEIVED; 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) { purple_notify_error(gc, NULL, _("File Transfer Failed"), _("Unable to establish file descriptor.")); purple_xfer_cancel_remote(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_send_cb_15(gpointer data, gint source, PurpleInputCondition condition) { PurpleXfer *xfer; struct yahoo_xfer_data *xd; int remaining, written; xfer = data; xd = xfer->data; remaining = xd->txbuflen - xd->txbuf_written; written = write(source, xd->txbuf + xd->txbuf_written, remaining); if (written < 0 && errno == EAGAIN) written = 0; else if (written <= 0) { purple_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno); purple_xfer_cancel_remote(xfer); return; } if (written < remaining) { xd->txbuf_written += written; return; } purple_input_remove(xd->tx_handler); xd->tx_handler = 0; g_free(xd->txbuf); xd->txbuf = NULL; xd->txbuflen = 0; 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); } else if(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE && xd->status_15 == HEAD_REPLY_RECEIVED) { xd->status_15 = TRANSFER_PHASE; xfer->fd = source; purple_xfer_start(xfer, source, NULL, 0); } else if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && xd->status_15 == ACCEPTED) { xd->status_15 = TRANSFER_PHASE; xfer->fd = source; purple_xfer_start(xfer, source, NULL, 0); } 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; struct yahoo_xfer_data *xd; PurpleAccount *account; struct yahoo_data* yd; if (!(xfer = data)) return; if (!(xd = xfer->data)) return; yd = xd->gc->proto_data; account = purple_connection_get_account(xd->gc); if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) { purple_xfer_error(PURPLE_XFER_RECEIVE, purple_xfer_get_account(xfer), xfer->who, _("Unable to connect.")); purple_xfer_cancel_remote(xfer); return; } /* The first time we get here, assemble the tx buffer */ if (xd->txbuflen == 0) { gchar* cookies; 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", 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", 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", purple_url_encode(xd->xfer_idstring_for_relay), purple_normalize(account, purple_account_get_username(account)), xfer->who, cookies, xd->host); } else { purple_debug_error("yahoo", "Unrecognized yahoo file transfer mode and stage (ymsg15):%d,%d\n", purple_xfer_get_type(xfer), xd->status_15); g_free(cookies); return; } xd->txbuflen = strlen(xd->txbuf); xd->txbuf_written = 0; g_free(cookies); } 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); } } void yahoo_process_filetrans_15(PurpleConnection *gc, struct yahoo_packet *pkt) { char *from = NULL; char *to = NULL; char *imv = NULL; long val_222 = 0L; PurpleXfer *xfer; struct yahoo_data *yd; struct yahoo_xfer_data *xfer_data; char *service = NULL; char *filename = NULL; char *xfer_idstring_between_peers = NULL; unsigned long filesize = 0L; GSList *l; GSList *filename_list = NULL; GSList *size_list = NULL; int nooffiles = 0; yd = gc->proto_data; for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; switch (pair->key) { case 4: from = pair->value; break; case 5: to = pair->value; break; case 265: xfer_idstring_between_peers = pair->value; break; case 27: filename_list = g_slist_prepend(filename_list, g_strdup(pair->value)); nooffiles++; break; case 28: size_list = g_slist_prepend(size_list, g_strdup(pair->value)); break; case 222: val_222 = atol(pair->value); /* 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.*/ case 49: service = pair->value; break; case 63: imv = pair->value; break; /*end check*/ } } if(!xfer_idstring_between_peers) return; if(val_222 == 2 || val_222 == 4) { xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_idstring_between_peers); if(!xfer) return; purple_xfer_cancel_remote(xfer); return; } if(val_222 == 3) { xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_idstring_between_peers); if(!xfer) return; /* * In the file trans info packet tht we must reply with , we are supposed to mention the ip address... * purple connect does not give me a way of finding the ip address... * so, purple dnsquery is used... but retries, trying with next ip address etc. is not implemented..TODO */ if (yd->jp) { purple_dnsquery_a(YAHOOJP_XFER_RELAY_HOST, YAHOOJP_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer); } else { purple_dnsquery_a(YAHOO_XFER_RELAY_HOST, YAHOO_XFER_RELAY_PORT, yahoo_xfer_dns_connected_15, xfer); } 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.*/ /* * The remote user has changed their IMVironment. We * record it for later use. */ if (from && imv && service && (strcmp("IMVIRONMENT", service) == 0)) { g_hash_table_replace(yd->imvironments, g_strdup(from), g_strdup(imv)); return; } if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) { if (service && (strcmp("FILEXFER", service) != 0)) { purple_debug_misc("yahoo", "unhandled service 0x%02x\n", pkt->service); return; } } /*end processing*/ if(!filename_list) return; /* have to change list into order in which client at other end sends */ filename_list = g_slist_reverse(filename_list); size_list = g_slist_reverse(size_list); filename = filename_list->data; filesize = atol(size_list->data); if(!from) return; xfer_data = g_new0(struct yahoo_xfer_data, 1); xfer_data->version = 15; xfer_data->firstoflist = TRUE; xfer_data->gc = gc; xfer_data->xfer_idstring_between_peers = g_strdup(xfer_idstring_between_peers); xfer_data->filename_list = filename_list; xfer_data->size_list = size_list; /* Build the file transfer handle. */ xfer = purple_xfer_new(gc->account, PURPLE_XFER_RECEIVE, from); xfer->message = NULL; if (xfer) { /* Set the info about the incoming file. */ char *utf8_filename = yahoo_string_decode(gc, filename, TRUE); purple_xfer_set_filename(xfer, utf8_filename); g_free(utf8_filename); purple_xfer_set_size(xfer, filesize); xfer->data = xfer_data; /* Setup our I/O op functions */ purple_xfer_set_init_fnc(xfer, yahoo_xfer_init_15); purple_xfer_set_start_fnc(xfer, yahoo_xfer_start); purple_xfer_set_end_fnc(xfer, yahoo_xfer_end); purple_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); purple_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); purple_xfer_set_read_fnc(xfer, yahoo_xfer_read); purple_xfer_set_write_fnc(xfer, yahoo_xfer_write); purple_xfer_set_request_denied_fnc(xfer,yahoo_xfer_cancel_recv); g_hash_table_insert(yd->xfer_peer_idstring_map, xfer_data->xfer_idstring_between_peers, xfer); if(nooffiles > 1) { gchar* message; message = g_strdup_printf(_("%s is trying to send you a group of %d files.\n"), xfer->who, nooffiles); purple_xfer_conversation_write(xfer, message, FALSE); g_free(message); } /* Now perform the request */ purple_xfer_request(xfer); } } void yahoo_process_filetrans_info_15(PurpleConnection *gc, struct yahoo_packet *pkt) { char *from = NULL; char *to = NULL; char *url = NULL; long val_249 = 0; long val_66 = 0; PurpleXfer *xfer; struct yahoo_data *yd; struct yahoo_xfer_data *xfer_data; char *filename = NULL; char *xfer_idstring_between_peers = NULL; char *xfer_idstring_for_relay = NULL; GSList *l; struct yahoo_packet *pkt_to_send; PurpleAccount *account; yd = gc->proto_data; for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; switch (pair->key) { case 4: from = pair->value; break; case 5: to = pair->value; break; case 265: xfer_idstring_between_peers = pair->value; break; case 27: filename = pair->value; break; case 66: 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. */ break; case 250: url = pair->value; break; case 251: xfer_idstring_for_relay = pair->value; break; } } if(!xfer_idstring_between_peers) return; xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_idstring_between_peers); if(!xfer) return; if(val_66==-1) { purple_xfer_cancel_remote(xfer); return; } xfer_data = xfer->data; 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; } 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_idstring_between_peers, 27, xfer->filename, 249, xfer_data->info_val_249, 251, xfer_data->xfer_idstring_for_relay, 222, 3); 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*/ void yahoo_process_filetrans_acc_15(PurpleConnection *gc, struct yahoo_packet *pkt) { gchar *xfer_idstring_between_peers = NULL; gchar *xfer_idstring_for_relay = NULL; PurpleXfer *xfer; struct yahoo_data *yd; struct yahoo_xfer_data *xfer_data; GSList *l; PurpleAccount *account; long val_66 = 0; yd = gc->proto_data; for (l = pkt->hash; l; l = l->next) { struct yahoo_pair *pair = l->data; switch (pair->key) { case 251: xfer_idstring_for_relay = pair->value; break; case 265: xfer_idstring_between_peers = pair->value; break; case 66: val_66 = atol(pair->value); } } xfer = g_hash_table_lookup(yd->xfer_peer_idstring_map, xfer_idstring_between_peers); if(!xfer) return; if(val_66 == -1 || !(xfer_idstring_for_relay)) { purple_xfer_cancel_remote(xfer); return; } xfer_data = xfer->data; xfer_data->xfer_idstring_for_relay = g_strdup(xfer_idstring_for_relay); xfer_data->status_15 = ACCEPTED; account = purple_connection_get_account(gc); 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 connect")); purple_xfer_cancel_remote(xfer); } }