]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
libssh: simplify myssh_statemach_act()
authorDaniel Stenberg <daniel@haxx.se>
Sun, 11 Jan 2026 10:15:00 +0000 (11:15 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Sun, 11 Jan 2026 22:28:54 +0000 (23:28 +0100)
Reduce complexity by splitting out sub functions.

Made myssh_quote_error() to reduce code duplication.

Closes #20252

lib/vssh/libssh.c

index 3e6cb3a832329fd33bde629e624dc0d11ff09dd1..aa029d3b7aa53a3dafa1d47123997cddab892737 100644 (file)
@@ -727,6 +727,19 @@ static int myssh_in_SFTP_READDIR_DONE(struct Curl_easy *data,
   return SSH_NO_ERROR;
 }
 
+static void myssh_quote_error(struct Curl_easy *data, struct ssh_conn *sshc,
+                              const char *cmd)
+{
+  if(cmd)
+    failf(data, "%s command failed: %s", cmd,
+          ssh_get_error(sshc->ssh_session));
+  Curl_safefree(sshc->quote_path1);
+  Curl_safefree(sshc->quote_path2);
+  myssh_to(data, sshc, SSH_SFTP_CLOSE);
+  sshc->nextstate = SSH_NO_STATE;
+  sshc->actualcode = CURLE_QUOTE_ERROR;
+}
+
 static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data,
                                        struct ssh_conn *sshc)
 {
@@ -734,12 +747,7 @@ static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data,
 
   statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1);
   if(!statvfs && !sshc->acceptfail) {
-    Curl_safefree(sshc->quote_path1);
-    failf(data, "statvfs command failed: %s",
-          ssh_get_error(sshc->ssh_session));
-    myssh_to(data, sshc, SSH_SFTP_CLOSE);
-    sshc->nextstate = SSH_NO_STATE;
-    sshc->actualcode = CURLE_QUOTE_ERROR;
+    myssh_quote_error(data, sshc, "statvfs");
     return SSH_OK;
   }
   else if(statvfs) {
@@ -1765,13 +1773,9 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data,
     sftp_attributes_free(sshc->quote_attrs);
   sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2);
   if(!sshc->quote_attrs) {
-    Curl_safefree(sshc->quote_path1);
-    Curl_safefree(sshc->quote_path2);
     failf(data, "Attempt to get SFTP stats failed: %d",
           sftp_get_error(sshc->sftp_session));
-    myssh_to(data, sshc, SSH_SFTP_CLOSE);
-    sshc->nextstate = SSH_NO_STATE;
-    sshc->actualcode = CURLE_QUOTE_ERROR;
+    myssh_quote_error(data, sshc, NULL);
     return SSH_NO_ERROR;
   }
 
@@ -1780,12 +1784,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data,
     const char *p = sshc->quote_path1;
     curl_off_t gid;
     if(curlx_str_number(&p, &gid, UINT_MAX)) {
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
       failf(data, "Syntax error: chgrp gid not a number");
-      myssh_to(data, sshc, SSH_SFTP_CLOSE);
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_QUOTE_ERROR;
+      myssh_quote_error(data, sshc, NULL);
       return SSH_NO_ERROR;
     }
     sshc->quote_attrs->gid = (uint32_t)gid;
@@ -1795,12 +1795,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data,
     curl_off_t perms;
     const char *p = sshc->quote_path1;
     if(curlx_str_octal(&p, &perms, 07777)) {
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
       failf(data, "Syntax error: chmod permissions not a number");
-      myssh_to(data, sshc, SSH_SFTP_CLOSE);
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_QUOTE_ERROR;
+      myssh_quote_error(data, sshc, NULL);
       return SSH_NO_ERROR;
     }
     sshc->quote_attrs->permissions = (mode_t)perms;
@@ -1810,12 +1806,8 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data,
     const char *p = sshc->quote_path1;
     curl_off_t uid;
     if(curlx_str_number(&p, &uid, UINT_MAX)) {
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
       failf(data, "Syntax error: chown uid not a number");
-      myssh_to(data, sshc, SSH_SFTP_CLOSE);
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_QUOTE_ERROR;
+      myssh_quote_error(data, sshc, NULL);
       return SSH_NO_ERROR;
     }
     sshc->quote_attrs->uid = (uint32_t)uid;
