Targeted say-operation messages
[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 - 2014 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 /************************** Types and definitions ***************************/
26
27 /* File transmission session */
28 struct SilcClientFtpSessionStruct {
29   SilcClient client;                  /* Client */
30   SilcClientConnection server_conn;   /* Connection to server */
31   SilcClientConnection conn;          /* Connection to remote host */
32   SilcClientEntry client_entry;       /* The client entry */
33   SilcClientListener listener;        /* Listener */
34   SilcAsyncOperation op;              /* Operation for connecting */
35   SilcClientConnectionParams params;  /* Connection params */
36   SilcPublicKey public_key;           /* Public key used in key exchange */
37   SilcPrivateKey private_key;         /* Private key used in key exchange */
38   SilcUInt32 session_id;              /* File transfer ID */
39
40   SilcClientFileMonitor monitor;      /* File transfer monitor callback */
41   void *monitor_context;
42   SilcClientFileAskName ask_name;     /* File name asking callback */
43   void *ask_name_context;
44   char *filepath;                     /* The remote filename */
45   char *path;                         /* User given path to save the file  */
46
47   SilcStream stream;                  /* Wrapped SilcPacketStream */
48   SilcSFTP sftp;                      /* SFTP server/client */
49   SilcSFTPFilesystem fs;              /* SFTP memory file system */
50   SilcSFTPHandle dir_handle;          /* SFTP session directory handle */
51   SilcSFTPHandle read_handle;         /* SFTP session file handles */
52
53   char *hostname;                     /* Remote host */
54   SilcUInt16 port;                    /* Remote port */
55   SilcUInt64 filesize;                /* File size */
56   SilcUInt64 read_offset;             /* Current read offset */
57   int fd;                             /* File descriptor */
58   unsigned int initiator : 1;         /* File sender sets this to TRUE */
59   unsigned int closed    : 1;         /* silc_client_file_close called */
60 };
61
62 /************************* SFTP Server Callbacks ****************************/
63
64 /* SFTP monitor callback for SFTP server. This reports the application
65    how the transmission is going along. This function is for the client
66    who made the file available for download. */
67
68 static void silc_client_ftp_monitor(SilcSFTP sftp,
69                                     SilcSFTPMonitors type,
70                                     const SilcSFTPMonitorData data,
71                                     void *context)
72 {
73   SilcClientFtpSession session = (SilcClientFtpSession)context;
74
75   if (type == SILC_SFTP_MONITOR_READ) {
76     /* Call the monitor for application */
77     if (session->monitor)
78       (*session->monitor)(session->client, session->conn,
79                           SILC_CLIENT_FILE_MONITOR_SEND,
80                           SILC_CLIENT_FILE_OK,
81                           data->offset, session->filesize,
82                           session->client_entry, session->session_id,
83                           session->filepath, session->monitor_context);
84   }
85 }
86
87 /************************* SFTP Client Callbacks ****************************/
88
89 /* Returns the read data. This is the downloader's function (client side)
90    to receive the read data and read more until EOF is received from
91    the other side. This will also monitor the transmission and notify
92    the application. */
93
94 static void silc_client_ftp_data(SilcSFTP sftp,
95                                  SilcSFTPStatus status,
96                                  const unsigned char *data,
97                                  SilcUInt32 data_len,
98                                  void *context)
99 {
100   SilcClientFtpSession session = (SilcClientFtpSession)context;
101
102   SILC_LOG_DEBUG(("Start"));
103
104   if (status == SILC_SFTP_STATUS_EOF) {
105     /* EOF received */
106
107     /* Close the handle */
108     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
109     session->read_handle = NULL;
110
111     /* Close the real file descriptor */
112     silc_file_close(session->fd);
113     return;
114   }
115
116   if (status != SILC_SFTP_STATUS_OK) {
117     /* Call monitor callback */
118     if (session->monitor)
119       (*session->monitor)(session->client, session->conn,
120                           SILC_CLIENT_FILE_MONITOR_ERROR,
121                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
122                            SILC_CLIENT_FILE_NO_SUCH_FILE :
123                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
124                            SILC_CLIENT_FILE_PERMISSION_DENIED :
125                            SILC_CLIENT_FILE_ERROR), 0, 0,
126                           session->client_entry, session->session_id,
127                           session->filepath, session->monitor_context);
128
129     /* Close the handle */
130     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
131     session->read_handle = NULL;
132
133     /* Close the real file descriptor */
134     silc_file_close(session->fd);
135     return;
136   }
137
138   /* Read more, until EOF is received */
139   session->read_offset += data_len;
140   silc_sftp_read(sftp, session->read_handle, session->read_offset,
141                  SILC_PACKET_MAX_LEN - 1024,
142                  silc_client_ftp_data, session);
143
144   /* Write the read data to the real file */
145   silc_file_write(session->fd, data, data_len);
146
147   /* Call monitor callback */
148   if (session->monitor)
149     (*session->monitor)(session->client, session->conn,
150                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
151                         SILC_CLIENT_FILE_OK,
152                         session->read_offset, session->filesize,
153                         session->client_entry, session->session_id,
154                         session->filepath, session->monitor_context);
155 }
156
157 /* Returns handle for the opened file. This is the downloader's function.
158    This will begin reading the data from the file. */
159
160 static void silc_client_ftp_open_handle(SilcSFTP sftp,
161                                         SilcSFTPStatus status,
162                                         SilcSFTPHandle handle,
163                                         void *context)
164 {
165   SilcClientFtpSession session = (SilcClientFtpSession)context;
166   char path[512];
167
168   SILC_LOG_DEBUG(("Start"));
169
170   if (status != SILC_SFTP_STATUS_OK) {
171     /* Call monitor callback */
172     if (session->monitor)
173       (*session->monitor)(session->client, session->conn,
174                           SILC_CLIENT_FILE_MONITOR_ERROR,
175                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
176                            SILC_CLIENT_FILE_NO_SUCH_FILE :
177                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
178                            SILC_CLIENT_FILE_PERMISSION_DENIED :
179                            SILC_CLIENT_FILE_ERROR), 0, 0,
180                           session->client_entry, session->session_id,
181                           session->filepath, session->monitor_context);
182     return;
183   }
184
185   /* Open the actual local file */
186   memset(path, 0, sizeof(path));
187   silc_snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
188                 session->path : "", session->filepath);
189   session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
190   if (session->fd < 0) {
191     /* Call monitor callback */
192     session->conn->context_type = SILC_ID_CLIENT;
193     session->conn->client_entry = session->client_entry;
194     session->client->internal->ops->say(session->client, session->conn,
195                                         SILC_CLIENT_MESSAGE_ERROR,
196                                         "File `%s' open failed: %s",
197                                         session->filepath,
198                                         strerror(errno));
199     session->conn->context_type = SILC_ID_NONE;
200
201     if (session->monitor)
202       (*session->monitor)(session->client, session->conn,
203                           SILC_CLIENT_FILE_MONITOR_ERROR,
204                           SILC_CLIENT_FILE_PERMISSION_DENIED, 0, 0,
205                           session->client_entry, session->session_id,
206                           session->filepath, session->monitor_context);
207     return;
208   }
209
210   session->read_handle = handle;
211
212   /* Now, start reading the file */
213   silc_sftp_read(sftp, session->read_handle, session->read_offset,
214                  SILC_PACKET_MAX_LEN - 1024,
215                  silc_client_ftp_data, session);
216
217   /* Call monitor callback */
218   if (session->monitor)
219     (*session->monitor)(session->client, session->conn,
220                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
221                         SILC_CLIENT_FILE_OK,
222                         session->read_offset, session->filesize,
223                         session->client_entry, session->session_id,
224                         session->filepath, session->monitor_context);
225 }
226
227 /* Ask filename completion callback.  Delivers the filepath selected by
228    user. */
229
230 static void silc_client_ftp_ask_name(const char *filepath,
231                                      void *context)
232 {
233   SilcClientFtpSession session = (SilcClientFtpSession)context;
234   SilcSFTPAttributesStruct attr;
235   char *remote_file = NULL;
236
237   SILC_LOG_DEBUG(("Start"));
238
239   if (filepath) {
240     remote_file = session->filepath;
241     session->filepath = NULL;
242     silc_free(session->path);
243     session->path = NULL;
244     session->filepath = strdup(filepath);
245   } else {
246     remote_file = strdup(session->filepath);
247   }
248
249   /* Now open the file */
250   memset(&attr, 0, sizeof(attr));
251   silc_sftp_open(session->sftp, remote_file, SILC_SFTP_FXF_READ, &attr,
252                  silc_client_ftp_open_handle, session);
253
254   /* Close the directory handle */
255   silc_sftp_close(session->sftp, session->dir_handle, NULL, NULL);
256   session->dir_handle = NULL;
257
258   silc_free(remote_file);
259 }
260
261 /* Returns the file name available for download. This is the downloader's
262    function. */
263
264 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
265                                          SilcSFTPStatus status,
266                                          const SilcSFTPName name,
267                                          void *context)
268 {
269   SilcClientFtpSession session = (SilcClientFtpSession)context;
270
271   SILC_LOG_DEBUG(("Start"));
272
273   if (status != SILC_SFTP_STATUS_OK) {
274     /* Call monitor callback */
275     if (session->monitor)
276       (*session->monitor)(session->client, session->conn,
277                           SILC_CLIENT_FILE_MONITOR_ERROR,
278                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
279                            SILC_CLIENT_FILE_NO_SUCH_FILE :
280                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
281                            SILC_CLIENT_FILE_PERMISSION_DENIED :
282                            SILC_CLIENT_FILE_ERROR), 0, 0,
283                           session->client_entry, session->session_id,
284                           session->filepath, session->monitor_context);
285     return;
286   }
287
288   /* Save the important attributes like filename and file size */
289   session->filepath = strdup(name->filename[0]);
290   session->filesize = name->attrs[0]->size;
291
292   /* If the path was not provided, ask from application where to save the
293      downloaded file. */
294   if (!session->path && session->ask_name) {
295     session->ask_name(session->client, session->conn, session->session_id,
296                       name->filename[0], silc_client_ftp_ask_name, session,
297                       session->ask_name_context);
298     return;
299   }
300
301   /* Start downloading immediately to current directory. */
302   silc_client_ftp_ask_name(NULL, session);
303 }
304
305 /* Returns the file handle after giving opendir command. This is the
306    downloader's function. */
307
308 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
309                                            SilcSFTPStatus status,
310                                            SilcSFTPHandle handle,
311                                            void *context)
312 {
313   SilcClientFtpSession session = (SilcClientFtpSession)context;
314
315   SILC_LOG_DEBUG(("Start"));
316
317   if (status != SILC_SFTP_STATUS_OK) {
318     /* Call monitor callback */
319     if (session->monitor)
320       (*session->monitor)(session->client, session->conn,
321                           SILC_CLIENT_FILE_MONITOR_ERROR,
322                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
323                            SILC_CLIENT_FILE_NO_SUCH_FILE :
324                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
325                            SILC_CLIENT_FILE_PERMISSION_DENIED :
326                            SILC_CLIENT_FILE_ERROR), 0, 0,
327                           session->client_entry, session->session_id,
328                           session->filepath, session->monitor_context);
329     return;
330   }
331
332   /* Now, read the directory */
333   silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
334   session->dir_handle = handle;
335 }
336
337 /* SFTP version callback for SFTP client. This is the downloader's function
338    after initializing the SFTP connection to the remote client. This will
339    find out the filename available for download. */
340
341 static void silc_client_ftp_version(SilcSFTP sftp,
342                                     SilcSFTPStatus status,
343                                     SilcSFTPVersion version,
344                                     void *context)
345 {
346   SilcClientFtpSession session = (SilcClientFtpSession)context;
347
348   SILC_LOG_DEBUG(("Start"));
349
350   if (status != SILC_SFTP_STATUS_OK) {
351     /* Call monitor callback */
352     if (session->monitor)
353       (*session->monitor)(session->client, session->conn,
354                           SILC_CLIENT_FILE_MONITOR_ERROR,
355                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
356                            SILC_CLIENT_FILE_NO_SUCH_FILE :
357                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
358                            SILC_CLIENT_FILE_PERMISSION_DENIED :
359                            SILC_CLIENT_FILE_ERROR), 0, 0,
360                           session->client_entry, session->session_id,
361                           session->filepath, session->monitor_context);
362     return;
363   }
364
365   /* The SFTP session is open, now retrieve the info about available file. */
366   silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
367 }
368
369 /* SFTP stream error callback */
370
371 static void silc_client_ftp_error(SilcSFTP sftp, SilcSFTPStatus status,
372                                   void *context)
373 {
374
375 }
376
377 /************************ Static utility functions **************************/
378
379 /* Free session resources.   Connection must be closed before getting
380    here. */
381
382 static void silc_client_ftp_session_free(SilcClientFtpSession session)
383 {
384   SILC_LOG_DEBUG(("Free session %d", session->session_id));
385
386   silc_schedule_task_del_by_context(session->client->schedule, session);
387
388   silc_dlist_del(session->client->internal->ftp_sessions, session);
389
390   /* Abort connecting  */
391   if (session->op)
392     silc_async_abort(session->op, NULL, NULL);
393
394   /* Destroy SFTP */
395   if (session->sftp) {
396     if (session->initiator)
397       silc_sftp_server_shutdown(session->sftp);
398     else
399       silc_sftp_client_shutdown(session->sftp);
400   }
401   if (session->fs)
402     silc_sftp_fs_memory_free(session->fs);
403
404   /* Destroy listener */
405   if (session->listener)
406     silc_client_listener_free(session->listener);
407
408   /* Destroy wrapped stream */
409   if (session->stream)
410     silc_stream_destroy(session->stream);
411
412   silc_client_unref_client(session->client, session->server_conn,
413                            session->client_entry);
414   silc_free(session->hostname);
415   silc_free(session->filepath);
416   silc_free(session->path);
417   silc_free(session);
418 }
419
420 /* File transfer session timeout */
421
422 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
423 {
424   SilcClientFtpSession session = context;
425
426   SILC_LOG_DEBUG(("Timeout"));
427
428   /* Close connection (destroyes the session context later).  If it is
429      already closed, destroy the session now. */
430   if (session->conn) {
431     silc_client_close_connection(session->client, session->conn);
432     session->conn = NULL;
433   } else {
434     /* Call monitor callback */
435     if (session->monitor)
436       (*session->monitor)(session->client, session->conn,
437                           SILC_CLIENT_FILE_MONITOR_ERROR,
438                           SILC_CLIENT_FILE_TIMEOUT, 0, 0,
439                           session->client_entry, session->session_id,
440                           session->filepath, session->monitor_context);
441
442     silc_client_ftp_session_free(context);
443   }
444 }
445
446 /* File transfer session closing task callback */
447
448 SILC_TASK_CALLBACK(silc_client_file_close_final)
449 {
450   SilcClientFtpSession session = context;
451
452   /* Close connection (destroyes the session context later).  If it is
453      already closed, destroy the session now. */
454   if (session->conn) {
455     silc_client_close_connection(session->client, session->conn);
456     session->conn = NULL;
457   } else {
458     silc_client_ftp_session_free(context);
459   }
460 }
461
462 /* Client resolving callback.  Continues with the FTP packet processing */
463
464 static void silc_client_ftp_client_resolved(SilcClient client,
465                                             SilcClientConnection conn,
466                                             SilcStatus status,
467                                             SilcDList clients,
468                                             void *context)
469 {
470   SilcFSMThread thread = context;
471   SilcPacket packet = silc_fsm_get_state_context(thread);
472
473   /* If no client found, ignore the packet, a silent error */
474   if (!clients) {
475     silc_packet_free(packet);
476     silc_fsm_finish(thread);
477     return;
478   }
479
480   /* Continue processing the packet */
481   SILC_FSM_CALL_CONTINUE(context);
482 }
483
484 /* FTP packet payload encoder/decoder.  This is called for every FTP packet.
485    We add/remove FTP payload in this function, because SFTP library does not
486    add/remove it. */
487
488 static SilcBool
489 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
490                       SilcBuffer buffer, void *context)
491 {
492   /* Pull FTP type in the payload, revealing SFTP payload */
493   if (status == SILC_STREAM_CAN_READ) {
494     if (silc_buffer_len(buffer) >= 1)
495       silc_buffer_pull(buffer, 1);
496     return TRUE;
497   }
498
499   /* Add FTP type before SFTP data */
500   if (status == SILC_STREAM_CAN_WRITE) {
501     if (silc_buffer_format(buffer,
502                            SILC_STR_UI_CHAR(1),
503                            SILC_STR_END) < 0)
504       return FALSE;
505     return TRUE;
506   }
507
508   return FALSE;
509 }
510
511 /* FTP Connection callback.  The SFTP session is started here. */
512
513 static void
514 silc_client_ftp_connect_completion(SilcClient client,
515                                    SilcClientConnection conn,
516                                    SilcClientConnectionStatus status,
517                                    SilcStatus error,
518                                    const char *message,
519                                    void *context)
520 {
521   SilcClientFtpSession session = context;
522
523   session->conn = conn;
524   session->op = NULL;
525
526   silc_schedule_task_del_by_context(client->schedule, session);
527
528   switch (status) {
529   case SILC_CLIENT_CONN_SUCCESS:
530     SILC_LOG_DEBUG(("Connected, conn %p", conn));
531
532     /* Wrap the connection packet stream */
533     session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
534                                               0, FALSE,
535                                               silc_client_ftp_coder, session);
536     if (!session->stream) {
537       /* Call monitor callback */
538       if (session->monitor)
539         (*session->monitor)(session->client, session->conn,
540                             SILC_CLIENT_FILE_MONITOR_ERROR,
541                             SILC_CLIENT_FILE_ERROR, 0, 0,
542                             session->client_entry, session->session_id,
543                             session->filepath, session->monitor_context);
544       silc_client_close_connection(client, conn);
545       session->conn = NULL;
546       return;
547     }
548
549     if (!session->initiator) {
550       /* If we are the SFTP client then start the SFTP session and retrieve
551          the info about the file available for download. */
552       session->sftp = silc_sftp_client_start(session->stream,
553                                              conn->internal->schedule,
554                                              silc_client_ftp_version,
555                                              silc_client_ftp_error, session);
556     } else {
557       /* Start SFTP server */
558       session->sftp = silc_sftp_server_start(session->stream,
559                                              conn->internal->schedule,
560                                              silc_client_ftp_error, session,
561                                              session->fs);
562
563       /* Monitor transmission */
564       silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
565                                    silc_client_ftp_monitor, session);
566     }
567
568     break;
569
570   case SILC_CLIENT_CONN_DISCONNECTED:
571     SILC_LOG_DEBUG(("Disconnected %p", conn));
572
573     /* Call monitor callback */
574     if (session->monitor)
575       (*session->monitor)(session->client, session->conn,
576                           SILC_CLIENT_FILE_MONITOR_DISCONNECT,
577                           SILC_CLIENT_FILE_ERROR, 0, 0,
578                           session->client_entry, session->session_id,
579                           session->filepath, session->monitor_context);
580
581     /* Connection already closed */
582     session->conn = NULL;
583
584     /* If closed by user, destroy the session now */
585     if (session->closed)
586       silc_client_ftp_session_free(session);
587     break;
588
589   case SILC_CLIENT_CONN_ERROR_TIMEOUT:
590     SILC_LOG_DEBUG(("Connecting timeout"));
591
592     /* Call monitor callback */
593     if (session->monitor)
594       (*session->monitor)(session->client, session->conn,
595                           SILC_CLIENT_FILE_MONITOR_ERROR,
596                           SILC_CLIENT_FILE_TIMEOUT, 0, 0,
597                           session->client_entry, session->session_id,
598                           session->filepath, session->monitor_context);
599
600     /* Connection already closed */
601     session->conn = NULL;
602     break;
603
604   default:
605     SILC_LOG_DEBUG(("Connecting error %d", status));
606
607     /* Call monitor callback */
608     if (session->monitor)
609       (*session->monitor)(session->client, session->conn,
610                           SILC_CLIENT_FILE_MONITOR_ERROR,
611                           status != SILC_CLIENT_CONN_ERROR ?
612                           SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
613                           SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
614                           session->client_entry, session->session_id,
615                           session->filepath, session->monitor_context);
616
617     /* Connection already closed */
618     session->conn = NULL;
619     break;
620   }
621 }
622
623 /*************************** File Transfer API ******************************/
624
625 /* Free all file transfer sessions. */
626
627 void silc_client_ftp_free_sessions(SilcClient client)
628 {
629   SilcClientFtpSession session;
630
631   if (!client->internal->ftp_sessions)
632     return;
633
634   silc_dlist_start(client->internal->ftp_sessions);
635   while ((session = silc_dlist_get(client->internal->ftp_sessions)))
636     silc_client_ftp_session_free(session);
637   silc_dlist_del(client->internal->ftp_sessions, session);
638 }
639
640 /* Free file transfer session by client entry. */
641
642 void silc_client_ftp_session_free_client(SilcClient client,
643                                          SilcClientEntry client_entry)
644 {
645   SilcClientFtpSession session;
646
647   if (!client->internal->ftp_sessions)
648     return;
649
650   /* Get the session */
651   silc_dlist_start(client->internal->ftp_sessions);
652   while ((session = silc_dlist_get(client->internal->ftp_sessions)))
653     if (session->client_entry == client_entry)
654       silc_client_ftp_session_free(session);
655 }
656
657 /* Sends a file indicated by the `filepath' to the remote client
658    indicated by the `client_entry'.  This will negotiate a secret key
659    with the remote client before actually starting the transmission of
660    the file.  The `monitor' callback will be called to monitor the
661    transmission of the file. */
662
663 SilcClientFileError
664 silc_client_file_send(SilcClient client,
665                       SilcClientConnection conn,
666                       SilcClientEntry client_entry,
667                       SilcClientConnectionParams *params,
668                       SilcPublicKey public_key,
669                       SilcPrivateKey private_key,
670                       SilcClientFileMonitor monitor,
671                       void *monitor_context,
672                       const char *filepath,
673                       SilcUInt32 *session_id)
674 {
675   SilcClientFtpSession session;
676   SilcBuffer keyagr;
677   char *filename, *path;
678   int fd;
679
680   SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
681
682   if (!client || !client_entry || !filepath || !params ||
683       !public_key || !private_key)
684     return SILC_CLIENT_FILE_ERROR;
685
686   /* Check for existing session for `filepath'. */
687   silc_dlist_start(client->internal->ftp_sessions);
688   while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
689     if (session->filepath && !strcmp(session->filepath, filepath) &&
690         session->client_entry == client_entry)
691       return SILC_CLIENT_FILE_ALREADY_STARTED;
692   }
693
694   /* See whether the file exists and can be opened */
695   fd = silc_file_open(filepath, O_RDONLY);
696   if (fd < 0)
697     return SILC_CLIENT_FILE_NO_SUCH_FILE;
698   silc_file_close(fd);
699
700   /* Add new session */
701   session = silc_calloc(1, sizeof(*session));
702   if (!session)
703     return SILC_CLIENT_FILE_ERROR;
704   session->session_id = ++client->internal->next_session_id;
705   session->client = client;
706   session->server_conn = conn;
707   session->initiator = TRUE;
708   session->client_entry = silc_client_ref_client(client, conn, client_entry);
709   session->monitor = monitor;
710   session->monitor_context = monitor_context;
711   session->filepath = strdup(filepath);
712   session->params = *params;
713   session->public_key = public_key;
714   session->private_key = private_key;
715
716   if (silc_asprintf(&path, "file://%s", filepath) < 0) {
717     silc_free(session);
718     return SILC_CLIENT_FILE_NO_MEMORY;
719   }
720
721   /* Allocate memory filesystem and put the file to it */
722   if (strrchr(path, '/'))
723     filename = strrchr(path, '/') + 1;
724   else
725     filename = (char *)path;
726   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
727                                           SILC_SFTP_FS_PERM_EXEC);
728   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
729                                filename, path);
730
731   session->filesize = silc_file_size(filepath);
732
733   /* If local IP is provided, create listener for incoming key exchange */
734   if (params->local_ip || params->bind_ip) {
735     session->listener =
736       silc_client_listener_add(client,
737                                conn->internal->schedule,
738                                params, public_key, private_key,
739                                silc_client_ftp_connect_completion,
740                                session);
741     if (!session->listener) {
742       conn->context_type = SILC_ID_CLIENT;
743       conn->client_entry = session->client_entry;
744       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
745                                  "Cannot create listener for file transfer: "
746                                  "%s", strerror(errno));
747       conn->context_type = SILC_ID_NONE;
748       silc_free(session);
749       return SILC_CLIENT_FILE_NO_MEMORY;
750     }
751
752     session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
753                          strdup(params->local_ip));
754     session->port = silc_client_listener_get_local_port(session->listener);
755   }
756
757   SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
758
759   /* Send the key agreement inside FTP packet */
760   keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
761                                              session->port);
762   if (!keyagr) {
763     if (session->listener)
764       silc_client_listener_free(session->listener);
765     silc_free(session);
766     return SILC_CLIENT_FILE_NO_MEMORY;
767   }
768   silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
769                           SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
770                           SILC_STR_UI_CHAR(1),
771                           SILC_STR_DATA(silc_buffer_data(keyagr),
772                                         silc_buffer_len(keyagr)),
773                           SILC_STR_END);
774
775   silc_buffer_free(keyagr);
776   silc_free(path);
777
778   silc_dlist_add(client->internal->ftp_sessions, session);
779   if (session_id)
780     *session_id = session->session_id;
781
782   /* Add session request timeout */
783   if (params && params->timeout_secs)
784     silc_schedule_task_add_timeout(client->schedule,
785                                    silc_client_ftp_timeout, session,
786                                    params->timeout_secs, 0);
787
788   return SILC_CLIENT_FILE_OK;
789 }
790
791 /* Receives a file from a client indicated by the `client_entry'.  The
792    `session_id' indicates the file transmission session and it has been
793    received in the `ftp' client operation function.  This will actually
794    perform the key agreement protocol with the remote client before
795    actually starting the file transmission.  The `monitor' callback
796    will be called to monitor the transmission. */
797
798 SilcClientFileError
799 silc_client_file_receive(SilcClient client,
800                          SilcClientConnection conn,
801                          SilcClientConnectionParams *params,
802                          SilcPublicKey public_key,
803                          SilcPrivateKey private_key,
804                          SilcClientFileMonitor monitor,
805                          void *monitor_context,
806                          const char *path,
807                          SilcUInt32 session_id,
808                          SilcClientFileAskName ask_name,
809                          void *ask_name_context)
810 {
811   SilcClientFtpSession session;
812   SilcBuffer keyagr;
813
814   if (!client || !conn)
815     return SILC_CLIENT_FILE_ERROR;
816
817   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
818
819   /* Get the session */
820   silc_dlist_start(client->internal->ftp_sessions);
821   while ((session = silc_dlist_get(client->internal->ftp_sessions))
822          != SILC_LIST_END) {
823     if (session->session_id == session_id) {
824       break;
825     }
826   }
827
828   if (session == SILC_LIST_END) {
829     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
830     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
831   }
832
833   /* See if we have this session running already */
834   if (session->sftp || session->listener) {
835     SILC_LOG_DEBUG(("Session already started"));
836     return SILC_CLIENT_FILE_ALREADY_STARTED;
837   }
838
839   session->monitor = monitor;
840   session->monitor_context = monitor_context;
841   session->ask_name = ask_name;
842   session->ask_name_context = ask_name_context;
843   session->path = path ? strdup(path) : NULL;
844
845   /* If the hostname and port already exists then the remote client did
846      provide the connection point to us and we won't create listener, but
847      create the connection ourselves. */
848   if (session->hostname && session->port) {
849     SILC_LOG_DEBUG(("Connecting to remote client"));
850     /* Connect to the remote client.  Performs key exchange automatically. */
851     session->op =
852       silc_client_connect_to_client(client, params, public_key, private_key,
853                                     session->hostname, session->port,
854                                     silc_client_ftp_connect_completion,
855                                     session);
856     if (!session->op) {
857       silc_free(session);
858       return SILC_CLIENT_FILE_ERROR;
859     }
860   } else {
861     /* Add the listener for the key agreement */
862     SILC_LOG_DEBUG(("Creating listener for file transfer"));
863     if (!params || (!params->local_ip && !params->bind_ip)) {
864       session->conn->context_type = SILC_ID_CLIENT;
865       session->conn->client_entry = session->client_entry;
866       session->client->internal->ops->say(session->client, session->conn,
867                                           SILC_CLIENT_MESSAGE_ERROR,
868                                           "Cannot create listener for file "
869                                           "transfer; IP address and/or port "
870                                           "not provided");
871       session->conn->context_type = SILC_ID_NONE;
872       silc_free(session);
873       return SILC_CLIENT_FILE_ERROR;
874     }
875     session->listener =
876       silc_client_listener_add(client, conn->internal->schedule, params,
877                                public_key, private_key,
878                                silc_client_ftp_connect_completion,
879                                session);
880     if (!session->listener) {
881       session->conn->context_type = SILC_ID_CLIENT;
882       session->conn->client_entry = session->client_entry;
883       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
884                                  "Cannot create listener for file transfer: "
885                                  "%s", strerror(errno));
886       session->conn->context_type = SILC_ID_NONE;
887       silc_free(session);
888       return SILC_CLIENT_FILE_NO_MEMORY;
889     }
890     session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
891                          strdup(params->local_ip));
892     session->port = silc_client_listener_get_local_port(session->listener);
893
894     /* Send the key agreement inside FTP packet */
895     SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
896     keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
897                                                session->port);
898     if (!keyagr) {
899       silc_client_listener_free(session->listener);
900       silc_free(session);
901       return SILC_CLIENT_FILE_NO_MEMORY;
902     }
903     silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
904                             SILC_ID_CLIENT, &session->client_entry->id,
905                             NULL, NULL,
906                             SILC_STR_UI_CHAR(1),
907                             SILC_STR_DATA(silc_buffer_data(keyagr),
908                                           silc_buffer_len(keyagr)),
909                             SILC_STR_END);
910     silc_buffer_free(keyagr);
911
912     /* Add session request timeout */
913     if (params && params->timeout_secs)
914       silc_schedule_task_add_timeout(client->schedule,
915                                      silc_client_ftp_timeout, session,
916                                      params->timeout_secs, 0);
917   }
918
919   return SILC_CLIENT_FILE_OK;
920 }
921
922 /* Closes file transmission session indicated by the `session_id'.
923    If file transmission is being conducted it will be aborted
924    automatically. This function is also used to close the session
925    after successful file transmission. This function can be used
926    also to reject incoming file transmission request. */
927
928 SilcClientFileError silc_client_file_close(SilcClient client,
929                                            SilcClientConnection conn,
930                                            SilcUInt32 session_id)
931 {
932   SilcClientFtpSession session;
933
934   if (!client || !conn)
935     return SILC_CLIENT_FILE_ERROR;
936
937   SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
938
939   /* Get the session */
940   silc_dlist_start(client->internal->ftp_sessions);
941   while ((session = silc_dlist_get(client->internal->ftp_sessions))
942          != SILC_LIST_END) {
943     if (session->session_id == session_id)
944       break;
945   }
946
947   if (session == SILC_LIST_END) {
948     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
949     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
950   }
951
952   if (session->monitor) {
953     (*session->monitor)(session->client, session->conn,
954                         SILC_CLIENT_FILE_MONITOR_CLOSED,
955                         SILC_CLIENT_FILE_OK, 0, 0,
956                         session->client_entry, session->session_id,
957                         session->filepath, session->monitor_context);
958
959     /* No more callbacks to application */
960     session->monitor = NULL;
961   }
962
963   silc_schedule_task_del_by_context(client->schedule, session);
964
965   session->closed = TRUE;
966
967   /* Destroy via timeout */
968   silc_schedule_task_add_timeout(conn->internal->schedule,
969                                  silc_client_file_close_final, session,
970                                  0, 1);
971
972   return SILC_CLIENT_FILE_OK;
973 }
974
975 /************************** FTP Request Processing **************************/
976
977 /* Received file transfer packet.  Only file transfer requests get here.
978    The actual file transfer is handled by the SFTP library when we give it
979    the packet stream wrapped into SilcStream context. */
980
981 SILC_FSM_STATE(silc_client_ftp)
982 {
983   SilcClientConnection conn = fsm_context;
984   SilcClient client = conn->client;
985   SilcPacket packet = state_context;
986   SilcClientFtpSession session;
987   SilcClientID remote_id;
988   SilcClientEntry remote_client;
989   SilcKeyAgreementPayload payload = NULL;
990   char *hostname;
991   SilcUInt16 port;
992
993   SILC_LOG_DEBUG(("Process file transfer packet"));
994
995   if (silc_buffer_len(&packet->buffer) < 1)
996     goto out;
997
998   /* We support file transfer type number 1 (== SFTP) */
999   if (packet->buffer.data[0] != 0x01) {
1000     SILC_LOG_DEBUG(("Unsupported file transfer type %d",
1001                     packet->buffer.data[0]));
1002     goto out;
1003   }
1004
1005   if (!silc_id_str2id(packet->src_id, packet->src_id_len,
1006                       SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
1007     SILC_LOG_DEBUG(("Invalid client ID"));
1008     goto out;
1009   }
1010
1011   /* Check whether we know this client already */
1012   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1013   if (!remote_client || !remote_client->internal.valid) {
1014     /** Resolve client info */
1015     silc_client_unref_client(client, conn, remote_client);
1016     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1017                                          client, conn, &remote_id, NULL,
1018                                          silc_client_ftp_client_resolved,
1019                                          fsm));
1020     /* NOT REACHED */
1021   }
1022
1023   /* Get session */
1024   silc_dlist_start(client->internal->ftp_sessions);
1025   while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1026     if (session->client_entry == remote_client &&
1027         (!session->initiator || !session->listener))
1028       break;
1029   }
1030
1031   /* Parse the key agreement payload */
1032   payload =
1033     silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1034                                      silc_buffer_len(&packet->buffer) - 1);
1035   if (!payload) {
1036     SILC_LOG_DEBUG(("Invalid key agreement payload"));
1037     goto out;
1038   }
1039
1040   hostname = silc_key_agreement_get_hostname(payload);
1041   port = silc_key_agreement_get_port(payload);
1042   if (!hostname || !port) {
1043     hostname = NULL;
1044     port = 0;
1045   }
1046
1047   /* If session doesn't exist, we create new one.  If session exists, but
1048      we are responder it means that the remote sent another request and user
1049      hasn't even accepted the first one yet.  We assume this session is new
1050      session as well. */
1051   if (!session || !hostname || !session->initiator) {
1052     /* New file transfer session */
1053     SILC_LOG_DEBUG(("New file transfer session %d",
1054                     client->internal->next_session_id + 1));
1055
1056     session = silc_calloc(1, sizeof(*session));
1057     if (!session)
1058       goto out;
1059     session->session_id = ++client->internal->next_session_id;
1060     session->client = client;
1061     session->server_conn = conn;
1062     session->client_entry = silc_client_ref_client(client, conn,
1063                                                    remote_client);
1064     if (hostname && port) {
1065       session->hostname = strdup(hostname);
1066       session->port = port;
1067     }
1068     silc_dlist_add(client->internal->ftp_sessions, session);
1069
1070     /* Notify application of incoming FTP request */
1071     client->internal->ops->ftp(client, conn, remote_client,
1072                                session->session_id, hostname, port);
1073     goto out;
1074   }
1075
1076   /* Session exists, continue with key agreement protocol. */
1077   SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1078                   session->session_id));
1079
1080   session->hostname = strdup(hostname);
1081   session->port = port;
1082
1083   /* Connect to the remote client.  Performs key exchange automatically. */
1084   session->op =
1085     silc_client_connect_to_client(client, &session->params,
1086                                   session->public_key, session->private_key,
1087                                   session->hostname, session->port,
1088                                   silc_client_ftp_connect_completion,
1089                                   session);
1090   if (!session->op) {
1091     /* Call monitor callback */
1092     if (session->monitor)
1093       (*session->monitor)(session->client, session->conn,
1094                           SILC_CLIENT_FILE_MONITOR_ERROR,
1095                           SILC_CLIENT_FILE_ERROR, 0, 0,
1096                           session->client_entry, session->session_id,
1097                           session->filepath, session->monitor_context);
1098   }
1099
1100  out:
1101   if (payload)
1102     silc_key_agreement_payload_free(payload);
1103   silc_packet_free(packet);
1104   return SILC_FSM_FINISH;
1105 }