Added connection authentication request support.
[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 - 2007 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   silc_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   silc_asprintf(&path, "file://%s", filepath);
871
872   /* Allocate memory filesystem and put the file to it */
873   if (strrchr(path, '/'))
874     filename = strrchr(path, '/') + 1;
875   else
876     filename = (char *)path;
877   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
878                                           SILC_SFTP_FS_PERM_EXEC);
879   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
880                                filename, path);
881
882   session->filesize = silc_file_size(filepath);
883
884   /* Create the listener for incoming key exchange protocol. */
885   if (!do_not_bind) {
886     session->listener = -1;
887     if (local_ip)
888       session->hostname = strdup(local_ip);
889     else
890       silc_net_check_local_by_sock(conn->sock->sock, NULL,
891                                    &session->hostname);
892     if (session->hostname)
893       session->listener = silc_net_create_server(local_port,
894                                                  session->hostname);
895     if (session->listener < 0) {
896       /* Could not create listener. Do the second best thing; send empty
897          key agreement packet and let the remote client provide the point
898          for the key exchange. */
899       SILC_LOG_DEBUG(("Could not create listener"));
900       silc_free(session->hostname);
901       session->listener = 0;
902       session->hostname = NULL;
903       session->port = 0;
904     } else {
905       /* Listener ready */
906       SILC_LOG_DEBUG(("Bound listener"));
907       session->port = silc_net_get_local_port(session->listener);
908       silc_schedule_task_add(client->schedule, session->listener,
909                              silc_client_ftp_process_key_agreement, session,
910                              0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
911       session->bound = TRUE;
912     }
913   }
914
915   SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
916
917   /* Send the key agreement inside FTP packet */
918   keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
919
920   ftp = silc_buffer_alloc(1 + keyagr->len);
921   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
922   silc_buffer_format(ftp,
923                      SILC_STR_UI_CHAR(1),
924                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
925                      SILC_STR_END);
926   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
927                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
928                           ftp->data, ftp->len, FALSE);
929
930   silc_buffer_free(keyagr);
931   silc_buffer_free(ftp);
932   silc_free(path);
933
934   if (session_id)
935     *session_id = session->session_id;
936
937   return SILC_CLIENT_FILE_OK;
938 }
939
940 /* Receives a file from a client indicated by the `client_entry'.  The
941    `session_id' indicates the file transmission session and it has been
942    received in the `ftp' client operation function.  This will actually
943    perform the key agreement protocol with the remote client before
944    actually starting the file transmission.  The `monitor' callback
945    will be called to monitor the transmission. */
946
947 SilcClientFileError
948 silc_client_file_receive(SilcClient client,
949                          SilcClientConnection conn,
950                          SilcClientFileMonitor monitor,
951                          void *monitor_context,
952                          const char *path,
953                          SilcUInt32 session_id,
954                          SilcClientFileAskName ask_name,
955                          void *ask_name_context)
956 {
957   SilcClientFtpSession session;
958   SilcBuffer keyagr, ftp;
959
960   assert(client && conn);
961
962   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
963
964   /* Get the session */
965   silc_dlist_start(conn->internal->ftp_sessions);
966   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
967          != SILC_LIST_END) {
968     if (session->session_id == session_id) {
969       break;
970     }
971   }
972
973   if (session == SILC_LIST_END) {
974     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
975     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
976   }
977
978   /* See if we have this session running already */
979   if (session->sftp || session->listener) {
980     SILC_LOG_DEBUG(("Session already started"));
981     return SILC_CLIENT_FILE_ALREADY_STARTED;
982   }
983
984   session->monitor = monitor;
985   session->monitor_context = monitor_context;
986   session->ask_name = ask_name;
987   session->ask_name_context = ask_name_context;
988   session->conn = conn;
989   session->path = path ? strdup(path) : NULL;
990
991   /* If the hostname and port already exists then the remote client did
992      provide the connection point to us and we won't create listener, but
993      create the connection ourselves. */
994   if (session->hostname && session->port) {
995     SILC_LOG_DEBUG(("Connecting to remote client"));
996     if (silc_client_connect_to_client(client, conn, session->port,
997                                       session->hostname, session) < 0)
998       return SILC_CLIENT_FILE_ERROR;
999   } else {
1000     /* Add the listener for the key agreement */
1001     SILC_LOG_DEBUG(("Creating listener for file transfer"));
1002     session->listener = -1;
1003     silc_net_check_local_by_sock(conn->sock->sock, NULL, &session->hostname);
1004     if (session->hostname)
1005       session->listener = silc_net_create_server(0, session->hostname);
1006     if (session->listener < 0) {
1007       SILC_LOG_DEBUG(("Could not create listener"));
1008       session->listener = 0;
1009       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1010                                  "Cannot create listener on %s: %s",
1011                                  session->hostname, strerror(errno));
1012       return SILC_CLIENT_FILE_ERROR;
1013     }
1014     session->port = silc_net_get_local_port(session->listener);
1015     silc_schedule_task_add(client->schedule, session->listener,
1016                            silc_client_ftp_process_key_agreement, session,
1017                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
1018
1019     /* Send the key agreement inside FTP packet */
1020     SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
1021     keyagr = silc_key_agreement_payload_encode(session->hostname,
1022                                                session->port);
1023     ftp = silc_buffer_alloc(1 + keyagr->len);
1024     silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
1025     silc_buffer_format(ftp,
1026                        SILC_STR_UI_CHAR(1),
1027                        SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
1028                        SILC_STR_END);
1029     silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
1030                             session->client_entry->id,
1031                             SILC_ID_CLIENT, NULL, NULL,
1032                             ftp->data, ftp->len, FALSE);
1033
1034     silc_buffer_free(keyagr);
1035     silc_buffer_free(ftp);
1036   }
1037
1038   return SILC_CLIENT_FILE_OK;
1039 }
1040
1041 SILC_TASK_CALLBACK(silc_client_file_close_final)
1042 {
1043   silc_client_ftp_session_free(context);
1044 }
1045
1046 /* Closes file transmission session indicated by the `session_id'.
1047    If file transmission is being conducted it will be aborted
1048    automatically. This function is also used to close the session
1049    after successful file transmission. This function can be used
1050    also to reject incoming file transmission request. */
1051
1052 SilcClientFileError silc_client_file_close(SilcClient client,
1053                                            SilcClientConnection conn,
1054                                            SilcUInt32 session_id)
1055 {
1056   SilcClientFtpSession session;
1057
1058   assert(client && conn);
1059
1060   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
1061
1062   /* Get the session */
1063   silc_dlist_start(conn->internal->ftp_sessions);
1064   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1065          != SILC_LIST_END) {
1066     if (session->session_id == session_id)
1067       break;
1068   }
1069
1070   if (session == SILC_LIST_END) {
1071     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1072     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1073   }
1074
1075   if (session->monitor)
1076     (*session->monitor)(session->client, session->conn,
1077                         SILC_CLIENT_FILE_MONITOR_CLOSED,
1078                         SILC_CLIENT_FILE_OK, 0, 0,
1079                         session->client_entry, session->session_id,
1080                         session->filepath, session->monitor_context);
1081
1082   /* Destroy via timeout */
1083   silc_schedule_task_add(session->client->schedule, 0,
1084                          silc_client_file_close_final, session,
1085                          0, 1, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
1086
1087   return SILC_CLIENT_FILE_OK;
1088 }
1089
1090 /************************** FTP Request Processing **************************/
1091
1092 /* Client resolving callback.  Continues with the FTP processing */
1093
1094 static void silc_client_ftp_client_resolved(SilcClient client,
1095                                             SilcClientConnection conn,
1096                                             SilcStatus status,
1097                                             SilcDList clients,
1098                                             void *context)
1099 {
1100   SilcFSMThread thread = context;
1101   SilcPacket packet = silc_fsm_get_state_context(thread);
1102
1103   /* If no client found, ignore the packet, a silent error */
1104   if (!clients) {
1105     silc_packet_free(packet);
1106     silc_fsm_finish(thread);
1107     return;
1108   }
1109
1110   /* Continue processing the packet */
1111   SILC_FSM_CALL_CONTINUE(context);
1112 }
1113
1114 /* Received file transfer packet.  Only file transfer requests get here.
1115    The actual file transfer is handled by the SFTP library when we give it
1116    the packet stream wrapped into SilcStream context. */
1117
1118 SILC_FSM_STATE(silc_client_ftp)
1119 {
1120   SilcClientConnection conn = fsm_context;
1121   SilcClient client = conn->client;
1122   SilcPacket packet = state_context;
1123   SilcClientID remote_id;
1124   SilcClientEntry remote_client;
1125   SilcKeyAgreementPayload payload;
1126   SilcUInt8 type;
1127   char *hostname;
1128   SilcUInt16 port;
1129   int ret;
1130
1131   SILC_LOG_DEBUG(("Process file transfer packet"));
1132
1133   if (silc_buffer_len(&packet->buffer) < 1)
1134     goto out;
1135
1136   /* We support file transfer type number 1 (== SFTP) */
1137   if (packet->buffer.data[0] != 0x01) {
1138     SILC_LOG_DEBUG(("Unsupported file transfer type %d",
1139                     packet->buffer.data[0]));
1140     goto out;
1141   }
1142
1143   if (!silc_id_str2id(packet->src_id, packet->src_id_len,
1144                       SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
1145     SILC_LOG_DEBUG(("Invalid client ID"));
1146     goto out;
1147   }
1148
1149   /* Check whether we know this client already */
1150   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1151   if (!remote_client || !remote_client->nickname[0]) {
1152     /** Resolve client info */
1153     silc_client_unref_client(client, conn, remote_client);
1154     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1155                                          client, conn, &remote_id, NULL,
1156                                          silc_client_ftp_client_resolved,
1157                                          fsm));
1158     /* NOT REACHED */
1159   }
1160
1161
1162   silc_dlist_start(conn->internal->ftp_sessions);
1163   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1164          != SILC_LIST_END) {
1165     if (session->client_entry == client_entry &&
1166         (!session->server || !session->bound))
1167       break;
1168   }
1169
1170   /* Parse the key agreement payload */
1171   payload = silc_key_agreement_payload_parse(packet->buffer->data + 1,
1172                                              packet->buffer->len - 1);
1173   if (!payload) {
1174     SILC_LOG_DEBUG(("Invalid key agreement payload"));
1175     goto out;
1176   }
1177
1178   hostname = silc_key_agreement_get_hostname(payload);
1179   port = silc_key_agreement_get_port(payload);
1180   if (!hostname)
1181     port = 0;
1182   if (!port)
1183     hostname = NULL;
1184
1185   /* If session doesn't exist, we create one and let applicationi know about
1186      incoming file transfer request.  If session exists, but we are responder
1187      it means that the remote sent another request and user hasn't even
1188      accepted the first one yet.  We assume this session is new session
1189      as well. */
1190   if (session == SILC_LIST_END || (!hostname && !port) ||
1191       (session && session->server == FALSE)) {
1192     /* No session found, create one and let the application know about
1193        incoming file transfer request. */
1194     SILC_LOG_DEBUG(("New file transfer session ID: %d",
1195                     conn->internal->next_session_id + 1));
1196
1197     /* Add new session */
1198     session = silc_calloc(1, sizeof(*session));
1199     session->session_id = ++conn->internal->next_session_id;
1200     session->client = client;
1201     session->conn = conn;
1202     session->client_entry = client_entry;
1203     silc_dlist_add(conn->internal->ftp_sessions, session);
1204
1205     if (hostname && port) {
1206       session->hostname = strdup(hostname);
1207       session->port = port;
1208     }
1209
1210     /* Let the application know */
1211     client->internal->ops->ftp(client, conn, client_entry,
1212                                session->session_id, hostname, port);
1213
1214     goto out;
1215   }
1216
1217   /* Session exists, continue with key agreement protocol. */
1218   SILC_LOG_DEBUG(("Session ID %d exists, connecting to remote client",
1219                   session->session_id));
1220
1221   session->hostname = strdup(hostname);
1222   session->port = port;
1223
1224   if (silc_client_connect_to_client(client, conn, port,
1225                                     hostname, session) < 0) {
1226     /* Call monitor callback */
1227     if (session->monitor)
1228       (*session->monitor)(session->client, session->conn,
1229                           SILC_CLIENT_FILE_MONITOR_ERROR,
1230                           SILC_CLIENT_FILE_ERROR, 0, 0,
1231                           session->client_entry, session->session_id,
1232                           session->filepath, session->monitor_context);
1233   }
1234
1235
1236
1237  out:
1238   silc_packet_free(packet);
1239   SILC_FSM_FINISH;
1240 }