5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2014 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 server_conn; /* Connection to server */
31 SilcClientConnection conn; /* Connection to remote host */
32 SilcClientEntry client_entry; /* The client entry */
33 SilcClientListener listener; /* Listener */
34 SilcAsyncOperation op; /* Operation for connecting */
35 SilcClientConnectionParams params; /* Connection params */
36 SilcPublicKey public_key; /* Public key used in key exchange */
37 SilcPrivateKey private_key; /* Private key used in key exchange */
38 SilcUInt32 session_id; /* File transfer ID */
40 SilcClientFileMonitor monitor; /* File transfer monitor callback */
41 void *monitor_context;
42 SilcClientFileAskName ask_name; /* File name asking callback */
43 void *ask_name_context;
44 char *filepath; /* The remote filename */
45 char *path; /* User given path to save the file */
47 SilcStream stream; /* Wrapped SilcPacketStream */
48 SilcSFTP sftp; /* SFTP server/client */
49 SilcSFTPFilesystem fs; /* SFTP memory file system */
50 SilcSFTPHandle dir_handle; /* SFTP session directory handle */
51 SilcSFTPHandle read_handle; /* SFTP session file handles */
53 char *hostname; /* Remote host */
54 SilcUInt16 port; /* Remote port */
55 SilcUInt64 filesize; /* File size */
56 SilcUInt64 read_offset; /* Current read offset */
57 int fd; /* File descriptor */
58 unsigned int initiator : 1; /* File sender sets this to TRUE */
59 unsigned int closed : 1; /* silc_client_file_close called */
62 /************************* SFTP Server Callbacks ****************************/
64 /* SFTP monitor callback for SFTP server. This reports the application
65 how the transmission is going along. This function is for the client
66 who made the file available for download. */
68 static void silc_client_ftp_monitor(SilcSFTP sftp,
69 SilcSFTPMonitors type,
70 const SilcSFTPMonitorData data,
73 SilcClientFtpSession session = (SilcClientFtpSession)context;
75 if (type == SILC_SFTP_MONITOR_READ) {
76 /* Call the monitor for application */
78 (*session->monitor)(session->client, session->conn,
79 SILC_CLIENT_FILE_MONITOR_SEND,
81 data->offset, session->filesize,
82 session->client_entry, session->session_id,
83 session->filepath, session->monitor_context);
87 /************************* SFTP Client Callbacks ****************************/
89 /* Returns the read data. This is the downloader's function (client side)
90 to receive the read data and read more until EOF is received from
91 the other side. This will also monitor the transmission and notify
94 static void silc_client_ftp_data(SilcSFTP sftp,
95 SilcSFTPStatus status,
96 const unsigned char *data,
100 SilcClientFtpSession session = (SilcClientFtpSession)context;
102 SILC_LOG_DEBUG(("Start"));
104 if (status == SILC_SFTP_STATUS_EOF) {
107 /* Close the handle */
108 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
109 session->read_handle = NULL;
111 /* Close the real file descriptor */
112 silc_file_close(session->fd);
116 if (status != SILC_SFTP_STATUS_OK) {
117 /* Call monitor callback */
118 if (session->monitor)
119 (*session->monitor)(session->client, session->conn,
120 SILC_CLIENT_FILE_MONITOR_ERROR,
121 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
122 SILC_CLIENT_FILE_NO_SUCH_FILE :
123 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
124 SILC_CLIENT_FILE_PERMISSION_DENIED :
125 SILC_CLIENT_FILE_ERROR), 0, 0,
126 session->client_entry, session->session_id,
127 session->filepath, session->monitor_context);
129 /* Close the handle */
130 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
131 session->read_handle = NULL;
133 /* Close the real file descriptor */
134 silc_file_close(session->fd);
138 /* Read more, until EOF is received */
139 session->read_offset += data_len;
140 silc_sftp_read(sftp, session->read_handle, session->read_offset,
141 SILC_PACKET_MAX_LEN - 1024,
142 silc_client_ftp_data, session);
144 /* Write the read data to the real file */
145 silc_file_write(session->fd, data, data_len);
147 /* Call monitor callback */
148 if (session->monitor)
149 (*session->monitor)(session->client, session->conn,
150 SILC_CLIENT_FILE_MONITOR_RECEIVE,
152 session->read_offset, session->filesize,
153 session->client_entry, session->session_id,
154 session->filepath, session->monitor_context);
157 /* Returns handle for the opened file. This is the downloader's function.
158 This will begin reading the data from the file. */
160 static void silc_client_ftp_open_handle(SilcSFTP sftp,
161 SilcSFTPStatus status,
162 SilcSFTPHandle handle,
165 SilcClientFtpSession session = (SilcClientFtpSession)context;
168 SILC_LOG_DEBUG(("Start"));
170 if (status != SILC_SFTP_STATUS_OK) {
171 /* Call monitor callback */
172 if (session->monitor)
173 (*session->monitor)(session->client, session->conn,
174 SILC_CLIENT_FILE_MONITOR_ERROR,
175 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
176 SILC_CLIENT_FILE_NO_SUCH_FILE :
177 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
178 SILC_CLIENT_FILE_PERMISSION_DENIED :
179 SILC_CLIENT_FILE_ERROR), 0, 0,
180 session->client_entry, session->session_id,
181 session->filepath, session->monitor_context);
185 /* Open the actual local file */
186 memset(path, 0, sizeof(path));
187 silc_snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
188 session->path : "", session->filepath);
189 session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
190 if (session->fd < 0) {
191 /* Call monitor callback */
192 session->conn->context_type = SILC_ID_CLIENT;
193 session->conn->client_entry = session->client_entry;
194 session->client->internal->ops->say(session->client, session->conn,
195 SILC_CLIENT_MESSAGE_ERROR,
196 "File `%s' open failed: %s",
199 session->conn->context_type = SILC_ID_NONE;
201 if (session->monitor)
202 (*session->monitor)(session->client, session->conn,
203 SILC_CLIENT_FILE_MONITOR_ERROR,
204 SILC_CLIENT_FILE_PERMISSION_DENIED, 0, 0,
205 session->client_entry, session->session_id,
206 session->filepath, session->monitor_context);
210 session->read_handle = handle;
212 /* Now, start reading the file */
213 silc_sftp_read(sftp, session->read_handle, session->read_offset,
214 SILC_PACKET_MAX_LEN - 1024,
215 silc_client_ftp_data, session);
217 /* Call monitor callback */
218 if (session->monitor)
219 (*session->monitor)(session->client, session->conn,
220 SILC_CLIENT_FILE_MONITOR_RECEIVE,
222 session->read_offset, session->filesize,
223 session->client_entry, session->session_id,
224 session->filepath, session->monitor_context);
227 /* Ask filename completion callback. Delivers the filepath selected by
230 static void silc_client_ftp_ask_name(const char *filepath,
233 SilcClientFtpSession session = (SilcClientFtpSession)context;
234 SilcSFTPAttributesStruct attr;
235 char *remote_file = NULL;
237 SILC_LOG_DEBUG(("Start"));
240 remote_file = session->filepath;
241 session->filepath = NULL;
242 silc_free(session->path);
243 session->path = NULL;
244 session->filepath = strdup(filepath);
246 remote_file = strdup(session->filepath);
249 /* Now open the file */
250 memset(&attr, 0, sizeof(attr));
251 silc_sftp_open(session->sftp, remote_file, SILC_SFTP_FXF_READ, &attr,
252 silc_client_ftp_open_handle, session);
254 /* Close the directory handle */
255 silc_sftp_close(session->sftp, session->dir_handle, NULL, NULL);
256 session->dir_handle = NULL;
258 silc_free(remote_file);
261 /* Returns the file name available for download. This is the downloader's
264 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
265 SilcSFTPStatus status,
266 const SilcSFTPName name,
269 SilcClientFtpSession session = (SilcClientFtpSession)context;
271 SILC_LOG_DEBUG(("Start"));
273 if (status != SILC_SFTP_STATUS_OK) {
274 /* Call monitor callback */
275 if (session->monitor)
276 (*session->monitor)(session->client, session->conn,
277 SILC_CLIENT_FILE_MONITOR_ERROR,
278 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
279 SILC_CLIENT_FILE_NO_SUCH_FILE :
280 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
281 SILC_CLIENT_FILE_PERMISSION_DENIED :
282 SILC_CLIENT_FILE_ERROR), 0, 0,
283 session->client_entry, session->session_id,
284 session->filepath, session->monitor_context);
288 /* Save the important attributes like filename and file size */
289 session->filepath = strdup(name->filename[0]);
290 session->filesize = name->attrs[0]->size;
292 /* If the path was not provided, ask from application where to save the
294 if (!session->path && session->ask_name) {
295 session->ask_name(session->client, session->conn, session->session_id,
296 name->filename[0], silc_client_ftp_ask_name, session,
297 session->ask_name_context);
301 /* Start downloading immediately to current directory. */
302 silc_client_ftp_ask_name(NULL, session);
305 /* Returns the file handle after giving opendir command. This is the
306 downloader's function. */
308 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
309 SilcSFTPStatus status,
310 SilcSFTPHandle handle,
313 SilcClientFtpSession session = (SilcClientFtpSession)context;
315 SILC_LOG_DEBUG(("Start"));
317 if (status != SILC_SFTP_STATUS_OK) {
318 /* Call monitor callback */
319 if (session->monitor)
320 (*session->monitor)(session->client, session->conn,
321 SILC_CLIENT_FILE_MONITOR_ERROR,
322 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
323 SILC_CLIENT_FILE_NO_SUCH_FILE :
324 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
325 SILC_CLIENT_FILE_PERMISSION_DENIED :
326 SILC_CLIENT_FILE_ERROR), 0, 0,
327 session->client_entry, session->session_id,
328 session->filepath, session->monitor_context);
332 /* Now, read the directory */
333 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
334 session->dir_handle = handle;
337 /* SFTP version callback for SFTP client. This is the downloader's function
338 after initializing the SFTP connection to the remote client. This will
339 find out the filename available for download. */
341 static void silc_client_ftp_version(SilcSFTP sftp,
342 SilcSFTPStatus status,
343 SilcSFTPVersion version,
346 SilcClientFtpSession session = (SilcClientFtpSession)context;
348 SILC_LOG_DEBUG(("Start"));
350 if (status != SILC_SFTP_STATUS_OK) {
351 /* Call monitor callback */
352 if (session->monitor)
353 (*session->monitor)(session->client, session->conn,
354 SILC_CLIENT_FILE_MONITOR_ERROR,
355 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
356 SILC_CLIENT_FILE_NO_SUCH_FILE :
357 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
358 SILC_CLIENT_FILE_PERMISSION_DENIED :
359 SILC_CLIENT_FILE_ERROR), 0, 0,
360 session->client_entry, session->session_id,
361 session->filepath, session->monitor_context);
365 /* The SFTP session is open, now retrieve the info about available file. */
366 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
369 /* SFTP stream error callback */
371 static void silc_client_ftp_error(SilcSFTP sftp, SilcSFTPStatus status,
377 /************************ Static utility functions **************************/
379 /* Free session resources. Connection must be closed before getting
382 static void silc_client_ftp_session_free(SilcClientFtpSession session)
384 SILC_LOG_DEBUG(("Free session %d", session->session_id));
386 silc_schedule_task_del_by_context(session->client->schedule, session);
388 silc_dlist_del(session->client->internal->ftp_sessions, session);
390 /* Abort connecting */
392 silc_async_abort(session->op, NULL, NULL);
396 if (session->initiator)
397 silc_sftp_server_shutdown(session->sftp);
399 silc_sftp_client_shutdown(session->sftp);
402 silc_sftp_fs_memory_free(session->fs);
404 /* Destroy listener */
405 if (session->listener)
406 silc_client_listener_free(session->listener);
408 /* Destroy wrapped stream */
410 silc_stream_destroy(session->stream);
412 silc_client_unref_client(session->client, session->server_conn,
413 session->client_entry);
414 silc_free(session->hostname);
415 silc_free(session->filepath);
416 silc_free(session->path);
420 /* File transfer session timeout */
422 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
424 SilcClientFtpSession session = context;
426 SILC_LOG_DEBUG(("Timeout"));
428 /* Close connection (destroyes the session context later). If it is
429 already closed, destroy the session now. */
431 silc_client_close_connection(session->client, session->conn);
432 session->conn = NULL;
434 /* Call monitor callback */
435 if (session->monitor)
436 (*session->monitor)(session->client, session->conn,
437 SILC_CLIENT_FILE_MONITOR_ERROR,
438 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
439 session->client_entry, session->session_id,
440 session->filepath, session->monitor_context);
442 silc_client_ftp_session_free(context);
446 /* File transfer session closing task callback */
448 SILC_TASK_CALLBACK(silc_client_file_close_final)
450 SilcClientFtpSession session = context;
452 /* Close connection (destroyes the session context later). If it is
453 already closed, destroy the session now. */
455 silc_client_close_connection(session->client, session->conn);
456 session->conn = NULL;
458 silc_client_ftp_session_free(context);
462 /* Client resolving callback. Continues with the FTP packet processing */
464 static void silc_client_ftp_client_resolved(SilcClient client,
465 SilcClientConnection conn,
470 SilcFSMThread thread = context;
471 SilcPacket packet = silc_fsm_get_state_context(thread);
473 /* If no client found, ignore the packet, a silent error */
475 silc_packet_free(packet);
476 silc_fsm_finish(thread);
480 /* Continue processing the packet */
481 SILC_FSM_CALL_CONTINUE(context);
484 /* FTP packet payload encoder/decoder. This is called for every FTP packet.
485 We add/remove FTP payload in this function, because SFTP library does not
489 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
490 SilcBuffer buffer, void *context)
492 /* Pull FTP type in the payload, revealing SFTP payload */
493 if (status == SILC_STREAM_CAN_READ) {
494 if (silc_buffer_len(buffer) >= 1)
495 silc_buffer_pull(buffer, 1);
499 /* Add FTP type before SFTP data */
500 if (status == SILC_STREAM_CAN_WRITE) {
501 if (silc_buffer_format(buffer,
511 /* FTP Connection callback. The SFTP session is started here. */
514 silc_client_ftp_connect_completion(SilcClient client,
515 SilcClientConnection conn,
516 SilcClientConnectionStatus status,
521 SilcClientFtpSession session = context;
523 session->conn = conn;
526 silc_schedule_task_del_by_context(client->schedule, session);
529 case SILC_CLIENT_CONN_SUCCESS:
530 SILC_LOG_DEBUG(("Connected, conn %p", conn));
532 /* Wrap the connection packet stream */
533 session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
535 silc_client_ftp_coder, session);
536 if (!session->stream) {
537 /* Call monitor callback */
538 if (session->monitor)
539 (*session->monitor)(session->client, session->conn,
540 SILC_CLIENT_FILE_MONITOR_ERROR,
541 SILC_CLIENT_FILE_ERROR, 0, 0,
542 session->client_entry, session->session_id,
543 session->filepath, session->monitor_context);
544 silc_client_close_connection(client, conn);
545 session->conn = NULL;
549 if (!session->initiator) {
550 /* If we are the SFTP client then start the SFTP session and retrieve
551 the info about the file available for download. */
552 session->sftp = silc_sftp_client_start(session->stream,
553 conn->internal->schedule,
554 silc_client_ftp_version,
555 silc_client_ftp_error, session);
557 /* Start SFTP server */
558 session->sftp = silc_sftp_server_start(session->stream,
559 conn->internal->schedule,
560 silc_client_ftp_error, session,
563 /* Monitor transmission */
564 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
565 silc_client_ftp_monitor, session);
570 case SILC_CLIENT_CONN_DISCONNECTED:
571 SILC_LOG_DEBUG(("Disconnected %p", conn));
573 /* Call monitor callback */
574 if (session->monitor)
575 (*session->monitor)(session->client, session->conn,
576 SILC_CLIENT_FILE_MONITOR_DISCONNECT,
577 SILC_CLIENT_FILE_ERROR, 0, 0,
578 session->client_entry, session->session_id,
579 session->filepath, session->monitor_context);
581 /* Connection already closed */
582 session->conn = NULL;
584 /* If closed by user, destroy the session now */
586 silc_client_ftp_session_free(session);
589 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
590 SILC_LOG_DEBUG(("Connecting timeout"));
592 /* Call monitor callback */
593 if (session->monitor)
594 (*session->monitor)(session->client, session->conn,
595 SILC_CLIENT_FILE_MONITOR_ERROR,
596 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
597 session->client_entry, session->session_id,
598 session->filepath, session->monitor_context);
600 /* Connection already closed */
601 session->conn = NULL;
605 SILC_LOG_DEBUG(("Connecting error %d", status));
607 /* Call monitor callback */
608 if (session->monitor)
609 (*session->monitor)(session->client, session->conn,
610 SILC_CLIENT_FILE_MONITOR_ERROR,
611 status != SILC_CLIENT_CONN_ERROR ?
612 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
613 SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
614 session->client_entry, session->session_id,
615 session->filepath, session->monitor_context);
617 /* Connection already closed */
618 session->conn = NULL;
623 /*************************** File Transfer API ******************************/
625 /* Free all file transfer sessions. */
627 void silc_client_ftp_free_sessions(SilcClient client)
629 SilcClientFtpSession session;
631 if (!client->internal->ftp_sessions)
634 silc_dlist_start(client->internal->ftp_sessions);
635 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
636 silc_client_ftp_session_free(session);
637 silc_dlist_del(client->internal->ftp_sessions, session);
640 /* Free file transfer session by client entry. */
642 void silc_client_ftp_session_free_client(SilcClient client,
643 SilcClientEntry client_entry)
645 SilcClientFtpSession session;
647 if (!client->internal->ftp_sessions)
650 /* Get the session */
651 silc_dlist_start(client->internal->ftp_sessions);
652 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
653 if (session->client_entry == client_entry)
654 silc_client_ftp_session_free(session);
657 /* Sends a file indicated by the `filepath' to the remote client
658 indicated by the `client_entry'. This will negotiate a secret key
659 with the remote client before actually starting the transmission of
660 the file. The `monitor' callback will be called to monitor the
661 transmission of the file. */
664 silc_client_file_send(SilcClient client,
665 SilcClientConnection conn,
666 SilcClientEntry client_entry,
667 SilcClientConnectionParams *params,
668 SilcPublicKey public_key,
669 SilcPrivateKey private_key,
670 SilcClientFileMonitor monitor,
671 void *monitor_context,
672 const char *filepath,
673 SilcUInt32 *session_id)
675 SilcClientFtpSession session;
677 char *filename, *path;
680 SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
682 if (!client || !client_entry || !filepath || !params ||
683 !public_key || !private_key)
684 return SILC_CLIENT_FILE_ERROR;
686 /* Check for existing session for `filepath'. */
687 silc_dlist_start(client->internal->ftp_sessions);
688 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
689 if (session->filepath && !strcmp(session->filepath, filepath) &&
690 session->client_entry == client_entry)
691 return SILC_CLIENT_FILE_ALREADY_STARTED;
694 /* See whether the file exists and can be opened */
695 fd = silc_file_open(filepath, O_RDONLY);
697 return SILC_CLIENT_FILE_NO_SUCH_FILE;
700 /* Add new session */
701 session = silc_calloc(1, sizeof(*session));
703 return SILC_CLIENT_FILE_ERROR;
704 session->session_id = ++client->internal->next_session_id;
705 session->client = client;
706 session->server_conn = conn;
707 session->initiator = TRUE;
708 session->client_entry = silc_client_ref_client(client, conn, client_entry);
709 session->monitor = monitor;
710 session->monitor_context = monitor_context;
711 session->filepath = strdup(filepath);
712 session->params = *params;
713 session->public_key = public_key;
714 session->private_key = private_key;
716 if (silc_asprintf(&path, "file://%s", filepath) < 0) {
718 return SILC_CLIENT_FILE_NO_MEMORY;
721 /* Allocate memory filesystem and put the file to it */
722 if (strrchr(path, '/'))
723 filename = strrchr(path, '/') + 1;
725 filename = (char *)path;
726 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
727 SILC_SFTP_FS_PERM_EXEC);
728 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
731 session->filesize = silc_file_size(filepath);
733 /* If local IP is provided, create listener for incoming key exchange */
734 if (params->local_ip || params->bind_ip) {
736 silc_client_listener_add(client,
737 conn->internal->schedule,
738 params, public_key, private_key,
739 silc_client_ftp_connect_completion,
741 if (!session->listener) {
742 conn->context_type = SILC_ID_CLIENT;
743 conn->client_entry = session->client_entry;
744 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
745 "Cannot create listener for file transfer: "
746 "%s", strerror(errno));
747 conn->context_type = SILC_ID_NONE;
749 return SILC_CLIENT_FILE_NO_MEMORY;
752 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
753 strdup(params->local_ip));
754 session->port = silc_client_listener_get_local_port(session->listener);
757 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
759 /* Send the key agreement inside FTP packet */
760 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
763 if (session->listener)
764 silc_client_listener_free(session->listener);
766 return SILC_CLIENT_FILE_NO_MEMORY;
768 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
769 SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
771 SILC_STR_DATA(silc_buffer_data(keyagr),
772 silc_buffer_len(keyagr)),
775 silc_buffer_free(keyagr);
778 silc_dlist_add(client->internal->ftp_sessions, session);
780 *session_id = session->session_id;
782 /* Add session request timeout */
783 if (params && params->timeout_secs)
784 silc_schedule_task_add_timeout(client->schedule,
785 silc_client_ftp_timeout, session,
786 params->timeout_secs, 0);
788 return SILC_CLIENT_FILE_OK;
791 /* Receives a file from a client indicated by the `client_entry'. The
792 `session_id' indicates the file transmission session and it has been
793 received in the `ftp' client operation function. This will actually
794 perform the key agreement protocol with the remote client before
795 actually starting the file transmission. The `monitor' callback
796 will be called to monitor the transmission. */
799 silc_client_file_receive(SilcClient client,
800 SilcClientConnection conn,
801 SilcClientConnectionParams *params,
802 SilcPublicKey public_key,
803 SilcPrivateKey private_key,
804 SilcClientFileMonitor monitor,
805 void *monitor_context,
807 SilcUInt32 session_id,
808 SilcClientFileAskName ask_name,
809 void *ask_name_context)
811 SilcClientFtpSession session;
814 if (!client || !conn)
815 return SILC_CLIENT_FILE_ERROR;
817 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
819 /* Get the session */
820 silc_dlist_start(client->internal->ftp_sessions);
821 while ((session = silc_dlist_get(client->internal->ftp_sessions))
823 if (session->session_id == session_id) {
828 if (session == SILC_LIST_END) {
829 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
830 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
833 /* See if we have this session running already */
834 if (session->sftp || session->listener) {
835 SILC_LOG_DEBUG(("Session already started"));
836 return SILC_CLIENT_FILE_ALREADY_STARTED;
839 session->monitor = monitor;
840 session->monitor_context = monitor_context;
841 session->ask_name = ask_name;
842 session->ask_name_context = ask_name_context;
843 session->path = path ? strdup(path) : NULL;
845 /* If the hostname and port already exists then the remote client did
846 provide the connection point to us and we won't create listener, but
847 create the connection ourselves. */
848 if (session->hostname && session->port) {
849 SILC_LOG_DEBUG(("Connecting to remote client"));
850 /* Connect to the remote client. Performs key exchange automatically. */
852 silc_client_connect_to_client(client, params, public_key, private_key,
853 session->hostname, session->port,
854 silc_client_ftp_connect_completion,
858 return SILC_CLIENT_FILE_ERROR;
861 /* Add the listener for the key agreement */
862 SILC_LOG_DEBUG(("Creating listener for file transfer"));
863 if (!params || (!params->local_ip && !params->bind_ip)) {
864 session->conn->context_type = SILC_ID_CLIENT;
865 session->conn->client_entry = session->client_entry;
866 session->client->internal->ops->say(session->client, session->conn,
867 SILC_CLIENT_MESSAGE_ERROR,
868 "Cannot create listener for file "
869 "transfer; IP address and/or port "
871 session->conn->context_type = SILC_ID_NONE;
873 return SILC_CLIENT_FILE_ERROR;
876 silc_client_listener_add(client, conn->internal->schedule, params,
877 public_key, private_key,
878 silc_client_ftp_connect_completion,
880 if (!session->listener) {
881 session->conn->context_type = SILC_ID_CLIENT;
882 session->conn->client_entry = session->client_entry;
883 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
884 "Cannot create listener for file transfer: "
885 "%s", strerror(errno));
886 session->conn->context_type = SILC_ID_NONE;
888 return SILC_CLIENT_FILE_NO_MEMORY;
890 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
891 strdup(params->local_ip));
892 session->port = silc_client_listener_get_local_port(session->listener);
894 /* Send the key agreement inside FTP packet */
895 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
896 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
899 silc_client_listener_free(session->listener);
901 return SILC_CLIENT_FILE_NO_MEMORY;
903 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
904 SILC_ID_CLIENT, &session->client_entry->id,
907 SILC_STR_DATA(silc_buffer_data(keyagr),
908 silc_buffer_len(keyagr)),
910 silc_buffer_free(keyagr);
912 /* Add session request timeout */
913 if (params && params->timeout_secs)
914 silc_schedule_task_add_timeout(client->schedule,
915 silc_client_ftp_timeout, session,
916 params->timeout_secs, 0);
919 return SILC_CLIENT_FILE_OK;
922 /* Closes file transmission session indicated by the `session_id'.
923 If file transmission is being conducted it will be aborted
924 automatically. This function is also used to close the session
925 after successful file transmission. This function can be used
926 also to reject incoming file transmission request. */
928 SilcClientFileError silc_client_file_close(SilcClient client,
929 SilcClientConnection conn,
930 SilcUInt32 session_id)
932 SilcClientFtpSession session;
934 if (!client || !conn)
935 return SILC_CLIENT_FILE_ERROR;
937 SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
939 /* Get the session */
940 silc_dlist_start(client->internal->ftp_sessions);
941 while ((session = silc_dlist_get(client->internal->ftp_sessions))
943 if (session->session_id == session_id)
947 if (session == SILC_LIST_END) {
948 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
949 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
952 if (session->monitor) {
953 (*session->monitor)(session->client, session->conn,
954 SILC_CLIENT_FILE_MONITOR_CLOSED,
955 SILC_CLIENT_FILE_OK, 0, 0,
956 session->client_entry, session->session_id,
957 session->filepath, session->monitor_context);
959 /* No more callbacks to application */
960 session->monitor = NULL;
963 silc_schedule_task_del_by_context(client->schedule, session);
965 session->closed = TRUE;
967 /* Destroy via timeout */
968 silc_schedule_task_add_timeout(conn->internal->schedule,
969 silc_client_file_close_final, session,
972 return SILC_CLIENT_FILE_OK;
975 /************************** FTP Request Processing **************************/
977 /* Received file transfer packet. Only file transfer requests get here.
978 The actual file transfer is handled by the SFTP library when we give it
979 the packet stream wrapped into SilcStream context. */
981 SILC_FSM_STATE(silc_client_ftp)
983 SilcClientConnection conn = fsm_context;
984 SilcClient client = conn->client;
985 SilcPacket packet = state_context;
986 SilcClientFtpSession session;
987 SilcClientID remote_id;
988 SilcClientEntry remote_client;
989 SilcKeyAgreementPayload payload = NULL;
993 SILC_LOG_DEBUG(("Process file transfer packet"));
995 if (silc_buffer_len(&packet->buffer) < 1)
998 /* We support file transfer type number 1 (== SFTP) */
999 if (packet->buffer.data[0] != 0x01) {
1000 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
1001 packet->buffer.data[0]));
1005 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
1006 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
1007 SILC_LOG_DEBUG(("Invalid client ID"));
1011 /* Check whether we know this client already */
1012 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1013 if (!remote_client || !remote_client->internal.valid) {
1014 /** Resolve client info */
1015 silc_client_unref_client(client, conn, remote_client);
1016 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1017 client, conn, &remote_id, NULL,
1018 silc_client_ftp_client_resolved,
1024 silc_dlist_start(client->internal->ftp_sessions);
1025 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1026 if (session->client_entry == remote_client &&
1027 (!session->initiator || !session->listener))
1031 /* Parse the key agreement payload */
1033 silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1034 silc_buffer_len(&packet->buffer) - 1);
1036 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1040 hostname = silc_key_agreement_get_hostname(payload);
1041 port = silc_key_agreement_get_port(payload);
1042 if (!hostname || !port) {
1047 /* If session doesn't exist, we create new one. If session exists, but
1048 we are responder it means that the remote sent another request and user
1049 hasn't even accepted the first one yet. We assume this session is new
1051 if (!session || !hostname || !session->initiator) {
1052 /* New file transfer session */
1053 SILC_LOG_DEBUG(("New file transfer session %d",
1054 client->internal->next_session_id + 1));
1056 session = silc_calloc(1, sizeof(*session));
1059 session->session_id = ++client->internal->next_session_id;
1060 session->client = client;
1061 session->server_conn = conn;
1062 session->client_entry = silc_client_ref_client(client, conn,
1064 if (hostname && port) {
1065 session->hostname = strdup(hostname);
1066 session->port = port;
1068 silc_dlist_add(client->internal->ftp_sessions, session);
1070 /* Notify application of incoming FTP request */
1071 client->internal->ops->ftp(client, conn, remote_client,
1072 session->session_id, hostname, port);
1076 /* Session exists, continue with key agreement protocol. */
1077 SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1078 session->session_id));
1080 session->hostname = strdup(hostname);
1081 session->port = port;
1083 /* Connect to the remote client. Performs key exchange automatically. */
1085 silc_client_connect_to_client(client, &session->params,
1086 session->public_key, session->private_key,
1087 session->hostname, session->port,
1088 silc_client_ftp_connect_completion,
1091 /* Call monitor callback */
1092 if (session->monitor)
1093 (*session->monitor)(session->client, session->conn,
1094 SILC_CLIENT_FILE_MONITOR_ERROR,
1095 SILC_CLIENT_FILE_ERROR, 0, 0,
1096 session->client_entry, session->session_id,
1097 session->filepath, session->monitor_context);
1102 silc_key_agreement_payload_free(payload);
1103 silc_packet_free(packet);
1104 return SILC_FSM_FINISH;