Added 'send_events' boolean argument to the 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 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 "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 static int
26 silc_client_connect_to_client(SilcClient client, 
27                               SilcClientConnection conn, int port,
28                               char *host, void *context);
29 static int 
30 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx);
31 SILC_TASK_CALLBACK(silc_client_ftp_connected);
32 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
33                                                 int sock);
34
35 /* File transmission session */
36 struct SilcClientFtpSessionStruct {
37   SilcUInt32 session_id;
38   SilcClient client;
39   SilcClientConnection conn;
40   SilcClientEntry client_entry;
41
42   SilcSocketConnection sock;
43   SilcBuffer packet;
44
45   char *hostname;
46   SilcUInt16 port;
47   int listener;
48
49   SilcClientFileMonitor monitor;
50   void *monitor_context;
51   char *filepath;
52   char *path;
53
54   SilcSFTP sftp;
55   SilcSFTPFilesystem fs;
56   bool server;
57
58   SilcSFTPHandle dir_handle;
59   SilcSFTPHandle read_handle;
60   SilcUInt64 filesize;
61   SilcUInt64 read_offset;
62   int fd;
63 };
64
65 SILC_TASK_CALLBACK(silc_client_ftp_connected)
66 {
67   SilcClientInternalConnectContext *ctx =
68     (SilcClientInternalConnectContext *)context;
69   SilcClient client = ctx->client;
70   SilcClientConnection conn = ctx->conn;
71   SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
72   int opt, opt_len = sizeof(opt);
73
74   SILC_LOG_DEBUG(("Start"));
75
76   /* Check the socket status as it might be in error */
77   silc_net_get_socket_opt(fd, SOL_SOCKET, SO_ERROR, &opt, &opt_len);
78   if (opt != 0) {
79     if (ctx->tries < 2) {
80       /* Connection failed but lets try again */
81       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
82                                  "Could not connect to client %s: %s",
83                                  ctx->host, strerror(opt));
84       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
85                                  "Connecting to port %d of client %s resumed", 
86                                  ctx->port, ctx->host);
87
88       /* Unregister old connection try */
89       silc_schedule_unset_listen_fd(client->schedule, fd);
90       silc_net_close_connection(fd);
91       silc_schedule_task_del(client->schedule, ctx->task);
92
93       /* Try again */
94       silc_client_connect_to_client_internal(ctx);
95       ctx->tries++;
96     } else {
97       /* Connection failed and we won't try anymore */
98       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
99                                  "Could not connect to client %s: %s",
100                                  ctx->host, strerror(opt));
101       silc_schedule_unset_listen_fd(client->schedule, fd);
102       silc_net_close_connection(fd);
103       silc_schedule_task_del(client->schedule, ctx->task);
104       silc_free(ctx);
105       silc_client_ftp_session_free(session);
106     }
107     return;
108   }
109
110   silc_schedule_unset_listen_fd(client->schedule, fd);
111   silc_schedule_task_del(client->schedule, ctx->task);
112
113   /* Start the key agreement */
114   silc_client_ftp_start_key_agreement(session, fd);
115 }
116
117 static int 
118 silc_client_connect_to_client_internal(SilcClientInternalConnectContext *ctx)
119 {
120   int sock;
121
122   /* Create connection to server asynchronously */
123   sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
124   if (sock < 0)
125     return -1;
126
127   /* Register task that will receive the async connect and will
128      read the result. */
129   ctx->task = silc_schedule_task_add(ctx->client->schedule, sock, 
130                                      silc_client_ftp_connected,
131                                      (void *)ctx, 0, 0, 
132                                      SILC_TASK_FD,
133                                      SILC_TASK_PRI_NORMAL);
134   silc_schedule_set_listen_fd(ctx->client->schedule, sock, SILC_TASK_WRITE,
135                               FALSE);
136   ctx->sock = sock;
137   return sock;
138 }
139
140 static int
141 silc_client_connect_to_client(SilcClient client, 
142                               SilcClientConnection conn, int port,
143                               char *host, void *context)
144 {
145   SilcClientInternalConnectContext *ctx;
146
147   /* Allocate internal context for connection process. This is
148      needed as we are doing async connecting. */
149   ctx = silc_calloc(1, sizeof(*ctx));
150   ctx->client = client;
151   ctx->conn = conn;
152   ctx->host = strdup(host);
153   ctx->port = port;
154   ctx->tries = 0;
155   ctx->context = context;
156
157   /* Do the actual connecting process */
158   return silc_client_connect_to_client_internal(ctx);
159 }
160
161 /* SFTP packet send callback. This will use preallocated buffer to avoid
162    reallocation of outgoing data buffer everytime. */
163
164 static void silc_client_ftp_send_packet(SilcBuffer packet, void *context)
165 {
166   SilcClientFtpSession session = (SilcClientFtpSession)context;
167   SilcClient client = session->client;
168
169   SILC_LOG_DEBUG(("Start"));
170
171   /* Allocate outgoing packet */
172   if (!session->packet)
173     session->packet = silc_buffer_alloc(1 + packet->len);
174
175   /* Enlarge outgoing packet if needed */
176   if (session->packet->truelen < 1 + packet->len)
177     session->packet = silc_buffer_realloc(session->packet, 1 + packet->len);
178
179   /* Encode packet */
180   silc_buffer_pull_tail(session->packet, 1 + packet->len);
181   silc_buffer_format(session->packet,
182                      SILC_STR_UI_CHAR(1),
183                      SILC_STR_UI_XNSTRING(packet->data, packet->len),
184                      SILC_STR_END);
185
186   /* Send the packet immediately */
187   silc_client_packet_send(client, session->sock, SILC_PACKET_FTP, NULL, 
188                           0, NULL, NULL, session->packet->data, 
189                           session->packet->len, TRUE);
190
191   /* Clear buffer */
192   session->packet->data = session->packet->tail = session->packet->head;
193   session->packet->len = 0;
194 }
195
196 /* SFTP monitor callback for SFTP server. This reports the application 
197    how the transmission is going along. This function is for the client
198    who made the file available for download. */
199
200 static void silc_client_ftp_monitor(SilcSFTP sftp,
201                                     SilcSFTPMonitors type,
202                                     const SilcSFTPMonitorData data,
203                                     void *context)
204 {
205   SilcClientFtpSession session = (SilcClientFtpSession)context;
206
207   if (type == SILC_SFTP_MONITOR_READ) {
208     /* Call the monitor for application */
209     if (session->monitor)
210       (*session->monitor)(session->client, session->conn,
211                           SILC_CLIENT_FILE_MONITOR_SEND,
212                           SILC_CLIENT_FILE_OK,
213                           data->offset, session->filesize,
214                           session->client_entry, session->session_id,
215                           session->filepath, session->monitor_context);
216   }
217 }
218
219 /* Returns the read data. This is the downloader's function (client side)
220    to receive the read data and read more until EOF is received from
221    the other side. This will also monitor the transmission and notify
222    the application. */
223
224 static void silc_client_ftp_data(SilcSFTP sftp,
225                                  SilcSFTPStatus status,
226                                  const unsigned char *data,
227                                  SilcUInt32 data_len,
228                                  void *context)
229 {
230   SilcClientFtpSession session = (SilcClientFtpSession)context;
231
232   SILC_LOG_DEBUG(("Start"));
233
234   if (status == SILC_SFTP_STATUS_EOF) {
235     /* EOF received */
236
237     /* Close the handle */
238     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
239     session->read_handle = NULL;
240
241     /* Close the read file descriptor */
242     silc_file_close(session->fd);
243     return;
244   }
245
246   if (status != SILC_SFTP_STATUS_OK) {
247     /* Call monitor callback */
248     if (session->monitor)
249       (*session->monitor)(session->client, session->conn,
250                           SILC_CLIENT_FILE_MONITOR_ERROR, 
251                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
252                            SILC_CLIENT_FILE_NO_SUCH_FILE :
253                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
254                            SILC_CLIENT_FILE_PERMISSION_DENIED :
255                            SILC_CLIENT_FILE_ERROR), 0, 0,
256                           session->client_entry, session->session_id,
257                           session->filepath, session->monitor_context);
258
259     /* Close the handle */
260     silc_sftp_close(sftp, session->read_handle, NULL, NULL);
261     session->read_handle = NULL;
262
263     /* Close the read file descriptor */
264     silc_file_close(session->fd);
265     return;
266   }
267
268   /* Read more, until EOF is received */
269   session->read_offset += data_len;
270   silc_sftp_read(sftp, session->read_handle, session->read_offset, 
271                  SILC_PACKET_MAX_LEN - 1024,
272                  silc_client_ftp_data, session);
273
274   /* Call monitor callback */
275   if (session->monitor)
276     (*session->monitor)(session->client, session->conn,
277                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
278                         SILC_CLIENT_FILE_OK,
279                         session->read_offset, session->filesize,
280                         session->client_entry, session->session_id,
281                         session->filepath, session->monitor_context);
282
283   /* Write the read data to the real file */
284   silc_file_write(session->fd, data, data_len);
285 }
286
287 /* Returns handle for the opened file. This is the downloader's function.
288    This will begin reading the data from the file. */
289
290 static void silc_client_ftp_open_handle(SilcSFTP sftp,
291                                         SilcSFTPStatus status,
292                                         SilcSFTPHandle handle,
293                                         void *context)
294 {
295   SilcClientFtpSession session = (SilcClientFtpSession)context;
296   char path[512];
297
298   SILC_LOG_DEBUG(("Start"));
299
300   if (status != SILC_SFTP_STATUS_OK) {
301     /* Call monitor callback */
302     if (session->monitor)
303       (*session->monitor)(session->client, session->conn,
304                           SILC_CLIENT_FILE_MONITOR_ERROR, 
305                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
306                            SILC_CLIENT_FILE_NO_SUCH_FILE :
307                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
308                            SILC_CLIENT_FILE_PERMISSION_DENIED :
309                            SILC_CLIENT_FILE_ERROR), 0, 0,
310                           session->client_entry, session->session_id,
311                           session->filepath, session->monitor_context);
312     return;
313   }
314
315   /* Open the actual local file */
316   memset(path, 0, sizeof(path));
317   snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
318            session->path : "", session->filepath);
319   session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
320   if (session->fd < 0) {
321     /* Call monitor callback */
322     session->client->internal->ops->say(session->client, session->conn, 
323                                         SILC_CLIENT_MESSAGE_ERROR, 
324                                         "File `%s' open failed: %s", 
325                                         session->filepath,
326                                         strerror(errno));
327
328     if (session->monitor)
329       (*session->monitor)(session->client, session->conn,
330                           SILC_CLIENT_FILE_MONITOR_ERROR, 
331                           SILC_CLIENT_FILE_ERROR, 0, 0,
332                           session->client_entry, session->session_id,
333                           session->filepath, session->monitor_context);
334     return;
335   }
336
337   session->read_handle = handle;
338
339   /* Now, start reading the file */
340   silc_sftp_read(sftp, session->read_handle, session->read_offset,
341                  SILC_PACKET_MAX_LEN - 1024,
342                  silc_client_ftp_data, session);
343
344   /* Call monitor callback */
345   if (session->monitor)
346     (*session->monitor)(session->client, session->conn,
347                         SILC_CLIENT_FILE_MONITOR_RECEIVE,
348                         SILC_CLIENT_FILE_OK,
349                         session->read_offset, session->filesize,
350                         session->client_entry, session->session_id,
351                         session->filepath, session->monitor_context);
352 }
353
354 /* Returns the file name available for download. This is the downloader's
355    function. */
356
357 static void silc_client_ftp_readdir_name(SilcSFTP sftp,
358                                          SilcSFTPStatus status,
359                                          const SilcSFTPName name,
360                                          void *context)
361 {
362   SilcClientFtpSession session = (SilcClientFtpSession)context;
363   SilcSFTPAttributesStruct attr;
364
365   SILC_LOG_DEBUG(("Start"));
366
367   if (status != SILC_SFTP_STATUS_OK) {
368     /* Call monitor callback */
369     if (session->monitor)
370       (*session->monitor)(session->client, session->conn,
371                           SILC_CLIENT_FILE_MONITOR_ERROR, 
372                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
373                            SILC_CLIENT_FILE_NO_SUCH_FILE :
374                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
375                            SILC_CLIENT_FILE_PERMISSION_DENIED :
376                            SILC_CLIENT_FILE_ERROR), 0, 0,
377                           session->client_entry, session->session_id,
378                           session->filepath, session->monitor_context);
379     return;
380   }
381
382   /* Now open the file */
383   memset(&attr, 0, sizeof(attr));
384   silc_sftp_open(sftp, name->filename[0], SILC_SFTP_FXF_READ, &attr,
385                  silc_client_ftp_open_handle, session);
386
387   /* Save the important attributes like filename and file size */
388   session->filepath = strdup(name->filename[0]);
389   session->filesize = name->attrs[0]->size;
390
391   /* Close the directory handle */
392   silc_sftp_close(sftp, session->dir_handle, NULL, NULL);
393   session->dir_handle = NULL;
394 }
395
396 /* Returns the file handle after giving opendir command. This is the
397    downloader's function. */
398
399 static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
400                                            SilcSFTPStatus status,
401                                            SilcSFTPHandle handle,
402                                            void *context)
403 {
404   SilcClientFtpSession session = (SilcClientFtpSession)context;
405
406   SILC_LOG_DEBUG(("Start"));
407
408   if (status != SILC_SFTP_STATUS_OK) {
409     /* Call monitor callback */
410     if (session->monitor)
411       (*session->monitor)(session->client, session->conn,
412                           SILC_CLIENT_FILE_MONITOR_ERROR, 
413                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
414                            SILC_CLIENT_FILE_NO_SUCH_FILE :
415                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
416                            SILC_CLIENT_FILE_PERMISSION_DENIED :
417                            SILC_CLIENT_FILE_ERROR), 0, 0,
418                           session->client_entry, session->session_id,
419                           session->filepath, session->monitor_context);
420     return;
421   }
422
423   /* Now, read the directory */
424   silc_sftp_readdir(sftp, handle, silc_client_ftp_readdir_name, session);
425   session->dir_handle = handle;
426 }
427
428 /* SFTP version callback for SFTP client. This is the downloader's function
429    after initializing the SFTP connection to the remote client. This will
430    find out the filename available for download. */
431
432 static void silc_client_ftp_version(SilcSFTP sftp,
433                                     SilcSFTPStatus status,
434                                     SilcSFTPVersion version,
435                                     void *context)
436 {
437   SilcClientFtpSession session = (SilcClientFtpSession)context;
438
439   SILC_LOG_DEBUG(("Start"));
440
441   if (status != SILC_SFTP_STATUS_OK) {
442     /* Call monitor callback */
443     if (session->monitor)
444       (*session->monitor)(session->client, session->conn,
445                           SILC_CLIENT_FILE_MONITOR_ERROR, 
446                           (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
447                            SILC_CLIENT_FILE_NO_SUCH_FILE :
448                            status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
449                            SILC_CLIENT_FILE_PERMISSION_DENIED :
450                            SILC_CLIENT_FILE_ERROR), 0, 0,
451                           session->client_entry, session->session_id,
452                           session->filepath, session->monitor_context);
453     return;
454   }
455
456   /* The SFTP session is open, now retrieve the info about available file. */
457   silc_sftp_opendir(sftp, "", silc_client_ftp_opendir_handle, session);
458 }
459
460 /* This callback is called after the key agreement protocol has been
461    performed. This calls the final completion callback for the application. */
462
463 SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
464 {
465   SilcProtocol protocol = (SilcProtocol)context;
466   SilcClientKEInternalContext *ctx = 
467     (SilcClientKEInternalContext *)protocol->context;
468   SilcClientFtpSession session = (SilcClientFtpSession)ctx->context;
469   SilcClientConnection conn = (SilcClientConnection)ctx->sock->user_data;
470
471   SILC_LOG_DEBUG(("Start"));
472
473   if (protocol->state == SILC_PROTOCOL_STATE_ERROR ||
474       protocol->state == SILC_PROTOCOL_STATE_FAILURE) {
475     /* Call monitor callback */
476     if (session->monitor)
477       (*session->monitor)(session->client, session->conn,
478                           SILC_CLIENT_FILE_MONITOR_ERROR, 
479                           SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED, 0, 0,
480                           session->client_entry, session->session_id,
481                           session->filepath, session->monitor_context);
482
483     /* Error occured during protocol */
484     silc_ske_free_key_material(ctx->keymat);
485     goto out;
486   }
487
488   /* Set keys into use */
489   silc_client_protocol_ke_set_keys(ctx->ske, ctx->sock, ctx->keymat,
490                                    ctx->ske->prop->cipher,
491                                    ctx->ske->prop->pkcs,
492                                    ctx->ske->prop->hash,
493                                    ctx->ske->prop->hmac,
494                                    ctx->ske->prop->group,
495                                    ctx->responder);
496
497   if (!session->server) {
498     /* If we are the SFTP client then start the SFTP session and retrieve
499        the info about the file available for download. */
500     session->sftp = silc_sftp_client_start(silc_client_ftp_send_packet,
501                                            session, silc_client_ftp_version, 
502                                            session);
503   } else {
504     /* Start SFTP server */
505     session->sftp = silc_sftp_server_start(silc_client_ftp_send_packet,
506                                            session, session->fs);
507
508     /* Monitor transmission */
509     silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
510                                  silc_client_ftp_monitor, session);
511   }
512
513   /* Set this as active session */
514   conn->active_session = session;
515
516  out:
517   silc_ske_free_key_material(ctx->keymat);
518   if (ctx->ske)
519     silc_ske_free(ctx->ske);
520   silc_free(ctx->dest_id);
521   ctx->sock->protocol = NULL;
522   silc_socket_free(ctx->sock);
523   silc_free(ctx);
524   silc_protocol_free(protocol);
525 }
526
527 /* The downloader's function to start the key agreement protocol with the
528    remote client after we have connected to it. */
529
530 static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
531                                                 int sock)
532 {
533   SilcClient client = session->client;
534   SilcClientKEInternalContext *proto_ctx;
535   SilcProtocol protocol;
536   SilcClientConnection conn;
537   void *context;
538
539   SILC_LOG_DEBUG(("Start"));
540
541   /* Call monitor callback */
542   if (session->monitor)
543     (*session->monitor)(session->client, session->conn,
544                         SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
545                         SILC_CLIENT_FILE_OK, 0, 0,
546                         session->client_entry, session->session_id,
547                         NULL, session->monitor_context);
548
549   /* Add new connection for this session */
550   conn = silc_client_add_connection(client, NULL, session->hostname,
551                                     session->port, session);
552
553   /* Allocate new socket connection object */
554   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, (void *)conn, &conn->sock);
555   conn->sock->hostname = strdup(session->hostname);
556   conn->sock->port = silc_net_get_remote_port(sock);
557   session->sock = silc_socket_dup(conn->sock);
558
559   /* Allocate internal context for key exchange protocol. This is
560      sent as context for the protocol. */
561   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
562   proto_ctx->client = client;
563   proto_ctx->sock = silc_socket_dup(conn->sock);
564   proto_ctx->rng = client->rng;
565   proto_ctx->responder = FALSE;
566   proto_ctx->context = session;
567   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
568   proto_ctx->verify = silc_client_protocol_ke_verify_key;
569
570   /* Perform key exchange protocol. */
571   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
572                       &protocol, (void *)proto_ctx,
573                       silc_client_ftp_key_agreement_final);
574   conn->sock->protocol = protocol;
575
576   /* Register the connection for network input and output. This sets
577      that scheduler will listen for incoming packets for this connection 
578      and sets that outgoing packets may be sent to this connection as well.
579      However, this doesn't set the scheduler for outgoing traffic, it will 
580      be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
581      later when outgoing data is available. */
582   context = (void *)client;
583   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
584
585   /* Execute the protocol */
586   silc_protocol_execute(protocol, client->schedule, 0, 0);
587 }
588
589 /* The remote client's (the client who made the file available for download)
590    function for accepting incoming connection. This will also start the
591    key agreement protocol with the other client. */
592
593 SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
594 {
595   SilcClientFtpSession session = (SilcClientFtpSession)context;
596   SilcClient client = session->client;
597   SilcClientConnection conn;
598   SilcSocketConnection newsocket;
599   SilcClientKEInternalContext *proto_ctx;
600   int sock;
601
602   SILC_LOG_DEBUG(("Start"));
603
604   sock = silc_net_accept_connection(session->listener);
605   if (sock < 0) {
606     /* Call monitor callback */
607     if (session->monitor)
608       (*session->monitor)(session->client, session->conn,
609                           SILC_CLIENT_FILE_MONITOR_ERROR, 
610                           SILC_CLIENT_FILE_ERROR, 0, 0,
611                           session->client_entry, session->session_id,
612                           session->filepath, session->monitor_context);
613     return;
614   }
615
616   /* Set socket options */
617   silc_net_set_socket_nonblock(sock);
618   silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
619
620   /* Allocate new socket connection object */
621   silc_socket_alloc(sock, SILC_SOCKET_TYPE_CLIENT, NULL, &newsocket);
622
623   /* Perform name and address lookups for the remote host. */
624   silc_net_check_host_by_sock(sock, &newsocket->hostname, &newsocket->ip);
625   if (!newsocket->hostname && !newsocket->ip) {
626     /* Call monitor callback */
627     if (session->monitor)
628       (*session->monitor)(session->client, session->conn,
629                           SILC_CLIENT_FILE_MONITOR_ERROR, 
630                           SILC_CLIENT_FILE_ERROR, 0, 0,
631                           session->client_entry, session->session_id,
632                           session->filepath, session->monitor_context);
633     return;
634   }
635   if (!newsocket->hostname)
636     newsocket->hostname = strdup(newsocket->ip);
637   newsocket->port = silc_net_get_remote_port(sock);
638
639   /* Call monitor callback */
640   if (session->monitor)
641     (*session->monitor)(session->client, session->conn,
642                         SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
643                         SILC_CLIENT_FILE_OK, 0, 0,
644                         session->client_entry, session->session_id,
645                         NULL, session->monitor_context);
646
647   /* Add new connection for this session */
648   conn = silc_client_add_connection(client, NULL, newsocket->hostname,
649                                     newsocket->port, session);
650   conn->sock = newsocket;
651   conn->sock->user_data = conn;
652   session->sock = silc_socket_dup(conn->sock);
653
654   /* Allocate internal context for key exchange protocol. This is
655      sent as context for the protocol. */
656   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
657   proto_ctx->client = client;
658   proto_ctx->sock = silc_socket_dup(conn->sock);
659   proto_ctx->rng = client->rng;
660   proto_ctx->responder = TRUE;
661   proto_ctx->context = session;
662   proto_ctx->send_packet = silc_client_protocol_ke_send_packet;
663   proto_ctx->verify = silc_client_protocol_ke_verify_key;
664
665   /* Prepare the connection for key exchange protocol. We allocate the
666      protocol but will not start it yet. The connector will be the
667      initiator of the protocol thus we will wait for initiation from 
668      there before we start the protocol. */
669   silc_protocol_alloc(SILC_PROTOCOL_CLIENT_KEY_EXCHANGE, 
670                       &newsocket->protocol, proto_ctx, 
671                       silc_client_ftp_key_agreement_final);
672
673   /* Register the connection for network input and output. This sets
674      that scheduler will listen for incoming packets for this connection 
675      and sets that outgoing packets may be sent to this connection as well.
676      However, this doesn't set the scheduler for outgoing traffic, it
677      will be set separately by calling SILC_CLIENT_SET_CONNECTION_FOR_OUTPUT,
678      later when outgoing data is available. */
679   context = (void *)client;
680   SILC_CLIENT_REGISTER_CONNECTION_FOR_IO(sock);
681 }
682
683 /* Free all file transfer sessions. */
684
685 void silc_client_ftp_free_sessions(SilcClient client,
686                                    SilcClientConnection conn)
687 {
688   if (conn->ftp_sessions) {
689     SilcClientFtpSession session;
690     silc_dlist_start(conn->ftp_sessions);
691     while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
692       if (session->sock)
693         session->sock->user_data = NULL;
694       silc_client_ftp_session_free(session);
695     }
696     silc_dlist_del(conn->ftp_sessions, session);
697   }
698 }
699
700 /* Free file transfer session by client entry. */
701
702 void silc_client_ftp_session_free_client(SilcClientConnection conn,
703                                          SilcClientEntry client_entry)
704 {
705   SilcClientFtpSession session;
706
707   if (!conn->ftp_sessions)
708     return;
709
710   /* Get the session */
711   silc_dlist_start(conn->ftp_sessions);
712   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
713     if (session->client_entry == client_entry) {
714       if (session->sock)
715         session->sock->user_data = NULL;
716       silc_client_ftp_session_free(session);
717     }
718   }
719 }
720
721 /* Free session resources. */
722
723 void silc_client_ftp_session_free(SilcClientFtpSession session)
724 {
725   SilcClientConnection conn;
726
727   SILC_LOG_DEBUG(("Free session"));
728
729   if (session->conn && session->conn->ftp_sessions)
730     silc_dlist_del(session->conn->ftp_sessions, session);
731
732   if (session->conn && session->conn->active_session == session)
733     session->conn->active_session = NULL;
734
735   if (session->sftp) {
736     if (session->server)
737       silc_sftp_server_shutdown(session->sftp);
738     else
739       silc_sftp_client_shutdown(session->sftp);
740   }
741
742   if (session->fs)
743     silc_sftp_fs_memory_free(session->fs);
744
745   /* Destroy listener */
746   if (session->listener) {
747     silc_schedule_unset_listen_fd(session->client->schedule, 
748                                   session->listener);
749     silc_net_close_connection(session->listener);
750     silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
751   }
752
753   /* Destroy session connection */
754   if (session->sock) {
755     silc_schedule_unset_listen_fd(session->client->schedule, 
756                                   session->sock->sock);
757     silc_net_close_connection(session->sock->sock);
758
759     if (session->sock->user_data) {
760       conn = (SilcClientConnection)session->sock->user_data;
761
762       if (conn->active_session == session)
763         conn->active_session = NULL;
764
765       silc_client_close_connection_real(session->client, session->sock, conn);
766     } else {
767       silc_socket_free(session->sock);
768     }
769   }
770
771   if (session->packet)
772     silc_buffer_free(session->packet);
773
774   silc_free(session->hostname);
775   silc_free(session->filepath);
776   silc_free(session->path);
777   memset(session, 'F', sizeof(*session));
778   silc_free(session);
779 }
780
781 /* Sends a file indicated by the `filepath' to the remote client 
782    indicated by the `client_entry'.  This will negotiate a secret key
783    with the remote client before actually starting the transmission of
784    the file.  The `monitor' callback will be called to monitor the
785    transmission of the file. */
786
787 SilcClientFileError 
788 silc_client_file_send(SilcClient client,
789                       SilcClientConnection conn,
790                       SilcClientFileMonitor monitor,
791                       void *monitor_context,
792                       const char *local_ip,
793                       SilcUInt32 local_port,
794                       SilcClientEntry client_entry,
795                       const char *filepath,
796                       SilcUInt32 *session_id)
797 {
798   SilcClientFtpSession session;
799   SilcBuffer keyagr, ftp;
800   char *filename, *path;
801   int fd;
802
803   SILC_LOG_DEBUG(("Start"));
804
805   /* Check for existing session for `filepath'. */
806   silc_dlist_start(conn->ftp_sessions);
807   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
808     if (session->filepath && !strcmp(session->filepath, filepath) && 
809         session->client_entry == client_entry)
810       return SILC_CLIENT_FILE_ALREADY_STARTED;
811   }
812
813   /* See whether the file exists, and can be opened in generally speaking */
814   fd = silc_file_open(filepath, O_RDONLY);
815   if (fd < 0)
816     return SILC_CLIENT_FILE_NO_SUCH_FILE;
817   silc_file_close(fd);
818
819   /* Add new session */
820   session = silc_calloc(1, sizeof(*session));
821   session->session_id = ++conn->next_session_id;
822   session->client = client;
823   session->conn = conn;
824   session->client_entry = client_entry;
825   session->monitor = monitor;
826   session->monitor_context = monitor_context;
827   session->filepath = strdup(filepath);
828   session->server = TRUE;
829   silc_dlist_add(conn->ftp_sessions, session);
830
831   path = silc_calloc(strlen(filepath) + 9, sizeof(*path));
832   silc_strncat(path, strlen(filepath) + 9, "file://", 7);
833   silc_strncat(path, strlen(filepath) + 9, filepath, strlen(filepath));
834
835   /* Allocate memory filesystem and put the file to it */
836   if (strrchr(path, '/'))
837     filename = strrchr(path, '/') + 1;
838   else
839     filename = (char *)path;
840   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
841                                           SILC_SFTP_FS_PERM_EXEC);
842   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
843                                filename, path);
844
845   session->filesize = silc_file_size(filepath);
846
847   /* Create the listener for incoming key exchange protocol. */
848   if (local_ip)
849     session->hostname = strdup(local_ip);
850   else
851     session->hostname = silc_net_localip();
852   session->listener = silc_net_create_server(local_port, session->hostname);
853   if (session->listener < 0) {
854     /* Could not create listener. Do the second best thing; send empty
855        key agreement packet and let the remote client provide the point
856        for the key exchange. */
857     SILC_LOG_DEBUG(("Could not create listener"));
858     silc_free(session->hostname);
859     session->hostname = NULL;
860     session->port = 0;
861   } else {
862     /* Listener ready */
863     session->port = silc_net_get_local_port(session->listener);
864     silc_schedule_task_add(client->schedule, session->listener,
865                            silc_client_ftp_process_key_agreement, session,
866                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
867   }
868
869   /* Send the key agreement inside FTP packet */
870   keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
871
872   ftp = silc_buffer_alloc(1 + keyagr->len);
873   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
874   silc_buffer_format(ftp,
875                      SILC_STR_UI_CHAR(1),
876                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
877                      SILC_STR_END);
878   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
879                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
880                           ftp->data, ftp->len, FALSE);
881
882   silc_buffer_free(keyagr);
883   silc_buffer_free(ftp);
884   silc_free(path);
885
886   if (session_id)
887     *session_id = session->session_id;
888
889   return SILC_CLIENT_FILE_OK;
890 }
891
892 /* Receives a file from a client indicated by the `client_entry'.  The
893    `session_id' indicates the file transmission session and it has been
894    received in the `ftp' client operation function.  This will actually
895    perform the key agreement protocol with the remote client before
896    actually starting the file transmission.  The `monitor' callback
897    will be called to monitor the transmission. */
898
899 SilcClientFileError 
900 silc_client_file_receive(SilcClient client,
901                          SilcClientConnection conn,
902                          SilcClientFileMonitor monitor,
903                          void *monitor_context,
904                          const char *path,
905                          SilcUInt32 session_id)
906 {
907   SilcClientFtpSession session;
908   SilcBuffer keyagr, ftp;
909
910   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
911
912   /* Get the session */
913   silc_dlist_start(conn->ftp_sessions);
914   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
915     if (session->session_id == session_id) {
916       break;
917     }
918   }
919
920   if (session == SILC_LIST_END) {
921     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
922     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
923   }
924
925   /* See if we have this session running already */
926   if (session->sftp || session->listener) {
927     SILC_LOG_DEBUG(("Session already started"));
928     return SILC_CLIENT_FILE_ALREADY_STARTED;
929   }
930
931   session->monitor = monitor;
932   session->monitor_context = monitor_context;
933   session->conn = conn;
934   session->path = path ? strdup(path) : NULL;
935
936   /* If the hostname and port already exists then the remote client did
937      provide the connection point to us and we won't create listener, but
938      create the connection ourselves. */
939   if (session->hostname && session->port) {
940     if (silc_client_connect_to_client(client, conn, session->port, 
941                                       session->hostname, session) < 0)
942       return SILC_CLIENT_FILE_ERROR;
943   } else {
944     /* Add the listener for the key agreement */
945     session->hostname = silc_net_localip();
946     session->listener = silc_net_create_server(0, session->hostname);
947     if (session->listener < 0) {
948       SILC_LOG_DEBUG(("Could not create listener"));
949       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
950                                  "Cannot create listener on %s: %s", 
951                                  session->hostname, strerror(errno));
952       return SILC_CLIENT_FILE_ERROR;
953     }
954     session->port = silc_net_get_local_port(session->listener);
955     silc_schedule_task_add(client->schedule, session->listener,
956                            silc_client_ftp_process_key_agreement, session,
957                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
958     
959     /* Send the key agreement inside FTP packet */
960     keyagr = silc_key_agreement_payload_encode(session->hostname, 
961                                                session->port);
962     ftp = silc_buffer_alloc(1 + keyagr->len);
963     silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
964     silc_buffer_format(ftp,
965                        SILC_STR_UI_CHAR(1),
966                        SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
967                        SILC_STR_END);
968     silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
969                             session->client_entry->id, 
970                             SILC_ID_CLIENT, NULL, NULL,
971                             ftp->data, ftp->len, FALSE);
972     
973     silc_buffer_free(keyagr);
974     silc_buffer_free(ftp);
975   }
976
977   return SILC_CLIENT_FILE_OK;
978 }
979
980 /* Closes file transmission session indicated by the `session_id'.
981    If file transmission is being conducted it will be aborted
982    automatically. This function is also used to close the session
983    after successful file transmission. This function can be used
984    also to reject incoming file transmission request. */
985
986 SilcClientFileError silc_client_file_close(SilcClient client,
987                                            SilcClientConnection conn,
988                                            SilcUInt32 session_id)
989 {
990   SilcClientFtpSession session;
991
992   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
993
994   /* Get the session */
995   silc_dlist_start(conn->ftp_sessions);
996   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
997     if (session->session_id == session_id) {
998       break;
999     }
1000   }
1001
1002   if (session == SILC_LIST_END) {
1003     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1004     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1005   }
1006
1007   silc_client_ftp_session_free(session);
1008
1009   return SILC_CLIENT_FILE_OK;
1010 }
1011
1012 /* Callback called after remote client information has been resolved.
1013    This will try to find existing session for the client entry.  If found
1014    then continue with the key agreement protocol.  If not then it means
1015    this is a file transfer request and we let the application know. */
1016
1017 static void silc_client_ftp_resolve_cb(SilcClient client,
1018                                        SilcClientConnection conn,
1019                                        SilcClientEntry *clients,
1020                                        SilcUInt32 clients_count,
1021                                        void *context)
1022 {
1023   SilcPacketContext *packet = (SilcPacketContext *)context;
1024   SilcClientFtpSession session;
1025   SilcKeyAgreementPayload payload = NULL;
1026   SilcClientEntry client_entry;
1027   char *hostname;
1028   SilcUInt16 port;
1029
1030   SILC_LOG_DEBUG(("Start"));
1031
1032   if (!clients)
1033     goto out;
1034
1035   client_entry = clients[0];
1036
1037   silc_dlist_start(conn->ftp_sessions);
1038   while ((session = silc_dlist_get(conn->ftp_sessions)) != SILC_LIST_END) {
1039     if (session->client_entry == client_entry && !session->server)
1040       break;
1041   }
1042
1043   /* Parse the key agreement payload */
1044   payload = silc_key_agreement_payload_parse(packet->buffer->data,
1045                                              packet->buffer->len);
1046   if (!payload)
1047     goto out;
1048
1049   hostname = silc_key_agreement_get_hostname(payload);
1050   port = silc_key_agreement_get_port(payload);
1051   if (!hostname)
1052     port = 0;
1053   if (!port)
1054     hostname = NULL;
1055
1056   if (session == SILC_LIST_END || (!hostname && !port)) {
1057     /* No session found, create one and let the application know about
1058        incoming file transfer request. */
1059     
1060     /* Add new session */
1061     session = silc_calloc(1, sizeof(*session));
1062     session->session_id = ++conn->next_session_id;
1063     session->client = client;
1064     session->conn = conn;
1065     session->client_entry = client_entry;
1066     silc_dlist_add(conn->ftp_sessions, session);
1067
1068     /* Let the application know */
1069     client->internal->ops->ftp(client, conn, client_entry,
1070                                session->session_id, hostname, port);
1071
1072     if (hostname && port) {
1073       session->hostname = strdup(hostname);
1074       session->port = port;
1075     }
1076     
1077     goto out;
1078   }
1079
1080   session->hostname = strdup(hostname);
1081   session->port = port;
1082
1083   /* Session exists, continue with key agreement protocol. */
1084   if (silc_client_connect_to_client(client, conn, port, 
1085                                     hostname, session) < 0) {
1086     /* Call monitor callback */
1087     if (session->monitor)
1088       (*session->monitor)(session->client, session->conn,
1089                           SILC_CLIENT_FILE_MONITOR_ERROR, 
1090                           SILC_CLIENT_FILE_ERROR, 0, 0,
1091                           session->client_entry, session->session_id,
1092                           session->filepath, session->monitor_context);
1093   }
1094
1095  out:
1096   if (payload)
1097     silc_key_agreement_payload_free(payload);
1098   silc_packet_context_free(packet);
1099 }
1100
1101 /* Called when file transfer packet is received. This will parse the
1102    packet and give it to the file transfer protocol. */
1103
1104 void silc_client_ftp(SilcClient client,
1105                      SilcSocketConnection sock,
1106                      SilcPacketContext *packet)
1107 {
1108   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1109   SilcUInt8 type;
1110   int ret;
1111
1112   SILC_LOG_DEBUG(("Start"));
1113
1114   /* Parse the payload */
1115   ret = silc_buffer_unformat(packet->buffer,
1116                              SILC_STR_UI_CHAR(&type),
1117                              SILC_STR_END);
1118   if (ret == -1)
1119     return;
1120
1121   /* We support only type number 1 (== SFTP) */
1122   if (type != 1)
1123     return;
1124
1125   silc_buffer_pull(packet->buffer, 1);
1126
1127   /* If we have active FTP session then give the packet directly to the
1128      protocol processor. */
1129   if (conn->active_session) {
1130     /* Give it to the SFTP */
1131     if (conn->active_session->server)
1132       silc_sftp_server_receive_process(conn->active_session->sftp, sock, 
1133                                        packet);
1134     else
1135       silc_sftp_client_receive_process(conn->active_session->sftp, sock, 
1136                                        packet);
1137   } else {
1138     /* We don't have active session, resolve the remote client information
1139        and then try to find the correct session. */
1140     SilcClientID *remote_id;
1141
1142     if (packet->src_id_type != SILC_ID_CLIENT)
1143       return;
1144
1145     remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
1146                                SILC_ID_CLIENT);
1147     if (!remote_id)
1148       return;
1149
1150     /* Resolve the client */
1151     silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1152                                          NULL, silc_client_ftp_resolve_cb,
1153                                          silc_packet_context_dup(packet));
1154     silc_free(remote_id);
1155   }
1156 }