[BACK]Return to sftp.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/sftp.c between version 1.96 and 1.97

version 1.96, 2007/01/03 04:09:15 version 1.97, 2007/10/24 03:30:02
Line 22 
Line 22 
 #include <sys/socket.h>  #include <sys/socket.h>
 #include <sys/param.h>  #include <sys/param.h>
   
   #include <ctype.h>
 #include <errno.h>  #include <errno.h>
 #include <glob.h>  #include <glob.h>
 #include <histedit.h>  #include <histedit.h>
Line 334 
Line 335 
 }  }
   
 static int  static int
 parse_getput_flags(const char **cpp, int *pflag)  parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
 {  {
         const char *cp = *cpp;          extern int optind, optreset, opterr;
           int ch;
   
         /* Check for flags */          optind = optreset = 1;
         if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {          opterr = 0;
                 switch (cp[1]) {  
           *pflag = 0;
           while ((ch = getopt(argc, argv, "Pp")) != -1) {
                   switch (ch) {
                 case 'p':                  case 'p':
                 case 'P':                  case 'P':
                         *pflag = 1;                          *pflag = 1;
                         break;                          break;
                 default:                  default:
                         error("Invalid flag -%c", cp[1]);                          error("%s: Invalid flag -%c", cmd, ch);
                         return(-1);                          return -1;
                 }                  }
                 cp += 2;  
                 *cpp = cp + strspn(cp, WHITESPACE);  
         }          }
   
         return(0);          return optind;
 }  }
   
 static int  static int
 parse_ls_flags(const char **cpp, int *lflag)  parse_ls_flags(char **argv, int argc, int *lflag)
 {  {
         const char *cp = *cpp;          extern int optind, optreset, opterr;
           int ch;
   
         /* Defaults */          optind = optreset = 1;
         *lflag = LS_NAME_SORT;          opterr = 0;
   
         /* Check for flags */          *lflag = LS_NAME_SORT;
         if (cp++[0] == '-') {          while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
                 for (; strchr(WHITESPACE, *cp) == NULL; cp++) {                  switch (ch) {
                         switch (*cp) {                  case '1':
                         case 'l':                          *lflag &= ~VIEW_FLAGS;
                                 *lflag &= ~VIEW_FLAGS;                          *lflag |= LS_SHORT_VIEW;
                                 *lflag |= LS_LONG_VIEW;                          break;
                                 break;                  case 'S':
                         case '1':                          *lflag &= ~SORT_FLAGS;
                                 *lflag &= ~VIEW_FLAGS;                          *lflag |= LS_SIZE_SORT;
                                 *lflag |= LS_SHORT_VIEW;                          break;
                                 break;                  case 'a':
                         case 'n':                          *lflag |= LS_SHOW_ALL;
                                 *lflag &= ~VIEW_FLAGS;                          break;
                                 *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;                  case 'f':
                                 break;                          *lflag &= ~SORT_FLAGS;
                         case 'S':                          break;
                                 *lflag &= ~SORT_FLAGS;                  case 'l':
                                 *lflag |= LS_SIZE_SORT;                          *lflag &= ~VIEW_FLAGS;
                                 break;                          *lflag |= LS_LONG_VIEW;
                         case 't':                          break;
                                 *lflag &= ~SORT_FLAGS;                  case 'n':
                                 *lflag |= LS_TIME_SORT;                          *lflag &= ~VIEW_FLAGS;
                                 break;                          *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
                         case 'r':                          break;
                                 *lflag |= LS_REVERSE_SORT;                  case 'r':
                                 break;                          *lflag |= LS_REVERSE_SORT;
                         case 'f':                          break;
                                 *lflag &= ~SORT_FLAGS;                  case 't':
                                 break;                          *lflag &= ~SORT_FLAGS;
                         case 'a':                          *lflag |= LS_TIME_SORT;
                                 *lflag |= LS_SHOW_ALL;                          break;
                                 break;                  default:
                         default:                          error("ls: Invalid flag -%c", ch);
                                 error("Invalid flag -%c", *cp);                          return -1;
                                 return(-1);  
                         }  
                 }                  }
                 *cpp = cp + strspn(cp, WHITESPACE);  
         }          }
   
         return(0);          return optind;
 }  }
   
 static int  static int
 get_pathname(const char **cpp, char **path)  
 {  
         const char *cp = *cpp, *end;  
         char quot;  
         u_int i, j;  
   
         cp += strspn(cp, WHITESPACE);  
         if (!*cp) {  
                 *cpp = cp;  
                 *path = NULL;  
                 return (0);  
         }  
   
         *path = xmalloc(strlen(cp) + 1);  
   
         /* Check for quoted filenames */  
         if (*cp == '\"' || *cp == '\'') {  
                 quot = *cp++;  
   
                 /* Search for terminating quote, unescape some chars */  
                 for (i = j = 0; i <= strlen(cp); i++) {  
                         if (cp[i] == quot) {    /* Found quote */  
                                 i++;  
                                 (*path)[j] = '\0';  
                                 break;  
                         }  
                         if (cp[i] == '\0') {    /* End of string */  
                                 error("Unterminated quote");  
                                 goto fail;  
                         }  
                         if (cp[i] == '\\') {    /* Escaped characters */  
                                 i++;  
                                 if (cp[i] != '\'' && cp[i] != '\"' &&  
                                     cp[i] != '\\') {  
                                         error("Bad escaped character '\\%c'",  
                                             cp[i]);  
                                         goto fail;  
                                 }  
                         }  
                         (*path)[j++] = cp[i];  
                 }  
   
                 if (j == 0) {  
                         error("Empty quotes");  
                         goto fail;  
                 }  
                 *cpp = cp + i + strspn(cp + i, WHITESPACE);  
         } else {  
                 /* Read to end of filename */  
                 end = strpbrk(cp, WHITESPACE);  
                 if (end == NULL)  
                         end = strchr(cp, '\0');  
                 *cpp = end + strspn(end, WHITESPACE);  
   
                 memcpy(*path, cp, end - cp);  
                 (*path)[end - cp] = '\0';  
         }  
         return (0);  
   
  fail:  
         xfree(*path);  
         *path = NULL;  
         return (-1);  
 }  
   
 static int  
 is_dir(char *path)  is_dir(char *path)
 {  {
         struct stat sb;          struct stat sb;
Line 854 
Line 789 
         return (0);          return (0);
 }  }
   
   /*
    * Undo escaping of glob sequences in place. Used to undo extra escaping
    * applied in makeargv() when the string is destined for a function that
    * does not glob it.
    */
   static void
   undo_glob_escape(char *s)
   {
           size_t i, j;
   
           for (i = j = 0;;) {
                   if (s[i] == '\0') {
                           s[j] = '\0';
                           return;
                   }
                   if (s[i] != '\\') {
                           s[j++] = s[i++];
                           continue;
                   }
                   /* s[i] == '\\' */
                   ++i;
                   switch (s[i]) {
                   case '?':
                   case '[':
                   case '*':
                   case '\\':
                           s[j++] = s[i++];
                           break;
                   case '\0':
                           s[j++] = '\\';
                           s[j] = '\0';
                           return;
                   default:
                           s[j++] = '\\';
                           s[j++] = s[i++];
                           break;
                   }
           }
   }
   
   /*
    * Split a string into an argument vector using sh(1)-style quoting,
    * comment and escaping rules, but with some tweaks to handle glob(3)
    * wildcards.
    * Returns NULL on error or a NULL-terminated array of arguments.
    */
   #define MAXARGS         128
   #define MAXARGLEN       8192
   static char **
   makeargv(const char *arg, int *argcp)
   {
           int argc, quot;
           size_t i, j;
           static char argvs[MAXARGLEN];
           static char *argv[MAXARGS + 1];
           enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
   
           *argcp = argc = 0;
           if (strlen(arg) > sizeof(argvs) - 1) {
    args_too_longs:
                   error("string too long");
                   return NULL;
           }
           state = MA_START;
           i = j = 0;
           for (;;) {
                   if (isspace(arg[i])) {
                           if (state == MA_UNQUOTED) {
                                   /* Terminate current argument */
                                   argvs[j++] = '\0';
                                   argc++;
                                   state = MA_START;
                           } else if (state != MA_START)
                                   argvs[j++] = arg[i];
                   } else if (arg[i] == '"' || arg[i] == '\'') {
                           q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
                           if (state == MA_START) {
                                   argv[argc] = argvs + j;
                                   state = q;
                           } else if (state == MA_UNQUOTED)
                                   state = q;
                           else if (state == q)
                                   state = MA_UNQUOTED;
                           else
                                   argvs[j++] = arg[i];
                   } else if (arg[i] == '\\') {
                           if (state == MA_SQUOTE || state == MA_DQUOTE) {
                                   quot = state == MA_SQUOTE ? '\'' : '"';
                                   /* Unescape quote we are in */
                                   /* XXX support \n and friends? */
                                   if (arg[i + 1] == quot) {
                                           i++;
                                           argvs[j++] = arg[i];
                                   } else if (arg[i + 1] == '?' ||
                                       arg[i + 1] == '[' || arg[i + 1] == '*') {
                                           /*
                                            * Special case for sftp: append
                                            * double-escaped glob sequence -
                                            * glob will undo one level of
                                            * escaping. NB. string can grow here.
                                            */
                                           if (j >= sizeof(argvs) - 5)
                                                   goto args_too_longs;
                                           argvs[j++] = '\\';
                                           argvs[j++] = arg[i++];
                                           argvs[j++] = '\\';
                                           argvs[j++] = arg[i];
                                   } else {
                                           argvs[j++] = arg[i++];
                                           argvs[j++] = arg[i];
                                   }
                           } else {
                                   if (state == MA_START) {
                                           argv[argc] = argvs + j;
                                           state = MA_UNQUOTED;
                                   }
                                   if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
                                       arg[i + 1] == '*' || arg[i + 1] == '\\') {
                                           /*
                                            * Special case for sftp: append
                                            * escaped glob sequence -
                                            * glob will undo one level of
                                            * escaping.
                                            */
                                           argvs[j++] = arg[i++];
                                           argvs[j++] = arg[i];
                                   } else {
                                           /* Unescape everything */
                                           /* XXX support \n and friends? */
                                           i++;
                                           argvs[j++] = arg[i];
                                   }
                           }
                   } else if (arg[i] == '#') {
                           if (state == MA_SQUOTE || state == MA_DQUOTE)
                                   argvs[j++] = arg[i];
                           else
                                   goto string_done;
                   } else if (arg[i] == '\0') {
                           if (state == MA_SQUOTE || state == MA_DQUOTE) {
                                   error("Unterminated quoted argument");
                                   return NULL;
                           }
    string_done:
                           if (state == MA_UNQUOTED) {
                                   argvs[j++] = '\0';
                                   argc++;
                           }
                           break;
                   } else {
                           if (state == MA_START) {
                                   argv[argc] = argvs + j;
                                   state = MA_UNQUOTED;
                           }
                           if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
                               (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
                                   /*
                                    * Special case for sftp: escape quoted
                                    * glob(3) wildcards. NB. string can grow
                                    * here.
                                    */
                                   if (j >= sizeof(argvs) - 3)
                                           goto args_too_longs;
                                   argvs[j++] = '\\';
                                   argvs[j++] = arg[i];
                           } else
                                   argvs[j++] = arg[i];
                   }
                   i++;
           }
           *argcp = argc;
           return argv;
   }
   
 static int  static int
 parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,  parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
     unsigned long *n_arg, char **path1, char **path2)      unsigned long *n_arg, char **path1, char **path2)
 {  {
         const char *cmd, *cp = *cpp;          const char *cmd, *cp = *cpp;
         char *cp2;          char *cp2, **argv;
         int base = 0;          int base = 0;
         long l;          long l;
         int i, cmdnum;          int i, cmdnum, optidx, argc;
   
         /* Skip leading whitespace */          /* Skip leading whitespace */
         cp = cp + strspn(cp, WHITESPACE);          cp = cp + strspn(cp, WHITESPACE);
Line 878 
Line 987 
                 cp++;                  cp++;
         }          }
   
         /* Figure out which command we have */          if ((argv = makeargv(cp, &argc)) == NULL)
         for (i = 0; cmds[i].c; i++) {                  return -1;
                 int cmdlen = strlen(cmds[i].c);  
   
                 /* Check for command followed by whitespace */          /* Figure out which command we have */
                 if (!strncasecmp(cp, cmds[i].c, cmdlen) &&          for (i = 0; cmds[i].c != NULL; i++) {
                     strchr(WHITESPACE, cp[cmdlen])) {                  if (strcasecmp(cmds[i].c, argv[0]) == 0)
                         cp += cmdlen;  
                         cp = cp + strspn(cp, WHITESPACE);  
                         break;                          break;
                 }  
         }          }
         cmdnum = cmds[i].n;          cmdnum = cmds[i].n;
         cmd = cmds[i].c;          cmd = cmds[i].c;