@@ -1836,11 +1828,7 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data,
     }
 #endif
     if(fail) {
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
-      myssh_to(data, sshc, SSH_SFTP_CLOSE);
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_QUOTE_ERROR;
+      myssh_quote_error(data, sshc, NULL);
       return SSH_NO_ERROR;
     }
     if(date > UINT_MAX)
@@ -1871,6 +1859,104 @@ static void conn_forget_socket(struct Curl_easy *data, int sockindex)
   }
 }
 
+static void myssh_SESSION_DISCONNECT(struct Curl_easy *data,
+                                     struct ssh_conn *sshc)
+{
+  /* during weird times when we have been prematurely aborted, the channel
+     is still alive when we reach this state and we MUST kill the channel
+     properly first */
+  if(sshc->scp_session) {
+    ssh_scp_free(sshc->scp_session);
+    sshc->scp_session = NULL;
+  }
+
+  if(sshc->sftp_file) {
+    sftp_close(sshc->sftp_file);
+    sshc->sftp_file = NULL;
+  }
+  if(sshc->sftp_session) {
+    sftp_free(sshc->sftp_session);
+    sshc->sftp_session = NULL;
+  }
+
+  ssh_disconnect(sshc->ssh_session);
+  if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
+    /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
+       tell the connection to forget about it. This libssh
+       bug is fixed in 0.10.0. */
+    conn_forget_socket(data, FIRSTSOCKET);
+  }
+
+  SSH_STRING_FREE_CHAR(sshc->homedir);
+
+  myssh_to(data, sshc, SSH_SESSION_FREE);
+}
+
+static int myssh_SSH_SCP_DOWNLOAD(struct Curl_easy *data,
+                                  struct ssh_conn *sshc)
+{
+  curl_off_t bytecount;
+  int rc = ssh_scp_pull_request(sshc->scp_session);
+  if(rc != SSH_SCP_REQUEST_NEWFILE) {
+    const char *err_msg = ssh_get_error(sshc->ssh_session);
+    failf(data, "%s", err_msg);
+    return myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND);
+  }
+
+  /* download data */
+  bytecount = ssh_scp_request_get_size(sshc->scp_session);
+  data->req.maxdownload = (curl_off_t)bytecount;
+  Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount);
+
+  /* not set by Curl_xfer_setup to preserve keepon bits */
+  data->conn->send_idx = 0;
+
+  myssh_to(data, sshc, SSH_STOP);
+  return 0;
+}
+
+static int myssh_in_TRANS_INIT(struct Curl_easy *data, struct ssh_conn *sshc,
+                               struct SSHPROTO *sshp)
+{
+  CURLcode result;
+  int rc = 0;
+  if(!sshp)
+    return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT);
+
+  result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+  if(result) {
+    sshc->actualcode = result;
+    myssh_to(data, sshc, SSH_STOP);
+    return 0;
+  }
+
+  /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */
+  ssh_set_blocking(sshc->ssh_session, 1);
+
+  if(data->state.upload) {
+    if(data->state.infilesize < 0) {
+      failf(data, "SCP requires a known file size for upload");
+      return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
+    }
+
+    sshc->scp_session =
+      ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path);
+    myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT);
+  }
+  else {
+    sshc->scp_session =
+      ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path);
+    myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT);
+  }
+
+  if(!sshc->scp_session) {
+    const char *err_msg = ssh_get_error(sshc->ssh_session);
+    failf(data, "%s", err_msg);
+    rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
+  }
+  return rc;
+}
+
 /*
  * ssh_statemach_act() runs the SSH state machine as far as it can without
  * blocking and without reaching the end. The data the pointer 'block' points
@@ -1883,9 +1969,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
                                     bool *block)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
   int rc = SSH_NO_ERROR, err;
-  const char *err_msg;
 
   *block = FALSE;                   /* we are not blocking by default */
   do {
@@ -1942,14 +2026,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       break;
     case SSH_SFTP_QUOTE_INIT:
       rc = sshp ? myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp) :
-           CURLE_FAILED_INIT;
+        CURLE_FAILED_INIT;
       break;
     case SSH_SFTP_POSTQUOTE_INIT:
       rc = myssh_in_SFTP_POSTQUOTE_INIT(data, sshc);
       break;
     case SSH_SFTP_QUOTE:
-      rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) :
-           CURLE_FAILED_INIT;
+      rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : CURLE_FAILED_INIT;
       break;
     case SSH_SFTP_NEXT_QUOTE:
       rc = myssh_in_SFTP_NEXT_QUOTE(data, sshc);
