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