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);
773 }
774
775 /* Sends a file indicated by the `filepath' to the remote client 
776    indicated by the `client_entry'.  This will negotiate a secret key
777    with the remote client before actually starting the transmission of
778    the file.  The `monitor' callback will be called to monitor the
779    transmission of the file. */
780
781 SilcClientFileError 
782 silc_client_file_send(SilcClient client,
783                       SilcClientConnection conn,
784                       SilcClientFileMonitor monitor,
785                       void *monitor_context,
786                       const char *local_ip,
787                       SilcUInt32 local_port,
788                       SilcClientEntry client_entry,
789                       const char *filepath,
790                       SilcUInt32 *session_id)
791 {
792   SilcClientFtpSession session;
793   SilcBuffer keyagr, ftp;
794   char *filename, *path;
795   int fd;
796
797   SILC_LOG_DEBUG(("Start"));
798
799   /* Check for existing session for `filepath'. */
800   silc_dlist_start(conn->ftp_sessions);
801   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
802     if (session->filepath && !strcmp(session->filepath, filepath) && 
803         session->client_entry == client_entry)
804       return SILC_CLIENT_FILE_ALREADY_STARTED;
805   }
806
807   /* See whether the file exists, and can be opened in generally speaking */
808   fd = silc_file_open(filepath, O_RDONLY);
809   if (fd < 0)
810     return SILC_CLIENT_FILE_NO_SUCH_FILE;
811   silc_file_close(fd);
812
813   /* Add new session */
814   session = silc_calloc(1, sizeof(*session));
815   session->session_id = ++conn->next_session_id;
816   session->client = client;
817   session->conn = conn;
818   session->client_entry = client_entry;
819   session->monitor = monitor;
820   session->monitor_context = monitor_context;
821   session->filepath = strdup(filepath);
822   session->server = TRUE;
823   silc_dlist_add(conn->ftp_sessions, session);
824
825   path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
826   strcat(path, "file://");
827   strncat(path, filepath, strlen(filepath));
828
829   /* Allocate memory filesystem and put the file to it */
830   if (strrchr(path, '/'))
831     filename = strrchr(path, '/') + 1;
832   else
833     filename = (char *)path;
834   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
835                                           SILC_SFTP_FS_PERM_EXEC);
836   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
837                                filename, path);
838
839   session->filesize = silc_file_size(filepath);
840
841   /* Create the listener for incoming key exchange protocol. */
842   if (local_ip)
843     session->hostname = strdup(local_ip);
844   else
845     session->hostname = silc_net_localip();
846   session->listener = silc_net_create_server(local_port, session->hostname);
847   if (session->listener < 0) {
848     /* Could not create listener. Do the second best thing; send empty
849        key agreement packet and let the remote client provide the point
850        for the key exchange. */
851     SILC_LOG_DEBUG(("Could not create listener"));
852     silc_free(session->hostname);
853     session->hostname = NULL;
854     session->port = 0;
855   } else {
856     /* Listener ready */
857     session->port = silc_net_get_local_port(session->listener);
858     silc_schedule_task_add(client->schedule, session->listener,
859                            silc_client_ftp_process_key_agreement, session,
860                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
861   }
862
863   /* Send the key agreement inside FTP packet */
864   keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
865
866   ftp = silc_buffer_alloc(1 + keyagr->len);
867   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
868   silc_buffer_format(ftp,
869                      SILC_STR_UI_CHAR(1),
870                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
871                      SILC_STR_END);
872   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
873                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
874                           ftp->data, ftp->len, FALSE);
875
876   silc_buffer_free(keyagr);
877   silc_buffer_free(ftp);
878   silc_free(path);
879
880   if (session_id)
881     *session_id = session->session_id;
882
883   return SILC_CLIENT_FILE_OK;
884 }
885
886 /* Receives a file from a client indicated by the `client_entry'.  The
887    `session_id' indicates the file transmission session and it has been
888    received in the `ftp' client operation function.  This will actually
889    perform the key agreement protocol with the remote client before
890    actually starting the file transmission.  The `monitor' callback
891    will be called to monitor the transmission. */
892
893 SilcClientFileError 
894 silc_client_file_receive(SilcClient client,
895                          SilcClientConnection conn,
896                          SilcClientFileMonitor monitor,
897                          void *monitor_context,
898                          const char *path,
899                          SilcUInt32 session_id)
900 {
901   SilcClientFtpSession session;
902   SilcBuffer keyagr, ftp;
903
904   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
905
906   /* Get the session */
907   silc_dlist_start(conn->ftp_sessions);
908   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
909     if (session->session_id == session_id) {
910       break;
911     }
912   }
913
914   if (session == SILC_LIST_END) {
915     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
916     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
917   }
918
919   /* See if we have this session running already */
920   if (session->sftp || session->listener) {
921     SILC_LOG_DEBUG(("Session already started"));
922     return SILC_CLIENT_FILE_ALREADY_STARTED;
923   }
924
925   session->monitor = monitor;
926   session->monitor_context = monitor_context;
927   session->conn = conn;
928   session->path = path ? strdup(path) : NULL;
929
930   /* If the hostname and port already exists then the remote client did
931      provide the connection point to us and we won't create listener, but
932      create the connection ourselves. */
933   if (session->hostname && session->port) {
934     if (silc_client_connect_to_client(client, conn, session->port, 
935                                       session->hostname, session) < 0)
936       return SILC_CLIENT_FILE_ERROR;
937   } else {
938     /* Add the listener for the key agreement */
939     session->hostname = silc_net_localip();
940     session->listener = silc_net_create_server(0, session->hostname);
941     if (session->listener < 0) {
942       SILC_LOG_DEBUG(("Could not create listener"));
943       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
944                                  "Cannot create listener on %s: %s", 
945                                  session->hostname, strerror(errno));
946       return SILC_CLIENT_FILE_ERROR;
947     }
948     session->port = silc_net_get_local_port(session->listener);
949     silc_schedule_task_add(client->schedule, session->listener,
950                            silc_client_ftp_process_key_agreement, session,
951                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
952     
953     /* Send the key agreement inside FTP packet */
954     keyagr = silc_key_agreement_payload_encode(session->hostname, 
955                                                session->port);
956     ftp = silc_buffer_alloc(1 + keyagr->len);
957     silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
958     silc_buffer_format(ftp,
959                        SILC_STR_UI_CHAR(1),
960                        SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
961                        SILC_STR_END);
962     silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
963                             session->client_entry->id, 
964                             SILC_ID_CLIENT, NULL, NULL,
965                             ftp->data, ftp->len, FALSE);
966     
967     silc_buffer_free(keyagr);
968     silc_buffer_free(ftp);
969   }
970
971   return SILC_CLIENT_FILE_OK;
972 }
973
974 /* Closes file transmission session indicated by the `session_id'.
975    If file transmission is being conducted it will be aborted
976    automatically. This function is also used to close the session
977    after successful file transmission. This function can be used
978    also to reject incoming file transmission request. */
979
980 SilcClientFileError silc_client_file_close(SilcClient client,
981                                            SilcClientConnection conn,
982                                            SilcUInt32 session_id)
983 {
984   SilcClientFtpSession session;
985
986   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
987
988   /* Get the session */
989   silc_dlist_start(conn->ftp_sessions);
990   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
991     if (session->session_id == session_id) {
992       break;
993     }
994   }
995
996   if (session == SILC_LIST_END) {
997     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
998     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
999   }
1000
1001   silc_client_ftp_session_free(session);
1002
1003   return SILC_CLIENT_FILE_OK;
1004 }
1005
1006 /* Callback called after remote client information has been resolved.
1007    This will try to find existing session for the client entry.  If found
1008    then continue with the key agreement protocol.  If not then it means
1009    this is a file transfer request and we let the application know. */
1010
1011 static void silc_client_ftp_resolve_cb(SilcClient client,
1012                                        SilcClientConnection conn,
1013                                        SilcClientEntry *clients,
1014                                        SilcUInt32 clients_count,
1015                                        void *context)
1016 {
1017   SilcPacketContext *packet = (SilcPacketContext *)context;
1018   SilcClientFtpSession session;
1019   SilcKeyAgreementPayload payload = NULL;
1020   SilcClientEntry client_entry;
1021   char *hostname;
1022   SilcUInt16 port;
1023
1024   SILC_LOG_DEBUG(("Start"));
1025
1026   if (!clients)
1027     goto out;
1028
1029   client_entry = clients[0];
1030
1031   silc_dlist_start(conn->ftp_sessions);
1032   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1033     if (session->client_entry == client_entry)
1034       break;
1035   }
1036
1037   /* Parse the key agreement payload */
1038   payload = silc_key_agreement_payload_parse(packet->buffer->data,
1039                                              packet->buffer->len);
1040   if (!payload)
1041     goto out;
1042
1043   hostname = silc_key_agreement_get_hostname(payload);
1044   port = silc_key_agreement_get_port(payload);
1045   if (!hostname)
1046     port = 0;
1047   if (!port)
1048     hostname = NULL;
1049
1050   if (session == SILC_LIST_END || (!hostname && !port)) {
1051     /* No session found, create one and let the application know about
1052        incoming file transfer request. */
1053     
1054     /* Add new session */
1055     session = silc_calloc(1, sizeof(*session));
1056     session->session_id = ++conn->next_session_id;
1057     session->client = client;
1058     session->conn = conn;
1059     session->client_entry = client_entry;
1060     silc_dlist_add(conn->ftp_sessions, session);
1061
1062     /* Let the application know */
1063     client->internal->ops->ftp(client, conn, client_entry,
1064                                session->session_id, hostname, port);
1065
1066     if (hostname && port) {
1067       session->hostname = strdup(hostname);
1068       session->port = port;
1069     }
1070     
1071     goto out;
1072   }
1073
1074   session->hostname = strdup(hostname);
1075   session->port = port;
1076
1077   /* Session exists, continue with key agreement protocol. */
1078   if (silc_client_connect_to_client(client, conn, port, 
1079                                     hostname, session) < 0) {
1080     /* Call monitor callback */
1081     if (session->monitor)
1082       (*session->monitor)(session->client, session->conn,
1083                           SILC_CLIENT_FILE_MONITOR_ERROR, 
1084                           SILC_CLIENT_FILE_ERROR, 0, 0,
1085                           session->client_entry, session->session_id,
1086                           session->filepath, session->monitor_context);
1087   }
1088
1089  out:
1090   if (payload)
1091     silc_key_agreement_payload_free(payload);
1092   silc_packet_context_free(packet);
1093 }
1094
1095 /* Called when file transfer packet is received. This will parse the
1096    packet and give it to the file transfer protocol. */
1097
1098 void silc_client_ftp(SilcClient client,
1099                      SilcSocketConnection sock,
1100                      SilcPacketContext *packet)
1101 {
1102   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1103   SilcUInt8 type;
1104   int ret;
1105
1106   SILC_LOG_DEBUG(("Start"));
1107
1108   /* Parse the payload */
1109   ret = silc_buffer_unformat(packet->buffer,
1110                              SILC_STR_UI_CHAR(&type),
1111                              SILC_STR_END);
1112   if (ret == -1)
1113     return;
1114
1115   /* We support only type number 1 (== SFTP) */
1116   if (type != 1)
1117     return;
1118
1119   silc_buffer_pull(packet->buffer, 1);
1120
1121   /* If we have active FTP session then give the packet directly to the
1122      protocol processor. */
1123   if (conn->active_session) {
1124     /* Give it to the SFTP */
1125     if (conn->active_session->server)
1126       silc_sftp_server_receive_process(conn->active_session->sftp, sock, 
1127                                        packet);
1128     else
1129       silc_sftp_client_receive_process(conn->active_session->sftp, sock, 
1130                                        packet);
1131   } else {
1132     /* We don't have active session, resolve the remote client information
1133        and then try to find the correct session. */
1134     SilcClientID *remote_id;
1135
1136     if (packet->src_id_type != SILC_ID_CLIENT)
1137       return;
1138
1139     remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
1140                                SILC_ID_CLIENT);
1141     if (!remote_id)
1142       return;
1143
1144     /* Resolve the client */
1145     silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1146                                          silc_client_ftp_resolve_cb,
1147                                          silc_packet_context_dup(packet));
1148     silc_free(remote_id);
1149   }
1150 }