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