2 * @file ft.c The file transfer interface.
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
42 static struct gaim_xfer_ui_ops
*xfer_ui_ops
= NULL
;
45 gaim_xfer_new(struct gaim_account
*account
, GaimXferType type
,
48 struct gaim_xfer
*xfer
;
49 struct gaim_xfer_ui_ops
*ui_ops
;
51 if (account
== NULL
|| type
== GAIM_XFER_UNKNOWN
|| who
== NULL
)
54 xfer
= g_malloc0(sizeof(struct gaim_xfer
));
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
)
70 gaim_xfer_destroy(struct gaim_xfer
*xfer
)
72 struct gaim_xfer_ui_ops
*ui_ops
;
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
);
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
);
98 gaim_xfer_request(struct gaim_xfer
*xfer
)
100 struct gaim_xfer_ui_ops
*ui_ops
;
102 if (xfer
== NULL
|| xfer
->ops
.init
== NULL
)
105 ui_ops
= gaim_get_xfer_ui_ops();
107 if (ui_ops
== NULL
|| ui_ops
->request_file
== NULL
)
110 ui_ops
->request_file(xfer
);
114 gaim_xfer_request_accepted(struct gaim_xfer
*xfer
, char *filename
)
118 if (xfer
== NULL
|| filename
== NULL
) {
120 if (filename
!= NULL
)
126 type
= gaim_xfer_get_type(xfer
);
128 if (type
== GAIM_XFER_SEND
) {
131 /* Check the filename. */
132 if (g_strrstr(filename
, "..")) {
135 msg
= g_strdup_printf(_("%s is not a valid filename.\n"),
138 gaim_xfer_error(type
, xfer
->who
, msg
);
146 if (stat(filename
, &sb
) == -1) {
149 msg
= g_strdup_printf(_("%s was not found.\n"), filename
);
151 gaim_xfer_error(type
, xfer
->who
, msg
);
159 gaim_xfer_set_local_filename(xfer
, filename
);
160 gaim_xfer_set_size(xfer
, sb
.st_size
);
163 /* TODO: Sanity checks and file overwrite checks. */
165 gaim_xfer_set_local_filename(xfer
, filename
);
170 xfer
->ops
.init(xfer
);
174 gaim_xfer_request_denied(struct gaim_xfer
*xfer
)
179 gaim_xfer_destroy(xfer
);
185 gaim_xfer_get_type(const struct gaim_xfer
*xfer
)
188 return GAIM_XFER_UNKNOWN
;
193 struct gaim_account
*
194 gaim_xfer_get_account(const struct gaim_xfer
*xfer
)
199 return xfer
->account
;
203 gaim_xfer_is_completed(const struct gaim_xfer
*xfer
)
208 return xfer
->completed
;
212 gaim_xfer_get_filename(const struct gaim_xfer
*xfer
)
217 return xfer
->filename
;
221 gaim_xfer_get_local_filename(const struct gaim_xfer
*xfer
)
226 return xfer
->local_filename
;
230 gaim_xfer_get_bytes_sent(const struct gaim_xfer
*xfer
)
235 return xfer
->bytes_sent
;
239 gaim_xfer_get_bytes_remaining(const struct gaim_xfer
*xfer
)
244 return xfer
->bytes_remaining
;
248 gaim_xfer_get_size(const struct gaim_xfer
*xfer
)
257 gaim_xfer_get_progress(const struct gaim_xfer
*xfer
)
262 if (gaim_xfer_get_size(xfer
) == 0)
265 return ((double)gaim_xfer_get_bytes_sent(xfer
) /
266 (double)gaim_xfer_get_size(xfer
));
270 gaim_xfer_get_local_ip(const struct gaim_xfer
*xfer
)
275 return xfer
->local_ip
;
279 gaim_xfer_get_local_port(const struct gaim_xfer
*xfer
)
284 return xfer
->local_port
;
288 gaim_xfer_get_remote_ip(const struct gaim_xfer
*xfer
)
293 return xfer
->remote_ip
;
297 gaim_xfer_get_remote_port(const struct gaim_xfer
*xfer
)
302 return xfer
->remote_port
;
306 gaim_xfer_set_completed(struct gaim_xfer
*xfer
, gboolean completed
)
308 struct gaim_xfer_ui_ops
*ui_ops
;
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
));
323 gaim_xfer_set_filename(struct gaim_xfer
*xfer
, const char *filename
)
328 if (xfer
->filename
!= NULL
)
329 g_free(xfer
->filename
);
331 xfer
->filename
= (filename
== NULL
? NULL
: g_strdup(filename
));
335 gaim_xfer_set_local_filename(struct gaim_xfer
*xfer
, const char *filename
)
340 if (xfer
->local_filename
!= NULL
)
341 g_free(xfer
->local_filename
);
343 xfer
->local_filename
= (filename
== NULL
? NULL
: g_strdup(filename
));
347 gaim_xfer_set_size(struct gaim_xfer
*xfer
, size_t size
)
353 xfer
->bytes_remaining
= size
- xfer
->bytes_sent
;
358 struct gaim_xfer_ui_ops
*
359 gaim_xfer_get_ui_ops(const struct gaim_xfer
*xfer
)
368 gaim_xfer_set_init_fnc(struct gaim_xfer
*xfer
,
369 void (*fnc
)(struct gaim_xfer
*))
374 xfer
->ops
.init
= fnc
;
379 gaim_xfer_set_read_fnc(struct gaim_xfer
*xfer
,
380 size_t (*fnc
)(char **, struct gaim_xfer
*))
385 xfer
->ops
.read
= fnc
;
389 gaim_xfer_set_write_fnc(struct gaim_xfer
*xfer
,
390 size_t (*fnc
)(const char *, size_t,
396 xfer
->ops
.write
= fnc
;
400 gaim_xfer_set_ack_fnc(struct gaim_xfer
*xfer
,
401 void (*fnc
)(struct gaim_xfer
*, const char *, size_t))
410 gaim_xfer_set_start_fnc(struct gaim_xfer
*xfer
,
411 void (*fnc
)(struct gaim_xfer
*))
416 xfer
->ops
.start
= fnc
;
420 gaim_xfer_set_end_fnc(struct gaim_xfer
*xfer
,
421 void (*fnc
)(struct gaim_xfer
*))
430 gaim_xfer_set_cancel_send_fnc(struct gaim_xfer
*xfer
,
431 void (*fnc
)(struct gaim_xfer
*))
436 xfer
->ops
.cancel_send
= fnc
;
440 gaim_xfer_set_cancel_recv_fnc(struct gaim_xfer
*xfer
,
441 void (*fnc
)(struct gaim_xfer
*))
446 xfer
->ops
.cancel_recv
= fnc
;
450 gaim_xfer_read(struct gaim_xfer
*xfer
, char **buffer
)
454 if (xfer
== NULL
|| buffer
== NULL
)
457 if (gaim_xfer_get_size(xfer
) == 0)
460 s
= MIN(gaim_xfer_get_bytes_remaining(xfer
), 4096);
462 if (xfer
->ops
.read
!= NULL
)
463 r
= (xfer
->ops
.read
)(buffer
, xfer
);
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
);
477 gaim_xfer_write(struct gaim_xfer
*xfer
, const char *buffer
, size_t size
)
481 if (xfer
== NULL
|| buffer
== NULL
|| size
== 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
);
489 r
= write(xfer
->fd
, buffer
, s
);
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
;
502 if (condition
& GAIM_INPUT_READ
) {
503 r
= gaim_xfer_read(xfer
, &buffer
);
505 fwrite(buffer
, 1, r
, xfer
->dest_fp
);
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
);
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
);
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
))
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
); /* ? */
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
);
573 connect_cb(gpointer data
, gint source
, GaimInputCondition condition
)
575 struct gaim_xfer
*xfer
= (struct gaim_xfer
*)data
;
579 begin_transfer(xfer
, condition
);
583 gaim_xfer_start(struct gaim_xfer
*xfer
, int fd
, const char *ip
,
586 GaimInputCondition cond
;
589 if (xfer
== NULL
|| gaim_xfer_get_type(xfer
) == GAIM_XFER_UNKNOWN
)
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
;
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
,
615 cond
= GAIM_INPUT_WRITE
;
620 begin_transfer(xfer
, cond
);
624 gaim_xfer_end(struct gaim_xfer
*xfer
)
629 /* See if we are actually trying to cancel this. */
630 if (!xfer
->completed
) {
631 gaim_xfer_cancel_local(xfer
);
635 if (xfer
->ops
.end
!= NULL
)
638 if (xfer
->watcher
!= 0) {
639 gaim_input_remove(xfer
->watcher
);
646 if (xfer
->dest_fp
!= NULL
) {
647 fclose(xfer
->dest_fp
);
648 xfer
->dest_fp
= NULL
;
653 gaim_xfer_cancel_local(struct gaim_xfer
*xfer
)
655 struct gaim_xfer_ui_ops
*ui_ops
;
660 if (gaim_xfer_get_type(xfer
) == GAIM_XFER_SEND
)
662 if (xfer
->ops
.cancel_send
!= NULL
)
663 xfer
->ops
.cancel_send(xfer
);
667 if (xfer
->ops
.cancel_recv
!= NULL
)
668 xfer
->ops
.cancel_recv(xfer
);
671 if (xfer
->watcher
!= 0) {
672 gaim_input_remove(xfer
->watcher
);
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;
693 gaim_xfer_cancel_remote(struct gaim_xfer
*xfer
)
695 struct gaim_xfer_ui_ops
*ui_ops
;
700 if (gaim_xfer_get_type(xfer
) == GAIM_XFER_SEND
)
702 if (xfer
->ops
.cancel_send
!= NULL
)
703 xfer
->ops
.cancel_send(xfer
);
707 if (xfer
->ops
.cancel_recv
!= NULL
)
708 xfer
->ops
.cancel_recv(xfer
);
711 if (xfer
->watcher
!= 0) {
712 gaim_input_remove(xfer
->watcher
);
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;
733 gaim_xfer_error(GaimXferType type
, const char *who
, const char *msg
)
737 if (msg
== NULL
|| type
== GAIM_XFER_UNKNOWN
)
740 if (type
== GAIM_XFER_SEND
)
741 title
= g_strdup_printf(_("File transfer to %s aborted.\n"), who
);
743 title
= g_strdup_printf(_("File transfer from %s aborted.\n"), who
);
745 gaim_notify_error(NULL
, NULL
, title
, msg
);
751 gaim_set_xfer_ui_ops(struct gaim_xfer_ui_ops
*ops
)
759 struct gaim_xfer_ui_ops
*
760 gaim_get_xfer_ui_ops(void)