@@ -1957,23 +2040,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
     case SSH_SFTP_QUOTE_STAT:
       rc = myssh_in_SFTP_QUOTE_STAT(data, sshc);
       break;
-
     case SSH_SFTP_QUOTE_SETSTAT:
       rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
                         sshc->quote_attrs);
       if(rc == SSH_AGAIN)
         break;
       if(rc && !sshc->acceptfail) {
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "Attempt to set SFTP stats failed: %s",
-              ssh_get_error(sshc->ssh_session));
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        /* sshc->actualcode = sftp_error_to_CURLE(err);
-         * we do not send the actual error; we return
-         * the error the libssh2 backend is returning */
+        myssh_quote_error(data, sshc, "setstat");
         break;
       }
       myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
@@ -1985,13 +2058,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       if(rc == SSH_AGAIN)
         break;
       if(rc && !sshc->acceptfail) {
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "symlink command failed: %s",
-              ssh_get_error(sshc->ssh_session));
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
+        myssh_quote_error(data, sshc, "symlink");
         break;
       }
       myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
@@ -2003,12 +2070,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       if(rc == SSH_AGAIN)
         break;
       if(rc && !sshc->acceptfail) {
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "mkdir command failed: %s",
-              ssh_get_error(sshc->ssh_session));
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
+        myssh_quote_error(data, sshc, "mkdir");
         break;
       }
       myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
@@ -2020,13 +2082,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       if(rc == SSH_AGAIN)
         break;
       if(rc && !sshc->acceptfail) {
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "rename command failed: %s",
-              ssh_get_error(sshc->ssh_session));
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
+        myssh_quote_error(data, sshc, "rename");
         break;
       }
       myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
@@ -2037,12 +2093,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       if(rc == SSH_AGAIN)
         break;
       if(rc && !sshc->acceptfail) {
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "rmdir command failed: %s",
-              ssh_get_error(sshc->ssh_session));
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
+        myssh_quote_error(data, sshc, "rmdir");
         break;
       }
       myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
@@ -2053,11 +2104,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       if(rc == SSH_AGAIN)
         break;
       if(rc && !sshc->acceptfail) {
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "rm command failed: %s", ssh_get_error(sshc->ssh_session));
-        myssh_to(data, sshc, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
+        myssh_quote_error(data, sshc, "rm");
         break;
       }
       myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE);
@@ -2170,11 +2217,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
 
     case SSH_SFTP_READDIR_INIT:
       rc = sshp ? myssh_in_SFTP_READDIR_INIT(data, sshc, sshp) :
-           CURLE_FAILED_INIT;
+        CURLE_FAILED_INIT;
       break;
     case SSH_SFTP_READDIR:
-      rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) :
-           CURLE_FAILED_INIT;
+      rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : CURLE_FAILED_INIT;
       break;
     case SSH_SFTP_READDIR_LINK:
       rc = myssh_in_SFTP_READDIR_LINK(data, sshc);
@@ -2187,57 +2233,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       break;
     case SSH_SFTP_DOWNLOAD_INIT:
       rc = sshp ? myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp) :
-           CURLE_FAILED_INIT;
+        CURLE_FAILED_INIT;
       break;
     case SSH_SFTP_DOWNLOAD_STAT:
       rc = myssh_in_SFTP_DOWNLOAD_STAT(data, sshc);
       break;
     case SSH_SFTP_CLOSE:
-      rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) :
-           CURLE_FAILED_INIT;
+      rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : CURLE_FAILED_INIT;
       break;
     case SSH_SFTP_SHUTDOWN:
       rc = myssh_in_SFTP_SHUTDOWN(data, sshc);
       break;
-
     case SSH_SCP_TRANS_INIT:
-      if(!sshp) {
-        result = CURLE_FAILED_INIT;
-        break;
-      }
-      result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
-      if(result) {
-        sshc->actualcode = result;
-        myssh_to(data, sshc, SSH_STOP);
-        break;
-      }
-
-      /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */
-      ssh_set_blocking(sshc->ssh_session, 1);
-
-      if(data->state.upload) {
-        if(data->state.infilesize < 0) {
-          failf(data, "SCP requires a known file size for upload");
-          sshc->actualcode = CURLE_UPLOAD_FAILED;
-          rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
-          break;
-        }
-
-        sshc->scp_session =
-          ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path);
-        myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT);
-      }
-      else {
-        sshc->scp_session =
-          ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path);
-        myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT);
-      }
-
-      if(!sshc->scp_session) {
-        err_msg = ssh_get_error(sshc->ssh_session);
-        failf(data, "%s", err_msg);
-        rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
-      }
+      rc = myssh_in_TRANS_INIT(data, sshc, sshp);
       break;
 
     case SSH_SCP_UPLOAD_INIT:
