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