updates.
[silc.git] / lib / silcclient / client_ftp.c
index fca04ad45fcefd03746b5fac8823f8f7796b4cb5..f86ccbd5faaae973ec4d68a2eb7aad022524c994 100644 (file)
@@ -76,12 +76,12 @@ SILC_TASK_CALLBACK(silc_client_ftp_connected)
   if (opt != 0) {
     if (ctx->tries < 2) {
       /* Connection failed but lets try again */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to client %s: %s",
-                      ctx->host, strerror(opt));
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
-                      "Connecting to port %d of client %s resumed", 
-                      ctx->port, ctx->host);
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_AUDIT, 
+                                "Connecting to port %d of client %s resumed", 
+                                ctx->port, ctx->host);
 
       /* Unregister old connection try */
       silc_schedule_unset_listen_fd(client->schedule, fd);
@@ -93,9 +93,9 @@ SILC_TASK_CALLBACK(silc_client_ftp_connected)
       ctx->tries++;
     } else {
       /* Connection failed and we won't try anymore */
-      client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
-                      "Could not connect to client %s: %s",
-                      ctx->host, strerror(opt));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
+                                "Could not connect to client %s: %s",
+                                ctx->host, strerror(opt));
       silc_schedule_unset_listen_fd(client->schedule, fd);
       silc_net_close_connection(fd);
       silc_schedule_task_del(client->schedule, ctx->task);
