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 silc_buffer_push(buffer, 1);
502 if (silc_buffer_format(buffer,
512 /* FTP Connection callback. The SFTP session is started here. */
515 silc_client_ftp_connect_completion(SilcClient client,
516 SilcClientConnection conn,
517 SilcClientConnectionStatus status,
522 SilcClientFtpSession session = context;
524 session->conn = conn;
527 silc_schedule_task_del_by_context(client->schedule, session);
530 case SILC_CLIENT_CONN_SUCCESS:
531 SILC_LOG_DEBUG(("Connected, conn %p", conn));
533 /* Wrap the connection packet stream */
534 session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
535 0, FALSE, 0, NULL, 0, NULL,
536 silc_client_ftp_coder, session);
537 if (!session->stream) {
538 /* Call monitor callback */
539 if (session->monitor)
540 (*session->monitor)(session->client, session->conn,
541 SILC_CLIENT_FILE_MONITOR_ERROR,
542 SILC_CLIENT_FILE_ERROR, 0, 0,
543 session->client_entry, session->session_id,
544 session->filepath, session->monitor_context);
545 silc_client_close_connection(client, conn);
546 session->conn = NULL;
550 if (!session->initiator) {
551 /* If we are the SFTP client then start the SFTP session and retrieve
552 the info about the file available for download. */
553 session->sftp = silc_sftp_client_start(session->stream,
554 conn->internal->schedule,
555 silc_client_ftp_version,
556 silc_client_ftp_error, session);
558 /* Start SFTP server */
559 session->sftp = silc_sftp_server_start(session->stream,
560 conn->internal->schedule,
561 silc_client_ftp_error, session,
564 /* Monitor transmission */
565 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
566 silc_client_ftp_monitor, session);
571 case SILC_CLIENT_CONN_DISCONNECTED:
572 SILC_LOG_DEBUG(("Disconnected %p", conn));
574 /* Call monitor callback */
575 if (session->monitor)
576 (*session->monitor)(session->client, session->conn,
577 SILC_CLIENT_FILE_MONITOR_DISCONNECT,
578 SILC_CLIENT_FILE_ERROR, 0, 0,
579 session->client_entry, session->session_id,
580 session->filepath, session->monitor_context);
582 /* Connection already closed */
583 session->conn = NULL;
585 /* If closed by user, destroy the session now */
587 silc_client_ftp_session_free(session);
590 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
591 SILC_LOG_DEBUG(("Connecting timeout"));
593 /* Call monitor callback */
594 if (session->monitor)
595 (*session->monitor)(session->client, session->conn,
596 SILC_CLIENT_FILE_MONITOR_ERROR,
597 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
598 session->client_entry, session->session_id,
599 session->filepath, session->monitor_context);
601 /* Connection already closed */
602 session->conn = NULL;
606 SILC_LOG_DEBUG(("Connecting error %d", status));
608 /* Call monitor callback */
609 if (session->monitor)
610 (*session->monitor)(session->client, session->conn,
611 SILC_CLIENT_FILE_MONITOR_ERROR,
612 status != SILC_CLIENT_CONN_ERROR ?
613 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
614 SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
615 session->client_entry, session->session_id,
616 session->filepath, session->monitor_context);
618 /* Connection already closed */
619 session->conn = NULL;
624 /*************************** File Transfer API ******************************/
626 /* Free all file transfer sessions. */
628 void silc_client_ftp_free_sessions(SilcClient client)
630 SilcClientFtpSession session;
632 if (!client->internal->ftp_sessions)
635 silc_dlist_start(client->internal->ftp_sessions);
636 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
637 silc_client_ftp_session_free(session);
638 silc_dlist_del(client->internal->ftp_sessions, session);
641 /* Free file transfer session by client entry. */
643 void silc_client_ftp_session_free_client(SilcClient client,
644 SilcClientEntry client_entry)
646 SilcClientFtpSession session;
648 if (!client->internal->ftp_sessions)
651 /* Get the session */
652 silc_dlist_start(client->internal->ftp_sessions);
653 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
654 if (session->client_entry == client_entry)
655 silc_client_ftp_session_free(session);
658 /* Sends a file indicated by the `filepath' to the remote client
659 indicated by the `client_entry'. This will negotiate a secret key
660 with the remote client before actually starting the transmission of
661 the file. The `monitor' callback will be called to monitor the
662 transmission of the file. */
665 silc_client_file_send(SilcClient client,
666 SilcClientConnection conn,
667 SilcClientEntry client_entry,
668 SilcClientConnectionParams *params,
669 SilcPublicKey public_key,
670 SilcPrivateKey private_key,
671 SilcClientFileMonitor monitor,
672 void *monitor_context,
673 const char *filepath,
674 SilcUInt32 *session_id)
676 SilcClientFtpSession session;
678 char *filename, *path;
681 SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
683 if (!client || !client_entry || !filepath || !params ||
684 !public_key || !private_key)
685 return SILC_CLIENT_FILE_ERROR;
687 /* Check for existing session for `filepath'. */
688 silc_dlist_start(client->internal->ftp_sessions);
689 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
690 if (session->filepath && !strcmp(session->filepath, filepath) &&
691 session->client_entry == client_entry)
692 return SILC_CLIENT_FILE_ALREADY_STARTED;
695 /* See whether the file exists and can be opened */
696 fd = silc_file_open(filepath, O_RDONLY);
698 return SILC_CLIENT_FILE_NO_SUCH_FILE;
701 /* Add new session */
702 session = silc_calloc(1, sizeof(*session));
704 return SILC_CLIENT_FILE_ERROR;
705 session->session_id = ++client->internal->next_session_id;
706 session->client = client;
707 session->server_conn = conn;
708 session->initiator = TRUE;
709 session->client_entry = silc_client_ref_client(client, conn, client_entry);
710 session->monitor = monitor;
711 session->monitor_context = monitor_context;
712 session->filepath = strdup(filepath);
713 session->params = *params;
714 session->public_key = public_key;
715 session->private_key = private_key;
717 if (silc_asprintf(&path, "file://%s", filepath) < 0) {
719 return SILC_CLIENT_FILE_NO_MEMORY;
722 /* Allocate memory filesystem and put the file to it */
723 if (strrchr(path, '/'))
724 filename = strrchr(path, '/') + 1;
726 filename = (char *)path;
727 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
728 SILC_SFTP_FS_PERM_EXEC);
729 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
732 session->filesize = silc_file_size(filepath);
734 /* If local IP is provided, create listener for incoming key exchange */
735 if (params->local_ip || params->bind_ip) {
737 silc_client_listener_add(client,
738 conn->internal->schedule,
739 params, public_key, private_key,
740 silc_client_ftp_connect_completion,
742 if (!session->listener) {
743 conn->context_type = SILC_ID_CLIENT;
744 conn->client_entry = session->client_entry;
745 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
746 "Cannot create listener for file transfer: "
747 "%s", strerror(errno));
748 conn->context_type = SILC_ID_NONE;
750 return SILC_CLIENT_FILE_NO_MEMORY;
753 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
754 strdup(params->local_ip));
755 session->port = silc_client_listener_get_local_port(session->listener);
758 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
760 /* Send the key agreement inside FTP packet */
761 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
764 if (session->listener)
765 silc_client_listener_free(session->listener);
767 return SILC_CLIENT_FILE_NO_MEMORY;
769 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
770 SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
772 SILC_STR_DATA(silc_buffer_data(keyagr),
773 silc_buffer_len(keyagr)),
776 silc_buffer_free(keyagr);
779 silc_dlist_add(client->internal->ftp_sessions, session);
781 *session_id = session->session_id;
783 /* Add session request timeout */
784 if (params && params->timeout_secs)
785 silc_schedule_task_add_timeout(client->schedule,
786 silc_client_ftp_timeout, session,
787 params->timeout_secs, 0);
789 return SILC_CLIENT_FILE_OK;
792 /* Receives a file from a client indicated by the `client_entry'. The
793 `session_id' indicates the file transmission session and it has been
794 received in the `ftp' client operation function. This will actually
795 perform the key agreement protocol with the remote client before
796 actually starting the file transmission. The `monitor' callback
797 will be called to monitor the transmission. */
800 silc_client_file_receive(SilcClient client,
801 SilcClientConnection conn,
802 SilcClientConnectionParams *params,
803 SilcPublicKey public_key,
804 SilcPrivateKey private_key,
805 SilcClientFileMonitor monitor,
806 void *monitor_context,
808 SilcUInt32 session_id,
809 SilcClientFileAskName ask_name,
810 void *ask_name_context)
812 SilcClientFtpSession session;
815 if (!client || !conn)
816 return SILC_CLIENT_FILE_ERROR;
818 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
820 /* Get the session */
821 silc_dlist_start(client->internal->ftp_sessions);
822 while ((session = silc_dlist_get(client->internal->ftp_sessions))
824 if (session->session_id == session_id) {
829 if (session == SILC_LIST_END) {
830 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
831 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
834 /* See if we have this session running already */
835 if (session->sftp || session->listener) {
836 SILC_LOG_DEBUG(("Session already started"));
837 return SILC_CLIENT_FILE_ALREADY_STARTED;
840 session->monitor = monitor;
841 session->monitor_context = monitor_context;
842 session->ask_name = ask_name;
843 session->ask_name_context = ask_name_context;
844 session->path = path ? strdup(path) : NULL;
846 /* If the hostname and port already exists then the remote client did
847 provide the connection point to us and we won't create listener, but
848 create the connection ourselves. */
849 if (session->hostname && session->port) {
850 SILC_LOG_DEBUG(("Connecting to remote client"));
851 /* Connect to the remote client. Performs key exchange automatically. */
853 silc_client_connect_to_client(client, params, public_key, private_key,
854 session->hostname, session->port,
855 silc_client_ftp_connect_completion,
859 return SILC_CLIENT_FILE_ERROR;
862 /* Add the listener for the key agreement */
863 SILC_LOG_DEBUG(("Creating listener for file transfer"));
864 if (!params || (!params->local_ip && !params->bind_ip)) {
865 session->conn->context_type = SILC_ID_CLIENT;
866 session->conn->client_entry = session->client_entry;
867 session->client->internal->ops->say(session->client, session->conn,
868 SILC_CLIENT_MESSAGE_ERROR,
869 "Cannot create listener for file "
870 "transfer; IP address and/or port "
872 session->conn->context_type = SILC_ID_NONE;
874 return SILC_CLIENT_FILE_ERROR;
877 silc_client_listener_add(client, conn->internal->schedule, params,
878 public_key, private_key,
879 silc_client_ftp_connect_completion,
881 if (!session->listener) {
882 session->conn->context_type = SILC_ID_CLIENT;
883 session->conn->client_entry = session->client_entry;
884 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
885 "Cannot create listener for file transfer: "
886 "%s", strerror(errno));
887 session->conn->context_type = SILC_ID_NONE;
889 return SILC_CLIENT_FILE_NO_MEMORY;
891 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
892 strdup(params->local_ip));
893 session->port = silc_client_listener_get_local_port(session->listener);
895 /* Send the key agreement inside FTP packet */
896 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
897 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
900 silc_client_listener_free(session->listener);
902 return SILC_CLIENT_FILE_NO_MEMORY;
904 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
905 SILC_ID_CLIENT, &session->client_entry->id,
908 SILC_STR_DATA(silc_buffer_data(keyagr),
909 silc_buffer_len(keyagr)),
911 silc_buffer_free(keyagr);
913 /* Add session request timeout */
914 if (params && params->timeout_secs)
915 silc_schedule_task_add_timeout(client->schedule,
916 silc_client_ftp_timeout, session,
917 params->timeout_secs, 0);
920 return SILC_CLIENT_FILE_OK;
923 /* Closes file transmission session indicated by the `session_id'.
924 If file transmission is being conducted it will be aborted
925 automatically. This function is also used to close the session
926 after successful file transmission. This function can be used
927 also to reject incoming file transmission request. */
929 SilcClientFileError silc_client_file_close(SilcClient client,
930 SilcClientConnection conn,
931 SilcUInt32 session_id)
933 SilcClientFtpSession session;
935 if (!client || !conn)
936 return SILC_CLIENT_FILE_ERROR;
938 SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
940 /* Get the session */
941 silc_dlist_start(client->internal->ftp_sessions);
942 while ((session = silc_dlist_get(client->internal->ftp_sessions))
944 if (session->session_id == session_id)
948 if (session == SILC_LIST_END) {
949 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
950 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
953 if (session->monitor) {
954 (*session->monitor)(session->client, session->conn,
955 SILC_CLIENT_FILE_MONITOR_CLOSED,
956 SILC_CLIENT_FILE_OK, 0, 0,
957 session->client_entry, session->session_id,
958 session->filepath, session->monitor_context);
960 /* No more callbacks to application */
961 session->monitor = NULL;
964 silc_schedule_task_del_by_context(client->schedule, session);
966 session->closed = TRUE;
968 /* Destroy via timeout */
969 silc_schedule_task_add_timeout(conn->internal->schedule,
970 silc_client_file_close_final, session,
973 return SILC_CLIENT_FILE_OK;
976 /************************** FTP Request Processing **************************/
978 /* Received file transfer packet. Only file transfer requests get here.
979 The actual file transfer is handled by the SFTP library when we give it
980 the packet stream wrapped into SilcStream context. */
982 SILC_FSM_STATE(silc_client_ftp)
984 SilcClientConnection conn = fsm_context;
985 SilcClient client = conn->client;
986 SilcPacket packet = state_context;
987 SilcClientFtpSession session;
988 SilcClientID remote_id;
989 SilcClientEntry remote_client;
990 SilcKeyAgreementPayload payload = NULL;
994 SILC_LOG_DEBUG(("Process file transfer packet"));
996 if (silc_buffer_len(&packet->buffer) < 1)
999 /* We support file transfer type number 1 (== SFTP) */
1000 if (packet->buffer.data[0] != 0x01) {
1001 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
1002 packet->buffer.data[0]));
1006 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
1007 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
1008 SILC_LOG_DEBUG(("Invalid client ID"));
1012 /* Check whether we know this client already */
1013 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1014 if (!remote_client || !remote_client->internal.valid) {
1015 /** Resolve client info */
1016 silc_client_unref_client(client, conn, remote_client);
1017 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1018 client, conn, &remote_id, NULL,
1019 silc_client_ftp_client_resolved,
1025 silc_dlist_start(client->internal->ftp_sessions);
1026 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1027 if (session->client_entry == remote_client &&
1028 (!session->initiator || !session->listener))
1032 /* Parse the key agreement payload */
1034 silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1035 silc_buffer_len(&packet->buffer) - 1);
1037 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1041 hostname = silc_key_agreement_get_hostname(payload);
1042 port = silc_key_agreement_get_port(payload);
1043 if (!hostname || !port) {
1048 /* If session doesn't exist, we create new one. If session exists, but
1049 we are responder it means that the remote sent another request and user
1050 hasn't even accepted the first one yet. We assume this session is new
1052 if (!session || !hostname || !session->initiator) {
1053 /* New file transfer session */
1054 SILC_LOG_DEBUG(("New file transfer session %d",
1055 client->internal->next_session_id + 1));
1057 session = silc_calloc(1, sizeof(*session));
1060 session->session_id = ++client->internal->next_session_id;
1061 session->client = client;
1062 session->server_conn = conn;
1063 session->client_entry = silc_client_ref_client(client, conn,
1065 if (hostname && port) {
1066 session->hostname = strdup(hostname);
1067 session->port = port;
1069 silc_dlist_add(client->internal->ftp_sessions, session);
1071 /* Notify application of incoming FTP request */
1072 client->internal->ops->ftp(client, conn, remote_client,
1073 session->session_id, hostname, port);
1077 /* Session exists, continue with key agreement protocol. */
1078 SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1079 session->session_id));
1081 session->hostname = strdup(hostname);
1082 session->port = port;
1084 /* Connect to the remote client. Performs key exchange automatically. */
1086 silc_client_connect_to_client(client, &session->params,
1087 session->public_key, session->private_key,
1088 session->hostname, session->port,
1089 silc_client_ftp_connect_completion,
1092 /* Call monitor callback */
1093 if (session->monitor)
1094 (*session->monitor)(session->client, session->conn,
1095 SILC_CLIENT_FILE_MONITOR_ERROR,
1096 SILC_CLIENT_FILE_ERROR, 0, 0,
1097 session->client_entry, session->session_id,
1098 session->filepath, session->monitor_context);
1103 silc_key_agreement_payload_free(payload);
1104 silc_packet_free(packet);
1105 return SILC_FSM_FINISH;