home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / k95source / ckosftp.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  57KB  |  2,606 lines

  1. char *cksftpv = "SFTP support, 8.0.003, 23 June 2003";
  2.  
  3. /*
  4.  * Copyright (c) 2001-2003 Damien Miller.  All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  *
  15.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  16.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  17.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  18.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  19.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  24.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27. /* XXX: memleaks */
  28. /* XXX: signed vs unsigned */
  29. /* XXX: remove all logging, only return status codes */
  30. /* XXX: copy between two remote sites */
  31.  
  32. #include "ckcdeb.h"
  33. #ifdef SFTP_BUILTIN
  34. #include "ckuusr.h"
  35.  
  36. #include "includes.h"
  37.  
  38. #include <fcntl.h>
  39. #include <sys/stat.h>
  40.  
  41. #include "openbsd-compat/sys-queue.h"
  42.  
  43. #include "buffer.h"
  44. #include "bufaux.h"
  45. #include "getput.h"
  46. #include "xmalloc.h"
  47. #include "log.h"
  48. #include "atomicio.h"
  49. #include "xmalloc.h"
  50. #include "pathnames.h"
  51. #include "misc.h"
  52.  
  53. #include "ckosftp.h"
  54.  
  55. extern int showprogress;
  56.  
  57. /* Minimum amount of data to read at at time */
  58. #define MIN_READ_SIZE    512
  59.  
  60. /* Maximum packet size */
  61. #define MAX_MSG_LENGTH    (256 * 1024)
  62.  
  63. struct sftp_conn {
  64.     u_int transfer_buflen;
  65.     u_int num_requests;
  66.     u_int version;
  67.     u_int msg_id;
  68. };
  69.  
  70. static int
  71. send_msg(Buffer *m)
  72. {
  73.     u_char mlen[4];
  74.  
  75.     if (buffer_len(m) > MAX_MSG_LENGTH) {
  76.         fatal("Outbound message too long %u", buffer_len(m));
  77.         return(-1);
  78.     }
  79.  
  80.     /* Send length first */
  81.     PUT_32BIT(mlen, buffer_len(m));
  82.     if (ssh_tol(mlen, sizeof(mlen)) <= 0) {
  83.         fatal("Couldn't send packet: %s", strerror(errno));
  84.         return(-1);
  85.     }
  86.  
  87.     if (ssh_tol(buffer_ptr(m), buffer_len(m)) <= 0) {
  88.         fatal("Couldn't send packet: %s", strerror(errno));
  89.         return(-1);
  90.     }
  91.  
  92.     buffer_clear(m);
  93.     return(0);
  94. }
  95.  
  96. static int
  97. get_msg(Buffer *m)
  98. {
  99.     ssize_t len;
  100.     u_int msg_len, msg_read;
  101.  
  102.     buffer_append_space(m, 4);
  103.     len = ssh_xin(4, buffer_ptr(m));
  104.     if (len == 0) {
  105.         fatal("Connection closed");
  106.         return(-1);
  107.     } else if (len == -1) {
  108.         fatal("Couldn't read packet: %s", strerror(errno));
  109.         return(-1);
  110.     }
  111.     msg_len = buffer_get_int(m);
  112.     if (msg_len > MAX_MSG_LENGTH) {
  113.         fatal("Received message too long %u", msg_len);
  114.         return(-1);
  115.     }
  116.  
  117.     buffer_append_space(m, msg_len);
  118.     msg_read = 0;
  119.     while (msg_read < msg_len) {
  120.         len = ssh_xin(msg_len-msg_read, ((char *)buffer_ptr(m))+msg_read);
  121.         if (len == 0) {
  122.             fatal("Connection closed");
  123.             return(-1);
  124.         } else if (len == -1) {
  125.             fatal("Read packet: %s", strerror(errno));
  126.             return(-1);
  127.         }
  128.         msg_read += len;
  129.     }
  130.     return(0);
  131. }
  132.  
  133. static int
  134. send_string_request(u_int id, u_int code, char *s,
  135.     u_int len)
  136. {
  137.     Buffer msg;
  138.     memset(&msg,0,sizeof(Buffer));
  139.     buffer_init(&msg);
  140.     buffer_put_char(&msg, code);
  141.     buffer_put_int(&msg, id);
  142.     buffer_put_string(&msg, s, len);
  143.     if (send_msg(&msg) < 0)
  144.         return(-1);
  145.     debug3("Sent message T:%u I:%u", code, id);
  146.     buffer_free(&msg);
  147.     return(0);
  148. }
  149.  
  150. static int
  151. send_string_attrs_request(u_int id, u_int code, char *s,
  152.     u_int len, Attrib *a)
  153. {
  154.     Buffer msg;
  155.         memset(&msg,0,sizeof(Buffer));
  156.     buffer_init(&msg);
  157.     buffer_put_char(&msg, code);
  158.     buffer_put_int(&msg, id);
  159.     buffer_put_string(&msg, s, len);
  160.     encode_attrib(&msg, a);
  161.     if (send_msg(&msg) < 0)
  162.         return(-1);
  163.     debug3("Sent message T:%u I:%u", code, id);
  164.     buffer_free(&msg);
  165.     return(0);
  166. }
  167.  
  168. static u_int
  169. get_status(u_int expected_id)
  170. {
  171.     Buffer msg;
  172.     u_int type, id, status;
  173.  
  174.     memset(&msg,0,sizeof(Buffer));
  175.     buffer_init(&msg);
  176.     if (get_msg(&msg) < 0)
  177.         return(-1);
  178.     type = buffer_get_char(&msg);
  179.     id = buffer_get_int(&msg);
  180.  
  181.     if (id != expected_id) {
  182.         fatal("ID mismatch (%u != %u)", id, expected_id);
  183.         return(-1);
  184.     }
  185.     if (type != SSH2_FXP_STATUS) {
  186.         fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
  187.                SSH2_FXP_STATUS, type);
  188.         return(    -1);
  189.     }
  190.  
  191.     status = buffer_get_int(&msg);
  192.     buffer_free(&msg);
  193.  
  194.     debug3("SSH2_FXP_STATUS %u", status);
  195.  
  196.     return(status);
  197. }
  198.  
  199. static char *
  200. get_handle(u_int expected_id, u_int *len)
  201. {
  202.     Buffer msg;
  203.     u_int type, id;
  204.     char *handle;
  205.  
  206.     memset(&msg,0,sizeof(Buffer));
  207.     buffer_init(&msg);
  208.     if (get_msg(&msg) < 0)
  209.         return(NULL);
  210.     type = buffer_get_char(&msg);
  211.     id = buffer_get_int(&msg);
  212.  
  213.     if (id != expected_id) {
  214.         fatal("ID mismatch (%u != %u)", id, expected_id);
  215.         return(NULL);
  216.     }
  217.     if (type == SSH2_FXP_STATUS) {
  218.         int status = buffer_get_int(&msg);
  219.  
  220.         error("Couldn't get handle: %s", fx2txt(status));
  221.         return(NULL);
  222.     } else if (type != SSH2_FXP_HANDLE) {
  223.         fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
  224.             SSH2_FXP_HANDLE, type);
  225.         return(NULL);
  226.     }
  227.  
  228.     handle = buffer_get_string(&msg, len);
  229.     buffer_free(&msg);
  230.  
  231.     return(handle);
  232. }
  233.  
  234. static Attrib *
  235. get_decode_stat(u_int expected_id, int quiet)
  236. {
  237.     Buffer msg;
  238.     u_int type, id;
  239.     Attrib *a;
  240.  
  241.         memset(&msg,0,sizeof(Buffer));
  242.         buffer_init(&msg);
  243.     if (get_msg(&msg) < 0)
  244.         return(NULL);
  245.  
  246.     type = buffer_get_char(&msg);
  247.     id = buffer_get_int(&msg);
  248.  
  249.     debug3("Received stat reply T:%u I:%u", type, id);
  250.     if (id != expected_id) {
  251.         fatal("ID mismatch (%u != %u)", id, expected_id);
  252.         return(NULL);
  253.     }
  254.     if (type == SSH2_FXP_STATUS) {
  255.         int status = buffer_get_int(&msg);
  256.  
  257.         if (quiet)
  258.             debug1("Couldn't stat remote file: %s", fx2txt(status));
  259.         else
  260.             error("Couldn't stat remote file: %s", fx2txt(status));
  261.         return(NULL);
  262.     } else if (type != SSH2_FXP_ATTRS) {
  263.         fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
  264.             SSH2_FXP_ATTRS, type);
  265.         return(NULL);
  266.     }
  267.     a = decode_attrib(&msg);
  268.     buffer_free(&msg);
  269.  
  270.     return(a);
  271. }
  272.  
  273. struct sftp_conn *
  274. do_init(u_int transfer_buflen, u_int num_requests)
  275. {
  276.     u_int type;
  277.     int version;
  278.     Buffer msg;
  279.     struct sftp_conn *ret;
  280.  
  281.     memset(&msg,0,sizeof(Buffer));
  282.     buffer_init(&msg);
  283.     buffer_put_char(&msg, SSH2_FXP_INIT);
  284.     buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
  285.     if (send_msg(&msg) < 0)
  286.         return(NULL);
  287.  
  288.     buffer_clear(&msg);
  289.  
  290.     if (get_msg(&msg) < 0)
  291.         return(NULL);
  292.  
  293.     /* Expecting a VERSION reply */
  294.     if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
  295.         error("Invalid packet back from SSH2_FXP_INIT (type %u)",
  296.             type);
  297.         buffer_free(&msg);
  298.         return(NULL);
  299.     }
  300.     version = buffer_get_int(&msg);
  301.  
  302.     debug2("Remote version: %d", version);
  303.  
  304.     /* Check for extensions */
  305.     while (buffer_len(&msg) > 0) {
  306.         char *name = buffer_get_string(&msg, NULL);
  307.         char *value = buffer_get_string(&msg, NULL);
  308.  
  309.         debug2("Init extension: \"%s\"", name);
  310.         xfree(name);
  311.         xfree(value);
  312.     }
  313.  
  314.     buffer_free(&msg);
  315.  
  316.     ret = xmalloc(sizeof(*ret));
  317.     ret->transfer_buflen = transfer_buflen;
  318.     ret->num_requests = num_requests;
  319.     ret->version = version;
  320.     ret->msg_id = 1;
  321.  
  322.     /* Some filexfer v.0 servers don't support large packets */
  323.     if (version == 0)
  324.         ret->transfer_buflen = MIN(ret->transfer_buflen, 20480);
  325.  
  326.     return(ret);
  327. }
  328.  
  329. u_int
  330. sftp_proto_version(struct sftp_conn *conn)
  331. {
  332.     return(conn->version);
  333. }
  334.  
  335. int
  336. do_close(struct sftp_conn *conn, char *handle, u_int handle_len)
  337. {
  338.     u_int id, status;
  339.     Buffer msg;
  340.  
  341.         memset(&msg,0,sizeof(Buffer));
  342.         buffer_init(&msg);
  343.  
  344.     id = conn->msg_id++;
  345.     buffer_put_char(&msg, SSH2_FXP_CLOSE);
  346.     buffer_put_int(&msg, id);
  347.     buffer_put_string(&msg, handle, handle_len);
  348.     if (send_msg(&msg) < 0)
  349.         return(-1);
  350.     debug3("Sent message SSH2_FXP_CLOSE I:%u", id);
  351.  
  352.     status = get_status(id);
  353.     if (status != SSH2_FX_OK)
  354.         error("Couldn't close file: %s", fx2txt(status));
  355.  
  356.     buffer_free(&msg);
  357.  
  358.     return(status);
  359. }
  360.  
  361.  
  362. static int
  363. do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
  364.     SFTP_DIRENT ***dir)
  365. {
  366.     Buffer msg;
  367.     u_int type, id, handle_len, i, expected_id, ents = 0;
  368.     char *handle;
  369.  
  370.     id = conn->msg_id++;
  371.  
  372.         memset(&msg,0,sizeof(Buffer));
  373.         buffer_init(&msg);
  374.     buffer_put_char(&msg, SSH2_FXP_OPENDIR);
  375.     buffer_put_int(&msg, id);
  376.     buffer_put_cstring(&msg, path);
  377.     if (send_msg(&msg) < 0)
  378.         return(-1);
  379.  
  380.     buffer_clear(&msg);
  381.  
  382.     handle = get_handle(id, &handle_len);
  383.     if (handle == NULL)
  384.         return(-1);
  385.  
  386.     if (dir) {
  387.         ents = 0;
  388.         *dir = xmalloc(sizeof(**dir));
  389.         (*dir)[0] = NULL;
  390.     }
  391.  
  392.     for (;;) {
  393.         int count;
  394.  
  395.         id = expected_id = conn->msg_id++;
  396.  
  397.         debug3("Sending SSH2_FXP_READDIR I:%u", id);
  398.  
  399.         buffer_clear(&msg);
  400.         buffer_put_char(&msg, SSH2_FXP_READDIR);
  401.         buffer_put_int(&msg, id);
  402.         buffer_put_string(&msg, handle, handle_len);
  403.         if (send_msg(&msg) < 0);
  404.             return(-1);
  405.  
  406.         buffer_clear(&msg);
  407.  
  408.         if (get_msg(&msg) < 0)
  409.             return(-1);
  410.  
  411.         type = buffer_get_char(&msg);
  412.         id = buffer_get_int(&msg);
  413.  
  414.         debug3("Received reply T:%u I:%u", type, id);
  415.  
  416.         if (id != expected_id) {
  417.             fatal("ID mismatch (%u != %u)", id, expected_id);
  418.             return(-1);
  419.         }
  420.  
  421.         if (type == SSH2_FXP_STATUS) {
  422.             int status = buffer_get_int(&msg);
  423.  
  424.             debug3("Received SSH2_FXP_STATUS %d", status);
  425.  
  426.             if (status == SSH2_FX_EOF) {
  427.                 break;
  428.             } else {
  429.                 error("Couldn't read directory: %s",
  430.                     fx2txt(status));
  431.                 do_close(conn, handle, handle_len);
  432.                 xfree(handle);
  433.                 return(status);
  434.             }
  435.         } else if (type != SSH2_FXP_NAME) {
  436.             fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
  437.                 SSH2_FXP_NAME, type);
  438.             return(-1);
  439.         }
  440.  
  441.         count = buffer_get_int(&msg);
  442.         if (count == 0)
  443.             break;
  444.         debug3("Received %d SSH2_FXP_NAME responses", count);
  445.         for (i = 0; i < count; i++) {
  446.             char *filename, *longname;
  447.             Attrib *a;
  448.  
  449.             filename = buffer_get_string(&msg, NULL);
  450.             longname = buffer_get_string(&msg, NULL);
  451.             a = decode_attrib(&msg);
  452.  
  453.             if (printflag)
  454.                 printf("%s\n", longname);
  455.  
  456.             if (dir) {
  457.                 *dir = xrealloc(*dir, sizeof(**dir) *
  458.                     (ents + 2));
  459.                 (*dir)[ents] = xmalloc(sizeof(***dir));
  460.                 (*dir)[ents]->filename = xstrdup(filename);
  461.                 (*dir)[ents]->longname = xstrdup(longname);
  462.                 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
  463.                 (*dir)[++ents] = NULL;
  464.             }
  465.  
  466.             xfree(filename);
  467.             xfree(longname);
  468.         }
  469.     }
  470.  
  471.     buffer_free(&msg);
  472.     do_close(conn, handle, handle_len);
  473.     xfree(handle);
  474.  
  475.     return(0);
  476. }
  477.  
  478. int
  479. do_readdir(struct sftp_conn *conn, char *path, SFTP_DIRENT ***dir)
  480. {
  481.     return(do_lsreaddir(conn, path, 0, dir));
  482. }
  483.  
  484. void 
  485. free_sftp_dirents(SFTP_DIRENT **s)
  486. {
  487.     int i;
  488.  
  489.     for (i = 0; s[i]; i++) {
  490.         xfree(s[i]->filename);
  491.         xfree(s[i]->longname);
  492.         xfree(s[i]);
  493.     }
  494.     xfree(s);
  495. }
  496.  
  497. int
  498. do_rm(struct sftp_conn *conn, char *path)
  499. {
  500.     u_int status, id;
  501.  
  502.     debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
  503.  
  504.     id = conn->msg_id++;
  505.     send_string_request(id, SSH2_FXP_REMOVE, path,
  506.         strlen(path));
  507.     status = get_status(id);
  508.     if (status != SSH2_FX_OK)
  509.         error("Couldn't delete file: %s", fx2txt(status));
  510.     return(status);
  511. }
  512.  
  513. int
  514. do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
  515. {
  516.     u_int status, id;
  517.  
  518.     id = conn->msg_id++;
  519.     send_string_attrs_request(id, SSH2_FXP_MKDIR, path,
  520.         strlen(path), a);
  521.  
  522.     status = get_status(id);
  523.     if (status != SSH2_FX_OK)
  524.         error("Couldn't create directory: %s", fx2txt(status));
  525.  
  526.     return(status);
  527. }
  528.  
  529. int
  530. do_rmdir(struct sftp_conn *conn, char *path)
  531. {
  532.     u_int status, id;
  533.  
  534.     id = conn->msg_id++;
  535.     send_string_request(id, SSH2_FXP_RMDIR, path,
  536.         strlen(path));
  537.  
  538.     status = get_status(id);
  539.     if (status != SSH2_FX_OK)
  540.         error("Couldn't remove directory: %s", fx2txt(status));
  541.  
  542.     return(status);
  543. }
  544.  
  545. Attrib *
  546. do_stat(struct sftp_conn *conn, char *path, int quiet)
  547. {
  548.     u_int id;
  549.  
  550.     id = conn->msg_id++;
  551.  
  552.     send_string_request( id,
  553.         conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT,
  554.         path, strlen(path));
  555.  
  556.     return(get_decode_stat(id, quiet));
  557. }
  558.  
  559. Attrib *
  560. do_lstat(struct sftp_conn *conn, char *path, int quiet)
  561. {
  562.     u_int id;
  563.  
  564.     if (conn->version == 0) {
  565.         if (quiet)
  566.             debug1("Server version does not support lstat operation");
  567.         else
  568.             error("Server version does not support lstat operation");
  569.         return(do_stat(conn, path, quiet));
  570.     }
  571.  
  572.     id = conn->msg_id++;
  573.     send_string_request( id, SSH2_FXP_LSTAT, path,
  574.         strlen(path));
  575.  
  576.     return(get_decode_stat(id, quiet));
  577. }
  578.  
  579. Attrib *
  580. do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
  581. {
  582.     u_int id;
  583.  
  584.     id = conn->msg_id++;
  585.     send_string_request( id, SSH2_FXP_FSTAT, handle,
  586.         handle_len);
  587.  
  588.     return(get_decode_stat(id, quiet));
  589. }
  590.  
  591. int
  592. do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
  593. {
  594.     u_int status, id;
  595.  
  596.     id = conn->msg_id++;
  597.     send_string_attrs_request( id, SSH2_FXP_SETSTAT, path,
  598.         strlen(path), a);
  599.  
  600.     status = get_status(id);
  601.     if (status != SSH2_FX_OK)
  602.         error("Couldn't setstat on \"%s\": %s", path,
  603.             fx2txt(status));
  604.  
  605.     return(status);
  606. }
  607.  
  608. int
  609. do_fsetstat(struct sftp_conn *conn, char *handle, u_int handle_len,
  610.     Attrib *a)
  611. {
  612.     u_int status, id;
  613.  
  614.     id = conn->msg_id++;
  615.     send_string_attrs_request( id, SSH2_FXP_FSETSTAT, handle,
  616.         handle_len, a);
  617.  
  618.     status = get_status(id);
  619.     if (status != SSH2_FX_OK)
  620.         error("Couldn't fsetstat: %s", fx2txt(status));
  621.  
  622.     return(status);
  623. }
  624.  
  625. char *
  626. do_realpath(struct sftp_conn *conn, char *path)
  627. {
  628.     Buffer msg;
  629.     u_int type, expected_id, count, id;
  630.     char *filename, *longname;
  631.     Attrib *a;
  632.  
  633.     expected_id = id = conn->msg_id++;
  634.     send_string_request( id, SSH2_FXP_REALPATH, path,
  635.         strlen(path));
  636.  
  637.         memset(&msg,0,sizeof(Buffer));
  638.         buffer_init(&msg);
  639.  
  640.     if (get_msg(&msg) < 0)
  641.         return(NULL);
  642.     type = buffer_get_char(&msg);
  643.     id = buffer_get_int(&msg);
  644.  
  645.     if (id != expected_id) {
  646.         fatal("ID mismatch (%u != %u)", id, expected_id);
  647.         return(NULL);
  648.     }
  649.  
  650.     if (type == SSH2_FXP_STATUS) {
  651.         u_int status = buffer_get_int(&msg);
  652.  
  653.         error("Couldn't canonicalise: %s", fx2txt(status));
  654.         return(NULL);
  655.     } else if (type != SSH2_FXP_NAME) {
  656.         fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
  657.             SSH2_FXP_NAME, type);
  658.         return(NULL);
  659.     }
  660.  
  661.     count = buffer_get_int(&msg);
  662.     if (count != 1) {
  663.         fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
  664.         return(NULL);
  665.     }
  666.  
  667.     filename = buffer_get_string(&msg, NULL);
  668.     longname = buffer_get_string(&msg, NULL);
  669.     a = decode_attrib(&msg);
  670.  
  671.     debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
  672.  
  673.     xfree(longname);
  674.  
  675.     buffer_free(&msg);
  676.  
  677.     return(filename);
  678. }
  679.  
  680. int
  681. do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
  682. {
  683.     Buffer msg;
  684.     u_int status, id;
  685.  
  686.         memset(&msg,0,sizeof(Buffer));
  687.         buffer_init(&msg);
  688.  
  689.     /* Send rename request */
  690.     id = conn->msg_id++;
  691.     buffer_put_char(&msg, SSH2_FXP_RENAME);
  692.     buffer_put_int(&msg, id);
  693.     buffer_put_cstring(&msg, oldpath);
  694.     buffer_put_cstring(&msg, newpath);
  695.     if (send_msg(&msg) < 0)
  696.         return(-1);
  697.  
  698.     debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
  699.         newpath);
  700.     buffer_free(&msg);
  701.  
  702.     status = get_status(id);
  703.     if (status != SSH2_FX_OK)
  704.         error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath,
  705.             newpath, fx2txt(status));
  706.  
  707.     return(status);
  708. }
  709.  
  710. int
  711. do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
  712. {
  713.     Buffer msg;
  714.     u_int status, id;
  715.  
  716.     if (conn->version < 3) {
  717.         error("This server does not support the symlink operation");
  718.         return(SSH2_FX_OP_UNSUPPORTED);
  719.     }
  720.  
  721.     memset(&msg,0,sizeof(Buffer));
  722.     buffer_init(&msg);
  723.  
  724.     /* Send rename request */
  725.     id = conn->msg_id++;
  726.     buffer_put_char(&msg, SSH2_FXP_SYMLINK);
  727.     buffer_put_int(&msg, id);
  728.     buffer_put_cstring(&msg, oldpath);
  729.     buffer_put_cstring(&msg, newpath);
  730.     if (send_msg(&msg) < 0)
  731.         return(-1);
  732.     debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath,
  733.         newpath);
  734.     buffer_free(&msg);
  735.  
  736.     status = get_status(id);
  737.     if (status != SSH2_FX_OK)
  738.         error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath,
  739.             newpath, fx2txt(status));
  740.  
  741.     return(status);
  742. }
  743.  
  744. char *
  745. do_readlink(struct sftp_conn *conn, char *path)
  746. {
  747.     Buffer msg;
  748.     u_int type, expected_id, count, id;
  749.     char *filename, *longname;
  750.     Attrib *a;
  751.  
  752.     expected_id = id = conn->msg_id++;
  753.     send_string_request( id, SSH2_FXP_READLINK, path,
  754.                          strlen(path));
  755.  
  756.     memset(&msg,0,sizeof(Buffer));
  757.     buffer_init(&msg);
  758.  
  759.     if (get_msg(&msg) < 0)
  760.         return(NULL);
  761.     type = buffer_get_char(&msg);
  762.     id = buffer_get_int(&msg);
  763.  
  764.     if (id != expected_id) {
  765.         fatal("ID mismatch (%u != %u)", id, expected_id);
  766.         return(NULL);
  767.     }
  768.  
  769.     if (type == SSH2_FXP_STATUS) {
  770.         u_int status = buffer_get_int(&msg);
  771.  
  772.         error("Couldn't readlink: %s", fx2txt(status));
  773.         return(NULL);
  774.     } else if (type != SSH2_FXP_NAME) {
  775.         fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
  776.             SSH2_FXP_NAME, type);
  777.         return(NULL);
  778.     }
  779.  
  780.     count = buffer_get_int(&msg);
  781.     if (count != 1) {
  782.         fatal("Got multiple names (%d) from SSH_FXP_READLINK", count);
  783.         return(NULL);
  784.     }
  785.  
  786.     filename = buffer_get_string(&msg, NULL);
  787.     longname = buffer_get_string(&msg, NULL);
  788.     a = decode_attrib(&msg);
  789.  
  790.     debug3("SSH_FXP_READLINK %s -> %s", path, filename);
  791.  
  792.     xfree(longname);
  793.  
  794.     buffer_free(&msg);
  795.  
  796.     return(filename);
  797. }
  798.  
  799. static int
  800. send_read_request(u_int id, u_int64_t offset, u_int len,
  801.     char *handle, u_int handle_len)
  802. {
  803.     Buffer msg;
  804.  
  805.     memset(&msg,0,sizeof(Buffer));
  806.     buffer_init(&msg);
  807.     buffer_clear(&msg);
  808.     buffer_put_char(&msg, SSH2_FXP_READ);
  809.     buffer_put_int(&msg, id);
  810.     buffer_put_string(&msg, handle, handle_len);
  811.     buffer_put_int64(&msg, offset);
  812.     buffer_put_int(&msg, len);
  813.     if (send_msg(&msg) < 0)
  814.         return(-1);
  815.     buffer_free(&msg);
  816.     return(0);
  817. }
  818.  
  819. int
  820. do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
  821.     int pflag)
  822. {
  823.     Attrib junk, *a;
  824.     Buffer msg;
  825.     char *handle;
  826.     int local_fd, status, num_req, max_req, write_error;
  827.     int read_error, write_errno;
  828.     u_int64_t offset, size;
  829.     u_int handle_len, mode, type, id, buflen;
  830.     off_t progress_counter;
  831.     struct request {
  832.         u_int id;
  833.         u_int len;
  834.         u_int64_t offset;
  835.         TAILQ_ENTRY(request) tq;
  836.     };
  837.     TAILQ_HEAD(reqhead, request) requests;
  838.     struct request *req;
  839.  
  840.     TAILQ_INIT(&requests);
  841.  
  842.     a = do_stat(conn, remote_path, 0);
  843.     if (a == NULL)
  844.         return(-1);
  845.  
  846.     /* XXX: should we preserve set[ug]id? */
  847.     if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
  848.         mode = a->perm & 0777;
  849.     else
  850.         mode = 0666;
  851.  
  852.     if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
  853.         (!S_ISREG(a->perm))) {
  854.         error("Cannot download non-regular file: %s", remote_path);
  855.         return(-1);
  856.     }
  857.  
  858.     if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
  859.         size = a->size;
  860.     else
  861.         size = 0;
  862.  
  863.     buflen = conn->transfer_buflen;
  864.         memset(&msg,0,sizeof(Buffer));
  865.         buffer_init(&msg);
  866.  
  867.     /* Send open request */
  868.     id = conn->msg_id++;
  869.     buffer_put_char(&msg, SSH2_FXP_OPEN);
  870.     buffer_put_int(&msg, id);
  871.     buffer_put_cstring(&msg, remote_path);
  872.     buffer_put_int(&msg, SSH2_FXF_READ);
  873.     attrib_clear(&junk); /* Send empty attributes */
  874.     encode_attrib(&msg, &junk);
  875.     if (send_msg(&msg) < 0)
  876.         return(-1);
  877.     debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
  878.  
  879.     handle = get_handle(id, &handle_len);
  880.     if (handle == NULL) {
  881.         buffer_free(&msg);
  882.         return(-1);
  883.     }
  884.  
  885.     local_fd = open(local_path, _O_WRONLY | _O_CREAT | _O_TRUNC, 
  886.         mode | _S_IWRITE);
  887.     if (local_fd == -1) {
  888.         error("Couldn't open local file \"%s\" for writing: %s",
  889.             local_path, strerror(errno));
  890.         buffer_free(&msg);
  891.         xfree(handle);
  892.         return(-1);
  893.     }
  894.  
  895.     /* Read from remote and write to local */
  896.     write_error = read_error = write_errno = num_req = offset = 0;
  897.     max_req = 1;
  898.     progress_counter = 0;
  899.  
  900. #ifdef COMMENT
  901.     if (showprogress) {
  902.         if (size)
  903.             start_progress_meter(remote_path, size,
  904.                 &progress_counter);
  905.         else
  906.             printf("Fetching %s to %s\n", remote_path, local_path);
  907.     }
  908. #endif 
  909.  
  910.     while (num_req > 0 || max_req > 0) {
  911.         char *data;
  912.         u_int len;
  913.  
  914.         /* Send some more requests */
  915.         while (num_req < max_req) {
  916.             debug3("Request range %llu -> %llu (%d/%d)",
  917.                 (u_int64_t)offset,
  918.                 (u_int64_t)offset + buflen - 1,
  919.                 num_req, max_req);
  920.             req = xmalloc(sizeof(*req));
  921.             req->id = conn->msg_id++;
  922.             req->len = buflen;
  923.             req->offset = offset;
  924.             offset += buflen;
  925.             num_req++;
  926.             TAILQ_INSERT_TAIL(&requests, req, tq);
  927.             if (send_read_request( req->id, req->offset,
  928.                 req->len, handle, handle_len) < 0)
  929.                 return(-1);
  930.         }
  931.  
  932.         buffer_clear(&msg);
  933.         if (get_msg(&msg) < 0)
  934.             return(-1);
  935.         type = buffer_get_char(&msg);
  936.         id = buffer_get_int(&msg);
  937.         debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
  938.  
  939.         /* Find the request in our queue */
  940.         for(req = TAILQ_FIRST(&requests);
  941.             req != NULL && req->id != id;
  942.             req = TAILQ_NEXT(req, tq))
  943.             ;
  944.             if (req == NULL) {
  945.                 fatal("Unexpected reply %u", id);
  946.                 return(-1);
  947.             }
  948.  
  949.         switch (type) {
  950.         case SSH2_FXP_STATUS:
  951.             status = buffer_get_int(&msg);
  952.             if (status != SSH2_FX_EOF)
  953.                 read_error = 1;
  954.             max_req = 0;
  955.             TAILQ_REMOVE(&requests, req, tq);
  956.             xfree(req);
  957.             num_req--;
  958.             break;
  959.         case SSH2_FXP_DATA:
  960.             data = buffer_get_string(&msg, &len);
  961.             debug3("Received data %llu -> %llu",
  962.                 (u_int64_t)req->offset,
  963.                 (u_int64_t)req->offset + len - 1);
  964.             if (len > req->len) {
  965.                 fatal("Received more data than asked for "
  966.                     "%u > %u", len, req->len);
  967.                 return(-1);
  968.             }
  969.             if ((lseek(local_fd, req->offset, SEEK_SET) == -1 ||
  970.                 write(local_fd, data, len) != len) &&
  971.                 !write_error) {
  972.                 write_errno = errno;
  973.                 write_error = 1;
  974.                 max_req = 0;
  975.             }
  976.             progress_counter += len;
  977.             xfree(data);
  978.  
  979.             if (len == req->len) {
  980.                 TAILQ_REMOVE(&requests, req, tq);
  981.                 xfree(req);
  982.                 num_req--;
  983.             } else {
  984.                 /* Resend the request for the missing data */
  985.                 debug3("Short data block, re-requesting "
  986.                     "%llu -> %llu (%2d)",
  987.                     (u_int64_t)req->offset + len,
  988.                     (u_int64_t)req->offset +
  989.                     req->len - 1, num_req);
  990.                 req->id = conn->msg_id++;
  991.                 req->len -= len;
  992.                 req->offset += len;
  993.                 if (send_read_request( req->id,
  994.                     req->offset, req->len, handle, handle_len) < 0)
  995.                     return(-1);
  996.                 /* Reduce the request size */
  997.                 if (len < buflen)
  998.                     buflen = MAX(MIN_READ_SIZE, len);
  999.             }
  1000.             if (max_req > 0) { /* max_req = 0 iff EOF received */
  1001.                 if (size > 0 && offset > size) {
  1002.                     /* Only one request at a time
  1003.                      * after the expected EOF */
  1004.                     debug3("Finish at %llu (%2d)",
  1005.                         (u_int64_t)offset,
  1006.                         num_req);
  1007.                     max_req = 1;
  1008.                 }
  1009.                 else if (max_req < conn->num_requests + 1) {
  1010.                     ++max_req;
  1011.                 }
  1012.             }
  1013.             break;
  1014.         default:
  1015.             fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
  1016.                 SSH2_FXP_DATA, type);
  1017.             return(-1);
  1018.         }
  1019.     }
  1020.  
  1021. #ifdef COMMENT
  1022.     if (showprogress && size)
  1023.         stop_progress_meter();
  1024. #endif
  1025.  
  1026.     /* Sanity check */
  1027.     if (TAILQ_FIRST(&requests) != NULL) {
  1028.         fatal("Transfer complete, but requests still in queue");
  1029.         return(-1);
  1030.     }
  1031.  
  1032.     if (read_error) {
  1033.         error("Couldn't read from remote file \"%s\" : %s",
  1034.             remote_path, fx2txt(status));
  1035.         do_close(conn, handle, handle_len);
  1036.     } else if (write_error) {
  1037.         error("Couldn't write to \"%s\": %s", local_path,
  1038.             strerror(write_errno));
  1039.         status = -1;
  1040.         do_close(conn, handle, handle_len);
  1041.     } else {
  1042.         status = do_close(conn, handle, handle_len);
  1043.  
  1044.         /* Override umask and utimes if asked */
  1045. #ifdef HAVE_FCHMOD
  1046.         if (pflag && fchmod(local_fd, mode) == -1)
  1047. #else 
  1048.         if (pflag && chmod(local_path, mode) == -1)
  1049. #endif /* HAVE_FCHMOD */
  1050.             error("Couldn't set mode on \"%s\": %s", local_path,
  1051.                 strerror(errno));
  1052.         if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
  1053.             struct timeval tv[2];
  1054.             tv[0].tv_sec = a->atime;
  1055.             tv[1].tv_sec = a->mtime;
  1056.             tv[0].tv_usec = tv[1].tv_usec = 0;
  1057.             if (utimes(local_path, tv) == -1)
  1058.                 error("Can't set times on \"%s\": %s",
  1059.                     local_path, strerror(errno));
  1060.         }
  1061.     }
  1062.     close(local_fd);
  1063.     buffer_free(&msg);
  1064.     xfree(handle);
  1065.  
  1066.     return(status);
  1067. }
  1068.  
  1069. int
  1070. do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
  1071.     int pflag)
  1072. {
  1073.     int local_fd, status;
  1074.     u_int handle_len, id, type;
  1075.     u_int64_t offset;
  1076.     char *handle, *data;
  1077.     Buffer msg;
  1078.     struct _stat sb;
  1079.     Attrib a;
  1080.     u_int32_t startid;
  1081.     u_int32_t ackid;
  1082.     struct outstanding_ack {
  1083.         u_int id;
  1084.         u_int len;
  1085.         u_int64_t offset;
  1086.         TAILQ_ENTRY(outstanding_ack) tq;
  1087.     };
  1088.     TAILQ_HEAD(ackhead, outstanding_ack) acks;
  1089.     struct outstanding_ack *ack;
  1090.  
  1091.     TAILQ_INIT(&acks);
  1092.  
  1093.     if ((local_fd = open(local_path, _O_RDONLY, 0)) == -1) {
  1094.         error("Couldn't open local file \"%s\" for reading: %s",
  1095.             local_path, strerror(errno));
  1096.         return(-1);
  1097.     }
  1098.     if (fstat(local_fd, &sb) == -1) {
  1099.         error("Couldn't fstat local file \"%s\": %s",
  1100.             local_path, strerror(errno));
  1101.         close(local_fd);
  1102.         return(-1);
  1103.     }
  1104.     if (!S_ISREG(sb.st_mode)) {
  1105.         error("%s is not a regular file", local_path);
  1106.         close(local_fd);
  1107.         return(-1);
  1108.     }
  1109.     stat_to_attrib(&sb, &a);
  1110.  
  1111.     a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
  1112.     a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
  1113.     a.perm &= 0777;
  1114.     if (!pflag)
  1115.         a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
  1116.  
  1117.         memset(&msg,0,sizeof(Buffer));
  1118.         buffer_init(&msg);
  1119.  
  1120.     /* Send open request */
  1121.     id = conn->msg_id++;
  1122.     buffer_put_char(&msg, SSH2_FXP_OPEN);
  1123.     buffer_put_int(&msg, id);
  1124.     buffer_put_cstring(&msg, remote_path);
  1125.     buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
  1126.     encode_attrib(&msg, &a);
  1127.     if (send_msg(&msg) < 0)
  1128.         return(-1);
  1129.     debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
  1130.  
  1131.     buffer_clear(&msg);
  1132.  
  1133.     handle = get_handle(id, &handle_len);
  1134.     if (handle == NULL) {
  1135.         close(local_fd);
  1136.         buffer_free(&msg);
  1137.         return(-1);
  1138.     }
  1139.  
  1140.     startid = ackid = id + 1;
  1141.     data = xmalloc(conn->transfer_buflen);
  1142.  
  1143.     /* Read from local and write to remote */
  1144.     offset = 0;
  1145. #ifdef COMMENT
  1146.     if (showprogress)
  1147.         start_progress_meter(local_path, sb.st_size, &offset);
  1148.     else
  1149.         printf("Uploading %s to %s\n", local_path, remote_path);
  1150. #endif
  1151.  
  1152.     for (;;) {
  1153.         int len;
  1154.  
  1155.         /*
  1156.          * Can't use atomicio here because it returns 0 on EOF, thus losing
  1157.          * the last block of the file
  1158.          */
  1159.         do
  1160.             len = read(local_fd, data, conn->transfer_buflen);
  1161.         while ((len == -1) && (errno == EINTR || errno == EAGAIN));
  1162.  
  1163.         if (len == -1) {
  1164.             fatal("Couldn't read from \"%s\": %s", local_path,
  1165.                 strerror(errno));
  1166.             return(-1);
  1167.         }
  1168.         if (len != 0) {
  1169.             ack = xmalloc(sizeof(*ack));
  1170.             ack->id = ++id;
  1171.             ack->offset = offset;
  1172.             ack->len = len;
  1173.             TAILQ_INSERT_TAIL(&acks, ack, tq);
  1174.  
  1175.             buffer_clear(&msg);
  1176.             buffer_put_char(&msg, SSH2_FXP_WRITE);
  1177.             buffer_put_int(&msg, ack->id);
  1178.             buffer_put_string(&msg, handle, handle_len);
  1179.             buffer_put_int64(&msg, offset);
  1180.             buffer_put_string(&msg, data, len);
  1181.             if (send_msg(&msg) < 0)
  1182.                 return(-1);
  1183.             debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
  1184.                 id, (u_int64_t)offset, len);
  1185.         } else if (TAILQ_FIRST(&acks) == NULL)
  1186.             break;
  1187.  
  1188.             if (ack == NULL) {
  1189.                 fatal("Unexpected ACK %u", id);
  1190.                 return(-1);
  1191.             }
  1192.  
  1193.         if (id == startid || len == 0 ||
  1194.             id - ackid >= conn->num_requests) {
  1195.             u_int r_id;
  1196.  
  1197.             buffer_clear(&msg);
  1198.             if (get_msg(&msg) < 0)
  1199.                 return(-1);
  1200.             type = buffer_get_char(&msg);
  1201.             r_id = buffer_get_int(&msg);
  1202.             
  1203.             if (type != SSH2_FXP_STATUS) {
  1204.                 fatal("Expected SSH2_FXP_STATUS(%d) packet, "
  1205.                     "got %d", SSH2_FXP_STATUS, type);
  1206.                 return(-1);
  1207.             }
  1208.  
  1209.             status = buffer_get_int(&msg);
  1210.             debug3("SSH2_FXP_STATUS %d", status);
  1211.  
  1212.             /* Find the request in our queue */
  1213.             for(ack = TAILQ_FIRST(&acks);
  1214.                 ack != NULL && ack->id != r_id;
  1215.                 ack = TAILQ_NEXT(ack, tq))
  1216.                 ;
  1217.             if (ack == NULL) {
  1218.                 fatal("Can't find request for ID %u", r_id);
  1219.                 return(-1);
  1220.             }
  1221.             TAILQ_REMOVE(&acks, ack, tq);
  1222.  
  1223.             if (status != SSH2_FX_OK) {
  1224.                 error("Couldn't write to remote file \"%s\": %s",
  1225.                     remote_path, fx2txt(status));
  1226.                 do_close(conn, handle, handle_len);
  1227.                 close(local_fd);
  1228.                 xfree(data);
  1229.                 xfree(ack);
  1230.                 goto done;
  1231.             }
  1232.             debug3("In write loop, ack for %u %u bytes at %llu",
  1233.                ack->id, ack->len, (u_int64_t)ack->offset);
  1234.             ++ackid;
  1235.             xfree(ack);
  1236.         }
  1237.         offset += len;
  1238.     }
  1239. #ifdef COMMENT
  1240.     if (showprogress)
  1241.         stop_progress_meter();
  1242. #endif
  1243.     xfree(data);
  1244.  
  1245.     if (close(local_fd) == -1) {
  1246.         error("Couldn't close local file \"%s\": %s", local_path,
  1247.             strerror(errno));
  1248.         do_close(conn, handle, handle_len);
  1249.         status = -1;
  1250.         goto done;
  1251.     }
  1252.  
  1253.     /* Override umask and utimes if asked */
  1254.     if (pflag)
  1255.         do_fsetstat(conn, handle, handle_len, &a);
  1256.  
  1257.     status = do_close(conn, handle, handle_len);
  1258.  
  1259. done:
  1260.     xfree(handle);
  1261.     buffer_free(&msg);
  1262.     return(status);
  1263. }
  1264.  
  1265. /* File to read commands from */
  1266. extern FILE *infile;
  1267.  
  1268. /* Size of buffer used when copying files */
  1269. size_t copy_buffer_len = 32768;
  1270.  
  1271. /* Number of concurrent outstanding requests */
  1272. size_t num_requests = 16;
  1273.  
  1274. /* This is set to 0 if the progressmeter is not desired. */
  1275. int showprogress = 1;
  1276.  
  1277. /* Seperators for interactive commands */
  1278. #define WHITESPACE " \t\r\n"
  1279.  
  1280. /* Commands for interactive mode */
  1281. #define I_CHDIR        1
  1282. #define I_CHGRP        2
  1283. #define I_CHMOD        3
  1284. #define I_CHOWN        4
  1285. #define I_GET        5
  1286. #define I_HELP        6
  1287. #define I_LCHDIR    7
  1288. #define I_LLS        8
  1289. #define I_LMKDIR    9
  1290. #define I_LPWD        10
  1291. #define I_LS        11
  1292. #define I_LUMASK    12
  1293. #define I_MKDIR        13
  1294. #define I_PUT        14
  1295. #define I_PWD        15
  1296. #define I_QUIT        16
  1297. #define I_RENAME    17
  1298. #define I_RM        18
  1299. #define I_RMDIR        19
  1300. #define I_SYMLINK    21
  1301. #define I_VERSION    22
  1302. #define I_PROGRESS    23
  1303.  
  1304. struct CMD {
  1305.     const char *c;
  1306.     const int n;
  1307. };
  1308.  
  1309. static const struct CMD cmds[] = {
  1310.     { "bye",    I_QUIT },
  1311.     { "cd",        I_CHDIR },
  1312.     { "chdir",    I_CHDIR },
  1313.     { "chgrp",    I_CHGRP },
  1314.     { "chmod",    I_CHMOD },
  1315.     { "chown",    I_CHOWN },
  1316.     { "dir",    I_LS },
  1317.     { "exit",    I_QUIT },
  1318.     { "get",    I_GET },
  1319.     { "mget",    I_GET },
  1320.     { "help",    I_HELP },
  1321.     { "lcd",    I_LCHDIR },
  1322.     { "lchdir",    I_LCHDIR },
  1323.     { "lls",    I_LLS },
  1324.     { "lmkdir",    I_LMKDIR },
  1325.     { "ln",        I_SYMLINK },
  1326.     { "lpwd",    I_LPWD },
  1327.     { "ls",        I_LS },
  1328.     { "lumask",    I_LUMASK },
  1329.     { "mkdir",    I_MKDIR },
  1330.     { "progress",    I_PROGRESS },
  1331.     { "put",    I_PUT },
  1332.     { "mput",    I_PUT },
  1333.     { "pwd",    I_PWD },
  1334.     { "quit",    I_QUIT },
  1335.     { "rename",    I_RENAME },
  1336.     { "rm",        I_RM },
  1337.     { "rmdir",    I_RMDIR },
  1338.     { "symlink",    I_SYMLINK },
  1339.     { "version",    I_VERSION },
  1340.     { "?",        I_HELP },
  1341.     { NULL,            -1}
  1342. };
  1343.  
  1344. static void
  1345. help(void)
  1346. {
  1347.     printf("Available commands:\n");
  1348.     printf("cd path                       Change remote directory to 'path'\n");
  1349.     printf("lcd path                      Change local directory to 'path'\n");
  1350.     printf("chgrp grp path                Change group of file 'path' to 'grp'\n");
  1351.     printf("chmod mode path               Change permissions of file 'path' to 'mode'\n");
  1352.     printf("chown own path                Change owner of file 'path' to 'own'\n");
  1353.     printf("help                          Display this help text\n");
  1354.     printf("get remote-path [local-path]  Download file\n");
  1355.     printf("lls [ls-options [path]]       Display local directory listing\n");
  1356.     printf("ln oldpath newpath            Symlink remote file\n");
  1357.     printf("lmkdir path                   Create local directory\n");
  1358.     printf("lpwd                          Print local working directory\n");
  1359.     printf("ls [path]                     Display remote directory listing\n");
  1360.     printf("lumask umask                  Set local umask to 'umask'\n");
  1361.     printf("mkdir path                    Create remote directory\n");
  1362.     printf("progress                      Toggle display of progress meter\n");
  1363.     printf("put local-path [remote-path]  Upload file\n");
  1364.     printf("pwd                           Display remote working directory\n");
  1365.     printf("exit                          Quit sftp\n");
  1366.     printf("quit                          Quit sftp\n");
  1367.     printf("rename oldpath newpath        Rename remote file\n");
  1368.     printf("rmdir path                    Remove remote directory\n");
  1369.     printf("rm path                       Delete remote file\n");
  1370.     printf("symlink oldpath newpath       Symlink remote file\n");
  1371.     printf("version                       Show SFTP version\n");
  1372.     printf("!command                      Execute 'command' in local shell\n");
  1373.     printf("!                             Escape to local shell\n");
  1374.     printf("?                             Synonym for help\n");
  1375. }
  1376.  
  1377. static void
  1378. local_do_ls(const char *args)
  1379. {
  1380.     /* XXX - Need to implement */
  1381.     error("Need to implement local_do_ls()");
  1382. }
  1383.  
  1384. /* Strip one path (usually the pwd) from the start of another */
  1385. static char *
  1386. path_strip(char *path, char *strip)
  1387. {
  1388.     size_t len;
  1389.  
  1390.     if (strip == NULL)
  1391.         return (xstrdup(path));
  1392.  
  1393.     len = strlen(strip);
  1394.     if (strip != NULL && strncmp(path, strip, len) == 0) {
  1395.         if (strip[len - 1] != '/' && path[len] == '/')
  1396.             len++;
  1397.         return (xstrdup(path + len));
  1398.     }
  1399.  
  1400.     return (xstrdup(path));
  1401. }
  1402.  
  1403. static char *
  1404. path_append(char *p1, char *p2)
  1405. {
  1406.     char *ret;
  1407.     int len = strlen(p1) + strlen(p2) + 2;
  1408.  
  1409.     ret = xmalloc(len);
  1410.     strlcpy(ret, p1, len);
  1411.     if (p1[strlen(p1) - 1] != '/')
  1412.         strlcat(ret, "/", len);
  1413.     strlcat(ret, p2, len);
  1414.  
  1415.     return(ret);
  1416. }
  1417.  
  1418. static char *
  1419. make_absolute(char *p, char *pwd)
  1420. {
  1421.     char *abs;
  1422.  
  1423.     /* Derelativise */
  1424.     if (p && p[0] != '/') {
  1425.         abs = path_append(pwd, p);
  1426.         xfree(p);
  1427.         return(abs);
  1428.     } else
  1429.         return(p);
  1430. }
  1431.  
  1432. static int
  1433. infer_path(const char *p, char **ifp)
  1434. {
  1435.     char *cp;
  1436.  
  1437.     cp = strrchr(p, '/');
  1438.     if (cp == NULL) {
  1439.         *ifp = xstrdup(p);
  1440.         return(0);
  1441.     }
  1442.  
  1443.     if (!cp[1]) {
  1444.         error("Invalid path");
  1445.         return(-1);
  1446.     }
  1447.  
  1448.     *ifp = xstrdup(cp + 1);
  1449.     return(0);
  1450. }
  1451.  
  1452. static int
  1453. parse_getput_flags(const char **cpp, int *pflag)
  1454. {
  1455.     const char *cp = *cpp;
  1456.  
  1457.     /* Check for flags */
  1458.     if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
  1459.         switch (cp[1]) {
  1460.         case 'p':
  1461.         case 'P':
  1462.             *pflag = 1;
  1463.             break;
  1464.         default:
  1465.             error("Invalid flag -%c", cp[1]);
  1466.             return(-1);
  1467.         }
  1468.         cp += 2;
  1469.         *cpp = cp + strspn(cp, WHITESPACE);
  1470.     }
  1471.  
  1472.     return(0);
  1473. }
  1474.  
  1475. static int
  1476. parse_ls_flags(const char **cpp, int *lflag)
  1477. {
  1478.     const char *cp = *cpp;
  1479.  
  1480.     /* Check for flags */
  1481.     if (cp++[0] == '-') {
  1482.         for(; strchr(WHITESPACE, *cp) == NULL; cp++) {
  1483.             switch (*cp) {
  1484.             case 'l':
  1485.                 *lflag = 1;
  1486.                 break;
  1487.             default:
  1488.                 error("Invalid flag -%c", *cp);
  1489.                 return(-1);
  1490.             }
  1491.         }
  1492.         *cpp = cp + strspn(cp, WHITESPACE);
  1493.     }
  1494.  
  1495.     return(0);
  1496. }
  1497.  
  1498. static int
  1499. get_pathname(const char **cpp, char **path)
  1500. {
  1501.     const char *cp = *cpp, *end;
  1502.     char quot;
  1503.     int i;
  1504.  
  1505.     cp += strspn(cp, WHITESPACE);
  1506.     if (!*cp) {
  1507.         *cpp = cp;
  1508.         *path = NULL;
  1509.         return (0);
  1510.     }
  1511.  
  1512.     /* Check for quoted filenames */
  1513.     if (*cp == '\"' || *cp == '\'') {
  1514.         quot = *cp++;
  1515.  
  1516.         end = strchr(cp, quot);
  1517.         if (end == NULL) {
  1518.             error("Unterminated quote");
  1519.             goto fail;
  1520.         }
  1521.         if (cp == end) {
  1522.             error("Empty quotes");
  1523.             goto fail;
  1524.         }
  1525.         *cpp = end + 1 + strspn(end + 1, WHITESPACE);
  1526.     } else {
  1527.         /* Read to end of filename */
  1528.         end = strpbrk(cp, WHITESPACE);
  1529.         if (end == NULL)
  1530.             end = strchr(cp, '\0');
  1531.         *cpp = end + strspn(end, WHITESPACE);
  1532.     }
  1533.  
  1534.     i = end - cp;
  1535.  
  1536.     *path = xmalloc(i + 1);
  1537.     memcpy(*path, cp, i);
  1538.     (*path)[i] = '\0';
  1539.     return(0);
  1540.  
  1541.  fail:
  1542.     *path = NULL;
  1543.     return (-1);
  1544. }
  1545.  
  1546. static int
  1547. is_dir(char *path)
  1548. {
  1549.     struct _stat sb;
  1550.  
  1551.     /* XXX: report errors? */
  1552.     if (stat(path, &sb) == -1)
  1553.         return(0);
  1554.  
  1555.     return(sb.st_mode & _S_IFDIR);
  1556. }
  1557.  
  1558. static int
  1559. is_reg(char *path)
  1560. {
  1561.     struct _stat sb;
  1562.  
  1563.     if (stat(path, &sb) == -1) {
  1564.         fatal("stat %s: %s", path, strerror(errno));
  1565.         return(-1);
  1566.     }
  1567.  
  1568.     return(S_ISREG(sb.st_mode));
  1569. }
  1570.  
  1571. static int
  1572. remote_is_dir(struct sftp_conn *conn, char *path)
  1573. {
  1574.     Attrib *a;
  1575.  
  1576.     /* XXX: report errors? */
  1577.     if ((a = do_stat(conn, path, 1)) == NULL)
  1578.         return(0);
  1579.     if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS))
  1580.         return(0);
  1581.     return(a->perm & _S_IFDIR);
  1582. }
  1583.  
  1584. static int
  1585. process_get(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
  1586. {
  1587.     char *abs_src = NULL;
  1588.     char *abs_dst = NULL;
  1589.     char *tmp;
  1590.     glob_t g;
  1591.     int err = 0;
  1592.     int i;
  1593.  
  1594.     abs_src = xstrdup(src);
  1595.     abs_src = make_absolute(abs_src, pwd);
  1596.  
  1597.     memset(&g, 0, sizeof(g));
  1598.     debug3("Looking up %s", abs_src);
  1599.     if (remote_glob(conn, abs_src, 0, NULL, &g)) {
  1600.         error("File \"%s\" not found.", abs_src);
  1601.         err = -1;
  1602.         goto out;
  1603.     }
  1604.  
  1605.     /* Only one match, dst may be file, directory or unspecified */
  1606.     if (g.gl_pathv[0] && g.gl_matchc == 1) {
  1607.         if (dst) {
  1608.             /* If directory specified, append filename */
  1609.             if (is_dir(dst)) {
  1610.                 if (infer_path(g.gl_pathv[0], &tmp)) {
  1611.                     err = 1;
  1612.                     goto out;
  1613.                 }
  1614.                 abs_dst = path_append(dst, tmp);
  1615.                 xfree(tmp);
  1616.             } else
  1617.                 abs_dst = xstrdup(dst);
  1618.         } else if (infer_path(g.gl_pathv[0], &abs_dst)) {
  1619.             err = -1;
  1620.             goto out;
  1621.         }
  1622.         err = do_download(conn, g.gl_pathv[0], abs_dst, pflag);
  1623.         goto out;
  1624.     }
  1625.  
  1626.     /* Multiple matches, dst may be directory or unspecified */
  1627.     if (dst && !is_dir(dst)) {
  1628.         error("Multiple files match, but \"%s\" is not a directory",
  1629.             dst);
  1630.         err = -1;
  1631.         goto out;
  1632.     }
  1633.  
  1634.     for (i = 0; g.gl_pathv[i]; i++) {
  1635.         if (infer_path(g.gl_pathv[i], &tmp)) {
  1636.             err = -1;
  1637.             goto out;
  1638.         }
  1639.         if (dst) {
  1640.             abs_dst = path_append(dst, tmp);
  1641.             xfree(tmp);
  1642.         } else
  1643.             abs_dst = tmp;
  1644.  
  1645.         printf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst);
  1646.         if (do_download(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
  1647.             err = -1;
  1648.         xfree(abs_dst);
  1649.         abs_dst = NULL;
  1650.     }
  1651.  
  1652. out:
  1653.     xfree(abs_src);
  1654.     if (abs_dst)
  1655.         xfree(abs_dst);
  1656.     globfree(&g);
  1657.     return(err);
  1658. }
  1659.  
  1660. static int
  1661. process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag)
  1662. {
  1663.     char *tmp_dst = NULL;
  1664.     char *abs_dst = NULL;
  1665.     char *tmp;
  1666.     glob_t g;
  1667.     int err = 0;
  1668.     int i;
  1669.  
  1670.     if (dst) {
  1671.         tmp_dst = xstrdup(dst);
  1672.         tmp_dst = make_absolute(tmp_dst, pwd);
  1673.     }
  1674.  
  1675.     memset(&g, 0, sizeof(g));
  1676.     debug3("Looking up %s", src);
  1677.     if (glob(src, 0, NULL, &g)) {
  1678.         error("File \"%s\" not found.", src);
  1679.         err = -1;
  1680.         goto out;
  1681.     }
  1682.  
  1683.     /* Only one match, dst may be file, directory or unspecified */
  1684.     if (g.gl_pathv[0] && g.gl_matchc == 1) {
  1685.         if (!is_reg(g.gl_pathv[0])) {
  1686.             error("Can't upload %s: not a regular file",
  1687.                 g.gl_pathv[0]);
  1688.             err = 1;
  1689.             goto out;
  1690.         }
  1691.         if (tmp_dst) {
  1692.             /* If directory specified, append filename */
  1693.             if (remote_is_dir(conn, tmp_dst)) {
  1694.                 if (infer_path(g.gl_pathv[0], &tmp)) {
  1695.                     err = 1;
  1696.                     goto out;
  1697.                 }
  1698.                 abs_dst = path_append(tmp_dst, tmp);
  1699.                 xfree(tmp);
  1700.             } else
  1701.                 abs_dst = xstrdup(tmp_dst);
  1702.         } else {
  1703.             if (infer_path(g.gl_pathv[0], &abs_dst)) {
  1704.                 err = -1;
  1705.                 goto out;
  1706.             }
  1707.             abs_dst = make_absolute(abs_dst, pwd);
  1708.         }
  1709.         err = do_upload(conn, g.gl_pathv[0], abs_dst, pflag);
  1710.         goto out;
  1711.     }
  1712.  
  1713.     /* Multiple matches, dst may be directory or unspecified */
  1714.     if (tmp_dst && !remote_is_dir(conn, tmp_dst)) {
  1715.         error("Multiple files match, but \"%s\" is not a directory",
  1716.             tmp_dst);
  1717.         err = -1;
  1718.         goto out;
  1719.     }
  1720.  
  1721.     for (i = 0; g.gl_pathv[i]; i++) {
  1722.         if (!is_reg(g.gl_pathv[i])) {
  1723.             error("skipping non-regular file %s", 
  1724.                 g.gl_pathv[i]);
  1725.             continue;
  1726.         }
  1727.         if (infer_path(g.gl_pathv[i], &tmp)) {
  1728.             err = -1;
  1729.             goto out;
  1730.         }
  1731.         if (tmp_dst) {
  1732.             abs_dst = path_append(tmp_dst, tmp);
  1733.             xfree(tmp);
  1734.         } else
  1735.             abs_dst = make_absolute(tmp, pwd);
  1736.  
  1737.         printf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst);
  1738.         if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag) == -1)
  1739.             err = -1;
  1740.     }
  1741.  
  1742. out:
  1743.     if (abs_dst)
  1744.         xfree(abs_dst);
  1745.     if (tmp_dst)
  1746.         xfree(tmp_dst);
  1747.     return(err);
  1748. }
  1749.  
  1750. static int
  1751. sdirent_comp(const void *aa, const void *bb)
  1752. {
  1753.     SFTP_DIRENT *a = *(SFTP_DIRENT **)aa;
  1754.     SFTP_DIRENT *b = *(SFTP_DIRENT **)bb;
  1755.  
  1756.     return (strcmp(a->filename, b->filename));
  1757. }
  1758.  
  1759. /* sftp ls.1 replacement for directories */
  1760. static int
  1761. do_ls_dir(struct sftp_conn *conn, char *path, char *strip_path, int lflag)
  1762. {
  1763.     int n;
  1764.     SFTP_DIRENT **d;
  1765.  
  1766.     if ((n = do_readdir(conn, path, &d)) != 0)
  1767.         return (n);
  1768.  
  1769.     /* Count entries for sort */
  1770.     for (n = 0; d[n] != NULL; n++)
  1771.         ;
  1772.  
  1773.     qsort(d, n, sizeof(*d), sdirent_comp);
  1774.  
  1775.     for (n = 0; d[n] != NULL; n++) {
  1776.         char *tmp, *fname;
  1777.  
  1778.         tmp = path_append(path, d[n]->filename);
  1779.         fname = path_strip(tmp, strip_path);
  1780.         xfree(tmp);
  1781.  
  1782.         if (lflag) {
  1783.             char *lname;
  1784.             struct _stat sb;
  1785.  
  1786.             memset(&sb, 0, sizeof(sb));
  1787.             attrib_to_stat(&d[n]->a, &sb);
  1788.             lname = ls_file(fname, &sb, 1);
  1789.             printf("%s\n", lname);
  1790.             xfree(lname);
  1791.         } else {
  1792.             /* XXX - multicolumn display would be nice here */
  1793.             printf("%s\n", fname);
  1794.         }
  1795.  
  1796.         xfree(fname);
  1797.     }
  1798.  
  1799.     free_sftp_dirents(d);
  1800.     return (0);
  1801. }
  1802.  
  1803. /* sftp ls.1 replacement which handles path globs */
  1804. static int
  1805. do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path,
  1806.     int lflag)
  1807. {
  1808.     glob_t g;
  1809.     int i;
  1810.     Attrib *a;
  1811.     struct _stat sb;
  1812.  
  1813.     memset(&g, 0, sizeof(g));
  1814.  
  1815.     if (remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE,
  1816.         NULL, &g)) {
  1817.         error("Can't ls: \"%s\" not found", path);
  1818.         return (-1);
  1819.     }
  1820.  
  1821.     /*
  1822.      * If the glob returns a single match, which is the same as the
  1823.      * input glob, and it is a directory, then just list its contents
  1824.      */
  1825.     if (g.gl_pathc == 1 &&
  1826.         strncmp(path, g.gl_pathv[0], strlen(g.gl_pathv[0]) - 1) == 0) {
  1827.         if ((a = do_lstat(conn, path, 1)) == NULL) {
  1828.             globfree(&g);
  1829.                 return (-1);
  1830.         }
  1831.         if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
  1832.             S_ISDIR(a->perm)) {
  1833.             globfree(&g);
  1834.             return (do_ls_dir(conn, path, strip_path, lflag));
  1835.         }
  1836.     }
  1837.  
  1838.     for (i = 0; g.gl_pathv[i]; i++) {
  1839.         char *fname, *lname;
  1840.  
  1841.         fname = path_strip(g.gl_pathv[i], strip_path);
  1842.  
  1843.         if (lflag) {
  1844.             /*
  1845.              * XXX: this is slow - 1 roundtrip per path
  1846.              * A solution to this is to fork glob() and
  1847.              * build a sftp specific version which keeps the
  1848.              * attribs (which currently get thrown away)
  1849.              * that the server returns as well as the filenames.
  1850.              */
  1851.             memset(&sb, 0, sizeof(sb));
  1852.             a = do_lstat(conn, g.gl_pathv[i], 1);
  1853.             if (a != NULL)
  1854.                 attrib_to_stat(a, &sb);
  1855.             lname = ls_file(fname, &sb, 1);
  1856.             printf("%s\n", lname);
  1857.             xfree(lname);
  1858.         } else {
  1859.             /* XXX - multicolumn display would be nice here */
  1860.             printf("%s\n", fname);
  1861.         }
  1862.         xfree(fname);
  1863.     }
  1864.  
  1865.     if (g.gl_pathc)
  1866.         globfree(&g);
  1867.  
  1868.     return (0);
  1869. }
  1870.  
  1871. static int
  1872. parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
  1873.     unsigned long *n_arg, char **path1, char **path2)
  1874. {
  1875.     const char *cmd, *cp = *cpp;
  1876.     char *cp2;
  1877.     int base = 0;
  1878.     long l;
  1879.     int i, cmdnum;
  1880.  
  1881.     /* Skip leading whitespace */
  1882.     cp = cp + strspn(cp, WHITESPACE);
  1883.  
  1884.     /* Ignore blank lines and lines which begin with comment '#' char */
  1885.     if (*cp == '\0' || *cp == '#')
  1886.         return (0);
  1887.  
  1888.     /* Check for leading '-' (disable error processing) */
  1889.     *iflag = 0;
  1890.     if (*cp == '-') {
  1891.         *iflag = 1;
  1892.         cp++;
  1893.     }
  1894.         
  1895.     /* Figure out which command we have */
  1896.     for (i = 0; cmds[i].c; i++) {
  1897.         int cmdlen = strlen(cmds[i].c);
  1898.  
  1899.         /* Check for command followed by whitespace */
  1900.         if (!ckstrcmp((char *)cp, (char *)cmds[i].c, cmdlen, 0) &&
  1901.             strchr(WHITESPACE, cp[cmdlen])) {
  1902.             cp += cmdlen;
  1903.             cp = cp + strspn(cp, WHITESPACE);
  1904.             break;
  1905.         }
  1906.     }
  1907.     cmdnum = cmds[i].n;
  1908.     cmd = cmds[i].c;
  1909.  
  1910.     if (cmdnum == -1) {
  1911.         error("Invalid command.");
  1912.         return (-1);
  1913.     }
  1914.  
  1915.     /* Get arguments and parse flags */
  1916.     *lflag = *pflag = *n_arg = 0;
  1917.     *path1 = *path2 = NULL;
  1918.     switch (cmdnum) {
  1919.     case I_GET:
  1920.     case I_PUT:
  1921.         if (parse_getput_flags(&cp, pflag))
  1922.             return(-1);
  1923.         /* Get first pathname (mandatory) */
  1924.         if (get_pathname(&cp, path1))
  1925.             return(-1);
  1926.         if (*path1 == NULL) {
  1927.             error("You must specify at least one path after a "
  1928.                 "%s command.", cmd);
  1929.             return(-1);
  1930.         }
  1931.         /* Try to get second pathname (optional) */
  1932.         if (get_pathname(&cp, path2))
  1933.             return(-1);
  1934.         break;
  1935.     case I_RENAME:
  1936.     case I_SYMLINK:
  1937.         if (get_pathname(&cp, path1))
  1938.             return(-1);
  1939.         if (get_pathname(&cp, path2))
  1940.             return(-1);
  1941.         if (!*path1 || !*path2) {
  1942.             error("You must specify two paths after a %s "
  1943.                 "command.", cmd);
  1944.             return(-1);
  1945.         }
  1946.         break;
  1947.     case I_RM:
  1948.     case I_MKDIR:
  1949.     case I_RMDIR:
  1950.     case I_CHDIR:
  1951.     case I_LCHDIR:
  1952.     case I_LMKDIR:
  1953.         /* Get pathname (mandatory) */
  1954.         if (get_pathname(&cp, path1))
  1955.             return(-1);
  1956.         if (*path1 == NULL) {
  1957.             error("You must specify a path after a %s command.",
  1958.                 cmd);
  1959.             return(-1);
  1960.         }
  1961.         break;
  1962.     case I_LS:
  1963.         if (parse_ls_flags(&cp, lflag))
  1964.             return(-1);
  1965.         /* Path is optional */
  1966.         if (get_pathname(&cp, path1))
  1967.             return(-1);
  1968.         break;
  1969.     case I_LLS:
  1970.         /* Uses the rest of the line */
  1971.         break;
  1972.     case I_LUMASK:
  1973.         base = 8;
  1974.     case I_CHMOD:
  1975.         base = 8;
  1976.     case I_CHOWN:
  1977.     case I_CHGRP:
  1978.         /* Get numeric arg (mandatory) */
  1979.         l = strtol(cp, &cp2, base);
  1980.         if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
  1981.             errno == ERANGE) || l < 0) {
  1982.             error("You must supply a numeric argument "
  1983.                 "to the %s command.", cmd);
  1984.             return(-1);
  1985.         }
  1986.         cp = cp2;
  1987.         *n_arg = l;
  1988.         if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
  1989.             break;
  1990.         if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
  1991.             error("You must supply a numeric argument "
  1992.                 "to the %s command.", cmd);
  1993.             return(-1);
  1994.         }
  1995.         cp += strspn(cp, WHITESPACE);
  1996.  
  1997.         /* Get pathname (mandatory) */
  1998.         if (get_pathname(&cp, path1))
  1999.             return(-1);
  2000.         if (*path1 == NULL) {
  2001.             error("You must specify a path after a %s command.",
  2002.                 cmd);
  2003.             return(-1);
  2004.         }
  2005.         break;
  2006.     case I_QUIT:
  2007.     case I_PWD:
  2008.     case I_LPWD:
  2009.     case I_HELP:
  2010.     case I_VERSION:
  2011.     case I_PROGRESS:
  2012.         break;
  2013.     default:
  2014.         fatal("Command not implemented");
  2015.         return(-1);
  2016.     }
  2017.  
  2018.     *cpp = cp;
  2019.     return(cmdnum);
  2020. }
  2021.  
  2022. static int
  2023. parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
  2024.     int err_abort)
  2025. {
  2026.     char *path1, *path2, *tmp;
  2027.     int pflag, lflag, iflag, cmdnum, i;
  2028.     unsigned long n_arg;
  2029.     Attrib a, *aa;
  2030.     char path_buf[MAXPATHLEN];
  2031.     int err = 0;
  2032.     glob_t g;
  2033.  
  2034.     path1 = path2 = NULL;
  2035.     cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg,
  2036.         &path1, &path2);
  2037.  
  2038.     if (iflag != 0)
  2039.         err_abort = 0;
  2040.  
  2041.     memset(&g, 0, sizeof(g));
  2042.  
  2043.     /* Perform command */
  2044.     switch (cmdnum) {
  2045.     case 0:
  2046.         /* Blank line */
  2047.         break;
  2048.     case -1:
  2049.         /* Unrecognized command */
  2050.         err = -1;
  2051.         break;
  2052.     case I_GET:
  2053.         err = process_get(conn, path1, path2, *pwd, pflag);
  2054.         break;
  2055.     case I_PUT:
  2056.         err = process_put(conn, path1, path2, *pwd, pflag);
  2057.         break;
  2058.     case I_RENAME:
  2059.         path1 = make_absolute(path1, *pwd);
  2060.         path2 = make_absolute(path2, *pwd);
  2061.         err = do_rename(conn, path1, path2);
  2062.         break;
  2063.     case I_SYMLINK:
  2064.         path2 = make_absolute(path2, *pwd);
  2065.         err = do_symlink(conn, path1, path2);
  2066.         break;
  2067.     case I_RM:
  2068.         path1 = make_absolute(path1, *pwd);
  2069.         remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
  2070.         for (i = 0; g.gl_pathv[i]; i++) {
  2071.             printf("Removing %s\n", g.gl_pathv[i]);
  2072.             err = do_rm(conn, g.gl_pathv[i]);
  2073.             if (err != 0 && err_abort)
  2074.                 break;
  2075.         }
  2076.         break;
  2077.     case I_MKDIR:
  2078.         path1 = make_absolute(path1, *pwd);
  2079.         attrib_clear(&a);
  2080.         a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
  2081.         a.perm = 0777;
  2082.         err = do_mkdir(conn, path1, &a);
  2083.         break;
  2084.     case I_RMDIR:
  2085.         path1 = make_absolute(path1, *pwd);
  2086.         err = do_rmdir(conn, path1);
  2087.         break;
  2088.     case I_CHDIR:
  2089.         path1 = make_absolute(path1, *pwd);
  2090.         if ((tmp = do_realpath(conn, path1)) == NULL) {
  2091.             err = 1;
  2092.             break;
  2093.         }
  2094.         if ((aa = do_stat(conn, tmp, 0)) == NULL) {
  2095.             xfree(tmp);
  2096.             err = 1;
  2097.             break;
  2098.         }
  2099.         if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) {
  2100.             error("Can't change directory: Can't check target");
  2101.             xfree(tmp);
  2102.             err = 1;
  2103.             break;
  2104.         }
  2105.         if (!S_ISDIR(aa->perm)) {
  2106.             error("Can't change directory: \"%s\" is not "
  2107.                 "a directory", tmp);
  2108.             xfree(tmp);
  2109.             err = 1;
  2110.             break;
  2111.         }
  2112.         xfree(*pwd);
  2113.         *pwd = tmp;
  2114.         break;
  2115.     case I_LS:
  2116.         if (!path1) {
  2117.             do_globbed_ls(conn, *pwd, *pwd, lflag);
  2118.             break;
  2119.         }
  2120.  
  2121.         /* Strip pwd off beginning of non-absolute paths */
  2122.         tmp = NULL;
  2123.         if (*path1 != '/')
  2124.             tmp = *pwd;
  2125.  
  2126.         path1 = make_absolute(path1, *pwd);
  2127.         err = do_globbed_ls(conn, path1, tmp, lflag);
  2128.         break;
  2129.     case I_LCHDIR:
  2130.         if (chdir(path1) == -1) {
  2131.             error("Couldn't change local directory to "
  2132.                 "\"%s\": %s", path1, strerror(errno));
  2133.             err = 1;
  2134.         }
  2135.         break;
  2136.     case I_LMKDIR:
  2137.         if (mkdir(path1, 0777) == -1) {
  2138.             error("Couldn't create local directory "
  2139.                 "\"%s\": %s", path1, strerror(errno));
  2140.             err = 1;
  2141.         }
  2142.         break;
  2143.     case I_LLS:
  2144.         local_do_ls(cmd);
  2145.         break;
  2146.     case I_LUMASK:
  2147.         umask(n_arg);
  2148.         printf("Local umask: %03lo\n", n_arg);
  2149.         break;
  2150.     case I_CHMOD:
  2151.         path1 = make_absolute(path1, *pwd);
  2152.         attrib_clear(&a);
  2153.         a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
  2154.         a.perm = n_arg;
  2155.         remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
  2156.         for (i = 0; g.gl_pathv[i]; i++) {
  2157.             printf("Changing mode on %s\n", g.gl_pathv[i]);
  2158.             err = do_setstat(conn, g.gl_pathv[i], &a);
  2159.             if (err != 0 && err_abort)
  2160.                 break;
  2161.         }
  2162.         break;
  2163.     case I_CHOWN:
  2164.     case I_CHGRP:
  2165.         path1 = make_absolute(path1, *pwd);
  2166.         remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
  2167.         for (i = 0; g.gl_pathv[i]; i++) {
  2168.             if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
  2169.                 if (err != 0 && err_abort)
  2170.                     break;
  2171.                 else
  2172.                     continue;
  2173.             }
  2174.             if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) {
  2175.                 error("Can't get current ownership of "
  2176.                     "remote file \"%s\"", g.gl_pathv[i]);
  2177.                 if (err != 0 && err_abort)
  2178.                     break;
  2179.                 else
  2180.                     continue;
  2181.             }
  2182.             aa->flags &= SSH2_FILEXFER_ATTR_UIDGID;
  2183.             if (cmdnum == I_CHOWN) {
  2184.                 printf("Changing owner on %s\n", g.gl_pathv[i]);
  2185.                 aa->uid = n_arg;
  2186.             } else {
  2187.                 printf("Changing group on %s\n", g.gl_pathv[i]);
  2188.                 aa->gid = n_arg;
  2189.             }
  2190.             err = do_setstat(conn, g.gl_pathv[i], aa);
  2191.             if (err != 0 && err_abort)
  2192.                 break;
  2193.         }
  2194.         break;
  2195.     case I_PWD:
  2196.         printf("Remote working directory: %s\n", *pwd);
  2197.         break;
  2198.     case I_LPWD:
  2199.         if (!getcwd(path_buf, sizeof(path_buf))) {
  2200.             error("Couldn't get local cwd: %s", strerror(errno));
  2201.             err = -1;
  2202.             break;
  2203.         }
  2204.         printf("Local working directory: %s\n", path_buf);
  2205.         break;
  2206.     case I_QUIT:
  2207.         /* Processed below */
  2208.         break;
  2209.     case I_HELP:
  2210.         help();
  2211.         break;
  2212.     case I_VERSION:
  2213.         printf("SFTP protocol version %u\n", sftp_proto_version(conn));
  2214.         break;
  2215.     case I_PROGRESS:
  2216.         showprogress = !showprogress;
  2217.         if (showprogress)
  2218.             printf("Progress meter enabled\n");
  2219.         else
  2220.             printf("Progress meter disabled\n");
  2221.         break;
  2222.     default:
  2223.         fatal("%d is not implemented", cmdnum);
  2224.         return(-1);
  2225.     }
  2226.  
  2227.     if (g.gl_pathc)
  2228.         globfree(&g);
  2229.     if (path1)
  2230.         xfree(path1);
  2231.     if (path2)
  2232.         xfree(path2);
  2233.  
  2234.     /* If an unignored error occurs in batch mode we should abort. */
  2235.     if (err_abort && err != 0)
  2236.         return (-1);
  2237.     else if (cmdnum == I_QUIT)
  2238.         return (1);
  2239.  
  2240.     return (0);
  2241. }
  2242.  
  2243. static     char *pwd = NULL;
  2244. static  struct sftp_conn *conn = NULL;
  2245.  
  2246. int
  2247. sftp_do_init(void)
  2248. {
  2249.     if ( conn ) {
  2250.         xfree(conn);
  2251.         conn = NULL;
  2252.     }
  2253.     if ( pwd ) {
  2254.         xfree(pwd);
  2255.         pwd = NULL;
  2256.     }
  2257.  
  2258.     conn = do_init(copy_buffer_len, num_requests);
  2259.     if (conn == NULL) {
  2260.         fatal("Couldn't initialise connection to server");
  2261.         return 0;
  2262.     }
  2263.     pwd = do_realpath(conn, ".");
  2264.     if (pwd == NULL) {
  2265.         fatal("Need cwd");
  2266.         return 0;
  2267.     }
  2268.  
  2269.     return 1;
  2270. }
  2271.  
  2272. int
  2273. sftp_do_cmd(int kval, char * args)
  2274. {
  2275.     char cmd[2048]="";
  2276.     int err;
  2277.  
  2278.     switch ( kval ) {
  2279.     case SFTP_CD:
  2280.         ckstrncpy(cmd,"cd ",sizeof(cmd));
  2281.         break;
  2282.     case SFTP_CHGRP:
  2283.         ckstrncpy(cmd,"chgrp ",sizeof(cmd));
  2284.         break;
  2285.     case SFTP_CHMOD:
  2286.         ckstrncpy(cmd,"chmod ",sizeof(cmd));
  2287.         break;
  2288.     case SFTP_CHOWN:
  2289.         ckstrncpy(cmd,"chown ",sizeof(cmd));
  2290.         break;
  2291.     case SFTP_RM:
  2292.         ckstrncpy(cmd,"rm ",sizeof(cmd));
  2293.         break;
  2294.     case SFTP_DIR:
  2295.         ckstrncpy(cmd,"ls ",sizeof(cmd));
  2296.         break;
  2297.     case SFTP_GET:
  2298.         ckstrncpy(cmd,"get ",sizeof(cmd));
  2299.         break;
  2300.     case SFTP_MKDIR:
  2301.         ckstrncpy(cmd,"mkdir ",sizeof(cmd));
  2302.         break;
  2303.     case SFTP_PUT:
  2304.         ckstrncpy(cmd,"put ",sizeof(cmd));
  2305.         break;
  2306.     case SFTP_PWD:
  2307.         ckstrncpy(cmd,"pwd ",sizeof(cmd));
  2308.         break;
  2309.     case SFTP_REN:
  2310.         ckstrncpy(cmd,"rename ",sizeof(cmd));
  2311.         break;
  2312.     case SFTP_RMDIR:
  2313.         ckstrncpy(cmd,"rmdir ",sizeof(cmd));
  2314.         break;
  2315.     case SFTP_LINK:
  2316.         ckstrncpy(cmd,"symlink ",sizeof(cmd));
  2317.         break;
  2318.     case SFTP_VER:
  2319.         ckstrncpy(cmd,"version ",sizeof(cmd));
  2320.         break;
  2321.     }
  2322.  
  2323.     ckstrncat(cmd,args,sizeof(cmd)); 
  2324.  
  2325.     err = parse_dispatch_command(conn, cmd, &pwd, 0);
  2326.     return (err >= 0 ? 1 : 0);
  2327. }
  2328.  
  2329. /* Clear contents of attributes structure */
  2330. void
  2331. attrib_clear(Attrib *a)
  2332. {
  2333.     a->flags = 0;
  2334.     a->size = 0;
  2335.     a->uid = 0;
  2336.     a->gid = 0;
  2337.     a->perm = 0;
  2338.     a->atime = 0;
  2339.     a->mtime = 0;
  2340. }
  2341.  
  2342. /* Convert from struct _stat to filexfer attribs */
  2343. void
  2344. stat_to_attrib(struct _stat *st, Attrib *a)
  2345. {
  2346.     attrib_clear(a);
  2347.     a->flags = 0;
  2348.     a->flags |= SSH2_FILEXFER_ATTR_SIZE;
  2349.     a->size = st->st_size;
  2350.     a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
  2351.     a->uid = st->st_uid;
  2352.     a->gid = st->st_gid;
  2353.     a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
  2354.     a->perm = st->st_mode;
  2355.     a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
  2356.     a->atime = st->st_atime;
  2357.     a->mtime = st->st_mtime;
  2358. }
  2359.  
  2360. /* Convert from filexfer attribs to struct _stat */
  2361. void
  2362. attrib_to_stat(Attrib *a, struct _stat *st)
  2363. {
  2364.     memset(st, 0, sizeof(*st));
  2365.  
  2366.     if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
  2367.         st->st_size = a->size;
  2368.     if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
  2369.         st->st_uid = a->uid;
  2370.         st->st_gid = a->gid;
  2371.     }
  2372.     if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
  2373.         st->st_mode = a->perm;
  2374.     if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
  2375.         st->st_atime = a->atime;
  2376.         st->st_mtime = a->mtime;
  2377.     }
  2378. }
  2379.  
  2380. /* Decode attributes in buffer */
  2381. Attrib *
  2382. decode_attrib(Buffer *b)
  2383. {
  2384.     static Attrib a;
  2385.  
  2386.     attrib_clear(&a);
  2387.     a.flags = buffer_get_int(b);
  2388.     if (a.flags & SSH2_FILEXFER_ATTR_SIZE)
  2389.         a.size = buffer_get_int64(b);
  2390.     if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
  2391.         a.uid = buffer_get_int(b);
  2392.         a.gid = buffer_get_int(b);
  2393.     }
  2394.     if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
  2395.         a.perm = buffer_get_int(b);
  2396.     if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
  2397.         a.atime = buffer_get_int(b);
  2398.         a.mtime = buffer_get_int(b);
  2399.     }
  2400.     /* vendor-specific extensions */
  2401.     if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
  2402.         char *type, *data;
  2403.         int i, count;
  2404.  
  2405.         count = buffer_get_int(b);
  2406.         for (i = 0; i < count; i++) {
  2407.             type = buffer_get_string(b, NULL);
  2408.             data = buffer_get_string(b, NULL);
  2409.             debug3("Got file attribute \"%s\"", type);
  2410.             xfree(type);
  2411.             xfree(data);
  2412.         }
  2413.     }
  2414.     return &a;
  2415. }
  2416.  
  2417. /* Encode attributes to buffer */
  2418. void
  2419. encode_attrib(Buffer *b, Attrib *a)
  2420. {
  2421.     buffer_put_int(b, a->flags);
  2422.     if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
  2423.         buffer_put_int64(b, a->size);
  2424.     if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
  2425.         buffer_put_int(b, a->uid);
  2426.         buffer_put_int(b, a->gid);
  2427.     }
  2428.     if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
  2429.         buffer_put_int(b, a->perm);
  2430.     if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
  2431.         buffer_put_int(b, a->atime);
  2432.         buffer_put_int(b, a->mtime);
  2433.     }
  2434. }
  2435.  
  2436. /* Convert from SSH2_FX_ status to text error message */
  2437. const char *
  2438. fx2txt(int status)
  2439. {
  2440.     switch (status) {
  2441.     case SSH2_FX_OK:
  2442.         return("No error");
  2443.     case SSH2_FX_EOF:
  2444.         return("End of file");
  2445.     case SSH2_FX_NO_SUCH_FILE:
  2446.         return("No such file or directory");
  2447.     case SSH2_FX_PERMISSION_DENIED:
  2448.         return("Permission denied");
  2449.     case SSH2_FX_FAILURE:
  2450.         return("Failure");
  2451.     case SSH2_FX_BAD_MESSAGE:
  2452.         return("Bad message");
  2453.     case SSH2_FX_NO_CONNECTION:
  2454.         return("No connection");
  2455.     case SSH2_FX_CONNECTION_LOST:
  2456.         return("Connection lost");
  2457.     case SSH2_FX_OP_UNSUPPORTED:
  2458.         return("Operation unsupported");
  2459.     default:
  2460.         return("Unknown status");
  2461.     }
  2462.     /* NOTREACHED */
  2463. }
  2464.  
  2465. /*
  2466.  * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
  2467.  */
  2468. char *
  2469. ls_file(char *name, struct _stat *st, int remote)
  2470. {
  2471.     int ulen, glen, sz = 0;
  2472.     struct passwd *pw;
  2473.     struct group *gr;
  2474.     struct tm *ltime = localtime(&st->st_mtime);
  2475.     char *user, *group;
  2476.     char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
  2477.  
  2478.     strmode(st->st_mode, mode);
  2479.     if (!remote && (pw = getpwuid(st->st_uid)) != NULL) {
  2480.         user = pw->pw_name;
  2481.     } else {
  2482.         snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid);
  2483.         user = ubuf;
  2484.     }
  2485.     if (!remote && (gr = getgrgid(st->st_gid)) != NULL) {
  2486.         group = gr->gr_name;
  2487.     } else {
  2488.         snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid);
  2489.         group = gbuf;
  2490.     }
  2491.     if (ltime != NULL) {
  2492.         if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
  2493.             sz = strftime(tbuf, sizeof tbuf, "%b %d %H:%M", ltime);
  2494.         else
  2495.             sz = strftime(tbuf, sizeof tbuf, "%b %d  %Y", ltime);
  2496.     }
  2497.     if (sz == 0)
  2498.         tbuf[0] = '\0';
  2499.     ulen = MAX(strlen(user), 8);
  2500.     glen = MAX(strlen(group), 8);
  2501.     snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8lu %s %s", mode,
  2502.         st->st_nlink, ulen, user, glen, group,
  2503.         st->st_size, tbuf, name);
  2504.     return xstrdup(buf);
  2505. }
  2506.  
  2507. struct SFTP_OPENDIR {
  2508.     SFTP_DIRENT **dir;
  2509.     int offset;
  2510. };
  2511.  
  2512. static struct {
  2513.     struct sftp_conn *conn;
  2514. } cur;
  2515.  
  2516. static void *
  2517. fudge_opendir(const char *path)
  2518. {
  2519.     struct SFTP_OPENDIR *r;
  2520.  
  2521.     r = xmalloc(sizeof(*r));
  2522.  
  2523.     if (do_readdir(cur.conn, (char *)path, &r->dir)) {
  2524.         xfree(r);
  2525.         return(NULL);
  2526.     }
  2527.  
  2528.     r->offset = 0;
  2529.  
  2530.     return((void *)r);
  2531. }
  2532.  
  2533. static struct dirent *
  2534. fudge_readdir(struct SFTP_OPENDIR *od)
  2535. {
  2536.     /* Solaris needs sizeof(dirent) + path length (see below) */
  2537.     static char buf[sizeof(struct dirent) + MAXPATHLEN];
  2538.     struct dirent *ret = (struct dirent *)buf;
  2539.     
  2540.     if (od->dir[od->offset] == NULL)
  2541.         return(NULL);
  2542.  
  2543.     memset(buf, 0, sizeof(buf));
  2544.  
  2545.     /*
  2546.      * Solaris defines dirent->d_name as a one byte array and expects
  2547.      * you to hack around it.
  2548.      */
  2549. #ifdef BROKEN_ONE_BYTE_DIRENT_D_NAME
  2550.     strlcpy(ret->d_name, od->dir[od->offset++]->filename, MAXPATHLEN);
  2551. #else
  2552.     strlcpy(ret->d_name, od->dir[od->offset++]->filename,
  2553.         sizeof(ret->d_name));
  2554. #endif
  2555.     return(ret);
  2556. }
  2557.  
  2558. static void
  2559. fudge_closedir(struct SFTP_OPENDIR *od)
  2560. {
  2561.     free_sftp_dirents(od->dir);
  2562.     xfree(od);
  2563. }
  2564.  
  2565. static int
  2566. fudge_lstat(const char *path, struct _stat *st)
  2567. {
  2568.     Attrib *a;
  2569.  
  2570.     if (!(a = do_lstat(cur.conn, (char *)path, 0)))
  2571.         return(-1);
  2572.  
  2573.     attrib_to_stat(a, st);
  2574.  
  2575.     return(0);
  2576. }
  2577.  
  2578. static int
  2579. fudge_stat(const char *path, struct _stat *st)
  2580. {
  2581.     Attrib *a;
  2582.  
  2583.     if (!(a = do_stat(cur.conn, (char *)path, 0)))
  2584.         return(-1);
  2585.  
  2586.     attrib_to_stat(a, st);
  2587.  
  2588.     return(0);
  2589. }
  2590.  
  2591. int
  2592. remote_glob(struct sftp_conn *conn, const char *pattern, int flags,
  2593.     int (*errfunc)(const char *, int), glob_t *pglob)
  2594. {
  2595.     pglob->gl_opendir = fudge_opendir;
  2596.     pglob->gl_readdir = (struct dirent *(*)(void *))fudge_readdir;
  2597.     pglob->gl_closedir = (void (*)(void *))fudge_closedir;
  2598.     pglob->gl_lstat = fudge_lstat;
  2599.     pglob->gl_stat = fudge_stat;
  2600.  
  2601.     memset(&cur, 0, sizeof(cur));
  2602.     cur.conn = conn;
  2603.  
  2604.     return(glob(pattern, flags | GLOB_ALTDIRFUNC, errfunc, pglob));
  2605. }
  2606. #endif /* SFTP_BUILTIN */