src/ft.c

branch
gaim
changeset 20470
77693555855f
parent 13071
b98e72d4089a
parent 20469
b2836a24d81e
child 20471
1966704b3e42
equal deleted inserted replaced
13071:b98e72d4089a 20470:77693555855f
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 if (gaim_xfer_get_filename(xfer) ||
381 gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_ACCEPTED) {
382 gchar* message = NULL;
383 message = g_strdup_printf(_("%s is offering to send file %s"),
384 xfer->who, gaim_xfer_get_filename(xfer));
385 gaim_xfer_conversation_write(xfer, message, FALSE);
386 g_free(message);
387 gaim_xfer_ask_recv(xfer);
388 } else {
389 gaim_xfer_ask_accept(xfer);
390 }
391 } else
392 gaim_xfer_choose_file(xfer);
393 }
394
395 void
396 gaim_xfer_request_accepted(GaimXfer *xfer, const char *filename)
397 {
398 GaimXferType type;
399 struct stat st;
400 char *msg, *utf8;
401 GaimAccount *account;
402 GaimBuddy *buddy;
403
404 if (xfer == NULL)
405 return;
406
407 type = gaim_xfer_get_type(xfer);
408 account = gaim_xfer_get_account(xfer);
409
410 if (!filename && type == GAIM_XFER_RECEIVE) {
411 xfer->status = GAIM_XFER_STATUS_ACCEPTED;
412 xfer->ops.init(xfer);
413 return;
414 }
415
416 buddy = gaim_find_buddy(account, xfer->who);
417
418 if (type == GAIM_XFER_SEND) {
419 /* Check the filename. */
420 #ifdef _WIN32
421 if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) {
422 #else
423 if (g_strrstr(filename, "../")) {
424 #endif
425 char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
426
427 msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8);
428 gaim_xfer_error(type, account, xfer->who, msg);
429 g_free(utf8);
430 g_free(msg);
431
432 gaim_xfer_unref(xfer);
433 return;
434 }
435
436 if (g_stat(filename, &st) == -1) {
437 gaim_xfer_show_file_error(xfer, filename);
438 gaim_xfer_unref(xfer);
439 return;
440 }
441
442 gaim_xfer_set_local_filename(xfer, filename);
443 gaim_xfer_set_size(xfer, st.st_size);
444
445 utf8 = g_filename_to_utf8(g_basename(filename), -1, NULL, NULL, NULL);
446 gaim_xfer_set_filename(xfer, utf8);
447
448 msg = g_strdup_printf(_("Offering to send %s to %s"),
449 utf8, buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
450 g_free(utf8);
451
452 gaim_xfer_conversation_write(xfer, msg, FALSE);
453 g_free(msg);
454 }
455 else {
456 xfer->status = GAIM_XFER_STATUS_ACCEPTED;
457 gaim_xfer_set_local_filename(xfer, filename);
458
459 msg = g_strdup_printf(_("Starting transfer of %s from %s"),
460 xfer->filename, buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
461 gaim_xfer_conversation_write(xfer, msg, FALSE);
462 g_free(msg);
463 }
464
465 gaim_xfer_add(xfer);
466 xfer->ops.init(xfer);
467
468 }
469
470 void
471 gaim_xfer_request_denied(GaimXfer *xfer)
472 {
473 g_return_if_fail(xfer != NULL);
474
475 if (xfer->ops.request_denied != NULL)
476 xfer->ops.request_denied(xfer);
477
478 gaim_xfer_unref(xfer);
479 }
480
481 GaimXferType
482 gaim_xfer_get_type(const GaimXfer *xfer)
483 {
484 g_return_val_if_fail(xfer != NULL, GAIM_XFER_UNKNOWN);
485
486 return xfer->type;
487 }
488
489 GaimAccount *
490 gaim_xfer_get_account(const GaimXfer *xfer)
491 {
492 g_return_val_if_fail(xfer != NULL, NULL);
493
494 return xfer->account;
495 }
496
497 GaimXferStatusType
498 gaim_xfer_get_status(const GaimXfer *xfer)
499 {
500 g_return_val_if_fail(xfer != NULL, GAIM_XFER_STATUS_UNKNOWN);
501
502 return xfer->status;
503 }
504
505 gboolean
506 gaim_xfer_is_canceled(const GaimXfer *xfer)
507 {
508 g_return_val_if_fail(xfer != NULL, TRUE);
509
510 if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) ||
511 (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_REMOTE))
512 return TRUE;
513 else
514 return FALSE;
515 }
516
517 gboolean
518 gaim_xfer_is_completed(const GaimXfer *xfer)
519 {
520 g_return_val_if_fail(xfer != NULL, TRUE);
521
522 return (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_DONE);
523 }
524
525 const char *
526 gaim_xfer_get_filename(const GaimXfer *xfer)
527 {
528 g_return_val_if_fail(xfer != NULL, NULL);
529
530 return xfer->filename;
531 }
532
533 const char *
534 gaim_xfer_get_local_filename(const GaimXfer *xfer)
535 {
536 g_return_val_if_fail(xfer != NULL, NULL);
537
538 return xfer->local_filename;
539 }
540
541 size_t
542 gaim_xfer_get_bytes_sent(const GaimXfer *xfer)
543 {
544 g_return_val_if_fail(xfer != NULL, 0);
545
546 return xfer->bytes_sent;
547 }
548
549 size_t
550 gaim_xfer_get_bytes_remaining(const GaimXfer *xfer)
551 {
552 g_return_val_if_fail(xfer != NULL, 0);
553
554 return xfer->bytes_remaining;
555 }
556
557 size_t
558 gaim_xfer_get_size(const GaimXfer *xfer)
559 {
560 g_return_val_if_fail(xfer != NULL, 0);
561
562 return xfer->size;
563 }
564
565 double
566 gaim_xfer_get_progress(const GaimXfer *xfer)
567 {
568 g_return_val_if_fail(xfer != NULL, 0.0);
569
570 if (gaim_xfer_get_size(xfer) == 0)
571 return 0.0;
572
573 return ((double)gaim_xfer_get_bytes_sent(xfer) /
574 (double)gaim_xfer_get_size(xfer));
575 }
576
577 unsigned int
578 gaim_xfer_get_local_port(const GaimXfer *xfer)
579 {
580 g_return_val_if_fail(xfer != NULL, -1);
581
582 return xfer->local_port;
583 }
584
585 const char *
586 gaim_xfer_get_remote_ip(const GaimXfer *xfer)
587 {
588 g_return_val_if_fail(xfer != NULL, NULL);
589
590 return xfer->remote_ip;
591 }
592
593 unsigned int
594 gaim_xfer_get_remote_port(const GaimXfer *xfer)
595 {
596 g_return_val_if_fail(xfer != NULL, -1);
597
598 return xfer->remote_port;
599 }
600
601 void
602 gaim_xfer_set_completed(GaimXfer *xfer, gboolean completed)
603 {
604 GaimXferUiOps *ui_ops;
605
606 g_return_if_fail(xfer != NULL);
607
608 if (completed == TRUE) {
609 char *msg = NULL;
610 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_DONE);
611
612 if (gaim_xfer_get_filename(xfer) != NULL)
613 msg = g_strdup_printf(_("Transfer of file %s complete"),
614 gaim_xfer_get_filename(xfer));
615 else
616 msg = g_strdup_printf(_("File transfer complete"));
617 gaim_xfer_conversation_write(xfer, msg, FALSE);
618 g_free(msg);
619 }
620
621 ui_ops = gaim_xfer_get_ui_ops(xfer);
622
623 if (ui_ops != NULL && ui_ops->update_progress != NULL)
624 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer));
625 }
626
627 void
628 gaim_xfer_set_message(GaimXfer *xfer, const char *message)
629 {
630 g_return_if_fail(xfer != NULL);
631
632 g_free(xfer->message);
633
634 if (message != NULL)
635 xfer->message = g_strdup(message);
636 else
637 xfer->message = NULL;
638 }
639
640 void
641 gaim_xfer_set_filename(GaimXfer *xfer, const char *filename)
642 {
643 g_return_if_fail(xfer != NULL);
644
645 if (xfer->filename != NULL)
646 g_free(xfer->filename);
647
648 xfer->filename = (filename == NULL ? NULL : g_strdup(filename));
649 }
650
651 void
652 gaim_xfer_set_local_filename(GaimXfer *xfer, const char *filename)
653 {
654 g_return_if_fail(xfer != NULL);
655
656 if (xfer->local_filename != NULL)
657 g_free(xfer->local_filename);
658
659 xfer->local_filename = (filename == NULL ? NULL : g_strdup(filename));
660 }
661
662 void
663 gaim_xfer_set_size(GaimXfer *xfer, size_t size)
664 {
665 g_return_if_fail(xfer != NULL);
666
667 if (xfer->size == 0)
668 xfer->bytes_remaining = size - xfer->bytes_sent;
669
670 xfer->size = size;
671 }
672
673 GaimXferUiOps *
674 gaim_xfer_get_ui_ops(const GaimXfer *xfer)
675 {
676 g_return_val_if_fail(xfer != NULL, NULL);
677
678 return xfer->ui_ops;
679 }
680
681 void
682 gaim_xfer_set_init_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
683 {
684 g_return_if_fail(xfer != NULL);
685
686 xfer->ops.init = fnc;
687 }
688
689 void gaim_xfer_set_request_denied_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
690 {
691 g_return_if_fail(xfer != NULL);
692
693 xfer->ops.request_denied = fnc;
694 }
695
696 void
697 gaim_xfer_set_read_fnc(GaimXfer *xfer, gssize (*fnc)(guchar **, GaimXfer *))
698 {
699 g_return_if_fail(xfer != NULL);
700
701 xfer->ops.read = fnc;
702 }
703
704 void
705 gaim_xfer_set_write_fnc(GaimXfer *xfer,
706 gssize (*fnc)(const guchar *, size_t, GaimXfer *))
707 {
708 g_return_if_fail(xfer != NULL);
709
710 xfer->ops.write = fnc;
711 }
712
713 void
714 gaim_xfer_set_ack_fnc(GaimXfer *xfer,
715 void (*fnc)(GaimXfer *, const guchar *, size_t))
716 {
717 g_return_if_fail(xfer != NULL);
718
719 xfer->ops.ack = fnc;
720 }
721
722 void
723 gaim_xfer_set_start_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
724 {
725 g_return_if_fail(xfer != NULL);
726
727 xfer->ops.start = fnc;
728 }
729
730 void
731 gaim_xfer_set_end_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
732 {
733 g_return_if_fail(xfer != NULL);
734
735 xfer->ops.end = fnc;
736 }
737
738 void
739 gaim_xfer_set_cancel_send_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
740 {
741 g_return_if_fail(xfer != NULL);
742
743 xfer->ops.cancel_send = fnc;
744 }
745
746 void
747 gaim_xfer_set_cancel_recv_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *))
748 {
749 g_return_if_fail(xfer != NULL);
750
751 xfer->ops.cancel_recv = fnc;
752 }
753
754 gssize
755 gaim_xfer_read(GaimXfer *xfer, guchar **buffer)
756 {
757 gssize s, r;
758
759 g_return_val_if_fail(xfer != NULL, 0);
760 g_return_val_if_fail(buffer != NULL, 0);
761
762 if (gaim_xfer_get_size(xfer) == 0)
763 s = 4096;
764 else
765 s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096);
766
767 if (xfer->ops.read != NULL)
768 r = (xfer->ops.read)(buffer, xfer);
769 else {
770 *buffer = g_malloc0(s);
771
772 r = read(xfer->fd, *buffer, s);
773 if (r < 0 && errno == EAGAIN)
774 r = 0;
775 else if (r < 0)
776 r = -1;
777 else if ((gaim_xfer_get_size(xfer) > 0) &&
778 ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer)))
779 gaim_xfer_set_completed(xfer, TRUE);
780 }
781
782 return r;
783 }
784
785 gssize
786 gaim_xfer_write(GaimXfer *xfer, const guchar *buffer, size_t size)
787 {
788 gssize r, s;
789
790 g_return_val_if_fail(xfer != NULL, 0);
791 g_return_val_if_fail(buffer != NULL, 0);
792 g_return_val_if_fail(size != 0, 0);
793
794 s = MIN(gaim_xfer_get_bytes_remaining(xfer), size);
795
796 if (xfer->ops.write != NULL) {
797 r = (xfer->ops.write)(buffer, s, xfer);
798 } else {
799 r = write(xfer->fd, buffer, s);
800 if (r < 0 && errno == EAGAIN)
801 r = 0;
802 if ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer))
803 gaim_xfer_set_completed(xfer, TRUE);
804 }
805
806 return r;
807 }
808
809 static void
810 transfer_cb(gpointer data, gint source, GaimInputCondition condition)
811 {
812 GaimXferUiOps *ui_ops;
813 GaimXfer *xfer = (GaimXfer *)data;
814 guchar *buffer = NULL;
815 gssize r = 0;
816
817 if (condition & GAIM_INPUT_READ) {
818 r = gaim_xfer_read(xfer, &buffer);
819 if (r > 0) {
820 fwrite(buffer, 1, r, xfer->dest_fp);
821 } else if(r < 0) {
822 gaim_xfer_cancel_remote(xfer);
823 return;
824 } else if(r == 0)
825 return;
826 }
827
828 if (condition & GAIM_INPUT_WRITE) {
829 size_t s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096);
830
831 /* this is so the prpl can keep the connection open
832 if it needs to for some odd reason. */
833 if (s == 0) {
834 if (xfer->watcher) {
835 gaim_input_remove(xfer->watcher);
836 xfer->watcher = 0;
837 }
838 return;
839 }
840
841 buffer = g_malloc0(s);
842
843 fread(buffer, 1, s, xfer->dest_fp);
844
845 /* Write as much as we're allowed to. */
846 r = gaim_xfer_write(xfer, buffer, s);
847
848 if (r == -1) {
849 gaim_xfer_cancel_remote(xfer);
850 g_free(buffer);
851 return;
852 } else if (r < s) {
853 /* We have to seek back in the file now. */
854 fseek(xfer->dest_fp, r - s, SEEK_CUR);
855 }
856 }
857
858 if (gaim_xfer_get_size(xfer) > 0)
859 xfer->bytes_remaining -= r;
860
861 xfer->bytes_sent += r;
862
863 if (xfer->ops.ack != NULL)
864 xfer->ops.ack(xfer, buffer, r);
865
866 g_free(buffer);
867
868 ui_ops = gaim_xfer_get_ui_ops(xfer);
869
870 if (ui_ops != NULL && ui_ops->update_progress != NULL)
871 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer));
872
873 if (gaim_xfer_is_completed(xfer))
874 gaim_xfer_end(xfer);
875 }
876
877 static void
878 begin_transfer(GaimXfer *xfer, GaimInputCondition cond)
879 {
880 GaimXferType type = gaim_xfer_get_type(xfer);
881
882 xfer->dest_fp = g_fopen(gaim_xfer_get_local_filename(xfer),
883 type == GAIM_XFER_RECEIVE ? "wb" : "rb");
884
885 if (xfer->dest_fp == NULL) {
886 gaim_xfer_show_file_error(xfer, gaim_xfer_get_local_filename(xfer));
887 gaim_xfer_cancel_local(xfer);
888 return;
889 }
890
891 xfer->watcher = gaim_input_add(xfer->fd, cond, transfer_cb, xfer);
892
893 if (xfer->ops.start != NULL)
894 xfer->ops.start(xfer);
895 }
896
897 static void
898 connect_cb(gpointer data, gint source, GaimInputCondition condition)
899 {
900 GaimXfer *xfer = (GaimXfer *)data;
901
902 xfer->fd = source;
903
904 begin_transfer(xfer, condition);
905 }
906
907 void
908 gaim_xfer_start(GaimXfer *xfer, int fd, const char *ip,
909 unsigned int port)
910 {
911 GaimInputCondition cond;
912 GaimXferType type;
913
914 g_return_if_fail(xfer != NULL);
915 g_return_if_fail(gaim_xfer_get_type(xfer) != GAIM_XFER_UNKNOWN);
916
917 type = gaim_xfer_get_type(xfer);
918
919 xfer->bytes_remaining = gaim_xfer_get_size(xfer);
920 xfer->bytes_sent = 0;
921
922 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_STARTED);
923
924 if (type == GAIM_XFER_RECEIVE) {
925 cond = GAIM_INPUT_READ;
926
927 if (ip != NULL) {
928 xfer->remote_ip = g_strdup(ip);
929 xfer->remote_port = port;
930
931 /* Establish a file descriptor. */
932 gaim_proxy_connect(xfer->account, xfer->remote_ip,
933 xfer->remote_port, connect_cb, xfer);
934
935 return;
936 }
937 else {
938 xfer->fd = fd;
939 }
940 }
941 else {
942 cond = GAIM_INPUT_WRITE;
943
944 xfer->fd = fd;
945 }
946
947 begin_transfer(xfer, cond);
948 }
949
950 void
951 gaim_xfer_end(GaimXfer *xfer)
952 {
953 g_return_if_fail(xfer != NULL);
954
955 /* See if we are actually trying to cancel this. */
956 if (gaim_xfer_get_status(xfer) != GAIM_XFER_STATUS_DONE) {
957 gaim_xfer_cancel_local(xfer);
958 return;
959 }
960
961 if (xfer->ops.end != NULL)
962 xfer->ops.end(xfer);
963
964 if (xfer->watcher != 0) {
965 gaim_input_remove(xfer->watcher);
966 xfer->watcher = 0;
967 }
968
969 if (xfer->fd != 0)
970 close(xfer->fd);
971
972 if (xfer->dest_fp != NULL) {
973 fclose(xfer->dest_fp);
974 xfer->dest_fp = NULL;
975 }
976
977 gaim_xfer_unref(xfer);
978 }
979
980 void
981 gaim_xfer_add(GaimXfer *xfer)
982 {
983 GaimXferUiOps *ui_ops;
984
985 g_return_if_fail(xfer != NULL);
986
987 ui_ops = gaim_xfer_get_ui_ops(xfer);
988
989 if (ui_ops != NULL && ui_ops->add_xfer != NULL)
990 ui_ops->add_xfer(xfer);
991 }
992
993 void
994 gaim_xfer_cancel_local(GaimXfer *xfer)
995 {
996 GaimXferUiOps *ui_ops;
997 char *msg = NULL;
998
999 g_return_if_fail(xfer != NULL);
1000
1001 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL);
1002
1003 if (gaim_xfer_get_filename(xfer) != NULL)
1004 {
1005 msg = g_strdup_printf(_("You canceled the transfer of %s"),
1006 gaim_xfer_get_filename(xfer));
1007 }
1008 else
1009 {
1010 msg = g_strdup_printf(_("File transfer cancelled"));
1011 }
1012 gaim_xfer_conversation_write(xfer, msg, FALSE);
1013 g_free(msg);
1014
1015 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND)
1016 {
1017 if (xfer->ops.cancel_send != NULL)
1018 xfer->ops.cancel_send(xfer);
1019 }
1020 else
1021 {
1022 if (xfer->ops.cancel_recv != NULL)
1023 xfer->ops.cancel_recv(xfer);
1024 }
1025
1026 if (xfer->watcher != 0) {
1027 gaim_input_remove(xfer->watcher);
1028 xfer->watcher = 0;
1029 }
1030
1031 if (xfer->fd != 0)
1032 close(xfer->fd);
1033
1034 if (xfer->dest_fp != NULL) {
1035 fclose(xfer->dest_fp);
1036 xfer->dest_fp = NULL;
1037 }
1038
1039 ui_ops = gaim_xfer_get_ui_ops(xfer);
1040
1041 if (ui_ops != NULL && ui_ops->cancel_local != NULL)
1042 ui_ops->cancel_local(xfer);
1043
1044 xfer->bytes_remaining = 0;
1045
1046 gaim_xfer_unref(xfer);
1047 }
1048
1049 void
1050 gaim_xfer_cancel_remote(GaimXfer *xfer)
1051 {
1052 GaimXferUiOps *ui_ops;
1053 gchar *msg;
1054 GaimAccount *account;
1055 GaimBuddy *buddy;
1056
1057 g_return_if_fail(xfer != NULL);
1058
1059 gaim_request_close_with_handle(xfer);
1060 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_REMOTE);
1061
1062 account = gaim_xfer_get_account(xfer);
1063 buddy = gaim_find_buddy(account, xfer->who);
1064
1065 if (gaim_xfer_get_filename(xfer) != NULL)
1066 {
1067 msg = g_strdup_printf(_("%s canceled the transfer of %s"),
1068 buddy ? gaim_buddy_get_alias(buddy) : xfer->who, gaim_xfer_get_filename(xfer));
1069 }
1070 else
1071 {
1072 msg = g_strdup_printf(_("%s canceled the file transfer"),
1073 buddy ? gaim_buddy_get_alias(buddy) : xfer->who);
1074 }
1075 gaim_xfer_conversation_write(xfer, msg, TRUE);
1076 gaim_xfer_error(gaim_xfer_get_type(xfer), account, xfer->who, msg);
1077 g_free(msg);
1078
1079 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND)
1080 {
1081 if (xfer->ops.cancel_send != NULL)
1082 xfer->ops.cancel_send(xfer);
1083 }
1084 else
1085 {
1086 if (xfer->ops.cancel_recv != NULL)
1087 xfer->ops.cancel_recv(xfer);
1088 }
1089
1090 if (xfer->watcher != 0) {
1091 gaim_input_remove(xfer->watcher);
1092 xfer->watcher = 0;
1093 }
1094
1095 if (xfer->fd != 0)
1096 close(xfer->fd);
1097
1098 if (xfer->dest_fp != NULL) {
1099 fclose(xfer->dest_fp);
1100 xfer->dest_fp = NULL;
1101 }
1102
1103 ui_ops = gaim_xfer_get_ui_ops(xfer);
1104
1105 if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
1106 ui_ops->cancel_remote(xfer);
1107
1108 xfer->bytes_remaining = 0;
1109
1110 gaim_xfer_unref(xfer);
1111 }
1112
1113 void
1114 gaim_xfer_error(GaimXferType type, GaimAccount *account, const char *who, const char *msg)
1115 {
1116 char *title;
1117
1118 g_return_if_fail(msg != NULL);
1119 g_return_if_fail(type != GAIM_XFER_UNKNOWN);
1120
1121 if (account) {
1122 GaimBuddy *buddy;
1123 buddy = gaim_find_buddy(account, who);
1124 if (buddy)
1125 who = gaim_buddy_get_alias(buddy);
1126 }
1127
1128 if (type == GAIM_XFER_SEND)
1129 title = g_strdup_printf(_("File transfer to %s failed."), who);
1130 else
1131 title = g_strdup_printf(_("File transfer from %s failed."), who);
1132
1133 gaim_notify_error(NULL, NULL, title, msg);
1134
1135 g_free(title);
1136 }
1137
1138 void
1139 gaim_xfer_update_progress(GaimXfer *xfer)
1140 {
1141 GaimXferUiOps *ui_ops;
1142
1143 g_return_if_fail(xfer != NULL);
1144
1145 ui_ops = gaim_xfer_get_ui_ops(xfer);
1146 if (ui_ops != NULL && ui_ops->update_progress != NULL)
1147 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer));
1148 }
1149
1150
1151 /**************************************************************************
1152 * File Transfer Subsystem API
1153 **************************************************************************/
1154 void *
1155 gaim_xfers_get_handle(void) {
1156 static int handle = 0;
1157
1158 return &handle;
1159 }
1160
1161 void
1162 gaim_xfers_init(void) {
1163 void *handle = gaim_xfers_get_handle();
1164
1165 /* register signals */
1166 gaim_signal_register(handle, "file-recv-accept",
1167 gaim_marshal_VOID__POINTER,
1168 NULL, 1,
1169 gaim_value_new(GAIM_TYPE_POINTER));
1170 gaim_signal_register(handle, "file-send-accept",
1171 gaim_marshal_VOID__POINTER,
1172 NULL, 1,
1173 gaim_value_new(GAIM_TYPE_POINTER));
1174 gaim_signal_register(handle, "file-recv-start",
1175 gaim_marshal_VOID__POINTER,
1176 NULL, 1,
1177 gaim_value_new(GAIM_TYPE_POINTER));
1178 gaim_signal_register(handle, "file-send-start",
1179 gaim_marshal_VOID__POINTER,
1180 NULL, 1,
1181 gaim_value_new(GAIM_TYPE_POINTER));
1182 gaim_signal_register(handle, "file-send-cancel",
1183 gaim_marshal_VOID__POINTER,
1184 NULL, 1,
1185 gaim_value_new(GAIM_TYPE_POINTER));
1186 gaim_signal_register(handle, "file-recv-cancel",
1187 gaim_marshal_VOID__POINTER,
1188 NULL, 1,
1189 gaim_value_new(GAIM_TYPE_POINTER));
1190 gaim_signal_register(handle, "file-send-complete",
1191 gaim_marshal_VOID__POINTER,
1192 NULL, 1,
1193 gaim_value_new(GAIM_TYPE_POINTER));
1194 gaim_signal_register(handle, "file-recv-complete",
1195 gaim_marshal_VOID__POINTER,
1196 NULL, 1,
1197 gaim_value_new(GAIM_TYPE_POINTER));
1198 }
1199
1200 void
1201 gaim_xfers_uninit(void) {
1202 gaim_signals_disconnect_by_handle(gaim_xfers_get_handle());
1203 }
1204
1205 void
1206 gaim_xfers_set_ui_ops(GaimXferUiOps *ops) {
1207 xfer_ui_ops = ops;
1208 }
1209
1210 GaimXferUiOps *
1211 gaim_xfers_get_ui_ops(void) {
1212 return xfer_ui_ops;
1213 }

mercurial