comparison libgaim/protocols/yahoo/yahoo_filexfer.c @ 14192:60b1bc8dbf37

[gaim-migrate @ 16863] Renamed 'core' to 'libgaim' committer: Tailor Script <tailor@pidgin.im>
author Evan Schoenberg <evan.s@dreskin.net>
date Sat, 19 Aug 2006 01:50:10 +0000
parents
children 1bee09450652
comparison
equal deleted inserted replaced
14191:009db0b357b5 14192:60b1bc8dbf37
1 /*
2 * @file yahoo_filexfer.c Yahoo Filetransfer
3 *
4 * Gaim is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include "prpl.h"
24 #include "internal.h"
25 #include "util.h"
26 #include "debug.h"
27 #include "notify.h"
28 #include "proxy.h"
29 #include "ft.h"
30 #include "yahoo.h"
31 #include "yahoo_packet.h"
32 #include "yahoo_filexfer.h"
33 #include "yahoo_doodle.h"
34
35 struct yahoo_xfer_data {
36 gchar *host;
37 gchar *path;
38 int port;
39 GaimConnection *gc;
40 long expires;
41 gboolean started;
42 gchar *txbuf;
43 gsize txbuflen;
44 gsize txbuf_written;
45 guint tx_handler;
46 gchar *rxqueue;
47 guint rxlen;
48 };
49
50 static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd)
51 {
52 g_free(xd->host);
53 g_free(xd->path);
54 g_free(xd->txbuf);
55 if (xd->tx_handler)
56 gaim_input_remove(xd->tx_handler);
57 g_free(xd);
58 }
59
60 static void yahoo_receivefile_send_cb(gpointer data, gint source, GaimInputCondition condition)
61 {
62 GaimXfer *xfer;
63 struct yahoo_xfer_data *xd;
64 int remaining, written;
65
66 xfer = data;
67 xd = xfer->data;
68
69 remaining = xd->txbuflen - xd->txbuf_written;
70 written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining);
71
72 if (written < 0 && errno == EAGAIN)
73 written = 0;
74 else if (written <= 0) {
75 gaim_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
76 gaim_xfer_cancel_remote(xfer);
77 return;
78 }
79
80 if (written < remaining) {
81 xd->txbuf_written += written;
82 return;
83 }
84
85 gaim_input_remove(xd->tx_handler);
86 xd->tx_handler = 0;
87 g_free(xd->txbuf);
88 xd->txbuf = NULL;
89 xd->txbuflen = 0;
90
91 gaim_xfer_start(xfer, source, NULL, 0);
92
93 }
94
95 static void yahoo_receivefile_connected(gpointer data, gint source, const gchar *error_message)
96 {
97 GaimXfer *xfer;
98 struct yahoo_xfer_data *xd;
99
100 gaim_debug(GAIM_DEBUG_INFO, "yahoo",
101 "AAA - in yahoo_receivefile_connected\n");
102 if (!(xfer = data))
103 return;
104 if (!(xd = xfer->data))
105 return;
106 if ((source < 0) || (xd->path == NULL) || (xd->host == NULL)) {
107 gaim_xfer_error(GAIM_XFER_RECEIVE, gaim_xfer_get_account(xfer),
108 xfer->who, _("Unable to connect."));
109 gaim_xfer_cancel_remote(xfer);
110 return;
111 }
112
113 xfer->fd = source;
114
115 /* The first time we get here, assemble the tx buffer */
116 if (xd->txbuflen == 0) {
117 xd->txbuf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
118 xd->path, xd->host);
119 xd->txbuflen = strlen(xd->txbuf);
120 xd->txbuf_written = 0;
121 }
122
123 if (!xd->tx_handler)
124 {
125 xd->tx_handler = gaim_input_add(source, GAIM_INPUT_WRITE,
126 yahoo_receivefile_send_cb, xfer);
127 yahoo_receivefile_send_cb(xfer, source, GAIM_INPUT_WRITE);
128 }
129 }
130
131 static void yahoo_sendfile_send_cb(gpointer data, gint source, GaimInputCondition condition)
132 {
133 GaimXfer *xfer;
134 struct yahoo_xfer_data *xd;
135 int written, remaining;
136
137 xfer = data;
138 xd = xfer->data;
139
140 remaining = xd->txbuflen - xd->txbuf_written;
141 written = write(xfer->fd, xd->txbuf + xd->txbuf_written, remaining);
142
143 if (written < 0 && errno == EAGAIN)
144 written = 0;
145 else if (written <= 0) {
146 gaim_debug_error("yahoo", "Unable to write in order to start ft errno = %d\n", errno);
147 gaim_xfer_cancel_remote(xfer);
148 return;
149 }
150
151 if (written < remaining) {
152 xd->txbuf_written += written;
153 return;
154 }
155
156 gaim_input_remove(xd->tx_handler);
157 xd->tx_handler = 0;
158 g_free(xd->txbuf);
159 xd->txbuf = NULL;
160 xd->txbuflen = 0;
161
162 gaim_xfer_start(xfer, source, NULL, 0);
163 }
164
165 static void yahoo_sendfile_connected(gpointer data, gint source, const gchar *error_message)
166 {
167 GaimXfer *xfer;
168 struct yahoo_xfer_data *xd;
169 struct yahoo_packet *pkt;
170 gchar *size, *filename, *encoded_filename, *header;
171 guchar *pkt_buf;
172 const char *host;
173 int port;
174 size_t content_length, header_len, pkt_buf_len;
175 GaimConnection *gc;
176 GaimAccount *account;
177 struct yahoo_data *yd;
178
179 gaim_debug(GAIM_DEBUG_INFO, "yahoo",
180 "AAA - in yahoo_sendfile_connected\n");
181 if (!(xfer = data))
182 return;
183 if (!(xd = xfer->data))
184 return;
185
186 if (source < 0) {
187 gaim_xfer_error(GAIM_XFER_RECEIVE, gaim_xfer_get_account(xfer),
188 xfer->who, _("Unable to connect."));
189 gaim_xfer_cancel_remote(xfer);
190 return;
191 }
192
193 xfer->fd = source;
194
195 /* Assemble the tx buffer */
196 gc = xd->gc;
197 account = gaim_connection_get_account(gc);
198 yd = gc->proto_data;
199
200 pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER,
201 YAHOO_STATUS_AVAILABLE, yd->session_id);
202
203 size = g_strdup_printf("%" G_GSIZE_FORMAT, gaim_xfer_get_size(xfer));
204 filename = g_path_get_basename(gaim_xfer_get_local_filename(xfer));
205 encoded_filename = yahoo_string_encode(gc, filename, NULL);
206
207 yahoo_packet_hash(pkt, "sssss", 0, gaim_connection_get_display_name(gc),
208 5, xfer->who, 14, "", 27, encoded_filename, 28, size);
209 g_free(size);
210 g_free(encoded_filename);
211 g_free(filename);
212
213 content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
214
215 pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, &pkt_buf);
216 yahoo_packet_free(pkt);
217
218 host = gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST);
219 port = gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT);
220 header = g_strdup_printf(
221 "POST http://%s:%d/notifyft HTTP/1.0\r\n"
222 "Content-length: %" G_GSIZE_FORMAT "\r\n"
223 "Host: %s:%d\r\n"
224 "Cookie: Y=%s; T=%s\r\n"
225 "\r\n",
226 host, port, content_length + 4 + gaim_xfer_get_size(xfer),
227 host, port, yd->cookie_y, yd->cookie_t);
228
229 header_len = strlen(header);
230
231 xd->txbuflen = header_len + pkt_buf_len + 4;
232 xd->txbuf = g_malloc(xd->txbuflen);
233
234 memcpy(xd->txbuf, header, header_len);
235 g_free(header);
236 memcpy(xd->txbuf + header_len, pkt_buf, pkt_buf_len);
237 g_free(pkt_buf);
238 memcpy(xd->txbuf + header_len + pkt_buf_len, "29\xc0\x80", 4);
239
240 xd->txbuf_written = 0;
241
242 if (xd->tx_handler == 0)
243 {
244 xd->tx_handler = gaim_input_add(source, GAIM_INPUT_WRITE,
245 yahoo_sendfile_send_cb, xfer);
246 yahoo_sendfile_send_cb(xfer, source, GAIM_INPUT_WRITE);
247 }
248 }
249
250 static void yahoo_xfer_init(GaimXfer *xfer)
251 {
252 struct yahoo_xfer_data *xfer_data;
253 GaimConnection *gc;
254 GaimAccount *account;
255 struct yahoo_data *yd;
256
257 xfer_data = xfer->data;
258 gc = xfer_data->gc;
259 yd = gc->proto_data;
260 account = gaim_connection_get_account(gc);
261
262 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
263 if (yd->jp) {
264 if (gaim_proxy_connect(account, gaim_account_get_string(account, "xferjp_host", YAHOOJP_XFER_HOST),
265 gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT),
266 yahoo_sendfile_connected, xfer) == NULL)
267 {
268 gaim_notify_error(gc, NULL, _("File Transfer Failed"),
269 _("Unable to establish file descriptor."));
270 gaim_xfer_cancel_remote(xfer);
271 }
272 } else {
273 if (gaim_proxy_connect(account, gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST),
274 gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT),
275 yahoo_sendfile_connected, xfer) == NULL)
276 {
277 gaim_notify_error(gc, NULL, _("File Transfer Failed"),
278 _("Unable to establish file descriptor."));
279 gaim_xfer_cancel_remote(xfer);
280 }
281 }
282 } else {
283 /* TODO: Using xfer->fd like this is probably a bad thing... */
284 if (gaim_proxy_connect(account, xfer_data->host, xfer_data->port,
285 yahoo_receivefile_connected, xfer) == NULL)
286 xfer->fd = -1;
287 else
288 xfer->fd = 0;
289 if (xfer->fd == -1) {
290 gaim_notify_error(gc, NULL, _("File Transfer Failed"),
291 _("Unable to establish file descriptor."));
292 gaim_xfer_cancel_remote(xfer);
293 }
294 }
295 }
296
297 static void yahoo_xfer_start(GaimXfer *xfer)
298 {
299 /* We don't need to do anything here, do we? */
300 }
301
302 static void yahoo_xfer_end(GaimXfer *xfer)
303 {
304 struct yahoo_xfer_data *xfer_data;
305
306 xfer_data = xfer->data;
307
308 if (xfer_data)
309 yahoo_xfer_data_free(xfer_data);
310 xfer->data = NULL;
311
312 }
313
314 static guint calculate_length(const gchar *l, size_t len)
315 {
316 int i;
317
318 for (i = 0; i < len; i++) {
319 if (!g_ascii_isdigit(l[i]))
320 continue;
321 return strtol(l + i, NULL, 10);
322 }
323 return 0;
324 }
325
326 static gssize yahoo_xfer_read(guchar **buffer, GaimXfer *xfer)
327 {
328 gchar buf[4096];
329 gssize len;
330 gchar *start = NULL;
331 gchar *length;
332 gchar *end;
333 int filelen;
334 struct yahoo_xfer_data *xd = xfer->data;
335
336 if (gaim_xfer_get_type(xfer) != GAIM_XFER_RECEIVE) {
337 return 0;
338 }
339
340 len = read(xfer->fd, buf, sizeof(buf));
341
342 if (len <= 0) {
343 if ((gaim_xfer_get_size(xfer) > 0) &&
344 (gaim_xfer_get_bytes_sent(xfer) >= gaim_xfer_get_size(xfer))) {
345 gaim_xfer_set_completed(xfer, TRUE);
346 return 0;
347 } else
348 return -1;
349 }
350
351 if (!xd->started) {
352 xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen);
353 memcpy(xd->rxqueue + xd->rxlen, buf, len);
354 xd->rxlen += len;
355
356 length = g_strstr_len(xd->rxqueue, len, "Content-length:");
357 /* some proxies re-write this header, changing the capitalization :(
358 * technically that's allowed since headers are case-insensitive
359 * [RFC 2616, section 4.2] */
360 if (length == NULL)
361 length = g_strstr_len(xd->rxqueue, len, "Content-Length:");
362 if (length) {
363 end = g_strstr_len(length, length - xd->rxqueue, "\r\n");
364 if (!end)
365 return 0;
366 if ((filelen = calculate_length(length, len - (length - xd->rxqueue))))
367 gaim_xfer_set_size(xfer, filelen);
368 }
369 start = g_strstr_len(xd->rxqueue, len, "\r\n\r\n");
370 if (start)
371 start += 4;
372 if (!start || start > (xd->rxqueue + len))
373 return 0;
374 xd->started = TRUE;
375
376 len -= (start - xd->rxqueue);
377
378 *buffer = g_malloc(len);
379 memcpy(*buffer, start, len);
380 g_free(xd->rxqueue);
381 xd->rxqueue = NULL;
382 xd->rxlen = 0;
383 } else {
384 *buffer = g_malloc(len);
385 memcpy(*buffer, buf, len);
386 }
387
388 return len;
389 }
390
391 static gssize yahoo_xfer_write(const guchar *buffer, size_t size, GaimXfer *xfer)
392 {
393 gssize len;
394 struct yahoo_xfer_data *xd = xfer->data;
395
396 if (!xd)
397 return -1;
398
399 if (gaim_xfer_get_type(xfer) != GAIM_XFER_SEND) {
400 return -1;
401 }
402
403 len = write(xfer->fd, buffer, size);
404
405 if (len == -1) {
406 if (gaim_xfer_get_bytes_sent(xfer) >= gaim_xfer_get_size(xfer))
407 gaim_xfer_set_completed(xfer, TRUE);
408 if ((errno != EAGAIN) && (errno != EINTR))
409 return -1;
410 return 0;
411 }
412
413 if ((gaim_xfer_get_bytes_sent(xfer) + len) >= gaim_xfer_get_size(xfer))
414 gaim_xfer_set_completed(xfer, TRUE);
415
416 return len;
417 }
418
419 static void yahoo_xfer_cancel_send(GaimXfer *xfer)
420 {
421 struct yahoo_xfer_data *xfer_data;
422
423 xfer_data = xfer->data;
424
425 if (xfer_data)
426 yahoo_xfer_data_free(xfer_data);
427 xfer->data = NULL;
428 }
429
430 static void yahoo_xfer_cancel_recv(GaimXfer *xfer)
431 {
432 struct yahoo_xfer_data *xfer_data;
433
434 xfer_data = xfer->data;
435
436 if (xfer_data)
437 yahoo_xfer_data_free(xfer_data);
438 xfer->data = NULL;
439 }
440
441 void yahoo_process_p2pfilexfer(GaimConnection *gc, struct yahoo_packet *pkt)
442 {
443 GSList *l = pkt->hash;
444
445 char *me = NULL;
446 char *from = NULL;
447 char *service = NULL;
448 char *message = NULL;
449 char *command = NULL;
450 char *imv = NULL;
451 char *unknown = NULL;
452
453 /* Get all the necessary values from this new packet */
454 while(l != NULL)
455 {
456 struct yahoo_pair *pair = l->data;
457
458 if(pair->key == 5) /* Get who the packet is for */
459 me = pair->value;
460
461 if(pair->key == 4) /* Get who the packet is from */
462 from = pair->value;
463
464 if(pair->key == 49) /* Get the type of service */
465 service = pair->value;
466
467 if(pair->key == 14) /* Get the 'message' of the packet */
468 message = pair->value;
469
470 if(pair->key == 13) /* Get the command associated with this packet */
471 command = pair->value;
472
473 if(pair->key == 63) /* IMVironment name and version */
474 imv = pair->value;
475
476 if(pair->key == 64) /* Not sure, but it does vary with initialization of Doodle */
477 unknown = pair->value; /* So, I'll keep it (for a little while atleast) */
478
479 l = l->next;
480 }
481
482 /* If this packet is an IMVIRONMENT, handle it accordingly */
483 if(service != NULL && imv != NULL && !strcmp(service, "IMVIRONMENT"))
484 {
485 /* Check for a Doodle packet and handle it accordingly */
486 if(!strcmp(imv, "doodle;11"))
487 yahoo_doodle_process(gc, me, from, command, message);
488
489 /* If an IMVIRONMENT packet comes without a specific imviroment name */
490 if(!strcmp(imv, ";0"))
491 {
492 /* It is unfortunately time to close all IMVironments with the remote client */
493 yahoo_doodle_command_got_shutdown(gc, from);
494 }
495 }
496 }
497
498 void yahoo_process_filetransfer(GaimConnection *gc, struct yahoo_packet *pkt)
499 {
500 char *from = NULL;
501 char *to = NULL;
502 char *msg = NULL;
503 char *url = NULL;
504 char *imv = NULL;
505 long expires = 0;
506 GaimXfer *xfer;
507 struct yahoo_data *yd;
508 struct yahoo_xfer_data *xfer_data;
509 char *service = NULL;
510 char *filename = NULL;
511 unsigned long filesize = 0L;
512 GSList *l;
513
514 yd = gc->proto_data;
515
516 for (l = pkt->hash; l; l = l->next) {
517 struct yahoo_pair *pair = l->data;
518
519 if (pair->key == 4)
520 from = pair->value;
521 if (pair->key == 5)
522 to = pair->value;
523 if (pair->key == 14)
524 msg = pair->value;
525 if (pair->key == 20)
526 url = pair->value;
527 if (pair->key == 38)
528 expires = strtol(pair->value, NULL, 10);
529 if (pair->key == 27)
530 filename = pair->value;
531 if (pair->key == 28)
532 filesize = atol(pair->value);
533 if (pair->key == 49)
534 service = pair->value;
535 if (pair->key == 63)
536 imv = pair->value;
537 }
538
539 /*
540 * The remote user has changed their IMVironment. We
541 * record it for later use.
542 */
543 if (from && imv && service && (strcmp("IMVIRONMENT", service) == 0)) {
544 g_hash_table_replace(yd->imvironments, g_strdup(from), g_strdup(imv));
545 return;
546 }
547
548 if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) {
549 if (service && (strcmp("FILEXFER", service) != 0)) {
550 gaim_debug_misc("yahoo", "unhandled service 0x%02x\n", pkt->service);
551 return;
552 }
553 }
554
555 if (msg) {
556 char *tmp;
557 tmp = strchr(msg, '\006');
558 if (tmp)
559 *tmp = '\0';
560 }
561
562 if (!url || !from)
563 return;
564
565 /* Setup the Yahoo-specific file transfer data */
566 xfer_data = g_new0(struct yahoo_xfer_data, 1);
567 xfer_data->gc = gc;
568 if (!gaim_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path), NULL, NULL)) {
569 g_free(xfer_data);
570 return;
571 }
572
573 gaim_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s, and the full url was %s.\n",
574 xfer_data->host, xfer_data->port, xfer_data->path, url);
575
576 /* Build the file transfer handle. */
577 xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, from);
578 xfer->data = xfer_data;
579
580 /* Set the info about the incoming file. */
581 if (filename) {
582 char *utf8_filename = yahoo_string_decode(gc, filename, TRUE);
583 gaim_xfer_set_filename(xfer, utf8_filename);
584 g_free(utf8_filename);
585 } else {
586 gchar *start, *end;
587 start = g_strrstr(xfer_data->path, "/");
588 if (start)
589 start++;
590 end = g_strrstr(xfer_data->path, "?");
591 if (start && *start && end) {
592 char *utf8_filename;
593 filename = g_strndup(start, end - start);
594 utf8_filename = yahoo_string_decode(gc, filename, TRUE);
595 g_free(filename);
596 gaim_xfer_set_filename(xfer, utf8_filename);
597 g_free(utf8_filename);
598 filename = NULL;
599 }
600 }
601
602 gaim_xfer_set_size(xfer, filesize);
603
604 /* Setup our I/O op functions */
605 gaim_xfer_set_init_fnc(xfer, yahoo_xfer_init);
606 gaim_xfer_set_start_fnc(xfer, yahoo_xfer_start);
607 gaim_xfer_set_end_fnc(xfer, yahoo_xfer_end);
608 gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
609 gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
610 gaim_xfer_set_read_fnc(xfer, yahoo_xfer_read);
611 gaim_xfer_set_write_fnc(xfer, yahoo_xfer_write);
612
613 /* Now perform the request */
614 gaim_xfer_request(xfer);
615 }
616
617 GaimXfer *yahoo_new_xfer(GaimConnection *gc, const char *who)
618 {
619 GaimXfer *xfer;
620 struct yahoo_xfer_data *xfer_data;
621
622 g_return_val_if_fail(who != NULL, NULL);
623
624 xfer_data = g_new0(struct yahoo_xfer_data, 1);
625 xfer_data->gc = gc;
626
627 /* Build the file transfer handle. */
628 xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who);
629 xfer->data = xfer_data;
630
631 /* Setup our I/O op functions */
632 gaim_xfer_set_init_fnc(xfer, yahoo_xfer_init);
633 gaim_xfer_set_start_fnc(xfer, yahoo_xfer_start);
634 gaim_xfer_set_end_fnc(xfer, yahoo_xfer_end);
635 gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send);
636 gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv);
637 gaim_xfer_set_read_fnc(xfer, yahoo_xfer_read);
638 gaim_xfer_set_write_fnc(xfer, yahoo_xfer_write);
639
640 return xfer;
641 }
642
643 void yahoo_send_file(GaimConnection *gc, const char *who, const char *file)
644 {
645 GaimXfer *xfer = yahoo_new_xfer(gc, who);
646
647 g_return_if_fail(xfer != NULL);
648
649 /* Now perform the request */
650 if (file)
651 gaim_xfer_request_accepted(xfer, file);
652 else
653 gaim_xfer_request(xfer);
654 }