@@ -2247,7 +2255,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       }
       rc = ssh_scp_init(sshc->scp_session);
       if(rc != SSH_OK) {
-        err_msg = ssh_get_error(sshc->ssh_session);
+        const char *err_msg = ssh_get_error(sshc->ssh_session);
         failf(data, "%s", err_msg);
         rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
         break;
@@ -2258,7 +2266,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
                                (int)data->set.new_file_perms);
 
       if(rc != SSH_OK) {
-        err_msg = ssh_get_error(sshc->ssh_session);
+        const char *err_msg = ssh_get_error(sshc->ssh_session);
         failf(data, "%s", err_msg);
         rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED);
         break;
@@ -2275,10 +2283,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       break;
 
     case SSH_SCP_DOWNLOAD_INIT:
-
       rc = ssh_scp_init(sshc->scp_session);
       if(rc != SSH_OK) {
-        err_msg = ssh_get_error(sshc->ssh_session);
+        const char *err_msg = ssh_get_error(sshc->ssh_session);
         failf(data, "%s", err_msg);
         rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT);
         break;
@@ -2286,28 +2293,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       myssh_to(data, sshc, SSH_SCP_DOWNLOAD);
       FALLTHROUGH();
 
-    case SSH_SCP_DOWNLOAD: {
-      curl_off_t bytecount;
-
-      rc = ssh_scp_pull_request(sshc->scp_session);
-      if(rc != SSH_SCP_REQUEST_NEWFILE) {
-        err_msg = ssh_get_error(sshc->ssh_session);
-        failf(data, "%s", err_msg);
-        rc = myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND);
-        break;
-      }
-
-      /* download data */
-      bytecount = ssh_scp_request_get_size(sshc->scp_session);
-      data->req.maxdownload = (curl_off_t)bytecount;
-      Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount);
-
-      /* not set by Curl_xfer_setup to preserve keepon bits */
-      conn->send_idx = 0;
-
-      myssh_to(data, sshc, SSH_STOP);
+    case SSH_SCP_DOWNLOAD:
+      rc = myssh_SSH_SCP_DOWNLOAD(data, sshc);
       break;
-    }
+
     case SSH_SCP_DONE:
       if(data->state.upload)
         myssh_to(data, sshc, SSH_SCP_SEND_EOF);
@@ -2346,41 +2335,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data,
       FALLTHROUGH();
 
     case SSH_SESSION_DISCONNECT:
-      /* during weird times when we have been prematurely aborted, the channel
-         is still alive when we reach this state and we MUST kill the channel
-         properly first */
-      if(sshc->scp_session) {
-        ssh_scp_free(sshc->scp_session);
-        sshc->scp_session = NULL;
-      }
-
-      if(sshc->sftp_file) {
-        sftp_close(sshc->sftp_file);
-        sshc->sftp_file = NULL;
-      }
-      if(sshc->sftp_session) {
-        sftp_free(sshc->sftp_session);
-        sshc->sftp_session = NULL;
-      }
-
-      ssh_disconnect(sshc->ssh_session);
-      if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
-        /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
-           tell the connection to forget about it. This libssh
-           bug is fixed in 0.10.0. */
-        conn_forget_socket(data, FIRSTSOCKET);
-      }
-
-      SSH_STRING_FREE_CHAR(sshc->homedir);
-
-      myssh_to(data, sshc, SSH_SESSION_FREE);
+      myssh_SESSION_DISCONNECT(data, sshc);
       FALLTHROUGH();
     case SSH_SESSION_FREE:
       sshc_cleanup(sshc);
       /* the code we are about to return */
       result = sshc->actualcode;
       memset(sshc, 0, sizeof(struct ssh_conn));
-      connclose(conn, "SSH session free");
+      connclose(data->conn, "SSH session free");
       sshc->state = SSH_SESSION_FREE;   /* current */
       sshc->nextstate = SSH_NO_STATE;
       myssh_to(data, sshc, SSH_STOP);