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   silc_buffer_clear(session->packet);
189 }
190
191 /* SFTP monitor callback for SFTP server. This reports the application 
192    how the transmission is going along. This function is for the client
193    who made the file available for download. */
194
195 static void silc_client_ftp_monitor(SilcSFTP sftp,
196                                     SilcSFTPMonitors type,
197                                     const SilcSFTPMonitorData data,
198                                     void *context)
199 {
200   SilcClientFtpSession session = (SilcClientFtpSession)context;
201
202   if (type == SILC_SFTP_MONITOR_READ) {
203     /* Call the monitor for application */
204     if (session->monitor)
205       (*session->monitor)(session->client, session->conn,
206                           SILC_CLIENT_FILE_MONITOR_SEND,
207                           data->offset, session->filesize,
208                           session->client_entry, session->session_id,
209                           session->filepath, session->monitor_context);
210   }
211 }
212
213 /* Returns the read data. This is the downloader's function (client side)
214    to receive the read data and read more until EOF is received from
215    the other side. This will also monitor the transmission and notify
216    the application. */
217
218 static void silc_client_ftp_data(SilcSFTP sftp,
219                                  SilcSFTPStatus status,
220                                  const unsigned char *data,
221                                  uint32 data_len,
222                                  void *context)
223 {
224   SilcClientFtpSession session = (SilcClientFtpSession)context;
225
226   SILC_LOG_DEBUG(("Start"));
227
228   if (status == SILC_SFTP_STATUS_EOF) {
229     /* EOF received */
230
231     /* Close the handle */
232     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
233     session->read_handle = NULL;
234
235     /* Close the read file descriptor */
236     silc_file_close(session->fd);
237     return;
238   }
239
240   if (status != SILC_SFTP_STATUS_OK) {
241     /* Call monitor callback */
242     if (session->monitor)
243       (*session->monitor)(session->client, session->conn,
244                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
245                           session->client_entry, session->session_id,
246                           session->filepath, session->monitor_context);
247
248     /* Close the handle */
249     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
250     session->read_handle = NULL;
251
252     /* Close the read file descriptor */
253     silc_file_close(session->fd);
254     return;
255   }
256
257   /* Read more, until EOF is received */
258   session->read_offset += data_len;
259   silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
260                  silc_client_ftp_data, session);
261
262   /* Call monitor callback */
263   if (session->monitor)
264     (*session->monitor)(session->client, session->conn,
265                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
266                         session->read_offset, session->filesize,
267                         session->client_entry, session->session_id,
268                         session->filepath, session->monitor_context);
269
270   /* Write the read data to the real file */
271   silc_file_write(session->fd, data, data_len);
272 }
273
274 /* Returns handle for the opened file. This is the downloader's function.
275    This will begin reading the data from the file. */
276
277 static void silc_client_ftp_open_handle(SilcSFTP sftp,
278                                         SilcSFTPStatus status,
279                                         SilcSFTPHandle handle,
280                                         void *context)
281 {
282   SilcClientFtpSession session = (SilcClientFtpSession)context;
283
284   SILC_LOG_DEBUG(("Start"));
285
286   if (status != SILC_SFTP_STATUS_OK) {
287     /* Call monitor callback */
288     if (session->monitor)
289       (*session->monitor)(session->client, session->conn,
290                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
291                           session->client_entry, session->session_id,
292                           session->filepath, session->monitor_context);
293     return;
294   }
295
296   /* Open the actual local file */
297   session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT);
298   if (session->fd < 0) {
299     /* Call monitor callback */
300     if (session->monitor)
301       (*session->monitor)(session->client, session->conn,
302                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
303                           session->client_entry, session->session_id,
304                           session->filepath, session->monitor_context);
305     return;
306   }
307
308   session->read_handle = handle;
309
310   /* Now, start reading the file */
311   silc_sftp_read(sftp, session->read_handle, session->read_offset, 64512,
312                  silc_client_ftp_data, session);
313
314   /* Call monitor callback */
315   if (session->monitor)
316     (*session->monitor)(session->client, session->conn,
317                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
318                         session->read_offset, session->filesize,
319                         session->client_entry, session->session_id,
320                         session->filepath, session->monitor_context);
321 }
322
323 /* Returns the file name available for download. This is the downloader's
324    function. */
325
326 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
327                                          SilcSFTPStatus status,
328                                          const SilcSFTPName name,
329                                          void *context)
330 {
331   SilcClientFtpSession session = (SilcClientFtpSession)context;
332   SilcSFTPAttributesStruct attr;
333
334   SILC_LOG_DEBUG(("Start"));
335
336   if (status != SILC_SFTP_STATUS_OK) {
337     /* Call monitor callback */
338     if (session->monitor)
339       (*session->monitor)(session->client, session->conn,
340                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
341                           session->client_entry, session->session_id,
342                           session->filepath, session->monitor_context);
343     return;
344   }
345
346   /* Now open the file */
347   memset(&attr, 0, sizeof(attr));
348   silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
349                  silc_client_ftp_open_handle, session);
350
351   /* Save the important attributes */
352   session->filepath = strdup(name->filename[0]);
353   session->filesize = name->attrs[0]->size;
354
355   /* Close the directory handle */
356   silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
357   session->dir_handle = NULL;
358 }
359
360 /* Returns the file handle after giving opendir command. This is the
361    downloader's function. */
362
363 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
364                                            SilcSFTPStatus status,
365                                            SilcSFTPHandle handle,
366                                            void *context)
367 {
368   SilcClientFtpSession session = (SilcClientFtpSession)context;
369
370   SILC_LOG_DEBUG(("Start"));
371
372   if (status != SILC_SFTP_STATUS_OK) {
373     /* Call monitor callback */
374     if (session->monitor)
375       (*session->monitor)(session->client, session->conn,
376                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
377                           session->client_entry, session->session_id,
378                           session->filepath, session->monitor_context);
379     return;
380   }
381
382   /* Now, read the directory */
383   silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
384   session->dir_handle = handle;
385 }
386
387 /* SFTP version callback for SFTP client. This is the downloader's function
388    after initializing the SFTP connection to the remote client. This will
389    find out the filename available for download. */
390
391 static void silc_client_ftp_version(SilcSFTP sftp,
392                                     SilcSFTPStatus status,
393                                     SilcSFTPVersion version,
394                                     void *context)
395 {
396   SilcClientFtpSession session = (SilcClientFtpSession)context;
397
398   SILC_LOG_DEBUG(("Start"));
399
400   if (status != SILC_SFTP_STATUS_OK) {
401     /* Call monitor callback */
402     if (session->monitor)
403       (*session->monitor)(session->client, session->conn,
404                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
405                           session->client_entry, session->session_id,
406                           session->filepath, session->monitor_context);
407     return;
408   }
409
410   /* The SFTP session is open, now retrieve the info about available file. */
411   silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
412 }
413
414 /* This callback is called after the key agreement protocol has been
415    performed. This calls the final completion callback for the application. */
416
417 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
418 {
419   SilcProtocol protocol = (SilcProtocol)context;
420   SilcClientKEInternalContext *ctx = 
421     (SilcClientKEInternalContext *)protocol->context;
422   SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
423   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
424
425   SILC_LOG_DEBUG(("Start"));
426
427   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
428       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
429     /* Error occured during protocol */
430     silc_ske_free_key_material(ctx->keymat);
431     goto out;
432   }
433
434   /* Set keys into use */
435   silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
436                                    ctx->ske->prop->cipher,
437                                    ctx->ske->prop->pkcs,
438                                    ctx->ske->prop->hash,
439                                    ctx->ske->prop->hmac,
440                                    ctx->ske->prop->group,
441                                    ctx->responder);
442
443   /* If we are the SFTP client then start the SFTP session and retrieve
444      the info about the file available for download. */
445   if (!session->server) {
446     session->sftp = silc_sftp_client_start(conn->sock,
447                                            silc_client_ftp_send_packet,
448                                            session, 
449                                            silc_client_ftp_version, session);
450   }
451
452   /* Set this as active session */
453   conn->active_session = session;
454
455  out:
456   silc_ske_free_key_material(ctx->keymat);
457   if (ctx->ske)
458     silc_ske_free(ctx->ske);
459   silc_free(ctx->dest_id);
460   ctx->sock->protocol = NULL;
461   silc_socket_free(ctx->sock);
462   silc_free(ctx);
463   silc_protocol_free(protocol);
464 }
465
466 /* The downloader's function to start the key agreement protocol with the
467    remote client after we have connected to it. */
468
469 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
470                                                 int sock)
471 {
472   SilcClient client = session->client;
473   SilcClientKEInternalContext *proto_ctx;
474   SilcProtocol protocol;
475   SilcClientConnection conn;
476   void *context;
477
478   SILC_LOG_DEBUG(("Start"));
479
480   /* Call monitor callback */
481   if (session->monitor)
482     (*session->monitor)(session->client, session->conn,
483                         SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
484                         session->client_entry, session->session_id,
485                         NULL, session->monitor_context);
486
487   /* Add new connection for this session */
488   conn = silc_client_add_connection(client, session->hostname,
489                                     session->port, session);
490
491   /* Allocate new socket connection object */
492   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
493   conn->sock->hostname = strdup(session->hostname);
494   conn->sock->port = silc_net_get_remote_port(sock);
495   session->sock = silc_socket_dup(conn->sock);
496
497   /* Allocate the SFTP */
498   if (session->server) {
499     session->sftp = silc_sftp_server_start(conn->sock,
500                                            silc_client_ftp_send_packet,
501                                            session, session->fs);
502
503     /* Monitor transmission */
504     silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
505                                  silc_client_ftp_monitor, session);
506   }
507
508   /* Allocate internal context for key exchange protocol. This is
509      sent as context for the protocol. */
510   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
511   proto_ctx->client = client;
512   proto_ctx->sock = silc_socket_dup(conn->sock);
513   proto_ctx->rng = client->rng;
514   proto_ctx->responder = FALSE;
515   proto_ctx->context = session;
516   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
517   proto_ctx->verify = silc_client_protocol_ke_verify_key;
518
519   /* Perform key exchange protocol. */
520   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
521                       &protocol, (void *)proto_ctx,
522                       silc_client_ftp_key_agreement_final);
523   conn->sock->protocol = protocol;
524
525   /* Register the connection for network input and output. This sets
526      that scheduler will listen for incoming packets for this connection 
527      and sets that outgoing packets may be sent to this connection as well.
528      However, this doesn't set the scheduler for outgoing traffic, it will 
529      be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
530      later when outgoing data is available. */
531   context = (void *)client;
532   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
533
534   /* Execute the protocol */
535   silc_protocol_execute(protocol, client->schedule, 0, 0);
536 }
537
538 /* The remote client's (the client who made the file available for download)
539    function for accepting incoming connection. This will also start the
540    key agreement protocol with the other client. */
541
542 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
543 {
544   SilcClientFtpSession session = (SilcClientFtpSession)context;
545   SilcClient client = session->client;
546   SilcClientConnection conn;
547   SilcSocketConnection newsocket;
548   SilcClientKEInternalContext *proto_ctx;
549   int sock;
550
551   SILC_LOG_DEBUG(("Start"));
552
553   sock = silc_net_accept_connection(session->listener);
554   if (sock < 0) {
555     /* Call monitor callback */
556     if (session->monitor)
557       (*session->monitor)(session->client, session->conn,
558                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
559                           session->client_entry, session->session_id,
560                           session->filepath, session->monitor_context);
561     return;
562   }
563
564   /* Set socket options */
565   silc_net_set_socket_nonblock(sock);
566   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
567
568   /* Allocate new socket connection object */
569   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
570
571   /* Perform name and address lookups for the remote host. */
572   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
573   if (!newsocket->hostname && !newsocket->ip) {
574     /* Call monitor callback */
575     if (session->monitor)
576       (*session->monitor)(session->client, session->conn,
577                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
578                           session->client_entry, session->session_id,
579                           session->filepath, session->monitor_context);
580     return;
581   }
582   if (!newsocket->hostname)
583     newsocket->hostname = strdup(newsocket->ip);
584   newsocket->port = silc_net_get_remote_port(sock);
585
586   /* Call monitor callback */
587   if (session->monitor)
588     (*session->monitor)(session->client, session->conn,
589                         SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
590                         session->client_entry, session->session_id,
591                         NULL, session->monitor_context);
592
593   /* Add new connection for this session */
594   conn = silc_client_add_connection(client, newsocket->hostname,
595                                     newsocket->port, session);
596   conn->sock = newsocket;
597   conn->sock->user_data = conn;
598   session->sock = silc_socket_dup(conn->sock);
599
600   /* Allocate internal context for key exchange protocol. This is
601      sent as context for the protocol. */
602   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
603   proto_ctx->client = client;
604   proto_ctx->sock = silc_socket_dup(conn->sock);
605   proto_ctx->rng = client->rng;
606   proto_ctx->responder = TRUE;
607   proto_ctx->context = session;
608   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
609   proto_ctx->verify = silc_client_protocol_ke_verify_key;
610
611   /* Prepare the connection for key exchange protocol. We allocate the
612      protocol but will not start it yet. The connector will be the
613      initiator of the protocol thus we will wait for initiation from 
614      there before we start the protocol. */
615   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
616                       &newsocket->protocol, proto_ctx, 
617                       silc_client_ftp_key_agreement_final);
618
619   /* Register the connection for network input and output. This sets
620      that scheduler will listen for incoming packets for this connection 
621      and sets that outgoing packets may be sent to this connection as well.
622      However, this doesn't set the scheduler for outgoing traffic, it
623      will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
624      later when outgoing data is available. */
625   context = (void *)client;
626   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
627 }
628
629 /* Free all file transfer sessions. */
630
631 void silc_client_ftp_free_sessions(SilcClient client,
632                                    SilcClientConnection conn)
633 {
634   if (conn->ftp_sessions) {
635     SilcClientFtpSession session;
636     silc_dlist_start(conn->ftp_sessions);
637     while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
638       session->sock->user_data = NULL;
639       silc_client_ftp_session_free(session);
640     }
641     silc_dlist_del(conn->ftp_sessions, session);
642     silc_dlist_uninit(conn->ftp_sessions);
643   }
644 }
645
646 /* Free file transfer session by client entry. */
647
648 void silc_client_ftp_session_free_client(SilcClientConnection conn,
649                                          SilcClientEntry client_entry)
650 {
651   SilcClientFtpSession session;
652
653   if (!conn->ftp_sessions)
654     return;
655
656   /* Get the session */
657   silc_dlist_start(conn->ftp_sessions);
658   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
659     if (session->client_entry == client_entry) {
660       session->sock->user_data = NULL;
661       silc_client_ftp_session_free(session);
662       break;
663     }
664   }
665 }
666
667 /* Free session resources. */
668
669 void silc_client_ftp_session_free(SilcClientFtpSession session)
670 {
671   SilcClientConnection conn;
672
673   SILC_LOG_DEBUG(("Free session"));
674
675   silc_dlist_del(session->conn->ftp_sessions, session);
676
677   if (session->sftp) {
678     if (session->server)
679       silc_sftp_server_shutdown(session->sftp);
680     else
681       silc_sftp_client_shutdown(session->sftp);
682   }
683
684   if (session->fs)
685     silc_sftp_fs_memory_free(session->fs);
686
687   /* Destroy listener */
688   if (session->listener) {
689     silc_schedule_unset_listen_fd(session->client->schedule, 
690                                   session->listener);
691     silc_net_close_connection(session->listener);
692     silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
693   }
694
695   /* Destroy session connection */
696   if (session->sock) {
697     silc_schedule_unset_listen_fd(session->client->schedule, 
698                                   session->sock->sock);
699     silc_net_close_connection(session->sock->sock);
700
701     if (session->sock->user_data) {
702       conn = (SilcClientConnection)session->sock->user_data;
703
704       if (conn->active_session == session)
705         conn->active_session = NULL;
706
707       silc_client_close_connection(session->client, session->sock, conn);
708     } else {
709       silc_socket_free(session->sock);
710     }
711   }
712
713   if (session->packet)
714     silc_buffer_free(session->packet);
715
716   silc_free(session->hostname);
717   silc_free(session->filepath);
718   silc_free(session);
719 }
720
721 /* Sends a file indicated by the `filepath' to the remote client 
722    indicated by the `client_entry'.  This will negotiate a secret key
723    with the remote client before actually starting the transmission of
724    the file.  The `monitor' callback will be called to monitor the
725    transmission of the file.
726
727    This returns a file session ID for the file transmission.  It can
728    be used to close the session (and abort the file transmission) by
729    calling the silc_client_file_close function.  The session ID is
730    also returned in the `monitor' callback. This returns 0 if the
731    file indicated by the `filepath' is being transmitted to the remote
732    client indicated by the `client_entry', already. */
733
734 uint32 silc_client_file_send(SilcClient client,
735                              SilcClientConnection conn,
736                              SilcClientFileMonitor monitor,
737                              void *monitor_context,
738                              SilcClientEntry client_entry,
739                              const char *filepath)
740 {
741   SilcClientFtpSession session;
742   SilcBuffer keyagr, ftp;
743   char *filename, *path;
744
745   SILC_LOG_DEBUG(("Start"));
746
747   /* Check for existing session for `filepath'. */
748   silc_dlist_start(conn->ftp_sessions);
749   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
750     if (!strcmp(session->filepath, filepath) && 
751         session->client_entry == client_entry)
752       return 0;
753   }
754
755   /* Add new session */
756   session = silc_calloc(1, sizeof(*session));
757   session->session_id = ++conn->next_session_id;
758   session->client = client;
759   session->conn = conn;
760   session->client_entry = client_entry;
761   session->monitor = monitor;
762   session->monitor_context = monitor_context;
763   session->filepath = strdup(filepath);
764   session->server = TRUE;
765   silc_dlist_add(conn->ftp_sessions, session);
766
767   path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
768   strcat(path, "file://");
769   strncat(path, filepath, strlen(filepath));
770
771   /* Allocate memory filesystem and put the file to it */
772   if (strrchr(path, '/'))
773     filename = strrchr(path, '/') + 1;
774   else
775     filename = (char *)path;
776   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
777                                           SILC_SFTP_FS_PERM_EXEC);
778   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
779                                filename, path);
780
781   session->filesize = silc_file_size(filepath);
782
783   /* Send the key agreement inside FTP packet */
784   keyagr = silc_key_agreement_payload_encode(NULL, 0);
785
786   ftp = silc_buffer_alloc(1 + keyagr->len);
787   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
788   silc_buffer_format(ftp,
789                      SILC_STR_UI_CHAR(1),
790                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
791                      SILC_STR_END);
792   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
793                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
794                           ftp->data, ftp->len, FALSE);
795
796   silc_buffer_free(keyagr);
797   silc_buffer_free(ftp);
798   silc_free(path);
799
800   return session->session_id;
801 }
802
803 /* Receives a file from a client indicated by the `client_entry'.  The
804    `session_id' indicates the file transmission session and it has been
805    received in the `ftp' client operation function.  This will actually
806    perform the key agreement protocol with the remote client before
807    actually starting the file transmission.  The `monitor' callback
808    will be called to monitor the transmission. */
809
810 SilcClientFileError 
811 silc_client_file_receive(SilcClient client,
812                          SilcClientConnection conn,
813                          SilcClientFileMonitor monitor,
814                          void *monitor_context,
815                          SilcClientEntry client_entry,
816                          uint32 session_id)
817 {
818   SilcClientFtpSession session;
819   SilcBuffer keyagr, ftp;
820
821   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
822
823   /* Get the session */
824   silc_dlist_start(conn->ftp_sessions);
825   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
826     if (session->session_id == session_id) {
827       break;
828     }
829   }
830
831   if (session == SILC_LIST_END) {
832     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
833     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
834   }
835
836   /* See if we have this session running already */
837   if (session->sftp || session->listener) {
838     SILC_LOG_DEBUG(("Session already started"));
839     return SILC_CLIENT_FILE_ALREADY_STARTED;
840   }
841
842   session->monitor = monitor;
843   session->monitor_context = monitor_context;
844   session->client_entry = client_entry;
845   session->conn = conn;
846
847   /* If the hostname and port already exists then the remote client did
848      provide the connection point to us and we won't create listener, but
849      create the connection ourselves. */
850   if (session->hostname && session->port) {
851     if (silc_client_connect_to_client(client, conn, session->port, 
852                                       session->hostname, session) < 0)
853       return SILC_CLIENT_FILE_ERROR;
854   } else {
855     /* Add the listener for the key agreement */
856     session->hostname = silc_net_localip();
857     session->listener = silc_net_create_server(0, session->hostname);
858     if (session->listener < 0) {
859       SILC_LOG_DEBUG(("Could not create listener"));
860       return SILC_CLIENT_FILE_ERROR;
861     }
862     session->port = silc_net_get_local_port(session->listener);
863     silc_schedule_task_add(client->schedule, session->listener,
864                            silc_client_ftp_process_key_agreement, session,
865                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
866     
867     /* Send the key agreement inside FTP packet */
868     keyagr = silc_key_agreement_payload_encode(session->hostname, 
869                                                session->port);
870     ftp = silc_buffer_alloc(1 + keyagr->len);
871     silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
872     silc_buffer_format(ftp,
873                        SILC_STR_UI_CHAR(1),
874                        SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
875                        SILC_STR_END);
876     silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
877                             client_entry->id, SILC_ID_CLIENT, NULL, NULL,
878                             ftp->data, ftp->len, FALSE);
879     
880     silc_buffer_free(keyagr);
881     silc_buffer_free(ftp);
882   }
883
884   return SILC_CLIENT_FILE_OK;
885 }
886
887 /* Closes file transmission session indicated by the `session_id'.
888    If file transmission is being conducted it will be aborted
889    automatically. This function is also used to close the session
890    after successful file transmission. This function can be used
891    also to reject incoming file transmission request. */
892
893 SilcClientFileError silc_client_file_close(SilcClient client,
894                                            SilcClientConnection conn,
895                                            uint32 session_id)
896 {
897   SilcClientFtpSession session;
898
899   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
900
901   /* Get the session */
902   silc_dlist_start(conn->ftp_sessions);
903   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
904     if (session->session_id == session_id) {
905       break;
906     }
907   }
908
909   if (session == SILC_LIST_END) {
910     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
911     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
912   }
913
914   silc_client_ftp_session_free(session);
915
916   return SILC_CLIENT_FILE_OK;
917 }
918
919 /* Callback called after remote client information has been resolved.
920    This will try to find existing session for the client entry.  If found
921    then continue with the key agreement protocol.  If not then it means
922    this is a file transfer request and we let the application know. */
923
924 static void silc_client_ftp_resolve_cb(SilcClient client,
925                                        SilcClientConnection conn,
926                                        SilcClientEntry *clients,
927                                        uint32 clients_count,
928                                        void *context)
929 {
930   SilcPacketContext *packet = (SilcPacketContext *)context;
931   SilcClientFtpSession session;
932   SilcKeyAgreementPayload payload = NULL;
933   SilcClientEntry client_entry;
934   char *hostname;
935   uint16 port;
936
937   SILC_LOG_DEBUG(("Start"));
938
939   if (!clients)
940     goto out;
941
942   client_entry = clients[0];
943
944   silc_dlist_start(conn->ftp_sessions);
945   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
946     if (session->client_entry == client_entry)
947       break;
948   }
949
950   /* Parse the key agreement payload */
951   payload = silc_key_agreement_payload_parse(packet->buffer);
952   if (!payload)
953     goto out;
954
955   hostname = silc_key_agreement_get_hostname(payload);
956   port = silc_key_agreement_get_port(payload);
957
958   if (session == SILC_LIST_END) {
959     /* No session found, create one and let the application know about
960        incoming file transfer request. */
961     
962     /* Add new session */
963     session = silc_calloc(1, sizeof(*session));
964     session->session_id = ++conn->next_session_id;
965     session->client = client;
966     session->conn = conn;
967     silc_dlist_add(conn->ftp_sessions, session);
968
969     /* Let the application know */
970     client->ops->ftp(client, conn, client_entry,
971                      session->session_id, hostname, port);
972
973     if (hostname && port) {
974       session->hostname = strdup(hostname);
975       session->port = port;
976     }
977     
978     goto out;
979   }
980
981   if (!hostname)
982     goto out;
983
984   session->hostname = strdup(hostname);
985   session->port = port;
986
987   /* Session exists, continue with key agreement protocol. */
988   if (silc_client_connect_to_client(client, conn, port, 
989                                     hostname, session) < 0) {
990     /* Call monitor callback */
991     if (session->monitor)
992       (*session->monitor)(session->client, session->conn,
993                           SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
994                           session->client_entry, session->session_id,
995                           session->filepath, session->monitor_context);
996   }
997
998  out:
999   if (payload)
1000     silc_key_agreement_payload_free(payload);
1001   silc_packet_context_free(packet);
1002 }
1003
1004 /* Called when file transfer packet is received. This will parse the
1005    packet and give it to the file transfer protocol. */
1006
1007 void silc_client_ftp(SilcClient client,
1008                      SilcSocketConnection sock,
1009                      SilcPacketContext *packet)
1010 {
1011   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1012   uint8 type;
1013   int ret;
1014
1015   SILC_LOG_DEBUG(("Start"));
1016
1017   /* Parse the payload */
1018   ret = silc_buffer_unformat(packet->buffer,
1019                              SILC_STR_UI_CHAR(&type),
1020                              SILC_STR_END);
1021   if (ret == -1)
1022     return;
1023
1024   /* We support only type number 1 (== SFTP) */
1025   if (type != 1)
1026     return;
1027
1028   silc_buffer_pull(packet->buffer, 1);
1029
1030   /* If we have active FTP session then give the packet directly to the
1031      protocol processor. */
1032   if (conn->active_session) {
1033     /* Give it to the SFTP */
1034     if (conn->active_session->server)
1035       silc_sftp_server_receive_process(conn->active_session->sftp, sock, 
1036                                        packet);
1037     else
1038       silc_sftp_client_receive_process(conn->active_session->sftp, sock, 
1039                                        packet);
1040   } else {
1041     /* We don't have active session, resolve the remote client information
1042        and then try to find the correct session. */
1043     SilcClientID *remote_id;
1044
1045     if (packet->src_id_type != SILC_ID_CLIENT)
1046       return;
1047
1048     remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
1049                                SILC_ID_CLIENT);
1050     if (!remote_id)
1051       return;
1052
1053     /* Resolve the client */
1054     silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1055                                          silc_client_ftp_resolve_cb,
1056                                          silc_packet_context_dup(packet));
1057     silc_free(remote_id);
1058   }
1059 }