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