5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2001 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 "clientlibincludes.h"
22 #include "client_internal.h"
25 silc_client_connect_to_client(SilcClient client,
26 SilcClientConnection conn, int port,
27 char *host, void *context);
29 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
30 SILC_TASK_CALLBACK(silc_client_ftp_connected);
31 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
34 /* File transmission session */
35 struct SilcClientFtpSessionStruct {
38 SilcClientConnection conn;
39 SilcClientEntry client_entry;
41 SilcSocketConnection sock;
48 SilcClientFileMonitor monitor;
49 void *monitor_context;
53 SilcSFTPFilesystem fs;
56 SilcSFTPHandle dir_handle;
57 SilcSFTPHandle read_handle;
63 SILC_TASK_CALLBACK(silc_client_ftp_connected)
65 SilcClientInternalConnectContext *ctx =
66 (SilcClientInternalConnectContext *)context;
67 SilcClient client = ctx->client;
68 SilcClientConnection conn = ctx->conn;
69 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
70 int opt, opt_len = sizeof(opt);
72 SILC_LOG_DEBUG(("Start"));
74 /* Check the socket status as it might be in error */
75 silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
78 /* Connection failed but lets try again */
79 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
80 "Could not connect to client %s: %s",
81 ctx->host, strerror(opt));
82 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT,
83 "Connecting to port %d of client %s resumed",
84 ctx->port, ctx->host);
86 /* Unregister old connection try */
87 silc_schedule_unset_listen_fd(client->schedule, fd);
88 silc_net_close_connection(fd);
89 silc_schedule_task_del(client->schedule, ctx->task);
92 silc_client_connect_to_client_internal(ctx);
95 /* Connection failed and we won't try anymore */
96 client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
97 "Could not connect to client %s: %s",
98 ctx->host, strerror(opt));
99 silc_schedule_unset_listen_fd(client->schedule, fd);
100 silc_net_close_connection(fd);
101 silc_schedule_task_del(client->schedule, ctx->task);
103 silc_client_ftp_session_free(session);
108 silc_schedule_unset_listen_fd(client->schedule, fd);
109 silc_schedule_task_del(client->schedule, ctx->task);
111 /* Start the key agreement */
112 silc_client_ftp_start_key_agreement(session, fd);
116 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
120 /* Create connection to server asynchronously */
121 sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
125 /* Register task that will receive the async connect and will
127 ctx->task = silc_schedule_task_add(ctx->client->schedule, sock,
128 silc_client_ftp_connected,
131 SILC_TASK_PRI_NORMAL);
132 silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
138 silc_client_connect_to_client(SilcClient client,
139 SilcClientConnection conn, int port,
140 char *host, void *context)
142 SilcClientInternalConnectContext *ctx;
144 /* Allocate internal context for connection process. This is
145 needed as we are doing async connecting. */
146 ctx = silc_calloc(1, sizeof(*ctx));
147 ctx->client = client;
149 ctx->host = strdup(host);
152 ctx->context = context;
154 /* Do the actual connecting process */
155 return silc_client_connect_to_client_internal(ctx);
158 /* SFTP packet send callback. This will use preallocated buffer to avoid
159 reallocation of outgoing data buffer everytime. */
161 static void silc_client_ftp_send_packet(SilcSocketConnection sock,
162 SilcBuffer packet, void *context)
164 SilcClientFtpSession session = (SilcClientFtpSession)context;
165 SilcClient client = session->client;
167 SILC_LOG_DEBUG(("Start"));
169 /* Allocate outgoing packet */
170 if (!session->packet)
171 session->packet = silc_buffer_alloc(1 + packet->len);
173 /* Enlarge outgoing packet if needed */
174 if (session->packet->truelen < 1 + packet->len)
175 session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
178 silc_buffer_pull_tail(session->packet, 1 + packet->len);
179 silc_buffer_format(session->packet,
181 SILC_STR_UI_XNSTRING(packet->data, packet->len),
184 /* Send the packet immediately */
185 silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
186 session->packet->data, session->packet->len, TRUE);
189 session->packet->data = session->packet->tail = session->packet->head;
190 session->packet->len = 0;
193 /* SFTP monitor callback for SFTP server. This reports the application
194 how the transmission is going along. This function is for the client
195 who made the file available for download. */
197 static void silc_client_ftp_monitor(SilcSFTP sftp,
198 SilcSFTPMonitors type,
199 const SilcSFTPMonitorData data,
202 SilcClientFtpSession session = (SilcClientFtpSession)context;
204 if (type == SILC_SFTP_MONITOR_READ) {
205 /* Call the monitor for application */
206 if (session->monitor)
207 (*session->monitor)(session->client, session->conn,
208 SILC_CLIENT_FILE_MONITOR_SEND,
209 data->offset, session->filesize,
210 session->client_entry, session->session_id,
211 session->filepath, session->monitor_context);
215 /* Returns the read data. This is the downloader's function (client side)
216 to receive the read data and read more until EOF is received from
217 the other side. This will also monitor the transmission and notify
220 static void silc_client_ftp_data(SilcSFTP sftp,
221 SilcSFTPStatus status,
222 const unsigned char *data,
226 SilcClientFtpSession session = (SilcClientFtpSession)context;
228 SILC_LOG_DEBUG(("Start"));
230 if (status == SILC_SFTP_STATUS_EOF) {
233 /* Close the handle */
234 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
235 session->read_handle = NULL;
237 /* Close the read file descriptor */
238 silc_file_close(session->fd);
242 if (status != SILC_SFTP_STATUS_OK) {
243 /* Call monitor callback */
244 if (session->monitor)
245 (*session->monitor)(session->client, session->conn,
246 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
247 session->client_entry, session->session_id,
248 session->filepath, session->monitor_context);
250 /* Close the handle */
251 silc_sftp_close(sftp, session->read_handle, NULL, NULL);
252 session->read_handle = NULL;
254 /* Close the read file descriptor */
255 silc_file_close(session->fd);
259 /* Read more, until EOF is received */
260 session->read_offset += data_len;
261 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
262 silc_client_ftp_data, session);
264 /* Call monitor callback */
265 if (session->monitor)
266 (*session->monitor)(session->client, session->conn,
267 SILC_CLIENT_FILE_MONITOR_RECEIVE,
268 session->read_offset, session->filesize,
269 session->client_entry, session->session_id,
270 session->filepath, session->monitor_context);
272 /* Write the read data to the real file */
273 silc_file_write(session->fd, data, data_len);
276 /* Returns handle for the opened file. This is the downloader's function.
277 This will begin reading the data from the file. */
279 static void silc_client_ftp_open_handle(SilcSFTP sftp,
280 SilcSFTPStatus status,
281 SilcSFTPHandle handle,
284 SilcClientFtpSession session = (SilcClientFtpSession)context;
286 SILC_LOG_DEBUG(("Start"));
288 if (status != SILC_SFTP_STATUS_OK) {
289 /* Call monitor callback */
290 if (session->monitor)
291 (*session->monitor)(session->client, session->conn,
292 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
293 session->client_entry, session->session_id,
294 session->filepath, session->monitor_context);
298 /* Open the actual local file */
299 session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT);
300 if (session->fd < 0) {
301 /* Call monitor callback */
302 if (session->monitor)
303 (*session->monitor)(session->client, session->conn,
304 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
305 session->client_entry, session->session_id,
306 session->filepath, session->monitor_context);
310 session->read_handle = handle;
312 /* Now, start reading the file */
313 silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
314 silc_client_ftp_data, session);
316 /* Call monitor callback */
317 if (session->monitor)
318 (*session->monitor)(session->client, session->conn,
319 SILC_CLIENT_FILE_MONITOR_RECEIVE,
320 session->read_offset, session->filesize,
321 session->client_entry, session->session_id,
322 session->filepath, session->monitor_context);
325 /* Returns the file name available for download. This is the downloader's
328 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
329 SilcSFTPStatus status,
330 const SilcSFTPName name,
333 SilcClientFtpSession session = (SilcClientFtpSession)context;
334 SilcSFTPAttributesStruct attr;
336 SILC_LOG_DEBUG(("Start"));
338 if (status != SILC_SFTP_STATUS_OK) {
339 /* Call monitor callback */
340 if (session->monitor)
341 (*session->monitor)(session->client, session->conn,
342 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
343 session->client_entry, session->session_id,
344 session->filepath, session->monitor_context);
348 /* Now open the file */
349 memset(&attr, 0, sizeof(attr));
350 silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
351 silc_client_ftp_open_handle, session);
353 /* Save the important attributes */
354 session->filepath = strdup(name->filename[0]);
355 session->filesize = name->attrs[0]->size;
357 /* Close the directory handle */
358 silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
359 session->dir_handle = NULL;
362 /* Returns the file handle after giving opendir command. This is the
363 downloader's function. */
365 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
366 SilcSFTPStatus status,
367 SilcSFTPHandle handle,
370 SilcClientFtpSession session = (SilcClientFtpSession)context;
372 SILC_LOG_DEBUG(("Start"));
374 if (status != SILC_SFTP_STATUS_OK) {
375 /* Call monitor callback */
376 if (session->monitor)
377 (*session->monitor)(session->client, session->conn,
378 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
379 session->client_entry, session->session_id,
380 session->filepath, session->monitor_context);
384 /* Now, read the directory */
385 silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
386 session->dir_handle = handle;
389 /* SFTP version callback for SFTP client. This is the downloader's function
390 after initializing the SFTP connection to the remote client. This will
391 find out the filename available for download. */
393 static void silc_client_ftp_version(SilcSFTP sftp,
394 SilcSFTPStatus status,
395 SilcSFTPVersion version,
398 SilcClientFtpSession session = (SilcClientFtpSession)context;
400 SILC_LOG_DEBUG(("Start"));
402 if (status != SILC_SFTP_STATUS_OK) {
403 /* Call monitor callback */
404 if (session->monitor)
405 (*session->monitor)(session->client, session->conn,
406 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
407 session->client_entry, session->session_id,
408 session->filepath, session->monitor_context);
412 /* The SFTP session is open, now retrieve the info about available file. */
413 silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
416 /* This callback is called after the key agreement protocol has been
417 performed. This calls the final completion callback for the application. */
419 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
421 SilcProtocol protocol = (SilcProtocol)context;
422 SilcClientKEInternalContext *ctx =
423 (SilcClientKEInternalContext *)protocol->context;
424 SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
425 SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
427 SILC_LOG_DEBUG(("Start"));
429 if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
430 protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
431 /* Error occured during protocol */
432 silc_ske_free_key_material(ctx->keymat);
436 /* Set keys into use */
437 silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
438 ctx->ske->prop->cipher,
439 ctx->ske->prop->pkcs,
440 ctx->ske->prop->hash,
441 ctx->ske->prop->hmac,
442 ctx->ske->prop->group,
445 /* If we are the SFTP client then start the SFTP session and retrieve
446 the info about the file available for download. */
447 if (!session->server) {
448 session->sftp = silc_sftp_client_start(conn->sock,
449 silc_client_ftp_send_packet,
451 silc_client_ftp_version, session);
454 /* Set this as active session */
455 conn->active_session = session;
458 silc_ske_free_key_material(ctx->keymat);
460 silc_ske_free(ctx->ske);
461 silc_free(ctx->dest_id);
462 ctx->sock->protocol = NULL;
463 silc_socket_free(ctx->sock);
465 silc_protocol_free(protocol);
468 /* The downloader's function to start the key agreement protocol with the
469 remote client after we have connected to it. */
471 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
474 SilcClient client = session->client;
475 SilcClientKEInternalContext *proto_ctx;
476 SilcProtocol protocol;
477 SilcClientConnection conn;
480 SILC_LOG_DEBUG(("Start"));
482 /* Call monitor callback */
483 if (session->monitor)
484 (*session->monitor)(session->client, session->conn,
485 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
486 session->client_entry, session->session_id,
487 NULL, session->monitor_context);
489 /* Add new connection for this session */
490 conn = silc_client_add_connection(client, session->hostname,
491 session->port, session);
493 /* Allocate new socket connection object */
494 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
495 conn->sock->hostname = strdup(session->hostname);
496 conn->sock->port = silc_net_get_remote_port(sock);
497 session->sock = silc_socket_dup(conn->sock);
499 /* Allocate the SFTP */
500 if (session->server) {
501 session->sftp = silc_sftp_server_start(conn->sock,
502 silc_client_ftp_send_packet,
503 session, session->fs);
505 /* Monitor transmission */
506 silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
507 silc_client_ftp_monitor, session);
510 /* Allocate internal context for key exchange protocol. This is
511 sent as context for the protocol. */
512 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
513 proto_ctx->client = client;
514 proto_ctx->sock = silc_socket_dup(conn->sock);
515 proto_ctx->rng = client->rng;
516 proto_ctx->responder = FALSE;
517 proto_ctx->context = session;
518 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
519 proto_ctx->verify = silc_client_protocol_ke_verify_key;
521 /* Perform key exchange protocol. */
522 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
523 &protocol, (void *)proto_ctx,
524 silc_client_ftp_key_agreement_final);
525 conn->sock->protocol = protocol;
527 /* Register the connection for network input and output. This sets
528 that scheduler will listen for incoming packets for this connection
529 and sets that outgoing packets may be sent to this connection as well.
530 However, this doesn't set the scheduler for outgoing traffic, it will
531 be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
532 later when outgoing data is available. */
533 context = (void *)client;
534 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
536 /* Execute the protocol */
537 silc_protocol_execute(protocol, client->schedule, 0, 0);
540 /* The remote client's (the client who made the file available for download)
541 function for accepting incoming connection. This will also start the
542 key agreement protocol with the other client. */
544 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
546 SilcClientFtpSession session = (SilcClientFtpSession)context;
547 SilcClient client = session->client;
548 SilcClientConnection conn;
549 SilcSocketConnection newsocket;
550 SilcClientKEInternalContext *proto_ctx;
553 SILC_LOG_DEBUG(("Start"));
555 sock = silc_net_accept_connection(session->listener);
557 /* Call monitor callback */
558 if (session->monitor)
559 (*session->monitor)(session->client, session->conn,
560 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
561 session->client_entry, session->session_id,
562 session->filepath, session->monitor_context);
566 /* Set socket options */
567 silc_net_set_socket_nonblock(sock);
568 silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
570 /* Allocate new socket connection object */
571 silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
573 /* Perform name and address lookups for the remote host. */
574 silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
575 if (!newsocket->hostname && !newsocket->ip) {
576 /* Call monitor callback */
577 if (session->monitor)
578 (*session->monitor)(session->client, session->conn,
579 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
580 session->client_entry, session->session_id,
581 session->filepath, session->monitor_context);
584 if (!newsocket->hostname)
585 newsocket->hostname = strdup(newsocket->ip);
586 newsocket->port = silc_net_get_remote_port(sock);
588 /* Call monitor callback */
589 if (session->monitor)
590 (*session->monitor)(session->client, session->conn,
591 SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
592 session->client_entry, session->session_id,
593 NULL, session->monitor_context);
595 /* Add new connection for this session */
596 conn = silc_client_add_connection(client, newsocket->hostname,
597 newsocket->port, session);
598 conn->sock = newsocket;
599 conn->sock->user_data = conn;
600 session->sock = silc_socket_dup(conn->sock);
602 /* Allocate internal context for key exchange protocol. This is
603 sent as context for the protocol. */
604 proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
605 proto_ctx->client = client;
606 proto_ctx->sock = silc_socket_dup(conn->sock);
607 proto_ctx->rng = client->rng;
608 proto_ctx->responder = TRUE;
609 proto_ctx->context = session;
610 proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
611 proto_ctx->verify = silc_client_protocol_ke_verify_key;
613 /* Prepare the connection for key exchange protocol. We allocate the
614 protocol but will not start it yet. The connector will be the
615 initiator of the protocol thus we will wait for initiation from
616 there before we start the protocol. */
617 silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE,
618 &newsocket->protocol, proto_ctx,
619 silc_client_ftp_key_agreement_final);
621 /* Register the connection for network input and output. This sets
622 that scheduler will listen for incoming packets for this connection
623 and sets that outgoing packets may be sent to this connection as well.
624 However, this doesn't set the scheduler for outgoing traffic, it
625 will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
626 later when outgoing data is available. */
627 context = (void *)client;
628 SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
631 /* Free all file transfer sessions. */
633 void silc_client_ftp_free_sessions(SilcClient client,
634 SilcClientConnection conn)
636 if (conn->ftp_sessions) {
637 SilcClientFtpSession session;
638 silc_dlist_start(conn->ftp_sessions);
639 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
641 session->sock->user_data = NULL;
642 silc_client_ftp_session_free(session);
644 silc_dlist_del(conn->ftp_sessions, session);
645 silc_dlist_uninit(conn->ftp_sessions);
649 /* Free file transfer session by client entry. */
651 void silc_client_ftp_session_free_client(SilcClientConnection conn,
652 SilcClientEntry client_entry)
654 SilcClientFtpSession session;
656 if (!conn->ftp_sessions)
659 /* Get the session */
660 silc_dlist_start(conn->ftp_sessions);
661 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
662 if (session->client_entry == client_entry) {
664 session->sock->user_data = NULL;
665 silc_client_ftp_session_free(session);
671 /* Free session resources. */
673 void silc_client_ftp_session_free(SilcClientFtpSession session)
675 SilcClientConnection conn;
677 SILC_LOG_DEBUG(("Free session"));
679 silc_dlist_del(session->conn->ftp_sessions, session);
683 silc_sftp_server_shutdown(session->sftp);
685 silc_sftp_client_shutdown(session->sftp);
689 silc_sftp_fs_memory_free(session->fs);
691 /* Destroy listener */
692 if (session->listener) {
693 silc_schedule_unset_listen_fd(session->client->schedule,
695 silc_net_close_connection(session->listener);
696 silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
699 /* Destroy session connection */
701 silc_schedule_unset_listen_fd(session->client->schedule,
702 session->sock->sock);
703 silc_net_close_connection(session->sock->sock);
705 if (session->sock->user_data) {
706 conn = (SilcClientConnection)session->sock->user_data;
708 if (conn->active_session == session)
709 conn->active_session = NULL;
711 silc_client_close_connection(session->client, session->sock, conn);
713 silc_socket_free(session->sock);
718 silc_buffer_free(session->packet);
720 silc_free(session->hostname);
721 silc_free(session->filepath);
725 /* Sends a file indicated by the `filepath' to the remote client
726 indicated by the `client_entry'. This will negotiate a secret key
727 with the remote client before actually starting the transmission of
728 the file. The `monitor' callback will be called to monitor the
729 transmission of the file.
731 This returns a file session ID for the file transmission. It can
732 be used to close the session (and abort the file transmission) by
733 calling the silc_client_file_close function. The session ID is
734 also returned in the `monitor' callback. This returns 0 if the
735 file indicated by the `filepath' is being transmitted to the remote
736 client indicated by the `client_entry', already. */
738 uint32 silc_client_file_send(SilcClient client,
739 SilcClientConnection conn,
740 SilcClientFileMonitor monitor,
741 void *monitor_context,
742 SilcClientEntry client_entry,
743 const char *filepath)
745 SilcClientFtpSession session;
746 SilcBuffer keyagr, ftp;
747 char *filename, *path;
749 SILC_LOG_DEBUG(("Start"));
751 /* Check for existing session for `filepath'. */
752 silc_dlist_start(conn->ftp_sessions);
753 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
754 if (!strcmp(session->filepath, filepath) &&
755 session->client_entry == client_entry)
759 /* Add new session */
760 session = silc_calloc(1, sizeof(*session));
761 session->session_id = ++conn->next_session_id;
762 session->client = client;
763 session->conn = conn;
764 session->client_entry = client_entry;
765 session->monitor = monitor;
766 session->monitor_context = monitor_context;
767 session->filepath = strdup(filepath);
768 session->server = TRUE;
769 silc_dlist_add(conn->ftp_sessions, session);
771 path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
772 strcat(path, "file://");
773 strncat(path, filepath, strlen(filepath));
775 /* Allocate memory filesystem and put the file to it */
776 if (strrchr(path, '/'))
777 filename = strrchr(path, '/') + 1;
779 filename = (char *)path;
780 session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
781 SILC_SFTP_FS_PERM_EXEC);
782 silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
785 session->filesize = silc_file_size(filepath);
787 /* Send the key agreement inside FTP packet */
788 keyagr = silc_key_agreement_payload_encode(NULL, 0);
790 ftp = silc_buffer_alloc(1 + keyagr->len);
791 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
792 silc_buffer_format(ftp,
794 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
796 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
797 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
798 ftp->data, ftp->len, FALSE);
800 silc_buffer_free(keyagr);
801 silc_buffer_free(ftp);
804 return session->session_id;
807 /* Receives a file from a client indicated by the `client_entry'. The
808 `session_id' indicates the file transmission session and it has been
809 received in the `ftp' client operation function. This will actually
810 perform the key agreement protocol with the remote client before
811 actually starting the file transmission. The `monitor' callback
812 will be called to monitor the transmission. */
815 silc_client_file_receive(SilcClient client,
816 SilcClientConnection conn,
817 SilcClientFileMonitor monitor,
818 void *monitor_context,
819 SilcClientEntry client_entry,
822 SilcClientFtpSession session;
823 SilcBuffer keyagr, ftp;
825 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
827 /* Get the session */
828 silc_dlist_start(conn->ftp_sessions);
829 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
830 if (session->session_id == session_id) {
835 if (session == SILC_LIST_END) {
836 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
837 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
840 /* See if we have this session running already */
841 if (session->sftp || session->listener) {
842 SILC_LOG_DEBUG(("Session already started"));
843 return SILC_CLIENT_FILE_ALREADY_STARTED;
846 session->monitor = monitor;
847 session->monitor_context = monitor_context;
848 session->client_entry = client_entry;
849 session->conn = conn;
851 /* If the hostname and port already exists then the remote client did
852 provide the connection point to us and we won't create listener, but
853 create the connection ourselves. */
854 if (session->hostname && session->port) {
855 if (silc_client_connect_to_client(client, conn, session->port,
856 session->hostname, session) < 0)
857 return SILC_CLIENT_FILE_ERROR;
859 /* Add the listener for the key agreement */
860 session->hostname = silc_net_localip();
861 session->listener = silc_net_create_server(0, session->hostname);
862 if (session->listener < 0) {
863 SILC_LOG_DEBUG(("Could not create listener"));
864 return SILC_CLIENT_FILE_ERROR;
866 session->port = silc_net_get_local_port(session->listener);
867 silc_schedule_task_add(client->schedule, session->listener,
868 silc_client_ftp_process_key_agreement, session,
869 0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
871 /* Send the key agreement inside FTP packet */
872 keyagr = silc_key_agreement_payload_encode(session->hostname,
874 ftp = silc_buffer_alloc(1 + keyagr->len);
875 silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
876 silc_buffer_format(ftp,
878 SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
880 silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
881 client_entry->id, SILC_ID_CLIENT, NULL, NULL,
882 ftp->data, ftp->len, FALSE);
884 silc_buffer_free(keyagr);
885 silc_buffer_free(ftp);
888 return SILC_CLIENT_FILE_OK;
891 /* Closes file transmission session indicated by the `session_id'.
892 If file transmission is being conducted it will be aborted
893 automatically. This function is also used to close the session
894 after successful file transmission. This function can be used
895 also to reject incoming file transmission request. */
897 SilcClientFileError silc_client_file_close(SilcClient client,
898 SilcClientConnection conn,
901 SilcClientFtpSession session;
903 SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
905 /* Get the session */
906 silc_dlist_start(conn->ftp_sessions);
907 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
908 if (session->session_id == session_id) {
913 if (session == SILC_LIST_END) {
914 SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
915 return SILC_CLIENT_FILE_UNKNOWN_SESSION;
918 silc_client_ftp_session_free(session);
920 return SILC_CLIENT_FILE_OK;
923 /* Callback called after remote client information has been resolved.
924 This will try to find existing session for the client entry. If found
925 then continue with the key agreement protocol. If not then it means
926 this is a file transfer request and we let the application know. */
928 static void silc_client_ftp_resolve_cb(SilcClient client,
929 SilcClientConnection conn,
930 SilcClientEntry *clients,
931 uint32 clients_count,
934 SilcPacketContext *packet = (SilcPacketContext *)context;
935 SilcClientFtpSession session;
936 SilcKeyAgreementPayload payload = NULL;
937 SilcClientEntry client_entry;
941 SILC_LOG_DEBUG(("Start"));
946 client_entry = clients[0];
948 silc_dlist_start(conn->ftp_sessions);
949 while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
950 if (session->client_entry == client_entry)
954 /* Parse the key agreement payload */
955 payload = silc_key_agreement_payload_parse(packet->buffer);
959 hostname = silc_key_agreement_get_hostname(payload);
960 port = silc_key_agreement_get_port(payload);
962 if (session == SILC_LIST_END) {
963 /* No session found, create one and let the application know about
964 incoming file transfer request. */
966 /* Add new session */
967 session = silc_calloc(1, sizeof(*session));
968 session->session_id = ++conn->next_session_id;
969 session->client = client;
970 session->conn = conn;
971 silc_dlist_add(conn->ftp_sessions, session);
973 /* Let the application know */
974 client->ops->ftp(client, conn, client_entry,
975 session->session_id, hostname, port);
977 if (hostname && port) {
978 session->hostname = strdup(hostname);
979 session->port = port;
988 session->hostname = strdup(hostname);
989 session->port = port;
991 /* Session exists, continue with key agreement protocol. */
992 if (silc_client_connect_to_client(client, conn, port,
993 hostname, session) < 0) {
994 /* Call monitor callback */
995 if (session->monitor)
996 (*session->monitor)(session->client, session->conn,
997 SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
998 session->client_entry, session->session_id,
999 session->filepath, session->monitor_context);
1004 silc_key_agreement_payload_free(payload);
1005 silc_packet_context_free(packet);
1008 /* Called when file transfer packet is received. This will parse the
1009 packet and give it to the file transfer protocol. */
1011 void silc_client_ftp(SilcClient client,
1012 SilcSocketConnection sock,
1013 SilcPacketContext *packet)
1015 SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1019 SILC_LOG_DEBUG(("Start"));
1021 /* Parse the payload */
1022 ret = silc_buffer_unformat(packet->buffer,
1023 SILC_STR_UI_CHAR(&type),
1028 /* We support only type number 1 (== SFTP) */
1032 silc_buffer_pull(packet->buffer, 1);
1034 /* If we have active FTP session then give the packet directly to the
1035 protocol processor. */
1036 if (conn->active_session) {
1037 /* Give it to the SFTP */
1038 if (conn->active_session->server)
1039 silc_sftp_server_receive_process(conn->active_session->sftp, sock,
1042 silc_sftp_client_receive_process(conn->active_session->sftp, sock,
1045 /* We don't have active session, resolve the remote client information
1046 and then try to find the correct session. */
1047 SilcClientID *remote_id;
1049 if (packet->src_id_type != SILC_ID_CLIENT)
1052 remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
1057 /* Resolve the client */
1058 silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1059 silc_client_ftp_resolve_cb,
1060 silc_packet_context_dup(packet));
1061 silc_free(remote_id);