| |
1 /** |
| |
2 * @file ft.c File Transfer API |
| |
3 * |
| |
4 * gaim |
| |
5 * |
| |
6 * Gaim is the legal property of its developers, whose names are too numerous |
| |
7 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
8 * source distribution. |
| |
9 * |
| |
10 * This program is free software; you can redistribute it and/or modify |
| |
11 * it under the terms of the GNU General Public License as published by |
| |
12 * the Free Software Foundation; either version 2 of the License, or |
| |
13 * (at your option) any later version. |
| |
14 * |
| |
15 * This program is distributed in the hope that it will be useful, |
| |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
18 * GNU General Public License for more details. |
| |
19 * |
| |
20 * You should have received a copy of the GNU General Public License |
| |
21 * along with this program; if not, write to the Free Software |
| |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
23 * |
| |
24 */ |
| |
25 #include "internal.h" |
| |
26 #include "ft.h" |
| |
27 #include "network.h" |
| |
28 #include "notify.h" |
| |
29 #include "prefs.h" |
| |
30 #include "proxy.h" |
| |
31 #include "request.h" |
| |
32 #include "util.h" |
| |
33 |
| |
34 #define FT_INITIAL_BUFFER_SIZE 4096 |
| |
35 #define FT_MAX_BUFFER_SIZE 65535 |
| |
36 |
| |
37 static GaimXferUiOps *xfer_ui_ops = NULL; |
| |
38 |
| |
39 static int gaim_xfer_choose_file(GaimXfer *xfer); |
| |
40 |
| |
41 GaimXfer * |
| |
42 gaim_xfer_new(GaimAccount *account, GaimXferType type, const char *who) |
| |
43 { |
| |
44 GaimXfer *xfer; |
| |
45 GaimXferUiOps *ui_ops; |
| |
46 |
| |
47 g_return_val_if_fail(type != GAIM_XFER_UNKNOWN, NULL); |
| |
48 g_return_val_if_fail(account != NULL, NULL); |
| |
49 g_return_val_if_fail(who != NULL, NULL); |
| |
50 |
| |
51 xfer = g_new0(GaimXfer, 1); |
| |
52 |
| |
53 xfer->ref = 1; |
| |
54 xfer->type = type; |
| |
55 xfer->account = account; |
| |
56 xfer->who = g_strdup(who); |
| |
57 xfer->ui_ops = gaim_xfers_get_ui_ops(); |
| |
58 xfer->message = NULL; |
| |
59 xfer->current_buffer_size = FT_INITIAL_BUFFER_SIZE; |
| |
60 |
| |
61 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
62 |
| |
63 if (ui_ops != NULL && ui_ops->new_xfer != NULL) |
| |
64 ui_ops->new_xfer(xfer); |
| |
65 |
| |
66 return xfer; |
| |
67 } |
| |
68 |
| |
69 static void |
| |
70 gaim_xfer_destroy(GaimXfer *xfer) |
| |
71 { |
| |
72 GaimXferUiOps *ui_ops; |
| |
73 |
| |
74 g_return_if_fail(xfer != NULL); |
| |
75 |
| |
76 /* Close the file browser, if it's open */ |
| |
77 gaim_request_close_with_handle(xfer); |
| |
78 |
| |
79 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_STARTED) |
| |
80 gaim_xfer_cancel_local(xfer); |
| |
81 |
| |
82 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
83 |
| |
84 if (ui_ops != NULL && ui_ops->destroy != NULL) |
| |
85 ui_ops->destroy(xfer); |
| |
86 |
| |
87 g_free(xfer->who); |
| |
88 g_free(xfer->filename); |
| |
89 g_free(xfer->remote_ip); |
| |
90 g_free(xfer->local_filename); |
| |
91 |
| |
92 g_free(xfer); |
| |
93 } |
| |
94 |
| |
95 void |
| |
96 gaim_xfer_ref(GaimXfer *xfer) |
| |
97 { |
| |
98 g_return_if_fail(xfer != NULL); |
| |
99 |
| |
100 xfer->ref++; |
| |
101 } |
| |
102 |
| |
103 void |
| |
104 gaim_xfer_unref(GaimXfer *xfer) |
| |
105 { |
| |
106 g_return_if_fail(xfer != NULL); |
| |
107 g_return_if_fail(xfer->ref > 0); |
| |
108 |
| |
109 xfer->ref--; |
| |
110 |
| |
111 if (xfer->ref == 0) |
| |
112 gaim_xfer_destroy(xfer); |
| |
113 } |
| |
114 |
| |
115 static void |
| |
116 gaim_xfer_set_status(GaimXfer *xfer, GaimXferStatusType status) |
| |
117 { |
| |
118 g_return_if_fail(xfer != NULL); |
| |
119 |
| |
120 if(xfer->type == GAIM_XFER_SEND) { |
| |
121 switch(status) { |
| |
122 case GAIM_XFER_STATUS_ACCEPTED: |
| |
123 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-accept", xfer); |
| |
124 break; |
| |
125 case GAIM_XFER_STATUS_STARTED: |
| |
126 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-start", xfer); |
| |
127 break; |
| |
128 case GAIM_XFER_STATUS_DONE: |
| |
129 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-complete", xfer); |
| |
130 break; |
| |
131 case GAIM_XFER_STATUS_CANCEL_LOCAL: |
| |
132 case GAIM_XFER_STATUS_CANCEL_REMOTE: |
| |
133 gaim_signal_emit(gaim_xfers_get_handle(), "file-send-cancel", xfer); |
| |
134 break; |
| |
135 default: |
| |
136 break; |
| |
137 } |
| |
138 } else if(xfer->type == GAIM_XFER_RECEIVE) { |
| |
139 switch(status) { |
| |
140 case GAIM_XFER_STATUS_ACCEPTED: |
| |
141 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-accept", xfer); |
| |
142 break; |
| |
143 case GAIM_XFER_STATUS_STARTED: |
| |
144 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-start", xfer); |
| |
145 break; |
| |
146 case GAIM_XFER_STATUS_DONE: |
| |
147 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-complete", xfer); |
| |
148 break; |
| |
149 case GAIM_XFER_STATUS_CANCEL_LOCAL: |
| |
150 case GAIM_XFER_STATUS_CANCEL_REMOTE: |
| |
151 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-cancel", xfer); |
| |
152 break; |
| |
153 default: |
| |
154 break; |
| |
155 } |
| |
156 } |
| |
157 |
| |
158 xfer->status = status; |
| |
159 } |
| |
160 |
| |
161 void gaim_xfer_conversation_write(GaimXfer *xfer, char *message, gboolean is_error) |
| |
162 { |
| |
163 GaimConversation *conv = NULL; |
| |
164 GaimMessageFlags flags = GAIM_MESSAGE_SYSTEM; |
| |
165 char *escaped; |
| |
166 |
| |
167 g_return_if_fail(xfer != NULL); |
| |
168 g_return_if_fail(message != NULL); |
| |
169 |
| |
170 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, xfer->who, |
| |
171 gaim_xfer_get_account(xfer)); |
| |
172 |
| |
173 if (conv == NULL) |
| |
174 return; |
| |
175 |
| |
176 escaped = g_markup_escape_text(message, -1); |
| |
177 |
| |
178 if (is_error) |
| |
179 flags = GAIM_MESSAGE_ERROR; |
| |
180 |
| |
181 gaim_conversation_write(conv, NULL, escaped, flags, time(NULL)); |
| |
182 g_free(escaped); |
| |
183 } |
| |
184 |
| |
185 static void gaim_xfer_show_file_error(GaimXfer *xfer, const char *filename) |
| |
186 { |
| |
187 int err = errno; |
| |
188 gchar *msg = NULL, *utf8; |
| |
189 GaimXferType xfer_type = gaim_xfer_get_type(xfer); |
| |
190 GaimAccount *account = gaim_xfer_get_account(xfer); |
| |
191 |
| |
192 utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); |
| |
193 switch(xfer_type) { |
| |
194 case GAIM_XFER_SEND: |
| |
195 msg = g_strdup_printf(_("Error reading %s: \n%s.\n"), |
| |
196 utf8, strerror(err)); |
| |
197 break; |
| |
198 case GAIM_XFER_RECEIVE: |
| |
199 msg = g_strdup_printf(_("Error writing %s: \n%s.\n"), |
| |
200 utf8, strerror(err)); |
| |
201 break; |
| |
202 default: |
| |
203 msg = g_strdup_printf(_("Error accessing %s: \n%s.\n"), |
| |
204 utf8, strerror(err)); |
| |
205 break; |
| |
206 } |
| |
207 g_free(utf8); |
| |
208 |
| |
209 gaim_xfer_conversation_write(xfer, msg, TRUE); |
| |
210 gaim_xfer_error(xfer_type, account, xfer->who, msg); |
| |
211 g_free(msg); |
| |
212 } |
| |
213 |
| |
214 static void |
| |
215 gaim_xfer_choose_file_ok_cb(void *user_data, const char *filename) |
| |
216 { |
| |
217 GaimXfer *xfer; |
| |
218 struct stat st; |
| |
219 gchar *dir; |
| |
220 |
| |
221 xfer = (GaimXfer *)user_data; |
| |
222 |
| |
223 if (g_stat(filename, &st) != 0) { |
| |
224 /* File not found. */ |
| |
225 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) { |
| |
226 #ifndef _WIN32 |
| |
227 int mode = W_OK; |
| |
228 #else |
| |
229 int mode = F_OK; |
| |
230 #endif |
| |
231 dir = g_path_get_dirname(filename); |
| |
232 |
| |
233 if (g_access(dir, mode) == 0) { |
| |
234 gaim_xfer_request_accepted(xfer, filename); |
| |
235 } else { |
| |
236 gaim_xfer_ref(xfer); |
| |
237 gaim_notify_message( |
| |
238 NULL, GAIM_NOTIFY_MSG_ERROR, NULL, |
| |
239 _("Directory is not writable."), NULL, |
| |
240 (GaimNotifyCloseCallback)gaim_xfer_choose_file, xfer); |
| |
241 } |
| |
242 |
| |
243 g_free(dir); |
| |
244 } |
| |
245 else { |
| |
246 gaim_xfer_show_file_error(xfer, filename); |
| |
247 gaim_xfer_request_denied(xfer); |
| |
248 } |
| |
249 } |
| |
250 else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) && |
| |
251 (st.st_size == 0)) { |
| |
252 |
| |
253 gaim_notify_error(NULL, NULL, |
| |
254 _("Cannot send a file of 0 bytes."), NULL); |
| |
255 |
| |
256 gaim_xfer_request_denied(xfer); |
| |
257 } |
| |
258 else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) && |
| |
259 S_ISDIR(st.st_mode)) { |
| |
260 /* |
| |
261 * XXX - Sending a directory should be valid for some protocols. |
| |
262 */ |
| |
263 gaim_notify_error(NULL, NULL, |
| |
264 _("Cannot send a directory."), NULL); |
| |
265 |
| |
266 gaim_xfer_request_denied(xfer); |
| |
267 } |
| |
268 else if ((gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) && |
| |
269 S_ISDIR(st.st_mode)) { |
| |
270 char *msg, *utf8; |
| |
271 utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); |
| |
272 msg = g_strdup_printf( |
| |
273 _("%s is not a regular file. Cowardly refusing to overwrite it.\n"), utf8); |
| |
274 g_free(utf8); |
| |
275 gaim_notify_error(NULL, NULL, msg, NULL); |
| |
276 g_free(msg); |
| |
277 gaim_xfer_request_denied(xfer); |
| |
278 } |
| |
279 else { |
| |
280 gaim_xfer_request_accepted(xfer, filename); |
| |
281 } |
| |
282 |
| |
283 gaim_xfer_unref(xfer); |
| |
284 } |
| |
285 |
| |
286 static void |
| |
287 gaim_xfer_choose_file_cancel_cb(void *user_data, const char *filename) |
| |
288 { |
| |
289 GaimXfer *xfer = (GaimXfer *)user_data; |
| |
290 |
| |
291 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL); |
| |
292 gaim_xfer_request_denied(xfer); |
| |
293 } |
| |
294 |
| |
295 static int |
| |
296 gaim_xfer_choose_file(GaimXfer *xfer) |
| |
297 { |
| |
298 gaim_request_file(xfer, NULL, gaim_xfer_get_filename(xfer), |
| |
299 (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE), |
| |
300 G_CALLBACK(gaim_xfer_choose_file_ok_cb), |
| |
301 G_CALLBACK(gaim_xfer_choose_file_cancel_cb), xfer); |
| |
302 |
| |
303 return 0; |
| |
304 } |
| |
305 |
| |
306 static int |
| |
307 cancel_recv_cb(GaimXfer *xfer) |
| |
308 { |
| |
309 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL); |
| |
310 gaim_xfer_request_denied(xfer); |
| |
311 gaim_xfer_unref(xfer); |
| |
312 |
| |
313 return 0; |
| |
314 } |
| |
315 |
| |
316 static void |
| |
317 gaim_xfer_ask_recv(GaimXfer *xfer) |
| |
318 { |
| |
319 char *buf, *size_buf; |
| |
320 size_t size; |
| |
321 |
| |
322 /* If we have already accepted the request, ask the destination file |
| |
323 name directly */ |
| |
324 if (gaim_xfer_get_status(xfer) != GAIM_XFER_STATUS_ACCEPTED) { |
| |
325 GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who); |
| |
326 |
| |
327 if (gaim_xfer_get_filename(xfer) != NULL) |
| |
328 { |
| |
329 size = gaim_xfer_get_size(xfer); |
| |
330 size_buf = gaim_str_size_to_units(size); |
| |
331 buf = g_strdup_printf(_("%s wants to send you %s (%s)"), |
| |
332 buddy ? gaim_buddy_get_alias(buddy) : xfer->who, |
| |
333 gaim_xfer_get_filename(xfer), size_buf); |
| |
334 g_free(size_buf); |
| |
335 } |
| |
336 else |
| |
337 { |
| |
338 buf = g_strdup_printf(_("%s wants to send you a file"), |
| |
339 buddy ? gaim_buddy_get_alias(buddy) : xfer->who); |
| |
340 } |
| |
341 |
| |
342 if (xfer->message != NULL) |
| |
343 serv_got_im(gaim_account_get_connection(xfer->account), |
| |
344 xfer->who, xfer->message, 0, time(NULL)); |
| |
345 |
| |
346 gaim_request_accept_cancel(xfer, NULL, buf, NULL, |
| |
347 GAIM_DEFAULT_ACTION_NONE, xfer, |
| |
348 G_CALLBACK(gaim_xfer_choose_file), |
| |
349 G_CALLBACK(cancel_recv_cb)); |
| |
350 |
| |
351 g_free(buf); |
| |
352 } else |
| |
353 gaim_xfer_choose_file(xfer); |
| |
354 } |
| |
355 |
| |
356 static int |
| |
357 ask_accept_ok(GaimXfer *xfer) |
| |
358 { |
| |
359 gaim_xfer_request_accepted(xfer, NULL); |
| |
360 |
| |
361 return 0; |
| |
362 } |
| |
363 |
| |
364 static int |
| |
365 ask_accept_cancel(GaimXfer *xfer) |
| |
366 { |
| |
367 gaim_xfer_request_denied(xfer); |
| |
368 gaim_xfer_unref(xfer); |
| |
369 |
| |
370 return 0; |
| |
371 } |
| |
372 |
| |
373 static void |
| |
374 gaim_xfer_ask_accept(GaimXfer *xfer) |
| |
375 { |
| |
376 char *buf, *buf2 = NULL; |
| |
377 GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who); |
| |
378 |
| |
379 buf = g_strdup_printf(_("Accept file transfer request from %s?"), |
| |
380 buddy ? gaim_buddy_get_alias(buddy) : xfer->who); |
| |
381 if (gaim_xfer_get_remote_ip(xfer) && |
| |
382 gaim_xfer_get_remote_port(xfer)) |
| |
383 buf2 = g_strdup_printf(_("A file is available for download from:\n" |
| |
384 "Remote host: %s\nRemote port: %d"), |
| |
385 gaim_xfer_get_remote_ip(xfer), |
| |
386 gaim_xfer_get_remote_port(xfer)); |
| |
387 gaim_request_accept_cancel(xfer, NULL, buf, buf2, |
| |
388 GAIM_DEFAULT_ACTION_NONE, xfer, |
| |
389 G_CALLBACK(ask_accept_ok), |
| |
390 G_CALLBACK(ask_accept_cancel)); |
| |
391 g_free(buf); |
| |
392 g_free(buf2); |
| |
393 } |
| |
394 |
| |
395 void |
| |
396 gaim_xfer_request(GaimXfer *xfer) |
| |
397 { |
| |
398 g_return_if_fail(xfer != NULL); |
| |
399 g_return_if_fail(xfer->ops.init != NULL); |
| |
400 |
| |
401 gaim_xfer_ref(xfer); |
| |
402 |
| |
403 if (gaim_xfer_get_type(xfer) == GAIM_XFER_RECEIVE) |
| |
404 { |
| |
405 gaim_signal_emit(gaim_xfers_get_handle(), "file-recv-request", xfer); |
| |
406 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) |
| |
407 { |
| |
408 /* The file-transfer was cancelled by a plugin */ |
| |
409 gaim_xfer_cancel_local(xfer); |
| |
410 } |
| |
411 else if (gaim_xfer_get_filename(xfer) || |
| |
412 gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_ACCEPTED) |
| |
413 { |
| |
414 gchar* message = NULL; |
| |
415 GaimBuddy *buddy = gaim_find_buddy(xfer->account, xfer->who); |
| |
416 message = g_strdup_printf(_("%s is offering to send file %s"), |
| |
417 buddy ? gaim_buddy_get_alias(buddy) : xfer->who, gaim_xfer_get_filename(xfer)); |
| |
418 gaim_xfer_conversation_write(xfer, message, FALSE); |
| |
419 g_free(message); |
| |
420 /* Ask for a filename to save to if it's not already given by a plugin */ |
| |
421 if (xfer->local_filename == NULL) |
| |
422 gaim_xfer_ask_recv(xfer); |
| |
423 } |
| |
424 else |
| |
425 { |
| |
426 gaim_xfer_ask_accept(xfer); |
| |
427 } |
| |
428 } |
| |
429 else |
| |
430 { |
| |
431 gaim_xfer_choose_file(xfer); |
| |
432 } |
| |
433 } |
| |
434 |
| |
435 void |
| |
436 gaim_xfer_request_accepted(GaimXfer *xfer, const char *filename) |
| |
437 { |
| |
438 GaimXferType type; |
| |
439 struct stat st; |
| |
440 char *msg, *utf8; |
| |
441 GaimAccount *account; |
| |
442 GaimBuddy *buddy; |
| |
443 |
| |
444 if (xfer == NULL) |
| |
445 return; |
| |
446 |
| |
447 type = gaim_xfer_get_type(xfer); |
| |
448 account = gaim_xfer_get_account(xfer); |
| |
449 |
| |
450 if (!filename && type == GAIM_XFER_RECEIVE) { |
| |
451 xfer->status = GAIM_XFER_STATUS_ACCEPTED; |
| |
452 xfer->ops.init(xfer); |
| |
453 return; |
| |
454 } |
| |
455 |
| |
456 buddy = gaim_find_buddy(account, xfer->who); |
| |
457 |
| |
458 if (type == GAIM_XFER_SEND) { |
| |
459 /* Sending a file */ |
| |
460 /* Check the filename. */ |
| |
461 #ifdef _WIN32 |
| |
462 if (g_strrstr(filename, "../") || g_strrstr(filename, "..\\")) { |
| |
463 #else |
| |
464 if (g_strrstr(filename, "../")) { |
| |
465 #endif |
| |
466 char *utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); |
| |
467 |
| |
468 msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8); |
| |
469 gaim_xfer_error(type, account, xfer->who, msg); |
| |
470 g_free(utf8); |
| |
471 g_free(msg); |
| |
472 |
| |
473 gaim_xfer_unref(xfer); |
| |
474 return; |
| |
475 } |
| |
476 |
| |
477 if (g_stat(filename, &st) == -1) { |
| |
478 gaim_xfer_show_file_error(xfer, filename); |
| |
479 gaim_xfer_unref(xfer); |
| |
480 return; |
| |
481 } |
| |
482 |
| |
483 gaim_xfer_set_local_filename(xfer, filename); |
| |
484 gaim_xfer_set_size(xfer, st.st_size); |
| |
485 |
| |
486 utf8 = g_filename_to_utf8(g_basename(filename), -1, NULL, NULL, NULL); |
| |
487 gaim_xfer_set_filename(xfer, utf8); |
| |
488 |
| |
489 msg = g_strdup_printf(_("Offering to send %s to %s"), |
| |
490 utf8, buddy ? gaim_buddy_get_alias(buddy) : xfer->who); |
| |
491 g_free(utf8); |
| |
492 |
| |
493 gaim_xfer_conversation_write(xfer, msg, FALSE); |
| |
494 g_free(msg); |
| |
495 } |
| |
496 else { |
| |
497 /* Receiving a file */ |
| |
498 xfer->status = GAIM_XFER_STATUS_ACCEPTED; |
| |
499 gaim_xfer_set_local_filename(xfer, filename); |
| |
500 |
| |
501 msg = g_strdup_printf(_("Starting transfer of %s from %s"), |
| |
502 xfer->filename, buddy ? gaim_buddy_get_alias(buddy) : xfer->who); |
| |
503 gaim_xfer_conversation_write(xfer, msg, FALSE); |
| |
504 g_free(msg); |
| |
505 } |
| |
506 |
| |
507 gaim_xfer_add(xfer); |
| |
508 xfer->ops.init(xfer); |
| |
509 |
| |
510 } |
| |
511 |
| |
512 void |
| |
513 gaim_xfer_request_denied(GaimXfer *xfer) |
| |
514 { |
| |
515 g_return_if_fail(xfer != NULL); |
| |
516 |
| |
517 if (xfer->ops.request_denied != NULL) |
| |
518 xfer->ops.request_denied(xfer); |
| |
519 |
| |
520 gaim_xfer_unref(xfer); |
| |
521 } |
| |
522 |
| |
523 GaimXferType |
| |
524 gaim_xfer_get_type(const GaimXfer *xfer) |
| |
525 { |
| |
526 g_return_val_if_fail(xfer != NULL, GAIM_XFER_UNKNOWN); |
| |
527 |
| |
528 return xfer->type; |
| |
529 } |
| |
530 |
| |
531 GaimAccount * |
| |
532 gaim_xfer_get_account(const GaimXfer *xfer) |
| |
533 { |
| |
534 g_return_val_if_fail(xfer != NULL, NULL); |
| |
535 |
| |
536 return xfer->account; |
| |
537 } |
| |
538 |
| |
539 GaimXferStatusType |
| |
540 gaim_xfer_get_status(const GaimXfer *xfer) |
| |
541 { |
| |
542 g_return_val_if_fail(xfer != NULL, GAIM_XFER_STATUS_UNKNOWN); |
| |
543 |
| |
544 return xfer->status; |
| |
545 } |
| |
546 |
| |
547 gboolean |
| |
548 gaim_xfer_is_canceled(const GaimXfer *xfer) |
| |
549 { |
| |
550 g_return_val_if_fail(xfer != NULL, TRUE); |
| |
551 |
| |
552 if ((gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) || |
| |
553 (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_REMOTE)) |
| |
554 return TRUE; |
| |
555 else |
| |
556 return FALSE; |
| |
557 } |
| |
558 |
| |
559 gboolean |
| |
560 gaim_xfer_is_completed(const GaimXfer *xfer) |
| |
561 { |
| |
562 g_return_val_if_fail(xfer != NULL, TRUE); |
| |
563 |
| |
564 return (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_DONE); |
| |
565 } |
| |
566 |
| |
567 const char * |
| |
568 gaim_xfer_get_filename(const GaimXfer *xfer) |
| |
569 { |
| |
570 g_return_val_if_fail(xfer != NULL, NULL); |
| |
571 |
| |
572 return xfer->filename; |
| |
573 } |
| |
574 |
| |
575 const char * |
| |
576 gaim_xfer_get_local_filename(const GaimXfer *xfer) |
| |
577 { |
| |
578 g_return_val_if_fail(xfer != NULL, NULL); |
| |
579 |
| |
580 return xfer->local_filename; |
| |
581 } |
| |
582 |
| |
583 size_t |
| |
584 gaim_xfer_get_bytes_sent(const GaimXfer *xfer) |
| |
585 { |
| |
586 g_return_val_if_fail(xfer != NULL, 0); |
| |
587 |
| |
588 return xfer->bytes_sent; |
| |
589 } |
| |
590 |
| |
591 size_t |
| |
592 gaim_xfer_get_bytes_remaining(const GaimXfer *xfer) |
| |
593 { |
| |
594 g_return_val_if_fail(xfer != NULL, 0); |
| |
595 |
| |
596 return xfer->bytes_remaining; |
| |
597 } |
| |
598 |
| |
599 size_t |
| |
600 gaim_xfer_get_size(const GaimXfer *xfer) |
| |
601 { |
| |
602 g_return_val_if_fail(xfer != NULL, 0); |
| |
603 |
| |
604 return xfer->size; |
| |
605 } |
| |
606 |
| |
607 double |
| |
608 gaim_xfer_get_progress(const GaimXfer *xfer) |
| |
609 { |
| |
610 g_return_val_if_fail(xfer != NULL, 0.0); |
| |
611 |
| |
612 if (gaim_xfer_get_size(xfer) == 0) |
| |
613 return 0.0; |
| |
614 |
| |
615 return ((double)gaim_xfer_get_bytes_sent(xfer) / |
| |
616 (double)gaim_xfer_get_size(xfer)); |
| |
617 } |
| |
618 |
| |
619 unsigned int |
| |
620 gaim_xfer_get_local_port(const GaimXfer *xfer) |
| |
621 { |
| |
622 g_return_val_if_fail(xfer != NULL, -1); |
| |
623 |
| |
624 return xfer->local_port; |
| |
625 } |
| |
626 |
| |
627 const char * |
| |
628 gaim_xfer_get_remote_ip(const GaimXfer *xfer) |
| |
629 { |
| |
630 g_return_val_if_fail(xfer != NULL, NULL); |
| |
631 |
| |
632 return xfer->remote_ip; |
| |
633 } |
| |
634 |
| |
635 unsigned int |
| |
636 gaim_xfer_get_remote_port(const GaimXfer *xfer) |
| |
637 { |
| |
638 g_return_val_if_fail(xfer != NULL, -1); |
| |
639 |
| |
640 return xfer->remote_port; |
| |
641 } |
| |
642 |
| |
643 void |
| |
644 gaim_xfer_set_completed(GaimXfer *xfer, gboolean completed) |
| |
645 { |
| |
646 GaimXferUiOps *ui_ops; |
| |
647 |
| |
648 g_return_if_fail(xfer != NULL); |
| |
649 |
| |
650 if (completed == TRUE) { |
| |
651 char *msg = NULL; |
| |
652 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_DONE); |
| |
653 |
| |
654 if (gaim_xfer_get_filename(xfer) != NULL) |
| |
655 msg = g_strdup_printf(_("Transfer of file %s complete"), |
| |
656 gaim_xfer_get_filename(xfer)); |
| |
657 else |
| |
658 msg = g_strdup_printf(_("File transfer complete")); |
| |
659 gaim_xfer_conversation_write(xfer, msg, FALSE); |
| |
660 g_free(msg); |
| |
661 } |
| |
662 |
| |
663 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
664 |
| |
665 if (ui_ops != NULL && ui_ops->update_progress != NULL) |
| |
666 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer)); |
| |
667 } |
| |
668 |
| |
669 void |
| |
670 gaim_xfer_set_message(GaimXfer *xfer, const char *message) |
| |
671 { |
| |
672 g_return_if_fail(xfer != NULL); |
| |
673 |
| |
674 g_free(xfer->message); |
| |
675 xfer->message = g_strdup(message); |
| |
676 } |
| |
677 |
| |
678 void |
| |
679 gaim_xfer_set_filename(GaimXfer *xfer, const char *filename) |
| |
680 { |
| |
681 g_return_if_fail(xfer != NULL); |
| |
682 |
| |
683 g_free(xfer->filename); |
| |
684 xfer->filename = g_strdup(filename); |
| |
685 } |
| |
686 |
| |
687 void |
| |
688 gaim_xfer_set_local_filename(GaimXfer *xfer, const char *filename) |
| |
689 { |
| |
690 g_return_if_fail(xfer != NULL); |
| |
691 |
| |
692 g_free(xfer->local_filename); |
| |
693 xfer->local_filename = g_strdup(filename); |
| |
694 } |
| |
695 |
| |
696 void |
| |
697 gaim_xfer_set_size(GaimXfer *xfer, size_t size) |
| |
698 { |
| |
699 g_return_if_fail(xfer != NULL); |
| |
700 |
| |
701 xfer->size = size; |
| |
702 xfer->bytes_remaining = xfer->size - gaim_xfer_get_bytes_sent(xfer); |
| |
703 } |
| |
704 |
| |
705 void |
| |
706 gaim_xfer_set_bytes_sent(GaimXfer *xfer, size_t bytes_sent) |
| |
707 { |
| |
708 g_return_if_fail(xfer != NULL); |
| |
709 |
| |
710 xfer->bytes_sent = bytes_sent; |
| |
711 xfer->bytes_remaining = gaim_xfer_get_size(xfer) - bytes_sent; |
| |
712 } |
| |
713 |
| |
714 GaimXferUiOps * |
| |
715 gaim_xfer_get_ui_ops(const GaimXfer *xfer) |
| |
716 { |
| |
717 g_return_val_if_fail(xfer != NULL, NULL); |
| |
718 |
| |
719 return xfer->ui_ops; |
| |
720 } |
| |
721 |
| |
722 void |
| |
723 gaim_xfer_set_init_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) |
| |
724 { |
| |
725 g_return_if_fail(xfer != NULL); |
| |
726 |
| |
727 xfer->ops.init = fnc; |
| |
728 } |
| |
729 |
| |
730 void gaim_xfer_set_request_denied_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) |
| |
731 { |
| |
732 g_return_if_fail(xfer != NULL); |
| |
733 |
| |
734 xfer->ops.request_denied = fnc; |
| |
735 } |
| |
736 |
| |
737 void |
| |
738 gaim_xfer_set_read_fnc(GaimXfer *xfer, gssize (*fnc)(guchar **, GaimXfer *)) |
| |
739 { |
| |
740 g_return_if_fail(xfer != NULL); |
| |
741 |
| |
742 xfer->ops.read = fnc; |
| |
743 } |
| |
744 |
| |
745 void |
| |
746 gaim_xfer_set_write_fnc(GaimXfer *xfer, |
| |
747 gssize (*fnc)(const guchar *, size_t, GaimXfer *)) |
| |
748 { |
| |
749 g_return_if_fail(xfer != NULL); |
| |
750 |
| |
751 xfer->ops.write = fnc; |
| |
752 } |
| |
753 |
| |
754 void |
| |
755 gaim_xfer_set_ack_fnc(GaimXfer *xfer, |
| |
756 void (*fnc)(GaimXfer *, const guchar *, size_t)) |
| |
757 { |
| |
758 g_return_if_fail(xfer != NULL); |
| |
759 |
| |
760 xfer->ops.ack = fnc; |
| |
761 } |
| |
762 |
| |
763 void |
| |
764 gaim_xfer_set_start_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) |
| |
765 { |
| |
766 g_return_if_fail(xfer != NULL); |
| |
767 |
| |
768 xfer->ops.start = fnc; |
| |
769 } |
| |
770 |
| |
771 void |
| |
772 gaim_xfer_set_end_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) |
| |
773 { |
| |
774 g_return_if_fail(xfer != NULL); |
| |
775 |
| |
776 xfer->ops.end = fnc; |
| |
777 } |
| |
778 |
| |
779 void |
| |
780 gaim_xfer_set_cancel_send_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) |
| |
781 { |
| |
782 g_return_if_fail(xfer != NULL); |
| |
783 |
| |
784 xfer->ops.cancel_send = fnc; |
| |
785 } |
| |
786 |
| |
787 void |
| |
788 gaim_xfer_set_cancel_recv_fnc(GaimXfer *xfer, void (*fnc)(GaimXfer *)) |
| |
789 { |
| |
790 g_return_if_fail(xfer != NULL); |
| |
791 |
| |
792 xfer->ops.cancel_recv = fnc; |
| |
793 } |
| |
794 |
| |
795 static void |
| |
796 gaim_xfer_increase_buffer_size(GaimXfer *xfer) |
| |
797 { |
| |
798 xfer->current_buffer_size = MIN(xfer->current_buffer_size * 1.5, |
| |
799 FT_MAX_BUFFER_SIZE); |
| |
800 } |
| |
801 |
| |
802 gssize |
| |
803 gaim_xfer_read(GaimXfer *xfer, guchar **buffer) |
| |
804 { |
| |
805 gssize s, r; |
| |
806 |
| |
807 g_return_val_if_fail(xfer != NULL, 0); |
| |
808 g_return_val_if_fail(buffer != NULL, 0); |
| |
809 |
| |
810 if (gaim_xfer_get_size(xfer) == 0) |
| |
811 s = xfer->current_buffer_size; |
| |
812 else |
| |
813 s = MIN(gaim_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); |
| |
814 |
| |
815 if (xfer->ops.read != NULL) |
| |
816 r = (xfer->ops.read)(buffer, xfer); |
| |
817 else { |
| |
818 *buffer = g_malloc0(s); |
| |
819 |
| |
820 r = read(xfer->fd, *buffer, s); |
| |
821 if (r < 0 && errno == EAGAIN) |
| |
822 r = 0; |
| |
823 else if (r < 0) |
| |
824 r = -1; |
| |
825 else if ((gaim_xfer_get_size(xfer) > 0) && |
| |
826 ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer))) |
| |
827 gaim_xfer_set_completed(xfer, TRUE); |
| |
828 else if (r == 0) |
| |
829 r = -1; |
| |
830 } |
| |
831 |
| |
832 if (r == xfer->current_buffer_size) |
| |
833 /* |
| |
834 * We managed to read the entire buffer. This means our this |
| |
835 * network is fast and our buffer is too small, so make it |
| |
836 * bigger. |
| |
837 */ |
| |
838 gaim_xfer_increase_buffer_size(xfer); |
| |
839 |
| |
840 return r; |
| |
841 } |
| |
842 |
| |
843 gssize |
| |
844 gaim_xfer_write(GaimXfer *xfer, const guchar *buffer, gsize size) |
| |
845 { |
| |
846 gssize r, s; |
| |
847 |
| |
848 g_return_val_if_fail(xfer != NULL, 0); |
| |
849 g_return_val_if_fail(buffer != NULL, 0); |
| |
850 g_return_val_if_fail(size != 0, 0); |
| |
851 |
| |
852 s = MIN(gaim_xfer_get_bytes_remaining(xfer), size); |
| |
853 |
| |
854 if (xfer->ops.write != NULL) { |
| |
855 r = (xfer->ops.write)(buffer, s, xfer); |
| |
856 } else { |
| |
857 r = write(xfer->fd, buffer, s); |
| |
858 if (r < 0 && errno == EAGAIN) |
| |
859 r = 0; |
| |
860 if ((gaim_xfer_get_bytes_sent(xfer)+r) >= gaim_xfer_get_size(xfer)) |
| |
861 gaim_xfer_set_completed(xfer, TRUE); |
| |
862 } |
| |
863 |
| |
864 return r; |
| |
865 } |
| |
866 |
| |
867 static void |
| |
868 transfer_cb(gpointer data, gint source, GaimInputCondition condition) |
| |
869 { |
| |
870 GaimXferUiOps *ui_ops; |
| |
871 GaimXfer *xfer = (GaimXfer *)data; |
| |
872 guchar *buffer = NULL; |
| |
873 gssize r = 0; |
| |
874 |
| |
875 if (condition & GAIM_INPUT_READ) { |
| |
876 r = gaim_xfer_read(xfer, &buffer); |
| |
877 if (r > 0) { |
| |
878 fwrite(buffer, 1, r, xfer->dest_fp); |
| |
879 } else if(r <= 0) { |
| |
880 gaim_xfer_cancel_remote(xfer); |
| |
881 return; |
| |
882 } |
| |
883 } |
| |
884 |
| |
885 if (condition & GAIM_INPUT_WRITE) { |
| |
886 size_t s = MIN(gaim_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); |
| |
887 |
| |
888 /* this is so the prpl can keep the connection open |
| |
889 if it needs to for some odd reason. */ |
| |
890 if (s == 0) { |
| |
891 if (xfer->watcher) { |
| |
892 gaim_input_remove(xfer->watcher); |
| |
893 xfer->watcher = 0; |
| |
894 } |
| |
895 return; |
| |
896 } |
| |
897 |
| |
898 buffer = g_malloc0(s); |
| |
899 |
| |
900 fread(buffer, 1, s, xfer->dest_fp); |
| |
901 |
| |
902 /* Write as much as we're allowed to. */ |
| |
903 r = gaim_xfer_write(xfer, buffer, s); |
| |
904 |
| |
905 if (r == -1) { |
| |
906 gaim_xfer_cancel_remote(xfer); |
| |
907 g_free(buffer); |
| |
908 return; |
| |
909 } else if (r < s) { |
| |
910 /* We have to seek back in the file now. */ |
| |
911 fseek(xfer->dest_fp, r - s, SEEK_CUR); |
| |
912 } else { |
| |
913 /* |
| |
914 * We managed to write the entire buffer. This means our |
| |
915 * network is fast and our buffer is too small, so make it |
| |
916 * bigger. |
| |
917 */ |
| |
918 gaim_xfer_increase_buffer_size(xfer); |
| |
919 } |
| |
920 } |
| |
921 |
| |
922 if (r > 0) { |
| |
923 if (gaim_xfer_get_size(xfer) > 0) |
| |
924 xfer->bytes_remaining -= r; |
| |
925 |
| |
926 xfer->bytes_sent += r; |
| |
927 |
| |
928 if (xfer->ops.ack != NULL) |
| |
929 xfer->ops.ack(xfer, buffer, r); |
| |
930 |
| |
931 g_free(buffer); |
| |
932 |
| |
933 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
934 |
| |
935 if (ui_ops != NULL && ui_ops->update_progress != NULL) |
| |
936 ui_ops->update_progress(xfer, |
| |
937 gaim_xfer_get_progress(xfer)); |
| |
938 } |
| |
939 |
| |
940 if (gaim_xfer_is_completed(xfer)) |
| |
941 gaim_xfer_end(xfer); |
| |
942 } |
| |
943 |
| |
944 static void |
| |
945 begin_transfer(GaimXfer *xfer, GaimInputCondition cond) |
| |
946 { |
| |
947 GaimXferType type = gaim_xfer_get_type(xfer); |
| |
948 |
| |
949 xfer->dest_fp = g_fopen(gaim_xfer_get_local_filename(xfer), |
| |
950 type == GAIM_XFER_RECEIVE ? "wb" : "rb"); |
| |
951 |
| |
952 if (xfer->dest_fp == NULL) { |
| |
953 gaim_xfer_show_file_error(xfer, gaim_xfer_get_local_filename(xfer)); |
| |
954 gaim_xfer_cancel_local(xfer); |
| |
955 return; |
| |
956 } |
| |
957 |
| |
958 fseek(xfer->dest_fp, xfer->bytes_sent, SEEK_SET); |
| |
959 |
| |
960 xfer->watcher = gaim_input_add(xfer->fd, cond, transfer_cb, xfer); |
| |
961 |
| |
962 xfer->start_time = time(NULL); |
| |
963 |
| |
964 if (xfer->ops.start != NULL) |
| |
965 xfer->ops.start(xfer); |
| |
966 } |
| |
967 |
| |
968 static void |
| |
969 connect_cb(gpointer data, gint source, const gchar *error_message) |
| |
970 { |
| |
971 GaimXfer *xfer = (GaimXfer *)data; |
| |
972 |
| |
973 xfer->fd = source; |
| |
974 |
| |
975 begin_transfer(xfer, GAIM_INPUT_READ); |
| |
976 } |
| |
977 |
| |
978 void |
| |
979 gaim_xfer_start(GaimXfer *xfer, int fd, const char *ip, |
| |
980 unsigned int port) |
| |
981 { |
| |
982 GaimInputCondition cond; |
| |
983 GaimXferType type; |
| |
984 |
| |
985 g_return_if_fail(xfer != NULL); |
| |
986 g_return_if_fail(gaim_xfer_get_type(xfer) != GAIM_XFER_UNKNOWN); |
| |
987 |
| |
988 type = gaim_xfer_get_type(xfer); |
| |
989 |
| |
990 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_STARTED); |
| |
991 |
| |
992 if (type == GAIM_XFER_RECEIVE) { |
| |
993 cond = GAIM_INPUT_READ; |
| |
994 |
| |
995 if (ip != NULL) { |
| |
996 xfer->remote_ip = g_strdup(ip); |
| |
997 xfer->remote_port = port; |
| |
998 |
| |
999 /* Establish a file descriptor. */ |
| |
1000 gaim_proxy_connect(NULL, xfer->account, xfer->remote_ip, |
| |
1001 xfer->remote_port, connect_cb, xfer); |
| |
1002 |
| |
1003 return; |
| |
1004 } |
| |
1005 else { |
| |
1006 xfer->fd = fd; |
| |
1007 } |
| |
1008 } |
| |
1009 else { |
| |
1010 cond = GAIM_INPUT_WRITE; |
| |
1011 |
| |
1012 xfer->fd = fd; |
| |
1013 } |
| |
1014 |
| |
1015 begin_transfer(xfer, cond); |
| |
1016 } |
| |
1017 |
| |
1018 void |
| |
1019 gaim_xfer_end(GaimXfer *xfer) |
| |
1020 { |
| |
1021 g_return_if_fail(xfer != NULL); |
| |
1022 |
| |
1023 /* See if we are actually trying to cancel this. */ |
| |
1024 if (!gaim_xfer_is_completed(xfer)) { |
| |
1025 gaim_xfer_cancel_local(xfer); |
| |
1026 return; |
| |
1027 } |
| |
1028 |
| |
1029 xfer->end_time = time(NULL); |
| |
1030 if (xfer->ops.end != NULL) |
| |
1031 xfer->ops.end(xfer); |
| |
1032 |
| |
1033 if (xfer->watcher != 0) { |
| |
1034 gaim_input_remove(xfer->watcher); |
| |
1035 xfer->watcher = 0; |
| |
1036 } |
| |
1037 |
| |
1038 if (xfer->fd != 0) |
| |
1039 close(xfer->fd); |
| |
1040 |
| |
1041 if (xfer->dest_fp != NULL) { |
| |
1042 fclose(xfer->dest_fp); |
| |
1043 xfer->dest_fp = NULL; |
| |
1044 } |
| |
1045 |
| |
1046 gaim_xfer_unref(xfer); |
| |
1047 } |
| |
1048 |
| |
1049 void |
| |
1050 gaim_xfer_add(GaimXfer *xfer) |
| |
1051 { |
| |
1052 GaimXferUiOps *ui_ops; |
| |
1053 |
| |
1054 g_return_if_fail(xfer != NULL); |
| |
1055 |
| |
1056 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
1057 |
| |
1058 if (ui_ops != NULL && ui_ops->add_xfer != NULL) |
| |
1059 ui_ops->add_xfer(xfer); |
| |
1060 } |
| |
1061 |
| |
1062 void |
| |
1063 gaim_xfer_cancel_local(GaimXfer *xfer) |
| |
1064 { |
| |
1065 GaimXferUiOps *ui_ops; |
| |
1066 char *msg = NULL; |
| |
1067 |
| |
1068 g_return_if_fail(xfer != NULL); |
| |
1069 |
| |
1070 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_LOCAL); |
| |
1071 xfer->end_time = time(NULL); |
| |
1072 |
| |
1073 if (gaim_xfer_get_filename(xfer) != NULL) |
| |
1074 { |
| |
1075 msg = g_strdup_printf(_("You canceled the transfer of %s"), |
| |
1076 gaim_xfer_get_filename(xfer)); |
| |
1077 } |
| |
1078 else |
| |
1079 { |
| |
1080 msg = g_strdup_printf(_("File transfer cancelled")); |
| |
1081 } |
| |
1082 gaim_xfer_conversation_write(xfer, msg, FALSE); |
| |
1083 g_free(msg); |
| |
1084 |
| |
1085 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) |
| |
1086 { |
| |
1087 if (xfer->ops.cancel_send != NULL) |
| |
1088 xfer->ops.cancel_send(xfer); |
| |
1089 } |
| |
1090 else |
| |
1091 { |
| |
1092 if (xfer->ops.cancel_recv != NULL) |
| |
1093 xfer->ops.cancel_recv(xfer); |
| |
1094 } |
| |
1095 |
| |
1096 if (xfer->watcher != 0) { |
| |
1097 gaim_input_remove(xfer->watcher); |
| |
1098 xfer->watcher = 0; |
| |
1099 } |
| |
1100 |
| |
1101 if (xfer->fd != 0) |
| |
1102 close(xfer->fd); |
| |
1103 |
| |
1104 if (xfer->dest_fp != NULL) { |
| |
1105 fclose(xfer->dest_fp); |
| |
1106 xfer->dest_fp = NULL; |
| |
1107 } |
| |
1108 |
| |
1109 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
1110 |
| |
1111 if (ui_ops != NULL && ui_ops->cancel_local != NULL) |
| |
1112 ui_ops->cancel_local(xfer); |
| |
1113 |
| |
1114 xfer->bytes_remaining = 0; |
| |
1115 |
| |
1116 gaim_xfer_unref(xfer); |
| |
1117 } |
| |
1118 |
| |
1119 void |
| |
1120 gaim_xfer_cancel_remote(GaimXfer *xfer) |
| |
1121 { |
| |
1122 GaimXferUiOps *ui_ops; |
| |
1123 gchar *msg; |
| |
1124 GaimAccount *account; |
| |
1125 GaimBuddy *buddy; |
| |
1126 |
| |
1127 g_return_if_fail(xfer != NULL); |
| |
1128 |
| |
1129 gaim_request_close_with_handle(xfer); |
| |
1130 gaim_xfer_set_status(xfer, GAIM_XFER_STATUS_CANCEL_REMOTE); |
| |
1131 xfer->end_time = time(NULL); |
| |
1132 |
| |
1133 account = gaim_xfer_get_account(xfer); |
| |
1134 buddy = gaim_find_buddy(account, xfer->who); |
| |
1135 |
| |
1136 if (gaim_xfer_get_filename(xfer) != NULL) |
| |
1137 { |
| |
1138 msg = g_strdup_printf(_("%s canceled the transfer of %s"), |
| |
1139 buddy ? gaim_buddy_get_alias(buddy) : xfer->who, gaim_xfer_get_filename(xfer)); |
| |
1140 } |
| |
1141 else |
| |
1142 { |
| |
1143 msg = g_strdup_printf(_("%s canceled the file transfer"), |
| |
1144 buddy ? gaim_buddy_get_alias(buddy) : xfer->who); |
| |
1145 } |
| |
1146 gaim_xfer_conversation_write(xfer, msg, TRUE); |
| |
1147 gaim_xfer_error(gaim_xfer_get_type(xfer), account, xfer->who, msg); |
| |
1148 g_free(msg); |
| |
1149 |
| |
1150 if (gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) |
| |
1151 { |
| |
1152 if (xfer->ops.cancel_send != NULL) |
| |
1153 xfer->ops.cancel_send(xfer); |
| |
1154 } |
| |
1155 else |
| |
1156 { |
| |
1157 if (xfer->ops.cancel_recv != NULL) |
| |
1158 xfer->ops.cancel_recv(xfer); |
| |
1159 } |
| |
1160 |
| |
1161 if (xfer->watcher != 0) { |
| |
1162 gaim_input_remove(xfer->watcher); |
| |
1163 xfer->watcher = 0; |
| |
1164 } |
| |
1165 |
| |
1166 if (xfer->fd != 0) |
| |
1167 close(xfer->fd); |
| |
1168 |
| |
1169 if (xfer->dest_fp != NULL) { |
| |
1170 fclose(xfer->dest_fp); |
| |
1171 xfer->dest_fp = NULL; |
| |
1172 } |
| |
1173 |
| |
1174 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
1175 |
| |
1176 if (ui_ops != NULL && ui_ops->cancel_remote != NULL) |
| |
1177 ui_ops->cancel_remote(xfer); |
| |
1178 |
| |
1179 xfer->bytes_remaining = 0; |
| |
1180 |
| |
1181 gaim_xfer_unref(xfer); |
| |
1182 } |
| |
1183 |
| |
1184 void |
| |
1185 gaim_xfer_error(GaimXferType type, GaimAccount *account, const char *who, const char *msg) |
| |
1186 { |
| |
1187 char *title; |
| |
1188 |
| |
1189 g_return_if_fail(msg != NULL); |
| |
1190 g_return_if_fail(type != GAIM_XFER_UNKNOWN); |
| |
1191 |
| |
1192 if (account) { |
| |
1193 GaimBuddy *buddy; |
| |
1194 buddy = gaim_find_buddy(account, who); |
| |
1195 if (buddy) |
| |
1196 who = gaim_buddy_get_alias(buddy); |
| |
1197 } |
| |
1198 |
| |
1199 if (type == GAIM_XFER_SEND) |
| |
1200 title = g_strdup_printf(_("File transfer to %s failed."), who); |
| |
1201 else |
| |
1202 title = g_strdup_printf(_("File transfer from %s failed."), who); |
| |
1203 |
| |
1204 gaim_notify_error(NULL, NULL, title, msg); |
| |
1205 |
| |
1206 g_free(title); |
| |
1207 } |
| |
1208 |
| |
1209 void |
| |
1210 gaim_xfer_update_progress(GaimXfer *xfer) |
| |
1211 { |
| |
1212 GaimXferUiOps *ui_ops; |
| |
1213 |
| |
1214 g_return_if_fail(xfer != NULL); |
| |
1215 |
| |
1216 ui_ops = gaim_xfer_get_ui_ops(xfer); |
| |
1217 if (ui_ops != NULL && ui_ops->update_progress != NULL) |
| |
1218 ui_ops->update_progress(xfer, gaim_xfer_get_progress(xfer)); |
| |
1219 } |
| |
1220 |
| |
1221 |
| |
1222 /************************************************************************** |
| |
1223 * File Transfer Subsystem API |
| |
1224 **************************************************************************/ |
| |
1225 void * |
| |
1226 gaim_xfers_get_handle(void) { |
| |
1227 static int handle = 0; |
| |
1228 |
| |
1229 return &handle; |
| |
1230 } |
| |
1231 |
| |
1232 void |
| |
1233 gaim_xfers_init(void) { |
| |
1234 void *handle = gaim_xfers_get_handle(); |
| |
1235 |
| |
1236 /* register signals */ |
| |
1237 gaim_signal_register(handle, "file-recv-accept", |
| |
1238 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1239 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1240 GAIM_SUBTYPE_XFER)); |
| |
1241 gaim_signal_register(handle, "file-send-accept", |
| |
1242 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1243 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1244 GAIM_SUBTYPE_XFER)); |
| |
1245 gaim_signal_register(handle, "file-recv-start", |
| |
1246 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1247 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1248 GAIM_SUBTYPE_XFER)); |
| |
1249 gaim_signal_register(handle, "file-send-start", |
| |
1250 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1251 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1252 GAIM_SUBTYPE_XFER)); |
| |
1253 gaim_signal_register(handle, "file-send-cancel", |
| |
1254 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1255 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1256 GAIM_SUBTYPE_XFER)); |
| |
1257 gaim_signal_register(handle, "file-recv-cancel", |
| |
1258 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1259 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1260 GAIM_SUBTYPE_XFER)); |
| |
1261 gaim_signal_register(handle, "file-send-complete", |
| |
1262 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1263 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1264 GAIM_SUBTYPE_XFER)); |
| |
1265 gaim_signal_register(handle, "file-recv-complete", |
| |
1266 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1267 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1268 GAIM_SUBTYPE_XFER)); |
| |
1269 gaim_signal_register(handle, "file-recv-request", |
| |
1270 gaim_marshal_VOID__POINTER, NULL, 1, |
| |
1271 gaim_value_new(GAIM_TYPE_SUBTYPE, |
| |
1272 GAIM_SUBTYPE_XFER)); |
| |
1273 } |
| |
1274 |
| |
1275 void |
| |
1276 gaim_xfers_uninit(void) { |
| |
1277 gaim_signals_disconnect_by_handle(gaim_xfers_get_handle()); |
| |
1278 } |
| |
1279 |
| |
1280 void |
| |
1281 gaim_xfers_set_ui_ops(GaimXferUiOps *ops) { |
| |
1282 xfer_ui_ops = ops; |
| |
1283 } |
| |
1284 |
| |
1285 GaimXferUiOps * |
| |
1286 gaim_xfers_get_ui_ops(void) { |
| |
1287 return xfer_ui_ops; |
| |
1288 } |