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