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