src/ft.c

branch
cpw.khc.msnp14
changeset 20472
6a6d2ef151e6
parent 13912
463b4fa9f067
parent 20469
b2836a24d81e
child 20473
91e1b3a49d10
equal deleted inserted replaced
13912:463b4fa9f067 20472:6a6d2ef151e6
1 /**
2 * @file ft.c File Transfer API
3 *
4 * gaim
5 *
6 * Gaim is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25 #include "internal.h"
26 #include "ft.h"
27 #include "network.h"
28 #include "notify.h"
29 #include "prefs.h"
30 #include "proxy.h"
31 #include "request.h"
32 #include "util.h"
33
34 static GaimXferUiOps *xfer_ui_ops = NULL;
35
36 GaimXfer *
37 gaim_xfer_new(GaimAccount *account, GaimXferType type, const char *who)
38 {
39 GaimXfer *xfer;
40 GaimXferUiOps *ui_ops;
41
42 g_return_val_if_fail(type != GAIM_XFER_UNKNOWN, NULL);
43 g_return_val_if_fail(account != NULL, NULL);
44 g_return_val_if_fail(who != NULL, NULL);
45
46 xfer = g_new0(GaimXfer, 1);
47
48 xfer->ref = 1;
49 xfer->type = type;
50 xfer->account = account;
51 xfer->who = g_strdup(who);
52 xfer->ui_ops = gaim_xfers_get_ui_ops();
53 xfer->message = NULL;
54
55 ui_ops = gaim_xfer_get_ui_ops(xfer);
56
57 if (ui_ops != NULL && ui_ops->new_xfer != NULL)
58 ui_ops->new_xfer(xfer);
59
60 return xfer;
61 }
62
63 static void
64 gaim_xfer_destroy(GaimXfer *xfer)
65 {
66 GaimXferUiOps *ui_ops;
67
68 g_return_if_fail(xfer != NULL);
69
70 /* Close the file browser, if it's open */
71 gaim_request_close_with_handle(xfer);
72
73 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_STARTED)
74 gaim_xfer_cancel_local(xfer);
75
76 ui_ops = gaim_xfer_get_ui_ops(xfer);
77
78 if (ui_ops != NULL && ui_ops->destroy != NULL)
79 ui_ops->destroy(xfer);
80
81 g_free(xfer->who);
82 g_free(xfer->filename);
83 g_free(xfer->remote_ip);
84 g_free(xfer->local_filename);
85
86 g_free(xfer);
87 }
88
89 void
90 gaim_xfer_ref(GaimXfer *xfer)
91 {
92 g_return_if_fail(xfer != NULL);
93
94 xfer->ref++;
95 }
96
97 void
98 gaim_xfer_unref(GaimXfer *xfer)
99 {
100 g_return_if_fail(xfer != NULL);
101 g_return_if_fail(xfer->ref > 0);
102
103 xfer->ref--;
104
105 if (xfer->ref == 0)
106 gaim_xfer_destroy(xfer);
107 }
108
109 static void
110 gaim_xfer_set_status(GaimXfer *xfer, GaimXferStatusType status)
111 {
112 g_return_if_fail(xfer != NULL);
113
114 if(xfer->type == GAIM_XFER_SEND) {
115 switch(status) {
116 case GAIM_XFER_STATUS_ACCEPTED:
117 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-accept", xfer);
118 break;
119 case GAIM_XFER_STATUS_STARTED:
120 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-start", xfer);
121 break;
122 case GAIM_XFER_STATUS_DONE:
123 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-complete", xfer);
124 break;
125 case GAIM_XFER_STATUS_CANCEL_LOCAL:
126 case GAIM_XFER_STATUS_CANCEL_REMOTE:
127 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-cancel", xfer);
128 break;
129 default:
130 break;
131 }
132 } else if(xfer->type == GAIM_XFER_RECEIVE) {
133 switch(status) {
134 case GAIM_XFER_STATUS_ACCEPTED:
135 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-accept", xfer);
136 break;
137 case GAIM_XFER_STATUS_STARTED:
138 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-start", xfer);
139 break;
140 case GAIM_XFER_STATUS_DONE:
141 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-complete", xfer);
142 break;
143 case GAIM_XFER_STATUS_CANCEL_LOCAL:
144 case GAIM_XFER_STATUS_CANCEL_REMOTE:
145 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-cancel", xfer);
146 break;
147 default:
148 break;
149 }
150 }
151
152 xfer->status = status;
153 }
154
155 void gaim_xfer_conversation_write(GaimXfer *xfer, char *message, gboolean is_error)
156 {
157 GaimConversation *conv = NULL;
158 GaimMessageFlags flags = GAIM_MESSAGE_SYSTEM;
159 char *escaped;
160
161 g_return_if_fail(xfer != NULL);
162 g_return_if_fail(message != NULL);
163
164 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, xfer->who,
165 gaim_xfer_get_account(xfer));
166
167 if (conv == NULL)
168 return;
169
170 escaped = g_markup_escape_text(message, -1);
171
172 if (is_error)
173 flags = GAIM_MESSAGE_ERROR;
174
175 gaim_conversation_write(conv, NULL, escaped, flags, time(NULL));
176 g_free(escaped);
177 }
178
179 static void gaim_xfer_show_file_error(GaimXfer *xfer, const char *filename)
180 {
181 int err = errno;
182 gchar *msg = NULL, *utf8;
183 GaimXferType xfer_type = gaim_xfer_get_type(xfer);
184 GaimAccount *account = gaim_xfer_get_account(xfer);
185
186 utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
187 switch(xfer_type) {
188 case GAIM_XFER_SEND:
189 msg = g_strdup_printf(_("Error reading %s: \n%s.\n"),
190 utf8, strerror(err));
191 break;
192 case GAIM_XFER_RECEIVE:
193 msg = g_strdup_printf(_("Error writing %s: \n%s.\n"),
194 utf8, strerror(err));
195 break;
196 default:
197 msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"),
198 utf8, strerror(err));
199 break;
200 }
201 g_free(utf8);
202
203 gaim_xfer_conversation_write(xfer, msg, TRUE);
204 gaim_xfer_error(xfer_type, account, xfer->who, msg);
205 g_free(msg);
206 }
207
208 static void
209 gaim_xfer_choose_file_ok_cb(void *user_data, const char *filename)
210 {
211 GaimXfer *xfer;
212 struct stat st;
213
214 xfer = (GaimXfer *)user_data;
215
216 if (g_stat(filename, &st) != 0) {
217 /* File not found. */
218 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) {
219 gaim_xfer_request_accepted(xfer, filename);
220 }
221 else {
222 gaim_xfer_show_file_error(xfer, filename);
223 gaim_xfer_request_denied(xfer);
224 }
225 }
226 else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) &&
227 (st.st_size == 0)) {
228
229 gaim_notify_error(NULL, NULL,
230 _("Cannot send a file of 0 bytes."), NULL);
231
232 gaim_xfer_request_denied(xfer);
233 }
234 else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) &&
235 S_ISDIR(st.st_mode)) {
236 /*
237 * XXX - Sending a directory should be valid for some protocols.
238 */
239 gaim_notify_error(NULL, NULL,
240 _("Cannot send a directory."), NULL);
241
242 gaim_xfer_request_denied(xfer);
243 }
244 else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) &&
245 S_ISDIR(st.st_mode)) {
246 char *msg, *utf8;
247 utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
248 msg = g_strdup_printf(
249 _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8);
250 g_free(utf8);
251 gaim_notify_error(NULL, NULL, msg, NULL);
252 g_free(msg);
253 gaim_xfer_request_denied(xfer);
254 }
255 else {
256 gaim_xfer_request_accepted(xfer, filename);
257 }
258
259 gaim_xfer_unref(xfer);
260 }
261
262 static void
263 gaim_xfer_choose_file_cancel_cb(void *user_data, const char *filename)
264 {
265 GaimXfer *xfer = (GaimXfer *)user_data;
266
267 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL);
268 gaim_xfer_request_denied(xfer);
269 }
270
271 static int
272 gaim_xfer_choose_file(GaimXfer *xfer)
273 {
274 gaim_request_file(xfer, NULL, gaim_xfer_get_filename(xfer),
275 (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE),
276 G_CALLBACK(gaim_xfer_choose_file_ok_cb),
277 G_CALLBACK(gaim_xfer_choose_file_cancel_cb), xfer);
278
279 return 0;
280 }
281
282 static int
283 cancel_recv_cb(GaimXfer *xfer)
284 {
285 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL);
286 gaim_xfer_request_denied(xfer);
287 gaim_xfer_unref(xfer);
288
289 return 0;
290 }
291
292 static void
293 gaim_xfer_ask_recv(GaimXfer *xfer)
294 {
295 char *buf, *size_buf;
296 size_t size;
297
298 /* If we have already accepted the request, ask the destination file
299 name directly */
300 if (gaim_xfer_get_status(xfer) != GAIM_XFER_STATUS_ACCEPTED) {
301 GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who);
302
303 if (gaim_xfer_get_filename(xfer) != NULL)
304 {
305 size = gaim_xfer_get_size(xfer);
306 size_buf = gaim_str_size_to_units(size);
307 buf = g_strdup_printf(_("%s wants to send you %s (%s)"),
308 buddy ? gaim_buddy_get_alias(buddy) : xfer->who,
309 gaim_xfer_get_filename(xfer), size_buf);
310 g_free(size_buf);
311 }
312 else
313 {
314 buf = g_strdup_printf(_("%s wants to send you a file"),
315 buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
316 }
317
318 if (xfer->message != NULL)
319 serv_got_im(gaim_account_get_connection(xfer->account),
320 xfer->who, xfer->message, 0, time(NULL));
321
322 gaim_request_accept_cancel(xfer, NULL, buf, NULL,
323 GAIM_DEFAULT_ACTION_NONE, xfer,
324 G_CALLBACK(gaim_xfer_choose_file),
325 G_CALLBACK(cancel_recv_cb));
326
327 g_free(buf);
328 } else
329 gaim_xfer_choose_file(xfer);
330 }
331
332 static int
333 ask_accept_ok(GaimXfer *xfer)
334 {
335 gaim_xfer_request_accepted(xfer, NULL);
336
337 return 0;
338 }
339
340 static int
341 ask_accept_cancel(GaimXfer *xfer)
342 {
343 gaim_xfer_request_denied(xfer);
344 gaim_xfer_unref(xfer);
345
346 return 0;
347 }
348
349 static void
350 gaim_xfer_ask_accept(GaimXfer *xfer)
351 {
352 char *buf, *buf2 = NULL;
353 GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who);
354
355 buf = g_strdup_printf(_("Accept file transfer request from %s?"),
356 buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
357 if (gaim_xfer_get_remote_ip(xfer) &&
358 gaim_xfer_get_remote_port(xfer))
359 buf2 = g_strdup_printf(_("A file is available for download from:\n"
360 "Remote host: %s\nRemote port: %d"),
361 gaim_xfer_get_remote_ip(xfer),
362 gaim_xfer_get_remote_port(xfer));
363 gaim_request_accept_cancel(xfer, NULL, buf, buf2,
364 GAIM_DEFAULT_ACTION_NONE, xfer,
365 G_CALLBACK(ask_accept_ok),
366 G_CALLBACK(ask_accept_cancel));
367 g_free(buf);
368 g_free(buf2);
369 }
370
371 void
372 gaim_xfer_request(GaimXfer *xfer)
373 {
374 g_return_if_fail(xfer != NULL);
375 g_return_if_fail(xfer->ops.init != NULL);
376
377 gaim_xfer_ref(xfer);
378
379 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE)
380 {
381 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-request", xfer);
382 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL)
383 {
384 /* The file-transfer was cancelled by a plugin */
385 gaim_xfer_cancel_local(xfer);
386 }
387 else if (gaim_xfer_get_filename(xfer) ||
388 gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_ACCEPTED)
389 {
390 gchar* message = NULL;
391 GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who);
392 message = g_strdup_printf(_("%s is offering to send file %s"),
393 buddy ? gaim_buddy_get_alias(buddy) : xfer->who, gaim_xfer_get_filename(xfer));
394 gaim_xfer_conversation_write(xfer, message, FALSE);
395 g_free(message);
396 /* Ask for a filename to save to if it's not already given by a plugin */
397 if (xfer->local_filename == NULL)
398 gaim_xfer_ask_recv(xfer);
399 }
400 else
401 {
402 gaim_xfer_ask_accept(xfer);
403 }
404 }
405 else
406 {
407 gaim_xfer_choose_file(xfer);
408 }
409 }
410
411 void
412 gaim_xfer_request_accepted(GaimXfer *xfer, const char *filename)
413 {
414 GaimXferType type;
415 struct stat st;
416 char *msg, *utf8;
417 GaimAccount *account;
418 GaimBuddy *buddy;
419
420 if (xfer == NULL)
421 return;
422
423 type = gaim_xfer_get_type(xfer);
424 account = gaim_xfer_get_account(xfer);
425
426 if (!filename && type == GAIM_XFER_RECEIVE) {
427 xfer->status = GAIM_XFER_STATUS_ACCEPTED;
428 xfer->ops.init(xfer);
429 return;
430 }
431
432 buddy = gaim_find_buddy(account, xfer->who);
433
434 if (type == GAIM_XFER_SEND) {
435 /* Sending a file */
436 /* Check the filename. */
437 #ifdef _WIN32
438 if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) {
439 #else
440 if (g_strrstr(filename, "../")) {
441 #endif
442 char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
443
444 msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8);
445 gaim_xfer_error(type, account, xfer->who, msg);
446 g_free(utf8);
447 g_free(msg);
448
449 gaim_xfer_unref(xfer);
450 return;
451 }
452
453 if (g_stat(filename, &st) == -1) {
454 gaim_xfer_show_file_error(xfer, filename);
455 gaim_xfer_unref(xfer);
456 return;
457 }
458
459 gaim_xfer_set_local_filename(xfer, filename);
460 gaim_xfer_set_size(xfer, st.st_size);
461
462 utf8 = g_filename_to_utf8(g_basename(filename), -1, NULL, NULL, NULL);
463 gaim_xfer_set_filename(xfer, utf8);
464
465 msg = g_strdup_printf(_("Offering to send %s to %s"),
466 utf8, buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
467 g_free(utf8);
468
469 gaim_xfer_conversation_write(xfer, msg, FALSE);
470 g_free(msg);
471 }
472 else {
473 /* Receiving a file */
474 xfer->status = GAIM_XFER_STATUS_ACCEPTED;
475 gaim_xfer_set_local_filename(xfer, filename);
476
477 msg = g_strdup_printf(_("Starting transfer of %s from %s"),
478 xfer->filename, buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
479 gaim_xfer_conversation_write(xfer, msg, FALSE);
480 g_free(msg);
481 }
482
483 gaim_xfer_add(xfer);
484 xfer->ops.init(xfer);
485
486 }
487
488 void
489 gaim_xfer_request_denied(GaimXfer *xfer)
490 {
491 g_return_if_fail(xfer != NULL);
492
493 if (xfer->ops.request_denied != NULL)
494 xfer->ops.request_denied(xfer);
495
496 gaim_xfer_unref(xfer);
497 }
498
499 GaimXferType
500 gaim_xfer_get_type(const GaimXfer *xfer)
501 {
502 g_return_val_if_fail(xfer != NULL, GAIM_XFER_UNKNOWN);
503
504 return xfer->type;
505 }
506
507 GaimAccount *
508 gaim_xfer_get_account(const GaimXfer *xfer)
509 {
510 g_return_val_if_fail(xfer != NULL, NULL);
511
512 return xfer->account;
513 }
514
515 GaimXferStatusType
516 gaim_xfer_get_status(const GaimXfer *xfer)
517 {
518 g_return_val_if_fail(xfer != NULL, GAIM_XFER_STATUS_UNKNOWN);
519
520 return xfer->status;
521 }
522
523 gboolean
524 gaim_xfer_is_canceled(const GaimXfer *xfer)
525 {
526 g_return_val_if_fail(xfer != NULL, TRUE);
527
528 if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) ||
529 (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_REMOTE))
530 return TRUE;
531 else
532 return FALSE;
533 }
534
535 gboolean
536 gaim_xfer_is_completed(const GaimXfer *xfer)
537 {
538 g_return_val_if_fail(xfer != NULL, TRUE);
539
540 return (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_DONE);
541 }
542
543 const char *
544 gaim_xfer_get_filename(const GaimXfer *xfer)
545 {
546 g_return_val_if_fail(xfer != NULL, NULL);
547
548 return xfer->filename;
549 }
550
551 const char *
552 gaim_xfer_get_local_filename(const GaimXfer *xfer)
553 {
554 g_return_val_if_fail(xfer != NULL, NULL);
555
556 return xfer->local_filename;
557 }
558
559 size_t
560 gaim_xfer_get_bytes_sent(const GaimXfer *xfer)
561 {
562 g_return_val_if_fail(xfer != NULL, 0);
563
564 return xfer->bytes_sent;
565 }
566
567 size_t
568 gaim_xfer_get_bytes_remaining(const GaimXfer *xfer)
569 {
570 g_return_val_if_fail(xfer != NULL, 0);
571
572 return xfer->bytes_remaining;
573 }
574
575 size_t
576 gaim_xfer_get_size(const GaimXfer *xfer)
577 {
578 g_return_val_if_fail(xfer != NULL, 0);
579
580 return xfer->size;
581 }
582
583 double
584 gaim_xfer_get_progress(const GaimXfer *xfer)
585 {
586 g_return_val_if_fail(xfer != NULL, 0.0);
587
588 if (gaim_xfer_get_size(xfer) == 0)
589 return 0.0;
590
591 return ((double)gaim_xfer_get_bytes_sent(xfer) /
592 (double)gaim_xfer_get_size(xfer));
593 }
594
595 unsigned int
596 gaim_xfer_get_local_port(const GaimXfer *xfer)
597 {
598 g_return_val_if_fail(xfer != NULL, -1);
599
600 return xfer->local_port;
601 }
602
603 const char *
604 gaim_xfer_get_remote_ip(const GaimXfer *xfer)
605 {
606 g_return_val_if_fail(xfer != NULL, NULL);
607
608 return xfer->remote_ip;
609 }
610
611 unsigned int
612 gaim_xfer_get_remote_port(const GaimXfer *xfer)
613 {
614 g_return_val_if_fail(xfer != NULL, -1);
615
616 return xfer->remote_port;
617 }
618
619 void
620 gaim_xfer_set_completed(GaimXfer *xfer, gboolean completed)
621 {
622 GaimXferUiOps *ui_ops;
623
624 g_return_if_fail(xfer != NULL);
625
626 if (completed == TRUE) {
627 char *msg = NULL;
628 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_DONE);
629
630 if (gaim_xfer_get_filename(xfer) != NULL)
631 msg = g_strdup_printf(_("Transfer of file %s complete"),
632 gaim_xfer_get_filename(xfer));
633 else
634 msg = g_strdup_printf(_("File transfer complete"));
635 gaim_xfer_conversation_write(xfer, msg, FALSE);
636 g_free(msg);
637 }
638
639 ui_ops = gaim_xfer_get_ui_ops(xfer);
640
641 if (ui_ops != NULL && ui_ops->update_progress != NULL)
642 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer));
643 }
644
645 void
646 gaim_xfer_set_message(GaimXfer *xfer, const char *message)
647 {
648 g_return_if_fail(xfer != NULL);
649
650 g_free(xfer->message);
651
652 if (message != NULL)
653 xfer->message = g_strdup(message);
654 else
655 xfer->message = NULL;
656 }
657
658 void
659 gaim_xfer_set_filename(GaimXfer *xfer, const char *filename)
660 {
661 g_return_if_fail(xfer != NULL);
662
663 if (xfer->filename != NULL)
664 g_free(xfer->filename);
665
666 xfer->filename = (filename == NULL ? NULL : g_strdup(filename));
667 }
668
669 void
670 gaim_xfer_set_local_filename(GaimXfer *xfer, const char *filename)
671 {
672 g_return_if_fail(xfer != NULL);
673
674 if (xfer->local_filename != NULL)
675 g_free(xfer->local_filename);
676
677 xfer->local_filename = (filename == NULL ? NULL : g_strdup(filename));
678 }
679
680 void
681 gaim_xfer_set_size(GaimXfer *xfer, size_t size)
682 {
683 g_return_if_fail(xfer != NULL);
684
685 if (xfer->size == 0)
686 xfer->bytes_remaining = size - xfer->bytes_sent;
687
688 xfer->size = size;
689 }
690
691 GaimXferUiOps *
692 gaim_xfer_get_ui_ops(const GaimXfer *xfer)
693 {
694 g_return_val_if_fail(xfer != NULL, NULL);
695
696 return xfer->ui_ops;
697 }
698
699 void
700 gaim_xfer_set_init_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
701 {
702 g_return_if_fail(xfer != NULL);
703
704 xfer->ops.init = fnc;
705 }
706
707 void gaim_xfer_set_request_denied_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
708 {
709 g_return_if_fail(xfer != NULL);
710
711 xfer->ops.request_denied = fnc;
712 }
713
714 void
715 gaim_xfer_set_read_fnc(GaimXfer *xfer, gssize (*fnc)(guchar **, GaimXfer *))
716 {
717 g_return_if_fail(xfer != NULL);
718
719 xfer->ops.read = fnc;
720 }
721
722 void
723 gaim_xfer_set_write_fnc(GaimXfer *xfer,
724 gssize (*fnc)(const guchar *, size_t, GaimXfer *))
725 {
726 g_return_if_fail(xfer != NULL);
727
728 xfer->ops.write = fnc;
729 }
730
731 void
732 gaim_xfer_set_ack_fnc(GaimXfer *xfer,
733 void (*fnc)(GaimXfer *, const guchar *, size_t))
734 {
735 g_return_if_fail(xfer != NULL);
736
737 xfer->ops.ack = fnc;
738 }
739
740 void
741 gaim_xfer_set_start_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
742 {
743 g_return_if_fail(xfer != NULL);
744
745 xfer->ops.start = fnc;
746 }
747
748 void
749 gaim_xfer_set_end_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
750 {
751 g_return_if_fail(xfer != NULL);
752
753 xfer->ops.end = fnc;
754 }
755
756 void
757 gaim_xfer_set_cancel_send_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
758 {
759 g_return_if_fail(xfer != NULL);
760
761 xfer->ops.cancel_send = fnc;
762 }
763
764 void
765 gaim_xfer_set_cancel_recv_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
766 {
767 g_return_if_fail(xfer != NULL);
768
769 xfer->ops.cancel_recv = fnc;
770 }
771
772 gssize
773 gaim_xfer_read(GaimXfer *xfer, guchar **buffer)
774 {
775 gssize s, r;
776
777 g_return_val_if_fail(xfer != NULL, 0);
778 g_return_val_if_fail(buffer != NULL, 0);
779
780 if (gaim_xfer_get_size(xfer) == 0)
781 s = 4096;
782 else
783 s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096);
784
785 if (xfer->ops.read != NULL)
786 r = (xfer->ops.read)(buffer, xfer);
787 else {
788 *buffer = g_malloc0(s);
789
790 r = read(xfer->fd, *buffer, s);
791 if (r < 0 && errno == EAGAIN)
792 r = 0;
793 else if (r < 0)
794 r = -1;
795 else if ((gaim_xfer_get_size(xfer) > 0) &&
796 ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer)))
797 gaim_xfer_set_completed(xfer, TRUE);
798 else if (r == 0)
799 r = -1;
800 }
801
802 return r;
803 }
804
805 gssize
806 gaim_xfer_write(GaimXfer *xfer, const guchar *buffer, gsize size)
807 {
808 gssize r, s;
809
810 g_return_val_if_fail(xfer != NULL, 0);
811 g_return_val_if_fail(buffer != NULL, 0);
812 g_return_val_if_fail(size != 0, 0);
813
814 s = MIN(gaim_xfer_get_bytes_remaining(xfer), size);
815
816 if (xfer->ops.write != NULL) {
817 r = (xfer->ops.write)(buffer, s, xfer);
818 } else {
819 r = write(xfer->fd, buffer, s);
820 if (r < 0 && errno == EAGAIN)
821 r = 0;
822 if ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer))
823 gaim_xfer_set_completed(xfer, TRUE);
824 }
825
826 return r;
827 }
828
829 static void
830 transfer_cb(gpointer data, gint source, GaimInputCondition condition)
831 {
832 GaimXferUiOps *ui_ops;
833 GaimXfer *xfer = (GaimXfer *)data;
834 guchar *buffer = NULL;
835 gssize r = 0;
836
837 if (condition & GAIM_INPUT_READ) {
838 r = gaim_xfer_read(xfer, &buffer);
839 if (r > 0) {
840 fwrite(buffer, 1, r, xfer->dest_fp);
841 } else if(r <= 0) {
842 gaim_xfer_cancel_remote(xfer);
843 return;
844 }
845 }
846
847 if (condition & GAIM_INPUT_WRITE) {
848 size_t s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096);
849
850 /* this is so the prpl can keep the connection open
851 if it needs to for some odd reason. */
852 if (s == 0) {
853 if (xfer->watcher) {
854 gaim_input_remove(xfer->watcher);
855 xfer->watcher = 0;
856 }
857 return;
858 }
859
860 buffer = g_malloc0(s);
861
862 fread(buffer, 1, s, xfer->dest_fp);
863
864 /* Write as much as we're allowed to. */
865 r = gaim_xfer_write(xfer, buffer, s);
866
867 if (r == -1) {
868 gaim_xfer_cancel_remote(xfer);
869 g_free(buffer);
870 return;
871 } else if (r < s) {
872 /* We have to seek back in the file now. */
873 fseek(xfer->dest_fp, r - s, SEEK_CUR);
874 }
875 }
876
877 if (r > 0) {
878 if (gaim_xfer_get_size(xfer) > 0)
879 xfer->bytes_remaining -= r;
880
881 xfer->bytes_sent += r;
882
883 if (xfer->ops.ack != NULL)
884 xfer->ops.ack(xfer, buffer, r);
885
886 g_free(buffer);
887
888 ui_ops = gaim_xfer_get_ui_ops(xfer);
889
890 if (ui_ops != NULL && ui_ops->update_progress != NULL)
891 ui_ops->update_progress(xfer,
892 gaim_xfer_get_progress(xfer));
893 }
894
895 if (gaim_xfer_is_completed(xfer))
896 gaim_xfer_end(xfer);
897 }
898
899 static void
900 begin_transfer(GaimXfer *xfer, GaimInputCondition cond)
901 {
902 GaimXferType type = gaim_xfer_get_type(xfer);
903
904 xfer->dest_fp = g_fopen(gaim_xfer_get_local_filename(xfer),
905 type == GAIM_XFER_RECEIVE ? "wb" : "rb");
906
907 if (xfer->dest_fp == NULL) {
908 gaim_xfer_show_file_error(xfer, gaim_xfer_get_local_filename(xfer));
909 gaim_xfer_cancel_local(xfer);
910 return;
911 }
912
913 xfer->watcher = gaim_input_add(xfer->fd, cond, transfer_cb, xfer);
914
915 xfer->start_time = time(NULL);
916
917 if (xfer->ops.start != NULL)
918 xfer->ops.start(xfer);
919 }
920
921 static void
922 connect_cb(gpointer data, gint source, GaimInputCondition condition)
923 {
924 GaimXfer *xfer = (GaimXfer *)data;
925
926 xfer->fd = source;
927
928 begin_transfer(xfer, condition);
929 }
930
931 void
932 gaim_xfer_start(GaimXfer *xfer, int fd, const char *ip,
933 unsigned int port)
934 {
935 GaimInputCondition cond;
936 GaimXferType type;
937
938 g_return_if_fail(xfer != NULL);
939 g_return_if_fail(gaim_xfer_get_type(xfer) != GAIM_XFER_UNKNOWN);
940
941 type = gaim_xfer_get_type(xfer);
942
943 xfer->bytes_remaining = gaim_xfer_get_size(xfer);
944 xfer->bytes_sent = 0;
945
946 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_STARTED);
947
948 if (type == GAIM_XFER_RECEIVE) {
949 cond = GAIM_INPUT_READ;
950
951 if (ip != NULL) {
952 xfer->remote_ip = g_strdup(ip);
953 xfer->remote_port = port;
954
955 /* Establish a file descriptor. */
956 gaim_proxy_connect(xfer->account, xfer->remote_ip,
957 xfer->remote_port, connect_cb, xfer);
958
959 return;
960 }
961 else {
962 xfer->fd = fd;
963 }
964 }
965 else {
966 cond = GAIM_INPUT_WRITE;
967
968 xfer->fd = fd;
969 }
970
971 begin_transfer(xfer, cond);
972 }
973
974 void
975 gaim_xfer_end(GaimXfer *xfer)
976 {
977 g_return_if_fail(xfer != NULL);
978
979 /* See if we are actually trying to cancel this. */
980 if (!gaim_xfer_is_completed(xfer)) {
981 gaim_xfer_cancel_local(xfer);
982 return;
983 }
984
985 xfer->end_time = time(NULL);
986 if (xfer->ops.end != NULL)
987 xfer->ops.end(xfer);
988
989 if (xfer->watcher != 0) {
990 gaim_input_remove(xfer->watcher);
991 xfer->watcher = 0;
992 }
993
994 if (xfer->fd != 0)
995 close(xfer->fd);
996
997 if (xfer->dest_fp != NULL) {
998 fclose(xfer->dest_fp);
999 xfer->dest_fp = NULL;
1000 }
1001
1002 gaim_xfer_unref(xfer);
1003 }
1004
1005 void
1006 gaim_xfer_add(GaimXfer *xfer)
1007 {
1008 GaimXferUiOps *ui_ops;
1009
1010 g_return_if_fail(xfer != NULL);
1011
1012 ui_ops = gaim_xfer_get_ui_ops(xfer);
1013
1014 if (ui_ops != NULL && ui_ops->add_xfer != NULL)
1015 ui_ops->add_xfer(xfer);
1016 }
1017
1018 void
1019 gaim_xfer_cancel_local(GaimXfer *xfer)
1020 {
1021 GaimXferUiOps *ui_ops;
1022 char *msg = NULL;
1023
1024 g_return_if_fail(xfer != NULL);
1025
1026 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL);
1027 xfer->end_time = time(NULL);
1028
1029 if (gaim_xfer_get_filename(xfer) != NULL)
1030 {
1031 msg = g_strdup_printf(_("You canceled the transfer of %s"),
1032 gaim_xfer_get_filename(xfer));
1033 }
1034 else
1035 {
1036 msg = g_strdup_printf(_("File transfer cancelled"));
1037 }
1038 gaim_xfer_conversation_write(xfer, msg, FALSE);
1039 g_free(msg);
1040
1041 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND)
1042 {
1043 if (xfer->ops.cancel_send != NULL)
1044 xfer->ops.cancel_send(xfer);
1045 }
1046 else
1047 {
1048 if (xfer->ops.cancel_recv != NULL)
1049 xfer->ops.cancel_recv(xfer);
1050 }
1051
1052 if (xfer->watcher != 0) {
1053 gaim_input_remove(xfer->watcher);
1054 xfer->watcher = 0;
1055 }
1056
1057 if (xfer->fd != 0)
1058 close(xfer->fd);
1059
1060 if (xfer->dest_fp != NULL) {
1061 fclose(xfer->dest_fp);
1062 xfer->dest_fp = NULL;
1063 }
1064
1065 ui_ops = gaim_xfer_get_ui_ops(xfer);
1066
1067 if (ui_ops != NULL && ui_ops->cancel_local != NULL)
1068 ui_ops->cancel_local(xfer);
1069
1070 xfer->bytes_remaining = 0;
1071
1072 gaim_xfer_unref(xfer);
1073 }
1074
1075 void
1076 gaim_xfer_cancel_remote(GaimXfer *xfer)
1077 {
1078 GaimXferUiOps *ui_ops;
1079 gchar *msg;
1080 GaimAccount *account;
1081 GaimBuddy *buddy;
1082
1083 g_return_if_fail(xfer != NULL);
1084
1085 gaim_request_close_with_handle(xfer);
1086 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_REMOTE);
1087 xfer->end_time = time(NULL);
1088
1089 account = gaim_xfer_get_account(xfer);
1090 buddy = gaim_find_buddy(account, xfer->who);
1091
1092 if (gaim_xfer_get_filename(xfer) != NULL)
1093 {
1094 msg = g_strdup_printf(_("%s canceled the transfer of %s"),
1095 buddy ? gaim_buddy_get_alias(buddy) : xfer->who, gaim_xfer_get_filename(xfer));
1096 }
1097 else
1098 {
1099 msg = g_strdup_printf(_("%s canceled the file transfer"),
1100 buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
1101 }
1102 gaim_xfer_conversation_write(xfer, msg, TRUE);
1103 gaim_xfer_error(gaim_xfer_get_type(xfer), account, xfer->who, msg);
1104 g_free(msg);
1105
1106 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND)
1107 {
1108 if (xfer->ops.cancel_send != NULL)
1109 xfer->ops.cancel_send(xfer);
1110 }
1111 else
1112 {
1113 if (xfer->ops.cancel_recv != NULL)
1114 xfer->ops.cancel_recv(xfer);
1115 }
1116
1117 if (xfer->watcher != 0) {
1118 gaim_input_remove(xfer->watcher);
1119 xfer->watcher = 0;
1120 }
1121
1122 if (xfer->fd != 0)
1123 close(xfer->fd);
1124
1125 if (xfer->dest_fp != NULL) {
1126 fclose(xfer->dest_fp);
1127 xfer->dest_fp = NULL;
1128 }
1129
1130 ui_ops = gaim_xfer_get_ui_ops(xfer);
1131
1132 if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
1133 ui_ops->cancel_remote(xfer);
1134
1135 xfer->bytes_remaining = 0;
1136
1137 gaim_xfer_unref(xfer);
1138 }
1139
1140 void
1141 gaim_xfer_error(GaimXferType type, GaimAccount *account, const char *who, const char *msg)
1142 {
1143 char *title;
1144
1145 g_return_if_fail(msg != NULL);
1146 g_return_if_fail(type != GAIM_XFER_UNKNOWN);
1147
1148 if (account) {
1149 GaimBuddy *buddy;
1150 buddy = gaim_find_buddy(account, who);
1151 if (buddy)
1152 who = gaim_buddy_get_alias(buddy);
1153 }
1154
1155 if (type == GAIM_XFER_SEND)
1156 title = g_strdup_printf(_("File transfer to %s failed."), who);
1157 else
1158 title = g_strdup_printf(_("File transfer from %s failed."), who);
1159
1160 gaim_notify_error(NULL, NULL, title, msg);
1161
1162 g_free(title);
1163 }
1164
1165 void
1166 gaim_xfer_update_progress(GaimXfer *xfer)
1167 {
1168 GaimXferUiOps *ui_ops;
1169
1170 g_return_if_fail(xfer != NULL);
1171
1172 ui_ops = gaim_xfer_get_ui_ops(xfer);
1173 if (ui_ops != NULL && ui_ops->update_progress != NULL)
1174 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer));
1175 }
1176
1177
1178 /**************************************************************************
1179 * File Transfer Subsystem API
1180 **************************************************************************/
1181 void *
1182 gaim_xfers_get_handle(void) {
1183 static int handle = 0;
1184
1185 return &handle;
1186 }
1187
1188 void
1189 gaim_xfers_init(void) {
1190 void *handle = gaim_xfers_get_handle();
1191
1192 /* register signals */
1193 gaim_signal_register(handle, "file-recv-accept",
1194 gaim_marshal_VOID__POINTER,
1195 NULL, 1,
1196 gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_SUBTYPE_XFER));
1197 gaim_signal_register(handle, "file-send-accept",
1198 gaim_marshal_VOID__POINTER,
1199 NULL, 1,
1200 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1201 gaim_signal_register(handle, "file-recv-start",
1202 gaim_marshal_VOID__POINTER,
1203 NULL, 1,
1204 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1205 gaim_signal_register(handle, "file-send-start",
1206 gaim_marshal_VOID__POINTER,
1207 NULL, 1,
1208 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1209 gaim_signal_register(handle, "file-send-cancel",
1210 gaim_marshal_VOID__POINTER,
1211 NULL, 1,
1212 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1213 gaim_signal_register(handle, "file-recv-cancel",
1214 gaim_marshal_VOID__POINTER,
1215 NULL, 1,
1216 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1217 gaim_signal_register(handle, "file-send-complete",
1218 gaim_marshal_VOID__POINTER,
1219 NULL, 1,
1220 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1221 gaim_signal_register(handle, "file-recv-complete",
1222 gaim_marshal_VOID__POINTER,
1223 NULL, 1,
1224 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1225 gaim_signal_register(handle, "file-recv-request",
1226 gaim_marshal_VOID__POINTER,
1227 NULL, 1,
1228 gaim_value_new(GAIM_TYPE_POINTER, GAIM_SUBTYPE_XFER));
1229 }
1230
1231 void
1232 gaim_xfers_uninit(void) {
1233 gaim_signals_disconnect_by_handle(gaim_xfers_get_handle());
1234 }
1235
1236 void
1237 gaim_xfers_set_ui_ops(GaimXferUiOps *ops) {
1238 xfer_ui_ops = ops;
1239 }
1240
1241 GaimXferUiOps *
1242 gaim_xfers_get_ui_ops(void) {
1243 return xfer_ui_ops;
1244 }

mercurial