SILC_FSM_* macro API changes.
[silc.git] / lib / silcclient / client_ftp.c
index 58c2419c049ab7964402183e14c0c3f0529a1cee..c3bbd177cb3adc87a0c61b312e51095630e4bdbf 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 2001 - 2004 Pekka Riikonen
+  Copyright (C) 2001 - 2007 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 */
 /* $Id$ */
 
-#include "silcincludes.h"
+#include "silc.h"
 #include "silcclient.h"
 #include "client_internal.h"
 
@@ -317,7 +317,7 @@ static void silc_client_ftp_open_handle(SilcSFTP sftp,
 
   /* Open the actual local file */
   memset(path, 0, sizeof(path));
-  snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
+  silc_snprintf(path, sizeof(path) - 1, "%s%s", session->path ?
           session->path : "", session->filepath);
   session->fd = silc_file_open(path, O_RDWR | O_CREAT | O_EXCL);
   if (session->fd < 0) {
@@ -821,13 +821,12 @@ void silc_client_ftp_session_free(SilcClientFtpSession session)
 
 SilcClientFileError
 silc_client_file_send(SilcClient client,
-                     SilcClientConnection conn,
+                     SilcClientEntry client_entry,
+                     SilcClientConnectionParams *params,
+                     SilcPublicKey public_key,
+                     SilcPrivateKey private_key,
                      SilcClientFileMonitor monitor,
                      void *monitor_context,
-                     const char *local_ip,
-                     SilcUInt32 local_port,
-                     SilcBool do_not_bind,
-                     SilcClientEntry client_entry,
                      const char *filepath,
                      SilcUInt32 *session_id)
 {
@@ -836,20 +835,20 @@ silc_client_file_send(SilcClient client,
   char *filename, *path;
   int fd;
 
-  assert(client && conn && client_entry);
+  SILC_LOG_DEBUG(("File send request (file: %s), filepath"));
 
-  SILC_LOG_DEBUG(("Start"));
+  if (!client || !client_entry || !filepath)
+    return SILC_CLIENT_FILE_ERROR;
 
   /* Check for existing session for `filepath'. */
-  silc_dlist_start(conn->internal->ftp_sessions);
-  while ((session = silc_dlist_get(conn->internal->ftp_sessions))
-        != SILC_LIST_END) {
+  silc_dlist_start(client->internal->ftp_sessions);
+  while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
     if (session->filepath && !strcmp(session->filepath, filepath) &&
        session->client_entry == client_entry)
       return SILC_CLIENT_FILE_ALREADY_STARTED;
   }
 
-  /* See whether the file exists, and can be opened in generally speaking */
+  /* See whether the file exists and can be opened */
   fd = silc_file_open(filepath, O_RDONLY);
   if (fd < 0)
     return SILC_CLIENT_FILE_NO_SUCH_FILE;
@@ -857,7 +856,9 @@ silc_client_file_send(SilcClient client,
 
   /* Add new session */
   session = silc_calloc(1, sizeof(*session));
-  session->session_id = ++conn->internal->next_session_id;
+  if (!session)
+    return SILC_CLIENT_FILE_ERROR;
+  session->session_id = ++client->internal->next_session_id;
   session->client = client;
   session->conn = conn;
   session->server = TRUE;
@@ -867,9 +868,7 @@ silc_client_file_send(SilcClient client,
   session->filepath = strdup(filepath);
   silc_dlist_add(conn->internal->ftp_sessions, session);
 
-  path = silc_calloc(strlen(filepath) + 9, sizeof(*path));
-  silc_strncat(path, strlen(filepath) + 9, "file://", 7);
-  silc_strncat(path, strlen(filepath) + 9, filepath, strlen(filepath));
+  silc_asprintf(&path, "file://%s", filepath);
 
   /* Allocate memory filesystem and put the file to it */
   if (strrchr(path, '/'))
@@ -883,34 +882,43 @@ silc_client_file_send(SilcClient client,
 
   session->filesize = silc_file_size(filepath);
 
-  /* Create the listener for incoming key exchange protocol. */
-  if (!do_not_bind) {
-    session->listener = -1;
-    if (local_ip)
-      session->hostname = strdup(local_ip);
-    else
-      silc_net_check_local_by_sock(conn->sock->sock, NULL,
-                                  &session->hostname);
-    if (session->hostname)
-      session->listener = silc_net_create_server(local_port,
-                                                session->hostname);
-    if (session->listener < 0) {
+  /* If local IP is provided, create listener for incoming key exchange */
+  if (params && (params->local_ip || params->bind_ip)) {
+    ke = silc_calloc(1, sizeof(*ke));
+    if (!ke) {
+      completion(client, conn, client_entry, SILC_KEY_AGREEMENT_NO_MEMORY,
+                NULL, context);
+      return;
+    }
+
+    /* TCP listener */
+    session->listener =
+      silc_net_tcp_create_listener(params->bind_ip ?
+                                  (const char **)&params->bind_ip :
+                                  (const char **)&params->local_ip,
+                                  1, params->local_port, FALSE, FALSE,
+                                  conn->internal->schedule,
+                                  silc_client_tcp_accept,
+                                  client_entry);
+    if (!session->listener) {
       /* Could not create listener. Do the second best thing; send empty
         key agreement packet and let the remote client provide the point
         for the key exchange. */
       SILC_LOG_DEBUG(("Could not create listener"));
       silc_free(session->hostname);
-      session->listener = 0;
       session->hostname = NULL;
       session->port = 0;
     } else {
-      /* Listener ready */
       SILC_LOG_DEBUG(("Bound listener"));
-      session->port = silc_net_get_local_port(session->listener);
-      silc_schedule_task_add(client->schedule, session->listener,
-                            silc_client_ftp_process_key_agreement, session,
-                            0, 0, SILC_TASK_FD, SILC_TASK_PRI_NORMAL);
       session->bound = TRUE;
+      session->port = params->local_port;
+      if (!session->port) {
+       /* Get listener port */
+       SilcUInt16 *ports;
+       ports = silc_net_listener_get_port(ke->tcp_listener, NULL);
+       session->port = ports[0];
+       silc_free(ports);
+      }
     }
   }
 
@@ -1089,30 +1097,77 @@ SilcClientFileError silc_client_file_close(SilcClient client,
   return SILC_CLIENT_FILE_OK;
 }
 
-/* Callback called after remote client information has been resolved.
-   This will try to find existing session for the client entry.  If found
-   then continue with the key agreement protocol.  If not then it means
-   this is a file transfer request and we let the application know. */
+/************************** FTP Request Processing **************************/
+
+/* Client resolving callback.  Continues with the FTP processing */
 
-static void silc_client_ftp_resolve_cb(SilcClient client,
-                                      SilcClientConnection conn,
-                                      SilcClientEntry *clients,
-                                      SilcUInt32 clients_count,
-                                      void *context)
+static void silc_client_ftp_client_resolved(SilcClient client,
+                                           SilcClientConnection conn,
+                                           SilcStatus status,
+                                           SilcDList clients,
+                                           void *context)
 {
-  SilcPacketContext *packet = (SilcPacketContext *)context;
-  SilcClientFtpSession session;
-  SilcKeyAgreementPayload payload = NULL;
-  SilcClientEntry client_entry;
+  SilcFSMThread thread = context;
+  SilcPacket packet = silc_fsm_get_state_context(thread);
+
+  /* If no client found, ignore the packet, a silent error */
+  if (!clients) {
+    silc_packet_free(packet);
+    silc_fsm_finish(thread);
+    return;
+  }
+
+  /* Continue processing the packet */
+  SILC_FSM_CALL_CONTINUE(context);
+}
+
+/* Received file transfer packet.  Only file transfer requests get here.
+   The actual file transfer is handled by the SFTP library when we give it
+   the packet stream wrapped into SilcStream context. */
+
+SILC_FSM_STATE(silc_client_ftp)
+{
+  SilcClientConnection conn = fsm_context;
+  SilcClient client = conn->client;
+  SilcPacket packet = state_context;
+  SilcClientID remote_id;
+  SilcClientEntry remote_client;
+  SilcKeyAgreementPayload payload;
+  SilcUInt8 type;
   char *hostname;
   SilcUInt16 port;
+  int ret;
 
-  SILC_LOG_DEBUG(("Start"));
+  SILC_LOG_DEBUG(("Process file transfer packet"));
+
+  if (silc_buffer_len(&packet->buffer) < 1)
+    goto out;
 
-  if (!clients)
+  /* We support file transfer type number 1 (== SFTP) */
+  if (packet->buffer.data[0] != 0x01) {
+    SILC_LOG_DEBUG(("Unsupported file transfer type %d",
+                   packet->buffer.data[0]));
     goto out;
+  }
+
+  if (!silc_id_str2id(packet->src_id, packet->src_id_len,
+                     SILC_ID_CLIENT, &remote_id, sizeof(remote_id))) {
+    SILC_LOG_DEBUG(("Invalid client ID"));
+    goto out;
+  }
+
+  /* Check whether we know this client already */
+  remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
+  if (!remote_client || !remote_client->nickname[0]) {
+    /** Resolve client info */
+    silc_client_unref_client(client, conn, remote_client);
+    SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
+                                        client, conn, &remote_id, NULL,
+                                        silc_client_ftp_client_resolved,
+                                        fsm));
+    /* NOT REACHED */
+  }
 
-  client_entry = clients[0];
 
   silc_dlist_start(conn->internal->ftp_sessions);
   while ((session = silc_dlist_get(conn->internal->ftp_sessions))
@@ -1123,10 +1178,12 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
   }
 
   /* Parse the key agreement payload */
-  payload = silc_key_agreement_payload_parse(packet->buffer->data,
-                                            packet->buffer->len);
-  if (!payload)
+  payload = silc_key_agreement_payload_parse(packet->buffer->data + 1,
+                                            packet->buffer->len - 1);
+  if (!payload) {
+    SILC_LOG_DEBUG(("Invalid key agreement payload"));
     goto out;
+  }
 
   hostname = silc_key_agreement_get_hostname(payload);
   port = silc_key_agreement_get_port(payload);
@@ -1155,15 +1212,15 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
     session->client_entry = client_entry;
     silc_dlist_add(conn->internal->ftp_sessions, session);
 
-    /* Let the application know */
-    client->internal->ops->ftp(client, conn, client_entry,
-                              session->session_id, hostname, port);
-
     if (hostname && port) {
       session->hostname = strdup(hostname);
       session->port = port;
     }
 
+    /* Let the application know */
+    client->internal->ops->ftp(client, conn, client_entry,
+                              session->session_id, hostname, port);
+
     goto out;
   }
 
@@ -1185,65 +1242,9 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
                          session->filepath, session->monitor_context);
   }
 
- out:
-  if (payload)
-    silc_key_agreement_payload_free(payload);
-  silc_packet_context_free(packet);
-}
-
-/* Called when file transfer packet is received. This will parse the
-   packet and give it to the file transfer protocol. */
 
-void silc_client_ftp(SilcClient client,
-                    SilcSocketConnection sock,
-                    SilcPacketContext *packet)
-{
-  SilcClientConnection conn = (SilcClientConnection)sock->user_data;
-  SilcUInt8 type;
-  int ret;
 
-  SILC_LOG_DEBUG(("Start"));
-
-  /* Parse the payload */
-  ret = silc_buffer_unformat(packet->buffer,
-                            SILC_STR_UI_CHAR(&type),
-                            SILC_STR_END);
-  if (ret == -1)
-    return;
-
-  /* We support only type number 1 (== SFTP) */
-  if (type != 1)
-    return;
-
-  silc_buffer_pull(packet->buffer, 1);
-
-  /* If we have active FTP session then give the packet directly to the
-     protocol processor. */
-  if (conn->internal->active_session) {
-    /* Give it to the SFTP */
-    if (conn->internal->active_session->server)
-      silc_sftp_server_receive_process(conn->internal->active_session->sftp,
-                                      sock, packet);
-    else
-      silc_sftp_client_receive_process(conn->internal->active_session->sftp,
-                                      sock, packet);
-  } else {
-    /* We don't have active session, resolve the remote client information
-       and then try to find the correct session. */
-    SilcClientID *remote_id;
-
-    if (packet->src_id_type != SILC_ID_CLIENT)
-      return;
-
-    remote_id = silc_id_str2id(packet->src_id, packet->src_id_len,
-                              SILC_ID_CLIENT);
-    if (!remote_id)
-      return;
-
-    /* Resolve the client */
-    silc_client_get_client_by_id_resolve(client, sock->user_data, remote_id,
-                                        NULL, silc_client_ftp_resolve_cb,
-                                        silc_packet_context_dup(packet));
-    silc_free(remote_id);
-  }
+ out:
+  silc_packet_free(packet);
+  return SILC_FSM_FINISH;
 }