diff options
Diffstat (limited to 'sftp.c')
-rw-r--r-- | sftp.c | 576 |
1 files changed, 395 insertions, 181 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sftp.c,v 1.96 2007/01/03 04:09:15 stevesk Exp $ */ | 1 | /* $OpenBSD: sftp.c,v 1.103 2008/07/13 22:16:03 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> | 3 | * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> |
4 | * | 4 | * |
@@ -25,7 +25,11 @@ | |||
25 | #include <sys/param.h> | 25 | #include <sys/param.h> |
26 | #include <sys/socket.h> | 26 | #include <sys/socket.h> |
27 | #include <sys/wait.h> | 27 | #include <sys/wait.h> |
28 | #ifdef HAVE_SYS_STATVFS_H | ||
29 | #include <sys/statvfs.h> | ||
30 | #endif | ||
28 | 31 | ||
32 | #include <ctype.h> | ||
29 | #include <errno.h> | 33 | #include <errno.h> |
30 | 34 | ||
31 | #ifdef HAVE_PATHS_H | 35 | #ifdef HAVE_PATHS_H |
@@ -43,6 +47,14 @@ typedef void EditLine; | |||
43 | #include <unistd.h> | 47 | #include <unistd.h> |
44 | #include <stdarg.h> | 48 | #include <stdarg.h> |
45 | 49 | ||
50 | #ifdef HAVE_UTIL_H | ||
51 | # include <util.h> | ||
52 | #endif | ||
53 | |||
54 | #ifdef HAVE_LIBUTIL_H | ||
55 | # include <libutil.h> | ||
56 | #endif | ||
57 | |||
46 | #include "xmalloc.h" | 58 | #include "xmalloc.h" |
47 | #include "log.h" | 59 | #include "log.h" |
48 | #include "pathnames.h" | 60 | #include "pathnames.h" |
@@ -63,7 +75,7 @@ int batchmode = 0; | |||
63 | size_t copy_buffer_len = 32768; | 75 | size_t copy_buffer_len = 32768; |
64 | 76 | ||
65 | /* Number of concurrent outstanding requests */ | 77 | /* Number of concurrent outstanding requests */ |
66 | size_t num_requests = 16; | 78 | size_t num_requests = 64; |
67 | 79 | ||
68 | /* PID of ssh transport process */ | 80 | /* PID of ssh transport process */ |
69 | static pid_t sshpid = -1; | 81 | static pid_t sshpid = -1; |
@@ -103,6 +115,7 @@ extern char *__progname; | |||
103 | #define I_CHGRP 2 | 115 | #define I_CHGRP 2 |
104 | #define I_CHMOD 3 | 116 | #define I_CHMOD 3 |
105 | #define I_CHOWN 4 | 117 | #define I_CHOWN 4 |
118 | #define I_DF 24 | ||
106 | #define I_GET 5 | 119 | #define I_GET 5 |
107 | #define I_HELP 6 | 120 | #define I_HELP 6 |
108 | #define I_LCHDIR 7 | 121 | #define I_LCHDIR 7 |
@@ -135,6 +148,7 @@ static const struct CMD cmds[] = { | |||
135 | { "chgrp", I_CHGRP }, | 148 | { "chgrp", I_CHGRP }, |
136 | { "chmod", I_CHMOD }, | 149 | { "chmod", I_CHMOD }, |
137 | { "chown", I_CHOWN }, | 150 | { "chown", I_CHOWN }, |
151 | { "df", I_DF }, | ||
138 | { "dir", I_LS }, | 152 | { "dir", I_LS }, |
139 | { "exit", I_QUIT }, | 153 | { "exit", I_QUIT }, |
140 | { "get", I_GET }, | 154 | { "get", I_GET }, |
@@ -199,6 +213,8 @@ help(void) | |||
199 | printf("chgrp grp path Change group of file 'path' to 'grp'\n"); | 213 | printf("chgrp grp path Change group of file 'path' to 'grp'\n"); |
200 | printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); | 214 | printf("chmod mode path Change permissions of file 'path' to 'mode'\n"); |
201 | printf("chown own path Change owner of file 'path' to 'own'\n"); | 215 | printf("chown own path Change owner of file 'path' to 'own'\n"); |
216 | printf("df [path] Display statistics for current directory or\n"); | ||
217 | printf(" filesystem containing 'path'\n"); | ||
202 | printf("help Display this help text\n"); | 218 | printf("help Display this help text\n"); |
203 | printf("get remote-path [local-path] Download file\n"); | 219 | printf("get remote-path [local-path] Download file\n"); |
204 | printf("lls [ls-options [path]] Display local directory listing\n"); | 220 | printf("lls [ls-options [path]] Display local directory listing\n"); |
@@ -346,144 +362,105 @@ infer_path(const char *p, char **ifp) | |||
346 | } | 362 | } |
347 | 363 | ||
348 | static int | 364 | static int |
349 | parse_getput_flags(const char **cpp, int *pflag) | 365 | parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag) |
350 | { | 366 | { |
351 | const char *cp = *cpp; | 367 | extern int opterr, optind, optopt, optreset; |
368 | int ch; | ||
352 | 369 | ||
353 | /* Check for flags */ | 370 | optind = optreset = 1; |
354 | if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) { | 371 | opterr = 0; |
355 | switch (cp[1]) { | 372 | |
373 | *pflag = 0; | ||
374 | while ((ch = getopt(argc, argv, "Pp")) != -1) { | ||
375 | switch (ch) { | ||
356 | case 'p': | 376 | case 'p': |
357 | case 'P': | 377 | case 'P': |
358 | *pflag = 1; | 378 | *pflag = 1; |
359 | break; | 379 | break; |
360 | default: | 380 | default: |
361 | error("Invalid flag -%c", cp[1]); | 381 | error("%s: Invalid flag -%c", cmd, optopt); |
362 | return(-1); | 382 | return -1; |
363 | } | 383 | } |
364 | cp += 2; | ||
365 | *cpp = cp + strspn(cp, WHITESPACE); | ||
366 | } | 384 | } |
367 | 385 | ||
368 | return(0); | 386 | return optind; |
369 | } | 387 | } |
370 | 388 | ||
371 | static int | 389 | static int |
372 | parse_ls_flags(const char **cpp, int *lflag) | 390 | parse_ls_flags(char **argv, int argc, int *lflag) |
373 | { | 391 | { |
374 | const char *cp = *cpp; | 392 | extern int opterr, optind, optopt, optreset; |
393 | int ch; | ||
375 | 394 | ||
376 | /* Defaults */ | 395 | optind = optreset = 1; |
377 | *lflag = LS_NAME_SORT; | 396 | opterr = 0; |
378 | 397 | ||
379 | /* Check for flags */ | 398 | *lflag = LS_NAME_SORT; |
380 | if (cp++[0] == '-') { | 399 | while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) { |
381 | for (; strchr(WHITESPACE, *cp) == NULL; cp++) { | 400 | switch (ch) { |
382 | switch (*cp) { | 401 | case '1': |
383 | case 'l': | 402 | *lflag &= ~VIEW_FLAGS; |
384 | *lflag &= ~VIEW_FLAGS; | 403 | *lflag |= LS_SHORT_VIEW; |
385 | *lflag |= LS_LONG_VIEW; | 404 | break; |
386 | break; | 405 | case 'S': |
387 | case '1': | 406 | *lflag &= ~SORT_FLAGS; |
388 | *lflag &= ~VIEW_FLAGS; | 407 | *lflag |= LS_SIZE_SORT; |
389 | *lflag |= LS_SHORT_VIEW; | 408 | break; |
390 | break; | 409 | case 'a': |
391 | case 'n': | 410 | *lflag |= LS_SHOW_ALL; |
392 | *lflag &= ~VIEW_FLAGS; | 411 | break; |
393 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; | 412 | case 'f': |
394 | break; | 413 | *lflag &= ~SORT_FLAGS; |
395 | case 'S': | 414 | break; |
396 | *lflag &= ~SORT_FLAGS; | 415 | case 'l': |
397 | *lflag |= LS_SIZE_SORT; | 416 | *lflag &= ~VIEW_FLAGS; |
398 | break; | 417 | *lflag |= LS_LONG_VIEW; |
399 | case 't': | 418 | break; |
400 | *lflag &= ~SORT_FLAGS; | 419 | case 'n': |
401 | *lflag |= LS_TIME_SORT; | 420 | *lflag &= ~VIEW_FLAGS; |
402 | break; | 421 | *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; |
403 | case 'r': | 422 | break; |
404 | *lflag |= LS_REVERSE_SORT; | 423 | case 'r': |
405 | break; | 424 | *lflag |= LS_REVERSE_SORT; |
406 | case 'f': | 425 | break; |
407 | *lflag &= ~SORT_FLAGS; | 426 | case 't': |
408 | break; | 427 | *lflag &= ~SORT_FLAGS; |
409 | case 'a': | 428 | *lflag |= LS_TIME_SORT; |
410 | *lflag |= LS_SHOW_ALL; | 429 | break; |
411 | break; | 430 | default: |
412 | default: | 431 | error("ls: Invalid flag -%c", optopt); |
413 | error("Invalid flag -%c", *cp); | 432 | return -1; |
414 | return(-1); | ||
415 | } | ||
416 | } | 433 | } |
417 | *cpp = cp + strspn(cp, WHITESPACE); | ||
418 | } | 434 | } |
419 | 435 | ||
420 | return(0); | 436 | return optind; |
421 | } | 437 | } |
422 | 438 | ||
423 | static int | 439 | static int |
424 | get_pathname(const char **cpp, char **path) | 440 | parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) |
425 | { | 441 | { |
426 | const char *cp = *cpp, *end; | 442 | extern int opterr, optind, optopt, optreset; |
427 | char quot; | 443 | int ch; |
428 | u_int i, j; | ||
429 | |||
430 | cp += strspn(cp, WHITESPACE); | ||
431 | if (!*cp) { | ||
432 | *cpp = cp; | ||
433 | *path = NULL; | ||
434 | return (0); | ||
435 | } | ||
436 | |||
437 | *path = xmalloc(strlen(cp) + 1); | ||
438 | |||
439 | /* Check for quoted filenames */ | ||
440 | if (*cp == '\"' || *cp == '\'') { | ||
441 | quot = *cp++; | ||
442 | 444 | ||
443 | /* Search for terminating quote, unescape some chars */ | 445 | optind = optreset = 1; |
444 | for (i = j = 0; i <= strlen(cp); i++) { | 446 | opterr = 0; |
445 | if (cp[i] == quot) { /* Found quote */ | ||
446 | i++; | ||
447 | (*path)[j] = '\0'; | ||
448 | break; | ||
449 | } | ||
450 | if (cp[i] == '\0') { /* End of string */ | ||
451 | error("Unterminated quote"); | ||
452 | goto fail; | ||
453 | } | ||
454 | if (cp[i] == '\\') { /* Escaped characters */ | ||
455 | i++; | ||
456 | if (cp[i] != '\'' && cp[i] != '\"' && | ||
457 | cp[i] != '\\') { | ||
458 | error("Bad escaped character '\\%c'", | ||
459 | cp[i]); | ||
460 | goto fail; | ||
461 | } | ||
462 | } | ||
463 | (*path)[j++] = cp[i]; | ||
464 | } | ||
465 | 447 | ||
466 | if (j == 0) { | 448 | *hflag = *iflag = 0; |
467 | error("Empty quotes"); | 449 | while ((ch = getopt(argc, argv, "hi")) != -1) { |
468 | goto fail; | 450 | switch (ch) { |
451 | case 'h': | ||
452 | *hflag = 1; | ||
453 | break; | ||
454 | case 'i': | ||
455 | *iflag = 1; | ||
456 | break; | ||
457 | default: | ||
458 | error("%s: Invalid flag -%c", cmd, optopt); | ||
459 | return -1; | ||
469 | } | 460 | } |
470 | *cpp = cp + i + strspn(cp + i, WHITESPACE); | ||
471 | } else { | ||
472 | /* Read to end of filename */ | ||
473 | end = strpbrk(cp, WHITESPACE); | ||
474 | if (end == NULL) | ||
475 | end = strchr(cp, '\0'); | ||
476 | *cpp = end + strspn(end, WHITESPACE); | ||
477 | |||
478 | memcpy(*path, cp, end - cp); | ||
479 | (*path)[end - cp] = '\0'; | ||
480 | } | 461 | } |
481 | return (0); | ||
482 | 462 | ||
483 | fail: | 463 | return optind; |
484 | xfree(*path); | ||
485 | *path = NULL; | ||
486 | return (-1); | ||
487 | } | 464 | } |
488 | 465 | ||
489 | static int | 466 | static int |
@@ -499,17 +476,6 @@ is_dir(char *path) | |||
499 | } | 476 | } |
500 | 477 | ||
501 | static int | 478 | static int |
502 | is_reg(char *path) | ||
503 | { | ||
504 | struct stat sb; | ||
505 | |||
506 | if (stat(path, &sb) == -1) | ||
507 | fatal("stat %s: %s", path, strerror(errno)); | ||
508 | |||
509 | return(S_ISREG(sb.st_mode)); | ||
510 | } | ||
511 | |||
512 | static int | ||
513 | remote_is_dir(struct sftp_conn *conn, char *path) | 479 | remote_is_dir(struct sftp_conn *conn, char *path) |
514 | { | 480 | { |
515 | Attrib *a; | 481 | Attrib *a; |
@@ -597,6 +563,7 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
597 | glob_t g; | 563 | glob_t g; |
598 | int err = 0; | 564 | int err = 0; |
599 | int i; | 565 | int i; |
566 | struct stat sb; | ||
600 | 567 | ||
601 | if (dst) { | 568 | if (dst) { |
602 | tmp_dst = xstrdup(dst); | 569 | tmp_dst = xstrdup(dst); |
@@ -605,7 +572,7 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
605 | 572 | ||
606 | memset(&g, 0, sizeof(g)); | 573 | memset(&g, 0, sizeof(g)); |
607 | debug3("Looking up %s", src); | 574 | debug3("Looking up %s", src); |
608 | if (glob(src, 0, NULL, &g)) { | 575 | if (glob(src, GLOB_NOCHECK, NULL, &g)) { |
609 | error("File \"%s\" not found.", src); | 576 | error("File \"%s\" not found.", src); |
610 | err = -1; | 577 | err = -1; |
611 | goto out; | 578 | goto out; |
@@ -620,7 +587,13 @@ process_put(struct sftp_conn *conn, char *src, char *dst, char *pwd, int pflag) | |||
620 | } | 587 | } |
621 | 588 | ||
622 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { | 589 | for (i = 0; g.gl_pathv[i] && !interrupted; i++) { |
623 | if (!is_reg(g.gl_pathv[i])) { | 590 | if (stat(g.gl_pathv[i], &sb) == -1) { |
591 | err = -1; | ||
592 | error("stat %s: %s", g.gl_pathv[i], strerror(errno)); | ||
593 | continue; | ||
594 | } | ||
595 | |||
596 | if (!S_ISREG(sb.st_mode)) { | ||
624 | error("skipping non-regular file %s", | 597 | error("skipping non-regular file %s", |
625 | g.gl_pathv[i]); | 598 | g.gl_pathv[i]); |
626 | continue; | 599 | continue; |
@@ -867,14 +840,238 @@ do_globbed_ls(struct sftp_conn *conn, char *path, char *strip_path, | |||
867 | } | 840 | } |
868 | 841 | ||
869 | static int | 842 | static int |
870 | parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | 843 | do_df(struct sftp_conn *conn, char *path, int hflag, int iflag) |
844 | { | ||
845 | struct sftp_statvfs st; | ||
846 | char s_used[FMT_SCALED_STRSIZE]; | ||
847 | char s_avail[FMT_SCALED_STRSIZE]; | ||
848 | char s_root[FMT_SCALED_STRSIZE]; | ||
849 | char s_total[FMT_SCALED_STRSIZE]; | ||
850 | |||
851 | if (do_statvfs(conn, path, &st, 1) == -1) | ||
852 | return -1; | ||
853 | if (iflag) { | ||
854 | printf(" Inodes Used Avail " | ||
855 | "(root) %%Capacity\n"); | ||
856 | printf("%11llu %11llu %11llu %11llu %3llu%%\n", | ||
857 | (unsigned long long)st.f_files, | ||
858 | (unsigned long long)(st.f_files - st.f_ffree), | ||
859 | (unsigned long long)st.f_favail, | ||
860 | (unsigned long long)st.f_ffree, | ||
861 | (unsigned long long)(100 * (st.f_files - st.f_ffree) / | ||
862 | st.f_files)); | ||
863 | } else if (hflag) { | ||
864 | strlcpy(s_used, "error", sizeof(s_used)); | ||
865 | strlcpy(s_avail, "error", sizeof(s_avail)); | ||
866 | strlcpy(s_root, "error", sizeof(s_root)); | ||
867 | strlcpy(s_total, "error", sizeof(s_total)); | ||
868 | fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); | ||
869 | fmt_scaled(st.f_bavail * st.f_frsize, s_avail); | ||
870 | fmt_scaled(st.f_bfree * st.f_frsize, s_root); | ||
871 | fmt_scaled(st.f_blocks * st.f_frsize, s_total); | ||
872 | printf(" Size Used Avail (root) %%Capacity\n"); | ||
873 | printf("%7sB %7sB %7sB %7sB %3llu%%\n", | ||
874 | s_total, s_used, s_avail, s_root, | ||
875 | (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / | ||
876 | st.f_blocks)); | ||
877 | } else { | ||
878 | printf(" Size Used Avail " | ||
879 | "(root) %%Capacity\n"); | ||
880 | printf("%12llu %12llu %12llu %12llu %3llu%%\n", | ||
881 | (unsigned long long)(st.f_frsize * st.f_blocks / 1024), | ||
882 | (unsigned long long)(st.f_frsize * | ||
883 | (st.f_blocks - st.f_bfree) / 1024), | ||
884 | (unsigned long long)(st.f_frsize * st.f_bavail / 1024), | ||
885 | (unsigned long long)(st.f_frsize * st.f_bfree / 1024), | ||
886 | (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / | ||
887 | st.f_blocks)); | ||
888 | } | ||
889 | return 0; | ||
890 | } | ||
891 | |||
892 | /* | ||
893 | * Undo escaping of glob sequences in place. Used to undo extra escaping | ||
894 | * applied in makeargv() when the string is destined for a function that | ||
895 | * does not glob it. | ||
896 | */ | ||
897 | static void | ||
898 | undo_glob_escape(char *s) | ||
899 | { | ||
900 | size_t i, j; | ||
901 | |||
902 | for (i = j = 0;;) { | ||
903 | if (s[i] == '\0') { | ||
904 | s[j] = '\0'; | ||
905 | return; | ||
906 | } | ||
907 | if (s[i] != '\\') { | ||
908 | s[j++] = s[i++]; | ||
909 | continue; | ||
910 | } | ||
911 | /* s[i] == '\\' */ | ||
912 | ++i; | ||
913 | switch (s[i]) { | ||
914 | case '?': | ||
915 | case '[': | ||
916 | case '*': | ||
917 | case '\\': | ||
918 | s[j++] = s[i++]; | ||
919 | break; | ||
920 | case '\0': | ||
921 | s[j++] = '\\'; | ||
922 | s[j] = '\0'; | ||
923 | return; | ||
924 | default: | ||
925 | s[j++] = '\\'; | ||
926 | s[j++] = s[i++]; | ||
927 | break; | ||
928 | } | ||
929 | } | ||
930 | } | ||
931 | |||
932 | /* | ||
933 | * Split a string into an argument vector using sh(1)-style quoting, | ||
934 | * comment and escaping rules, but with some tweaks to handle glob(3) | ||
935 | * wildcards. | ||
936 | * Returns NULL on error or a NULL-terminated array of arguments. | ||
937 | */ | ||
938 | #define MAXARGS 128 | ||
939 | #define MAXARGLEN 8192 | ||
940 | static char ** | ||
941 | makeargv(const char *arg, int *argcp) | ||
942 | { | ||
943 | int argc, quot; | ||
944 | size_t i, j; | ||
945 | static char argvs[MAXARGLEN]; | ||
946 | static char *argv[MAXARGS + 1]; | ||
947 | enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; | ||
948 | |||
949 | *argcp = argc = 0; | ||
950 | if (strlen(arg) > sizeof(argvs) - 1) { | ||
951 | args_too_longs: | ||
952 | error("string too long"); | ||
953 | return NULL; | ||
954 | } | ||
955 | state = MA_START; | ||
956 | i = j = 0; | ||
957 | for (;;) { | ||
958 | if (isspace(arg[i])) { | ||
959 | if (state == MA_UNQUOTED) { | ||
960 | /* Terminate current argument */ | ||
961 | argvs[j++] = '\0'; | ||
962 | argc++; | ||
963 | state = MA_START; | ||
964 | } else if (state != MA_START) | ||
965 | argvs[j++] = arg[i]; | ||
966 | } else if (arg[i] == '"' || arg[i] == '\'') { | ||
967 | q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; | ||
968 | if (state == MA_START) { | ||
969 | argv[argc] = argvs + j; | ||
970 | state = q; | ||
971 | } else if (state == MA_UNQUOTED) | ||
972 | state = q; | ||
973 | else if (state == q) | ||
974 | state = MA_UNQUOTED; | ||
975 | else | ||
976 | argvs[j++] = arg[i]; | ||
977 | } else if (arg[i] == '\\') { | ||
978 | if (state == MA_SQUOTE || state == MA_DQUOTE) { | ||
979 | quot = state == MA_SQUOTE ? '\'' : '"'; | ||
980 | /* Unescape quote we are in */ | ||
981 | /* XXX support \n and friends? */ | ||
982 | if (arg[i + 1] == quot) { | ||
983 | i++; | ||
984 | argvs[j++] = arg[i]; | ||
985 | } else if (arg[i + 1] == '?' || | ||
986 | arg[i + 1] == '[' || arg[i + 1] == '*') { | ||
987 | /* | ||
988 | * Special case for sftp: append | ||
989 | * double-escaped glob sequence - | ||
990 | * glob will undo one level of | ||
991 | * escaping. NB. string can grow here. | ||
992 | */ | ||
993 | if (j >= sizeof(argvs) - 5) | ||
994 | goto args_too_longs; | ||
995 | argvs[j++] = '\\'; | ||
996 | argvs[j++] = arg[i++]; | ||
997 | argvs[j++] = '\\'; | ||
998 | argvs[j++] = arg[i]; | ||
999 | } else { | ||
1000 | argvs[j++] = arg[i++]; | ||
1001 | argvs[j++] = arg[i]; | ||
1002 | } | ||
1003 | } else { | ||
1004 | if (state == MA_START) { | ||
1005 | argv[argc] = argvs + j; | ||
1006 | state = MA_UNQUOTED; | ||
1007 | } | ||
1008 | if (arg[i + 1] == '?' || arg[i + 1] == '[' || | ||
1009 | arg[i + 1] == '*' || arg[i + 1] == '\\') { | ||
1010 | /* | ||
1011 | * Special case for sftp: append | ||
1012 | * escaped glob sequence - | ||
1013 | * glob will undo one level of | ||
1014 | * escaping. | ||
1015 | */ | ||
1016 | argvs[j++] = arg[i++]; | ||
1017 | argvs[j++] = arg[i]; | ||
1018 | } else { | ||
1019 | /* Unescape everything */ | ||
1020 | /* XXX support \n and friends? */ | ||
1021 | i++; | ||
1022 | argvs[j++] = arg[i]; | ||
1023 | } | ||
1024 | } | ||
1025 | } else if (arg[i] == '#') { | ||
1026 | if (state == MA_SQUOTE || state == MA_DQUOTE) | ||
1027 | argvs[j++] = arg[i]; | ||
1028 | else | ||
1029 | goto string_done; | ||
1030 | } else if (arg[i] == '\0') { | ||
1031 | if (state == MA_SQUOTE || state == MA_DQUOTE) { | ||
1032 | error("Unterminated quoted argument"); | ||
1033 | return NULL; | ||
1034 | } | ||
1035 | string_done: | ||
1036 | if (state == MA_UNQUOTED) { | ||
1037 | argvs[j++] = '\0'; | ||
1038 | argc++; | ||
1039 | } | ||
1040 | break; | ||
1041 | } else { | ||
1042 | if (state == MA_START) { | ||
1043 | argv[argc] = argvs + j; | ||
1044 | state = MA_UNQUOTED; | ||
1045 | } | ||
1046 | if ((state == MA_SQUOTE || state == MA_DQUOTE) && | ||
1047 | (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { | ||
1048 | /* | ||
1049 | * Special case for sftp: escape quoted | ||
1050 | * glob(3) wildcards. NB. string can grow | ||
1051 | * here. | ||
1052 | */ | ||
1053 | if (j >= sizeof(argvs) - 3) | ||
1054 | goto args_too_longs; | ||
1055 | argvs[j++] = '\\'; | ||
1056 | argvs[j++] = arg[i]; | ||
1057 | } else | ||
1058 | argvs[j++] = arg[i]; | ||
1059 | } | ||
1060 | i++; | ||
1061 | } | ||
1062 | *argcp = argc; | ||
1063 | return argv; | ||
1064 | } | ||
1065 | |||
1066 | static int | ||
1067 | parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, int *hflag, | ||
871 | unsigned long *n_arg, char **path1, char **path2) | 1068 | unsigned long *n_arg, char **path1, char **path2) |
872 | { | 1069 | { |
873 | const char *cmd, *cp = *cpp; | 1070 | const char *cmd, *cp = *cpp; |
874 | char *cp2; | 1071 | char *cp2, **argv; |
875 | int base = 0; | 1072 | int base = 0; |
876 | long l; | 1073 | long l; |
877 | int i, cmdnum; | 1074 | int i, cmdnum, optidx, argc; |
878 | 1075 | ||
879 | /* Skip leading whitespace */ | 1076 | /* Skip leading whitespace */ |
880 | cp = cp + strspn(cp, WHITESPACE); | 1077 | cp = cp + strspn(cp, WHITESPACE); |
@@ -890,17 +1087,13 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
890 | cp++; | 1087 | cp++; |
891 | } | 1088 | } |
892 | 1089 | ||
1090 | if ((argv = makeargv(cp, &argc)) == NULL) | ||
1091 | return -1; | ||
1092 | |||
893 | /* Figure out which command we have */ | 1093 | /* Figure out which command we have */ |
894 | for (i = 0; cmds[i].c; i++) { | 1094 | for (i = 0; cmds[i].c != NULL; i++) { |
895 | int cmdlen = strlen(cmds[i].c); | 1095 | if (strcasecmp(cmds[i].c, argv[0]) == 0) |
896 | |||
897 | /* Check for command followed by whitespace */ | ||
898 | if (!strncasecmp(cp, cmds[i].c, cmdlen) && | ||
899 | strchr(WHITESPACE, cp[cmdlen])) { | ||
900 | cp += cmdlen; | ||
901 | cp = cp + strspn(cp, WHITESPACE); | ||
902 | break; | 1096 | break; |
903 | } | ||
904 | } | 1097 | } |
905 | cmdnum = cmds[i].n; | 1098 | cmdnum = cmds[i].n; |
906 | cmd = cmds[i].c; | 1099 | cmd = cmds[i].c; |
@@ -911,40 +1104,44 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
911 | cmdnum = I_SHELL; | 1104 | cmdnum = I_SHELL; |
912 | } else if (cmdnum == -1) { | 1105 | } else if (cmdnum == -1) { |
913 | error("Invalid command."); | 1106 | error("Invalid command."); |
914 | return (-1); | 1107 | return -1; |
915 | } | 1108 | } |
916 | 1109 | ||
917 | /* Get arguments and parse flags */ | 1110 | /* Get arguments and parse flags */ |
918 | *lflag = *pflag = *n_arg = 0; | 1111 | *lflag = *pflag = *hflag = *n_arg = 0; |
919 | *path1 = *path2 = NULL; | 1112 | *path1 = *path2 = NULL; |
1113 | optidx = 1; | ||
920 | switch (cmdnum) { | 1114 | switch (cmdnum) { |
921 | case I_GET: | 1115 | case I_GET: |
922 | case I_PUT: | 1116 | case I_PUT: |
923 | if (parse_getput_flags(&cp, pflag)) | 1117 | if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1) |
924 | return(-1); | 1118 | return -1; |
925 | /* Get first pathname (mandatory) */ | 1119 | /* Get first pathname (mandatory) */ |
926 | if (get_pathname(&cp, path1)) | 1120 | if (argc - optidx < 1) { |
927 | return(-1); | ||
928 | if (*path1 == NULL) { | ||
929 | error("You must specify at least one path after a " | 1121 | error("You must specify at least one path after a " |
930 | "%s command.", cmd); | 1122 | "%s command.", cmd); |
931 | return(-1); | 1123 | return -1; |
1124 | } | ||
1125 | *path1 = xstrdup(argv[optidx]); | ||
1126 | /* Get second pathname (optional) */ | ||
1127 | if (argc - optidx > 1) { | ||
1128 | *path2 = xstrdup(argv[optidx + 1]); | ||
1129 | /* Destination is not globbed */ | ||
1130 | undo_glob_escape(*path2); | ||
932 | } | 1131 | } |
933 | /* Try to get second pathname (optional) */ | ||
934 | if (get_pathname(&cp, path2)) | ||
935 | return(-1); | ||
936 | break; | 1132 | break; |
937 | case I_RENAME: | 1133 | case I_RENAME: |
938 | case I_SYMLINK: | 1134 | case I_SYMLINK: |
939 | if (get_pathname(&cp, path1)) | 1135 | if (argc - optidx < 2) { |
940 | return(-1); | ||
941 | if (get_pathname(&cp, path2)) | ||
942 | return(-1); | ||
943 | if (!*path1 || !*path2) { | ||
944 | error("You must specify two paths after a %s " | 1136 | error("You must specify two paths after a %s " |
945 | "command.", cmd); | 1137 | "command.", cmd); |
946 | return(-1); | 1138 | return -1; |
947 | } | 1139 | } |
1140 | *path1 = xstrdup(argv[optidx]); | ||
1141 | *path2 = xstrdup(argv[optidx + 1]); | ||
1142 | /* Paths are not globbed */ | ||
1143 | undo_glob_escape(*path1); | ||
1144 | undo_glob_escape(*path2); | ||
948 | break; | 1145 | break; |
949 | case I_RM: | 1146 | case I_RM: |
950 | case I_MKDIR: | 1147 | case I_MKDIR: |
@@ -953,59 +1150,69 @@ parse_args(const char **cpp, int *pflag, int *lflag, int *iflag, | |||
953 | case I_LCHDIR: | 1150 | case I_LCHDIR: |
954 | case I_LMKDIR: | 1151 | case I_LMKDIR: |
955 | /* Get pathname (mandatory) */ | 1152 | /* Get pathname (mandatory) */ |
956 | if (get_pathname(&cp, path1)) | 1153 | if (argc - optidx < 1) { |
957 | return(-1); | ||
958 | if (*path1 == NULL) { | ||
959 | error("You must specify a path after a %s command.", | 1154 | error("You must specify a path after a %s command.", |
960 | cmd); | 1155 | cmd); |
961 | return(-1); | 1156 | return -1; |
1157 | } | ||
1158 | *path1 = xstrdup(argv[optidx]); | ||
1159 | /* Only "rm" globs */ | ||
1160 | if (cmdnum != I_RM) | ||
1161 | undo_glob_escape(*path1); | ||
1162 | break; | ||
1163 | case I_DF: | ||
1164 | if ((optidx = parse_df_flags(cmd, argv, argc, hflag, | ||
1165 | iflag)) == -1) | ||
1166 | return -1; | ||
1167 | /* Default to current directory if no path specified */ | ||
1168 | if (argc - optidx < 1) | ||
1169 | *path1 = NULL; | ||
1170 | else { | ||
1171 | *path1 = xstrdup(argv[optidx]); | ||
1172 | undo_glob_escape(*path1); | ||
962 | } | 1173 | } |
963 | break; | 1174 | break; |
964 | case I_LS: | 1175 | case I_LS: |
965 | if (parse_ls_flags(&cp, lflag)) | 1176 | if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) |
966 | return(-1); | 1177 | return(-1); |
967 | /* Path is optional */ | 1178 | /* Path is optional */ |
968 | if (get_pathname(&cp, path1)) | 1179 | if (argc - optidx > 0) |
969 | return(-1); | 1180 | *path1 = xstrdup(argv[optidx]); |
970 | break; | 1181 | break; |
971 | case I_LLS: | 1182 | case I_LLS: |
1183 | /* Skip ls command and following whitespace */ | ||
1184 | cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); | ||
972 | case I_SHELL: | 1185 | case I_SHELL: |
973 | /* Uses the rest of the line */ | 1186 | /* Uses the rest of the line */ |
974 | break; | 1187 | break; |
975 | case I_LUMASK: | 1188 | case I_LUMASK: |
976 | base = 8; | ||
977 | case I_CHMOD: | 1189 | case I_CHMOD: |
978 | base = 8; | 1190 | base = 8; |
979 | case I_CHOWN: | 1191 | case I_CHOWN: |
980 | case I_CHGRP: | 1192 | case I_CHGRP: |
981 | /* Get numeric arg (mandatory) */ | 1193 | /* Get numeric arg (mandatory) */ |
1194 | if (argc - optidx < 1) | ||
1195 | goto need_num_arg; | ||
982 | errno = 0; | 1196 | errno = 0; |
983 | l = strtol(cp, &cp2, base); | 1197 | l = strtol(argv[optidx], &cp2, base); |
984 | if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) && | 1198 | if (cp2 == argv[optidx] || *cp2 != '\0' || |
985 | errno == ERANGE) || l < 0) { | 1199 | ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || |
1200 | l < 0) { | ||
1201 | need_num_arg: | ||
986 | error("You must supply a numeric argument " | 1202 | error("You must supply a numeric argument " |
987 | "to the %s command.", cmd); | 1203 | "to the %s command.", cmd); |
988 | return(-1); | 1204 | return -1; |
989 | } | 1205 | } |
990 | cp = cp2; | ||
991 | *n_arg = l; | 1206 | *n_arg = l; |
992 | if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp)) | 1207 | if (cmdnum == I_LUMASK) |
993 | break; | 1208 | break; |
994 | if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) { | ||
995 | error("You must supply a numeric argument " | ||
996 | "to the %s command.", cmd); | ||
997 | return(-1); | ||
998 | } | ||
999 | cp += strspn(cp, WHITESPACE); | ||
1000 | |||
1001 | /* Get pathname (mandatory) */ | 1209 | /* Get pathname (mandatory) */ |
1002 | if (get_pathname(&cp, path1)) | 1210 | if (argc - optidx < 2) { |
1003 | return(-1); | ||
1004 | if (*path1 == NULL) { | ||
1005 | error("You must specify a path after a %s command.", | 1211 | error("You must specify a path after a %s command.", |
1006 | cmd); | 1212 | cmd); |
1007 | return(-1); | 1213 | return -1; |
1008 | } | 1214 | } |
1215 | *path1 = xstrdup(argv[optidx + 1]); | ||
1009 | break; | 1216 | break; |
1010 | case I_QUIT: | 1217 | case I_QUIT: |
1011 | case I_PWD: | 1218 | case I_PWD: |
@@ -1027,7 +1234,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1027 | int err_abort) | 1234 | int err_abort) |
1028 | { | 1235 | { |
1029 | char *path1, *path2, *tmp; | 1236 | char *path1, *path2, *tmp; |
1030 | int pflag, lflag, iflag, cmdnum, i; | 1237 | int pflag, lflag, iflag, hflag, cmdnum, i; |
1031 | unsigned long n_arg; | 1238 | unsigned long n_arg; |
1032 | Attrib a, *aa; | 1239 | Attrib a, *aa; |
1033 | char path_buf[MAXPATHLEN]; | 1240 | char path_buf[MAXPATHLEN]; |
@@ -1035,7 +1242,7 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1035 | glob_t g; | 1242 | glob_t g; |
1036 | 1243 | ||
1037 | path1 = path2 = NULL; | 1244 | path1 = path2 = NULL; |
1038 | cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &n_arg, | 1245 | cmdnum = parse_args(&cmd, &pflag, &lflag, &iflag, &hflag, &n_arg, |
1039 | &path1, &path2); | 1246 | &path1, &path2); |
1040 | 1247 | ||
1041 | if (iflag != 0) | 1248 | if (iflag != 0) |
@@ -1129,6 +1336,13 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, | |||
1129 | path1 = make_absolute(path1, *pwd); | 1336 | path1 = make_absolute(path1, *pwd); |
1130 | err = do_globbed_ls(conn, path1, tmp, lflag); | 1337 | err = do_globbed_ls(conn, path1, tmp, lflag); |
1131 | break; | 1338 | break; |
1339 | case I_DF: | ||
1340 | /* Default to current directory if no path specified */ | ||
1341 | if (path1 == NULL) | ||
1342 | path1 = xstrdup(*pwd); | ||
1343 | path1 = make_absolute(path1, *pwd); | ||
1344 | err = do_df(conn, path1, hflag, iflag); | ||
1345 | break; | ||
1132 | case I_LCHDIR: | 1346 | case I_LCHDIR: |
1133 | if (chdir(path1) == -1) { | 1347 | if (chdir(path1) == -1) { |
1134 | error("Couldn't change local directory to " | 1348 | error("Couldn't change local directory to " |