[gaim-migrate @ 5891]
[pidgin-git.git] / src / ft.c
blob3dec06369d33fda41d38fa0dbe756c8950f05b7b
1 /**
2 * @file ft.c The file transfer interface.
4 * gaim
6 * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org>
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.
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.
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
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <string.h>
33 #include <gtk/gtk.h>
34 #include "gaim.h"
35 #include "proxy.h"
36 #include "notify.h"
38 #ifdef _WIN32
39 #include "win32dep.h"
40 #endif
42 static struct gaim_xfer_ui_ops *xfer_ui_ops = NULL;
44 struct gaim_xfer *
45 gaim_xfer_new(struct gaim_account *account, GaimXferType type,
46 const char *who)
48 struct gaim_xfer *xfer;
49 struct gaim_xfer_ui_ops *ui_ops;
51 if (account == NULL || type == GAIM_XFER_UNKNOWN || who == NULL)
52 return NULL;
54 xfer = g_malloc0(sizeof(struct gaim_xfer));
56 xfer->type = type;
57 xfer->account = account;
58 xfer->who = g_strdup(who);
59 xfer->ui_ops = gaim_get_xfer_ui_ops();
61 ui_ops = gaim_xfer_get_ui_ops(xfer);
63 if (ui_ops != NULL && ui_ops->new != NULL)
64 ui_ops->new(xfer);
66 return xfer;
69 void
70 gaim_xfer_destroy(struct gaim_xfer *xfer)
72 struct gaim_xfer_ui_ops *ui_ops;
74 if (xfer == NULL)
75 return;
77 if (!xfer->completed)
78 gaim_xfer_cancel_local(xfer);
80 ui_ops = gaim_xfer_get_ui_ops(xfer);
82 if (ui_ops != NULL && ui_ops->destroy != NULL)
83 ui_ops->destroy(xfer);
85 g_free(xfer->who);
86 g_free(xfer->filename);
88 if (xfer->remote_ip != NULL) g_free(xfer->remote_ip);
89 if (xfer->local_ip != NULL) g_free(xfer->local_ip);
91 if (xfer->local_filename != NULL)
92 g_free(xfer->local_filename);
94 g_free(xfer);
97 void
98 gaim_xfer_request(struct gaim_xfer *xfer)
100 struct gaim_xfer_ui_ops *ui_ops;
102 if (xfer == NULL || xfer->ops.init == NULL)
103 return;
105 ui_ops = gaim_get_xfer_ui_ops();
107 if (ui_ops == NULL || ui_ops->request_file == NULL)
108 return;
110 ui_ops->request_file(xfer);
113 void
114 gaim_xfer_request_accepted(struct gaim_xfer *xfer, char *filename)
116 GaimXferType type;
118 if (xfer == NULL || filename == NULL) {
120 if (filename != NULL)
121 g_free(filename);
123 return;
126 type = gaim_xfer_get_type(xfer);
128 if (type == GAIM_XFER_SEND) {
129 struct stat sb;
131 /* Check the filename. */
132 if (g_strrstr(filename, "..")) {
133 char *msg;
135 msg = g_strdup_printf(_("%s is not a valid filename.\n"),
136 filename);
138 gaim_xfer_error(type, xfer->who, msg);
140 g_free(msg);
141 g_free(filename);
143 return;
146 if (stat(filename, &sb) == -1) {
147 char *msg;
149 msg = g_strdup_printf(_("%s was not found.\n"), filename);
151 gaim_xfer_error(type, xfer->who, msg);
153 g_free(msg);
154 g_free(filename);
156 return;
159 gaim_xfer_set_local_filename(xfer, filename);
160 gaim_xfer_set_size(xfer, sb.st_size);
162 else {
163 /* TODO: Sanity checks and file overwrite checks. */
165 gaim_xfer_set_local_filename(xfer, filename);
168 g_free(filename);
170 xfer->ops.init(xfer);
173 void
174 gaim_xfer_request_denied(struct gaim_xfer *xfer)
176 if (xfer == NULL)
177 return;
179 gaim_xfer_destroy(xfer);
181 /* TODO */
184 GaimXferType
185 gaim_xfer_get_type(const struct gaim_xfer *xfer)
187 if (xfer == NULL)
188 return GAIM_XFER_UNKNOWN;
190 return xfer->type;
193 struct gaim_account *
194 gaim_xfer_get_account(const struct gaim_xfer *xfer)
196 if (xfer == NULL)
197 return NULL;
199 return xfer->account;
202 gboolean
203 gaim_xfer_is_completed(const struct gaim_xfer *xfer)
205 if (xfer == NULL)
206 return TRUE;
208 return xfer->completed;
211 const char *
212 gaim_xfer_get_filename(const struct gaim_xfer *xfer)
214 if (xfer == NULL)
215 return NULL;
217 return xfer->filename;
220 const char *
221 gaim_xfer_get_local_filename(const struct gaim_xfer *xfer)
223 if (xfer == NULL)
224 return NULL;
226 return xfer->local_filename;
229 size_t
230 gaim_xfer_get_bytes_sent(const struct gaim_xfer *xfer)
232 if (xfer == NULL)
233 return 0;
235 return xfer->bytes_sent;
238 size_t
239 gaim_xfer_get_bytes_remaining(const struct gaim_xfer *xfer)
241 if (xfer == NULL)
242 return 0;
244 return xfer->bytes_remaining;
247 size_t
248 gaim_xfer_get_size(const struct gaim_xfer *xfer)
250 if (xfer == NULL)
251 return 0;
253 return xfer->size;
256 double
257 gaim_xfer_get_progress(const struct gaim_xfer *xfer)
259 if (xfer == NULL)
260 return 0.0;
262 if (gaim_xfer_get_size(xfer) == 0)
263 return 0.0;
265 return ((double)gaim_xfer_get_bytes_sent(xfer) /
266 (double)gaim_xfer_get_size(xfer));
269 const char *
270 gaim_xfer_get_local_ip(const struct gaim_xfer *xfer)
272 if (xfer == NULL)
273 return NULL;
275 return xfer->local_ip;
278 unsigned int
279 gaim_xfer_get_local_port(const struct gaim_xfer *xfer)
281 if (xfer == NULL)
282 return -1;
284 return xfer->local_port;
287 const char *
288 gaim_xfer_get_remote_ip(const struct gaim_xfer *xfer)
290 if (xfer == NULL)
291 return NULL;
293 return xfer->remote_ip;
296 unsigned int
297 gaim_xfer_get_remote_port(const struct gaim_xfer *xfer)
299 if (xfer == NULL)
300 return -1;
302 return xfer->remote_port;
305 void
306 gaim_xfer_set_completed(struct gaim_xfer *xfer, gboolean completed)
308 struct gaim_xfer_ui_ops *ui_ops;
310 if (xfer == NULL)
311 return;
313 xfer->completed = completed;
315 ui_ops = gaim_xfer_get_ui_ops(xfer);
317 if (ui_ops != NULL && ui_ops->update_progress != NULL)
318 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer));
322 void
323 gaim_xfer_set_filename(struct gaim_xfer *xfer, const char *filename)
325 if (xfer == NULL)
326 return;
328 if (xfer->filename != NULL)
329 g_free(xfer->filename);
331 xfer->filename = (filename == NULL ? NULL : g_strdup(filename));
334 void
335 gaim_xfer_set_local_filename(struct gaim_xfer *xfer, const char *filename)
337 if (xfer == NULL)
338 return;
340 if (xfer->local_filename != NULL)
341 g_free(xfer->local_filename);
343 xfer->local_filename = (filename == NULL ? NULL : g_strdup(filename));
346 void
347 gaim_xfer_set_size(struct gaim_xfer *xfer, size_t size)
349 if (xfer == NULL)
350 return;
352 if (xfer->size == 0)
353 xfer->bytes_remaining = size - xfer->bytes_sent;
355 xfer->size = size;
358 struct gaim_xfer_ui_ops *
359 gaim_xfer_get_ui_ops(const struct gaim_xfer *xfer)
361 if (xfer == NULL)
362 return NULL;
364 return xfer->ui_ops;
367 void
368 gaim_xfer_set_init_fnc(struct gaim_xfer *xfer,
369 void (*fnc)(struct gaim_xfer *))
371 if (xfer == NULL)
372 return;
374 xfer->ops.init = fnc;
378 void
379 gaim_xfer_set_read_fnc(struct gaim_xfer *xfer,
380 size_t (*fnc)(char **, struct gaim_xfer *))
382 if (xfer == NULL)
383 return;
385 xfer->ops.read = fnc;
388 void
389 gaim_xfer_set_write_fnc(struct gaim_xfer *xfer,
390 size_t (*fnc)(const char *, size_t,
391 struct gaim_xfer *))
393 if (xfer == NULL)
394 return;
396 xfer->ops.write = fnc;
399 void
400 gaim_xfer_set_ack_fnc(struct gaim_xfer *xfer,
401 void (*fnc)(struct gaim_xfer *, const char *, size_t))
403 if (xfer == NULL)
404 return;
406 xfer->ops.ack = fnc;
409 void
410 gaim_xfer_set_start_fnc(struct gaim_xfer *xfer,
411 void (*fnc)(struct gaim_xfer *))
413 if (xfer == NULL)
414 return;
416 xfer->ops.start = fnc;
419 void
420 gaim_xfer_set_end_fnc(struct gaim_xfer *xfer,
421 void (*fnc)(struct gaim_xfer *))
423 if (xfer == NULL)
424 return;
426 xfer->ops.end = fnc;
429 void
430 gaim_xfer_set_cancel_send_fnc(struct gaim_xfer *xfer,
431 void (*fnc)(struct gaim_xfer *))
433 if (xfer == NULL)
434 return;
436 xfer->ops.cancel_send = fnc;
439 void
440 gaim_xfer_set_cancel_recv_fnc(struct gaim_xfer *xfer,
441 void (*fnc)(struct gaim_xfer *))
443 if (xfer == NULL)
444 return;
446 xfer->ops.cancel_recv = fnc;
449 size_t
450 gaim_xfer_read(struct gaim_xfer *xfer, char **buffer)
452 size_t s, r;
454 if (xfer == NULL || buffer == NULL)
455 return 0;
457 if (gaim_xfer_get_size(xfer) == 0)
458 s = 4096;
459 else
460 s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096);
462 if (xfer->ops.read != NULL)
463 r = (xfer->ops.read)(buffer, xfer);
464 else {
465 *buffer = g_malloc0(s);
467 r = read(xfer->fd, *buffer, s);
468 if ((gaim_xfer_get_size > 0) &&
469 ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer)))
470 gaim_xfer_set_completed(xfer, TRUE);
473 return r;
476 size_t
477 gaim_xfer_write(struct gaim_xfer *xfer, const char *buffer, size_t size)
479 size_t r, s;
481 if (xfer == NULL || buffer == NULL || size == 0)
482 return 0;
484 s = MIN(gaim_xfer_get_bytes_remaining(xfer), size);
486 if (xfer->ops.write != NULL)
487 r = (xfer->ops.write)(buffer, s, xfer);
488 else
489 r = write(xfer->fd, buffer, s);
491 return r;
494 static void
495 transfer_cb(gpointer data, gint source, GaimInputCondition condition)
497 struct gaim_xfer_ui_ops *ui_ops;
498 struct gaim_xfer *xfer = (struct gaim_xfer *)data;
499 char *buffer = NULL;
500 size_t r;
502 if (condition & GAIM_INPUT_READ) {
503 r = gaim_xfer_read(xfer, &buffer);
504 if (r > 0)
505 fwrite(buffer, 1, r, xfer->dest_fp);
507 else {
508 size_t s = MIN(gaim_xfer_get_bytes_remaining(xfer), 4096);
510 buffer = g_malloc0(s);
512 fread(buffer, 1, s, xfer->dest_fp);
514 /* Write as much as we're allowed to. */
515 r = gaim_xfer_write(xfer, buffer, s);
517 if (r < s) {
518 /* We have to seek back in the file now. */
519 fseek(xfer->dest_fp, r - s, SEEK_CUR);
523 if (gaim_xfer_get_size(xfer) > 0)
524 xfer->bytes_remaining -= r;
526 xfer->bytes_sent += r;
528 if (xfer->ops.ack != NULL)
529 xfer->ops.ack(xfer, buffer, r);
531 g_free(buffer);
533 ui_ops = gaim_xfer_get_ui_ops(xfer);
535 if (ui_ops != NULL && ui_ops->update_progress != NULL)
536 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer));
538 if (gaim_xfer_is_completed(xfer))
539 gaim_xfer_end(xfer);
542 static void
543 begin_transfer(struct gaim_xfer *xfer, GaimInputCondition cond)
545 struct gaim_xfer_ui_ops *ui_ops;
546 GaimXferType type = gaim_xfer_get_type(xfer);
548 ui_ops = gaim_xfer_get_ui_ops(xfer);
551 * We'll have already tried to open this earlier to make sure we can
552 * read/write here. Should be safe.
554 xfer->dest_fp = fopen(gaim_xfer_get_local_filename(xfer),
555 type == GAIM_XFER_RECEIVE ? "wb" : "rb");
557 /* Just in case, though. */
558 if (xfer->dest_fp == NULL) {
559 gaim_xfer_cancel_local(xfer); /* ? */
560 return;
563 xfer->watcher = gaim_input_add(xfer->fd, cond, transfer_cb, xfer);
565 if (ui_ops != NULL && ui_ops->add_xfer != NULL)
566 ui_ops->add_xfer(xfer);
568 if (xfer->ops.start != NULL)
569 xfer->ops.start(xfer);
572 static void
573 connect_cb(gpointer data, gint source, GaimInputCondition condition)
575 struct gaim_xfer *xfer = (struct gaim_xfer *)data;
577 xfer->fd = source;
579 begin_transfer(xfer, condition);
582 void
583 gaim_xfer_start(struct gaim_xfer *xfer, int fd, const char *ip,
584 unsigned int port)
586 GaimInputCondition cond;
587 GaimXferType type;
589 if (xfer == NULL || gaim_xfer_get_type(xfer) == GAIM_XFER_UNKNOWN)
590 return;
592 type = gaim_xfer_get_type(xfer);
594 xfer->bytes_remaining = gaim_xfer_get_size(xfer);
595 xfer->bytes_sent = 0;
597 if (type == GAIM_XFER_RECEIVE) {
598 cond = GAIM_INPUT_READ;
600 if (ip != NULL) {
601 xfer->remote_ip = g_strdup(ip);
602 xfer->remote_port = port;
604 /* Establish a file descriptor. */
605 proxy_connect(xfer->account, xfer->remote_ip, xfer->remote_port,
606 connect_cb, xfer);
608 return;
610 else {
611 xfer->fd = fd;
614 else {
615 cond = GAIM_INPUT_WRITE;
617 xfer->fd = fd;
620 begin_transfer(xfer, cond);
623 void
624 gaim_xfer_end(struct gaim_xfer *xfer)
626 if (xfer == NULL)
627 return;
629 /* See if we are actually trying to cancel this. */
630 if (!xfer->completed) {
631 gaim_xfer_cancel_local(xfer);
632 return;
635 if (xfer->ops.end != NULL)
636 xfer->ops.end(xfer);
638 if (xfer->watcher != 0) {
639 gaim_input_remove(xfer->watcher);
640 xfer->watcher = 0;
643 if (xfer->fd != 0)
644 close(xfer->fd);
646 if (xfer->dest_fp != NULL) {
647 fclose(xfer->dest_fp);
648 xfer->dest_fp = NULL;
652 void
653 gaim_xfer_cancel_local(struct gaim_xfer *xfer)
655 struct gaim_xfer_ui_ops *ui_ops;
657 if (xfer == NULL)
658 return;
660 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND)
662 if (xfer->ops.cancel_send != NULL)
663 xfer->ops.cancel_send(xfer);
665 else
667 if (xfer->ops.cancel_recv != NULL)
668 xfer->ops.cancel_recv(xfer);
671 if (xfer->watcher != 0) {
672 gaim_input_remove(xfer->watcher);
673 xfer->watcher = 0;
676 if (xfer->fd != 0)
677 close(xfer->fd);
679 if (xfer->dest_fp != NULL) {
680 fclose(xfer->dest_fp);
681 xfer->dest_fp = NULL;
684 ui_ops = gaim_xfer_get_ui_ops(xfer);
686 if (ui_ops != NULL && ui_ops->cancel_local != NULL)
687 ui_ops->cancel_local(xfer);
689 xfer->bytes_remaining = 0;
692 void
693 gaim_xfer_cancel_remote(struct gaim_xfer *xfer)
695 struct gaim_xfer_ui_ops *ui_ops;
697 if (xfer == NULL)
698 return;
700 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND)
702 if (xfer->ops.cancel_send != NULL)
703 xfer->ops.cancel_send(xfer);
705 else
707 if (xfer->ops.cancel_recv != NULL)
708 xfer->ops.cancel_recv(xfer);
711 if (xfer->watcher != 0) {
712 gaim_input_remove(xfer->watcher);
713 xfer->watcher = 0;
716 if (xfer->fd != 0)
717 close(xfer->fd);
719 if (xfer->dest_fp != NULL) {
720 fclose(xfer->dest_fp);
721 xfer->dest_fp = NULL;
724 ui_ops = gaim_xfer_get_ui_ops(xfer);
726 if (ui_ops != NULL && ui_ops->cancel_remote != NULL)
727 ui_ops->cancel_remote(xfer);
729 xfer->bytes_remaining = 0;
732 void
733 gaim_xfer_error(GaimXferType type, const char *who, const char *msg)
735 char *title;
737 if (msg == NULL || type == GAIM_XFER_UNKNOWN)
738 return;
740 if (type == GAIM_XFER_SEND)
741 title = g_strdup_printf(_("File transfer to %s aborted.\n"), who);
742 else
743 title = g_strdup_printf(_("File transfer from %s aborted.\n"), who);
745 gaim_notify_error(NULL, NULL, title, msg);
747 g_free(title);
750 void
751 gaim_set_xfer_ui_ops(struct gaim_xfer_ui_ops *ops)
753 if (ops == NULL)
754 return;
756 xfer_ui_ops = ops;
759 struct gaim_xfer_ui_ops *
760 gaim_get_xfer_ui_ops(void)
762 return xfer_ui_ops;