Mercurial > pidgin
annotate src/protocols/yahoo/yahoo_filexfer.c @ 8282:ab35a0bec13a
[gaim-migrate @ 9006]
" This works around the crash on trying to send a file
that's too big, by giving an error. It also makes Gaim
not crash on canceling the send right there.
It doesn't fix the crash on the server doing whatever
it did to make us crash. Someone should send me a
backtrace of trying to send say a 2meg file without
this patch." --Tim Ringenbach
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Tue, 17 Feb 2004 18:45:25 +0000 |
parents | f50c059b6384 |
children | 76125b842b23 |
rev | line source |
---|---|
7651 | 1 /* |
2 * @file yahoo_filexfer.c Yahoo Filetransfer | |
3 * | |
8046 | 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. | |
7651 | 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_filexfer.h" | |
32 | |
33 | |
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 *rxqueue; | |
43 guint rxlen; | |
44 }; | |
45 | |
46 static void yahoo_xfer_data_free(struct yahoo_xfer_data *xd) | |
47 { | |
48 if (xd->host) | |
49 g_free(xd->host); | |
50 if (xd->path) | |
51 g_free(xd->path); | |
52 g_free(xd); | |
53 } | |
54 | |
55 static void yahoo_receivefile_connected(gpointer data, gint source, GaimInputCondition condition) | |
56 { | |
57 GaimXfer *xfer; | |
58 struct yahoo_xfer_data *xd; | |
59 gchar *buf; | |
60 | |
61 gaim_debug(GAIM_DEBUG_INFO, "yahoo", | |
62 "AAA - in yahoo_receivefile_connected\n"); | |
63 if (!(xfer = data)) | |
64 return; | |
65 if (!(xd = xfer->data)) | |
66 return; | |
67 if (source < 0) { | |
68 gaim_xfer_error(GAIM_XFER_RECEIVE, xfer->who, _("Unable to connect.")); | |
7805 | 69 gaim_xfer_cancel_remote(xfer); |
7651 | 70 return; |
71 } | |
72 | |
73 xfer->fd = source; | |
74 gaim_xfer_start(xfer, source, NULL, 0); | |
75 | |
76 buf = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", xd->path, xd->host); | |
77 write(xfer->fd, buf, strlen(buf)); | |
78 g_free(buf); | |
79 | |
80 return; | |
81 } | |
82 | |
83 static int yahoo_send_packet_special(int fd, struct yahoo_packet *pkt, int pad) | |
84 { | |
85 int pktlen = yahoo_packet_length(pkt); | |
86 int len = YAHOO_PACKET_HDRLEN + pktlen; | |
87 int ret; | |
88 | |
89 guchar *data; | |
90 int pos = 0; | |
91 | |
92 if (fd < 0) | |
93 return -1; | |
94 | |
95 data = g_malloc0(len + 1); | |
96 | |
97 memcpy(data + pos, "YMSG", 4); pos += 4; | |
98 pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); | |
99 pos += yahoo_put16(data + pos, 0x0000); | |
100 pos += yahoo_put16(data + pos, pktlen + pad); | |
101 pos += yahoo_put16(data + pos, pkt->service); | |
102 pos += yahoo_put32(data + pos, pkt->status); | |
103 pos += yahoo_put32(data + pos, pkt->id); | |
104 | |
105 yahoo_packet_write(pkt, data + pos); | |
106 | |
107 ret = write(fd, data, len); | |
108 g_free(data); | |
109 | |
110 return ret; | |
111 } | |
112 | |
113 static void yahoo_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) | |
114 { | |
115 GaimXfer *xfer; | |
116 struct yahoo_xfer_data *xd; | |
117 struct yahoo_packet *pkt; | |
118 gchar *size, *post, *buf; | |
119 int content_length; | |
120 GaimConnection *gc; | |
121 GaimAccount *account; | |
122 struct yahoo_data *yd; | |
7805 | 123 char *filename; |
7651 | 124 |
125 gaim_debug(GAIM_DEBUG_INFO, "yahoo", | |
126 "AAA - in yahoo_sendfile_connected\n"); | |
127 if (!(xfer = data)) | |
128 return; | |
129 if (!(xd = xfer->data)) | |
130 return; | |
131 | |
132 gc = xd->gc; | |
133 account = gaim_connection_get_account(gc); | |
134 yd = gc->proto_data; | |
135 | |
136 | |
137 | |
138 if (source < 0) { | |
139 gaim_xfer_error(GAIM_XFER_RECEIVE, xfer->who, _("Unable to connect.")); | |
7805 | 140 gaim_xfer_cancel_remote(xfer); |
7651 | 141 return; |
142 } | |
143 | |
144 xfer->fd = source; | |
145 gaim_xfer_start(xfer, source, NULL, 0); | |
146 | |
147 | |
148 pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id); | |
149 | |
150 size = g_strdup_printf("%d", gaim_xfer_get_size(xfer)); | |
151 | |
152 yahoo_packet_hash(pkt, 0, gaim_connection_get_display_name(gc)); | |
153 yahoo_packet_hash(pkt, 5, xfer->who); | |
154 yahoo_packet_hash(pkt, 14, ""); | |
7805 | 155 filename = g_path_get_basename(gaim_xfer_get_local_filename(xfer)); |
156 yahoo_packet_hash(pkt, 27, filename); | |
7651 | 157 yahoo_packet_hash(pkt, 28, size); |
158 | |
159 content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); | |
160 | |
161 buf = g_strdup_printf("Y=%s; T=%s", yd->cookie_y, yd->cookie_t); | |
162 | |
163 post = g_strdup_printf("POST /notifyft HTTP/1.0\r\n" | |
164 "Content-length: %d\r\n" | |
165 "Host: %s:%d\r\n" | |
166 "Cookie: %s\r\n" | |
167 "\r\n", | |
168 content_length + 4 + gaim_xfer_get_size(xfer), | |
169 gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST), | |
170 gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT), | |
171 buf); | |
172 write(xfer->fd, post, strlen(post)); | |
173 | |
174 yahoo_send_packet_special(xfer->fd, pkt, 8); | |
175 yahoo_packet_free(pkt); | |
176 | |
177 write(xfer->fd, "29\xc0\x80", 4); | |
178 | |
179 g_free(size); | |
180 g_free(post); | |
181 g_free(buf); | |
7805 | 182 g_free(filename); |
7651 | 183 } |
184 | |
185 static void yahoo_xfer_init(GaimXfer *xfer) | |
186 { | |
187 struct yahoo_xfer_data *xfer_data; | |
188 GaimConnection *gc; | |
189 GaimAccount *account; | |
7827 | 190 struct yahoo_data *yd; |
7651 | 191 |
192 xfer_data = xfer->data; | |
193 gc = xfer_data->gc; | |
7827 | 194 yd = gc->proto_data; |
7651 | 195 account = gaim_connection_get_account(gc); |
196 | |
197 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { | |
8282 | 198 if (gaim_xfer_get_size(xfer) >= 1048577) { |
7651 | 199 gaim_notify_error(gc, NULL, _("File Transfer Aborted"), |
8282 | 200 _("Gaim cannot send files over Yahoo! that are bigger than " |
201 "One Megabyte (1,048,576 bytes).")); | |
202 gaim_xfer_cancel_local(xfer); | |
203 } else { | |
204 if (gaim_proxy_connect(account, gaim_account_get_string(account, "xfer_host", YAHOO_XFER_HOST), | |
205 gaim_account_get_int(account, "xfer_port", YAHOO_XFER_PORT), | |
206 yahoo_sendfile_connected, xfer) == -1) | |
207 { | |
208 gaim_notify_error(gc, NULL, _("File Transfer Aborted"), | |
209 _("Unable to establish file descriptor.")); | |
210 gaim_xfer_cancel_remote(xfer); | |
211 } | |
7651 | 212 } |
213 } else { | |
214 xfer->fd = gaim_proxy_connect(account, xfer_data->host, xfer_data->port, | |
215 yahoo_receivefile_connected, xfer); | |
216 if (xfer->fd == -1) { | |
217 gaim_notify_error(gc, NULL, _("File Transfer Aborted"), | |
218 _("Unable to establish file descriptor.")); | |
219 gaim_xfer_cancel_remote(xfer); | |
220 } | |
221 } | |
222 } | |
223 | |
224 static void yahoo_xfer_start(GaimXfer *xfer) | |
225 { | |
226 /* We don't need to do anything here, do we? */ | |
227 } | |
228 | |
229 static void yahoo_xfer_end(GaimXfer *xfer) | |
230 { | |
231 GaimAccount *account; | |
232 struct yahoo_xfer_data *xfer_data; | |
233 | |
234 account = gaim_xfer_get_account(xfer); | |
235 xfer_data = xfer->data; | |
236 | |
237 | |
238 if (xfer_data) | |
239 yahoo_xfer_data_free(xfer_data); | |
240 xfer->data = NULL; | |
241 | |
242 } | |
243 | |
244 guint calculate_length(const gchar *l, size_t len) | |
245 { | |
246 int i; | |
247 | |
248 for (i = 0; i < len; i++) { | |
249 if (!g_ascii_isdigit(l[i])) | |
250 continue; | |
251 return strtol(l + i, NULL, 10); | |
252 } | |
253 return 0; | |
254 } | |
255 | |
256 | |
8231
f50c059b6384
[gaim-migrate @ 8954]
Christian Hammond <chipx86@chipx86.com>
parents:
8046
diff
changeset
|
257 ssize_t yahoo_xfer_read(char **buffer, GaimXfer *xfer) |
7651 | 258 { |
7710 | 259 gchar buf[4096]; |
7682 | 260 ssize_t len; |
7651 | 261 gchar *start = NULL; |
262 gchar *length; | |
263 gchar *end; | |
7710 | 264 int filelen; |
7651 | 265 struct yahoo_xfer_data *xd = xfer->data; |
266 | |
267 if (gaim_xfer_get_type(xfer) != GAIM_XFER_RECEIVE) { | |
268 return 0; | |
269 } | |
270 | |
271 len = read(xfer->fd, buf, sizeof(buf)); | |
272 | |
7682 | 273 if (len <= 0) { |
7710 | 274 if ((gaim_xfer_get_size(xfer) > 0) && |
275 (gaim_xfer_get_bytes_sent(xfer) >= gaim_xfer_get_size(xfer))) | |
7682 | 276 gaim_xfer_set_completed(xfer, TRUE); |
7651 | 277 else |
278 gaim_xfer_cancel_remote(xfer); | |
279 return 0; | |
280 } | |
281 | |
282 | |
283 if (!xd->started) { | |
284 xd->rxqueue = g_realloc(xd->rxqueue, len + xd->rxlen); | |
285 memcpy(xd->rxqueue + xd->rxlen, buf, len); | |
286 xd->rxlen += len; | |
287 | |
288 length = g_strstr_len(xd->rxqueue, len, "Content-length:"); | |
289 if (length) { | |
290 end = g_strstr_len(length, length - xd->rxqueue, "\r\n"); | |
291 if (!end) | |
292 return 0; | |
7710 | 293 if ((filelen = calculate_length(length, len - (length - xd->rxqueue)))) |
294 gaim_xfer_set_size(xfer, filelen); | |
7651 | 295 } |
296 start = g_strstr_len(xd->rxqueue, len, "\r\n\r\n"); | |
297 if (start) | |
298 start += 4; | |
299 if (!start || start > (xd->rxqueue + len)) | |
300 return 0; | |
301 xd->started = TRUE; | |
302 | |
303 len -= (start - xd->rxqueue); | |
304 | |
305 *buffer = g_malloc(len); | |
306 memcpy(*buffer, start, len); | |
307 g_free(xd->rxqueue); | |
308 xd->rxqueue = NULL; | |
309 xd->rxlen = 0; | |
310 } else { | |
311 *buffer = g_malloc(len); | |
312 memcpy(*buffer, buf, len); | |
313 } | |
314 | |
315 return len; | |
316 } | |
317 | |
8231
f50c059b6384
[gaim-migrate @ 8954]
Christian Hammond <chipx86@chipx86.com>
parents:
8046
diff
changeset
|
318 ssize_t yahoo_xfer_write(const char *buffer, size_t size, GaimXfer *xfer) |
7651 | 319 { |
7710 | 320 ssize_t len; |
7651 | 321 struct yahoo_xfer_data *xd = xfer->data; |
322 | |
323 if (!xd) | |
324 return 0; | |
325 | |
326 if (gaim_xfer_get_type(xfer) != GAIM_XFER_SEND) { | |
327 return 0; | |
328 } | |
329 | |
330 len = write(xfer->fd, buffer, size); | |
331 | |
7710 | 332 if (len == -1) { |
333 if (gaim_xfer_get_bytes_sent(xfer) >= gaim_xfer_get_size(xfer)) | |
334 gaim_xfer_set_completed(xfer, TRUE); | |
335 if ((errno != EAGAIN) && (errno != EINTR)) | |
336 gaim_xfer_cancel_remote(xfer); | |
337 return 0; | |
338 } | |
339 | |
340 if ((gaim_xfer_get_bytes_sent(xfer) + len) >= gaim_xfer_get_size(xfer)) | |
7651 | 341 gaim_xfer_set_completed(xfer, TRUE); |
342 | |
343 return len; | |
344 } | |
345 | |
346 static void yahoo_xfer_cancel_send(GaimXfer *xfer) | |
347 { | |
348 GaimAccount *account; | |
349 struct yahoo_xfer_data *xfer_data; | |
350 | |
351 xfer_data = xfer->data; | |
352 account = gaim_xfer_get_account(xfer); | |
353 | |
354 if (xfer_data) | |
355 yahoo_xfer_data_free(xfer_data); | |
356 xfer->data = NULL; | |
357 } | |
358 | |
359 static void yahoo_xfer_cancel_recv(GaimXfer *xfer) | |
360 { | |
361 GaimAccount *account; | |
362 struct yahoo_xfer_data *xfer_data; | |
363 | |
364 account = gaim_xfer_get_account(xfer); | |
365 xfer_data = xfer->data; | |
366 | |
367 if (xfer_data) | |
368 yahoo_xfer_data_free(xfer_data); | |
369 xfer->data = NULL; | |
370 } | |
371 | |
372 void yahoo_process_filetransfer(GaimConnection *gc, struct yahoo_packet *pkt) | |
373 { | |
374 char *from = NULL; | |
375 char *to = NULL; | |
376 char *msg = NULL; | |
377 char *url = NULL; | |
378 long expires = 0; | |
379 GaimXfer *xfer; | |
380 struct yahoo_xfer_data *xfer_data; | |
381 | |
382 char *service = NULL; | |
383 | |
384 char *filename = NULL; | |
385 unsigned long filesize = 0L; | |
386 | |
387 GSList *l; | |
388 | |
389 for (l = pkt->hash; l; l = l->next) { | |
390 struct yahoo_pair *pair = l->data; | |
391 | |
392 if (pair->key == 4) | |
393 from = pair->value; | |
394 if (pair->key == 5) | |
395 to = pair->value; | |
396 if (pair->key == 14) | |
397 msg = pair->value; | |
398 if (pair->key == 20) | |
399 url = pair->value; | |
400 if (pair->key == 38) | |
401 expires = strtol(pair->value, NULL, 10); | |
402 | |
403 if (pair->key == 27) | |
404 filename = pair->value; | |
405 if (pair->key == 28) | |
406 filesize = atol(pair->value); | |
407 | |
408 if (pair->key == 49) | |
409 service = pair->value; | |
410 } | |
411 | |
412 if (pkt->service == YAHOO_SERVICE_P2PFILEXFER) { | |
413 if (strcmp("FILEXFER", service) != 0) { | |
414 gaim_debug_misc("yahoo", "unhandled service 0x%02x", pkt->service); | |
415 return; | |
416 } | |
417 } | |
418 | |
419 if (msg) { | |
420 char *tmp; | |
421 tmp = strchr(msg, '\006'); | |
422 if (tmp) | |
423 *tmp = '\0'; | |
424 } | |
425 | |
426 if (!url || !from) | |
427 return; | |
428 | |
429 | |
430 /* Setup the Yahoo-specific file transfer data */ | |
431 xfer_data = g_new0(struct yahoo_xfer_data, 1); | |
432 xfer_data->gc = gc; | |
433 if (!gaim_url_parse(url, &(xfer_data->host), &(xfer_data->port), &(xfer_data->path))) { | |
434 g_free(xfer_data); | |
435 return; | |
436 } | |
437 | |
438 gaim_debug_misc("yahoo_filexfer", "Host is %s, port is %d, path is %s, and the full url was %s.\n", | |
439 xfer_data->host, xfer_data->port, xfer_data->path, url); | |
440 | |
441 /* Build the file transfer handle. */ | |
442 xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, from); | |
443 xfer->data = xfer_data; | |
444 | |
445 /* Set the info about the incoming file. */ | |
446 if (filename) | |
447 gaim_xfer_set_filename(xfer, filename); | |
448 else { | |
449 gchar *start, *end; | |
450 start = g_strrstr(xfer_data->path, "/"); | |
451 if (start) | |
452 start++; | |
453 end = g_strrstr(xfer_data->path, "?"); | |
454 if (start && *start && end) { | |
455 filename = g_strndup(start, end - start); | |
456 gaim_xfer_set_filename(xfer, filename); | |
457 g_free(filename); | |
458 filename = NULL; | |
459 } | |
460 } | |
461 | |
462 gaim_xfer_set_size(xfer, filesize); | |
463 | |
464 /* Setup our I/O op functions */ | |
465 gaim_xfer_set_init_fnc(xfer, yahoo_xfer_init); | |
466 gaim_xfer_set_start_fnc(xfer, yahoo_xfer_start); | |
467 gaim_xfer_set_end_fnc(xfer, yahoo_xfer_end); | |
468 gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); | |
469 gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); | |
470 gaim_xfer_set_read_fnc(xfer, yahoo_xfer_read); | |
471 gaim_xfer_set_write_fnc(xfer, yahoo_xfer_write); | |
472 | |
473 /* Now perform the request */ | |
474 gaim_xfer_request(xfer); | |
475 } | |
476 | |
477 void yahoo_ask_send_file(GaimConnection *gc, const char *who) | |
478 { | |
479 GaimXfer *xfer; | |
480 struct yahoo_xfer_data *xfer_data; | |
481 | |
482 xfer_data = g_new0(struct yahoo_xfer_data, 1); | |
483 xfer_data->gc = gc; | |
484 | |
485 | |
486 /* Build the file transfer handle. */ | |
487 xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who); | |
488 xfer->data = xfer_data; | |
489 | |
490 /* Setup our I/O op functions */ | |
491 gaim_xfer_set_init_fnc(xfer, yahoo_xfer_init); | |
492 gaim_xfer_set_start_fnc(xfer, yahoo_xfer_start); | |
493 gaim_xfer_set_end_fnc(xfer, yahoo_xfer_end); | |
494 gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); | |
495 gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); | |
496 gaim_xfer_set_read_fnc(xfer, yahoo_xfer_read); | |
497 gaim_xfer_set_write_fnc(xfer, yahoo_xfer_write); | |
498 | |
499 /* Now perform the request */ | |
500 gaim_xfer_request(xfer); | |
501 } | |
502 | |
503 void yahoo_send_file(GaimConnection *gc, const char *who, const char *file) | |
504 { | |
505 GaimXfer *xfer; | |
506 struct yahoo_xfer_data *xfer_data; | |
507 | |
508 if (!who || !file) | |
509 return; | |
510 | |
511 xfer_data = g_new0(struct yahoo_xfer_data, 1); | |
512 xfer_data->gc = gc; | |
513 | |
514 | |
515 /* Build the file transfer handle. */ | |
516 xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who); | |
517 xfer->data = xfer_data; | |
518 | |
519 /* Setup our I/O op functions */ | |
520 gaim_xfer_set_init_fnc(xfer, yahoo_xfer_init); | |
521 gaim_xfer_set_start_fnc(xfer, yahoo_xfer_start); | |
522 gaim_xfer_set_end_fnc(xfer, yahoo_xfer_end); | |
523 gaim_xfer_set_cancel_send_fnc(xfer, yahoo_xfer_cancel_send); | |
524 gaim_xfer_set_cancel_recv_fnc(xfer, yahoo_xfer_cancel_recv); | |
525 gaim_xfer_set_read_fnc(xfer, yahoo_xfer_read); | |
526 gaim_xfer_set_write_fnc(xfer, yahoo_xfer_write); | |
527 | |
528 /* Now perform the request */ | |
7805 | 529 gaim_xfer_request_accepted(xfer, file); |
7651 | 530 } |