1c20d625adbf8bc66d417de91b807ba05fa38e4d
[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 (!session->server) {
481     /* If we are the SFTP client then start the SFTP session and retrieve
482        the info about the file available for download. */
483     session->sftp = silc_sftp_client_start(conn->sock,
484                                            silc_client_ftp_send_packet,
485                                            session, 
486                                            silc_client_ftp_version, session);
487   } else {
488     /* Start SFTP server */
489     session->sftp = silc_sftp_server_start(conn->sock,
490                                            silc_client_ftp_send_packet,
491                                            session, session->fs);
492
493     /* Monitor transmission */
494     silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
495                                  silc_client_ftp_monitor, session);
496   }
497
498   /* Set this as active session */
499   conn->active_session = session;
500
501  out:
502   silc_ske_free_key_material(ctx->keymat);
503   if (ctx->ske)
504     silc_ske_free(ctx->ske);
505   silc_free(ctx->dest_id);
506   ctx->sock->protocol = NULL;
507   silc_socket_free(ctx->sock);
508   silc_free(ctx);
509   silc_protocol_free(protocol);
510 }
511
512 /* The downloader's function to start the key agreement protocol with the
513    remote client after we have connected to it. */
514
515 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
516                                                 int sock)
517 {
518   SilcClient client = session->client;
519   SilcClientKEInternalContext *proto_ctx;
520   SilcProtocol protocol;
521   SilcClientConnection conn;
522   void *context;
523
524   SILC_LOG_DEBUG(("Start"));
525
526   /* Call monitor callback */
527   if (session->monitor)
528     (*session->monitor)(session->client, session->conn,
529                         SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
530                         SILC_CLIENT_FILE_OK, 0, 0,
531                         session->client_entry, session->session_id,
532                         NULL, session->monitor_context);
533
534   /* Add new connection for this session */
535   conn = silc_client_add_connection(client, session->hostname,
536                                     session->port, session);
537
538   /* Allocate new socket connection object */
539   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
540   conn->sock->hostname = strdup(session->hostname);
541   conn->sock->port = silc_net_get_remote_port(sock);
542   session->sock = silc_socket_dup(conn->sock);
543
544   /* Allocate internal context for key exchange protocol. This is
545      sent as context for the protocol. */
546   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
547   proto_ctx->client = client;
548   proto_ctx->sock = silc_socket_dup(conn->sock);
549   proto_ctx->rng = client->rng;
550   proto_ctx->responder = FALSE;
551   proto_ctx->context = session;
552   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
553   proto_ctx->verify = silc_client_protocol_ke_verify_key;
554
555   /* Perform key exchange protocol. */
556   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
557                       &protocol, (void *)proto_ctx,
558                       silc_client_ftp_key_agreement_final);
559   conn->sock->protocol = protocol;
560
561   /* Register the connection for network input and output. This sets
562      that scheduler will listen for incoming packets for this connection 
563      and sets that outgoing packets may be sent to this connection as well.
564      However, this doesn't set the scheduler for outgoing traffic, it will 
565      be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
566      later when outgoing data is available. */
567   context = (void *)client;
568   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
569
570   /* Execute the protocol */
571   silc_protocol_execute(protocol, client->schedule, 0, 0);
572 }
573
574 /* The remote client's (the client who made the file available for download)
575    function for accepting incoming connection. This will also start the
576    key agreement protocol with the other client. */
577
578 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
579 {
580   SilcClientFtpSession session = (SilcClientFtpSession)context;
581   SilcClient client = session->client;
582   SilcClientConnection conn;
583   SilcSocketConnection newsocket;
584   SilcClientKEInternalContext *proto_ctx;
585   int sock;
586
587   SILC_LOG_DEBUG(("Start"));
588
589   sock = silc_net_accept_connection(session->listener);
590   if (sock < 0) {
591     /* Call monitor callback */
592     if (session->monitor)
593       (*session->monitor)(session->client, session->conn,
594                           SILC_CLIENT_FILE_MONITOR_ERROR, 
595                           SILC_CLIENT_FILE_ERROR, 0, 0,
596                           session->client_entry, session->session_id,
597                           session->filepath, session->monitor_context);
598     return;
599   }
600
601   /* Set socket options */
602   silc_net_set_socket_nonblock(sock);
603   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
604
605   /* Allocate new socket connection object */
606   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
607
608   /* Perform name and address lookups for the remote host. */
609   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
610   if (!newsocket->hostname && !newsocket->ip) {
611     /* Call monitor callback */
612     if (session->monitor)
613       (*session->monitor)(session->client, session->conn,
614                           SILC_CLIENT_FILE_MONITOR_ERROR, 
615                           SILC_CLIENT_FILE_ERROR, 0, 0,
616                           session->client_entry, session->session_id,
617                           session->filepath, session->monitor_context);
618     return;
619   }
620   if (!newsocket->hostname)
621     newsocket->hostname = strdup(newsocket->ip);
622   newsocket->port = silc_net_get_remote_port(sock);
623
624   /* Call monitor callback */
625   if (session->monitor)
626     (*session->monitor)(session->client, session->conn,
627                         SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
628                         SILC_CLIENT_FILE_OK, 0, 0,
629                         session->client_entry, session->session_id,
630                         NULL, session->monitor_context);
631
632   /* Add new connection for this session */
633   conn = silc_client_add_connection(client, newsocket->hostname,
634                                     newsocket->port, session);
635   conn->sock = newsocket;
636   conn->sock->user_data = conn;
637   session->sock = silc_socket_dup(conn->sock);
638
639   /* Allocate internal context for key exchange protocol. This is
640      sent as context for the protocol. */
641   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
642   proto_ctx->client = client;
643   proto_ctx->sock = silc_socket_dup(conn->sock);
644   proto_ctx->rng = client->rng;
645   proto_ctx->responder = TRUE;
646   proto_ctx->context = session;
647   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
648   proto_ctx->verify = silc_client_protocol_ke_verify_key;
649
650   /* Prepare the connection for key exchange protocol. We allocate the
651      protocol but will not start it yet. The connector will be the
652      initiator of the protocol thus we will wait for initiation from 
653      there before we start the protocol. */
654   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
655                       &newsocket->protocol, proto_ctx, 
656                       silc_client_ftp_key_agreement_final);
657
658   /* Register the connection for network input and output. This sets
659      that scheduler will listen for incoming packets for this connection 
660      and sets that outgoing packets may be sent to this connection as well.
661      However, this doesn't set the scheduler for outgoing traffic, it
662      will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
663      later when outgoing data is available. */
664   context = (void *)client;
665   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
666 }
667
668 /* Free all file transfer sessions. */
669
670 void silc_client_ftp_free_sessions(SilcClient client,
671                                    SilcClientConnection conn)
672 {
673   if (conn->ftp_sessions) {
674     SilcClientFtpSession session;
675     silc_dlist_start(conn->ftp_sessions);
676     while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
677       if (session->sock)
678         session->sock->user_data = NULL;
679       silc_client_ftp_session_free(session);
680     }
681     silc_dlist_del(conn->ftp_sessions, session);
682     silc_dlist_uninit(conn->ftp_sessions);
683   }
684 }
685
686 /* Free file transfer session by client entry. */
687
688 void silc_client_ftp_session_free_client(SilcClientConnection conn,
689                                          SilcClientEntry client_entry)
690 {
691   SilcClientFtpSession session;
692
693   if (!conn->ftp_sessions)
694     return;
695
696   /* Get the session */
697   silc_dlist_start(conn->ftp_sessions);
698   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
699     if (session->client_entry == client_entry) {
700       if (session->sock)
701         session->sock->user_data = NULL;
702       silc_client_ftp_session_free(session);
703     }
704   }
705 }
706
707 /* Free session resources. */
708
709 void silc_client_ftp_session_free(SilcClientFtpSession session)
710 {
711   SilcClientConnection conn;
712
713   SILC_LOG_DEBUG(("Free session"));
714
715   silc_dlist_del(session->conn->ftp_sessions, session);
716
717   if (session->sftp) {
718     if (session->server)
719       silc_sftp_server_shutdown(session->sftp);
720     else
721       silc_sftp_client_shutdown(session->sftp);
722   }
723
724   if (session->fs)
725     silc_sftp_fs_memory_free(session->fs);
726
727   /* Destroy listener */
728   if (session->listener) {
729     silc_schedule_unset_listen_fd(session->client->schedule, 
730                                   session->listener);
731     silc_net_close_connection(session->listener);
732     silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
733   }
734
735   /* Destroy session connection */
736   if (session->sock) {
737     silc_schedule_unset_listen_fd(session->client->schedule, 
738                                   session->sock->sock);
739     silc_net_close_connection(session->sock->sock);
740
741     if (session->sock->user_data) {
742       conn = (SilcClientConnection)session->sock->user_data;
743
744       if (conn->active_session == session)
745         conn->active_session = NULL;
746
747       silc_client_close_connection(session->client, session->sock, conn);
748     } else {
749       silc_socket_free(session->sock);
750     }
751   }
752
753   if (session->packet)
754     silc_buffer_free(session->packet);
755
756   silc_free(session->hostname);
757   silc_free(session->filepath);
758   silc_free(session);
759 }
760
761 /* Sends a file indicated by the `filepath' to the remote client 
762    indicated by the `client_entry'.  This will negotiate a secret key
763    with the remote client before actually starting the transmission of
764    the file.  The `monitor' callback will be called to monitor the
765    transmission of the file.
766
767    This returns a file session ID for the file transmission.  It can
768    be used to close the session (and abort the file transmission) by
769    calling the silc_client_file_close function.  The session ID is
770    also returned in the `monitor' callback. This returns 0 if the
771    file indicated by the `filepath' is being transmitted to the remote
772    client indicated by the `client_entry', already. */
773
774 uint32 silc_client_file_send(SilcClient client,
775                              SilcClientConnection conn,
776                              SilcClientFileMonitor monitor,
777                              void *monitor_context,
778                              const char *local_ip,
779                              uint32 local_port,
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   /* Create the listener for incoming key exchange protocol. */
826   if (local_ip)
827     session->hostname = strdup(local_ip);
828   else
829     session->hostname = silc_net_localip();
830   session->listener = silc_net_create_server(local_port, session->hostname);
831   if (session->listener < 0) {
832     /* Could not create listener. Do the second best thing; send empty
833        key agreement packet and let the remote client provide the point
834        for the key exchange. */
835     SILC_LOG_DEBUG(("Could not create listener"));
836     silc_free(session->hostname);
837     session->hostname = NULL;
838   } else {
839     /* Listener ready */
840     session->port = silc_net_get_local_port(session->listener);
841     silc_schedule_task_add(client->schedule, session->listener,
842                            silc_client_ftp_process_key_agreement, session,
843                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
844   }
845
846   /* Send the key agreement inside FTP packet */
847   keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
848
849   ftp = silc_buffer_alloc(1 + keyagr->len);
850   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
851   silc_buffer_format(ftp,
852                      SILC_STR_UI_CHAR(1),
853                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
854                      SILC_STR_END);
855   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
856                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
857                           ftp->data, ftp->len, FALSE);
858
859   silc_buffer_free(keyagr);
860   silc_buffer_free(ftp);
861   silc_free(path);
862
863   return session->session_id;
864 }
865
866 /* Receives a file from a client indicated by the `client_entry'.  The
867    `session_id' indicates the file transmission session and it has been
868    received in the `ftp' client operation function.  This will actually
869    perform the key agreement protocol with the remote client before
870    actually starting the file transmission.  The `monitor' callback
871    will be called to monitor the transmission. */
872
873 SilcClientFileError 
874 silc_client_file_receive(SilcClient client,
875                          SilcClientConnection conn,
876                          SilcClientFileMonitor monitor,
877                          void *monitor_context,
878                          uint32 session_id)
879 {
880   SilcClientFtpSession session;
881   SilcBuffer keyagr, ftp;
882
883   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
884
885   /* Get the session */
886   silc_dlist_start(conn->ftp_sessions);
887   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
888     if (session->session_id == session_id) {
889       break;
890     }
891   }
892
893   if (session == SILC_LIST_END) {
894     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
895     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
896   }
897
898   /* See if we have this session running already */
899   if (session->sftp || session->listener) {
900     SILC_LOG_DEBUG(("Session already started"));
901     return SILC_CLIENT_FILE_ALREADY_STARTED;
902   }
903
904   session->monitor = monitor;
905   session->monitor_context = monitor_context;
906   session->conn = conn;
907
908   /* If the hostname and port already exists then the remote client did
909      provide the connection point to us and we won't create listener, but
910      create the connection ourselves. */
911   if (session->hostname && session->port) {
912     if (silc_client_connect_to_client(client, conn, session->port, 
913                                       session->hostname, session) < 0)
914       return SILC_CLIENT_FILE_ERROR;
915   } else {
916     /* Add the listener for the key agreement */
917     session->hostname = silc_net_localip();
918     session->listener = silc_net_create_server(0, session->hostname);
919     if (session->listener < 0) {
920       SILC_LOG_DEBUG(("Could not create listener"));
921       client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
922                        "Cannot create listener on %s: %s", 
923                        session->hostname, strerror(errno));
924       return SILC_CLIENT_FILE_ERROR;
925     }
926     session->port = silc_net_get_local_port(session->listener);
927     silc_schedule_task_add(client->schedule, session->listener,
928                            silc_client_ftp_process_key_agreement, session,
929                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
930     
931     /* Send the key agreement inside FTP packet */
932     keyagr = silc_key_agreement_payload_encode(session->hostname, 
933                                                session->port);
934     ftp = silc_buffer_alloc(1 + keyagr->len);
935     silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
936     silc_buffer_format(ftp,
937                        SILC_STR_UI_CHAR(1),
938                        SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
939                        SILC_STR_END);
940     silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
941                             session->client_entry->id, 
942                             SILC_ID_CLIENT, NULL, NULL,
943                             ftp->data, ftp->len, FALSE);
944     
945     silc_buffer_free(keyagr);
946     silc_buffer_free(ftp);
947   }
948
949   return SILC_CLIENT_FILE_OK;
950 }
951
952 /* Closes file transmission session indicated by the `session_id'.
953    If file transmission is being conducted it will be aborted
954    automatically. This function is also used to close the session
955    after successful file transmission. This function can be used
956    also to reject incoming file transmission request. */
957
958 SilcClientFileError silc_client_file_close(SilcClient client,
959                                            SilcClientConnection conn,
960                                            uint32 session_id)
961 {
962   SilcClientFtpSession session;
963
964   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
965
966   /* Get the session */
967   silc_dlist_start(conn->ftp_sessions);
968   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
969     if (session->session_id == session_id) {
970       break;
971     }
972   }
973
974   if (session == SILC_LIST_END) {
975     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
976     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
977   }
978
979   silc_client_ftp_session_free(session);
980
981   return SILC_CLIENT_FILE_OK;
982 }
983
984 /* Callback called after remote client information has been resolved.
985    This will try to find existing session for the client entry.  If found
986    then continue with the key agreement protocol.  If not then it means
987    this is a file transfer request and we let the application know. */
988
989 static void silc_client_ftp_resolve_cb(SilcClient client,
990                                        SilcClientConnection conn,
991                                        SilcClientEntry *clients,
992                                        uint32 clients_count,
993                                        void *context)
994 {
995   SilcPacketContext *packet = (SilcPacketContext *)context;
996   SilcClientFtpSession session;
997   SilcKeyAgreementPayload payload = NULL;
998   SilcClientEntry client_entry;
999   char *hostname;
1000   uint16 port;
1001
1002   SILC_LOG_DEBUG(("Start"));
1003
1004   if (!clients)
1005     goto out;
1006
1007   client_entry = clients[0];
1008
1009   silc_dlist_start(conn->ftp_sessions);
1010   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1011     if (session->client_entry == client_entry)
1012       break;
1013   }
1014
1015   /* Parse the key agreement payload */
1016   payload = silc_key_agreement_payload_parse(packet->buffer->data,
1017                                              packet->buffer->len);
1018   if (!payload)
1019     goto out;
1020
1021   hostname = silc_key_agreement_get_hostname(payload);
1022   port = silc_key_agreement_get_port(payload);
1023
1024   if (session == SILC_LIST_END || (!hostname && !port)) {
1025     /* No session found, create one and let the application know about
1026        incoming file transfer request. */
1027     
1028     /* Add new session */
1029     session = silc_calloc(1, sizeof(*session));
1030     session->session_id = ++conn->next_session_id;
1031     session->client = client;
1032     session->conn = conn;
1033     session->client_entry = client_entry;
1034     silc_dlist_add(conn->ftp_sessions, session);
1035
1036     /* Let the application know */
1037     client->ops->ftp(client, conn, client_entry,
1038                      session->session_id, hostname, port);
1039
1040     if (hostname && port) {
1041       session->hostname = strdup(hostname);
1042       session->port = port;
1043     }
1044     
1045     goto out;
1046   }
1047
1048   session->hostname = strdup(hostname);
1049   session->port = port;
1050
1051   /* Session exists, continue with key agreement protocol. */
1052   if (silc_client_connect_to_client(client, conn, port, 
1053                                     hostname, session) < 0) {
1054     /* Call monitor callback */
1055     if (session->monitor)
1056       (*session->monitor)(session->client, session->conn,
1057                           SILC_CLIENT_FILE_MONITOR_ERROR, 
1058                           SILC_CLIENT_FILE_ERROR, 0, 0,
1059                           session->client_entry, session->session_id,
1060                           session->filepath, session->monitor_context);
1061   }
1062
1063  out:
1064   if (payload)
1065     silc_key_agreement_payload_free(payload);
1066   silc_packet_context_free(packet);
1067 }
1068
1069 /* Called when file transfer packet is received. This will parse the
1070    packet and give it to the file transfer protocol. */
1071
1072 void silc_client_ftp(SilcClient client,
1073                      SilcSocketConnection sock,
1074                      SilcPacketContext *packet)
1075 {
1076   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1077   uint8 type;
1078   int ret;
1079
1080   SILC_LOG_DEBUG(("Start"));
1081
1082   /* Parse the payload */
1083   ret = silc_buffer_unformat(packet->buffer,
1084                              SILC_STR_UI_CHAR(&type),
1085                              SILC_STR_END);
1086   if (ret == -1)
1087     return;
1088
1089   /* We support only type number 1 (== SFTP) */
1090   if (type != 1)
1091     return;
1092
1093   silc_buffer_pull(packet->buffer, 1);
1094
1095   /* If we have active FTP session then give the packet directly to the
1096      protocol processor. */
1097   if (conn->active_session) {
1098     /* Give it to the SFTP */
1099     if (conn->active_session->server)
1100       silc_sftp_server_receive_process(conn->active_session->sftp, sock, 
1101                                        packet);
1102     else
1103       silc_sftp_client_receive_process(conn->active_session->sftp, sock, 
1104                                        packet);
1105   } else {
1106     /* We don't have active session, resolve the remote client information
1107        and then try to find the correct session. */
1108     SilcClientID *remote_id;
1109
1110     if (packet->src_id_type != SILC_ID_CLIENT)
1111       return;
1112
1113     remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
1114                                SILC_ID_CLIENT);
1115     if (!remote_id)
1116       return;
1117
1118     /* Resolve the client */
1119     silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1120                                          silc_client_ftp_resolve_cb,
1121                                          silc_packet_context_dup(packet));
1122     silc_free(remote_id);
1123   }
1124 }