Mercurial > pidgin
annotate src/ft.c @ 4359:5fb47ec9bfe4
[gaim-migrate @ 4625]
Wow, okay, where to begin with this one ;)
I rewrote the whole conversation backend. It is now core/UI split. Here's
how it works..
Every conversation is represented by a gaim_conversation structure. This
branches out into gaim_im and gaim_chat structures. Every conversation
lives in (well, normally, but it doesn't have to) a gaim_window structure.
This is a _CORE_ representation of a window. There can be multiple
gaim_window structures around.
The gaim_window and gaim_conversation structures have UI-specific operation
structures associated with them. At the moment, the only UI is GTK+, and
this will be for some time. Don't start thinking you can write a QT UI now.
It's just not going to happen.
Everything that is done on a conversation is done through the core API.
This API does core processing and then calls the UI operations for the
rendering and anything else.
Now, what does this give the user?
- Multiple windows.
- Multiple tabs per window.
- Draggable tabs.
- Send As menu is moved to the menubar.
- Menubar for chats.
- Some very cool stuff in the future, like replacing, say, IRC chat windows
with an X-Chat interface, or whatever.
- Later on, customizable window/conversation positioning.
For developers:
- Fully documented API
- Core/UI split
- Variable checking and mostly sane handling of incorrect variables.
- Logical structure to conversations, both core and UI.
- Some very cool stuff in the future, like replacing, say, IRC chat windows
with an X-Chat interface, or whatever.
- Later on, customizable window/conversation positioning.
- Oh yeah, and the beginning of a stock icon system.
Now, there are things that aren't there yet. You will see tabs even if you
have them turned off. This will be fixed in time. Also, the preferences
will change to work with the new structure. I'm starting school in 2 days,
so it may not be done immediately, but hopefully in the next week.
Enjoy!
committer: Tailor Script <tailor@pidgin.im>
| author | Christian Hammond <chipx86@chipx86.com> |
|---|---|
| date | Mon, 20 Jan 2003 09:10:23 +0000 |
| parents | 9c7fcb211886 |
| children | 7521e29658bc |
| 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); | |
|
4247
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
88 static gboolean ft_choose_file_close(GtkWidget *widget, GdkEvent *event, |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
89 gpointer user_data); |
| 3609 | 90 |
| 91 static struct file_transfer *ft_new(int type, struct gaim_connection *gc, | |
| 92 const char *who) | |
| 93 { | |
| 94 struct file_transfer *xfer = g_new0(struct file_transfer, 1); | |
| 95 xfer->type = type; | |
| 96 xfer->state = FILE_TRANSFER_STATE_ASK; | |
| 97 xfer->gc = gc; | |
| 98 xfer->who = g_strdup(who); | |
| 99 xfer->filesdone = 0; | |
|
4247
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
100 xfer->w = NULL; |
| 3609 | 101 |
| 102 return xfer; | |
| 103 } | |
| 104 | |
| 105 struct file_transfer *transfer_in_add(struct gaim_connection *gc, | |
| 106 const char *who, const char *initname, int totsize, | |
| 107 int totfiles, const char *msg) | |
| 108 { | |
| 109 struct file_transfer *xfer = ft_new(FILE_TRANSFER_TYPE_RECEIVE, gc, | |
| 110 who); | |
| 111 char *buf; | |
| 3621 | 112 char *sizebuf; |
| 3609 | 113 static const char *sizestr[4] = { "bytes", "KB", "MB", "GB" }; |
| 114 float sizemag = (float)totsize; | |
| 115 int szindex = 0; | |
| 116 | |
| 117 xfer->initname = g_strdup(initname); | |
| 118 xfer->totsize = totsize; | |
| 119 xfer->totfiles = totfiles; | |
| 120 xfer->filesdone = 0; | |
| 121 | |
| 122 /* Ask the user whether he or she accepts the transfer. */ | |
| 123 while ((szindex < 4) && (sizemag > 1024)) { | |
| 124 sizemag /= 1024; | |
| 125 szindex++; | |
| 126 } | |
| 127 | |
| 3621 | 128 if (totsize == -1) |
| 129 sizebuf = g_strdup_printf(_("Unkown")); | |
| 3609 | 130 else |
| 3621 | 131 sizebuf = g_strdup_printf("%.3g %s", sizemag, sizestr[szindex]); |
| 132 | |
| 133 if (xfer->totfiles == 1) { | |
| 134 buf = g_strdup_printf(_("%s requests that %s accept a file: %s (%s)"), | |
| 135 who, xfer->gc->username, initname, sizebuf); | |
| 136 } else { | |
| 137 buf = g_strdup_printf(_("%s requests that %s accept %d files: %s (%s)"), | |
| 3609 | 138 who, xfer->gc->username, xfer->totfiles, |
| 3621 | 139 initname, sizebuf); |
| 140 } | |
| 141 | |
| 142 g_free(sizebuf); | |
| 3609 | 143 |
| 144 if (msg) { | |
| 145 char *newmsg = g_strconcat(buf, ": ", msg, NULL); | |
| 146 g_free(buf); | |
| 147 buf = newmsg; | |
| 148 } | |
| 149 | |
| 4249 | 150 do_ask_dialog(buf, NULL, xfer, _("Accept"), ft_choose_file, _("Cancel"), ft_cancel, NULL, FALSE); |
| 3609 | 151 g_free(buf); |
| 152 | |
| 153 return xfer; | |
| 154 } | |
| 155 | |
| 156 struct file_transfer *transfer_out_add(struct gaim_connection *gc, | |
| 157 const char *who) | |
| 158 { | |
| 159 struct file_transfer *xfer = ft_new(FILE_TRANSFER_TYPE_SEND, gc, | |
| 160 who); | |
| 161 | |
| 3730 | 162 ft_choose_file(xfer); |
| 3609 | 163 |
| 164 return xfer; | |
| 165 } | |
| 166 | |
| 167 /* We canceled the transfer, either by declining the initial | |
| 168 * confirmation dialog or canceling the file dialog. | |
| 169 */ | |
| 3730 | 170 static void ft_cancel(struct file_transfer *xfer) |
| 3609 | 171 { |
|
4247
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
172 debug_printf("** ft_cancel\n"); |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
173 |
| 3609 | 174 /* Make sure we weren't aborted while waiting for |
| 175 * confirmation from the user. | |
| 176 */ | |
| 177 if (xfer->state == FILE_TRANSFER_STATE_INTERRUPTED) { | |
| 178 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
| 179 transfer_abort(xfer, NULL); | |
| 180 return; | |
| 181 } | |
| 182 | |
| 183 xfer->state = FILE_TRANSFER_STATE_CANCELED; | |
| 184 if (xfer->w) { | |
| 185 gtk_widget_destroy(xfer->w); | |
| 186 xfer->w = NULL; | |
| 187 } | |
| 188 | |
| 189 if (xfer->gc->prpl->file_transfer_cancel) | |
| 190 xfer->gc->prpl->file_transfer_cancel(xfer->gc, xfer); | |
| 191 | |
| 192 ft_delete(xfer); | |
| 193 } | |
| 194 | |
| 195 /* This is called when the other user aborts the transfer, | |
| 196 * possibly in the middle of a transfer. | |
| 197 */ | |
| 198 int transfer_abort(struct file_transfer *xfer, const char *why) | |
| 199 { | |
| 200 if (xfer->state == FILE_TRANSFER_STATE_INTERRUPTED) { | |
| 201 /* If for some reason we have already been | |
| 202 * here and are waiting on some event before | |
| 203 * cleaning up, but we get another abort request, | |
| 204 * we don't need to do anything else. | |
| 205 */ | |
| 206 return 1; | |
| 207 } | |
| 208 else if (xfer->state == FILE_TRANSFER_STATE_ASK) { | |
| 209 /* Kludge: since there is no way to kill a | |
| 210 * do_ask_dialog() window, we just note the | |
| 211 * status here and clean up after the user | |
| 212 * makes a selection. | |
| 213 */ | |
| 214 xfer->state = FILE_TRANSFER_STATE_INTERRUPTED; | |
| 215 return 1; | |
| 216 } | |
| 217 else if (xfer->state == FILE_TRANSFER_STATE_TRANSFERRING) { | |
| 218 if (xfer->watcher) { | |
| 219 gaim_input_remove(xfer->watcher); | |
| 220 xfer->watcher = 0; | |
| 221 } | |
| 222 if (xfer->file) { | |
| 223 fclose(xfer->file); | |
| 224 xfer->file = NULL; | |
| 225 } | |
| 226 /* XXX theoretically, there is a race condition here, | |
| 227 * because we could be inside ft_callback() when we | |
| 228 * free xfer below, with undefined results. Since | |
| 229 * we use non-blocking IO, this doesn't seem to be | |
| 230 * a problem, but it still makes me nervous--I don't | |
| 231 * know how to fix it other than using locks, though. | |
| 232 * -- wtm | |
| 233 */ | |
| 234 } | |
| 235 else if (xfer->state == FILE_TRANSFER_STATE_CHOOSEFILE) { | |
| 236 /* It's safe to clean up now. Just make sure we | |
| 237 * destroy the dialog window first. | |
| 238 */ | |
| 239 if (xfer->w) { | |
|
4247
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
240 g_signal_handlers_disconnect_by_func(G_OBJECT(xfer->w), |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
241 G_CALLBACK(ft_choose_file_close), xfer); |
| 3609 | 242 gtk_widget_destroy(xfer->w); |
| 243 xfer->w = NULL; | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 /* Let the user know that we were aborted, unless we already | |
| 248 * finished or the user aborted first. | |
| 249 */ | |
| 250 /* if ((xfer->state != FILE_TRANSFER_STATE_DONE) && | |
| 251 (xfer->state != FILE_TRANSFER_STATE_CANCELED)) { */ | |
| 252 if (why) { | |
| 253 char *msg; | |
| 254 | |
| 255 if (xfer->type == FILE_TRANSFER_TYPE_SEND) | |
| 256 msg = g_strdup_printf(_("File transfer to %s aborted."), xfer->who); | |
| 257 else | |
| 258 msg = g_strdup_printf(_("File transfer from %s aborted."), xfer->who); | |
| 259 do_error_dialog(msg, why, GAIM_ERROR); | |
| 260 g_free(msg); | |
| 261 } | |
| 262 | |
| 263 ft_delete(xfer); | |
| 264 | |
| 265 return 0; | |
| 266 } | |
| 267 | |
| 268 | |
| 269 static void ft_delete(struct file_transfer *xfer) | |
| 270 { | |
| 271 if (xfer->names) | |
| 272 g_strfreev(xfer->names); | |
| 273 if (xfer->initname) | |
| 274 g_free(xfer->initname); | |
| 275 if (xfer->who) | |
| 276 g_free(xfer->who); | |
| 277 if (xfer->sizes) | |
| 278 g_free(xfer->sizes); | |
| 279 g_free(xfer); | |
| 280 } | |
| 281 | |
| 282 static void ft_choose_ok(gpointer a, struct file_transfer *xfer) { | |
| 283 gboolean exists, is_dir; | |
| 284 struct stat st; | |
| 285 const char *err = NULL; | |
| 286 | |
| 287 xfer->names = gtk_file_selection_get_selections(GTK_FILE_SELECTION(xfer->w)); | |
| 288 exists = !stat(*xfer->names, &st); | |
| 289 is_dir = (exists) ? S_ISDIR(st.st_mode) : 0; | |
| 290 | |
| 291 if (exists) { | |
| 292 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) | |
| 293 /* XXX overwrite/append/cancel prompt */ | |
| 294 err = _("That file already exists; please choose another name."); | |
| 295 else { /* (xfer->type == FILE_TRANSFER_TYPE_SEND) */ | |
| 296 char **cur; | |
| 297 /* First find the total number of files, | |
| 298 * so we know how much space to allocate. | |
| 299 */ | |
| 300 xfer->totfiles = 0; | |
| 301 for (cur = xfer->names; *cur; cur++) { | |
| 302 xfer->totfiles++; | |
| 303 } | |
| 304 | |
| 305 /* Now get sizes for each file. */ | |
| 306 xfer->totsize = st.st_size; | |
| 307 xfer->sizes = g_malloc(xfer->totfiles | |
| 308 * sizeof(*xfer->sizes)); | |
| 309 xfer->sizes[0] = st.st_size; | |
| 310 for (cur = xfer->names + 1; *cur; cur++) { | |
| 311 exists = !stat(*cur, &st); | |
| 312 if (!exists) { | |
| 313 err = _("File not found."); | |
| 314 break; | |
| 315 } | |
| 316 xfer->sizes[cur - xfer->names] = | |
| 317 st.st_size; | |
| 318 xfer->totsize += st.st_size; | |
| 319 } | |
| 320 } | |
| 321 } | |
| 322 else { /* doesn't exist */ | |
| 323 if (xfer->type == FILE_TRANSFER_TYPE_SEND) | |
| 324 err = _("File not found."); | |
| 325 else if (xfer->totfiles > 1) { | |
| 326 if (!xfer->names[0] || xfer->names[1]) { | |
| 327 err = _("You may only choose one new directory."); | |
| 328 } | |
| 329 else { | |
| 330 if (ft_mkdir(*xfer->names)) | |
| 331 err = _("Unable to create directory."); | |
| 332 else | |
| 333 xfer->dir = g_strconcat(xfer->names[0], | |
| 334 "/", NULL); | |
| 335 } | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 if (err) | |
| 340 do_error_dialog(err, NULL, GAIM_ERROR); | |
| 341 else { | |
| 342 /* File name looks valid */ | |
| 343 gtk_widget_destroy(xfer->w); | |
| 344 xfer->w = NULL; | |
| 345 | |
| 346 if (xfer->type == FILE_TRANSFER_TYPE_SEND) { | |
| 347 char *desc; | |
| 348 if (xfer->totfiles == 1) | |
| 349 desc = *xfer->names; | |
| 350 else | |
| 351 /* XXX what else? */ | |
| 352 desc = "*"; | |
| 353 /* desc = g_path_get_basename(g_path_get_dirname(*xfer->names)); */ | |
| 354 xfer->gc->prpl->file_transfer_out(xfer->gc, xfer, | |
| 355 desc, xfer->totfiles, | |
| 356 xfer->totsize); | |
| 357 } | |
| 358 else | |
| 359 xfer->gc->prpl->file_transfer_in(xfer->gc, xfer, | |
| 360 0); /* XXX */ | |
| 361 } | |
| 362 } | |
| 363 | |
| 364 /* Called on outgoing transfers to get information about the | |
| 365 * current file. | |
| 366 */ | |
| 367 int transfer_get_file_info(struct file_transfer *xfer, int *size, | |
| 368 char **name) | |
| 369 { | |
| 370 *size = xfer->sizes[xfer->filesdone]; | |
| 371 *name = xfer->names[xfer->filesdone]; | |
| 372 return 0; | |
| 373 } | |
| 374 | |
|
4247
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
375 static gboolean |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
376 ft_choose_file_close(GtkWidget *widget, GdkEvent *event, gpointer user_data) |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
377 { |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
378 ft_cancel((struct file_transfer *)user_data); |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
379 |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
380 return FALSE; |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
381 } |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
382 |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
383 static void |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
384 ft_cancel_button_cb(GtkButton *button, gpointer user_data) |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
385 { |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
386 ft_cancel((struct file_transfer *)user_data); |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
387 } |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
388 |
| 3730 | 389 static int ft_choose_file(struct file_transfer *xfer) |
| 3609 | 390 { |
| 391 char *curdir = g_get_current_dir(); /* should be freed */ | |
| 392 char *initstr; | |
| 393 | |
| 394 /* If the connection is interrupted while we are waiting | |
| 395 * for do_ask_dialog(), then we can't clean up until we | |
| 396 * get here, after the user makes a selection. | |
| 397 */ | |
| 398 if (xfer->state == FILE_TRANSFER_STATE_INTERRUPTED) { | |
| 399 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
| 400 transfer_abort(xfer, NULL); | |
| 401 return 1; | |
| 402 } | |
| 403 | |
| 404 xfer->state = FILE_TRANSFER_STATE_CHOOSEFILE; | |
| 405 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) | |
| 406 xfer->w = gtk_file_selection_new(_("Gaim - Save As...")); | |
| 407 else /* (xfer->type == FILE_TRANSFER_TYPE_SEND) */ { | |
| 408 xfer->w = gtk_file_selection_new(_("Gaim - Open...")); | |
| 409 gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(xfer->w), | |
| 410 1); | |
| 411 } | |
| 412 | |
| 413 if (xfer->initname) { | |
| 414 initstr = g_strdup_printf("%s/%s", curdir, xfer->initname); | |
| 415 } else | |
| 416 initstr = g_strconcat(curdir, "/", NULL); | |
| 417 g_free(curdir); | |
| 418 | |
| 419 gtk_file_selection_set_filename(GTK_FILE_SELECTION(xfer->w), | |
| 420 initstr); | |
| 421 g_free(initstr); | |
| 422 | |
|
4247
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
423 g_signal_connect(G_OBJECT(xfer->w), "delete_event", |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
424 G_CALLBACK(ft_choose_file_close), xfer); |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
425 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(xfer->w)->cancel_button), "clicked", |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
426 G_CALLBACK(ft_cancel_button_cb), xfer); |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
427 g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(xfer->w)->ok_button), "clicked", |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
428 G_CALLBACK(ft_choose_ok), xfer); |
|
3ea14f99c35c
[gaim-migrate @ 4497]
Christian Hammond <chipx86@chipx86.com>
parents:
4245
diff
changeset
|
429 |
| 3609 | 430 gtk_widget_show(xfer->w); |
| 431 | |
| 432 return 0; | |
| 433 } | |
| 434 | |
| 435 static int ft_open_file(struct file_transfer *xfer, const char *filename, | |
| 436 int offset) | |
| 437 { | |
| 438 char *err = NULL; | |
| 439 | |
| 440 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) { | |
| 441 xfer->file = fopen(filename, | |
| 442 (offset > 0) ? "a" : "w"); | |
| 443 | |
| 444 if (!xfer->file) | |
| 445 err = g_strdup_printf(_("Could not open %s for writing: %s"), | |
| 446 filename, g_strerror(errno)); | |
| 447 | |
| 448 } | |
| 449 else /* (xfer->type == FILE_TRANSFER_TYPE_SEND) */ { | |
| 450 xfer->file = fopen(filename, "r"); | |
| 451 if (!xfer->file) | |
| 452 err = g_strdup_printf(_("Could not open %s for reading: %s"), | |
| 453 filename, g_strerror(errno)); | |
| 454 } | |
| 455 | |
| 456 if (err) { | |
| 457 do_error_dialog(err, NULL, GAIM_ERROR); | |
| 458 g_free(err); | |
| 459 return -1; | |
| 460 } | |
| 461 | |
| 462 fseek(xfer->file, offset, SEEK_SET); | |
| 463 | |
| 464 return 0; | |
| 465 } | |
| 466 | |
| 467 /* Takes a full file name, and creates any directories above it | |
| 468 * that don't exist already. | |
| 469 */ | |
| 470 static int ft_mkdir(const char *name) { | |
| 471 int ret = 0; | |
| 472 struct stat st; | |
| 473 mode_t m = umask(0077); | |
| 474 char *dir; | |
| 475 | |
| 476 dir = g_path_get_dirname(name); | |
| 477 if (stat(dir, &st)) | |
| 478 ret = ft_mkdir_help(dir); | |
| 479 | |
| 480 g_free(dir); | |
| 481 umask(m); | |
| 482 return ret; | |
| 483 } | |
| 484 | |
| 485 /* Two functions, one recursive, just to make a directory. Yuck. */ | |
| 486 static int ft_mkdir_help(char *dir) { | |
| 487 int ret; | |
|
3716
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
488 |
| 3611 | 489 ret = mkdir(dir, 0775); |
|
3716
d7e83b4db191
[gaim-migrate @ 3849]
Herman Bloggs <hermanator12002@yahoo.com>
parents:
3630
diff
changeset
|
490 |
| 3609 | 491 if (ret) { |
| 492 char *index = strrchr(dir, G_DIR_SEPARATOR); | |
| 493 if (!index) | |
| 494 return -1; | |
| 495 *index = '\0'; | |
| 496 ret = ft_mkdir_help(dir); | |
| 497 *index = G_DIR_SEPARATOR; | |
| 498 if (!ret) | |
| 3611 | 499 ret = mkdir(dir, 0775); |
| 3609 | 500 } |
| 501 | |
| 502 return ret; | |
| 503 } | |
| 504 | |
| 505 int transfer_in_do(struct file_transfer *xfer, int fd, | |
| 506 const char *filename, int size) | |
| 507 { | |
| 508 char *fullname; | |
| 509 | |
| 510 xfer->state = FILE_TRANSFER_STATE_TRANSFERRING; | |
| 511 xfer->fd = fd; | |
| 512 xfer->bytesleft = size; | |
| 513 | |
| 514 /* XXX implement resuming incoming transfers */ | |
| 515 #if 0 | |
| 516 if (xfer->sizes) | |
| 517 xfer->bytesleft -= xfer->sizes[0]; | |
| 518 #endif | |
| 519 | |
| 520 /* Security check */ | |
| 521 if (g_strrstr(filename, "..")) { | |
| 522 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
| 523 transfer_abort(xfer, _("Invalid incoming filename component")); | |
| 524 return -1; | |
| 525 } | |
| 526 | |
| 527 if (xfer->totfiles > 1) | |
| 528 fullname = g_strconcat(xfer->dir, filename, NULL); | |
| 529 else | |
| 530 /* Careful: filename is the name on the *other* | |
| 531 * end; don't use it here. */ | |
| 532 fullname = g_strdup(xfer->names[xfer->filesdone]); | |
| 533 | |
| 534 | |
| 535 if (ft_mkdir(fullname)) { | |
| 536 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
| 537 transfer_abort(xfer, _("Invalid incoming filename")); | |
| 538 return -1; | |
| 539 } | |
| 540 | |
| 541 if (!ft_open_file(xfer, fullname, 0)) { | |
| 542 /* Special case: if we are receiving an empty file, | |
| 543 * we would never enter the callback. Just avoid the | |
| 544 * callback altogether. | |
| 545 */ | |
| 546 if (xfer->bytesleft == 0) | |
| 547 ft_nextfile(xfer); | |
| 548 else | |
| 549 xfer->watcher = gaim_input_add(fd, | |
| 550 GAIM_INPUT_READ, | |
| 551 ft_callback, xfer); | |
| 552 } else { | |
| 553 /* Error opening file */ | |
| 554 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
| 555 transfer_abort(xfer, NULL); | |
| 556 g_free(fullname); | |
| 557 return -1; | |
| 558 } | |
| 559 | |
| 560 g_free(fullname); | |
| 561 return 0; | |
| 562 } | |
| 563 | |
| 564 int transfer_out_do(struct file_transfer *xfer, int fd, int offset) { | |
| 565 xfer->state = FILE_TRANSFER_STATE_TRANSFERRING; | |
| 566 xfer->fd = fd; | |
| 567 xfer->bytesleft = xfer->sizes[xfer->filesdone] - offset; | |
| 568 | |
| 569 if (!ft_open_file(xfer, xfer->names[xfer->filesdone], offset)) { | |
| 570 /* Special case: see transfer_in_do(). | |
| 571 */ | |
| 572 if (xfer->bytesleft == 0) | |
| 573 ft_nextfile(xfer); | |
| 574 else | |
| 575 xfer->watcher = gaim_input_add(fd, | |
| 576 GAIM_INPUT_WRITE, ft_callback, | |
| 577 xfer); | |
| 578 } | |
| 579 else { | |
| 580 /* Error opening file */ | |
| 581 xfer->state = FILE_TRANSFER_STATE_CLEANUP; | |
| 582 transfer_abort(xfer, NULL); | |
| 583 return -1; | |
| 584 } | |
| 585 | |
| 586 return 0; | |
| 587 } | |
| 588 | |
| 589 static void ft_callback(gpointer data, gint source, | |
| 590 GaimInputCondition condition) | |
| 591 { | |
| 592 struct file_transfer *xfer = (struct file_transfer *)data; | |
|
4201
511c2b63caa4
[gaim-migrate @ 4432]
Christian Hammond <chipx86@chipx86.com>
parents:
4162
diff
changeset
|
593 int rt; |
| 4150 | 594 char *buf = NULL; |
| 3609 | 595 |
| 596 if (condition & GAIM_INPUT_READ) { | |
| 4150 | 597 if (xfer->gc->prpl->file_transfer_read) |
| 598 rt = xfer->gc->prpl->file_transfer_read(xfer->gc, xfer, | |
| 599 xfer->fd, &buf); | |
| 600 else { | |
| 601 buf = g_new0(char, MIN(xfer->bytesleft, FT_BUFFER_SIZE)); | |
| 602 rt = read(xfer->fd, buf, MIN(xfer->bytesleft, FT_BUFFER_SIZE)); | |
| 603 } | |
| 604 | |
| 3609 | 605 /* XXX What if the transfer is interrupted while we |
| 606 * are inside read()? How can this be handled safely? | |
| 607 * -- wtm | |
| 608 */ | |
| 609 if (rt > 0) { | |
| 610 xfer->bytesleft -= rt; | |
| 4150 | 611 fwrite(buf, 1, rt, xfer->file); |
| 3609 | 612 } |
| 613 | |
| 614 } | |
| 615 else /* (condition & GAIM_INPUT_WRITE) */ { | |
| 616 int remain = MIN(xfer->bytesleft, FT_BUFFER_SIZE); | |
| 617 | |
| 4150 | 618 buf = g_new0(char, remain); |
| 619 | |
| 620 fread(buf, 1, remain, xfer->file); | |
| 3609 | 621 |
| 4150 | 622 if (xfer->gc->prpl->file_transfer_write) |
| 623 rt = xfer->gc->prpl->file_transfer_write(xfer->gc, xfer, xfer->fd, | |
| 624 buf, remain); | |
| 625 else | |
| 626 rt = write(xfer->fd, buf, remain); | |
| 627 | |
| 3609 | 628 if (rt > 0) |
| 629 xfer->bytesleft -= rt; | |
| 630 } | |
| 631 | |
| 4150 | 632 if (rt < 0) { |
| 633 if (buf != NULL) | |
| 634 g_free(buf); | |
| 635 | |
| 3609 | 636 return; |
| 4150 | 637 } |
| 3609 | 638 |
| 639 xfer->bytessent += rt; | |
| 640 | |
| 641 if (xfer->gc->prpl->file_transfer_data_chunk) | |
| 642 xfer->gc->prpl->file_transfer_data_chunk(xfer->gc, xfer, buf, rt); | |
| 643 | |
| 644 if (rt > 0 && xfer->bytesleft == 0) { | |
| 645 /* We are done with this file! */ | |
| 646 gaim_input_remove(xfer->watcher); | |
| 647 xfer->watcher = 0; | |
| 648 fclose(xfer->file); | |
| 649 xfer->file = 0; | |
| 650 ft_nextfile(xfer); | |
| 651 } | |
| 4150 | 652 |
| 653 if (buf != NULL) | |
| 654 g_free(buf); | |
| 3609 | 655 } |
| 656 | |
| 657 static void ft_nextfile(struct file_transfer *xfer) | |
| 658 { | |
| 659 debug_printf("file transfer %d of %d done\n", | |
| 660 xfer->filesdone + 1, xfer->totfiles); | |
| 661 | |
| 662 if (++xfer->filesdone == xfer->totfiles) { | |
| 663 char *msg; | |
| 664 char *msg2; | |
| 665 | |
| 666 xfer->gc->prpl->file_transfer_done(xfer->gc, xfer); | |
| 667 | |
| 668 if (xfer->type == FILE_TRANSFER_TYPE_RECEIVE) | |
| 669 msg = g_strdup_printf(_("File transfer from %s to %s completed successfully."), | |
| 670 xfer->who, xfer->gc->username); | |
| 671 else | |
| 672 msg = g_strdup_printf(_("File transfer from %s to %s completed successfully."), | |
| 673 xfer->gc->username, xfer->who); | |
| 674 xfer->state = FILE_TRANSFER_STATE_DONE; | |
| 675 | |
| 676 if (xfer->totfiles > 1) | |
| 677 msg2 = g_strdup_printf(_("%d files transferred."), | |
| 678 xfer->totfiles); | |
| 679 else | |
| 680 msg2 = NULL; | |
| 681 | |
| 682 do_error_dialog(msg, msg2, GAIM_INFO); | |
| 683 g_free(msg); | |
| 684 if (msg2) | |
| 685 g_free(msg2); | |
| 686 | |
| 687 ft_delete(xfer); | |
| 688 } | |
| 689 else { | |
| 690 xfer->gc->prpl->file_transfer_nextfile(xfer->gc, xfer); | |
| 691 } | |
| 692 } | |
| 693 |
