Moved the SilcClient and SilcClientConnection to the public
[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->internal->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->internal->ftp_sessions) {
689     SilcClientFtpSession session;
690     silc_dlist_start(conn->internal->ftp_sessions);
691     while ((session = silc_dlist_get(conn->internal->ftp_sessions))
692            != SILC_LIST_END) {
693       if (session->sock)
694         session->sock->user_data = NULL;
695       silc_client_ftp_session_free(session);
696     }
697     silc_dlist_del(conn->internal->ftp_sessions, session);
698   }
699 }
700
701 /* Free file transfer session by client entry. */
702
703 void silc_client_ftp_session_free_client(SilcClientConnection conn,
704                                          SilcClientEntry client_entry)
705 {
706   SilcClientFtpSession session;
707
708   if (!conn->internal->ftp_sessions)
709     return;
710
711   /* Get the session */
712   silc_dlist_start(conn->internal->ftp_sessions);
713   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
714          != SILC_LIST_END) {
715     if (session->client_entry == client_entry) {
716       if (session->sock)
717         session->sock->user_data = NULL;
718       silc_client_ftp_session_free(session);
719     }
720   }
721 }
722
723 /* Free session resources. */
724
725 void silc_client_ftp_session_free(SilcClientFtpSession session)
726 {
727   SilcClientConnection conn;
728
729   SILC_LOG_DEBUG(("Free session"));
730
731   if (session->conn && session->conn->internal->ftp_sessions)
732     silc_dlist_del(session->conn->internal->ftp_sessions, session);
733
734   if (session->conn && session->conn->internal->active_session == session)
735     session->conn->internal->active_session = NULL;
736
737   if (session->sftp) {
738     if (session->server)
739       silc_sftp_server_shutdown(session->sftp);
740     else
741       silc_sftp_client_shutdown(session->sftp);
742   }
743
744   if (session->fs)
745     silc_sftp_fs_memory_free(session->fs);
746
747   /* Destroy listener */
748   if (session->listener) {
749     silc_schedule_unset_listen_fd(session->client->schedule, 
750                                   session->listener);
751     silc_net_close_connection(session->listener);
752     silc_schedule_task_del_by_fd(session->client->schedule, session->listener);
753   }
754
755   /* Destroy session connection */
756   if (session->sock) {
757     silc_schedule_unset_listen_fd(session->client->schedule, 
758                                   session->sock->sock);
759     silc_net_close_connection(session->sock->sock);
760
761     if (session->sock->user_data) {
762       conn = (SilcClientConnection)session->sock->user_data;
763
764       if (conn->internal->active_session == session)
765         conn->internal->active_session = NULL;
766
767       silc_client_close_connection_real(session->client, session->sock, conn);
768     } else {
769       silc_socket_free(session->sock);
770     }
771   }
772
773   if (session->packet)
774     silc_buffer_free(session->packet);
775
776   silc_free(session->hostname);
777   silc_free(session->filepath);
778   silc_free(session->path);
779   memset(session, 'F', sizeof(*session));
780   silc_free(session);
781 }
782
783 /* Sends a file indicated by the `filepath' to the remote client 
784    indicated by the `client_entry'.  This will negotiate a secret key
785    with the remote client before actually starting the transmission of
786    the file.  The `monitor' callback will be called to monitor the
787    transmission of the file. */
788
789 SilcClientFileError 
790 silc_client_file_send(SilcClient client,
791                       SilcClientConnection conn,
792                       SilcClientFileMonitor monitor,
793                       void *monitor_context,
794                       const char *local_ip,
795                       SilcUInt32 local_port,
796                       SilcClientEntry client_entry,
797                       const char *filepath,
798                       SilcUInt32 *session_id)
799 {
800   SilcClientFtpSession session;
801   SilcBuffer keyagr, ftp;
802   char *filename, *path;
803   int fd;
804
805   SILC_LOG_DEBUG(("Start"));
806
807   /* Check for existing session for `filepath'. */
808   silc_dlist_start(conn->internal->ftp_sessions);
809   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
810          != SILC_LIST_END) {
811     if (session->filepath && !strcmp(session->filepath, filepath) && 
812         session->client_entry == client_entry)
813       return SILC_CLIENT_FILE_ALREADY_STARTED;
814   }
815
816   /* See whether the file exists, and can be opened in generally speaking */
817   fd = silc_file_open(filepath, O_RDONLY);
818   if (fd < 0)
819     return SILC_CLIENT_FILE_NO_SUCH_FILE;
820   silc_file_close(fd);
821
822   /* Add new session */
823   session = silc_calloc(1, sizeof(*session));
824   session->session_id = ++conn->internal->next_session_id;
825   session->client = client;
826   session->conn = conn;
827   session->client_entry = client_entry;
828   session->monitor = monitor;
829   session->monitor_context = monitor_context;
830   session->filepath = strdup(filepath);
831   session->server = TRUE;
832   silc_dlist_add(conn->internal->ftp_sessions, session);
833
834   path = silc_calloc(strlen(filepath) + 9, sizeof(*path));
835   silc_strncat(path, strlen(filepath) + 9, "file://", 7);
836   silc_strncat(path, strlen(filepath) + 9, filepath, strlen(filepath));
837
838   /* Allocate memory filesystem and put the file to it */
839   if (strrchr(path, '/'))
840     filename = strrchr(path, '/') + 1;
841   else
842     filename = (char *)path;
843   session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
844                                           SILC_SFTP_FS_PERM_EXEC);
845   silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
846                                filename, path);
847
848   session->filesize = silc_file_size(filepath);
849
850   /* Create the listener for incoming key exchange protocol. */
851   if (local_ip)
852     session->hostname = strdup(local_ip);
853   else
854     session->hostname = silc_net_localip();
855   session->listener = silc_net_create_server(local_port, session->hostname);
856   if (session->listener < 0) {
857     /* Could not create listener. Do the second best thing; send empty
858        key agreement packet and let the remote client provide the point
859        for the key exchange. */
860     SILC_LOG_DEBUG(("Could not create listener"));
861     silc_free(session->hostname);
862     session->hostname = NULL;
863     session->port = 0;
864   } else {
865     /* Listener ready */
866     session->port = silc_net_get_local_port(session->listener);
867     silc_schedule_task_add(client->schedule, session->listener,
868                            silc_client_ftp_process_key_agreement, session,
869                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
870   }
871
872   /* Send the key agreement inside FTP packet */
873   keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
874
875   ftp = silc_buffer_alloc(1 + keyagr->len);
876   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
877   silc_buffer_format(ftp,
878                      SILC_STR_UI_CHAR(1),
879                      SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
880                      SILC_STR_END);
881   silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
882                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
883                           ftp->data, ftp->len, FALSE);
884
885   silc_buffer_free(keyagr);
886   silc_buffer_free(ftp);
887   silc_free(path);
888
889   if (session_id)
890     *session_id = session->session_id;
891
892   return SILC_CLIENT_FILE_OK;
893 }
894
895 /* Receives a file from a client indicated by the `client_entry'.  The
896    `session_id' indicates the file transmission session and it has been
897    received in the `ftp' client operation function.  This will actually
898    perform the key agreement protocol with the remote client before
899    actually starting the file transmission.  The `monitor' callback
900    will be called to monitor the transmission. */
901
902 SilcClientFileError 
903 silc_client_file_receive(SilcClient client,
904                          SilcClientConnection conn,
905                          SilcClientFileMonitor monitor,
906                          void *monitor_context,
907                          const char *path,
908                          SilcUInt32 session_id)
909 {
910   SilcClientFtpSession session;
911   SilcBuffer keyagr, ftp;
912
913   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
914
915   /* Get the session */
916   silc_dlist_start(conn->internal->ftp_sessions);
917   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
918          != SILC_LIST_END) {
919     if (session->session_id == session_id) {
920       break;
921     }
922   }
923
924   if (session == SILC_LIST_END) {
925     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
926     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
927   }
928
929   /* See if we have this session running already */
930   if (session->sftp || session->listener) {
931     SILC_LOG_DEBUG(("Session already started"));
932     return SILC_CLIENT_FILE_ALREADY_STARTED;
933   }
934
935   session->monitor = monitor;
936   session->monitor_context = monitor_context;
937   session->conn = conn;
938   session->path = path ? strdup(path) : NULL;
939
940   /* If the hostname and port already exists then the remote client did
941      provide the connection point to us and we won't create listener, but
942      create the connection ourselves. */
943   if (session->hostname && session->port) {
944     if (silc_client_connect_to_client(client, conn, session->port, 
945                                       session->hostname, session) < 0)
946       return SILC_CLIENT_FILE_ERROR;
947   } else {
948     /* Add the listener for the key agreement */
949     session->hostname = silc_net_localip();
950     session->listener = silc_net_create_server(0, session->hostname);
951     if (session->listener < 0) {
952       SILC_LOG_DEBUG(("Could not create listener"));
953       client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
954                                  "Cannot create listener on %s: %s", 
955                                  session->hostname, strerror(errno));
956       return SILC_CLIENT_FILE_ERROR;
957     }
958     session->port = silc_net_get_local_port(session->listener);
959     silc_schedule_task_add(client->schedule, session->listener,
960                            silc_client_ftp_process_key_agreement, session,
961                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
962     
963     /* Send the key agreement inside FTP packet */
964     keyagr = silc_key_agreement_payload_encode(session->hostname, 
965                                                session->port);
966     ftp = silc_buffer_alloc(1 + keyagr->len);
967     silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
968     silc_buffer_format(ftp,
969                        SILC_STR_UI_CHAR(1),
970                        SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
971                        SILC_STR_END);
972     silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
973                             session->client_entry->id, 
974                             SILC_ID_CLIENT, NULL, NULL,
975                             ftp->data, ftp->len, FALSE);
976     
977     silc_buffer_free(keyagr);
978     silc_buffer_free(ftp);
979   }
980
981   return SILC_CLIENT_FILE_OK;
982 }
983
984 /* Closes file transmission session indicated by the `session_id'.
985    If file transmission is being conducted it will be aborted
986    automatically. This function is also used to close the session
987    after successful file transmission. This function can be used
988    also to reject incoming file transmission request. */
989
990 SilcClientFileError silc_client_file_close(SilcClient client,
991                                            SilcClientConnection conn,
992                                            SilcUInt32 session_id)
993 {
994   SilcClientFtpSession session;
995
996   SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));
997
998   /* Get the session */
999   silc_dlist_start(conn->internal->ftp_sessions);
1000   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1001          != SILC_LIST_END) {
1002     if (session->session_id == session_id) {
1003       break;
1004     }
1005   }
1006
1007   if (session == SILC_LIST_END) {
1008     SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
1009     return SILC_CLIENT_FILE_UNKNOWN_SESSION;
1010   }
1011
1012   silc_client_ftp_session_free(session);
1013
1014   return SILC_CLIENT_FILE_OK;
1015 }
1016
1017 /* Callback called after remote client information has been resolved.
1018    This will try to find existing session for the client entry.  If found
1019    then continue with the key agreement protocol.  If not then it means
1020    this is a file transfer request and we let the application know. */
1021
1022 static void silc_client_ftp_resolve_cb(SilcClient client,
1023                                        SilcClientConnection conn,
1024                                        SilcClientEntry *clients,
1025                                        SilcUInt32 clients_count,
1026                                        void *context)
1027 {
1028   SilcPacketContext *packet = (SilcPacketContext *)context;
1029   SilcClientFtpSession session;
1030   SilcKeyAgreementPayload payload = NULL;
1031   SilcClientEntry client_entry;
1032   char *hostname;
1033   SilcUInt16 port;
1034
1035   SILC_LOG_DEBUG(("Start"));
1036
1037   if (!clients)
1038     goto out;
1039
1040   client_entry = clients[0];
1041
1042   silc_dlist_start(conn->internal->ftp_sessions);
1043   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
1044          != SILC_LIST_END) {
1045     if (session->client_entry == client_entry && !session->server)
1046       break;
1047   }
1048
1049   /* Parse the key agreement payload */
1050   payload = silc_key_agreement_payload_parse(packet->buffer->data,
1051                                              packet->buffer->len);
1052   if (!payload)
1053     goto out;
1054
1055   hostname = silc_key_agreement_get_hostname(payload);
1056   port = silc_key_agreement_get_port(payload);
1057   if (!hostname)
1058     port = 0;
1059   if (!port)
1060     hostname = NULL;
1061
1062   if (session == SILC_LIST_END || (!hostname && !port)) {
1063     /* No session found, create one and let the application know about
1064        incoming file transfer request. */
1065     
1066     /* Add new session */
1067     session = silc_calloc(1, sizeof(*session));
1068     session->session_id = ++conn->internal->next_session_id;
1069     session->client = client;
1070     session->conn = conn;
1071     session->client_entry = client_entry;
1072     silc_dlist_add(conn->internal->ftp_sessions, session);
1073
1074     /* Let the application know */
1075     client->internal->ops->ftp(client, conn, client_entry,
1076                                session->session_id, hostname, port);
1077
1078     if (hostname && port) {
1079       session->hostname = strdup(hostname);
1080       session->port = port;
1081     }
1082     
1083     goto out;
1084   }
1085
1086   session->hostname = strdup(hostname);
1087   session->port = port;
1088
1089   /* Session exists, continue with key agreement protocol. */
1090   if (silc_client_connect_to_client(client, conn, port, 
1091                                     hostname, session) < 0) {
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_context_free(packet);
1105 }
1106
1107 /* Called when file transfer packet is received. This will parse the
1108    packet and give it to the file transfer protocol. */
1109
1110 void silc_client_ftp(SilcClient client,
1111                      SilcSocketConnection sock,
1112                      SilcPacketContext *packet)
1113 {
1114   SilcClientConnection conn = (SilcClientConnection)sock->user_data;
1115   SilcUInt8 type;
1116   int ret;
1117
1118   SILC_LOG_DEBUG(("Start"));
1119
1120   /* Parse the payload */
1121   ret = silc_buffer_unformat(packet->buffer,
1122                              SILC_STR_UI_CHAR(&type),
1123                              SILC_STR_END);
1124   if (ret == -1)
1125     return;
1126
1127   /* We support only type number 1 (== SFTP) */
1128   if (type != 1)
1129     return;
1130
1131   silc_buffer_pull(packet->buffer, 1);
1132
1133   /* If we have active FTP session then give the packet directly to the
1134      protocol processor. */
1135   if (conn->internal->active_session) {
1136     /* Give it to the SFTP */
1137     if (conn->internal->active_session->server)
1138       silc_sftp_server_receive_process(conn->internal->active_session->sftp,
1139                                        sock, packet);
1140     else
1141       silc_sftp_client_receive_process(conn->internal->active_session->sftp,
1142                                        sock, packet);
1143   } else {
1144     /* We don't have active session, resolve the remote client information
1145        and then try to find the correct session. */
1146     SilcClientID *remote_id;
1147
1148     if (packet->src_id_type != SILC_ID_CLIENT)
1149       return;
1150
1151     remote_id = silc_id_str2id(packet->src_id, packet->src_id_len, 
1152                                SILC_ID_CLIENT);
1153     if (!remote_id)
1154       return;
1155
1156     /* Resolve the client */
1157     silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
1158                                          NULL, silc_client_ftp_resolve_cb,
1159                                          silc_packet_context_dup(packet));
1160     silc_free(remote_id);
1161   }
1162 }