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 read 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 read 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_dlist_del(session->client->internal->ftp_sessions, session);
385 /* Abort connecting */
387 silc_async_abort(session->op, NULL, NULL);
391 if (session->initiator)
392 silc_sftp_server_shutdown(session->sftp);
394 silc_sftp_client_shutdown(session->sftp);
397 silc_sftp_fs_memory_free(session->fs);
399 /* Destroy listener */
400 if (session->listener)
401 silc_client_listener_free(session->listener);
403 /* Destroy wrapped stream */
405 silc_stream_destroy(session->stream);
407 silc_client_unref_client(session->client, session->server_conn,
408 session->client_entry);
409 silc_free(session->hostname);
410 silc_free(session->filepath);
411 silc_free(session->path);
415 /* File transfer session timeout */
417 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
419 SilcClientFtpSession session = context;
421 SILC_LOG_DEBUG(("Timeout"));
423 /* Close connection (destroyes the session context later). If it is
424 already closed, destroy the session now. */
426 silc_client_close_connection(session->client, session->conn);
427 session->conn = NULL;
429 /* Call monitor callback */
430 if (session->monitor)
431 (*session->monitor)(session->client, session->conn,
432 SILC_CLIENT_FILE_MONITOR_ERROR,
433 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
434 session->client_entry, session->session_id,
435 session->filepath, session->monitor_context);
437 silc_client_ftp_session_free(context);
441 /* File transfer session closing task callback */
443 SILC_TASK_CALLBACK(silc_client_file_close_final)
445 SilcClientFtpSession session = context;
447 /* Close connection (destroyes the session context later). If it is
448 already closed, destroy the session now. */
450 silc_client_close_connection(session->client, session->conn);
451 session->conn = NULL;
453 silc_client_ftp_session_free(context);
457 /* Client resolving callback. Continues with the FTP packet processing */
459 static void silc_client_ftp_client_resolved(SilcClient client,
460 SilcClientConnection conn,
465 SilcFSMThread thread = context;
466 SilcPacket packet = silc_fsm_get_state_context(thread);
468 /* If no client found, ignore the packet, a silent error */
470 silc_packet_free(packet);
471 silc_fsm_finish(thread);
475 /* Continue processing the packet */
476 SILC_FSM_CALL_CONTINUE(context);
479 /* FTP packet payload encoder/decoder. This is called for every FTP packet.
480 We add/remove FTP payload in this function, because SFTP library does not
484 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
485 SilcBuffer buffer, void *context)
487 /* Pull FTP type in the payload, revealing SFTP payload */
488 if (status == SILC_STREAM_CAN_READ) {
489 if (silc_buffer_len(buffer) >= 1)
490 silc_buffer_pull(buffer, 1);
494 /* Add FTP type before SFTP data */
495 if (status == SILC_STREAM_CAN_WRITE) {
496 if (silc_buffer_format(buffer,
506 /* FTP Connection callback. The SFTP session is started here. */
509 silc_client_ftp_connect_completion(SilcClient client,
510 SilcClientConnection conn,
511 SilcClientConnectionStatus status,
516 SilcClientFtpSession session = context;
518 session->conn = conn;
521 silc_schedule_task_del_by_context(client->schedule, session);
524 case SILC_CLIENT_CONN_SUCCESS:
525 SILC_LOG_DEBUG(("Connected, conn %p", conn));
527 /* Wrap the connection packet stream */
528 session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
530 silc_client_ftp_coder, session);
531 if (!session->stream) {
532 /* Call monitor callback */
533 if (session->monitor)
534 (*session->monitor)(session->client, session->conn,
535 SILC_CLIENT_FILE_MONITOR_ERROR,
536 SILC_CLIENT_FILE_ERROR, 0, 0,
537 session->client_entry, session->session_id,
538 session->filepath, session->monitor_context);
539 silc_client_close_connection(client, conn);
540 session->conn = NULL;
544 if (!session->initiator) {
545 /* If we are the SFTP client then start the SFTP session and retrieve
546 the info about the file available for download. */
547 session->sftp = silc_sftp_client_start(session->stream,
548 conn->internal->schedule,
549 silc_client_ftp_version,
550 silc_client_ftp_error, session);
552 /* Start SFTP server */
553 session->sftp = silc_sftp_server_start(session->stream,
554 conn->internal->schedule,
555 silc_client_ftp_error, session,
558 /* Monitor transmission */
559 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
560 silc_client_ftp_monitor, session);
565 case SILC_CLIENT_CONN_DISCONNECTED:
566 SILC_LOG_DEBUG(("Disconnected %p", conn));
568 /* Call monitor callback */
569 if (session->monitor)
570 (*session->monitor)(session->client, session->conn,
571 SILC_CLIENT_FILE_MONITOR_DISCONNECT,
572 SILC_CLIENT_FILE_ERROR, 0, 0,
573 session->client_entry, session->session_id,
574 session->filepath, session->monitor_context);
576 /* Connection already closed */
577 session->conn = NULL;
579 /* If closed by user, destroy the session now */
581 silc_client_ftp_session_free(session);
584 case SILC_CLIENT_CONN_ERROR_TIMEOUT:
585 SILC_LOG_DEBUG(("Connecting timeout"));
587 /* Call monitor callback */
588 if (session->monitor)
589 (*session->monitor)(session->client, session->conn,
590 SILC_CLIENT_FILE_MONITOR_ERROR,
591 SILC_CLIENT_FILE_TIMEOUT, 0, 0,
592 session->client_entry, session->session_id,
593 session->filepath, session->monitor_context);
595 /* Connection already closed */
596 session->conn = NULL;
600 SILC_LOG_DEBUG(("Connecting error %d", status));
602 /* Call monitor callback */
603 if (session->monitor)
604 (*session->monitor)(session->client, session->conn,
605 SILC_CLIENT_FILE_MONITOR_ERROR,
606 status != SILC_CLIENT_CONN_ERROR ?
607 SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
608 SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
609 session->client_entry, session->session_id,
610 session->filepath, session->monitor_context);
612 /* Connection already closed */
613 session->conn = NULL;
618 /*************************** File Transfer API ******************************/
620 /* Free all file transfer sessions. */
622 void silc_client_ftp_free_sessions(SilcClient client)
624 SilcClientFtpSession session;
626 if (!client->internal->ftp_sessions)
629 silc_dlist_start(client->internal->ftp_sessions);
630 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
631 silc_client_ftp_session_free(session);
632 silc_dlist_del(client->internal->ftp_sessions, session);
635 /* Free file transfer session by client entry. */
637 void silc_client_ftp_session_free_client(SilcClient client,
638 SilcClientEntry client_entry)
640 SilcClientFtpSession session;
642 if (!client->internal->ftp_sessions)
645 /* Get the session */
646 silc_dlist_start(client->internal->ftp_sessions);
647 while ((session = silc_dlist_get(client->internal->ftp_sessions)))
648 if (session->client_entry == client_entry)
649 silc_client_ftp_session_free(session);
652 /* Sends a file indicated by the `filepath' to the remote client
653 indicated by the `client_entry'. This will negotiate a secret key
654 with the remote client before actually starting the transmission of
655 the file. The `monitor' callback will be called to monitor the
656 transmission of the file. */
659 silc_client_file_send(SilcClient client,
660 SilcClientConnection conn,
661 SilcClientEntry client_entry,
662 SilcClientConnectionParams *params,
663 SilcPublicKey public_key,
664 SilcPrivateKey private_key,
665 SilcClientFileMonitor monitor,
666 void *monitor_context,
667 const char *filepath,
668 SilcUInt32 *session_id)
670 SilcClientFtpSession session;
672 char *filename, *path;
675 SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
677 if (!client || !client_entry || !filepath || !params ||
678 !public_key || !private_key)
679 return SILC_CLIENT_FILE_ERROR;
681 /* Check for existing session for `filepath'. */
682 silc_dlist_start(client->internal->ftp_sessions);
683 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
684 if (session->filepath && !strcmp(session->filepath, filepath) &&
685 session->client_entry == client_entry)
686 return SILC_CLIENT_FILE_ALREADY_STARTED;
689 /* See whether the file exists and can be opened */
690 fd = silc_file_open(filepath, O_RDONLY);
692 return SILC_CLIENT_FILE_NO_SUCH_FILE;
695 /* Add new session */
696 session = silc_calloc(1, sizeof(*session));
698 return SILC_CLIENT_FILE_ERROR;
699 session->session_id = ++client->internal->next_session_id;
700 session->client = client;
701 session->server_conn = conn;
702 session->initiator = TRUE;
703 session->client_entry = silc_client_ref_client(client, conn, client_entry);
704 session->monitor = monitor;
705 session->monitor_context = monitor_context;
706 session->filepath = strdup(filepath);
707 session->params = *params;
708 session->public_key = public_key;
709 session->private_key = private_key;
711 if (silc_asprintf(&path, "file://%s", filepath) < 0) {
713 return SILC_CLIENT_FILE_NO_MEMORY;
716 /* Allocate memory filesystem and put the file to it */
717 if (strrchr(path, '/'))
718 filename = strrchr(path, '/') + 1;
720 filename = (char *)path;
721 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
722 SILC_SFTP_FS_PERM_EXEC);
723 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
726 session->filesize = silc_file_size(filepath);
728 /* If local IP is provided, create listener for incoming key exchange */
729 if (params->local_ip || params->bind_ip) {
731 silc_client_listener_add(client,
732 conn->internal->schedule,
733 params, public_key, private_key,
734 silc_client_ftp_connect_completion,
736 if (!session->listener) {
737 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
738 "Cannot create listener for file transfer: "
739 "%s", strerror(errno));
741 return SILC_CLIENT_FILE_NO_MEMORY;
744 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
745 strdup(params->local_ip));
746 session->port = silc_client_listener_get_local_port(session->listener);
749 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
751 /* Send the key agreement inside FTP packet */
752 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
755 if (session->listener)
756 silc_client_listener_free(session->listener);
758 return SILC_CLIENT_FILE_NO_MEMORY;
760 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
761 SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
763 SILC_STR_DATA(silc_buffer_data(keyagr),
764 silc_buffer_len(keyagr)),
767 silc_buffer_free(keyagr);
770 silc_dlist_add(client->internal->ftp_sessions, session);
772 *session_id = session->session_id;
774 /* Add session request timeout */
775 if (params && params->timeout_secs)
776 silc_schedule_task_add_timeout(client->schedule,
777 silc_client_ftp_timeout, session,
778 params->timeout_secs, 0);
780 return SILC_CLIENT_FILE_OK;
783 /* Receives a file from a client indicated by the `client_entry'. The
784 `session_id' indicates the file transmission session and it has been
785 received in the `ftp' client operation function. This will actually
786 perform the key agreement protocol with the remote client before
787 actually starting the file transmission. The `monitor' callback
788 will be called to monitor the transmission. */
791 silc_client_file_receive(SilcClient client,
792 SilcClientConnection conn,
793 SilcClientConnectionParams *params,
794 SilcPublicKey public_key,
795 SilcPrivateKey private_key,
796 SilcClientFileMonitor monitor,
797 void *monitor_context,
799 SilcUInt32 session_id,
800 SilcClientFileAskName ask_name,
801 void *ask_name_context)
803 SilcClientFtpSession session;
806 if (!client || !conn)
807 return SILC_CLIENT_FILE_ERROR;
809 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
811 /* Get the session */
812 silc_dlist_start(client->internal->ftp_sessions);
813 while ((session = silc_dlist_get(client->internal->ftp_sessions))
815 if (session->session_id == session_id) {
820 if (session == SILC_LIST_END) {
821 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
822 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
825 /* See if we have this session running already */
826 if (session->sftp || session->listener) {
827 SILC_LOG_DEBUG(("Session already started"));
828 return SILC_CLIENT_FILE_ALREADY_STARTED;
831 session->monitor = monitor;
832 session->monitor_context = monitor_context;
833 session->ask_name = ask_name;
834 session->ask_name_context = ask_name_context;
835 session->path = path ? strdup(path) : NULL;
837 /* If the hostname and port already exists then the remote client did
838 provide the connection point to us and we won't create listener, but
839 create the connection ourselves. */
840 if (session->hostname && session->port) {
841 SILC_LOG_DEBUG(("Connecting to remote client"));
842 /* Connect to the remote client. Performs key exchange automatically. */
844 silc_client_connect_to_client(client, params, public_key, private_key,
845 session->hostname, session->port,
846 silc_client_ftp_connect_completion,
850 return SILC_CLIENT_FILE_ERROR;
853 /* Add the listener for the key agreement */
854 SILC_LOG_DEBUG(("Creating listener for file transfer"));
855 if (!params || (!params->local_ip && !params->bind_ip)) {
857 return SILC_CLIENT_FILE_ERROR;
860 silc_client_listener_add(client, conn->internal->schedule, params,
861 public_key, private_key,
862 silc_client_ftp_connect_completion,
864 if (!session->listener) {
865 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
866 "Cannot create listener for file transfer: "
867 "%s", strerror(errno));
869 return SILC_CLIENT_FILE_NO_MEMORY;
871 session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
872 strdup(params->local_ip));
873 session->port = silc_client_listener_get_local_port(session->listener);
875 /* Send the key agreement inside FTP packet */
876 SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
877 keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
880 silc_client_listener_free(session->listener);
882 return SILC_CLIENT_FILE_NO_MEMORY;
884 silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
885 SILC_ID_CLIENT, &session->client_entry->id,
888 SILC_STR_DATA(silc_buffer_data(keyagr),
889 silc_buffer_len(keyagr)),
891 silc_buffer_free(keyagr);
893 /* Add session request timeout */
894 if (params && params->timeout_secs)
895 silc_schedule_task_add_timeout(client->schedule,
896 silc_client_ftp_timeout, session,
897 params->timeout_secs, 0);
900 return SILC_CLIENT_FILE_OK;
903 /* Closes file transmission session indicated by the `session_id'.
904 If file transmission is being conducted it will be aborted
905 automatically. This function is also used to close the session
906 after successful file transmission. This function can be used
907 also to reject incoming file transmission request. */
909 SilcClientFileError silc_client_file_close(SilcClient client,
910 SilcClientConnection conn,
911 SilcUInt32 session_id)
913 SilcClientFtpSession session;
915 if (!client || !conn)
916 return SILC_CLIENT_FILE_ERROR;
918 SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
920 /* Get the session */
921 silc_dlist_start(client->internal->ftp_sessions);
922 while ((session = silc_dlist_get(client->internal->ftp_sessions))
924 if (session->session_id == session_id)
928 if (session == SILC_LIST_END) {
929 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
930 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
933 if (session->monitor) {
934 (*session->monitor)(session->client, session->conn,
935 SILC_CLIENT_FILE_MONITOR_CLOSED,
936 SILC_CLIENT_FILE_OK, 0, 0,
937 session->client_entry, session->session_id,
938 session->filepath, session->monitor_context);
940 /* No more callbacks to application */
941 session->monitor = NULL;
944 silc_schedule_task_del_by_context(client->schedule, session);
946 session->closed = TRUE;
948 /* Destroy via timeout */
949 silc_schedule_task_add_timeout(conn->internal->schedule,
950 silc_client_file_close_final, session,
953 return SILC_CLIENT_FILE_OK;
956 /************************** FTP Request Processing **************************/
958 /* Received file transfer packet. Only file transfer requests get here.
959 The actual file transfer is handled by the SFTP library when we give it
960 the packet stream wrapped into SilcStream context. */
962 SILC_FSM_STATE(silc_client_ftp)
964 SilcClientConnection conn = fsm_context;
965 SilcClient client = conn->client;
966 SilcPacket packet = state_context;
967 SilcClientFtpSession session;
968 SilcClientID remote_id;
969 SilcClientEntry remote_client;
970 SilcKeyAgreementPayload payload = NULL;
974 SILC_LOG_DEBUG(("Process file transfer packet"));
976 if (silc_buffer_len(&packet->buffer) < 1)
979 /* We support file transfer type number 1 (== SFTP) */
980 if (packet->buffer.data[0] != 0x01) {
981 SILC_LOG_DEBUG(("Unsupported file transfer type %d",
982 packet->buffer.data[0]));
986 if (!silc_id_str2id(packet->src_id, packet->src_id_len,
987 SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
988 SILC_LOG_DEBUG(("Invalid client ID"));
992 /* Check whether we know this client already */
993 remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
994 if (!remote_client || !remote_client->internal.valid) {
995 /** Resolve client info */
996 silc_client_unref_client(client, conn, remote_client);
997 SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
998 client, conn, &remote_id, NULL,
999 silc_client_ftp_client_resolved,
1005 silc_dlist_start(client->internal->ftp_sessions);
1006 while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1007 if (session->client_entry == remote_client &&
1008 (!session->initiator || !session->listener))
1012 /* Parse the key agreement payload */
1014 silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1015 silc_buffer_len(&packet->buffer) - 1);
1017 SILC_LOG_DEBUG(("Invalid key agreement payload"));
1021 hostname = silc_key_agreement_get_hostname(payload);
1022 port = silc_key_agreement_get_port(payload);
1023 if (!hostname || !port) {
1028 /* If session doesn't exist, we create new one. If session exists, but
1029 we are responder it means that the remote sent another request and user
1030 hasn't even accepted the first one yet. We assume this session is new
1032 if (!session || !hostname || !session->initiator) {
1033 /* New file transfer session */
1034 SILC_LOG_DEBUG(("New file transfer session %d",
1035 client->internal->next_session_id + 1));
1037 session = silc_calloc(1, sizeof(*session));
1040 session->session_id = ++client->internal->next_session_id;
1041 session->client = client;
1042 session->server_conn = conn;
1043 session->client_entry = silc_client_ref_client(client, conn,
1045 if (hostname && port) {
1046 session->hostname = strdup(hostname);
1047 session->port = port;
1049 silc_dlist_add(client->internal->ftp_sessions, session);
1051 /* Notify application of incoming FTP request */
1052 client->internal->ops->ftp(client, conn, remote_client,
1053 session->session_id, hostname, port);
1057 /* Session exists, continue with key agreement protocol. */
1058 SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1059 session->session_id));
1061 session->hostname = strdup(hostname);
1062 session->port = port;
1064 /* Connect to the remote client. Performs key exchange automatically. */
1066 silc_client_connect_to_client(client, &session->params,
1067 session->public_key, session->private_key,
1068 session->hostname, session->port,
1069 silc_client_ftp_connect_completion,
1072 /* Call monitor callback */
1073 if (session->monitor)
1074 (*session->monitor)(session->client, session->conn,
1075 SILC_CLIENT_FILE_MONITOR_ERROR,
1076 SILC_CLIENT_FILE_ERROR, 0, 0,
1077 session->client_entry, session->session_id,
1078 session->filepath, session->monitor_context);
1083 silc_key_agreement_payload_free(payload);
1084 silc_packet_free(packet);
1085 return SILC_FSM_FINISH;