5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 - 2007 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
22 #include "silcclient.h"
23 #include "client_internal.h"
25 /************************** Types and definitions ***************************/
27 /* File transmission session */
28 struct SilcClientFtpSessionStruct {
29 SilcClient client; /* Client */
30 SilcClientConnection 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->client->internal->ops->say(session->client, session->conn,
193 SILC_CLIENT_MESSAGE_ERROR,
194 "File `%s' open failed: %s",
198 if (session->monitor)
199 (*session->monitor)(session->client, session->conn,
200 SILC_CLIENT_FILE_MONITOR_ERROR,
201 SILC_CLIENT_FILE_PERMISSION_DENIED, 0, 0,
202 session->client_entry, session->session_id,
203 session->filepath, session->monitor_context);
207 session->read_handle = handle;
209 /* Now, start reading the file */
210 silc_sftp_read(sftp, session->read_handle, session->read_offset,
211 SILC_PACKET_MAX_LEN - 1024,
212 silc_client_ftp_data, session);
214 /* Call monitor callback */
215 if (session->monitor)
216 (*session->monitor)(session->client, session->conn,
217 SILC_CLIENT_FILE_MONITOR_RECEIVE,
219 session->read_offset, session->filesize,
220 session->client_entry, session->session_id,
221 session->filepath, session->monitor_context);
224 /* Ask filename completion callback. Delivers the filepath selected by
227 static void silc_client_ftp_ask_name(const char *filepath,
230 SilcClientFtpSession session = (SilcClientFtpSession)context;
231 SilcSFTPAttributesStruct attr;
232 char *remote_file = NULL;
234 SILC_LOG_DEBUG(("Start"));
237 remote_file = session->filepath;
238 session->filepath = NULL;
239 silc_free(session->path);
240 session->path = NULL;
241 session->filepath = strdup(filepath);
243 remote_file = strdup(session->filepath);
246 /* Now open the file */
247 memset(&attr, 0, sizeof(attr));
248 silc_sftp_open(session->sftp, remote_file, SILC_SFTP_FXF_READ, &attr,
249 silc_client_ftp_open_handle, session);
251 /* Close the directory handle */
252 silc_sftp_close(session->sftp, session->dir_handle, NULL, NULL);
253 session->dir_handle = NULL;
255 silc_free(remote_file);
258 /* Returns the file name available for download. This is the downloader's
261 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
262 SilcSFTPStatus status,
263 const SilcSFTPName name,
266 SilcClientFtpSession session = (SilcClientFtpSession)context;
268 SILC_LOG_DEBUG(("Start"));
270 if (status != SILC_SFTP_STATUS_OK) {
271 /* Call monitor callback */
272 if (session->monitor)
273 (*session->monitor)(session->client, session->conn,
274 SILC_CLIENT_FILE_MONITOR_ERROR,
275 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
276 SILC_CLIENT_FILE_NO_SUCH_FILE :
277 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
278 SILC_CLIENT_FILE_PERMISSION_DENIED :
279 SILC_CLIENT_FILE_ERROR), 0, 0,
280 session->client_entry, session->session_id,
281 session->filepath, session->monitor_context);
285 /* Save the important attributes like filename and file size */
286 session->filepath = strdup(name->filename[0]);
287 session->filesize = name->attrs[0]->size;
289 /* If the path was not provided, ask from application where to save the
291 if (!session->path && session->ask_name) {
292 session->ask_name(session->client, session->conn, session->session_id,
293 name->filename[0], silc_client_ftp_ask_name, session,
294 session->ask_name_context);
298 /* Start downloading immediately to current directory. */
299 silc_client_ftp_ask_name(NULL, session);
302 /* Returns the file handle after giving opendir command. This is the
303 downloader's function. */
305 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
306 SilcSFTPStatus status,
307 SilcSFTPHandle handle,
310 SilcClientFtpSession session = (SilcClientFtpSession)context;
312 SILC_LOG_DEBUG(("Start"));
314 if (status != SILC_SFTP_STATUS_OK) {
315 /* Call monitor callback */
316 if (session->monitor)
317 (*session->monitor)(session->client, session->conn,
318 SILC_CLIENT_FILE_MONITOR_ERROR,
319 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
320 SILC_CLIENT_FILE_NO_SUCH_FILE :
321 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
322 SILC_CLIENT_FILE_PERMISSION_DENIED :
323 SILC_CLIENT_FILE_ERROR), 0, 0,
324 session->client_entry, session->session_id,
325 session->filepath, session->monitor_context);
329 /* Now, read the directory */
330 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
331 session->dir_handle = handle;
334 /* SFTP version callback for SFTP client. This is the downloader's function
335 after initializing the SFTP connection to the remote client. This will
336 find out the filename available for download. */
338 static void silc_client_ftp_version(SilcSFTP sftp,
339 SilcSFTPStatus status,
340 SilcSFTPVersion version,
343 SilcClientFtpSession session = (SilcClientFtpSession)context;
345 SILC_LOG_DEBUG(("Start"));
347 if (status != SILC_SFTP_STATUS_OK) {
348 /* Call monitor callback */
349 if (session->monitor)
350 (*session->monitor)(session->client, session->conn,
351 SILC_CLIENT_FILE_MONITOR_ERROR,
352 (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
353 SILC_CLIENT_FILE_NO_SUCH_FILE :
354 status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
355 SILC_CLIENT_FILE_PERMISSION_DENIED :
356 SILC_CLIENT_FILE_ERROR), 0, 0,
357 session->client_entry, session->session_id,
358 session->filepath, session->monitor_context);
362 /* The SFTP session is open, now retrieve the info about available file. */
363 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
366 /* SFTP stream error callback */
368 static void silc_client_ftp_error(SilcSFTP sftp, SilcSFTPStatus status,
374 /************************ Static utility functions **************************/
376 /* Free session resources. Connection must be closed before getting
379 static void silc_client_ftp_session_free(SilcClientFtpSession session)
381 SILC_LOG_DEBUG(("Free session %d", session->session_id));
383 silc_schedule_task_del_by_context(session->client->schedule, session);
385 silc_dlist_del(session->client->internal->ftp_sessions, session);
387 /* Abort connecting */
389 silc_async_abort(session->op, NULL, NULL);
393 if (session->initiator)
394 silc_sftp_server_shutdown(session->sftp);
396 silc_sftp_client_shutdown(session->sftp);
399 silc_sftp_fs_memory_free(session->fs);
401 /* Destroy listener */
402 if (session->listener)
403 silc_client_listener_free(session->listener);
405 /* Destroy wrapped stream */
407 silc_stream_destroy(session->stream);
409 silc_client_unref_client(session->client, session->server_conn,
410 session->client_entry);
411 silc_free(session->hostname);
412 silc_free(session->filepath);
413 silc_free(session->path);
417 /* File transfer session timeout */
419 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
421 SilcClientFtpSession session = context;
423 SILC_LOG_DEBUG(("Timeout"));
425 /* Close connection (destroyes the session context later). If it is
426 already closed, destroy the session now. */
428 silc_client_close_connection(session->client, session->conn);
429 session->conn = NULL;
431 /* Call monitor callback */
432 if (session->monitor)
433 (*session->monitor)(session->client, session->conn,
434 SILC_CLIENT_FILE_MONITOR_ERROR,
435 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
436 session->client_entry, session->session_id,
437 session->filepath, session->monitor_context);
439 silc_client_ftp_session_free(context);
443 /* File transfer session closing task callback */
445 SILC_TASK_CALLBACK(silc_client_file_close_final)
447 SilcClientFtpSession session = context;
449 /* Close connection (destroyes the session context later). If it is
450 already closed, destroy the session now. */
452 silc_client_close_connection(session->client, session->conn);
453 session->conn = NULL;
455 silc_client_ftp_session_free(context);
459 /* Client resolving callback. Continues with the FTP packet processing */
461 static void silc_client_ftp_client_resolved(SilcClient client,
462 SilcClientConnection conn,
467 SilcFSMThread thread = context;
468 SilcPacket packet = silc_fsm_get_state_context(thread);
470 /* If no client found, ignore the packet, a silent error */
472 silc_packet_free(packet);
473 silc_fsm_finish(thread);
477 /* Continue processing the packet */
478 SILC_FSM_CALL_CONTINUE(context);
481 /* FTP packet payload encoder/decoder. This is called for every FTP packet.
482 We add/remove FTP payload in this function, because SFTP library does not
486 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
487 SilcBuffer buffer, void *context)
489 /* Pull FTP type in the payload, revealing SFTP payload */
490 if (status == SILC_STREAM_CAN_READ) {
491 if (silc_buffer_len(buffer) >= 1)
492 silc_buffer_pull(buffer, 1);
496 /* Add FTP type before SFTP data */
497 if (status == SILC_STREAM_CAN_WRITE) {
498 if (silc_buffer_format(buffer,
508 /* FTP Connection callback. The SFTP session is started here. */
511 silc_client_ftp_connect_completion(SilcClient client,
512 SilcClientConnection conn,
513 SilcClientConnectionStatus status,
518 SilcClientFtpSession session = context;
520 session->conn = conn;
523 silc_schedule_task_del_by_context(client->schedule, session);
526 case SILC_CLIENT_CONN_SUCCESS:
527 SILC_LOG_DEBUG(("Connected, conn %p", conn));
529 /* Wrap the connection packet stream */
530 session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
532 silc_client_ftp_coder, session);
533 if (!session->stream) {
534 /* Call monitor callback */
535 if (session->monitor)
536 (*session->monitor)(session->client, session->conn,
537 SILC_CLIENT_FILE_MONITOR_ERROR,
538 SILC_CLIENT_FILE_ERROR, 0, 0,
539 session->client_entry, session->session_id,
540 session->filepath, session->monitor_context);
541 silc_client_close_connection(client, conn);
542 session->conn = NULL;
546 if (!session->initiator) {
547 /* If we are the SFTP client then start the SFTP session and retrieve
548 the info about the file available for download. */
549 session->sftp = silc_sftp_client_start(session->stream,
550 conn->internal->schedule,
551 silc_client_ftp_version,
552 silc_client_ftp_error, session);
554 /* Start SFTP server */
555 session->sftp = silc_sftp_server_start(session->stream,
556 conn->internal->schedule,
557 silc_client_ftp_error, session,
560 /* Monitor transmission */
561 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
562 silc_client_ftp_monitor, session);
567 case SILC_CLIENT_CONN_DISCONNECTED:
568 SILC_LOG_DEBUG(("Disconnected %p", conn));
570 /* Call monitor callback */
571 if (session->monitor)
572 (*session->monitor)(session->client, session->conn,
573 SILC_CLIENT_FILE_MONITOR_DISCONNECT,
574 SILC_CLIENT_FILE_ERROR, 0, 0,
575 session->client_entry, session->session_id,
576 session->filepath, session->monitor_context);
578 /* Connection already closed */
579 session->conn = NULL;
581 /* If closed by user, destroy the session now */
583 silc_client_ftp_session_free(session);
586 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
587 SILC_LOG_DEBUG(("Connecting timeout"));
589 /* Call monitor callback */
590 if (session->monitor)
591 (*session->monitor)(session->client, session->conn,
592 SILC_CLIENT_FILE_MONITOR_ERROR,
593 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
594 session->client_entry, session->session_id,
595 session->filepath, session->monitor_context);
597 /* Connection already closed */
598 session->conn = NULL;
602 SILC_LOG_DEBUG(("Connecting error %d", status));
604 /* Call monitor callback */
605 if (session->monitor)
606 (*session->monitor)(session->client, session->conn,
607 SILC_CLIENT_FILE_MONITOR_ERROR,
608 status != SILC_CLIENT_CONN_ERROR ?
609 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
610 SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
611 session->client_entry, session->session_id,
612 session->filepath, session->monitor_context);
614 /* Connection already closed */
615 session->conn = NULL;
620 /*************************** File Transfer API ******************************/
622 /* Free all file transfer sessions. */
624 void silc_client_ftp_free_sessions(SilcClient client)
626 SilcClientFtpSession session;
628 if (!client->internal->ftp_sessions)
631 silc_dlist_start(client->internal->ftp_sessions);
632 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
633 silc_client_ftp_session_free(session);
634 silc_dlist_del(client->internal->ftp_sessions, session);
637 /* Free file transfer session by client entry. */
639 void silc_client_ftp_session_free_client(SilcClient client,
640 SilcClientEntry client_entry)
642 SilcClientFtpSession session;
644 if (!client->internal->ftp_sessions)
647 /* Get the session */
648 silc_dlist_start(client->internal->ftp_sessions);
649 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
650 if (session->client_entry == client_entry)
651 silc_client_ftp_session_free(session);
654 /* Sends a file indicated by the `filepath' to the remote client
655 indicated by the `client_entry'. This will negotiate a secret key
656 with the remote client before actually starting the transmission of
657 the file. The `monitor' callback will be called to monitor the
658 transmission of the file. */
661 silc_client_file_send(SilcClient client,
662 SilcClientConnection conn,
663 SilcClientEntry client_entry,
664 SilcClientConnectionParams *params,
665 SilcPublicKey public_key,
666 SilcPrivateKey private_key,
667 SilcClientFileMonitor monitor,
668 void *monitor_context,
669 const char *filepath,
670 SilcUInt32 *session_id)
672 SilcClientFtpSession session;
674 char *filename, *path;
677 SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
679 if (!client || !client_entry || !filepath || !params ||
680 !public_key || !private_key)
681 return SILC_CLIENT_FILE_ERROR;
683 /* Check for existing session for `filepath'. */
684 silc_dlist_start(client->internal->ftp_sessions);
685 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
686 if (session->filepath && !strcmp(session->filepath, filepath) &&
687 session->client_entry == client_entry)
688 return SILC_CLIENT_FILE_ALREADY_STARTED;
691 /* See whether the file exists and can be opened */
692 fd = silc_file_open(filepath, O_RDONLY);
694 return SILC_CLIENT_FILE_NO_SUCH_FILE;
697 /* Add new session */
698 session = silc_calloc(1, sizeof(*session));
700 return SILC_CLIENT_FILE_ERROR;
701 session->session_id = ++client->internal->next_session_id;
702 session->client = client;
703 session->server_conn = conn;
704 session->initiator = TRUE;
705 session->client_entry = silc_client_ref_client(client, conn, client_entry);
706 session->monitor = monitor;
707 session->monitor_context = monitor_context;
708 session->filepath = strdup(filepath);
709 session->params = *params;
710 session->public_key = public_key;
711 session->private_key = private_key;
713 if (silc_asprintf(&path, "file://%s", filepath) < 0) {
715 return SILC_CLIENT_FILE_NO_MEMORY;
718 /* Allocate memory filesystem and put the file to it */
719 if (strrchr(path, '/'))
720 filename = strrchr(path, '/') + 1;
722 filename = (char *)path;
723 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
724 SILC_SFTP_FS_PERM_EXEC);
725 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
728 session->filesize = silc_file_size(filepath);
730 /* If local IP is provided, create listener for incoming key exchange */
731 if (params->local_ip || params->bind_ip) {
733 silc_client_listener_add(client,
734 conn->internal->schedule,
735 params, public_key, private_key,
736 silc_client_ftp_connect_completion,
738 if (!session->listener) {
739 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
740 "Cannot create listener for file transfer: "
741 "%s", strerror(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", strerror(errno));
876 return SILC_CLIENT_FILE_NO_MEMORY;
878 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
879 strdup(params->local_ip));
880 session->port = silc_client_listener_get_local_port(session->listener);
882 /* Send the key agreement inside FTP packet */
883 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
884 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
887 silc_client_listener_free(session->listener);
889 return SILC_CLIENT_FILE_NO_MEMORY;
891 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
892 SILC_ID_CLIENT, &session->client_entry->id,
895 SILC_STR_DATA(silc_buffer_data(keyagr),
896 silc_buffer_len(keyagr)),
898 silc_buffer_free(keyagr);
900 /* Add session request timeout */
901 if (params && params->timeout_secs)
902 silc_schedule_task_add_timeout(client->schedule,
903 silc_client_ftp_timeout, session,
904 params->timeout_secs, 0);
907 return SILC_CLIENT_FILE_OK;
910 /* Closes file transmission session indicated by the `session_id'.
911 If file transmission is being conducted it will be aborted
912 automatically. This function is also used to close the session
913 after successful file transmission. This function can be used
914 also to reject incoming file transmission request. */
916 SilcClientFileError silc_client_file_close(SilcClient client,
917 SilcClientConnection conn,
918 SilcUInt32 session_id)
920 SilcClientFtpSession session;
922 if (!client || !conn)
923 return SILC_CLIENT_FILE_ERROR;
925 SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
927 /* Get the session */
928 silc_dlist_start(client->internal->ftp_sessions);
929 while ((session = silc_dlist_get(client->internal->ftp_sessions))
931 if (session->session_id == session_id)
935 if (session == SILC_LIST_END) {
936 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
937 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
940 if (session->monitor) {
941 (*session->monitor)(session->client, session->conn,
942 SILC_CLIENT_FILE_MONITOR_CLOSED,
943 SILC_CLIENT_FILE_OK, 0, 0,
944 session->client_entry, session->session_id,
945 session->filepath, session->monitor_context);
947 /* No more callbacks to application */
948 session->monitor = NULL;
951 silc_schedule_task_del_by_context(client->schedule, session);
953 session->closed = TRUE;
955 /* Destroy via timeout */
956 silc_schedule_task_add_timeout(conn->internal->schedule,
957 silc_client_file_close_final, session,
960 return SILC_CLIENT_FILE_OK;
963 /************************** FTP Request Processing **************************/
965 /* Received file transfer packet. Only file transfer requests get here.
966 The actual file transfer is handled by the SFTP library when we give it
967 the packet stream wrapped into SilcStream context. */
969 SILC_FSM_STATE(silc_client_ftp)
971 SilcClientConnection conn = fsm_context;
972 SilcClient client = conn->client;
973 SilcPacket packet = state_context;
974 SilcClientFtpSession session;
975 SilcClientID remote_id;
976 SilcClientEntry remote_client;
977 SilcKeyAgreementPayload payload = NULL;
981 SILC_LOG_DEBUG(("Process file transfer packet"));
983 if (silc_buffer_len(&packet->buffer) < 1)
986 /* We support file transfer type number 1 (== SFTP) */
987 if (packet->buffer.data[0] != 0x01) {
988 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
989 packet->buffer.data[0]));
993 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
994 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
995 SILC_LOG_DEBUG(("Invalid client ID"));
999 /* Check whether we know this client already */
1000 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1001 if (!remote_client || !remote_client->internal.valid) {
1002 /** Resolve client info */
1003 silc_client_unref_client(client, conn, remote_client);
1004 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1005 client, conn, &remote_id, NULL,
1006 silc_client_ftp_client_resolved,
1012 silc_dlist_start(client->internal->ftp_sessions);
1013 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1014 if (session->client_entry == remote_client &&
1015 (!session->initiator || !session->listener))
1019 /* Parse the key agreement payload */
1021 silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1022 silc_buffer_len(&packet->buffer) - 1);
1024 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1028 hostname = silc_key_agreement_get_hostname(payload);
1029 port = silc_key_agreement_get_port(payload);
1030 if (!hostname || !port) {
1035 /* If session doesn't exist, we create new one. If session exists, but
1036 we are responder it means that the remote sent another request and user
1037 hasn't even accepted the first one yet. We assume this session is new
1039 if (!session || !hostname || !session->initiator) {
1040 /* New file transfer session */
1041 SILC_LOG_DEBUG(("New file transfer session %d",
1042 client->internal->next_session_id + 1));
1044 session = silc_calloc(1, sizeof(*session));
1047 session->session_id = ++client->internal->next_session_id;
1048 session->client = client;
1049 session->server_conn = conn;
1050 session->client_entry = silc_client_ref_client(client, conn,
1052 if (hostname && port) {
1053 session->hostname = strdup(hostname);
1054 session->port = port;
1056 silc_dlist_add(client->internal->ftp_sessions, session);
1058 /* Notify application of incoming FTP request */
1059 client->internal->ops->ftp(client, conn, remote_client,
1060 session->session_id, hostname, port);
1064 /* Session exists, continue with key agreement protocol. */
1065 SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1066 session->session_id));
1068 session->hostname = strdup(hostname);
1069 session->port = port;
1071 /* Connect to the remote client. Performs key exchange automatically. */
1073 silc_client_connect_to_client(client, &session->params,
1074 session->public_key, session->private_key,
1075 session->hostname, session->port,
1076 silc_client_ftp_connect_completion,
1079 /* Call monitor callback */
1080 if (session->monitor)
1081 (*session->monitor)(session->client, session->conn,
1082 SILC_CLIENT_FILE_MONITOR_ERROR,
1083 SILC_CLIENT_FILE_ERROR, 0, 0,
1084 session->client_entry, session->session_id,
1085 session->filepath, session->monitor_context);
1090 silc_key_agreement_payload_free(payload);
1091 silc_packet_free(packet);
1092 return SILC_FSM_FINISH;