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