5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 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.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
27 /* File transmission session */
28 struct SilcClientFtpSessionStruct {
29 SilcClient client; /* Client */
30 SilcClientConnection conn; /* Connection to remote host */
31 SilcClientEntry client_entry; /* The client entry */
32 SilcClientListener listener; /* Listener */
33 SilcAsyncOperation op; /* Operation for connecting */
34 SilcClientConnectionParams params; /* Connection params */
35 SilcPublicKey public_key; /* Public key used in key exchange */
36 SilcPrivateKey private_key; /* Private key used in key exchange */
37 SilcUInt32 session_id; /* File transfer ID */
39 SilcClientFileMonitor monitor; /* File transfer monitor callback */
40 void *monitor_context;
41 SilcClientFileAskName ask_name; /* File name asking callback */
42 void *ask_name_context;
43 char *filepath; /* The remote filename */
44 char *path; /* User given path to save the file */
46 SilcStream stream; /* Wrapped SilcPacketStream */
47 SilcSFTP sftp; /* SFTP server/client */
48 SilcSFTPFilesystem fs; /* SFTP memory file system */
49 SilcSFTPHandle dir_handle; /* SFTP session directory handle */
50 SilcSFTPHandle read_handle; /* SFTP session file handles */
52 char *hostname; /* Remote host */
53 SilcUInt16 port; /* Remote port */
54 SilcUInt64 filesize; /* File size */
55 SilcUInt64 read_offset; /* Current read offset */
56 int fd; /* File descriptor */
57 unsigned int initiator : 1; /* File sender sets this to TRUE */
58 unsigned int closed : 1; /* silc_client_file_close called */
61 /************************* SFTP Server Callbacks ****************************/
63 /* SFTP monitor callback for SFTP server. This reports the application
64 how the transmission is going along. This function is for the client
65 who made the file available for download. */
67 static void silc_client_ftp_monitor(SilcSFTP sftp,
68 SilcSFTPMonitors type,
69 const SilcSFTPMonitorData data,
72 SilcClientFtpSession session = (SilcClientFtpSession)context;
74 if (type == SILC_SFTP_MONITOR_READ) {
75 /* Call the monitor for application */
77 (*session->monitor)(session->client, session->conn,
78 SILC_CLIENT_FILE_MONITOR_SEND,
80 data->offset, session->filesize,
81 session->client_entry, session->session_id,
82 session->filepath, session->monitor_context);
86 /************************* SFTP Client Callbacks ****************************/
88 /* Returns the read data. This is the downloader's function (client side)
89 to receive the read data and read more until EOF is received from
90 the other side. This will also monitor the transmission and notify
93 static void silc_client_ftp_data(SilcSFTP sftp,
94 SilcSFTPStatus status,
95 const unsigned char *data,
99 SilcClientFtpSession session = (SilcClientFtpSession)context;
101 SILC_LOG_DEBUG(("Start"));
103 if (status == SILC_SFTP_STATUS_EOF) {
106 /* Close the handle */
107 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
108 session->read_handle = NULL;
110 /* Close the read file descriptor */
111 silc_file_close(session->fd);
115 if (status != SILC_SFTP_STATUS_OK) {
116 /* Call monitor callback */
117 if (session->monitor)
118 (*session->monitor)(session->client, session->conn,
119 SILC_CLIENT_FILE_MONITOR_ERROR,
120 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
121 SILC_CLIENT_FILE_NO_SUCH_FILE :
122 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
123 SILC_CLIENT_FILE_PERMISSION_DENIED :
124 SILC_CLIENT_FILE_ERROR), 0, 0,
125 session->client_entry, session->session_id,
126 session->filepath, session->monitor_context);
128 /* Close the handle */
129 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
130 session->read_handle = NULL;
132 /* Close the read file descriptor */
133 silc_file_close(session->fd);
137 /* Read more, until EOF is received */
138 session->read_offset += data_len;
139 silc_sftp_read(sftp, session->read_handle, session->read_offset,
140 SILC_PACKET_MAX_LEN - 1024,
141 silc_client_ftp_data, session);
143 /* Write the read data to the real file */
144 silc_file_write(session->fd, data, data_len);
146 /* Call monitor callback */
147 if (session->monitor)
148 (*session->monitor)(session->client, session->conn,
149 SILC_CLIENT_FILE_MONITOR_RECEIVE,
151 session->read_offset, session->filesize,
152 session->client_entry, session->session_id,
153 session->filepath, session->monitor_context);
156 /* Returns handle for the opened file. This is the downloader's function.
157 This will begin reading the data from the file. */
159 static void silc_client_ftp_open_handle(SilcSFTP sftp,
160 SilcSFTPStatus status,
161 SilcSFTPHandle handle,
164 SilcClientFtpSession session = (SilcClientFtpSession)context;
167 SILC_LOG_DEBUG(("Start"));
169 if (status != SILC_SFTP_STATUS_OK) {
170 /* Call monitor callback */
171 if (session->monitor)
172 (*session->monitor)(session->client, session->conn,
173 SILC_CLIENT_FILE_MONITOR_ERROR,
174 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
175 SILC_CLIENT_FILE_NO_SUCH_FILE :
176 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
177 SILC_CLIENT_FILE_PERMISSION_DENIED :
178 SILC_CLIENT_FILE_ERROR), 0, 0,
179 session->client_entry, session->session_id,
180 session->filepath, session->monitor_context);
184 /* Open the actual local file */
185 memset(path, 0, sizeof(path));
186 silc_snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
187 session->path : "", session->filepath);
188 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
189 if (session->fd < 0) {
190 /* Call monitor callback */
191 session->client->internal->ops->say(session->client, session->conn,
192 SILC_CLIENT_MESSAGE_ERROR,
193 "File `%s' open failed: %s",
197 if (session->monitor)
198 (*session->monitor)(session->client, session->conn,
199 SILC_CLIENT_FILE_MONITOR_ERROR,
200 SILC_CLIENT_FILE_PERMISSION_DENIED, 0, 0,
201 session->client_entry, session->session_id,
202 session->filepath, session->monitor_context);
206 session->read_handle = handle;
208 /* Now, start reading the file */
209 silc_sftp_read(sftp, session->read_handle, session->read_offset,
210 SILC_PACKET_MAX_LEN - 1024,
211 silc_client_ftp_data, session);
213 /* Call monitor callback */
214 if (session->monitor)
215 (*session->monitor)(session->client, session->conn,
216 SILC_CLIENT_FILE_MONITOR_RECEIVE,
218 session->read_offset, session->filesize,
219 session->client_entry, session->session_id,
220 session->filepath, session->monitor_context);
223 /* Ask filename completion callback. Delivers the filepath selected by
226 static void silc_client_ftp_ask_name(const char *filepath,
229 SilcClientFtpSession session = (SilcClientFtpSession)context;
230 SilcSFTPAttributesStruct attr;
231 char *remote_file = NULL;
233 SILC_LOG_DEBUG(("Start"));
236 remote_file = session->filepath;
237 session->filepath = NULL;
238 silc_free(session->path);
239 session->path = NULL;
240 session->filepath = strdup(filepath);
242 remote_file = strdup(session->filepath);
245 /* Now open the file */
246 memset(&attr, 0, sizeof(attr));
247 silc_sftp_open(session->sftp, remote_file, SILC_SFTP_FXF_READ, &attr,
248 silc_client_ftp_open_handle, session);
250 /* Close the directory handle */
251 silc_sftp_close(session->sftp, session->dir_handle, NULL, NULL);
252 session->dir_handle = NULL;
254 silc_free(remote_file);
257 /* Returns the file name available for download. This is the downloader's
260 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
261 SilcSFTPStatus status,
262 const SilcSFTPName name,
265 SilcClientFtpSession session = (SilcClientFtpSession)context;
267 SILC_LOG_DEBUG(("Start"));
269 if (status != SILC_SFTP_STATUS_OK) {
270 /* Call monitor callback */
271 if (session->monitor)
272 (*session->monitor)(session->client, session->conn,
273 SILC_CLIENT_FILE_MONITOR_ERROR,
274 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
275 SILC_CLIENT_FILE_NO_SUCH_FILE :
276 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
277 SILC_CLIENT_FILE_PERMISSION_DENIED :
278 SILC_CLIENT_FILE_ERROR), 0, 0,
279 session->client_entry, session->session_id,
280 session->filepath, session->monitor_context);
284 /* Save the important attributes like filename and file size */
285 session->filepath = strdup(name->filename[0]);
286 session->filesize = name->attrs[0]->size;
288 /* If the path was not provided, ask from application where to save the
290 if (!session->path && session->ask_name) {
291 session->ask_name(session->client, session->conn, session->session_id,
292 name->filename[0], silc_client_ftp_ask_name, session,
293 session->ask_name_context);
297 /* Start downloading immediately to current directory. */
298 silc_client_ftp_ask_name(NULL, session);
301 /* Returns the file handle after giving opendir command. This is the
302 downloader's function. */
304 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
305 SilcSFTPStatus status,
306 SilcSFTPHandle handle,
309 SilcClientFtpSession session = (SilcClientFtpSession)context;
311 SILC_LOG_DEBUG(("Start"));
313 if (status != SILC_SFTP_STATUS_OK) {
314 /* Call monitor callback */
315 if (session->monitor)
316 (*session->monitor)(session->client, session->conn,
317 SILC_CLIENT_FILE_MONITOR_ERROR,
318 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
319 SILC_CLIENT_FILE_NO_SUCH_FILE :
320 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
321 SILC_CLIENT_FILE_PERMISSION_DENIED :
322 SILC_CLIENT_FILE_ERROR), 0, 0,
323 session->client_entry, session->session_id,
324 session->filepath, session->monitor_context);
328 /* Now, read the directory */
329 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
330 session->dir_handle = handle;
333 /* SFTP version callback for SFTP client. This is the downloader's function
334 after initializing the SFTP connection to the remote client. This will
335 find out the filename available for download. */
337 static void silc_client_ftp_version(SilcSFTP sftp,
338 SilcSFTPStatus status,
339 SilcSFTPVersion version,
342 SilcClientFtpSession session = (SilcClientFtpSession)context;
344 SILC_LOG_DEBUG(("Start"));
346 if (status != SILC_SFTP_STATUS_OK) {
347 /* Call monitor callback */
348 if (session->monitor)
349 (*session->monitor)(session->client, session->conn,
350 SILC_CLIENT_FILE_MONITOR_ERROR,
351 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
352 SILC_CLIENT_FILE_NO_SUCH_FILE :
353 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
354 SILC_CLIENT_FILE_PERMISSION_DENIED :
355 SILC_CLIENT_FILE_ERROR), 0, 0,
356 session->client_entry, session->session_id,
357 session->filepath, session->monitor_context);
361 /* The SFTP session is open, now retrieve the info about available file. */
362 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
365 /* SFTP stream error callback */
367 static void silc_client_ftp_error(SilcSFTP sftp, SilcSFTPStatus status,
373 /************************ Static utility functions **************************/
375 /* Free session resources. Connection must be closed before getting
378 static void silc_client_ftp_session_free(SilcClientFtpSession session)
380 SILC_LOG_DEBUG(("Free session %d", session->session_id));
382 silc_dlist_del(session->client->internal->ftp_sessions, session);
384 /* Abort connecting */
386 silc_async_abort(session->op, NULL, NULL);
390 if (session->initiator)
391 silc_sftp_server_shutdown(session->sftp);
393 silc_sftp_client_shutdown(session->sftp);
396 silc_sftp_fs_memory_free(session->fs);
398 /* Destroy listener */
399 if (session->listener)
400 silc_client_listener_free(session->listener);
402 /* Destroy wrapped stream */
404 silc_stream_destroy(session->stream);
406 silc_client_unref_client(session->client, session->conn,
407 session->client_entry);
408 silc_free(session->hostname);
409 silc_free(session->filepath);
410 silc_free(session->path);
414 /* File transfer session timeout */
416 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
418 SilcClientFtpSession session = context;
420 SILC_LOG_DEBUG(("Timeout"));
422 /* Close connection (destroyes the session context later). If it is
423 already closed, destroy the session now. */
425 silc_client_close_connection(session->client, session->conn);
426 session->conn = NULL;
428 /* Call monitor callback */
429 if (session->monitor)
430 (*session->monitor)(session->client, session->conn,
431 SILC_CLIENT_FILE_MONITOR_ERROR,
432 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
433 session->client_entry, session->session_id,
434 session->filepath, session->monitor_context);
436 silc_client_ftp_session_free(context);
440 /* File transfer session closing task callback */
442 SILC_TASK_CALLBACK(silc_client_file_close_final)
444 SilcClientFtpSession session = context;
446 /* Close connection (destroyes the session context later). If it is
447 already closed, destroy the session now. */
449 silc_client_close_connection(session->client, session->conn);
450 session->conn = NULL;
452 silc_client_ftp_session_free(context);
456 /* Client resolving callback. Continues with the FTP packet processing */
458 static void silc_client_ftp_client_resolved(SilcClient client,
459 SilcClientConnection conn,
464 SilcFSMThread thread = context;
465 SilcPacket packet = silc_fsm_get_state_context(thread);
467 /* If no client found, ignore the packet, a silent error */
469 silc_packet_free(packet);
470 silc_fsm_finish(thread);
474 /* Continue processing the packet */
475 SILC_FSM_CALL_CONTINUE(context);
478 /* FTP packet payload encoder/decoder. This is called for every FTP packet.
479 We add/remove FTP payload in this function, because SFTP library does not
483 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
484 SilcBuffer buffer, void *context)
486 /* Pull FTP type in the payload, revealing SFTP payload */
487 if (status == SILC_STREAM_CAN_READ) {
488 if (silc_buffer_len(buffer) >= 1)
489 silc_buffer_pull(buffer, 1);
493 /* Add FTP type before SFTP data */
494 if (status == SILC_STREAM_CAN_WRITE) {
495 if (silc_buffer_format(buffer,
505 /* FTP Connection callback. The SFTP session is started here. */
508 silc_client_ftp_connect_completion(SilcClient client,
509 SilcClientConnection conn,
510 SilcClientConnectionStatus status,
515 SilcClientFtpSession session = context;
517 session->conn = conn;
520 silc_schedule_task_del_by_context(client->schedule, session);
523 case SILC_CLIENT_CONN_SUCCESS:
524 SILC_LOG_DEBUG(("Connected, conn %p", conn));
526 /* Wrap the connection packet stream */
527 session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
529 silc_client_ftp_coder, session);
530 if (!session->stream) {
531 /* Call monitor callback */
532 if (session->monitor)
533 (*session->monitor)(session->client, session->conn,
534 SILC_CLIENT_FILE_MONITOR_ERROR,
535 SILC_CLIENT_FILE_ERROR, 0, 0,
536 session->client_entry, session->session_id,
537 session->filepath, session->monitor_context);
538 silc_client_close_connection(client, conn);
539 session->conn = NULL;
543 if (!session->initiator) {
544 /* If we are the SFTP client then start the SFTP session and retrieve
545 the info about the file available for download. */
546 session->sftp = silc_sftp_client_start(session->stream,
547 conn->internal->schedule,
548 silc_client_ftp_version,
549 silc_client_ftp_error, session);
551 /* Start SFTP server */
552 session->sftp = silc_sftp_server_start(session->stream,
553 conn->internal->schedule,
554 silc_client_ftp_error, session,
557 /* Monitor transmission */
558 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
559 silc_client_ftp_monitor, session);
564 case SILC_CLIENT_CONN_DISCONNECTED:
565 SILC_LOG_DEBUG(("Disconnected %p", conn));
567 /* Call monitor callback */
568 if (session->monitor)
569 (*session->monitor)(session->client, session->conn,
570 SILC_CLIENT_FILE_MONITOR_DISCONNECT,
571 SILC_CLIENT_FILE_ERROR, 0, 0,
572 session->client_entry, session->session_id,
573 session->filepath, session->monitor_context);
575 /* Connection already closed */
576 session->conn = NULL;
578 /* If closed by user, destroy the session now */
580 silc_client_ftp_session_free(session);
583 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
584 SILC_LOG_DEBUG(("Connecting timeout"));
586 /* Call monitor callback */
587 if (session->monitor)
588 (*session->monitor)(session->client, session->conn,
589 SILC_CLIENT_FILE_MONITOR_ERROR,
590 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
591 session->client_entry, session->session_id,
592 session->filepath, session->monitor_context);
594 /* Connection already closed */
595 session->conn = NULL;
599 SILC_LOG_DEBUG(("Connecting error %d", status));
601 /* Call monitor callback */
602 if (session->monitor)
603 (*session->monitor)(session->client, session->conn,
604 SILC_CLIENT_FILE_MONITOR_ERROR,
605 status != SILC_CLIENT_CONN_ERROR ?
606 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
607 SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
608 session->client_entry, session->session_id,
609 session->filepath, session->monitor_context);
611 /* Connection already closed */
612 session->conn = NULL;
617 /*************************** File Transfer API ******************************/
619 /* Free all file transfer sessions. */
621 void silc_client_ftp_free_sessions(SilcClient client)
623 SilcClientFtpSession session;
625 if (!client->internal->ftp_sessions)
628 silc_dlist_start(client->internal->ftp_sessions);
629 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
630 silc_client_ftp_session_free(session);
631 silc_dlist_del(client->internal->ftp_sessions, session);
634 /* Free file transfer session by client entry. */
636 void silc_client_ftp_session_free_client(SilcClient client,
637 SilcClientEntry client_entry)
639 SilcClientFtpSession session;
641 if (!client->internal->ftp_sessions)
644 /* Get the session */
645 silc_dlist_start(client->internal->ftp_sessions);
646 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
647 if (session->client_entry == client_entry)
648 silc_client_ftp_session_free(session);
651 /* Sends a file indicated by the `filepath' to the remote client
652 indicated by the `client_entry'. This will negotiate a secret key
653 with the remote client before actually starting the transmission of
654 the file. The `monitor' callback will be called to monitor the
655 transmission of the file. */
658 silc_client_file_send(SilcClient client,
659 SilcClientConnection conn,
660 SilcClientEntry client_entry,
661 SilcClientConnectionParams *params,
662 SilcPublicKey public_key,
663 SilcPrivateKey private_key,
664 SilcClientFileMonitor monitor,
665 void *monitor_context,
666 const char *filepath,
667 SilcUInt32 *session_id)
669 SilcClientFtpSession session;
671 char *filename, *path;
674 SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
676 if (!client || !client_entry || !filepath || !params ||
677 !public_key || !private_key)
678 return SILC_CLIENT_FILE_ERROR;
680 /* Check for existing session for `filepath'. */
681 silc_dlist_start(client->internal->ftp_sessions);
682 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
683 if (session->filepath && !strcmp(session->filepath, filepath) &&
684 session->client_entry == client_entry)
685 return SILC_CLIENT_FILE_ALREADY_STARTED;
688 /* See whether the file exists and can be opened */
689 fd = silc_file_open(filepath, O_RDONLY);
691 return SILC_CLIENT_FILE_NO_SUCH_FILE;
694 /* Add new session */
695 session = silc_calloc(1, sizeof(*session));
697 return SILC_CLIENT_FILE_ERROR;
698 session->session_id = ++client->internal->next_session_id;
699 session->client = client;
700 session->initiator = TRUE;
701 session->client_entry = silc_client_ref_client(client, conn, client_entry);
702 session->monitor = monitor;
703 session->monitor_context = monitor_context;
704 session->filepath = strdup(filepath);
705 session->params = *params;
706 session->public_key = public_key;
707 session->private_key = private_key;
709 if (silc_asprintf(&path, "file://%s", filepath) < 0) {
711 return SILC_CLIENT_FILE_NO_MEMORY;
714 /* Allocate memory filesystem and put the file to it */
715 if (strrchr(path, '/'))
716 filename = strrchr(path, '/') + 1;
718 filename = (char *)path;
719 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
720 SILC_SFTP_FS_PERM_EXEC);
721 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
724 session->filesize = silc_file_size(filepath);
726 /* If local IP is provided, create listener for incoming key exchange */
727 if (params->local_ip || params->bind_ip) {
729 silc_client_listener_add(client,
730 conn->internal->schedule,
731 params, public_key, private_key,
732 silc_client_ftp_connect_completion,
734 if (!session->listener) {
735 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
736 "Cannot create listener for file transfer: "
737 "%s", strerror(errno));
739 return SILC_CLIENT_FILE_NO_MEMORY;
742 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
743 strdup(params->local_ip));
744 session->port = silc_client_listener_get_local_port(session->listener);
747 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
749 /* Send the key agreement inside FTP packet */
750 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
753 if (session->listener)
754 silc_client_listener_free(session->listener);
756 return SILC_CLIENT_FILE_NO_MEMORY;
758 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
759 SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
761 SILC_STR_DATA(silc_buffer_data(keyagr),
762 silc_buffer_len(keyagr)),
765 silc_buffer_free(keyagr);
768 silc_dlist_add(client->internal->ftp_sessions, session);
770 *session_id = session->session_id;
772 /* Add session request timeout */
773 if (params && params->timeout_secs)
774 silc_schedule_task_add_timeout(client->schedule,
775 silc_client_ftp_timeout, session,
776 params->timeout_secs, 0);
778 return SILC_CLIENT_FILE_OK;
781 /* Receives a file from a client indicated by the `client_entry'. The
782 `session_id' indicates the file transmission session and it has been
783 received in the `ftp' client operation function. This will actually
784 perform the key agreement protocol with the remote client before
785 actually starting the file transmission. The `monitor' callback
786 will be called to monitor the transmission. */
789 silc_client_file_receive(SilcClient client,
790 SilcClientConnection conn,
791 SilcClientConnectionParams *params,
792 SilcPublicKey public_key,
793 SilcPrivateKey private_key,
794 SilcClientFileMonitor monitor,
795 void *monitor_context,
797 SilcUInt32 session_id,
798 SilcClientFileAskName ask_name,
799 void *ask_name_context)
801 SilcClientFtpSession session;
804 if (!client || !conn)
805 return SILC_CLIENT_FILE_ERROR;
807 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
809 /* Get the session */
810 silc_dlist_start(client->internal->ftp_sessions);
811 while ((session = silc_dlist_get(client->internal->ftp_sessions))
813 if (session->session_id == session_id) {
818 if (session == SILC_LIST_END) {
819 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
820 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
823 /* See if we have this session running already */
824 if (session->sftp || session->listener) {
825 SILC_LOG_DEBUG(("Session already started"));
826 return SILC_CLIENT_FILE_ALREADY_STARTED;
829 session->monitor = monitor;
830 session->monitor_context = monitor_context;
831 session->ask_name = ask_name;
832 session->ask_name_context = ask_name_context;
833 session->path = path ? strdup(path) : NULL;
835 /* If the hostname and port already exists then the remote client did
836 provide the connection point to us and we won't create listener, but
837 create the connection ourselves. */
838 if (session->hostname && session->port) {
839 SILC_LOG_DEBUG(("Connecting to remote client"));
840 /* Connect to the remote client. Performs key exchange automatically. */
842 silc_client_connect_to_client(client, params, public_key, private_key,
843 session->hostname, session->port,
844 silc_client_ftp_connect_completion,
848 return SILC_CLIENT_FILE_ERROR;
851 /* Add the listener for the key agreement */
852 SILC_LOG_DEBUG(("Creating listener for file transfer"));
853 if (!params || (!params->local_ip && !params->bind_ip)) {
855 return SILC_CLIENT_FILE_ERROR;
858 silc_client_listener_add(client, conn->internal->schedule, params,
859 public_key, private_key,
860 silc_client_ftp_connect_completion,
862 if (!session->listener) {
863 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
864 "Cannot create listener for file transfer: "
865 "%s", strerror(errno));
867 return SILC_CLIENT_FILE_NO_MEMORY;
869 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
870 strdup(params->local_ip));
871 session->port = silc_client_listener_get_local_port(session->listener);
873 /* Send the key agreement inside FTP packet */
874 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
875 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
878 silc_client_listener_free(session->listener);
880 return SILC_CLIENT_FILE_NO_MEMORY;
882 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
883 SILC_ID_CLIENT, &session->client_entry->id,
886 SILC_STR_DATA(silc_buffer_data(keyagr),
887 silc_buffer_len(keyagr)),
889 silc_buffer_free(keyagr);
891 /* Add session request timeout */
892 if (params && params->timeout_secs)
893 silc_schedule_task_add_timeout(client->schedule,
894 silc_client_ftp_timeout, session,
895 params->timeout_secs, 0);
898 return SILC_CLIENT_FILE_OK;
901 /* Closes file transmission session indicated by the `session_id'.
902 If file transmission is being conducted it will be aborted
903 automatically. This function is also used to close the session
904 after successful file transmission. This function can be used
905 also to reject incoming file transmission request. */
907 SilcClientFileError silc_client_file_close(SilcClient client,
908 SilcClientConnection conn,
909 SilcUInt32 session_id)
911 SilcClientFtpSession session;
913 if (!client || !conn)
914 return SILC_CLIENT_FILE_ERROR;
916 SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
918 /* Get the session */
919 silc_dlist_start(client->internal->ftp_sessions);
920 while ((session = silc_dlist_get(client->internal->ftp_sessions))
922 if (session->session_id == session_id)
926 if (session == SILC_LIST_END) {
927 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
928 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
931 if (session->monitor) {
932 (*session->monitor)(session->client, session->conn,
933 SILC_CLIENT_FILE_MONITOR_CLOSED,
934 SILC_CLIENT_FILE_OK, 0, 0,
935 session->client_entry, session->session_id,
936 session->filepath, session->monitor_context);
938 /* No more callbacks to application */
939 session->monitor = NULL;
942 session->closed = TRUE;
944 /* Destroy via timeout */
945 silc_schedule_task_add_timeout(conn->internal->schedule,
946 silc_client_file_close_final, session,
949 return SILC_CLIENT_FILE_OK;
952 /************************** FTP Request Processing **************************/
954 /* Received file transfer packet. Only file transfer requests get here.
955 The actual file transfer is handled by the SFTP library when we give it
956 the packet stream wrapped into SilcStream context. */
958 SILC_FSM_STATE(silc_client_ftp)
960 SilcClientConnection conn = fsm_context;
961 SilcClient client = conn->client;
962 SilcPacket packet = state_context;
963 SilcClientFtpSession session;
964 SilcClientID remote_id;
965 SilcClientEntry remote_client;
966 SilcKeyAgreementPayload payload;
970 SILC_LOG_DEBUG(("Process file transfer packet"));
972 if (silc_buffer_len(&packet->buffer) < 1)
975 /* We support file transfer type number 1 (== SFTP) */
976 if (packet->buffer.data[0] != 0x01) {
977 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
978 packet->buffer.data[0]));
982 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
983 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
984 SILC_LOG_DEBUG(("Invalid client ID"));
988 /* Check whether we know this client already */
989 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
990 if (!remote_client || !remote_client->internal.valid) {
991 /** Resolve client info */
992 silc_client_unref_client(client, conn, remote_client);
993 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
994 client, conn, &remote_id, NULL,
995 silc_client_ftp_client_resolved,
1001 silc_dlist_start(client->internal->ftp_sessions);
1002 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1003 if (session->client_entry == remote_client &&
1004 (!session->initiator || !session->listener))
1008 /* Parse the key agreement payload */
1010 silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1011 silc_buffer_len(&packet->buffer) - 1);
1013 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1017 hostname = silc_key_agreement_get_hostname(payload);
1018 port = silc_key_agreement_get_port(payload);
1019 if (!hostname || !port) {
1024 /* If session doesn't exist, we create new one. If session exists, but
1025 we are responder it means that the remote sent another request and user
1026 hasn't even accepted the first one yet. We assume this session is new
1028 if (!session || !hostname || !session->initiator) {
1029 /* New file transfer session */
1030 SILC_LOG_DEBUG(("New file transfer session %d",
1031 client->internal->next_session_id + 1));
1033 session = silc_calloc(1, sizeof(*session));
1036 session->session_id = ++client->internal->next_session_id;
1037 session->client = client;
1038 session->client_entry = silc_client_ref_client(client, conn,
1040 if (hostname && port) {
1041 session->hostname = strdup(hostname);
1042 session->port = port;
1044 silc_dlist_add(client->internal->ftp_sessions, session);
1046 /* Notify application of incoming FTP request */
1047 client->internal->ops->ftp(client, conn, remote_client,
1048 session->session_id, hostname, port);
1052 /* Session exists, continue with key agreement protocol. */
1053 SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1054 session->session_id));
1056 session->hostname = strdup(hostname);
1057 session->port = port;
1059 /* Connect to the remote client. Performs key exchange automatically. */
1061 silc_client_connect_to_client(client, &session->params,
1062 session->public_key, session->private_key,
1063 session->hostname, session->port,
1064 silc_client_ftp_connect_completion,
1067 /* Call monitor callback */
1068 if (session->monitor)
1069 (*session->monitor)(session->client, session->conn,
1070 SILC_CLIENT_FILE_MONITOR_ERROR,
1071 SILC_CLIENT_FILE_ERROR, 0, 0,
1072 session->client_entry, session->session_id,
1073 session->filepath, session->monitor_context);
1077 silc_packet_free(packet);
1078 return SILC_FSM_FINISH;