5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2008 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcclient.h"
22 #include "client_internal.h"
24 /************************** Types and definitions ***************************/
26 /* File transmission session */
27 struct SilcClientFtpSessionStruct {
28 SilcClient client; /* Client */
29 SilcClientConnection server_conn; /* Connection to server */
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 real 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 real 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",
195 silc_errno_string(silc_errno));
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_schedule_task_del_by_context(session->client->schedule, session);
384 silc_dlist_del(session->client->internal->ftp_sessions, session);
386 /* Abort connecting */
388 silc_async_abort(session->op, NULL, NULL);
392 if (session->initiator)
393 silc_sftp_server_shutdown(session->sftp);
395 silc_sftp_client_shutdown(session->sftp);
398 silc_sftp_fs_memory_free(session->fs);
400 /* Destroy listener */
401 if (session->listener)
402 silc_client_listener_free(session->listener);
404 /* Destroy wrapped stream */
406 silc_stream_destroy(session->stream);
408 silc_client_unref_client(session->client, session->server_conn,
409 session->client_entry);
410 silc_free(session->hostname);
411 silc_free(session->filepath);
412 silc_free(session->path);
416 /* File transfer session timeout */
418 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
420 SilcClientFtpSession session = context;
422 SILC_LOG_DEBUG(("Timeout"));
424 /* Close connection (destroyes the session context later). If it is
425 already closed, destroy the session now. */
427 silc_client_close_connection(session->client, session->conn);
428 session->conn = NULL;
430 /* Call monitor callback */
431 if (session->monitor)
432 (*session->monitor)(session->client, session->conn,
433 SILC_CLIENT_FILE_MONITOR_ERROR,
434 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
435 session->client_entry, session->session_id,
436 session->filepath, session->monitor_context);
438 silc_client_ftp_session_free(context);
442 /* File transfer session closing task callback */
444 SILC_TASK_CALLBACK(silc_client_file_close_final)
446 SilcClientFtpSession session = context;
448 /* Close connection (destroyes the session context later). If it is
449 already closed, destroy the session now. */
451 silc_client_close_connection(session->client, session->conn);
452 session->conn = NULL;
454 silc_client_ftp_session_free(context);
458 /* Client resolving callback. Continues with the FTP packet processing */
460 static void silc_client_ftp_client_resolved(SilcClient client,
461 SilcClientConnection conn,
466 SilcFSMThread thread = context;
467 SilcPacket packet = silc_fsm_get_state_context(thread);
469 /* If no client found, ignore the packet, a silent error */
471 silc_packet_free(packet);
472 silc_fsm_finish(thread);
476 /* Continue processing the packet */
477 SILC_FSM_CALL_CONTINUE(context);
480 /* FTP packet payload encoder/decoder. This is called for every FTP packet.
481 We add/remove FTP payload in this function, because SFTP library does not
485 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
486 SilcBuffer buffer, void *context)
488 /* Pull FTP type in the payload, revealing SFTP payload */
489 if (status == SILC_STREAM_CAN_READ) {
490 if (silc_buffer_len(buffer) >= 1)
491 silc_buffer_pull(buffer, 1);
495 /* Add FTP type before SFTP data */
496 if (status == SILC_STREAM_CAN_WRITE) {
497 if (silc_buffer_format(buffer,
507 /* FTP Connection callback. The SFTP session is started here. */
510 silc_client_ftp_connect_completion(SilcClient client,
511 SilcClientConnection conn,
512 SilcClientConnectionStatus status,
517 SilcClientFtpSession session = context;
519 session->conn = conn;
522 silc_schedule_task_del_by_context(client->schedule, session);
525 case SILC_CLIENT_CONN_SUCCESS:
526 SILC_LOG_DEBUG(("Connected, conn %p", conn));
528 /* Wrap the connection packet stream */
529 session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
531 silc_client_ftp_coder, session);
532 if (!session->stream) {
533 /* Call monitor callback */
534 if (session->monitor)
535 (*session->monitor)(session->client, session->conn,
536 SILC_CLIENT_FILE_MONITOR_ERROR,
537 SILC_CLIENT_FILE_ERROR, 0, 0,
538 session->client_entry, session->session_id,
539 session->filepath, session->monitor_context);
540 silc_client_close_connection(client, conn);
541 session->conn = NULL;
545 if (!session->initiator) {
546 /* If we are the SFTP client then start the SFTP session and retrieve
547 the info about the file available for download. */
548 session->sftp = silc_sftp_client_start(session->stream,
549 conn->internal->schedule,
550 silc_client_ftp_version,
551 silc_client_ftp_error, session);
553 /* Start SFTP server */
554 session->sftp = silc_sftp_server_start(session->stream,
555 conn->internal->schedule,
556 silc_client_ftp_error, session,
559 /* Monitor transmission */
560 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
561 silc_client_ftp_monitor, session);
566 case SILC_CLIENT_CONN_DISCONNECTED:
567 SILC_LOG_DEBUG(("Disconnected %p", conn));
569 /* Call monitor callback */
570 if (session->monitor)
571 (*session->monitor)(session->client, session->conn,
572 SILC_CLIENT_FILE_MONITOR_DISCONNECT,
573 SILC_CLIENT_FILE_ERROR, 0, 0,
574 session->client_entry, session->session_id,
575 session->filepath, session->monitor_context);
577 /* Connection already closed */
578 session->conn = NULL;
580 /* If closed by user, destroy the session now */
582 silc_client_ftp_session_free(session);
585 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
586 SILC_LOG_DEBUG(("Connecting timeout"));
588 /* Call monitor callback */
589 if (session->monitor)
590 (*session->monitor)(session->client, session->conn,
591 SILC_CLIENT_FILE_MONITOR_ERROR,
592 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
593 session->client_entry, session->session_id,
594 session->filepath, session->monitor_context);
596 /* Connection already closed */
597 session->conn = NULL;
601 SILC_LOG_DEBUG(("Connecting error %d", status));
603 /* Call monitor callback */
604 if (session->monitor)
605 (*session->monitor)(session->client, session->conn,
606 SILC_CLIENT_FILE_MONITOR_ERROR,
607 status != SILC_CLIENT_CONN_ERROR ?
608 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
609 SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
610 session->client_entry, session->session_id,
611 session->filepath, session->monitor_context);
613 /* Connection already closed */
614 session->conn = NULL;
619 /*************************** File Transfer API ******************************/
621 /* Free all file transfer sessions. */
623 void silc_client_ftp_free_sessions(SilcClient client)
625 SilcClientFtpSession session;
627 if (!client->internal->ftp_sessions)
630 silc_dlist_start(client->internal->ftp_sessions);
631 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
632 silc_client_ftp_session_free(session);
633 silc_dlist_del(client->internal->ftp_sessions, session);
636 /* Free file transfer session by client entry. */
638 void silc_client_ftp_session_free_client(SilcClient client,
639 SilcClientEntry client_entry)
641 SilcClientFtpSession session;
643 if (!client->internal->ftp_sessions)
646 /* Get the session */
647 silc_dlist_start(client->internal->ftp_sessions);
648 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
649 if (session->client_entry == client_entry)
650 silc_client_ftp_session_free(session);
653 /* Sends a file indicated by the `filepath' to the remote client
654 indicated by the `client_entry'. This will negotiate a secret key
655 with the remote client before actually starting the transmission of
656 the file. The `monitor' callback will be called to monitor the
657 transmission of the file. */
660 silc_client_file_send(SilcClient client,
661 SilcClientConnection conn,
662 SilcClientEntry client_entry,
663 SilcClientConnectionParams *params,
664 SilcPublicKey public_key,
665 SilcPrivateKey private_key,
666 SilcClientFileMonitor monitor,
667 void *monitor_context,
668 const char *filepath,
669 SilcUInt32 *session_id)
671 SilcClientFtpSession session;
673 char *filename, *path;
676 SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
678 if (!client || !client_entry || !filepath || !params ||
679 !public_key || !private_key)
680 return SILC_CLIENT_FILE_ERROR;
682 /* Check for existing session for `filepath'. */
683 silc_dlist_start(client->internal->ftp_sessions);
684 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
685 if (session->filepath && !strcmp(session->filepath, filepath) &&
686 session->client_entry == client_entry)
687 return SILC_CLIENT_FILE_ALREADY_STARTED;
690 /* See whether the file exists and can be opened */
691 fd = silc_file_open(filepath, O_RDONLY);
693 return SILC_CLIENT_FILE_NO_SUCH_FILE;
696 /* Add new session */
697 session = silc_calloc(1, sizeof(*session));
699 return SILC_CLIENT_FILE_ERROR;
700 session->session_id = ++client->internal->next_session_id;
701 session->client = client;
702 session->server_conn = conn;
703 session->initiator = TRUE;
704 session->client_entry = silc_client_ref_client(client, conn, client_entry);
705 session->monitor = monitor;
706 session->monitor_context = monitor_context;
707 session->filepath = strdup(filepath);
708 session->params = *params;
709 session->public_key = public_key;
710 session->private_key = private_key;
712 if (silc_asprintf(&path, "file://%s", filepath) < 0) {
714 return SILC_CLIENT_FILE_NO_MEMORY;
717 /* Allocate memory filesystem and put the file to it */
718 if (strrchr(path, '/'))
719 filename = strrchr(path, '/') + 1;
721 filename = (char *)path;
722 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
723 SILC_SFTP_FS_PERM_EXEC);
724 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
727 session->filesize = silc_file_size(filepath);
729 /* If local IP is provided, create listener for incoming key exchange */
730 if (params->local_ip || params->bind_ip) {
732 silc_client_listener_add(client,
733 conn->internal->schedule,
734 params, public_key, private_key,
735 silc_client_ftp_connect_completion,
737 if (!session->listener) {
738 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
739 "Cannot create listener for file transfer: "
740 "%s", silc_errno_string(silc_errno));
743 return SILC_CLIENT_FILE_NO_MEMORY;
746 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
747 strdup(params->local_ip));
748 session->port = silc_client_listener_get_local_port(session->listener);
751 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
753 /* Send the key agreement inside FTP packet */
754 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
757 if (session->listener)
758 silc_client_listener_free(session->listener);
760 return SILC_CLIENT_FILE_NO_MEMORY;
762 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
763 SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
765 SILC_STR_DATA(silc_buffer_data(keyagr),
766 silc_buffer_len(keyagr)),
769 silc_buffer_free(keyagr);
772 silc_dlist_add(client->internal->ftp_sessions, session);
774 *session_id = session->session_id;
776 /* Add session request timeout */
777 if (params && params->timeout_secs)
778 silc_schedule_task_add_timeout(client->schedule,
779 silc_client_ftp_timeout, session,
780 params->timeout_secs, 0);
782 return SILC_CLIENT_FILE_OK;
785 /* Receives a file from a client indicated by the `client_entry'. The
786 `session_id' indicates the file transmission session and it has been
787 received in the `ftp' client operation function. This will actually
788 perform the key agreement protocol with the remote client before
789 actually starting the file transmission. The `monitor' callback
790 will be called to monitor the transmission. */
793 silc_client_file_receive(SilcClient client,
794 SilcClientConnection conn,
795 SilcClientConnectionParams *params,
796 SilcPublicKey public_key,
797 SilcPrivateKey private_key,
798 SilcClientFileMonitor monitor,
799 void *monitor_context,
801 SilcUInt32 session_id,
802 SilcClientFileAskName ask_name,
803 void *ask_name_context)
805 SilcClientFtpSession session;
808 if (!client || !conn)
809 return SILC_CLIENT_FILE_ERROR;
811 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
813 /* Get the session */
814 silc_dlist_start(client->internal->ftp_sessions);
815 while ((session = silc_dlist_get(client->internal->ftp_sessions))
817 if (session->session_id == session_id) {
822 if (session == SILC_LIST_END) {
823 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
824 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
827 /* See if we have this session running already */
828 if (session->sftp || session->listener) {
829 SILC_LOG_DEBUG(("Session already started"));
830 return SILC_CLIENT_FILE_ALREADY_STARTED;
833 session->monitor = monitor;
834 session->monitor_context = monitor_context;
835 session->ask_name = ask_name;
836 session->ask_name_context = ask_name_context;
837 session->path = path ? strdup(path) : NULL;
839 /* If the hostname and port already exists then the remote client did
840 provide the connection point to us and we won't create listener, but
841 create the connection ourselves. */
842 if (session->hostname && session->port) {
843 SILC_LOG_DEBUG(("Connecting to remote client"));
844 /* Connect to the remote client. Performs key exchange automatically. */
846 silc_client_connect_to_client(client, params, public_key, private_key,
847 session->hostname, session->port,
848 silc_client_ftp_connect_completion,
852 return SILC_CLIENT_FILE_ERROR;
855 /* Add the listener for the key agreement */
856 SILC_LOG_DEBUG(("Creating listener for file transfer"));
857 if (!params || (!params->local_ip && !params->bind_ip)) {
858 session->client->internal->ops->say(session->client, session->conn,
859 SILC_CLIENT_MESSAGE_ERROR,
860 "Cannot create listener for file "
861 "transfer; IP address and/or port "
864 return SILC_CLIENT_FILE_ERROR;
867 silc_client_listener_add(client, conn->internal->schedule, params,
868 public_key, private_key,
869 silc_client_ftp_connect_completion,
871 if (!session->listener) {
872 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
873 "Cannot create listener for file transfer: "
874 "%s", silc_errno_string(silc_errno));
877 return SILC_CLIENT_FILE_NO_MEMORY;
879 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
880 strdup(params->local_ip));
881 session->port = silc_client_listener_get_local_port(session->listener);
883 /* Send the key agreement inside FTP packet */
884 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
885 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
888 silc_client_listener_free(session->listener);
890 return SILC_CLIENT_FILE_NO_MEMORY;
892 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
893 SILC_ID_CLIENT, &session->client_entry->id,
896 SILC_STR_DATA(silc_buffer_data(keyagr),
897 silc_buffer_len(keyagr)),
899 silc_buffer_free(keyagr);
901 /* Add session request timeout */
902 if (params && params->timeout_secs)
903 silc_schedule_task_add_timeout(client->schedule,
904 silc_client_ftp_timeout, session,
905 params->timeout_secs, 0);
908 return SILC_CLIENT_FILE_OK;
911 /* Closes file transmission session indicated by the `session_id'.
912 If file transmission is being conducted it will be aborted
913 automatically. This function is also used to close the session
914 after successful file transmission. This function can be used
915 also to reject incoming file transmission request. */
917 SilcClientFileError silc_client_file_close(SilcClient client,
918 SilcClientConnection conn,
919 SilcUInt32 session_id)
921 SilcClientFtpSession session;
923 if (!client || !conn)
924 return SILC_CLIENT_FILE_ERROR;
926 SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
928 /* Get the session */
929 silc_dlist_start(client->internal->ftp_sessions);
930 while ((session = silc_dlist_get(client->internal->ftp_sessions))
932 if (session->session_id == session_id)
936 if (session == SILC_LIST_END) {
937 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
938 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
941 if (session->monitor) {
942 (*session->monitor)(session->client, session->conn,
943 SILC_CLIENT_FILE_MONITOR_CLOSED,
944 SILC_CLIENT_FILE_OK, 0, 0,
945 session->client_entry, session->session_id,
946 session->filepath, session->monitor_context);
948 /* No more callbacks to application */
949 session->monitor = NULL;
952 silc_schedule_task_del_by_context(client->schedule, session);
954 session->closed = TRUE;
956 /* Destroy via timeout */
957 silc_schedule_task_add_timeout(conn->internal->schedule,
958 silc_client_file_close_final, session,
961 return SILC_CLIENT_FILE_OK;
964 /************************** FTP Request Processing **************************/
966 /* Received file transfer packet. Only file transfer requests get here.
967 The actual file transfer is handled by the SFTP library when we give it
968 the packet stream wrapped into SilcStream context. */
970 SILC_FSM_STATE(silc_client_ftp)
972 SilcClientConnection conn = fsm_context;
973 SilcClient client = conn->client;
974 SilcPacket packet = state_context;
975 SilcClientFtpSession session;
976 SilcClientID remote_id;
977 SilcClientEntry remote_client;
978 SilcKeyAgreementPayload payload = NULL;
982 SILC_LOG_DEBUG(("Process file transfer packet"));
984 if (silc_buffer_len(&packet->buffer) < 1)
987 /* We support file transfer type number 1 (== SFTP) */
988 if (packet->buffer.data[0] != 0x01) {
989 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
990 packet->buffer.data[0]));
994 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
995 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
996 SILC_LOG_DEBUG(("Invalid client ID"));
1000 /* Check whether we know this client already */
1001 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1002 if (!remote_client || !remote_client->internal.valid) {
1003 /** Resolve client info */
1004 silc_client_unref_client(client, conn, remote_client);
1005 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1006 client, conn, &remote_id, NULL,
1007 silc_client_ftp_client_resolved,
1013 silc_dlist_start(client->internal->ftp_sessions);
1014 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1015 if (session->client_entry == remote_client &&
1016 (!session->initiator || !session->listener))
1020 /* Parse the key agreement payload */
1022 silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1023 silc_buffer_len(&packet->buffer) - 1);
1025 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1029 hostname = silc_key_agreement_get_hostname(payload);
1030 port = silc_key_agreement_get_port(payload);
1031 if (!hostname || !port) {
1036 /* If session doesn't exist, we create new one. If session exists, but
1037 we are responder it means that the remote sent another request and user
1038 hasn't even accepted the first one yet. We assume this session is new
1040 if (!session || !hostname || !session->initiator) {
1041 /* New file transfer session */
1042 SILC_LOG_DEBUG(("New file transfer session %d",
1043 client->internal->next_session_id + 1));
1045 session = silc_calloc(1, sizeof(*session));
1048 session->session_id = ++client->internal->next_session_id;
1049 session->client = client;
1050 session->server_conn = conn;
1051 session->client_entry = silc_client_ref_client(client, conn,
1053 if (hostname && port) {
1054 session->hostname = strdup(hostname);
1055 session->port = port;
1057 silc_dlist_add(client->internal->ftp_sessions, session);
1059 /* Notify application of incoming FTP request */
1060 client->internal->ops->ftp(client, conn, remote_client,
1061 session->session_id, hostname, port);
1065 /* Session exists, continue with key agreement protocol. */
1066 SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1067 session->session_id));
1069 session->hostname = strdup(hostname);
1070 session->port = port;
1072 /* Connect to the remote client. Performs key exchange automatically. */
1074 silc_client_connect_to_client(client, &session->params,
1075 session->public_key, session->private_key,
1076 session->hostname, session->port,
1077 silc_client_ftp_connect_completion,
1080 /* Call monitor callback */
1081 if (session->monitor)
1082 (*session->monitor)(session->client, session->conn,
1083 SILC_CLIENT_FILE_MONITOR_ERROR,
1084 SILC_CLIENT_FILE_ERROR, 0, 0,
1085 session->client_entry, session->session_id,
1086 session->filepath, session->monitor_context);
1091 silc_key_agreement_payload_free(payload);
1092 silc_packet_free(packet);
1093 return SILC_FSM_FINISH;