comparison libpurple/protocols/silc10/ft.c @ 17567:ba1b50f114f6

Duplicate the current SILC prpl as silc10 for backwards compatibility with SILC Toolkit 1.0
author Stu Tomlinson <stu@nosnilmot.com>
date Sat, 09 Jun 2007 16:39:00 +0000
parents
children
comparison
equal deleted inserted replaced
17566:016eee704a96 17567:ba1b50f114f6
1 /*
2
3 silcpurple_ft.c
4
5 Author: Pekka Riikonen <priikone@silcnet.org>
6
7 Copyright (C) 2004 Pekka Riikonen
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 */
19
20 #include "silcincludes.h"
21 #include "silcclient.h"
22 #include "silcpurple.h"
23
24 /****************************** File Transfer ********************************/
25
26 /* This implements the secure file transfer protocol (SFTP) using the SILC
27 SFTP library implementation. The API we use from the SILC Toolkit is the
28 SILC Client file transfer API, as it provides a simple file transfer we
29 need in this case. We could use the SILC SFTP API directly, but it would
30 be an overkill since we'd effectively re-implement the file transfer what
31 the SILC Client's file transfer API already provides.
32
33 From Purple we do NOT use the FT API to do the transfer as it is very limiting.
34 In fact it does not suite to file transfers like SFTP at all. For example,
35 it assumes that read operations are synchronous what they are not in SFTP.
36 It also assumes that the file transfer socket is to be handled by the Purple
37 eventloop, and this naturally is something we don't want to do in case of
38 SILC Toolkit. The FT API suites well to purely stream based file transfers
39 like HTTP GET and similar.
40
41 For this reason, we directly access the Purple GKT FT API and hack the FT
42 API to merely provide the user interface experience and all the magic
43 is done in the SILC Toolkit. Ie. we update the statistics information in
44 the FT API for user interface, and that's it. A bit dirty but until the
45 FT API gets better this is the way to go. Good thing that FT API allowed
46 us to do this. */
47
48 typedef struct {
49 SilcPurple sg;
50 SilcClientEntry client_entry;
51 SilcUInt32 session_id;
52 char *hostname;
53 SilcUInt16 port;
54 PurpleXfer *xfer;
55
56 SilcClientFileName completion;
57 void *completion_context;
58 } *SilcPurpleXfer;
59
60 static void
61 silcpurple_ftp_monitor(SilcClient client,
62 SilcClientConnection conn,
63 SilcClientMonitorStatus status,
64 SilcClientFileError error,
65 SilcUInt64 offset,
66 SilcUInt64 filesize,
67 SilcClientEntry client_entry,
68 SilcUInt32 session_id,
69 const char *filepath,
70 void *context)
71 {
72 SilcPurpleXfer xfer = context;
73 PurpleConnection *gc = xfer->sg->gc;
74 char tmp[256];
75
76 if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) {
77 purple_xfer_unref(xfer->xfer);
78 silc_free(xfer);
79 return;
80 }
81
82 if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT)
83 return;
84
85 if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
86 if (error == SILC_CLIENT_FILE_NO_SUCH_FILE) {
87 g_snprintf(tmp, sizeof(tmp), "No such file %s",
88 filepath ? filepath : "[N/A]");
89 purple_notify_error(gc, _("Secure File Transfer"),
90 _("Error during file transfer"), tmp);
91 } else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) {
92 purple_notify_error(gc, _("Secure File Transfer"),
93 _("Error during file transfer"),
94 _("Permission denied"));
95 } else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) {
96 purple_notify_error(gc, _("Secure File Transfer"),
97 _("Error during file transfer"),
98 _("Key agreement failed"));
99 } else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) {
100 purple_notify_error(gc, _("Secure File Transfer"),
101 _("Error during file transfer"),
102 _("File transfer session does not exist"));
103 } else {
104 purple_notify_error(gc, _("Secure File Transfer"),
105 _("Error during file transfer"), NULL);
106 }
107 silc_client_file_close(client, conn, session_id);
108 purple_xfer_unref(xfer->xfer);
109 silc_free(xfer);
110 return;
111 }
112
113 /* Update file transfer UI */
114 if (!offset && filesize)
115 purple_xfer_set_size(xfer->xfer, filesize);
116 if (offset && filesize) {
117 xfer->xfer->bytes_sent = offset;
118 xfer->xfer->bytes_remaining = filesize - offset;
119 }
120 purple_xfer_update_progress(xfer->xfer);
121
122 if (status == SILC_CLIENT_FILE_MONITOR_SEND ||
123 status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
124 if (offset == filesize) {
125 /* Download finished */
126 purple_xfer_set_completed(xfer->xfer, TRUE);
127 silc_client_file_close(client, conn, session_id);
128 }
129 }
130 }
131
132 static void
133 silcpurple_ftp_cancel(PurpleXfer *x)
134 {
135 SilcPurpleXfer xfer = x->data;
136 xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL;
137 purple_xfer_update_progress(xfer->xfer);
138 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
139 }
140
141 static void
142 silcpurple_ftp_ask_name_cancel(PurpleXfer *x)
143 {
144 SilcPurpleXfer xfer = x->data;
145
146 /* Cancel the transmission */
147 xfer->completion(NULL, xfer->completion_context);
148 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
149 }
150
151 static void
152 silcpurple_ftp_ask_name_ok(PurpleXfer *x)
153 {
154 SilcPurpleXfer xfer = x->data;
155 const char *name;
156
157 name = purple_xfer_get_local_filename(x);
158 g_unlink(name);
159 xfer->completion(name, xfer->completion_context);
160 }
161
162 static void
163 silcpurple_ftp_ask_name(SilcClient client,
164 SilcClientConnection conn,
165 SilcUInt32 session_id,
166 const char *remote_filename,
167 SilcClientFileName completion,
168 void *completion_context,
169 void *context)
170 {
171 SilcPurpleXfer xfer = context;
172
173 xfer->completion = completion;
174 xfer->completion_context = completion_context;
175
176 purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_ask_name_ok);
177 purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_ask_name_cancel);
178
179 /* Request to save the file */
180 purple_xfer_set_filename(xfer->xfer, remote_filename);
181 purple_xfer_request(xfer->xfer);
182 }
183
184 static void
185 silcpurple_ftp_request_result(PurpleXfer *x)
186 {
187 SilcPurpleXfer xfer = x->data;
188 SilcClientFileError status;
189 PurpleConnection *gc = xfer->sg->gc;
190
191 if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED)
192 return;
193
194 /* Start the file transfer */
195 status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn,
196 silcpurple_ftp_monitor, xfer,
197 NULL, xfer->session_id,
198 silcpurple_ftp_ask_name, xfer);
199 switch (status) {
200 case SILC_CLIENT_FILE_OK:
201 return;
202 break;
203
204 case SILC_CLIENT_FILE_UNKNOWN_SESSION:
205 purple_notify_error(gc, _("Secure File Transfer"),
206 _("No file transfer session active"), NULL);
207 break;
208
209 case SILC_CLIENT_FILE_ALREADY_STARTED:
210 purple_notify_error(gc, _("Secure File Transfer"),
211 _("File transfer already started"), NULL);
212 break;
213
214 case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED:
215 purple_notify_error(gc, _("Secure File Transfer"),
216 _("Could not perform key agreement for file transfer"),
217 NULL);
218 break;
219
220 default:
221 purple_notify_error(gc, _("Secure File Transfer"),
222 _("Could not start the file transfer"), NULL);
223 break;
224 }
225
226 /* Error */
227 purple_xfer_unref(xfer->xfer);
228 g_free(xfer->hostname);
229 silc_free(xfer);
230 }
231
232 static void
233 silcpurple_ftp_request_denied(PurpleXfer *x)
234 {
235
236 }
237
238 void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
239 SilcClientEntry client_entry, SilcUInt32 session_id,
240 const char *hostname, SilcUInt16 port)
241 {
242 PurpleConnection *gc = client->application;
243 SilcPurple sg = gc->proto_data;
244 SilcPurpleXfer xfer;
245
246 xfer = silc_calloc(1, sizeof(*xfer));
247 if (!xfer) {
248 silc_client_file_close(sg->client, sg->conn, session_id);
249 return;
250 }
251
252 xfer->sg = sg;
253 xfer->client_entry = client_entry;
254 xfer->session_id = session_id;
255 xfer->hostname = g_strdup(hostname);
256 xfer->port = port;
257 xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE,
258 xfer->client_entry->nickname);
259 if (!xfer->xfer) {
260 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
261 g_free(xfer->hostname);
262 silc_free(xfer);
263 return;
264 }
265 purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_request_result);
266 purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
267 purple_xfer_set_cancel_recv_fnc(xfer->xfer, silcpurple_ftp_cancel);
268 xfer->xfer->remote_ip = g_strdup(hostname);
269 xfer->xfer->remote_port = port;
270 xfer->xfer->data = xfer;
271
272 /* File transfer request */
273 purple_xfer_request(xfer->xfer);
274 }
275
276 static void
277 silcpurple_ftp_send_cancel(PurpleXfer *x)
278 {
279 SilcPurpleXfer xfer = x->data;
280 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
281 purple_xfer_unref(xfer->xfer);
282 g_free(xfer->hostname);
283 silc_free(xfer);
284 }
285
286 static void
287 silcpurple_ftp_send(PurpleXfer *x)
288 {
289 SilcPurpleXfer xfer = x->data;
290 const char *name;
291 char *local_ip = NULL, *remote_ip = NULL;
292 gboolean local = TRUE;
293
294 name = purple_xfer_get_local_filename(x);
295
296 /* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
297 to see if we are behind NAT. */
298 if (silc_net_check_local_by_sock(xfer->sg->conn->sock->sock,
299 NULL, &local_ip)) {
300 /* Check if the IP is private */
301 if (silcpurple_ip_is_private(local_ip)) {
302 local = FALSE;
303 /* Local IP is private, resolve the remote server IP to see whether
304 we are talking to Internet or just on LAN. */
305 if (silc_net_check_host_by_sock(xfer->sg->conn->sock->sock, NULL,
306 &remote_ip))
307 if (silcpurple_ip_is_private(remote_ip))
308 /* We assume we are in LAN. Let's provide the connection point. */
309 local = TRUE;
310 }
311 }
312
313 if (local && !local_ip)
314 local_ip = silc_net_localip();
315
316 /* Send the file */
317 silc_client_file_send(xfer->sg->client, xfer->sg->conn,
318 silcpurple_ftp_monitor, xfer,
319 local_ip, 0, !local, xfer->client_entry,
320 name, &xfer->session_id);
321
322 silc_free(local_ip);
323 silc_free(remote_ip);
324 }
325
326 static void
327 silcpurple_ftp_send_file_resolved(SilcClient client,
328 SilcClientConnection conn,
329 SilcClientEntry *clients,
330 SilcUInt32 clients_count,
331 void *context)
332 {
333 PurpleConnection *gc = client->application;
334 char tmp[256];
335
336 if (!clients) {
337 g_snprintf(tmp, sizeof(tmp),
338 _("User %s is not present in the network"),
339 (const char *)context);
340 purple_notify_error(gc, _("Secure File Transfer"),
341 _("Cannot send file"), tmp);
342 silc_free(context);
343 return;
344 }
345
346 silcpurple_ftp_send_file(client->application, (const char *)context, NULL);
347 silc_free(context);
348 }
349
350 PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name)
351 {
352 SilcPurple sg = gc->proto_data;
353 SilcClient client = sg->client;
354 SilcClientConnection conn = sg->conn;
355 SilcClientEntry *clients;
356 SilcUInt32 clients_count;
357 SilcPurpleXfer xfer;
358 char *nickname;
359
360 g_return_val_if_fail(name != NULL, NULL);
361
362 if (!silc_parse_userfqdn(name, &nickname, NULL))
363 return NULL;
364
365 /* Find client entry */
366 clients = silc_client_get_clients_local(client, conn, nickname, name,
367 &clients_count);
368 if (!clients) {
369 silc_client_get_clients(client, conn, nickname, NULL,
370 silcpurple_ftp_send_file_resolved,
371 strdup(name));
372 silc_free(nickname);
373 return NULL;
374 }
375
376 xfer = silc_calloc(1, sizeof(*xfer));
377
378 g_return_val_if_fail(xfer != NULL, NULL);
379
380 xfer->sg = sg;
381 xfer->client_entry = clients[0];
382 xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND,
383 xfer->client_entry->nickname);
384 if (!xfer->xfer) {
385 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
386 g_free(xfer->hostname);
387 silc_free(xfer);
388 return NULL;
389 }
390 purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_send);
391 purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
392 purple_xfer_set_cancel_send_fnc(xfer->xfer, silcpurple_ftp_send_cancel);
393 xfer->xfer->data = xfer;
394
395 silc_free(clients);
396 silc_free(nickname);
397
398 return xfer->xfer;
399 }
400
401 void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file)
402 {
403 PurpleXfer *xfer = silcpurple_ftp_new_xfer(gc, name);
404
405 g_return_if_fail(xfer != NULL);
406
407 /* Choose file to send */
408 if (file)
409 purple_xfer_request_accepted(xfer, file);
410 else
411 purple_xfer_request(xfer);
412 }