rename accountopt.[ch] to purpleaccountoption.[ch]
[pidgin-git.git] / libpurple / protocols / silc / ft.c
blob1331b796a0a9ee7ed0bde6abd68e7e950c3ce02b
1 /*
3 silcpurple_ft.c
5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2004 - 2007 Pekka Riikonen
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.
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.
20 #include "internal.h"
21 PURPLE_BEGIN_IGNORE_CAST_ALIGN
22 #include "silc.h"
23 PURPLE_END_IGNORE_CAST_ALIGN
24 #include "silcclient.h"
25 #include "silcpurple.h"
27 /****************************** File Transfer ********************************/
29 /* This implements the secure file transfer protocol (SFTP) using the SILC
30 SFTP library implementation. The API we use from the SILC Toolkit is the
31 SILC Client file transfer API, as it provides a simple file transfer we
32 need in this case. We could use the SILC SFTP API directly, but it would
33 be an overkill since we'd effectively re-implement the file transfer what
34 the SILC Client's file transfer API already provides.
36 From Purple we do NOT use the FT API to do the transfer as it is very limiting.
37 In fact it does not suite to file transfers like SFTP at all. For example,
38 it assumes that read operations are synchronous what they are not in SFTP.
39 It also assumes that the file transfer socket is to be handled by the Purple
40 eventloop, and this naturally is something we don't want to do in case of
41 SILC Toolkit. The FT API suites well to purely stream based file transfers
42 like HTTP GET and similar.
44 For this reason, we directly access the Purple GKT FT API and hack the FT
45 API to merely provide the user interface experience and all the magic
46 is done in the SILC Toolkit. Ie. we update the statistics information in
47 the FT API for user interface, and that's it. A bit dirty but until the
48 FT API gets better this is the way to go. Good thing that FT API allowed
49 us to do this. */
51 typedef struct {
52 SilcPurple sg;
53 SilcClientEntry client_entry;
54 SilcUInt32 session_id;
55 char *hostname;
56 SilcUInt16 port;
57 PurpleXfer *xfer;
59 SilcClientFileName completion;
60 void *completion_context;
61 } *SilcPurpleXfer;
63 static void
64 silcpurple_ftp_monitor(SilcClient client,
65 SilcClientConnection conn,
66 SilcClientMonitorStatus status,
67 SilcClientFileError error,
68 SilcUInt64 offset,
69 SilcUInt64 filesize,
70 SilcClientEntry client_entry,
71 SilcUInt32 session_id,
72 const char *filepath,
73 void *context)
75 SilcPurpleXfer xfer = context;
76 PurpleConnection *gc = xfer->sg->gc;
77 char tmp[256];
79 if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) {
80 /* All started sessions terminate here */
81 purple_xfer_set_protocol_data(xfer->xfer, NULL);
82 g_object_unref(xfer->xfer);
83 silc_free(xfer);
84 return;
87 if (status == SILC_CLIENT_FILE_MONITOR_DISCONNECT) {
88 purple_notify_error(gc, _("Secure File Transfer"), _("Error "
89 "during file transfer"), _("Remote disconnected"),
90 purple_request_cpar_from_connection(gc));
91 purple_xfer_set_status(xfer->xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
92 purple_xfer_update_progress(xfer->xfer);
93 silc_client_file_close(client, conn, session_id);
94 return;
97 if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT)
98 return;
100 if (status == SILC_CLIENT_FILE_MONITOR_ERROR) {
101 if (error == SILC_CLIENT_FILE_NO_SUCH_FILE) {
102 g_snprintf(tmp, sizeof(tmp), "No such file %s",
103 filepath ? filepath : "[N/A]");
104 purple_notify_error(gc, _("Secure File Transfer"),
105 _("Error during file transfer"), tmp,
106 purple_request_cpar_from_connection(gc));
107 } else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) {
108 purple_notify_error(gc, _("Secure File Transfer"),
109 _("Error during file transfer"),
110 _("Permission denied"),
111 purple_request_cpar_from_connection(gc));
112 } else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) {
113 purple_notify_error(gc, _("Secure File Transfer"),
114 _("Error during file transfer"),
115 _("Key agreement failed"),
116 purple_request_cpar_from_connection(gc));
117 } else if (error == SILC_CLIENT_FILE_TIMEOUT) {
118 purple_notify_error(gc, _("Secure File Transfer"),
119 _("Error during file transfer"),
120 _("Connection timed out"),
121 purple_request_cpar_from_connection(gc));
122 } else if (error == SILC_CLIENT_FILE_CONNECT_FAILED) {
123 purple_notify_error(gc, _("Secure File Transfer"),
124 _("Error during file transfer"),
125 _("Creating connection failed"),
126 purple_request_cpar_from_connection(gc));
127 } else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) {
128 purple_notify_error(gc, _("Secure File Transfer"),
129 _("Error during file transfer"),
130 _("File transfer session does not exist"),
131 purple_request_cpar_from_connection(gc));
133 purple_xfer_set_status(xfer->xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
134 purple_xfer_update_progress(xfer->xfer);
135 silc_client_file_close(client, conn, session_id);
136 return;
139 /* Update file transfer UI */
140 if (!offset && filesize)
141 purple_xfer_set_size(xfer->xfer, filesize);
142 if (offset && filesize) {
143 purple_xfer_set_bytes_sent(xfer->xfer, offset);
145 purple_xfer_update_progress(xfer->xfer);
147 if (status == SILC_CLIENT_FILE_MONITOR_SEND ||
148 status == SILC_CLIENT_FILE_MONITOR_RECEIVE) {
149 if (offset == filesize) {
150 /* Download finished */
151 purple_xfer_set_completed(xfer->xfer, TRUE);
152 silc_client_file_close(client, conn, session_id);
157 static void
158 silcpurple_ftp_cancel(PurpleXfer *x)
160 SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
162 if (!xfer)
163 return;
165 purple_xfer_set_status(xfer->xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
166 purple_xfer_update_progress(xfer->xfer);
167 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
170 static void
171 silcpurple_ftp_ask_name_cancel(PurpleXfer *x)
173 SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
175 if (!xfer)
176 return;
178 /* Cancel the transmission */
179 xfer->completion(NULL, xfer->completion_context);
180 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
183 static void
184 silcpurple_ftp_ask_name_ok(PurpleXfer *x)
186 SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
187 const char *name;
189 if (!xfer)
190 return;
192 name = purple_xfer_get_local_filename(x);
193 g_unlink(name);
194 xfer->completion(name, xfer->completion_context);
197 static void
198 silcpurple_ftp_ask_name(SilcClient client,
199 SilcClientConnection conn,
200 SilcUInt32 session_id,
201 const char *remote_filename,
202 SilcClientFileName completion,
203 void *completion_context,
204 void *context)
206 SilcPurpleXfer xfer = context;
208 xfer->completion = completion;
209 xfer->completion_context = completion_context;
211 purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_ask_name_ok);
212 purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_ask_name_cancel);
214 /* Request to save the file */
215 purple_xfer_set_filename(xfer->xfer, remote_filename);
216 purple_xfer_request(xfer->xfer);
219 static void
220 silcpurple_ftp_request_result(PurpleXfer *x)
222 SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
223 SilcClientFileError status;
224 PurpleConnection *gc = xfer->sg->gc;
225 SilcClientConnectionParams params;
226 gboolean local = xfer->hostname ? FALSE : TRUE;
227 char *local_ip = NULL, *remote_ip = NULL;
228 SilcSocket sock;
230 if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED)
231 return;
233 silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream),
234 &sock, NULL, NULL, NULL);
236 if (local) {
237 /* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
238 to see if we are behind NAT. */
239 if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
240 /* Check if the IP is private */
241 if (silcpurple_ip_is_private(local_ip)) {
242 local = TRUE;
243 /* Local IP is private, resolve the remote server IP to see whether
244 we are talking to Internet or just on LAN. */
245 if (silc_net_check_host_by_sock(sock, NULL,
246 &remote_ip))
247 if (silcpurple_ip_is_private(remote_ip))
248 /* We assume we are in LAN. Let's provide the connection point. */
249 local = TRUE;
253 if (local && !local_ip)
254 local_ip = silc_net_localip();
257 memset(&params, 0, sizeof(params));
258 params.timeout_secs = 60;
259 if (local)
260 /* Provide connection point */
261 params.local_ip = local_ip;
263 /* Start the file transfer */
264 status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn,
265 &params, xfer->sg->public_key,
266 xfer->sg->private_key,
267 silcpurple_ftp_monitor, xfer,
268 NULL, xfer->session_id,
269 silcpurple_ftp_ask_name, xfer);
270 switch (status) {
271 case SILC_CLIENT_FILE_OK:
272 silc_free(local_ip);
273 silc_free(remote_ip);
274 return;
275 break;
277 case SILC_CLIENT_FILE_UNKNOWN_SESSION:
278 purple_notify_error(gc, _("Secure File Transfer"),
279 _("No file transfer session active"), NULL,
280 purple_request_cpar_from_connection(gc));
281 break;
283 case SILC_CLIENT_FILE_ALREADY_STARTED:
284 purple_notify_error(gc, _("Secure File Transfer"),
285 _("File transfer already started"), NULL,
286 purple_request_cpar_from_connection(gc));
287 break;
289 case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED:
290 purple_notify_error(gc, _("Secure File Transfer"),
291 _("Could not perform key agreement for file transfer"),
292 NULL, purple_request_cpar_from_connection(gc));
293 break;
295 default:
296 purple_notify_error(gc, _("Secure File Transfer"),
297 _("Could not start the file transfer"), NULL,
298 purple_request_cpar_from_connection(gc));
299 break;
302 /* Error */
303 g_object_unref(xfer->xfer);
304 g_free(xfer->hostname);
305 silc_free(xfer);
306 silc_free(local_ip);
307 silc_free(remote_ip);
310 static void
311 silcpurple_ftp_request_denied(PurpleXfer *x)
316 void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn,
317 SilcClientEntry client_entry, SilcUInt32 session_id,
318 const char *hostname, SilcUInt16 port)
320 PurpleConnection *gc = client->application;
321 SilcPurple sg = purple_connection_get_protocol_data(gc);
322 SilcPurpleXfer xfer;
324 xfer = silc_calloc(1, sizeof(*xfer));
325 if (!xfer) {
326 silc_client_file_close(sg->client, sg->conn, session_id);
327 return;
330 xfer->sg = sg;
331 xfer->client_entry = client_entry;
332 xfer->session_id = session_id;
333 xfer->hostname = g_strdup(hostname);
334 xfer->port = port;
335 xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_TYPE_RECEIVE,
336 xfer->client_entry->nickname);
337 if (!xfer->xfer) {
338 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
339 g_free(xfer->hostname);
340 silc_free(xfer);
341 return;
343 purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_request_result);
344 purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
345 purple_xfer_set_cancel_recv_fnc(xfer->xfer, silcpurple_ftp_cancel);
346 purple_xfer_start(xfer->xfer, -1, hostname, port);
347 purple_xfer_set_protocol_data(xfer->xfer, xfer);
349 /* File transfer request */
350 purple_xfer_request(xfer->xfer);
353 static void
354 silcpurple_ftp_send_cancel(PurpleXfer *x)
356 SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
358 if (!xfer)
359 return;
361 /* This call will free all resources */
362 silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id);
365 static void
366 silcpurple_ftp_send(PurpleXfer *x)
368 SilcPurpleXfer xfer = purple_xfer_get_protocol_data(x);
369 const char *name;
370 char *local_ip = NULL, *remote_ip = NULL;
371 gboolean local = TRUE;
372 SilcClientConnectionParams params;
373 SilcSocket sock;
375 if (!xfer)
376 return;
378 name = purple_xfer_get_local_filename(x);
380 silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream),
381 &sock, NULL, NULL, NULL);
383 /* Do the same magic what we do with key agreement (see silcpurple_buddy.c)
384 to see if we are behind NAT. */
385 if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) {
386 /* Check if the IP is private */
387 if (silcpurple_ip_is_private(local_ip)) {
388 local = FALSE;
389 /* Local IP is private, resolve the remote server IP to see whether
390 we are talking to Internet or just on LAN. */
391 if (silc_net_check_host_by_sock(sock, NULL,
392 &remote_ip))
393 if (silcpurple_ip_is_private(remote_ip))
394 /* We assume we are in LAN. Let's provide the connection point. */
395 local = TRUE;
399 if (local && !local_ip)
400 local_ip = silc_net_localip();
402 memset(&params, 0, sizeof(params));
403 params.timeout_secs = 60;
404 if (local)
405 /* Provide connection point */
406 params.local_ip = local_ip;
408 /* Send the file */
409 silc_client_file_send(xfer->sg->client, xfer->sg->conn,
410 xfer->client_entry, &params,
411 xfer->sg->public_key, xfer->sg->private_key,
412 silcpurple_ftp_monitor, xfer,
413 name, &xfer->session_id);
415 silc_free(local_ip);
416 silc_free(remote_ip);
419 static void
420 silcpurple_ftp_send_file_resolved(SilcClient client,
421 SilcClientConnection conn,
422 SilcStatus status,
423 SilcDList clients,
424 void *context)
426 PurpleConnection *gc = client->application;
427 char tmp[256];
429 if (!clients) {
430 g_snprintf(tmp, sizeof(tmp),
431 _("User %s is not present in the network"),
432 (const char *)context);
433 purple_notify_error(gc, _("Secure File Transfer"),
434 _("Cannot send file"), tmp,
435 purple_request_cpar_from_connection(gc));
436 g_free(context);
437 return;
440 silcpurple_ftp_send_file(client->application, (const char *)context, NULL);
441 g_free(context);
444 PurpleXfer *silcpurple_ftp_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *name)
446 SilcPurple sg = purple_connection_get_protocol_data(gc);
447 SilcClient client = sg->client;
448 SilcClientConnection conn = sg->conn;
449 SilcDList clients;
450 SilcPurpleXfer xfer;
452 g_return_val_if_fail(name != NULL, NULL);
454 /* Find client entry */
455 clients = silc_client_get_clients_local(client, conn, name, FALSE);
456 if (!clients) {
457 silc_client_get_clients(client, conn, name, NULL,
458 silcpurple_ftp_send_file_resolved,
459 g_strdup(name));
460 return NULL;
462 silc_dlist_start(clients);
464 xfer = silc_calloc(1, sizeof(*xfer));
465 g_return_val_if_fail(xfer != NULL, NULL);
467 xfer->sg = sg;
468 xfer->client_entry = silc_dlist_get(clients);
469 xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_TYPE_SEND,
470 xfer->client_entry->nickname);
471 if (!xfer->xfer) {
472 silc_free(xfer);
473 return NULL;
475 purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_send);
476 purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied);
477 purple_xfer_set_cancel_send_fnc(xfer->xfer, silcpurple_ftp_send_cancel);
478 purple_xfer_set_protocol_data(xfer->xfer, xfer);
480 silc_free(clients);
482 return xfer->xfer;
485 void silcpurple_ftp_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *name, const char *file)
487 PurpleXfer *xfer = silcpurple_ftp_new_xfer(gc, name);
489 g_return_if_fail(xfer != NULL);
491 /* Choose file to send */
492 if (file)
493 purple_xfer_request_accepted(xfer, file);
494 else
495 purple_xfer_request(xfer);