API documentation changes.
[silc.git] / lib / silcclient / client_ftp.c
1 /*
2
3   client_ftp.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2001 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** 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 read 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 read 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_dlist_del(session->client->internal->ftp_sessions, session);
384
385   /* Abort connecting  */
386   if (session->op)
387     silc_async_abort(session->op, NULL, NULL);
388
389   /* Destroy SFTP */
390   if (session->sftp) {
391     if (session->initiator)
392       silc_sftp_server_shutdown(session->sftp);
393     else
394       silc_sftp_client_shutdown(session->sftp);
395   }
396   if (session->fs)
397     silc_sftp_fs_memory_free(session->fs);
398
399   /* Destroy listener */
400   if (session->listener)
401     silc_client_listener_free(session->listener);
402
403   /* Destroy wrapped stream */
404   if (session->stream)
405     silc_stream_destroy(session->stream);
406
407   silc_client_unref_client(session->client, session->server_conn,
408                            session->client_entry);
409   silc_free(session->hostname);
410   silc_free(session->filepath);
411   silc_free(session->path);
412   silc_free(session);
413 }
414
415 /* File transfer session timeout */
416
417 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
418 {
419   SilcClientFtpSession session = context;
420
421   SILC_LOG_DEBUG(("Timeout"));
422
423   /* Close connection (destroyes the session context later).  If it is
424      already closed, destroy the session now. */
425   if (session->conn) {
426     silc_client_close_connection(session->client, session->conn);
427     session->conn = NULL;
428   } else {
429     /* Call monitor callback */
430     if (session->monitor)
431       (*session->monitor)(session->client, session->conn,
432                           SILC_CLIENT_FILE_MONITOR_ERROR,
433                           SILC_CLIENT_FILE_TIMEOUT, 0, 0,
434                           session->client_entry, session->session_id,
435                           session->filepath, session->monitor_context);
436
437     silc_client_ftp_session_free(context);
438   }
439 }
440
441 /* File transfer session closing task callback */
442
443 SILC_TASK_CALLBACK(silc_client_file_close_final)
444 {
445   SilcClientFtpSession session = context;
446
447   /* Close connection (destroyes the session context later).  If it is
448      already closed, destroy the session now. */
449   if (session->conn) {
450     silc_client_close_connection(session->client, session->conn);
451     session->conn = NULL;
452   } else {
453     silc_client_ftp_session_free(context);
454   }
455 }
456
457 /* Client resolving callback.  Continues with the FTP packet processing */
458
459 static void silc_client_ftp_client_resolved(SilcClient client,
460                                             SilcClientConnection conn,
461                                             SilcStatus status,
462                                             SilcDList clients,
463                                             void *context)
464 {
465   SilcFSMThread thread = context;
466   SilcPacket packet = silc_fsm_get_state_context(thread);
467
468   /* If no client found, ignore the packet, a silent error */
469   if (!clients) {
470     silc_packet_free(packet);
471     silc_fsm_finish(thread);
472     return;
473   }
474
475   /* Continue processing the packet */
476   SILC_FSM_CALL_CONTINUE(context);
477 }
478
479 /* FTP packet payload encoder/decoder.  This is called for every FTP packet.
480    We add/remove FTP payload in this function, because SFTP library does not
481    add/remove it. */
482
483 static SilcBool
484 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
485                       SilcBuffer buffer, void *context)
486 {
487   /* Pull FTP type in the payload, revealing SFTP payload */
488   if (status == SILC_STREAM_CAN_READ) {
489     if (silc_buffer_len(buffer) >= 1)
490       silc_buffer_pull(buffer, 1);
491     return TRUE;
492   }
493
494   /* Add FTP type before SFTP data */
495   if (status == SILC_STREAM_CAN_WRITE) {
496     if (silc_buffer_format(buffer,
497                            SILC_STR_UI_CHAR(1),
498                            SILC_STR_END) < 0)
499       return FALSE;
500     return TRUE;
501   }
502
503   return FALSE;
504 }
505
506 /* FTP Connection callback.  The SFTP session is started here. */
507
508 static void
509 silc_client_ftp_connect_completion(SilcClient client,
510                                    SilcClientConnection conn,
511                                    SilcClientConnectionStatus status,
512                                    SilcStatus error,
513                                    const char *message,
514                                    void *context)
515 {
516   SilcClientFtpSession session = context;
517
518   session->conn = conn;
519   session->op = NULL;
520
521   silc_schedule_task_del_by_context(client->schedule, session);
522
523   switch (status) {
524   case SILC_CLIENT_CONN_SUCCESS:
525     SILC_LOG_DEBUG(("Connected, conn %p", conn));
526
527     /* Wrap the connection packet stream */
528     session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
529                                               0, FALSE,
530                                               silc_client_ftp_coder, session);
531     if (!session->stream) {
532       /* Call monitor callback */
533       if (session->monitor)
534         (*session->monitor)(session->client, session->conn,
535                             SILC_CLIENT_FILE_MONITOR_ERROR,
536                             SILC_CLIENT_FILE_ERROR, 0, 0,
537                             session->client_entry, session->session_id,
538                             session->filepath, session->monitor_context);
539       silc_client_close_connection(client, conn);
540       session->conn = NULL;
541       return;
542     }
543
544     if (!session->initiator) {
545       /* If we are the SFTP client then start the SFTP session and retrieve
546          the info about the file available for download. */
547       session->sftp = silc_sftp_client_start(session->stream,
548                                              conn->internal->schedule,
549                                              silc_client_ftp_version,
550                                              silc_client_ftp_error, session);
551     } else {
552       /* Start SFTP server */
553       session->sftp = silc_sftp_server_start(session->stream,
554                                              conn->internal->schedule,
555                                              silc_client_ftp_error, session,
556                                              session->fs);
557
558       /* Monitor transmission */
559       silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
560                                    silc_client_ftp_monitor, session);
561     }
562
563     break;
564
565   case SILC_CLIENT_CONN_DISCONNECTED:
566     SILC_LOG_DEBUG(("Disconnected %p", conn));
567
568     /* Call monitor callback */
569     if (session->monitor)
570       (*session->monitor)(session->client, session->conn,
571                           SILC_CLIENT_FILE_MONITOR_DISCONNECT,
572                           SILC_CLIENT_FILE_ERROR, 0, 0,
573                           session->client_entry, session->session_id,
574                           session->filepath, session->monitor_context);
575
576     /* Connection already closed */
577     session->conn = NULL;
578
579     /* If closed by user, destroy the session now */
580     if (session->closed)
581       silc_client_ftp_session_free(session);
582     break;
583
584   case SILC_CLIENT_CONN_ERROR_TIMEOUT:
585     SILC_LOG_DEBUG(("Connecting timeout"));
586
587     /* Call monitor callback */
588     if (session->monitor)
589       (*session->monitor)(session->client, session->conn,
590                           SILC_CLIENT_FILE_MONITOR_ERROR,
591                           SILC_CLIENT_FILE_TIMEOUT, 0, 0,
592                           session->client_entry, session->session_id,
593                           session->filepath, session->monitor_context);
594
595     /* Connection already closed */
596     session->conn = NULL;
597     break;
598
599   default:
600     SILC_LOG_DEBUG(("Connecting error %d", status));
601
602     /* Call monitor callback */
603     if (session->monitor)
604       (*session->monitor)(session->client, session->conn,
605                           SILC_CLIENT_FILE_MONITOR_ERROR,
606                           status != SILC_CLIENT_CONN_ERROR ?
607                           SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
608                           SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
609                           session->client_entry, session->session_id,
610                           session->filepath, session->monitor_context);
611
612     /* Connection already closed */
613     session->conn = NULL;
614     break;
615   }
616 }
617
618 /*************************** File Transfer API ******************************/
619
620 /* Free all file transfer sessions. */
621
622 void silc_client_ftp_free_sessions(SilcClient client)
623 {
624   SilcClientFtpSession session;
625
626   if (!client->internal->ftp_sessions)
627     return;
628
629   silc_dlist_start(client->internal->ftp_sessions);
630   while ((session = silc_dlist_get(client->internal->ftp_sessions)))
631     silc_client_ftp_session_free(session);
632   silc_dlist_del(client->internal->ftp_sessions, session);
633 }
634
635 /* Free file transfer session by client entry. */
636
637 void silc_client_ftp_session_free_client(SilcClient client,
638                                          SilcClientEntry client_entry)
639 {
640   SilcClientFtpSession session;
641
642   if (!client->internal->ftp_sessions)
643     return;
644
645   /* Get the session */
646   silc_dlist_start(client->internal->ftp_sessions);
647   while ((session = silc_dlist_get(client->internal->ftp_sessions)))
648     if (session->client_entry == client_entry)
649       silc_client_ftp_session_free(session);
650 }
651
652 /* Sends a file indicated by the `filepath' to the remote client
653    indicated by the `client_entry'.  This will negotiate a secret key
654    with the remote client before actually starting the transmission of
655    the file.  The `monitor' callback will be called to monitor the
656    transmission of the file. */
657
658 SilcClientFileError
659 silc_client_file_send(SilcClient client,
660                       SilcClientConnection conn,
661                       SilcClientEntry client_entry,
662                       SilcClientConnectionParams *params,
663                       SilcPublicKey public_key,
664                       SilcPrivateKey private_key,
665                       SilcClientFileMonitor monitor,
666                       void *monitor_context,
667                       const char *filepath,
668                       SilcUInt32 *session_id)
669 {
670   SilcClientFtpSession session;
671   SilcBuffer keyagr;
672   char *filename, *path;
673   int fd;
674
675   SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
676
677   if (!client || !client_entry || !filepath || !params ||
678       !public_key || !private_key)
679     return SILC_CLIENT_FILE_ERROR;
680
681   /* Check for existing session for `filepath'. */
682   silc_dlist_start(client->internal->ftp_sessions);
683   while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
684     if (session->filepath && !strcmp(session->filepath, filepath) &&
685         session->client_entry == client_entry)
686       return SILC_CLIENT_FILE_ALREADY_STARTED;
687   }
688
689   /* See whether the file exists and can be opened */
690   fd = silc_file_open(filepath, O_RDONLY);
691   if (fd < 0)
692     return SILC_CLIENT_FILE_NO_SUCH_FILE;
693   silc_file_close(fd);
694
695   /* Add new session */
696   session = silc_calloc(1, sizeof(*session));
697   if (!session)
698     return SILC_CLIENT_FILE_ERROR;
699   session->session_id = ++client->internal->next_session_id;
700   session->client = client;
701   session->server_conn = conn;
702   session->initiator = TRUE;
703   session->client_entry = silc_client_ref_client(client, conn, client_entry);
704   session->monitor = monitor;
705   session->monitor_context = monitor_context;
706   session->filepath = strdup(filepath);
707   session->params = *params;
708   session->public_key = public_key;
709   session->private_key = private_key;
710
711   if (silc_asprintf(&path, "file://%s", filepath) < 0) {
712     silc_free(session);
713     return SILC_CLIENT_FILE_NO_MEMORY;
714   }
715
716   /* Allocate memory filesystem and put the file to it */
717   if (strrchr(path, '/'))
718     filename = strrchr(path, '/') + 1;
719   else
720     filename = (char *)path;
721   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
722                                           SILC_SFTP_FS_PERM_EXEC);
723   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
724                                filename, path);
725
726   session->filesize = silc_file_size(filepath);
727
728   /* If local IP is provided, create listener for incoming key exchange */
729   if (params->local_ip || params->bind_ip) {
730     session->listener =
731       silc_client_listener_add(client,
732                                conn->internal->schedule,
733                                params, public_key, private_key,
734                                silc_client_ftp_connect_completion,
735                                session);
736     if (!session->listener) {
737       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
738                                  "Cannot create listener for file transfer: "
739                                  "%s", strerror(errno));
740       silc_free(session);
741       return SILC_CLIENT_FILE_NO_MEMORY;
742     }
743
744     session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
745                          strdup(params->local_ip));
746     session->port = silc_client_listener_get_local_port(session->listener);
747   }
748
749   SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
750
751   /* Send the key agreement inside FTP packet */
752   keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
753                                              session->port);
754   if (!keyagr) {
755     if (session->listener)
756       silc_client_listener_free(session->listener);
757     silc_free(session);
758     return SILC_CLIENT_FILE_NO_MEMORY;
759   }
760   silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
761                           SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
762                           SILC_STR_UI_CHAR(1),
763                           SILC_STR_DATA(silc_buffer_data(keyagr),
764                                         silc_buffer_len(keyagr)),
765                           SILC_STR_END);
766
767   silc_buffer_free(keyagr);
768   silc_free(path);
769
770   silc_dlist_add(client->internal->ftp_sessions, session);
771   if (session_id)
772     *session_id = session->session_id;
773
774   /* Add session request timeout */
775   if (params && params->timeout_secs)
776     silc_schedule_task_add_timeout(client->schedule,
777                                    silc_client_ftp_timeout, session,
778                                    params->timeout_secs, 0);
779
780   return SILC_CLIENT_FILE_OK;
781 }
782
783 /* Receives a file from a client indicated by the `client_entry'.  The
784    `session_id' indicates the file transmission session and it has been
785    received in the `ftp' client operation function.  This will actually
786    perform the key agreement protocol with the remote client before
787    actually starting the file transmission.  The `monitor' callback
788    will be called to monitor the transmission. */
789
790 SilcClientFileError
791 silc_client_file_receive(SilcClient client,
792                          SilcClientConnection conn,
793                          SilcClientConnectionParams *params,
794                          SilcPublicKey public_key,
795                          SilcPrivateKey private_key,
796                          SilcClientFileMonitor monitor,
797                          void *monitor_context,
798                          const char *path,
799                          SilcUInt32 session_id,
800                          SilcClientFileAskName ask_name,
801                          void *ask_name_context)
802 {
803   SilcClientFtpSession session;
804   SilcBuffer keyagr;
805
806   if (!client || !conn)
807     return SILC_CLIENT_FILE_ERROR;
808
809   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
810
811   /* Get the session */
812   silc_dlist_start(client->internal->ftp_sessions);
813   while ((session = silc_dlist_get(client->internal->ftp_sessions))
814          != SILC_LIST_END) {
815     if (session->session_id == session_id) {
816       break;
817     }
818   }
819
820   if (session == SILC_LIST_END) {
821     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
822     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
823   }
824
825   /* See if we have this session running already */
826   if (session->sftp || session->listener) {
827     SILC_LOG_DEBUG(("Session already started"));
828     return SILC_CLIENT_FILE_ALREADY_STARTED;
829   }
830
831   session->monitor = monitor;
832   session->monitor_context = monitor_context;
833   session->ask_name = ask_name;
834   session->ask_name_context = ask_name_context;
835   session->path = path ? strdup(path) : NULL;
836
837   /* If the hostname and port already exists then the remote client did
838      provide the connection point to us and we won't create listener, but
839      create the connection ourselves. */
840   if (session->hostname && session->port) {
841     SILC_LOG_DEBUG(("Connecting to remote client"));
842     /* Connect to the remote client.  Performs key exchange automatically. */
843     session->op =
844       silc_client_connect_to_client(client, params, public_key, private_key,
845                                     session->hostname, session->port,
846                                     silc_client_ftp_connect_completion,
847                                     session);
848     if (!session->op) {
849       silc_free(session);
850       return SILC_CLIENT_FILE_ERROR;
851     }
852   } else {
853     /* Add the listener for the key agreement */
854     SILC_LOG_DEBUG(("Creating listener for file transfer"));
855     if (!params || (!params->local_ip && !params->bind_ip)) {
856       session->client->internal->ops->say(session->client, session->conn,
857                                           SILC_CLIENT_MESSAGE_ERROR,
858                                           "Cannot create listener for file "
859                                           "transfer; IP address and/or port "
860                                           "not provided");
861       silc_free(session);
862       return SILC_CLIENT_FILE_ERROR;
863     }
864     session->listener =
865       silc_client_listener_add(client, conn->internal->schedule, params,
866                                public_key, private_key,
867                                silc_client_ftp_connect_completion,
868                                session);
869     if (!session->listener) {
870       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
871                                  "Cannot create listener for file transfer: "
872                                  "%s", strerror(errno));
873       silc_free(session);
874       return SILC_CLIENT_FILE_NO_MEMORY;
875     }
876     session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
877                          strdup(params->local_ip));
878     session->port = silc_client_listener_get_local_port(session->listener);
879
880     /* Send the key agreement inside FTP packet */
881     SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
882     keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
883                                                session->port);
884     if (!keyagr) {
885       silc_client_listener_free(session->listener);
886       silc_free(session);
887       return SILC_CLIENT_FILE_NO_MEMORY;
888     }
889     silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
890                             SILC_ID_CLIENT, &session->client_entry->id,
891                             NULL, NULL,
892                             SILC_STR_UI_CHAR(1),
893                             SILC_STR_DATA(silc_buffer_data(keyagr),
894                                           silc_buffer_len(keyagr)),
895                             SILC_STR_END);
896     silc_buffer_free(keyagr);
897
898     /* Add session request timeout */
899     if (params && params->timeout_secs)
900       silc_schedule_task_add_timeout(client->schedule,
901                                      silc_client_ftp_timeout, session,
902                                      params->timeout_secs, 0);
903   }
904
905   return SILC_CLIENT_FILE_OK;
906 }
907
908 /* Closes file transmission session indicated by the `session_id'.
909    If file transmission is being conducted it will be aborted
910    automatically. This function is also used to close the session
911    after successful file transmission. This function can be used
912    also to reject incoming file transmission request. */
913
914 SilcClientFileError silc_client_file_close(SilcClient client,
915                                            SilcClientConnection conn,
916                                            SilcUInt32 session_id)
917 {
918   SilcClientFtpSession session;
919
920   if (!client || !conn)
921     return SILC_CLIENT_FILE_ERROR;
922
923   SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
924
925   /* Get the session */
926   silc_dlist_start(client->internal->ftp_sessions);
927   while ((session = silc_dlist_get(client->internal->ftp_sessions))
928          != SILC_LIST_END) {
929     if (session->session_id == session_id)
930       break;
931   }
932
933   if (session == SILC_LIST_END) {
934     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
935     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
936   }
937
938   if (session->monitor) {
939     (*session->monitor)(session->client, session->conn,
940                         SILC_CLIENT_FILE_MONITOR_CLOSED,
941                         SILC_CLIENT_FILE_OK, 0, 0,
942                         session->client_entry, session->session_id,
943                         session->filepath, session->monitor_context);
944
945     /* No more callbacks to application */
946     session->monitor = NULL;
947   }
948
949   silc_schedule_task_del_by_context(client->schedule, session);
950
951   session->closed = TRUE;
952
953   /* Destroy via timeout */
954   silc_schedule_task_add_timeout(conn->internal->schedule,
955                                  silc_client_file_close_final, session,
956                                  0, 1);
957
958   return SILC_CLIENT_FILE_OK;
959 }
960
961 /************************** FTP Request Processing **************************/
962
963 /* Received file transfer packet.  Only file transfer requests get here.
964    The actual file transfer is handled by the SFTP library when we give it
965    the packet stream wrapped into SilcStream context. */
966
967 SILC_FSM_STATE(silc_client_ftp)
968 {
969   SilcClientConnection conn = fsm_context;
970   SilcClient client = conn->client;
971   SilcPacket packet = state_context;
972   SilcClientFtpSession session;
973   SilcClientID remote_id;
974   SilcClientEntry remote_client;
975   SilcKeyAgreementPayload payload = NULL;
976   char *hostname;
977   SilcUInt16 port;
978
979   SILC_LOG_DEBUG(("Process file transfer packet"));
980
981   if (silc_buffer_len(&packet->buffer) < 1)
982     goto out;
983
984   /* We support file transfer type number 1 (== SFTP) */
985   if (packet->buffer.data[0] != 0x01) {
986     SILC_LOG_DEBUG(("Unsupported file transfer type %d",
987                     packet->buffer.data[0]));
988     goto out;
989   }
990
991   if (!silc_id_str2id(packet->src_id, packet->src_id_len,
992                       SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
993     SILC_LOG_DEBUG(("Invalid client ID"));
994     goto out;
995   }
996
997   /* Check whether we know this client already */
998   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
999   if (!remote_client || !remote_client->internal.valid) {
1000     /** Resolve client info */
1001     silc_client_unref_client(client, conn, remote_client);
1002     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1003                                          client, conn, &remote_id, NULL,
1004                                          silc_client_ftp_client_resolved,
1005                                          fsm));
1006     /* NOT REACHED */
1007   }
1008
1009   /* Get session */
1010   silc_dlist_start(client->internal->ftp_sessions);
1011   while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1012     if (session->client_entry == remote_client &&
1013         (!session->initiator || !session->listener))
1014       break;
1015   }
1016
1017   /* Parse the key agreement payload */
1018   payload =
1019     silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1020                                      silc_buffer_len(&packet->buffer) - 1);
1021   if (!payload) {
1022     SILC_LOG_DEBUG(("Invalid key agreement payload"));
1023     goto out;
1024   }
1025
1026   hostname = silc_key_agreement_get_hostname(payload);
1027   port = silc_key_agreement_get_port(payload);
1028   if (!hostname || !port) {
1029     hostname = NULL;
1030     port = 0;
1031   }
1032
1033   /* If session doesn't exist, we create new one.  If session exists, but
1034      we are responder it means that the remote sent another request and user
1035      hasn't even accepted the first one yet.  We assume this session is new
1036      session as well. */
1037   if (!session || !hostname || !session->initiator) {
1038     /* New file transfer session */
1039     SILC_LOG_DEBUG(("New file transfer session %d",
1040                     client->internal->next_session_id + 1));
1041
1042     session = silc_calloc(1, sizeof(*session));
1043     if (!session)
1044       goto out;
1045     session->session_id = ++client->internal->next_session_id;
1046     session->client = client;
1047     session->server_conn = conn;
1048     session->client_entry = silc_client_ref_client(client, conn,
1049                                                    remote_client);
1050     if (hostname && port) {
1051       session->hostname = strdup(hostname);
1052       session->port = port;
1053     }
1054     silc_dlist_add(client->internal->ftp_sessions, session);
1055
1056     /* Notify application of incoming FTP request */
1057     client->internal->ops->ftp(client, conn, remote_client,
1058                                session->session_id, hostname, port);
1059     goto out;
1060   }
1061
1062   /* Session exists, continue with key agreement protocol. */
1063   SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1064                   session->session_id));
1065
1066   session->hostname = strdup(hostname);
1067   session->port = port;
1068
1069   /* Connect to the remote client.  Performs key exchange automatically. */
1070   session->op =
1071     silc_client_connect_to_client(client, &session->params,
1072                                   session->public_key, session->private_key,
1073                                   session->hostname, session->port,
1074                                   silc_client_ftp_connect_completion,
1075                                   session);
1076   if (!session->op) {
1077     /* Call monitor callback */
1078     if (session->monitor)
1079       (*session->monitor)(session->client, session->conn,
1080                           SILC_CLIENT_FILE_MONITOR_ERROR,
1081                           SILC_CLIENT_FILE_ERROR, 0, 0,
1082                           session->client_entry, session->session_id,
1083                           session->filepath, session->monitor_context);
1084   }
1085
1086  out:
1087   if (payload)
1088     silc_key_agreement_payload_free(payload);
1089   silc_packet_free(packet);
1090   return SILC_FSM_FINISH;
1091 }