Line 899 
Line 1004 
                 cmdnum = I_SHELL;                  cmdnum = I_SHELL;
         } else if (cmdnum == -1) {          } else if (cmdnum == -1) {
                 error("Invalid command.");                  error("Invalid command.");
                 return (-1);                  return -1;
         }          }
   
         /* Get arguments and parse flags */          /* Get arguments and parse flags */
         *lflag = *pflag = *n_arg = 0;          *lflag = *pflag = *n_arg = 0;
         *path1 = *path2 = NULL;          *path1 = *path2 = NULL;
           optidx = 1;
         switch (cmdnum) {          switch (cmdnum) {
         case I_GET:          case I_GET:
         case I_PUT:          case I_PUT:
                 if (parse_getput_flags(&cp, pflag))                  if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
                         return(-1);                          return -1;
                 /* Get first pathname (mandatory) */                  /* Get first pathname (mandatory) */
                 if (get_pathname(&cp, path1))                  if (argc - optidx < 1) {
                         return(-1);  
                 if (*path1 == NULL) {  
                         error("You must specify at least one path after a "                          error("You must specify at least one path after a "
                             "%s command.", cmd);                              "%s command.", cmd);
                         return(-1);                          return -1;
                 }                  }
                 /* Try to get second pathname (optional) */                  *path1 = xstrdup(argv[optidx]);
                 if (get_pathname(&cp, path2))                  /* Get second pathname (optional) */
                         return(-1);                  if (argc - optidx > 1) {
                           *path2 = xstrdup(argv[optidx + 1]);
                           /* Destination is not globbed */
                           undo_glob_escape(*path2);
                   }
                 break;                  break;
         case I_RENAME:          case I_RENAME:
         case I_SYMLINK:          case I_SYMLINK:
                 if (get_pathname(&cp, path1))                  if (argc - optidx < 2) {
                         return(-1);  
                 if (get_pathname(&cp, path2))  
                         return(-1);  
                 if (!*path1 || !*path2) {  
                         error("You must specify two paths after a %s "                          error("You must specify two paths after a %s "
                             "command.", cmd);                              "command.", cmd);
                         return(-1);                          return -1;
                 }                  }
                   *path1 = xstrdup(argv[optidx]);
                   *path2 = xstrdup(argv[optidx + 1]);
                   /* Paths are not globbed */
                   undo_glob_escape(*path1);
                   undo_glob_escape(*path2);
                 break;                  break;
         case I_RM:          case I_RM:
         case I_MKDIR:          case I_MKDIR:
Line 941 
Line 1050 
         case I_LCHDIR:          case I_LCHDIR:
         case I_LMKDIR:          case I_LMKDIR:
                 /* Get pathname (mandatory) */                  /* Get pathname (mandatory) */
                 if (get_pathname(&cp, path1))                  if (argc - optidx < 1) {
                         return(-1);  
                 if (*path1 == NULL) {  
                         error("You must specify a path after a %s command.",                          error("You must specify a path after a %s command.",
                             cmd);                              cmd);
                         return(-1);                          return -1;
                 }                  }
                   *path1 = xstrdup(argv[optidx]);
                   /* Only "rm" globs */
                   if (cmdnum != I_RM)
                           undo_glob_escape(*path1);
                 break;                  break;
         case I_LS:          case I_LS:
                 if (parse_ls_flags(&cp, lflag))                  if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
                         return(-1);                          return(-1);
                 /* Path is optional */                  /* Path is optional */
                 if (get_pathname(&cp, path1))                  if (argc - optidx > 0)
                         return(-1);                          *path1 = xstrdup(argv[optidx]);
                 break;                  break;
         case I_LLS:          case I_LLS:
         case I_SHELL:          case I_SHELL:
                 /* Uses the rest of the line */                  /* Uses the rest of the line */
                 break;                  break;
         case I_LUMASK:          case I_LUMASK:
                 base = 8;  
         case I_CHMOD:          case I_CHMOD:
                 base = 8;                  base = 8;
         case I_CHOWN:          case I_CHOWN:
         case I_CHGRP:          case I_CHGRP:
                 /* Get numeric arg (mandatory) */                  /* Get numeric arg (mandatory) */
                   if (argc - optidx < 1)
                           goto need_num_arg;
                 errno = 0;                  errno = 0;
                 l = strtol(cp, &cp2, base);                  l = strtol(argv[optidx], &cp2, base);
                 if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&                  if (cp2 == argv[optidx] || *cp2 != '\0' ||
                     errno == ERANGE) || l < 0) {                      ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
                       l < 0) {
    need_num_arg:
                         error("You must supply a numeric argument "                          error("You must supply a numeric argument "
                             "to the %s command.", cmd);                              "to the %s command.", cmd);
                         return(-1);                          return -1;
                 }                  }
                 cp = cp2;  
                 *n_arg = l;                  *n_arg = l;
                 if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))                  if (cmdnum == I_LUMASK)
                         break;                          break;
                 if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {  
                         error("You must supply a numeric argument "  
                             "to the %s command.", cmd);  
                         return(-1);  
                 }  
                 cp += strspn(cp, WHITESPACE);  
   
                 /* Get pathname (mandatory) */                  /* Get pathname (mandatory) */
                 if (get_pathname(&cp, path1))                  if (argc - optidx < 2) {
                         return(-1);  
                 if (*path1 == NULL) {  
                         error("You must specify a path after a %s command.",                          error("You must specify a path after a %s command.",
                             cmd);                              cmd);
                         return(-1);                          return -1;
                 }                  }
                   *path1 = xstrdup(argv[optidx + 1]);
                 break;                  break;
         case I_QUIT:          case I_QUIT:
         case I_PWD:          case I_PWD:

Legend:
Removed from v.1.96  
changed lines
  Added in v.1.97