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