Changed SILC code to use new SRT and SCT APIs.
[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 - 2008 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
20 #include "silc.h"
21 #include "silcclient.h"
22 #include "client_internal.h"
23
24 /************************** Types and definitions ***************************/
25
26 /* File transmission session */
27 struct SilcClientFtpSessionStruct {
28   SilcClient client;                  /* Client */
29   SilcClientConnection server_conn;   /* Connection to server */
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 real 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 real 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                                         silc_errno_string(silc_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_schedule_task_del_by_context(session->client->schedule, session);
383
384   silc_dlist_del(session->client->internal->ftp_sessions, session);
385
386   /* Abort connecting  */
387   if (session->op)
388     silc_async_abort(session->op, NULL, NULL);
389
390   /* Destroy SFTP */
391   if (session->sftp) {
392     if (session->initiator)
393       silc_sftp_server_shutdown(session->sftp);
394     else
395       silc_sftp_client_shutdown(session->sftp);
396   }
397   if (session->fs)
398     silc_sftp_fs_memory_free(session->fs);
399
400   /* Destroy listener */
401   if (session->listener)
402     silc_client_listener_free(session->listener);
403
404   /* Destroy wrapped stream */
405   if (session->stream)
406     silc_stream_destroy(session->stream);
407
408   silc_client_unref_client(session->client, session->server_conn,
409                            session->client_entry);
410   silc_free(session->hostname);
411   silc_free(session->filepath);
412   silc_free(session->path);
413   silc_free(session);
414 }
415
416 /* File transfer session timeout */
417
418 SILC_TASK_CALLBACK(silc_client_ftp_timeout)
419 {
420   SilcClientFtpSession session = context;
421
422   SILC_LOG_DEBUG(("Timeout"));
423
424   /* Close connection (destroyes the session context later).  If it is
425      already closed, destroy the session now. */
426   if (session->conn) {
427     silc_client_close_connection(session->client, session->conn);
428     session->conn = NULL;
429   } else {
430     /* Call monitor callback */
431     if (session->monitor)
432       (*session->monitor)(session->client, session->conn,
433                           SILC_CLIENT_FILE_MONITOR_ERROR,
434                           SILC_CLIENT_FILE_TIMEOUT, 0, 0,
435                           session->client_entry, session->session_id,
436                           session->filepath, session->monitor_context);
437
438     silc_client_ftp_session_free(context);
439   }
440 }
441
442 /* File transfer session closing task callback */
443
444 SILC_TASK_CALLBACK(silc_client_file_close_final)
445 {
446   SilcClientFtpSession session = context;
447
448   /* Close connection (destroyes the session context later).  If it is
449      already closed, destroy the session now. */
450   if (session->conn) {
451     silc_client_close_connection(session->client, session->conn);
452     session->conn = NULL;
453   } else {
454     silc_client_ftp_session_free(context);
455   }
456 }
457
458 /* Client resolving callback.  Continues with the FTP packet processing */
459
460 static void silc_client_ftp_client_resolved(SilcClient client,
461                                             SilcClientConnection conn,
462                                             SilcStatus status,
463                                             SilcDList clients,
464                                             void *context)
465 {
466   SilcFSMThread thread = context;
467   SilcPacket packet = silc_fsm_get_state_context(thread);
468
469   /* If no client found, ignore the packet, a silent error */
470   if (!clients) {
471     silc_packet_free(packet);
472     silc_fsm_finish(thread);
473     return;
474   }
475
476   /* Continue processing the packet */
477   SILC_FSM_CALL_CONTINUE(context);
478 }
479
480 /* FTP packet payload encoder/decoder.  This is called for every FTP packet.
481    We add/remove FTP payload in this function, because SFTP library does not
482    add/remove it. */
483
484 static SilcBool
485 silc_client_ftp_coder(SilcStream stream, SilcStreamStatus status,
486                       SilcBuffer buffer, void *context)
487 {
488   /* Pull FTP type in the payload, revealing SFTP payload */
489   if (status == SILC_STREAM_CAN_READ) {
490     if (silc_buffer_len(buffer) >= 1)
491       silc_buffer_pull(buffer, 1);
492     return TRUE;
493   }
494
495   /* Add FTP type before SFTP data */
496   if (status == SILC_STREAM_CAN_WRITE) {
497     if (silc_buffer_format(buffer,
498                            SILC_STR_UI_CHAR(1),
499                            SILC_STR_END) < 0)
500       return FALSE;
501     return TRUE;
502   }
503
504   return FALSE;
505 }
506
507 /* FTP Connection callback.  The SFTP session is started here. */
508
509 static void
510 silc_client_ftp_connect_completion(SilcClient client,
511                                    SilcClientConnection conn,
512                                    SilcClientConnectionStatus status,
513                                    SilcStatus error,
514                                    const char *message,
515                                    void *context)
516 {
517   SilcClientFtpSession session = context;
518
519   session->conn = conn;
520   session->op = NULL;
521
522   silc_schedule_task_del_by_context(client->schedule, session);
523
524   switch (status) {
525   case SILC_CLIENT_CONN_SUCCESS:
526     SILC_LOG_DEBUG(("Connected, conn %p", conn));
527
528     /* Wrap the connection packet stream */
529     session->stream = silc_packet_stream_wrap(conn->stream, SILC_PACKET_FTP,
530                                               0, FALSE,
531                                               silc_client_ftp_coder, session);
532     if (!session->stream) {
533       /* Call monitor callback */
534       if (session->monitor)
535         (*session->monitor)(session->client, session->conn,
536                             SILC_CLIENT_FILE_MONITOR_ERROR,
537                             SILC_CLIENT_FILE_ERROR, 0, 0,
538                             session->client_entry, session->session_id,
539                             session->filepath, session->monitor_context);
540       silc_client_close_connection(client, conn);
541       session->conn = NULL;
542       return;
543     }
544
545     if (!session->initiator) {
546       /* If we are the SFTP client then start the SFTP session and retrieve
547          the info about the file available for download. */
548       session->sftp = silc_sftp_client_start(session->stream,
549                                              conn->internal->schedule,
550                                              silc_client_ftp_version,
551                                              silc_client_ftp_error, session);
552     } else {
553       /* Start SFTP server */
554       session->sftp = silc_sftp_server_start(session->stream,
555                                              conn->internal->schedule,
556                                              silc_client_ftp_error, session,
557                                              session->fs);
558
559       /* Monitor transmission */
560       silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
561                                    silc_client_ftp_monitor, session);
562     }
563
564     break;
565
566   case SILC_CLIENT_CONN_DISCONNECTED:
567     SILC_LOG_DEBUG(("Disconnected %p", conn));
568
569     /* Call monitor callback */
570     if (session->monitor)
571       (*session->monitor)(session->client, session->conn,
572                           SILC_CLIENT_FILE_MONITOR_DISCONNECT,
573                           SILC_CLIENT_FILE_ERROR, 0, 0,
574                           session->client_entry, session->session_id,
575                           session->filepath, session->monitor_context);
576
577     /* Connection already closed */
578     session->conn = NULL;
579
580     /* If closed by user, destroy the session now */
581     if (session->closed)
582       silc_client_ftp_session_free(session);
583     break;
584
585   case SILC_CLIENT_CONN_ERROR_TIMEOUT:
586     SILC_LOG_DEBUG(("Connecting timeout"));
587
588     /* Call monitor callback */
589     if (session->monitor)
590       (*session->monitor)(session->client, session->conn,
591                           SILC_CLIENT_FILE_MONITOR_ERROR,
592                           SILC_CLIENT_FILE_TIMEOUT, 0, 0,
593                           session->client_entry, session->session_id,
594                           session->filepath, session->monitor_context);
595
596     /* Connection already closed */
597     session->conn = NULL;
598     break;
599
600   default:
601     SILC_LOG_DEBUG(("Connecting error %d", status));
602
603     /* Call monitor callback */
604     if (session->monitor)
605       (*session->monitor)(session->client, session->conn,
606                           SILC_CLIENT_FILE_MONITOR_ERROR,
607                           status != SILC_CLIENT_CONN_ERROR ?
608                           SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED :
609                           SILC_CLIENT_FILE_CONNECT_FAILED, 0, 0,
610                           session->client_entry, session->session_id,
611                           session->filepath, session->monitor_context);
612
613     /* Connection already closed */
614     session->conn = NULL;
615     break;
616   }
617 }
618
619 /*************************** File Transfer API ******************************/
620
621 /* Free all file transfer sessions. */
622
623 void silc_client_ftp_free_sessions(SilcClient client)
624 {
625   SilcClientFtpSession session;
626
627   if (!client->internal->ftp_sessions)
628     return;
629
630   silc_dlist_start(client->internal->ftp_sessions);
631   while ((session = silc_dlist_get(client->internal->ftp_sessions)))
632     silc_client_ftp_session_free(session);
633   silc_dlist_del(client->internal->ftp_sessions, session);
634 }
635
636 /* Free file transfer session by client entry. */
637
638 void silc_client_ftp_session_free_client(SilcClient client,
639                                          SilcClientEntry client_entry)
640 {
641   SilcClientFtpSession session;
642
643   if (!client->internal->ftp_sessions)
644     return;
645
646   /* Get the session */
647   silc_dlist_start(client->internal->ftp_sessions);
648   while ((session = silc_dlist_get(client->internal->ftp_sessions)))
649     if (session->client_entry == client_entry)
650       silc_client_ftp_session_free(session);
651 }
652
653 /* Sends a file indicated by the `filepath' to the remote client
654    indicated by the `client_entry'.  This will negotiate a secret key
655    with the remote client before actually starting the transmission of
656    the file.  The `monitor' callback will be called to monitor the
657    transmission of the file. */
658
659 SilcClientFileError
660 silc_client_file_send(SilcClient client,
661                       SilcClientConnection conn,
662                       SilcClientEntry client_entry,
663                       SilcClientConnectionParams *params,
664                       SilcPublicKey public_key,
665                       SilcPrivateKey private_key,
666                       SilcClientFileMonitor monitor,
667                       void *monitor_context,
668                       const char *filepath,
669                       SilcUInt32 *session_id)
670 {
671   SilcClientFtpSession session;
672   SilcBuffer keyagr;
673   char *filename, *path;
674   int fd;
675
676   SILC_LOG_DEBUG(("File send request (file: %s)", filepath));
677
678   if (!client || !client_entry || !filepath || !params ||
679       !public_key || !private_key)
680     return SILC_CLIENT_FILE_ERROR;
681
682   /* Check for existing session for `filepath'. */
683   silc_dlist_start(client->internal->ftp_sessions);
684   while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
685     if (session->filepath && !strcmp(session->filepath, filepath) &&
686         session->client_entry == client_entry)
687       return SILC_CLIENT_FILE_ALREADY_STARTED;
688   }
689
690   /* See whether the file exists and can be opened */
691   fd = silc_file_open(filepath, O_RDONLY);
692   if (fd < 0)
693     return SILC_CLIENT_FILE_NO_SUCH_FILE;
694   silc_file_close(fd);
695
696   /* Add new session */
697   session = silc_calloc(1, sizeof(*session));
698   if (!session)
699     return SILC_CLIENT_FILE_ERROR;
700   session->session_id = ++client->internal->next_session_id;
701   session->client = client;
702   session->server_conn = conn;
703   session->initiator = TRUE;
704   session->client_entry = silc_client_ref_client(client, conn, client_entry);
705   session->monitor = monitor;
706   session->monitor_context = monitor_context;
707   session->filepath = strdup(filepath);
708   session->params = *params;
709   session->public_key = public_key;
710   session->private_key = private_key;
711
712   if (silc_asprintf(&path, "file://%s", filepath) < 0) {
713     silc_free(session);
714     return SILC_CLIENT_FILE_NO_MEMORY;
715   }
716
717   /* Allocate memory filesystem and put the file to it */
718   if (strrchr(path, '/'))
719     filename = strrchr(path, '/') + 1;
720   else
721     filename = (char *)path;
722   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
723                                           SILC_SFTP_FS_PERM_EXEC);
724   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
725                                filename, path);
726
727   session->filesize = silc_file_size(filepath);
728
729   /* If local IP is provided, create listener for incoming key exchange */
730   if (params->local_ip || params->bind_ip) {
731     session->listener =
732       silc_client_listener_add(client,
733                                conn->internal->schedule,
734                                params, public_key, private_key,
735                                silc_client_ftp_connect_completion,
736                                session);
737     if (!session->listener) {
738       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
739                                  "Cannot create listener for file transfer: "
740                                  "%s", silc_errno_string(silc_errno));
741
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", silc_errno_string(silc_errno));
875
876       silc_free(session);
877       return SILC_CLIENT_FILE_NO_MEMORY;
878     }
879     session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
880                          strdup(params->local_ip));
881     session->port = silc_client_listener_get_local_port(session->listener);
882
883     /* Send the key agreement inside FTP packet */
884     SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
885     keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
886                                                session->port);
887     if (!keyagr) {
888       silc_client_listener_free(session->listener);
889       silc_free(session);
890       return SILC_CLIENT_FILE_NO_MEMORY;
891     }
892     silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
893                             SILC_ID_CLIENT, &session->client_entry->id,
894                             NULL, NULL,
895                             SILC_STR_UI_CHAR(1),
896                             SILC_STR_DATA(silc_buffer_data(keyagr),
897                                           silc_buffer_len(keyagr)),
898                             SILC_STR_END);
899     silc_buffer_free(keyagr);
900
901     /* Add session request timeout */
902     if (params && params->timeout_secs)
903       silc_schedule_task_add_timeout(client->schedule,
904                                      silc_client_ftp_timeout, session,
905                                      params->timeout_secs, 0);
906   }
907
908   return SILC_CLIENT_FILE_OK;
909 }
910
911 /* Closes file transmission session indicated by the `session_id'.
912    If file transmission is being conducted it will be aborted
913    automatically. This function is also used to close the session
914    after successful file transmission. This function can be used
915    also to reject incoming file transmission request. */
916
917 SilcClientFileError silc_client_file_close(SilcClient client,
918                                            SilcClientConnection conn,
919                                            SilcUInt32 session_id)
920 {
921   SilcClientFtpSession session;
922
923   if (!client || !conn)
924     return SILC_CLIENT_FILE_ERROR;
925
926   SILC_LOG_DEBUG(("Closing file transer session %d", session_id));
927
928   /* Get the session */
929   silc_dlist_start(client->internal->ftp_sessions);
930   while ((session = silc_dlist_get(client->internal->ftp_sessions))
931          != SILC_LIST_END) {
932     if (session->session_id == session_id)
933       break;
934   }
935
936   if (session == SILC_LIST_END) {
937     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
938     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
939   }
940
941   if (session->monitor) {
942     (*session->monitor)(session->client, session->conn,
943                         SILC_CLIENT_FILE_MONITOR_CLOSED,
944                         SILC_CLIENT_FILE_OK, 0, 0,
945                         session->client_entry, session->session_id,
946                         session->filepath, session->monitor_context);
947
948     /* No more callbacks to application */
949     session->monitor = NULL;
950   }
951
952   silc_schedule_task_del_by_context(client->schedule, session);
953
954   session->closed = TRUE;
955
956   /* Destroy via timeout */
957   silc_schedule_task_add_timeout(conn->internal->schedule,
958                                  silc_client_file_close_final, session,
959                                  0, 1);
960
961   return SILC_CLIENT_FILE_OK;
962 }
963
964 /************************** FTP Request Processing **************************/
965
966 /* Received file transfer packet.  Only file transfer requests get here.
967    The actual file transfer is handled by the SFTP library when we give it
968    the packet stream wrapped into SilcStream context. */
969
970 SILC_FSM_STATE(silc_client_ftp)
971 {
972   SilcClientConnection conn = fsm_context;
973   SilcClient client = conn->client;
974   SilcPacket packet = state_context;
975   SilcClientFtpSession session;
976   SilcClientID remote_id;
977   SilcClientEntry remote_client;
978   SilcKeyAgreementPayload payload = NULL;
979   char *hostname;
980   SilcUInt16 port;
981
982   SILC_LOG_DEBUG(("Process file transfer packet"));
983
984   if (silc_buffer_len(&packet->buffer) < 1)
985     goto out;
986
987   /* We support file transfer type number 1 (== SFTP) */
988   if (packet->buffer.data[0] != 0x01) {
989     SILC_LOG_DEBUG(("Unsupported file transfer type %d",
990                     packet->buffer.data[0]));
991     goto out;
992   }
993
994   if (!silc_id_str2id(packet->src_id, packet->src_id_len,
995                       SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
996     SILC_LOG_DEBUG(("Invalid client ID"));
997     goto out;
998   }
999
1000   /* Check whether we know this client already */
1001   remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
1002   if (!remote_client || !remote_client->internal.valid) {
1003     /** Resolve client info */
1004     silc_client_unref_client(client, conn, remote_client);
1005     SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
1006                                          client, conn, &remote_id, NULL,
1007                                          silc_client_ftp_client_resolved,
1008                                          fsm));
1009     /* NOT REACHED */
1010   }
1011
1012   /* Get session */
1013   silc_dlist_start(client->internal->ftp_sessions);
1014   while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
1015     if (session->client_entry == remote_client &&
1016         (!session->initiator || !session->listener))
1017       break;
1018   }
1019
1020   /* Parse the key agreement payload */
1021   payload =
1022     silc_key_agreement_payload_parse(silc_buffer_data(&packet->buffer) + 1,
1023                                      silc_buffer_len(&packet->buffer) - 1);
1024   if (!payload) {
1025     SILC_LOG_DEBUG(("Invalid key agreement payload"));
1026     goto out;
1027   }
1028
1029   hostname = silc_key_agreement_get_hostname(payload);
1030   port = silc_key_agreement_get_port(payload);
1031   if (!hostname || !port) {
1032     hostname = NULL;
1033     port = 0;
1034   }
1035
1036   /* If session doesn't exist, we create new one.  If session exists, but
1037      we are responder it means that the remote sent another request and user
1038      hasn't even accepted the first one yet.  We assume this session is new
1039      session as well. */
1040   if (!session || !hostname || !session->initiator) {
1041     /* New file transfer session */
1042     SILC_LOG_DEBUG(("New file transfer session %d",
1043                     client->internal->next_session_id + 1));
1044
1045     session = silc_calloc(1, sizeof(*session));
1046     if (!session)
1047       goto out;
1048     session->session_id = ++client->internal->next_session_id;
1049     session->client = client;
1050     session->server_conn = conn;
1051     session->client_entry = silc_client_ref_client(client, conn,
1052                                                    remote_client);
1053     if (hostname && port) {
1054       session->hostname = strdup(hostname);
1055       session->port = port;
1056     }
1057     silc_dlist_add(client->internal->ftp_sessions, session);
1058
1059     /* Notify application of incoming FTP request */
1060     client->internal->ops->ftp(client, conn, remote_client,
1061                                session->session_id, hostname, port);
1062     goto out;
1063   }
1064
1065   /* Session exists, continue with key agreement protocol. */
1066   SILC_LOG_DEBUG(("Session %d exists, connecting to remote client",
1067                   session->session_id));
1068
1069   session->hostname = strdup(hostname);
1070   session->port = port;
1071
1072   /* Connect to the remote client.  Performs key exchange automatically. */
1073   session->op =
1074     silc_client_connect_to_client(client, &session->params,
1075                                   session->public_key, session->private_key,
1076                                   session->hostname, session->port,
1077                                   silc_client_ftp_connect_completion,
1078                                   session);
1079   if (!session->op) {
1080     /* Call monitor callback */
1081     if (session->monitor)
1082       (*session->monitor)(session->client, session->conn,
1083                           SILC_CLIENT_FILE_MONITOR_ERROR,
1084                           SILC_CLIENT_FILE_ERROR, 0, 0,
1085                           session->client_entry, session->session_id,
1086                           session->filepath, session->monitor_context);
1087   }
1088
1089  out:
1090   if (payload)
1091     silc_key_agreement_payload_free(payload);
1092   silc_packet_free(packet);
1093   return SILC_FSM_FINISH;
1094 }