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