@@ -206,6 +206,7 @@ static void silc_client_ftp_monitor(SilcSFTP sftp,
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
                          SILC_CLIENT_FILE_MONITOR_SEND,
+                         SILC_CLIENT_FILE_OK,
                          data->offset, session->filesize,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
@@ -243,7 +244,12 @@ static void silc_client_ftp_data(SilcSFTP sftp,
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
 
@@ -265,6 +271,7 @@ static void silc_client_ftp_data(SilcSFTP sftp,
   if (session->monitor)
     (*session->monitor)(session->client, session->conn,
                        SILC_CLIENT_FILE_MONITOR_RECEIVE,
+                       SILC_CLIENT_FILE_OK,
                        session->read_offset, session->filesize,
                        session->client_entry, session->session_id,
                        session->filepath, session->monitor_context);
@@ -289,19 +296,32 @@ static void silc_client_ftp_open_handle(SilcSFTP sftp,
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
     return;
   }
 
   /* Open the actual local file */
-  session->fd = silc_file_open(session->filepath, O_RDWR | O_CREAT);
+  session->fd = silc_file_open(session->filepath, 
+                              O_RDWR | O_CREAT | O_EXCL);
   if (session->fd < 0) {
     /* Call monitor callback */
+    session->client->internal->ops->say(session->client, session->conn, 
+                                       SILC_CLIENT_MESSAGE_ERROR, 
+                                       "File `%s' open failed: %s", 
+                                       session->filepath,
+                                       strerror(errno));
+
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
     return;
@@ -317,6 +337,7 @@ static void silc_client_ftp_open_handle(SilcSFTP sftp,
   if (session->monitor)
     (*session->monitor)(session->client, session->conn,
                        SILC_CLIENT_FILE_MONITOR_RECEIVE,
+                       SILC_CLIENT_FILE_OK,
                        session->read_offset, session->filesize,
                        session->client_entry, session->session_id,
                        session->filepath, session->monitor_context);
@@ -339,7 +360,12 @@ static void silc_client_ftp_readdir_name(SilcSFTP sftp,
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
     return;
@@ -375,7 +401,12 @@ static void silc_client_ftp_opendir_handle(SilcSFTP sftp,
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
     return;
@@ -403,7 +434,12 @@ static void silc_client_ftp_version(SilcSFTP sftp,
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         (status == SILC_SFTP_STATUS_NO_SUCH_FILE ?
+                          SILC_CLIENT_FILE_NO_SUCH_FILE :
+                          status == SILC_SFTP_STATUS_PERMISSION_DENIED ?
+                          SILC_CLIENT_FILE_PERMISSION_DENIED :
+                          SILC_CLIENT_FILE_ERROR), 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
     return;
@@ -442,13 +478,22 @@ SILC_TASK_CALLBACK(silc_client_ftp_key_agreement_final)
                                   ctx->ske->prop->group,
                                   ctx->responder);
 
-  /* If we are the SFTP client then start the SFTP session and retrieve
-     the info about the file available for download. */
   if (!session->server) {
+    /* If we are the SFTP client then start the SFTP session and retrieve
+       the info about the file available for download. */
     session->sftp = silc_sftp_client_start(conn->sock,
                                           silc_client_ftp_send_packet,
                                           session, 
                                           silc_client_ftp_version, session);
+  } else {
+    /* Start SFTP server */
+    session->sftp = silc_sftp_server_start(conn->sock,
+                                          silc_client_ftp_send_packet,
+                                          session, session->fs);
+
+    /* Monitor transmission */
+    silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
+                                silc_client_ftp_monitor, session);
   }
 
   /* Set this as active session */
@@ -482,7 +527,8 @@ static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
   /* Call monitor callback */
   if (session->monitor)
     (*session->monitor)(session->client, session->conn,
-                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
+                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
+                       SILC_CLIENT_FILE_OK, 0, 0,
                        session->client_entry, session->session_id,
                        NULL, session->monitor_context);
 
@@ -496,17 +542,6 @@ static void silc_client_ftp_start_key_agreement(SilcClientFtpSession session,
   conn->sock->port = silc_net_get_remote_port(sock);
   session->sock = silc_socket_dup(conn->sock);
 
-  /* Allocate the SFTP */
-  if (session->server) {
-    session->sftp = silc_sftp_server_start(conn->sock,
-                                          silc_client_ftp_send_packet,
-                                          session, session->fs);
-
-    /* Monitor transmission */
-    silc_sftp_server_set_monitor(session->sftp, SILC_SFTP_MONITOR_READ,
-                                silc_client_ftp_monitor, session);
-  }
-
   /* Allocate internal context for key exchange protocol. This is
      sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
@@ -557,7 +592,8 @@ SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
     return;
@@ -576,7 +612,8 @@ SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
     return;
@@ -588,7 +625,8 @@ SILC_TASK_CALLBACK(silc_client_ftp_process_key_agreement)
   /* Call monitor callback */
   if (session->monitor)
     (*session->monitor)(session->client, session->conn,
-                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 0, 0,
+                       SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT, 
+                       SILC_CLIENT_FILE_OK, 0, 0,
                        session->client_entry, session->session_id,
                        NULL, session->monitor_context);
 
@@ -663,7 +701,6 @@ void silc_client_ftp_session_free_client(SilcClientConnection conn,
       if (session->sock)
        session->sock->user_data = NULL;
       silc_client_ftp_session_free(session);
-      break;
     }
   }
 }
@@ -739,6 +776,8 @@ uint32 silc_client_file_send(SilcClient client,
                             SilcClientConnection conn,
                             SilcClientFileMonitor monitor,
                             void *monitor_context,
+                            const char *local_ip,
+                            uint32 local_port,
                             SilcClientEntry client_entry,
                             const char *filepath)
 {
@@ -784,8 +823,29 @@ uint32 silc_client_file_send(SilcClient client,
 
   session->filesize = silc_file_size(filepath);
 
+  /* Create the listener for incoming key exchange protocol. */
+  if (local_ip)
+    session->hostname = strdup(local_ip);
+  else
+    session->hostname = silc_net_localip();
+  session->listener = silc_net_create_server(local_port, session->hostname);
+  if (session->listener < 0) {
+    /* 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->hostname = NULL;
+  } else {
+    /* Listener ready */
+    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);
+  }
+
   /* Send the key agreement inside FTP packet */
-  keyagr = silc_key_agreement_payload_encode(NULL, 0);
+  keyagr = silc_key_agreement_payload_encode(session->hostname, session->port);
 
   ftp = silc_buffer_alloc(1 + keyagr->len);
   silc_buffer_pull_tail(ftp, SILC_BUFFER_END(ftp));
@@ -816,7 +876,6 @@ silc_client_file_receive(SilcClient client,
                         SilcClientConnection conn,
                         SilcClientFileMonitor monitor,
                         void *monitor_context,
-                        SilcClientEntry client_entry,
                         uint32 session_id)
 {
   SilcClientFtpSession session;
@@ -845,7 +904,6 @@ silc_client_file_receive(SilcClient client,
 
   session->monitor = monitor;
   session->monitor_context = monitor_context;
-  session->client_entry = client_entry;
   session->conn = conn;
 
   /* If the hostname and port already exists then the remote client did
@@ -861,6 +919,9 @@ silc_client_file_receive(SilcClient client,
     session->listener = silc_net_create_server(0, session->hostname);
     if (session->listener < 0) {
       SILC_LOG_DEBUG(("Could not create listener"));
+      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR, 
+                                "Cannot create listener on %s: %s", 
+                                session->hostname, strerror(errno));
       return SILC_CLIENT_FILE_ERROR;
     }
     session->port = silc_net_get_local_port(session->listener);
@@ -878,7 +939,8 @@ silc_client_file_receive(SilcClient client,
                       SILC_STR_UI_XNSTRING(keyagr->data, keyagr->len),
                       SILC_STR_END);
     silc_client_packet_send(client, conn->sock, SILC_PACKET_FTP,
-                           client_entry->id, SILC_ID_CLIENT, NULL, NULL,
+                           session->client_entry->id, 
+                           SILC_ID_CLIENT, NULL, NULL,
                            ftp->data, ftp->len, FALSE);
     
     silc_buffer_free(keyagr);
@@ -952,14 +1014,15 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
   }
 
   /* Parse the key agreement payload */
-  payload = silc_key_agreement_payload_parse(packet->buffer);
+  payload = silc_key_agreement_payload_parse(packet->buffer->data,
+                                            packet->buffer->len);
   if (!payload)
     goto out;
 
   hostname = silc_key_agreement_get_hostname(payload);
   port = silc_key_agreement_get_port(payload);
 
-  if (session == SILC_LIST_END) {
+  if (session == SILC_LIST_END || (!hostname && !port)) {
     /* No session found, create one and let the application know about
        incoming file transfer request. */
     
@@ -968,11 +1031,12 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
     session->session_id = ++conn->next_session_id;
     session->client = client;
     session->conn = conn;
+    session->client_entry = client_entry;
     silc_dlist_add(conn->ftp_sessions, session);
 
     /* Let the application know */
-    client->ops->ftp(client, conn, client_entry,
-                    session->session_id, hostname, port);
+    client->internal->ops->ftp(client, conn, client_entry,
+                              session->session_id, hostname, port);
 
     if (hostname && port) {
       session->hostname = strdup(hostname);
@@ -982,9 +1046,6 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
     goto out;
   }
 
-  if (!hostname)
-    goto out;
-
   session->hostname = strdup(hostname);
   session->port = port;
 
@@ -994,7 +1055,8 @@ static void silc_client_ftp_resolve_cb(SilcClient client,
     /* Call monitor callback */
     if (session->monitor)
       (*session->monitor)(session->client, session->conn,
-                         SILC_CLIENT_FILE_MONITOR_ERROR, 0, 0,
+                         SILC_CLIENT_FILE_MONITOR_ERROR, 
+                         SILC_CLIENT_FILE_ERROR, 0, 0,
                          session->client_entry, session->session_id,
                          session->filepath, session->monitor_context);
   }