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