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