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.
21 PURPLE_BEGIN_IGNORE_CAST_ALIGN
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
53 SilcClientEntry client_entry
;
54 SilcUInt32 session_id
;
59 SilcClientFileName completion
;
60 void *completion_context
;
64 silcpurple_ftp_monitor(SilcClient client
,
65 SilcClientConnection conn
,
66 SilcClientMonitorStatus status
,
67 SilcClientFileError error
,
70 SilcClientEntry client_entry
,
71 SilcUInt32 session_id
,
75 SilcPurpleXfer xfer
= context
;
76 PurpleConnection
*gc
= xfer
->sg
->gc
;
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
);
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
);
97 if (status
== SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT
)
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
);
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
);
158 silcpurple_ftp_cancel(PurpleXfer
*x
)
160 SilcPurpleXfer xfer
= purple_xfer_get_protocol_data(x
);
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
);
171 silcpurple_ftp_ask_name_cancel(PurpleXfer
*x
)
173 SilcPurpleXfer xfer
= purple_xfer_get_protocol_data(x
);
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
);
184 silcpurple_ftp_ask_name_ok(PurpleXfer
*x
)
186 SilcPurpleXfer xfer
= purple_xfer_get_protocol_data(x
);
192 name
= purple_xfer_get_local_filename(x
);
194 xfer
->completion(name
, xfer
->completion_context
);
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
,
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
);
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
;
230 if (purple_xfer_get_status(x
) != PURPLE_XFER_STATUS_ACCEPTED
)
233 silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer
->sg
->conn
->stream
),
234 &sock
, NULL
, NULL
, NULL
);
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
)) {
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
,
247 if (silcpurple_ip_is_private(remote_ip
))
248 /* We assume we are in LAN. Let's provide the connection point. */
253 if (local
&& !local_ip
)
254 local_ip
= silc_net_localip();
257 memset(¶ms
, 0, sizeof(params
));
258 params
.timeout_secs
= 60;
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 ¶ms
, 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
);
271 case SILC_CLIENT_FILE_OK
:
273 silc_free(remote_ip
);
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
));
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
));
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
));
296 purple_notify_error(gc
, _("Secure File Transfer"),
297 _("Could not start the file transfer"), NULL
,
298 purple_request_cpar_from_connection(gc
));
303 g_object_unref(xfer
->xfer
);
304 g_free(xfer
->hostname
);
307 silc_free(remote_ip
);
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
);
324 xfer
= silc_calloc(1, sizeof(*xfer
));
326 silc_client_file_close(sg
->client
, sg
->conn
, session_id
);
331 xfer
->client_entry
= client_entry
;
332 xfer
->session_id
= session_id
;
333 xfer
->hostname
= g_strdup(hostname
);
335 xfer
->xfer
= purple_xfer_new(xfer
->sg
->account
, PURPLE_XFER_TYPE_RECEIVE
,
336 xfer
->client_entry
->nickname
);
338 silc_client_file_close(xfer
->sg
->client
, xfer
->sg
->conn
, xfer
->session_id
);
339 g_free(xfer
->hostname
);
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
);
354 silcpurple_ftp_send_cancel(PurpleXfer
*x
)
356 SilcPurpleXfer xfer
= purple_xfer_get_protocol_data(x
);
361 /* This call will free all resources */
362 silc_client_file_close(xfer
->sg
->client
, xfer
->sg
->conn
, xfer
->session_id
);
366 silcpurple_ftp_send(PurpleXfer
*x
)
368 SilcPurpleXfer xfer
= purple_xfer_get_protocol_data(x
);
370 char *local_ip
= NULL
, *remote_ip
= NULL
;
371 gboolean local
= TRUE
;
372 SilcClientConnectionParams params
;
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
)) {
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
,
393 if (silcpurple_ip_is_private(remote_ip
))
394 /* We assume we are in LAN. Let's provide the connection point. */
399 if (local
&& !local_ip
)
400 local_ip
= silc_net_localip();
402 memset(¶ms
, 0, sizeof(params
));
403 params
.timeout_secs
= 60;
405 /* Provide connection point */
406 params
.local_ip
= local_ip
;
409 silc_client_file_send(xfer
->sg
->client
, xfer
->sg
->conn
,
410 xfer
->client_entry
, ¶ms
,
411 xfer
->sg
->public_key
, xfer
->sg
->private_key
,
412 silcpurple_ftp_monitor
, xfer
,
413 name
, &xfer
->session_id
);
416 silc_free(remote_ip
);
420 silcpurple_ftp_send_file_resolved(SilcClient client
,
421 SilcClientConnection conn
,
426 PurpleConnection
*gc
= client
->application
;
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
));
440 silcpurple_ftp_send_file(client
->application
, (const char *)context
, NULL
);
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
;
452 g_return_val_if_fail(name
!= NULL
, NULL
);
454 /* Find client entry */
455 clients
= silc_client_get_clients_local(client
, conn
, name
, FALSE
);
457 silc_client_get_clients(client
, conn
, name
, NULL
,
458 silcpurple_ftp_send_file_resolved
,
462 silc_dlist_start(clients
);
464 xfer
= silc_calloc(1, sizeof(*xfer
));
465 g_return_val_if_fail(xfer
!= NULL
, NULL
);
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
);
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
);
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 */
493 purple_xfer_request_accepted(xfer
, file
);
495 purple_xfer_request(xfer
);