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