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