Mercurial > pidgin
annotate src/ft.c @ 4064:b188b93ebf48
[gaim-migrate @ 4274]
(23:01:14) LSchiere: okay, this patch fixes at least some of the problems saving sound, away, and font preferences. it makes spin buttons and entry
widgets save prefs on loss of focus or dialog close instead of on change.
(23:01:17) LSchiere: anything else it does?
(23:01:44) faceprint: it makes nothing save unless you hit close. however, everything saves when you hit close.
(23:02:06) LSchiere: they still apply before close?
(23:02:09) faceprint: it should right most of the wrongs of prefs
(23:02:25) faceprint: they apply before close, but aren't written to disk every time you check or uncheck a checkbox or something
(23:02:27) Paco-Paco: it looks OK
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Thu, 12 Dec 2002 04:03:53 +0000 |
parents | a20bf3d247ff |
children | 1bd663beada5 |
rev | line source |
---|---|
3609 | 1 /* |
2 * gaim - file transfer functions | |
3 * | |
4 * Copyright (C) 2002, Wil Mahan <wtm2@duke.edu> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 * | |
20 */ | |
21 | |
22 #ifdef HAVE_CONFIG_H | |
23 #include <config.h> | |
24 #endif | |
25 | |
26 #include <sys/stat.h> | |
27 #include <unistd.h> | |
28 #include <errno.h> | |
29 #include <string.h> | |
30 | |
31 #define FT_BUFFER_SIZE (4096) | |
32 | |
33 #include <gtk/gtk.h> | |
34 #include "gaim.h" | |
35 #include "proxy.h" | |
36 #include "prpl.h" | |
37 | |
3716
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
38 #ifdef _WIN32 |
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
39 #include "win32dep.h" |
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
40 #endif |
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
41 |
3609 | 42 /* Completely describes a file transfer. Opaque to callers. */ |
43 struct file_transfer { | |
44 enum { FILE_TRANSFER_TYPE_SEND, FILE_TRANSFER_TYPE_RECEIVE } type; | |
45 | |
46 enum { | |
47 FILE_TRANSFER_STATE_ASK, /* waiting for confirmation */ | |
48 FILE_TRANSFER_STATE_CHOOSEFILE, /* waiting for file dialog */ | |
49 FILE_TRANSFER_STATE_TRANSFERRING, /* in actual transfer */ | |
50 FILE_TRANSFER_STATE_INTERRUPTED, /* other user canceled */ | |
51 FILE_TRANSFER_STATE_CANCELED, /* we canceled */ | |
52 FILE_TRANSFER_STATE_DONE, /* transfer complete */ | |
53 FILE_TRANSFER_STATE_CLEANUP /* freeing memory */ | |
54 } state; | |
55 | |
56 char *who; | |
57 | |
58 /* file selection dialog */ | |
59 GtkWidget *w; | |
60 char **names; | |
61 int *sizes; | |
62 char *dir; | |
63 char *initname; | |
64 FILE *file; | |
65 | |
66 /* connection info */ | |
67 struct gaim_connection *gc; | |
68 int fd; | |
69 int watcher; | |
70 | |
71 /* state */ | |
72 int totfiles; | |
73 int filesdone; | |
74 int totsize; | |
75 int bytessent; | |
76 int bytesleft; | |
77 }; | |
78 | |
3621 | 79 |
80 | |
3730 | 81 static int ft_choose_file(struct file_transfer *xfer); |
82 static void ft_cancel(struct file_transfer *xfer); | |
3609 | 83 static void ft_delete(struct file_transfer *xfer); |
3610 | 84 static void ft_callback(gpointer data, gint source, GaimInputCondition condition); |
3609 | 85 static void ft_nextfile(struct file_transfer *xfer); |
86 static int ft_mkdir(const char *name); | |
87 static int ft_mkdir_help(char *dir); | |
88 | |
89 static struct file_transfer *ft_new(int type, struct gaim_connection *gc, | |
90 const char *who) | |
91 { | |
92 struct file_transfer *xfer = g_new0(struct file_transfer, 1); | |
93 xfer->type = type; | |
94 xfer->state = FILE_TRANSFER_STATE_ASK; | |
95 xfer->gc = gc; | |
96 xfer->who = g_strdup(who); | |
97 xfer->filesdone = 0; | |
98 | |
99 return xfer; | |
100 } | |
101 | |
102 struct file_transfer *transfer_in_add(struct gaim_connection *gc, | |
103 const char *who, const char *initname, int totsize, | |
104 int totfiles, const char *msg) | |
105 { | |
106 struct file_transfer *xfer = ft_new(FILE_TRANSFER_TYPE_RECEIVE, gc, | |
107 who); | |
108 char *buf; | |
3621 | 109 char *sizebuf; |
3609 | 110 static const char *sizestr[4] = { "bytes", "KB", "MB", "GB" }; |
111 float sizemag = (float)totsize; | |
112 int szindex = 0; | |
113 | |
114 xfer->initname = g_strdup(initname); | |
115 xfer->totsize = totsize; | |
116 xfer->totfiles = totfiles; | |
117 xfer->filesdone = 0; | |
118 | |
119 /* Ask the user whether he or she accepts the transfer. */ | |
120 while ((szindex < 4) && (sizemag > 1024)) { | |
121 sizemag /= 1024; | |
122 szindex++; | |
123 } | |
124 | |
3621 | 125 if (totsize == -1) |
126 sizebuf = g_strdup_printf(_("Unkown")); | |
3609 | 127 else |
3621 | 128 sizebuf = g_strdup_printf("%.3g %s", sizemag, sizestr[szindex]); |
129 | |
130 if (xfer->totfiles == 1) { | |
131 buf = g_strdup_printf(_("%s requests that %s accept a file: %s (%s)"), | |
132 who, xfer->gc->username, initname, sizebuf); | |
133 } else { | |
134 buf = g_strdup_printf(_("%s requests that %s accept %d files: %s (%s)"), | |
3609 | 135 who, xfer->gc->username, xfer->totfiles, |
3621 | 136 initname, sizebuf); |
137 } | |
138 | |
139 g_free(sizebuf); | |
3609 | 140 |
141 if (msg) { | |
142 char *newmsg = g_strconcat(buf, ": ", msg, NULL); | |
143 g_free(buf); | |
144 buf = newmsg; | |
145 } | |
146 | |
3730 | 147 do_ask_dialog(buf, NULL, xfer, _("Accept"), ft_choose_file, _("Cancel"), ft_cancel); |
3609 | 148 g_free(buf); |
149 | |
150 return xfer; | |
151 } | |
152 | |
153 struct file_transfer *transfer_out_add(struct gaim_connection *gc, | |
154 const char *who) | |
155 { | |
156 struct file_transfer *xfer = ft_new(FILE_TRANSFER_TYPE_SEND, gc, | |
157 who); | |
158 | |
3730 | 159 ft_choose_file(xfer); |
3609 | 160 |
161 return xfer; | |
162 } | |
163 | |
164 /* We canceled the transfer, either by declining the initial | |
165 * confirmation dialog or canceling the file dialog. | |
166 */ | |
3730 | 167 static void ft_cancel(struct file_transfer *xfer) |
3609 | 168 { |
169 /* Make sure we weren't aborted while waiting for | |
170 * confirmation from the user. | |
171 */ | |
172 if (xfer->state == FILE_TRANSFER_STATE_INTERRUPTED) { | |
173 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
174 transfer_abort(xfer, NULL); | |
175 return; | |
176 } | |
177 | |
178 xfer->state = FILE_TRANSFER_STATE_CANCELED; | |
179 if (xfer->w) { | |
180 gtk_widget_destroy(xfer->w); | |
181 xfer->w = NULL; | |
182 } | |
183 | |
184 if (xfer->gc->prpl->file_transfer_cancel) | |
185 xfer->gc->prpl->file_transfer_cancel(xfer->gc, xfer); | |
186 | |
187 ft_delete(xfer); | |
188 } | |
189 | |
190 /* This is called when the other user aborts the transfer, | |
191 * possibly in the middle of a transfer. | |
192 */ | |
193 int transfer_abort(struct file_transfer *xfer, const char *why) | |
194 { | |
195 if (xfer->state == FILE_TRANSFER_STATE_INTERRUPTED) { | |
196 /* If for some reason we have already been | |
197 * here and are waiting on some event before | |
198 * cleaning up, but we get another abort request, | |
199 * we don't need to do anything else. | |
200 */ | |
201 return 1; | |
202 } | |
203 else if (xfer->state == FILE_TRANSFER_STATE_ASK) { | |
204 /* Kludge: since there is no way to kill a | |
205 * do_ask_dialog() window, we just note the | |
206 * status here and clean up after the user | |
207 * makes a selection. | |
208 */ | |
209 xfer->state = FILE_TRANSFER_STATE_INTERRUPTED; | |
210 return 1; | |
211 } | |
212 else if (xfer->state == FILE_TRANSFER_STATE_TRANSFERRING) { | |
213 if (xfer->watcher) { | |
214 gaim_input_remove(xfer->watcher); | |
215 xfer->watcher = 0; | |
216 } | |
217 if (xfer->file) { | |
218 fclose(xfer->file); | |
219 xfer->file = NULL; | |
220 } | |
221 /* XXX theoretically, there is a race condition here, | |
222 * because we could be inside ft_callback() when we | |
223 * free xfer below, with undefined results. Since | |
224 * we use non-blocking IO, this doesn't seem to be | |
225 * a problem, but it still makes me nervous--I don't | |
226 * know how to fix it other than using locks, though. | |
227 * -- wtm | |
228 */ | |
229 } | |
230 else if (xfer->state == FILE_TRANSFER_STATE_CHOOSEFILE) { | |
231 /* It's safe to clean up now. Just make sure we | |
232 * destroy the dialog window first. | |
233 */ | |
234 if (xfer->w) { | |
235 gtk_signal_disconnect_by_func(GTK_OBJECT(xfer->w), | |
236 GTK_SIGNAL_FUNC(ft_cancel), xfer); | |
237 gtk_widget_destroy(xfer->w); | |
238 xfer->w = NULL; | |
239 } | |
240 } | |
241 | |
242 /* Let the user know that we were aborted, unless we already | |
243 * finished or the user aborted first. | |
244 */ | |
245 /* if ((xfer->state != FILE_TRANSFER_STATE_DONE) && | |
246 (xfer->state != FILE_TRANSFER_STATE_CANCELED)) { */ | |
247 if (why) { | |
248 char *msg; | |
249 | |
250 if (xfer->type == FILE_TRANSFER_TYPE_SEND) | |
251 msg = g_strdup_printf(_("File transfer to %s aborted."), xfer->who); | |
252 else | |
253 msg = g_strdup_printf(_("File transfer from %s aborted."), xfer->who); | |
254 do_error_dialog(msg, why, GAIM_ERROR); | |
255 g_free(msg); | |
256 } | |
257 | |
258 ft_delete(xfer); | |
259 | |
260 return 0; | |
261 } | |
262 | |
263 | |
264 static void ft_delete(struct file_transfer *xfer) | |
265 { | |
266 if (xfer->names) | |
267 g_strfreev(xfer->names); | |
268 if (xfer->initname) | |
269 g_free(xfer->initname); | |
270 if (xfer->who) | |
271 g_free(xfer->who); | |
272 if (xfer->sizes) | |
273 g_free(xfer->sizes); | |
274 g_free(xfer); | |
275 } | |
276 | |
277 static void ft_choose_ok(gpointer a, struct file_transfer *xfer) { | |
278 gboolean exists, is_dir; | |
279 struct stat st; | |
280 const char *err = NULL; | |
281 | |
282 xfer->names = gtk_file_selection_get_selections(GTK_FILE_SELECTION(xfer->w)); | |
283 exists = !stat(*xfer->names, &st); | |
284 is_dir = (exists) ? S_ISDIR(st.st_mode) : 0; | |
285 | |
286 if (exists) { | |
287 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) | |
288 /* XXX overwrite/append/cancel prompt */ | |
289 err = _("That file already exists; please choose another name."); | |
290 else { /* (xfer->type == FILE_TRANSFER_TYPE_SEND) */ | |
291 char **cur; | |
292 /* First find the total number of files, | |
293 * so we know how much space to allocate. | |
294 */ | |
295 xfer->totfiles = 0; | |
296 for (cur = xfer->names; *cur; cur++) { | |
297 xfer->totfiles++; | |
298 } | |
299 | |
300 /* Now get sizes for each file. */ | |
301 xfer->totsize = st.st_size; | |
302 xfer->sizes = g_malloc(xfer->totfiles | |
303 * sizeof(*xfer->sizes)); | |
304 xfer->sizes[0] = st.st_size; | |
305 for (cur = xfer->names + 1; *cur; cur++) { | |
306 exists = !stat(*cur, &st); | |
307 if (!exists) { | |
308 err = _("File not found."); | |
309 break; | |
310 } | |
311 xfer->sizes[cur - xfer->names] = | |
312 st.st_size; | |
313 xfer->totsize += st.st_size; | |
314 } | |
315 } | |
316 } | |
317 else { /* doesn't exist */ | |
318 if (xfer->type == FILE_TRANSFER_TYPE_SEND) | |
319 err = _("File not found."); | |
320 else if (xfer->totfiles > 1) { | |
321 if (!xfer->names[0] || xfer->names[1]) { | |
322 err = _("You may only choose one new directory."); | |
323 } | |
324 else { | |
325 if (ft_mkdir(*xfer->names)) | |
326 err = _("Unable to create directory."); | |
327 else | |
328 xfer->dir = g_strconcat(xfer->names[0], | |
329 "/", NULL); | |
330 } | |
331 } | |
332 } | |
333 | |
334 if (err) | |
335 do_error_dialog(err, NULL, GAIM_ERROR); | |
336 else { | |
337 /* File name looks valid */ | |
338 gtk_widget_destroy(xfer->w); | |
339 xfer->w = NULL; | |
340 | |
341 if (xfer->type == FILE_TRANSFER_TYPE_SEND) { | |
342 char *desc; | |
343 if (xfer->totfiles == 1) | |
344 desc = *xfer->names; | |
345 else | |
346 /* XXX what else? */ | |
347 desc = "*"; | |
348 /* desc = g_path_get_basename(g_path_get_dirname(*xfer->names)); */ | |
349 xfer->gc->prpl->file_transfer_out(xfer->gc, xfer, | |
350 desc, xfer->totfiles, | |
351 xfer->totsize); | |
352 } | |
353 else | |
354 xfer->gc->prpl->file_transfer_in(xfer->gc, xfer, | |
355 0); /* XXX */ | |
356 } | |
357 } | |
358 | |
359 /* Called on outgoing transfers to get information about the | |
360 * current file. | |
361 */ | |
362 int transfer_get_file_info(struct file_transfer *xfer, int *size, | |
363 char **name) | |
364 { | |
365 *size = xfer->sizes[xfer->filesdone]; | |
366 *name = xfer->names[xfer->filesdone]; | |
367 return 0; | |
368 } | |
369 | |
3730 | 370 static int ft_choose_file(struct file_transfer *xfer) |
3609 | 371 { |
372 char *curdir = g_get_current_dir(); /* should be freed */ | |
373 char *initstr; | |
374 | |
375 /* If the connection is interrupted while we are waiting | |
376 * for do_ask_dialog(), then we can't clean up until we | |
377 * get here, after the user makes a selection. | |
378 */ | |
379 if (xfer->state == FILE_TRANSFER_STATE_INTERRUPTED) { | |
380 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
381 transfer_abort(xfer, NULL); | |
382 return 1; | |
383 } | |
384 | |
385 xfer->state = FILE_TRANSFER_STATE_CHOOSEFILE; | |
386 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) | |
387 xfer->w = gtk_file_selection_new(_("Gaim - Save As...")); | |
388 else /* (xfer->type == FILE_TRANSFER_TYPE_SEND) */ { | |
389 xfer->w = gtk_file_selection_new(_("Gaim - Open...")); | |
390 gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(xfer->w), | |
391 1); | |
392 } | |
393 | |
394 if (xfer->initname) { | |
395 initstr = g_strdup_printf("%s/%s", curdir, xfer->initname); | |
396 } else | |
397 initstr = g_strconcat(curdir, "/", NULL); | |
398 g_free(curdir); | |
399 | |
400 gtk_file_selection_set_filename(GTK_FILE_SELECTION(xfer->w), | |
401 initstr); | |
402 g_free(initstr); | |
403 | |
404 gtk_signal_connect(GTK_OBJECT(xfer->w), "delete_event", | |
405 GTK_SIGNAL_FUNC(ft_cancel), xfer); | |
406 gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(xfer->w)->cancel_button), | |
407 "clicked", GTK_SIGNAL_FUNC(ft_cancel), xfer); | |
408 gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(xfer->w)->ok_button), | |
409 "clicked", GTK_SIGNAL_FUNC(ft_choose_ok), xfer); | |
410 gtk_widget_show(xfer->w); | |
411 | |
412 return 0; | |
413 } | |
414 | |
415 static int ft_open_file(struct file_transfer *xfer, const char *filename, | |
416 int offset) | |
417 { | |
418 char *err = NULL; | |
419 | |
420 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) { | |
421 xfer->file = fopen(filename, | |
422 (offset > 0) ? "a" : "w"); | |
423 | |
424 if (!xfer->file) | |
425 err = g_strdup_printf(_("Could not open %s for writing: %s"), | |
426 filename, g_strerror(errno)); | |
427 | |
428 } | |
429 else /* (xfer->type == FILE_TRANSFER_TYPE_SEND) */ { | |
430 xfer->file = fopen(filename, "r"); | |
431 if (!xfer->file) | |
432 err = g_strdup_printf(_("Could not open %s for reading: %s"), | |
433 filename, g_strerror(errno)); | |
434 } | |
435 | |
436 if (err) { | |
437 do_error_dialog(err, NULL, GAIM_ERROR); | |
438 g_free(err); | |
439 return -1; | |
440 } | |
441 | |
442 fseek(xfer->file, offset, SEEK_SET); | |
443 | |
444 return 0; | |
445 } | |
446 | |
447 /* Takes a full file name, and creates any directories above it | |
448 * that don't exist already. | |
449 */ | |
450 static int ft_mkdir(const char *name) { | |
451 int ret = 0; | |
452 struct stat st; | |
453 mode_t m = umask(0077); | |
454 char *dir; | |
455 | |
456 dir = g_path_get_dirname(name); | |
457 if (stat(dir, &st)) | |
458 ret = ft_mkdir_help(dir); | |
459 | |
460 g_free(dir); | |
461 umask(m); | |
462 return ret; | |
463 } | |
464 | |
465 /* Two functions, one recursive, just to make a directory. Yuck. */ | |
466 static int ft_mkdir_help(char *dir) { | |
467 int ret; | |
3716
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
468 |
3611 | 469 ret = mkdir(dir, 0775); |
3716
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
470 |
3609 | 471 if (ret) { |
472 char *index = strrchr(dir, G_DIR_SEPARATOR); | |
473 if (!index) | |
474 return -1; | |
475 *index = '\0'; | |
476 ret = ft_mkdir_help(dir); | |
477 *index = G_DIR_SEPARATOR; | |
478 if (!ret) | |
3611 | 479 ret = mkdir(dir, 0775); |
3609 | 480 } |
481 | |
482 return ret; | |
483 } | |
484 | |
485 int transfer_in_do(struct file_transfer *xfer, int fd, | |
486 const char *filename, int size) | |
487 { | |
488 char *fullname; | |
489 | |
490 xfer->state = FILE_TRANSFER_STATE_TRANSFERRING; | |
491 xfer->fd = fd; | |
492 xfer->bytesleft = size; | |
493 | |
494 /* XXX implement resuming incoming transfers */ | |
495 #if 0 | |
496 if (xfer->sizes) | |
497 xfer->bytesleft -= xfer->sizes[0]; | |
498 #endif | |
499 | |
500 /* Security check */ | |
501 if (g_strrstr(filename, "..")) { | |
502 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
503 transfer_abort(xfer, _("Invalid incoming filename component")); | |
504 return -1; | |
505 } | |
506 | |
507 if (xfer->totfiles > 1) | |
508 fullname = g_strconcat(xfer->dir, filename, NULL); | |
509 else | |
510 /* Careful: filename is the name on the *other* | |
511 * end; don't use it here. */ | |
512 fullname = g_strdup(xfer->names[xfer->filesdone]); | |
513 | |
514 | |
515 if (ft_mkdir(fullname)) { | |
516 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
517 transfer_abort(xfer, _("Invalid incoming filename")); | |
518 return -1; | |
519 } | |
520 | |
521 if (!ft_open_file(xfer, fullname, 0)) { | |
522 /* Special case: if we are receiving an empty file, | |
523 * we would never enter the callback. Just avoid the | |
524 * callback altogether. | |
525 */ | |
526 if (xfer->bytesleft == 0) | |
527 ft_nextfile(xfer); | |
528 else | |
529 xfer->watcher = gaim_input_add(fd, | |
530 GAIM_INPUT_READ, | |
531 ft_callback, xfer); | |
532 } else { | |
533 /* Error opening file */ | |
534 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
535 transfer_abort(xfer, NULL); | |
536 g_free(fullname); | |
537 return -1; | |
538 } | |
539 | |
540 g_free(fullname); | |
541 return 0; | |
542 } | |
543 | |
544 int transfer_out_do(struct file_transfer *xfer, int fd, int offset) { | |
545 xfer->state = FILE_TRANSFER_STATE_TRANSFERRING; | |
546 xfer->fd = fd; | |
547 xfer->bytesleft = xfer->sizes[xfer->filesdone] - offset; | |
548 | |
549 if (!ft_open_file(xfer, xfer->names[xfer->filesdone], offset)) { | |
550 /* Special case: see transfer_in_do(). | |
551 */ | |
552 if (xfer->bytesleft == 0) | |
553 ft_nextfile(xfer); | |
554 else | |
555 xfer->watcher = gaim_input_add(fd, | |
556 GAIM_INPUT_WRITE, ft_callback, | |
557 xfer); | |
558 } | |
559 else { | |
560 /* Error opening file */ | |
561 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
562 transfer_abort(xfer, NULL); | |
563 return -1; | |
564 } | |
565 | |
566 return 0; | |
567 } | |
568 | |
569 static void ft_callback(gpointer data, gint source, | |
570 GaimInputCondition condition) | |
571 { | |
572 struct file_transfer *xfer = (struct file_transfer *)data; | |
573 int rt, i; | |
574 char buf[FT_BUFFER_SIZE]; | |
575 | |
576 if (condition & GAIM_INPUT_READ) { | |
577 rt = read(xfer->fd, buf, MIN(xfer->bytesleft, FT_BUFFER_SIZE)); | |
578 /* XXX What if the transfer is interrupted while we | |
579 * are inside read()? How can this be handled safely? | |
580 * -- wtm | |
581 */ | |
582 if (rt > 0) { | |
583 xfer->bytesleft -= rt; | |
584 for (i = 0; i < rt; i++) { | |
585 fprintf(xfer->file, "%c", buf[i]); | |
586 } | |
587 } | |
588 | |
589 } | |
590 else /* (condition & GAIM_INPUT_WRITE) */ { | |
591 int remain = MIN(xfer->bytesleft, FT_BUFFER_SIZE); | |
592 | |
593 for (i = 0; i < remain; i++) | |
594 fscanf(xfer->file, "%c", &buf[i]); | |
595 | |
596 rt = write(xfer->fd, buf, remain); | |
597 if (rt > 0) | |
598 xfer->bytesleft -= rt; | |
599 } | |
600 | |
601 if (rt < 0) | |
602 return; | |
603 | |
604 xfer->bytessent += rt; | |
605 | |
606 if (xfer->gc->prpl->file_transfer_data_chunk) | |
607 xfer->gc->prpl->file_transfer_data_chunk(xfer->gc, xfer, buf, rt); | |
608 | |
609 if (rt > 0 && xfer->bytesleft == 0) { | |
610 /* We are done with this file! */ | |
611 gaim_input_remove(xfer->watcher); | |
612 xfer->watcher = 0; | |
613 fclose(xfer->file); | |
614 xfer->file = 0; | |
615 ft_nextfile(xfer); | |
616 } | |
617 } | |
618 | |
619 static void ft_nextfile(struct file_transfer *xfer) | |
620 { | |
621 debug_printf("file transfer %d of %d done\n", | |
622 xfer->filesdone + 1, xfer->totfiles); | |
623 | |
624 if (++xfer->filesdone == xfer->totfiles) { | |
625 char *msg; | |
626 char *msg2; | |
627 | |
628 xfer->gc->prpl->file_transfer_done(xfer->gc, xfer); | |
629 | |
630 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) | |
631 msg = g_strdup_printf(_("File transfer from %s to %s completed successfully."), | |
632 xfer->who, xfer->gc->username); | |
633 else | |
634 msg = g_strdup_printf(_("File transfer from %s to %s completed successfully."), | |
635 xfer->gc->username, xfer->who); | |
636 xfer->state = FILE_TRANSFER_STATE_DONE; | |
637 | |
638 if (xfer->totfiles > 1) | |
639 msg2 = g_strdup_printf(_("%d files transferred."), | |
640 xfer->totfiles); | |
641 else | |
642 msg2 = NULL; | |
643 | |
644 do_error_dialog(msg, msg2, GAIM_INFO); | |
645 g_free(msg); | |
646 if (msg2) | |
647 g_free(msg2); | |
648 | |
649 ft_delete(xfer); | |
650 } | |
651 else { | |
652 xfer->gc->prpl->file_transfer_nextfile(xfer->gc, xfer); | |
653 } | |
654 } | |
655 |