udpates.
[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
43   char *hostname;
44   uint16 port;
45   int listener;
46
47   SilcClientFileMonitor monitor;
48   void *monitor_context;
49   char *filepath;
50
51   SilcSFTP sftp;
52   SilcSFTPFilesystem fs;
53   bool server;
54
55   SilcSFTPHandle dir_handle;
56   SilcSFTPHandle read_handle;
57   uint64 filesize;
58   uint64 read_offset;
59   int fd;
60 };
61
62 void silc_client_ftp_free_sessions(SilcClient client,
63                                    SilcClientConnection conn)
64 {
65   if (conn->ftp_sessions) {
66     SilcClientFtpSession session;
67     silc_dlist_start(conn->ftp_sessions);
68     while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
69       session->sock->user_data = NULL;
70       silc_client_ftp_session_free(session);
71     }
72     silc_dlist_del(conn->ftp_sessions, session);
73     silc_dlist_uninit(conn->ftp_sessions);
74   }
75 }
76
77 void silc_client_ftp_session_free_client(SilcClientConnection conn,
78                                          SilcClientEntry client_entry)
79 {
80   SilcClientFtpSession session;
81
82   if (!conn->ftp_sessions)
83     return;
84
85   /* Get the session */
86   silc_dlist_start(conn->ftp_sessions);
87   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
88     if (session->client_entry == client_entry) {
89       session->sock->user_data = NULL;
90       silc_client_ftp_session_free(session);
91       break;
92     }
93   }
94 }
95
96 /* SFTP packet send callback */
97
98 static void silc_client_ftp_send_packet(SilcSocketConnection sock,
99                                         SilcBuffer packet, void *context)
100 {
101   SilcClientFtpSession session = (SilcClientFtpSession)context;
102   SilcClient client = session->client;
103   SilcBuffer buffer;
104
105   SILC_LOG_DEBUG(("Start"));
106
107   buffer = silc_buffer_alloc(1 + packet->len);
108   silc_buffer_pull_tail(buffer, SILC_BUFFER_END(buffer));
109   silc_buffer_format(buffer, 
110                      SILC_STR_UI_CHAR(1),
111                      SILC_STR_UI_XNSTRING(packet->data, packet->len),
112                      SILC_STR_END);
113
114   /* Send the packet immediately */
115   silc_client_packet_send(client, sock, SILC_PACKET_FTP, NULL, 0, NULL, NULL,
116                           buffer->data, buffer->len, TRUE);
117
118   silc_buffer_free(buffer);
119 }
120
121 /* SFTP monitor callback for SFTP server */
122
123 static void silc_client_ftp_monitor(SilcSFTP sftp,
124                                     SilcSFTPMonitors type,
125                                     const SilcSFTPMonitorData data,
126                                     void *context)
127 {
128   SilcClientFtpSession session = (SilcClientFtpSession)context;
129
130   if (type == SILC_SFTP_MONITOR_READ) {
131     /* Call the monitor for application */
132     if (session->monitor)
133       (*session->monitor)(session->client, session->conn,
134                           SILC_CLIENT_FILE_MONITOR_SEND,
135                           data->offset, session->filesize,
136                           session->client_entry, session->session_id,
137                           session->filepath, session->monitor_context);
138   }
139 }
140
141 /* Returns the read data */
142
143 static void silc_client_ftp_data(SilcSFTP sftp,
144                                  SilcSFTPStatus status,
145                                  const unsigned char *data,
146                                  uint32 data_len,
147                                  void *context)
148 {
149   SilcClientFtpSession session = (SilcClientFtpSession)context;
150
151   SILC_LOG_DEBUG(("Start"));
152
153   if (status == SILC_SFTP_STATUS_EOF) {
154
155     /* Close the handle */
156     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
157     session->read_handle = NULL;
158
159     /* Close the read file descriptor */
160     silc_file_close(session->fd);
161     return;
162   }
163
164   if (status != SILC_SFTP_STATUS_OK) {
165     /* XXX errror */
166
167     /* Close the handle */
168     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
169     session->read_handle = NULL;
170
171     /* Close the read file descriptor */
172     silc_file_close(session->fd);
173     return;
174   }
175
176   /* Read more, until EOF is received */
177   session->read_offset += data_len;
178   silc_sftp_read(sftp, session->read_handle, session->read_offset, 16384,
179                  silc_client_ftp_data, session);
180
181   /* Call monitor callback */
182   if (session->monitor)
183     (*session->monitor)(session->client, session->conn,
184                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
185                         session->read_offset, session->filesize,
186                         session->client_entry, session->session_id,
187                         session->filepath, session->monitor_context);
188
189   /* Write the read data */
190   silc_file_write(session->fd, data, data_len);
191 }
192
193 static void silc_client_ftp_open_handle(SilcSFTP sftp,
194                                         SilcSFTPStatus status,
195                                         SilcSFTPHandle handle,
196                                         void *context)
197 {
198   SilcClientFtpSession session = (SilcClientFtpSession)context;
199
200   SILC_LOG_DEBUG(("Start"));
201
202   if (status != SILC_SFTP_STATUS_OK) {
203     /* XXX errror */
204     return;
205   }
206
207   /* Open the actual local file */
208   session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT);
209   if (session->fd < 0) {
210     /* XXX errror */
211     return;
212   }
213
214   session->read_handle =  handle;
215
216   /* Now, start reading the file */
217   silc_sftp_read(sftp, session->read_handle, session->read_offset, 16384,
218                  silc_client_ftp_data, session);
219
220   /* Call monitor callback */
221   if (session->monitor)
222     (*session->monitor)(session->client, session->conn,
223                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
224                         session->read_offset, session->filesize,
225                         session->client_entry, session->session_id,
226                         session->filepath, session->monitor_context);
227 }
228
229 /* Returns the file name available for download. */
230
231 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
232                                          SilcSFTPStatus status,
233                                          const SilcSFTPName name,
234                                          void *context)
235 {
236   SilcClientFtpSession session = (SilcClientFtpSession)context;
237   SilcSFTPAttributesStruct attr;
238
239   SILC_LOG_DEBUG(("Start"));
240
241   if (status != SILC_SFTP_STATUS_OK) {
242     /* XXX errror */
243   }
244
245   /* Now open the file */
246   memset(&attr, 0, sizeof(attr));
247   silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
248                  silc_client_ftp_open_handle, session);
249
250   /* Save the important attributes */
251   session->filepath = strdup(name->filename[0]);
252   session->filesize = name->attrs[0]->size;
253
254   /* Close the directory handle */
255   silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
256   session->dir_handle = NULL;
257 }
258
259 /* Returns the file handle after giving opendir command. */
260
261 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
262                                            SilcSFTPStatus status,
263                                            SilcSFTPHandle handle,
264                                            void *context)
265 {
266   SilcClientFtpSession session = (SilcClientFtpSession)context;
267
268   SILC_LOG_DEBUG(("Start"));
269
270   if (status != SILC_SFTP_STATUS_OK) {
271     /* XXX errror */
272   }
273
274   /* Now, read the directory */
275   silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
276   session->dir_handle = handle;
277 }
278
279 /* SFTP version callback for SFTP client */
280
281 static void silc_client_ftp_version(SilcSFTP sftp,
282                                     SilcSFTPStatus status,
283                                     SilcSFTPVersion version,
284                                     void *context)
285 {
286   SilcClientFtpSession session = (SilcClientFtpSession)context;
287
288   SILC_LOG_DEBUG(("Start"));
289
290   if (status != SILC_SFTP_STATUS_OK) {
291     /* XXX errror */
292   }
293
294   /* The SFTP session is open, now retrieve the info about available file. */
295   silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
296 }
297
298 /* This callback is called after the key agreement protocol has been
299    performed. This calls the final completion callback for the application. */
300
301 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
302 {
303   SilcProtocol protocol = (SilcProtocol)context;
304   SilcClientKEInternalContext *ctx = 
305     (SilcClientKEInternalContext *)protocol->context;
306   SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
307   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
308
309   SILC_LOG_DEBUG(("Start"));
310
311   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
312       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
313     /* Error occured during protocol */
314     silc_ske_free_key_material(ctx->keymat);
315     goto out;
316   }
317
318   /* Set keys into use */
319   silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
320                                    ctx->ske->prop->cipher,
321                                    ctx->ske->prop->pkcs,
322                                    ctx->ske->prop->hash,
323                                    ctx->ske->prop->hmac,
324                                    ctx->ske->prop->group,
325                                    ctx->responder);
326
327   /* If we are the SFTP client then start the SFTP session and retrieve
328      the info about the file available for download. */
329   if (!session->server) {
330     session->sftp = silc_sftp_client_start(conn->sock,
331                                            silc_client_ftp_send_packet,
332                                            session, 
333                                            silc_client_ftp_version, session);
334   }
335
336   /* Set this as active session */
337   conn->active_session = session;
338
339  out:
340   silc_ske_free_key_material(ctx->keymat);
341   if (ctx->ske)
342     silc_ske_free(ctx->ske);
343   silc_free(ctx->dest_id);
344   ctx->sock->protocol = NULL;
345   silc_socket_free(ctx->sock);
346   silc_free(ctx);
347   silc_protocol_free(protocol);
348 }
349
350 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
351                                                 int sock)
352 {
353   SilcClient client = session->client;
354   SilcClientKEInternalContext *proto_ctx;
355   SilcProtocol protocol;
356   SilcClientConnection conn;
357   void *context;
358
359   SILC_LOG_DEBUG(("Start"));
360
361   /* Add new connection for this session */
362   conn = silc_client_add_connection(client, session->hostname,
363                                     session->port, session);
364
365   /* Allocate new socket connection object */
366   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
367   conn->sock->hostname = strdup(session->hostname);
368   conn->sock->port = silc_net_get_remote_port(sock);
369   session->sock = silc_socket_dup(conn->sock);
370
371   /* Allocate the SFTP */
372   if (session->server) {
373     session->sftp = silc_sftp_server_start(conn->sock,
374                                            silc_client_ftp_send_packet,
375                                            session, session->fs);
376
377     /* Monitor transmission */
378     silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
379                                  silc_client_ftp_monitor, session);
380   }
381
382   /* Allocate internal context for key exchange protocol. This is
383      sent as context for the protocol. */
384   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
385   proto_ctx->client = client;
386   proto_ctx->sock = silc_socket_dup(conn->sock);
387   proto_ctx->rng = client->rng;
388   proto_ctx->responder = FALSE;
389   proto_ctx->context = session;
390   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
391   proto_ctx->verify = silc_client_protocol_ke_verify_key;
392
393   /* Perform key exchange protocol. */
394   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
395                       &protocol, (void *)proto_ctx,
396                       silc_client_ftp_key_agreement_final);
397   conn->sock->protocol = protocol;
398
399   /* Register the connection for network input and output. This sets
400      that scheduler will listen for incoming packets for this connection 
401      and sets that outgoing packets may be sent to this connection as well.
402      However, this doesn't set the scheduler for outgoing traffic, it will 
403      be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
404      later when outgoing data is available. */
405   context = (void *)client;
406   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
407
408   /* Execute the protocol */
409   silc_protocol_execute(protocol, client->schedule, 0, 0);
410 }
411
412 SILC_TASK_CALLBACK(silc_client_ftp_connected)
413 {
414   SilcClientInternalConnectContext *ctx =
415     (SilcClientInternalConnectContext *)context;
416   SilcClient client = ctx->client;
417   SilcClientConnection conn = ctx->conn;
418   SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
419   int opt, opt_len = sizeof(opt);
420
421   SILC_LOG_DEBUG(("Start"));
422
423   /* Check the socket status as it might be in error */
424   silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
425   if (opt != 0) {
426     if (ctx->tries < 2) {
427       /* Connection failed but lets try again */
428       client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
429                        "Could not connect to client %s: %s",
430                        ctx->host, strerror(opt));
431       client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
432                        "Connecting to port %d of client %s resumed", 
433                        ctx->port, ctx->host);
434
435       /* Unregister old connection try */
436       silc_schedule_unset_listen_fd(client->schedule, fd);
437       silc_net_close_connection(fd);
438       silc_schedule_task_del(client->schedule, ctx->task);
439
440       /* Try again */
441       silc_client_connect_to_client_internal(ctx);
442       ctx->tries++;
443     } else {
444       /* Connection failed and we won't try anymore */
445       client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
446                        "Could not connect to client %s: %s",
447                        ctx->host, strerror(opt));
448       silc_schedule_unset_listen_fd(client->schedule, fd);
449       silc_net_close_connection(fd);
450       silc_schedule_task_del(client->schedule, ctx->task);
451       silc_free(ctx);
452       silc_client_ftp_session_free(session);
453     }
454     return;
455   }
456
457   silc_schedule_unset_listen_fd(client->schedule, fd);
458   silc_schedule_task_del(client->schedule, ctx->task);
459
460   /* Start the key agreement */
461   silc_client_ftp_start_key_agreement(session, fd);
462 }
463
464 static int 
465 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
466 {
467   int sock;
468
469   /* Create connection to server asynchronously */
470   sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
471   if (sock < 0)
472     return -1;
473
474   /* Register task that will receive the async connect and will
475      read the result. */
476   ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, 
477                                      silc_client_ftp_connected,
478                                      (void *)ctx, 0, 0, 
479                                      SILC_TASK_FD,
480                                      SILC_TASK_PRI_NORMAL);
481   silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE);
482
483   ctx->sock = sock;
484
485   return sock;
486 }
487
488 static int
489 silc_client_connect_to_client(SilcClient client, 
490                               SilcClientConnection conn, int port,
491                               char *host, void *context)
492 {
493   SilcClientInternalConnectContext *ctx;
494
495   /* Allocate internal context for connection process. This is
496      needed as we are doing async connecting. */
497   ctx = silc_calloc(1, sizeof(*ctx));
498   ctx->client = client;
499   ctx->conn = conn;
500   ctx->host = strdup(host);
501   ctx->port = port;
502   ctx->tries = 0;
503   ctx->context = context;
504
505   /* Do the actual connecting process */
506   return silc_client_connect_to_client_internal(ctx);
507 }
508
509 /* Free session */
510
511 void silc_client_ftp_session_free(SilcClientFtpSession session)
512 {
513   SilcClientConnection conn;
514
515   silc_dlist_del(session->conn->ftp_sessions, session);
516
517   if (session->sftp) {
518     if (session->server)
519       silc_sftp_server_shutdown(session->sftp);
520     else
521       silc_sftp_client_shutdown(session->sftp);
522   }
523
524   if (session->fs)
525     silc_sftp_fs_memory_free(session->fs);
526
527   if (session->listener) {
528     silc_schedule_unset_listen_fd(session->client->schedule, 
529                                   session->listener);
530     silc_net_close_connection(session->listener);
531   }
532
533   if (session->sock) {
534     silc_schedule_unset_listen_fd(session->client->schedule, 
535                                   session->sock->sock);
536     silc_net_close_connection(session->sock->sock);
537
538     if (session->sock->user_data) {
539       conn = (SilcClientConnection)session->sock->user_data;
540
541       if (conn->active_session == session)
542         conn->active_session = NULL;
543
544       silc_client_close_connection(session->client, session->sock, conn);
545     } else {
546       silc_socket_free(session->sock);
547     }
548   }
549
550   silc_free(session->hostname);
551   silc_free(session->filepath);
552   silc_free(session);
553 }
554
555 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
556 {
557   SilcClientFtpSession session = (SilcClientFtpSession)context;
558   SilcClient client = session->client;
559   SilcClientConnection conn;
560   SilcSocketConnection newsocket;
561   SilcClientKEInternalContext *proto_ctx;
562   int sock;
563
564   SILC_LOG_DEBUG(("Start"));
565
566   sock = silc_net_accept_connection(session->listener);
567   if (sock < 0) {
568     /* XXX error */
569     return;
570   }
571
572   /* Set socket options */
573   silc_net_set_socket_nonblock(sock);
574   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
575
576   /* Allocate new socket connection object */
577   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
578
579   /* Perform name and address lookups for the remote host. */
580   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
581   if (!newsocket->hostname && !newsocket->ip) {
582     /* XXX error */
583     return;
584   }
585   if (!newsocket->hostname)
586     newsocket->hostname = strdup(newsocket->ip);
587   newsocket->port = silc_net_get_remote_port(sock);
588
589   /* Add new connection for this session */
590   conn = silc_client_add_connection(client, newsocket->hostname,
591                                     newsocket->port, session);
592   conn->sock = newsocket;
593   conn->sock->user_data = conn;
594   session->sock = silc_socket_dup(conn->sock);
595
596   /* Allocate internal context for key exchange protocol. This is
597      sent as context for the protocol. */
598   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
599   proto_ctx->client = client;
600   proto_ctx->sock = silc_socket_dup(conn->sock);
601   proto_ctx->rng = client->rng;
602   proto_ctx->responder = TRUE;
603   proto_ctx->context = session;
604   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
605   proto_ctx->verify = silc_client_protocol_ke_verify_key;
606
607   /* Prepare the connection for key exchange protocol. We allocate the
608      protocol but will not start it yet. The connector will be the
609      initiator of the protocol thus we will wait for initiation from 
610      there before we start the protocol. */
611   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
612                       &newsocket->protocol, proto_ctx, 
613                       silc_client_ftp_key_agreement_final);
614
615   /* Register the connection for network input and output. This sets
616      that scheduler will listen for incoming packets for this connection 
617      and sets that outgoing packets may be sent to this connection as well.
618      However, this doesn't set the scheduler for outgoing traffic, it
619      will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
620      later when outgoing data is available. */
621   context = (void *)client;
622   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
623 }
624
625 uint32 silc_client_file_send(SilcClient client,
626                              SilcClientConnection conn,
627                              SilcClientFileMonitor monitor,
628                              void *monitor_context,
629                              SilcClientEntry client_entry,
630                              const char *filepath)
631 {
632   SilcClientFtpSession session;
633   SilcBuffer keyagr, ftp;
634   char *filename, *path;
635
636   SILC_LOG_DEBUG(("Start"));
637
638   /* Check for existing session for `filepath'. */
639   silc_dlist_start(conn->ftp_sessions);
640   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
641     if (!strcmp(session->filepath, filepath) && 
642         session->client_entry == client_entry)
643       return 0;
644   }
645
646   /* Add new session */
647   session = silc_calloc(1, sizeof(*session));
648   session->session_id = conn->next_session_id++;
649   session->client = client;
650   session->conn = conn;
651   session->client_entry = client_entry;
652   session->monitor = monitor;
653   session->monitor_context = monitor_context;
654   session->filepath = strdup(filepath);
655   session->server = TRUE;
656   silc_dlist_add(conn->ftp_sessions, session);
657
658   path = silc_calloc(strlen(filepath) + 8, sizeof(*path));
659   strcat(path, "file://");
660   strncat(path, filepath, strlen(filepath));
661
662   /* Allocate memory filesystem and put the file to it */
663   if (strrchr(path, '/'))
664     filename = strrchr(path, '/') + 1;
665   else
666     filename = (char *)path;
667   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
668                                           SILC_SFTP_FS_PERM_EXEC);
669   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
670                                filename, path);
671
672   session->filesize = silc_file_size(filepath);
673
674   /* Send the key agreement inside FTP packet */
675   keyagr = silc_key_agreement_payload_encode(NULL, 0);
676
677   ftp = silc_buffer_alloc(1 + keyagr->len);
678   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
679   silc_buffer_format(ftp,
680                      SILC_STR_UI_CHAR(1),
681                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
682                      SILC_STR_END);
683   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
684                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
685                           ftp->data, ftp->len, FALSE);
686
687   silc_buffer_free(keyagr);
688   silc_buffer_free(ftp);
689   silc_free(path);
690
691   return session->session_id;
692 }
693
694 bool silc_client_file_receive(SilcClient client,
695                               SilcClientConnection conn,
696                               SilcClientFileMonitor monitor,
697                               void *monitor_context,
698                               SilcClientEntry client_entry,
699                               uint32 session_id)
700 {
701   SilcClientFtpSession session;
702   SilcBuffer keyagr, ftp;
703
704   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
705
706   /* Get the session */
707   silc_dlist_start(conn->ftp_sessions);
708   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
709     if (session->session_id == session_id) {
710       break;
711     }
712   }
713
714   if (session == SILC_LIST_END) {
715     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
716     return FALSE;
717   }
718
719   /* See if we have this session running already */
720   if (session->sftp || session->listener) {
721     SILC_LOG_DEBUG(("Session already started"));
722     return FALSE;
723   }
724
725   session->monitor = monitor;
726   session->monitor_context = monitor_context;
727   session->client_entry = client_entry;
728   session->conn = conn;
729
730   /* Add the listener for the key agreement */
731   session->hostname = silc_net_localip();
732   session->listener = silc_net_create_server(0, session->hostname);
733   if (session->listener < 0) {
734     /* XXX Error */
735     SILC_LOG_DEBUG(("Could not create listener"));
736     return FALSE;
737   }
738   session->port = silc_net_get_local_port(session->listener);
739   silc_schedule_task_add(client->schedule, session->listener,
740                          silc_client_ftp_process_key_agreement, session,
741                          0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
742
743   /* Send the key agreement inside FTP packet */
744   keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
745
746   ftp = silc_buffer_alloc(1 + keyagr->len);
747   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
748   silc_buffer_format(ftp,
749                      SILC_STR_UI_CHAR(1),
750                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
751                      SILC_STR_END);
752   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
753                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
754                           ftp->data, ftp->len, FALSE);
755
756   silc_buffer_free(keyagr);
757   silc_buffer_free(ftp);
758
759   return TRUE;
760 }
761
762 /* Closes FTP session */
763
764 bool silc_client_file_close(SilcClient client,
765                             SilcClientConnection conn,
766                             uint32 session_id)
767 {
768   SilcClientFtpSession session;
769
770   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
771
772   /* Get the session */
773   silc_dlist_start(conn->ftp_sessions);
774   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
775     if (session->session_id == session_id) {
776       break;
777     }
778   }
779
780   if (session == SILC_LIST_END) {
781     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
782     return FALSE;
783   }
784
785   silc_client_ftp_session_free(session);
786
787   return TRUE;
788 }
789
790 /* Callback called after remote client information has been resolved.
791    This will try to find existing session for the client entry.  If found
792    then continue with the key agreement protocol.  If not then it means
793    this is a file transfer request and we let the application know. */
794
795 static void 
796 silc_client_ftp_resolve_cb(SilcClient client,
797                            SilcClientConnection conn,
798                            SilcClientEntry *clients,
799                            uint32 clients_count,
800                            void *context)
801 {
802   SilcPacketContext *packet = (SilcPacketContext *)context;
803   SilcClientFtpSession session;
804   SilcKeyAgreementPayload payload;
805   SilcClientEntry client_entry;
806   char *hostname;
807   uint16 port;
808   int sock;
809
810   SILC_LOG_DEBUG(("Start"));
811
812   if (!clients)
813     goto out;
814
815   client_entry = clients[0];
816
817   silc_dlist_start(conn->ftp_sessions);
818   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
819     if (session->client_entry == client_entry)
820       break;
821   }
822
823   /* Parse the key agreement payload */
824   payload = silc_key_agreement_payload_parse(packet->buffer);
825   if (!payload)
826     goto out;
827
828   hostname = silc_key_agreement_get_hostname(payload);
829   port = silc_key_agreement_get_port(payload);
830
831   if (session == SILC_LIST_END) {
832     /* No session found, create one and let the application know about
833        incomoing file transfer request. */
834     
835     /* Add new session */
836     session = silc_calloc(1, sizeof(*session));
837     session->session_id = conn->next_session_id++;
838     session->client = client;
839     session->conn = conn;
840     silc_dlist_add(conn->ftp_sessions, session);
841
842     /* Let the application know */
843     client->ops->ftp(client, conn, client_entry,
844                      session->session_id, hostname, port);
845
846     /* If hostname was provided we'll start the key exchange now. */
847     if (hostname && port) {
848       /* XXX */
849     }
850
851     silc_key_agreement_payload_free(payload);
852     goto out;
853   }
854
855   if (!hostname)
856     goto out;
857
858   session->hostname = strdup(hostname);
859   session->port = port;
860
861   /* Session exists, continue with key agreement protocol. */
862   sock = silc_client_connect_to_client(client, conn, port, hostname,
863                                        session);
864   if (sock < 0)
865     goto out;
866
867  out:
868   silc_packet_context_free(packet);
869 }
870
871 /* Called when file transfer packet is received. This will parse the
872    packet and give it to the file transfer protocol. */
873
874 void silc_client_ftp(SilcClient client,
875                      SilcSocketConnection sock,
876                      SilcPacketContext *packet)
877 {
878   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
879   uint8 type;
880   int ret;
881
882   SILC_LOG_DEBUG(("Start"));
883
884   /* Parse the payload */
885   ret = silc_buffer_unformat(packet->buffer,
886                              SILC_STR_UI_CHAR(&type),
887                              SILC_STR_END);
888   if (ret == -1)
889     return;
890
891   /* We support only type number 1 (== SFTP) */
892   if (type != 1)
893     return;
894
895   silc_buffer_pull(packet->buffer, 1);
896
897   /* If we have active FTP session then give the packet to the
898      protocol processor. */
899   if (conn->active_session) {
900     /* Give it to the SFTP */
901     if (conn->active_session->server)
902       silc_sftp_server_receive_process(conn->active_session->sftp, sock, 
903                                        packet);
904     else
905       silc_sftp_client_receive_process(conn->active_session->sftp, sock, 
906                                        packet);
907   } else {
908     /* We don't have active session, resolve the remote client information
909        and then try to find the correct session. */
910     SilcClientID *remote_id;
911
912     if (packet->src_id_type != SILC_ID_CLIENT)
913       return;
914
915     remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
916                                SILC_ID_CLIENT);
917     if (!remote_id)
918       return;
919
920     /* Resolve the client */
921     silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
922                                          silc_client_ftp_resolve_cb,
923                                          silc_packet_context_dup(packet));
924     silc_free(remote_id);
925   }
926 }