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