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