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