Mercurial > pidgin
annotate src/ft.c @ 4243:eae97ca4bbea
[gaim-migrate @ 4493]
Guess what?
Another SSI patch!
This one fixes the automatic deletion of empty groups upon signin.
The problem was that apparently WinICQ handles empty groups slightly
differently than WinAIM. It's all good now.
Uh, I fixed some comments.
Oh, and moving a buddy that you've requested authorization from to
a different group. I don't know if that used to give you the dialog
that prompted if you wanted to send another auth request, but it
shouldn't anymore (as long as you have sent 1 auth request).
I also changed the button title for ICQ's receive contacts. I changed
it from "Deny" to "Decline." Thanks to Nathan for pointing that out.
I am to Time Warner as spiders are to the bottom of my shoe.
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Wed, 08 Jan 2003 04:06:20 +0000 |
| parents | 511c2b63caa4 |
| children | cd84b0fd63fc |
| 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), | |
|
4162
d3c8d2b40494
[gaim-migrate @ 4391]
Christian Hammond <chipx86@chipx86.com>
parents:
4150
diff
changeset
|
236 G_CALLBACK(ft_cancel), xfer); |
| 3609 | 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 | |
|
4162
d3c8d2b40494
[gaim-migrate @ 4391]
Christian Hammond <chipx86@chipx86.com>
parents:
4150
diff
changeset
|
404 g_signal_connect(GTK_OBJECT(xfer->w), "delete_event", |
|
d3c8d2b40494
[gaim-migrate @ 4391]
Christian Hammond <chipx86@chipx86.com>
parents:
4150
diff
changeset
|
405 G_CALLBACK(ft_cancel), xfer); |
|
d3c8d2b40494
[gaim-migrate @ 4391]
Christian Hammond <chipx86@chipx86.com>
parents:
4150
diff
changeset
|
406 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(xfer->w)->cancel_button), |
|
d3c8d2b40494
[gaim-migrate @ 4391]
Christian Hammond <chipx86@chipx86.com>
parents:
4150
diff
changeset
|
407 "clicked", G_CALLBACK(ft_cancel), xfer); |
|
d3c8d2b40494
[gaim-migrate @ 4391]
Christian Hammond <chipx86@chipx86.com>
parents:
4150
diff
changeset
|
408 g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(xfer->w)->ok_button), |
|
d3c8d2b40494
[gaim-migrate @ 4391]
Christian Hammond <chipx86@chipx86.com>
parents:
4150
diff
changeset
|
409 "clicked", G_CALLBACK(ft_choose_ok), xfer); |
| 3609 | 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; | |
|
4201
511c2b63caa4
[gaim-migrate @ 4432]
Christian Hammond <chipx86@chipx86.com>
parents:
4162
diff
changeset
|
573 int rt; |
| 4150 | 574 char *buf = NULL; |
| 3609 | 575 |
| 576 if (condition & GAIM_INPUT_READ) { | |
| 4150 | 577 if (xfer->gc->prpl->file_transfer_read) |
| 578 rt = xfer->gc->prpl->file_transfer_read(xfer->gc, xfer, | |
| 579 xfer->fd, &buf); | |
| 580 else { | |
| 581 buf = g_new0(char, MIN(xfer->bytesleft, FT_BUFFER_SIZE)); | |
| 582 rt = read(xfer->fd, buf, MIN(xfer->bytesleft, FT_BUFFER_SIZE)); | |
| 583 } | |
| 584 | |
| 3609 | 585 /* XXX What if the transfer is interrupted while we |
| 586 * are inside read()? How can this be handled safely? | |
| 587 * -- wtm | |
| 588 */ | |
| 589 if (rt > 0) { | |
| 590 xfer->bytesleft -= rt; | |
| 4150 | 591 fwrite(buf, 1, rt, xfer->file); |
| 3609 | 592 } |
| 593 | |
| 594 } | |
| 595 else /* (condition & GAIM_INPUT_WRITE) */ { | |
| 596 int remain = MIN(xfer->bytesleft, FT_BUFFER_SIZE); | |
| 597 | |
| 4150 | 598 buf = g_new0(char, remain); |
| 599 | |
| 600 fread(buf, 1, remain, xfer->file); | |
| 3609 | 601 |
| 4150 | 602 if (xfer->gc->prpl->file_transfer_write) |
| 603 rt = xfer->gc->prpl->file_transfer_write(xfer->gc, xfer, xfer->fd, | |
| 604 buf, remain); | |
| 605 else | |
| 606 rt = write(xfer->fd, buf, remain); | |
| 607 | |
| 3609 | 608 if (rt > 0) |
| 609 xfer->bytesleft -= rt; | |
| 610 } | |
| 611 | |
| 4150 | 612 if (rt < 0) { |
| 613 if (buf != NULL) | |
| 614 g_free(buf); | |
| 615 | |
| 3609 | 616 return; |
| 4150 | 617 } |
| 3609 | 618 |
| 619 xfer->bytessent += rt; | |
| 620 | |
| 621 if (xfer->gc->prpl->file_transfer_data_chunk) | |
| 622 xfer->gc->prpl->file_transfer_data_chunk(xfer->gc, xfer, buf, rt); | |
| 623 | |
| 624 if (rt > 0 && xfer->bytesleft == 0) { | |
| 625 /* We are done with this file! */ | |
| 626 gaim_input_remove(xfer->watcher); | |
| 627 xfer->watcher = 0; | |
| 628 fclose(xfer->file); | |
| 629 xfer->file = 0; | |
| 630 ft_nextfile(xfer); | |
| 631 } | |
| 4150 | 632 |
| 633 if (buf != NULL) | |
| 634 g_free(buf); | |
| 3609 | 635 } |
| 636 | |
| 637 static void ft_nextfile(struct file_transfer *xfer) | |
| 638 { | |
| 639 debug_printf("file transfer %d of %d done\n", | |
| 640 xfer->filesdone + 1, xfer->totfiles); | |
| 641 | |
| 642 if (++xfer->filesdone == xfer->totfiles) { | |
| 643 char *msg; | |
| 644 char *msg2; | |
| 645 | |
| 646 xfer->gc->prpl->file_transfer_done(xfer->gc, xfer); | |
| 647 | |
| 648 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) | |
| 649 msg = g_strdup_printf(_("File transfer from %s to %s completed successfully."), | |
| 650 xfer->who, xfer->gc->username); | |
| 651 else | |
| 652 msg = g_strdup_printf(_("File transfer from %s to %s completed successfully."), | |
| 653 xfer->gc->username, xfer->who); | |
| 654 xfer->state = FILE_TRANSFER_STATE_DONE; | |
| 655 | |
| 656 if (xfer->totfiles > 1) | |
| 657 msg2 = g_strdup_printf(_("%d files transferred."), | |
| 658 xfer->totfiles); | |
| 659 else | |
| 660 msg2 = NULL; | |
| 661 | |
| 662 do_error_dialog(msg, msg2, GAIM_INFO); | |
| 663 g_free(msg); | |
| 664 if (msg2) | |
| 665 g_free(msg2); | |
| 666 | |
| 667 ft_delete(xfer); | |
| 668 } | |
| 669 else { | |
| 670 xfer->gc->prpl->file_transfer_nextfile(xfer->gc, xfer); | |
| 671 } | |
| 672 } | |
| 673 |
