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