updates.
[silc.git] / lib / silcclient / client_ftp.c
1 /*
2
3   client_ftp.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 Pekka Riikonen
8
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.
12
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.
17
18 */
19 /* $Id$ */
20
21 #include "clientlibincludes.h"
22 #include "client_internal.h"
23
24 static int
25 silc_client_connect_to_client(SilcClient client, 
26                               SilcClientConnection conn, int port,
27                               char *host, void *context);
28 static int 
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,
32                                                 int sock);
33
34 /* File transmission session */
35 struct SilcClientFtpSessionStruct {
36   uint32 session_id;
37   SilcClient client;
38   SilcClientConnection conn;
39   SilcClientEntry client_entry;
40
41   SilcSocketConnection sock;
42   SilcBuffer packet;
43
44   char *hostname;
45   uint16 port;
46   int listener;
47
48   SilcClientFileMonitor monitor;
49   void *monitor_context;
50   char *filepath;
51
52   SilcSFTP sftp;
53   SilcSFTPFilesystem fs;
54   bool server;
55
56   SilcSFTPHandle dir_handle;
57   SilcSFTPHandle read_handle;
58   uint64 filesize;
59   uint64 read_offset;
60   int fd;
61 };
62
63 SILC_TASK_CALLBACK(silc_client_ftp_connected)
64 {
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);
71
72   SILC_LOG_DEBUG(("Start"));
73
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);
76   if (opt != 0) {
77     if (ctx->tries < 2) {
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);
85
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);
90
91       /* Try again */
92       silc_client_connect_to_client_internal(ctx);
93       ctx->tries++;
94     } else {
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);
102       silc_free(ctx);
103       silc_client_ftp_session_free(session);
104     }
105     return;
106   }
107
108   silc_schedule_unset_listen_fd(client->schedule, fd);
109   silc_schedule_task_del(client->schedule, ctx->task);
110
111   /* Start the key agreement */
112   silc_client_ftp_start_key_agreement(session, fd);
113 }
114
115 static int 
116 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
117 {
118   int sock;
119
120   /* Create connection to server asynchronously */
121   sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
122   if (sock < 0)
123     return -1;
124
125   /* Register task that will receive the async connect and will
126      read the result. */
127   ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, 
128                                      silc_client_ftp_connected,
129                                      (void *)ctx, 0, 0, 
130                                      SILC_TASK_FD,
131                                      SILC_TASK_PRI_NORMAL);
132   silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
133   ctx->sock = sock;
134   return sock;
135 }
136
137 static int
138 silc_client_connect_to_client(SilcClient client, 
139                               SilcClientConnection conn, int port,
140                               char *host, void *context)
141 {
142   SilcClientInternalConnectContext *ctx;
143
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;
148   ctx->conn = conn;
149   ctx->host = strdup(host);
150   ctx->port = port;
151   ctx->tries = 0;
152   ctx->context = context;
153
154   /* Do the actual connecting process */
155   return silc_client_connect_to_client_internal(ctx);
156 }
157
158 /* SFTP packet send callback. This will use preallocated buffer to avoid
159    reallocation of outgoing data buffer everytime. */
160
161 static void silc_client_ftp_send_packet(SilcSocketConnection sock,
162                                         SilcBuffer packet, void *context)
163 {
164   SilcClientFtpSession session = (SilcClientFtpSession)context;
165   SilcClient client = session->client;
166
167   SILC_LOG_DEBUG(("Start"));
168
169   /* Allocate outgoing packet */
170   if (!session->packet)
171     session->packet = silc_buffer_alloc(1 + packet->len);
172
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);
176
177   /* Encode packet */
178   silc_buffer_pull_tail(session->packet, 1 + packet->len);
179   silc_buffer_format(session->packet,
180                      SILC_STR_UI_CHAR(1),
181                      SILC_STR_UI_XNSTRING(packet->data, packet->len),
182                      SILC_STR_END);
183
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);
187
188   /* Clear buffer */
189   session->packet->data = session->packet->tail = session->packet->head;
190   session->packet->len = 0;
191 }
192
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. */
196
197 static void silc_client_ftp_monitor(SilcSFTP sftp,
198                                     SilcSFTPMonitors type,
199                                     const SilcSFTPMonitorData data,
200                                     void *context)
201 {
202   SilcClientFtpSession session = (SilcClientFtpSession)context;
203
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);
212   }
213 }
214
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
218    the application. */
219
220 static void silc_client_ftp_data(SilcSFTP sftp,
221                                  SilcSFTPStatus status,
222                                  const unsigned char *data,
223                                  uint32 data_len,
224                                  void *context)
225 {
226   SilcClientFtpSession session = (SilcClientFtpSession)context;
227
228   SILC_LOG_DEBUG(("Start"));
229
230   if (status == SILC_SFTP_STATUS_EOF) {
231     /* EOF received */
232
233     /* Close the handle */
234     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
235     session->read_handle = NULL;
236
237     /* Close the read file descriptor */
238     silc_file_close(session->fd);
239     return;
240   }
241
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);
249
250     /* Close the handle */
251     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
252     session->read_handle = NULL;
253
254     /* Close the read file descriptor */
255     silc_file_close(session->fd);
256     return;
257   }
258
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);
263
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);
271
272   /* Write the read data to the real file */
273   silc_file_write(session->fd, data, data_len);
274 }
275
276 /* Returns handle for the opened file. This is the downloader's function.
277    This will begin reading the data from the file. */
278
279 static void silc_client_ftp_open_handle(SilcSFTP sftp,
280                                         SilcSFTPStatus status,
281                                         SilcSFTPHandle handle,
282                                         void *context)
283 {
284   SilcClientFtpSession session = (SilcClientFtpSession)context;
285
286   SILC_LOG_DEBUG(("Start"));
287
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);
295     return;
296   }
297
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);
307     return;
308   }
309
310   session->read_handle = handle;
311
312   /* Now, start reading the file */
313   silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
314                  silc_client_ftp_data, session);
315
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);
323 }
324
325 /* Returns the file name available for download. This is the downloader's
326    function. */
327
328 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
329                                          SilcSFTPStatus status,
330                                          const SilcSFTPName name,
331                                          void *context)
332 {
333   SilcClientFtpSession session = (SilcClientFtpSession)context;
334   SilcSFTPAttributesStruct attr;
335
336   SILC_LOG_DEBUG(("Start"));
337
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);
345     return;
346   }
347
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);
352
353   /* Save the important attributes */
354   session->filepath = strdup(name->filename[0]);
355   session->filesize = name->attrs[0]->size;
356
357   /* Close the directory handle */
358   silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
359   session->dir_handle = NULL;
360 }
361
362 /* Returns the file handle after giving opendir command. This is the
363    downloader's function. */
364
365 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
366                                            SilcSFTPStatus status,
367                                            SilcSFTPHandle handle,
368                                            void *context)
369 {
370   SilcClientFtpSession session = (SilcClientFtpSession)context;
371
372   SILC_LOG_DEBUG(("Start"));
373
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);
381     return;
382   }
383
384   /* Now, read the directory */
385   silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
386   session->dir_handle = handle;
387 }
388
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. */
392
393 static void silc_client_ftp_version(SilcSFTP sftp,
394                                     SilcSFTPStatus status,
395                                     SilcSFTPVersion version,
396                                     void *context)
397 {
398   SilcClientFtpSession session = (SilcClientFtpSession)context;
399
400   SILC_LOG_DEBUG(("Start"));
401
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);
409     return;
410   }
411
412   /* The SFTP session is open, now retrieve the info about available file. */
413   silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
414 }
415
416 /* This callback is called after the key agreement protocol has been
417    performed. This calls the final completion callback for the application. */
418
419 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
420 {
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;
426
427   SILC_LOG_DEBUG(("Start"));
428
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);
433     goto out;
434   }
435
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,
443                                    ctx->responder);
444
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,
450                                            session, 
451                                            silc_client_ftp_version, session);
452   }
453
454   /* Set this as active session */
455   conn->active_session = session;
456
457  out:
458   silc_ske_free_key_material(ctx->keymat);
459   if (ctx->ske)
460     silc_ske_free(ctx->ske);
461   silc_free(ctx->dest_id);
462   ctx->sock->protocol = NULL;
463   silc_socket_free(ctx->sock);
464   silc_free(ctx);
465   silc_protocol_free(protocol);
466 }
467
468 /* The downloader's function to start the key agreement protocol with the
469    remote client after we have connected to it. */
470
471 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
472                                                 int sock)
473 {
474   SilcClient client = session->client;
475   SilcClientKEInternalContext *proto_ctx;
476   SilcProtocol protocol;
477   SilcClientConnection conn;
478   void *context;
479
480   SILC_LOG_DEBUG(("Start"));
481
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);
488
489   /* Add new connection for this session */
490   conn = silc_client_add_connection(client, session->hostname,
491                                     session->port, session);
492
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);
498
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);
504
505     /* Monitor transmission */
506     silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
507                                  silc_client_ftp_monitor, session);
508   }
509
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;
520
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;
526
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);
535
536   /* Execute the protocol */
537   silc_protocol_execute(protocol, client->schedule, 0, 0);
538 }
539
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. */
543
544 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
545 {
546   SilcClientFtpSession session = (SilcClientFtpSession)context;
547   SilcClient client = session->client;
548   SilcClientConnection conn;
549   SilcSocketConnection newsocket;
550   SilcClientKEInternalContext *proto_ctx;
551   int sock;
552
553   SILC_LOG_DEBUG(("Start"));
554
555   sock = silc_net_accept_connection(session->listener);
556   if (sock < 0) {
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);
563     return;
564   }
565
566   /* Set socket options */
567   silc_net_set_socket_nonblock(sock);
568   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
569
570   /* Allocate new socket connection object */
571   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
572
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);
582     return;
583   }
584   if (!newsocket->hostname)
585     newsocket->hostname = strdup(newsocket->ip);
586   newsocket->port = silc_net_get_remote_port(sock);
587
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);
594
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);
601
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;
612
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);
620
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);
629 }
630
631 /* Free all file transfer sessions. */
632
633 void silc_client_ftp_free_sessions(SilcClient client,
634                                    SilcClientConnection conn)
635 {
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) {
640       if (session->sock)
641         session->sock->user_data = NULL;
642       silc_client_ftp_session_free(session);
643     }
644     silc_dlist_del(conn->ftp_sessions, session);
645     silc_dlist_uninit(conn->ftp_sessions);
646   }
647 }
648
649 /* Free file transfer session by client entry. */
650
651 void silc_client_ftp_session_free_client(SilcClientConnection conn,
652                                          SilcClientEntry client_entry)
653 {
654   SilcClientFtpSession session;
655
656   if (!conn->ftp_sessions)
657     return;
658
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) {
663       if (session->sock)
664         session->sock->user_data = NULL;
665       silc_client_ftp_session_free(session);
666       break;
667     }
668   }
669 }
670
671 /* Free session resources. */
672
673 void silc_client_ftp_session_free(SilcClientFtpSession session)
674 {
675   SilcClientConnection conn;
676
677   SILC_LOG_DEBUG(("Free session"));
678
679   silc_dlist_del(session->conn->ftp_sessions, session);
680
681   if (session->sftp) {
682     if (session->server)
683       silc_sftp_server_shutdown(session->sftp);
684     else
685       silc_sftp_client_shutdown(session->sftp);
686   }
687
688   if (session->fs)
689     silc_sftp_fs_memory_free(session->fs);
690
691   /* Destroy listener */
692   if (session->listener) {
693     silc_schedule_unset_listen_fd(session->client->schedule, 
694                                   session->listener);
695     silc_net_close_connection(session->listener);
696     silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
697   }
698
699   /* Destroy session connection */
700   if (session->sock) {
701     silc_schedule_unset_listen_fd(session->client->schedule, 
702                                   session->sock->sock);
703     silc_net_close_connection(session->sock->sock);
704
705     if (session->sock->user_data) {
706       conn = (SilcClientConnection)session->sock->user_data;
707
708       if (conn->active_session == session)
709         conn->active_session = NULL;
710
711       silc_client_close_connection(session->client, session->sock, conn);
712     } else {
713       silc_socket_free(session->sock);
714     }
715   }
716
717   if (session->packet)
718     silc_buffer_free(session->packet);
719
720   silc_free(session->hostname);
721   silc_free(session->filepath);
722   silc_free(session);
723 }
724
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.
730
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. */
737
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)
744 {
745   SilcClientFtpSession session;
746   SilcBuffer keyagr, ftp;
747   char *filename, *path;
748
749   SILC_LOG_DEBUG(("Start"));
750
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)
756       return 0;
757   }
758
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);
770
771   path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
772   strcat(path, "file://");
773   strncat(path, filepath, strlen(filepath));
774
775   /* Allocate memory filesystem and put the file to it */
776   if (strrchr(path, '/'))
777     filename = strrchr(path, '/') + 1;
778   else
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,
783                                filename, path);
784
785   session->filesize = silc_file_size(filepath);
786
787   /* Send the key agreement inside FTP packet */
788   keyagr = silc_key_agreement_payload_encode(NULL, 0);
789
790   ftp = silc_buffer_alloc(1 + keyagr->len);
791   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
792   silc_buffer_format(ftp,
793                      SILC_STR_UI_CHAR(1),
794                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
795                      SILC_STR_END);
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);
799
800   silc_buffer_free(keyagr);
801   silc_buffer_free(ftp);
802   silc_free(path);
803
804   return session->session_id;
805 }
806
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. */
813
814 SilcClientFileError 
815 silc_client_file_receive(SilcClient client,
816                          SilcClientConnection conn,
817                          SilcClientFileMonitor monitor,
818                          void *monitor_context,
819                          SilcClientEntry client_entry,
820                          uint32 session_id)
821 {
822   SilcClientFtpSession session;
823   SilcBuffer keyagr, ftp;
824
825   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
826
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) {
831       break;
832     }
833   }
834
835   if (session == SILC_LIST_END) {
836     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
837     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
838   }
839
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;
844   }
845
846   session->monitor = monitor;
847   session->monitor_context = monitor_context;
848   session->client_entry = client_entry;
849   session->conn = conn;
850
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;
858   } else {
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;
865     }
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);
870     
871     /* Send the key agreement inside FTP packet */
872     keyagr = silc_key_agreement_payload_encode(session->hostname, 
873                                                session->port);
874     ftp = silc_buffer_alloc(1 + keyagr->len);
875     silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
876     silc_buffer_format(ftp,
877                        SILC_STR_UI_CHAR(1),
878                        SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
879                        SILC_STR_END);
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);
883     
884     silc_buffer_free(keyagr);
885     silc_buffer_free(ftp);
886   }
887
888   return SILC_CLIENT_FILE_OK;
889 }
890
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. */
896
897 SilcClientFileError silc_client_file_close(SilcClient client,
898                                            SilcClientConnection conn,
899                                            uint32 session_id)
900 {
901   SilcClientFtpSession session;
902
903   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
904
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) {
909       break;
910     }
911   }
912
913   if (session == SILC_LIST_END) {
914     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
915     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
916   }
917
918   silc_client_ftp_session_free(session);
919
920   return SILC_CLIENT_FILE_OK;
921 }
922
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. */
927
928 static void silc_client_ftp_resolve_cb(SilcClient client,
929                                        SilcClientConnection conn,
930                                        SilcClientEntry *clients,
931                                        uint32 clients_count,
932                                        void *context)
933 {
934   SilcPacketContext *packet = (SilcPacketContext *)context;
935   SilcClientFtpSession session;
936   SilcKeyAgreementPayload payload = NULL;
937   SilcClientEntry client_entry;
938   char *hostname;
939   uint16 port;
940
941   SILC_LOG_DEBUG(("Start"));
942
943   if (!clients)
944     goto out;
945
946   client_entry = clients[0];
947
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)
951       break;
952   }
953
954   /* Parse the key agreement payload */
955   payload = silc_key_agreement_payload_parse(packet->buffer);
956   if (!payload)
957     goto out;
958
959   hostname = silc_key_agreement_get_hostname(payload);
960   port = silc_key_agreement_get_port(payload);
961
962   if (session == SILC_LIST_END) {
963     /* No session found, create one and let the application know about
964        incoming file transfer request. */
965     
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);
972
973     /* Let the application know */
974     client->ops->ftp(client, conn, client_entry,
975                      session->session_id, hostname, port);
976
977     if (hostname && port) {
978       session->hostname = strdup(hostname);
979       session->port = port;
980     }
981     
982     goto out;
983   }
984
985   if (!hostname)
986     goto out;
987
988   session->hostname = strdup(hostname);
989   session->port = port;
990
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);
1000   }
1001
1002  out:
1003   if (payload)
1004     silc_key_agreement_payload_free(payload);
1005   silc_packet_context_free(packet);
1006 }
1007
1008 /* Called when file transfer packet is received. This will parse the
1009    packet and give it to the file transfer protocol. */
1010
1011 void silc_client_ftp(SilcClient client,
1012                      SilcSocketConnection sock,
1013                      SilcPacketContext *packet)
1014 {
1015   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1016   uint8 type;
1017   int ret;
1018
1019   SILC_LOG_DEBUG(("Start"));
1020
1021   /* Parse the payload */
1022   ret = silc_buffer_unformat(packet->buffer,
1023                              SILC_STR_UI_CHAR(&type),
1024                              SILC_STR_END);
1025   if (ret == -1)
1026     return;
1027
1028   /* We support only type number 1 (== SFTP) */
1029   if (type != 1)
1030     return;
1031
1032   silc_buffer_pull(packet->buffer, 1);
1033
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, 
1040                                        packet);
1041     else
1042       silc_sftp_client_receive_process(conn->active_session->sftp, sock, 
1043                                        packet);
1044   } else {
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;
1048
1049     if (packet->src_id_type != SILC_ID_CLIENT)
1050       return;
1051
1052     remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
1053                                SILC_ID_CLIENT);
1054     if (!remote_id)
1055       return;
1056
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);
1062   }
1063 }