View | Details | Raw Unified | Return to bug 778 | Differences between
and this patch

Collapse All | Expand All

(-)sftp.c (-171 / +275 lines)
Lines 22-27 Link Here
22
#include <sys/socket.h>
22
#include <sys/socket.h>
23
#include <sys/param.h>
23
#include <sys/param.h>
24
24
25
#include <ctype.h>
25
#include <errno.h>
26
#include <errno.h>
26
#include <glob.h>
27
#include <glob.h>
27
#include <histedit.h>
28
#include <histedit.h>
Lines 334-477 infer_path(const char *p, char **ifp) Link Here
334
}
335
}
335
336
336
static int
337
static int
337
parse_getput_flags(const char **cpp, int *pflag)
338
parse_getput_flags(const char *cmd, char **argv, int argc, int *pflag)
338
{
339
{
339
	const char *cp = *cpp;
340
	extern int optind, optreset, opterr;
341
	int ch;
340
342
341
	/* Check for flags */
343
	optind = optreset = 1;
342
	if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
344
	opterr = 0;
343
		switch (cp[1]) {
345
346
	*pflag = 0;
347
	while ((ch = getopt(argc, argv, "Pp")) != -1) {
348
		switch (ch) {
344
		case 'p':
349
		case 'p':
345
		case 'P':
350
		case 'P':
346
			*pflag = 1;
351
			*pflag = 1;
347
			break;
352
			break;
348
		default:
353
		default:
349
			error("Invalid flag -%c", cp[1]);
354
			error("%s: Invalid flag -%c", cmd, ch);
350
			return(-1);
355
			return -1;
351
		}
352
		cp += 2;
353
		*cpp = cp + strspn(cp, WHITESPACE);
354
	}
355
356
	return(0);
357
}
358
359
static int
360
parse_ls_flags(const char **cpp, int *lflag)
361
{
362
	const char *cp = *cpp;
363
364
	/* Defaults */
365
	*lflag = LS_NAME_SORT;
366
367
	/* Check for flags */
368
	if (cp++[0] == '-') {
369
		for (; strchr(WHITESPACE, *cp) == NULL; cp++) {
370
			switch (*cp) {
371
			case 'l':
372
				*lflag &= ~VIEW_FLAGS;
373
				*lflag |= LS_LONG_VIEW;
374
				break;
375
			case '1':
376
				*lflag &= ~VIEW_FLAGS;
377
				*lflag |= LS_SHORT_VIEW;
378
				break;
379
			case 'n':
380
				*lflag &= ~VIEW_FLAGS;
381
				*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
382
				break;
383
			case 'S':
384
				*lflag &= ~SORT_FLAGS;
385
				*lflag |= LS_SIZE_SORT;
386
				break;
387
			case 't':
388
				*lflag &= ~SORT_FLAGS;
389
				*lflag |= LS_TIME_SORT;
390
				break;
391
			case 'r':
392
				*lflag |= LS_REVERSE_SORT;
393
				break;
394
			case 'f':
395
				*lflag &= ~SORT_FLAGS;
396
				break;
397
			case 'a':
398
				*lflag |= LS_SHOW_ALL;
399
				break;
400
			default:
401
				error("Invalid flag -%c", *cp);
402
				return(-1);
403
			}
404
		}
356
		}
405
		*cpp = cp + strspn(cp, WHITESPACE);
406
	}
357
	}
407
358
408
	return(0);
359
	return optind;
409
}
360
}
410
361
411
static int
362
static int
412
get_pathname(const char **cpp, char **path)
363
parse_ls_flags(char **argv, int argc, int *lflag)
413
{
364
{
414
	const char *cp = *cpp, *end;
365
	extern int optind, optreset, opterr;
415
	char quot;
366
	int ch;
416
	u_int i, j;
417
418
	cp += strspn(cp, WHITESPACE);
419
	if (!*cp) {
420
		*cpp = cp;
421
		*path = NULL;
422
		return (0);
423
	}
424
367
425
	*path = xmalloc(strlen(cp) + 1);
368
	optind = optreset = 1;
369
	opterr = 0;
426
370
427
	/* Check for quoted filenames */
371
	*lflag = LS_NAME_SORT;
428
	if (*cp == '\"' || *cp == '\'') {
372
	while ((ch = getopt(argc, argv, "1Saflnrt")) != -1) {
429
		quot = *cp++;
373
		switch (ch) {
430
374
		case '1':
431
		/* Search for terminating quote, unescape some chars */
375
			*lflag &= ~VIEW_FLAGS;
432
		for (i = j = 0; i <= strlen(cp); i++) {
376
			*lflag |= LS_SHORT_VIEW;
433
			if (cp[i] == quot) {	/* Found quote */
377
			break;
434
				i++;
378
		case 'S':
435
				(*path)[j] = '\0';
379
			*lflag &= ~SORT_FLAGS;
436
				break;
380
			*lflag |= LS_SIZE_SORT;
437
			}
381
			break;
438
			if (cp[i] == '\0') {	/* End of string */
382
		case 'a':
439
				error("Unterminated quote");
383
			*lflag |= LS_SHOW_ALL;
440
				goto fail;
384
			break;
441
			}
385
		case 'f':
442
			if (cp[i] == '\\') {	/* Escaped characters */
386
			*lflag &= ~SORT_FLAGS;
443
				i++;
387
			break;
444
				if (cp[i] != '\'' && cp[i] != '\"' &&
388
		case 'l':
445
				    cp[i] != '\\') {
389
			*lflag &= ~VIEW_FLAGS;
446
					error("Bad escaped character '\\%c'",
390
			*lflag |= LS_LONG_VIEW;
447
					    cp[i]);
391
			break;
448
					goto fail;
392
		case 'n':
449
				}
393
			*lflag &= ~VIEW_FLAGS;
450
			}
394
			*lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW;
451
			(*path)[j++] = cp[i];
395
			break;
396
		case 'r':
397
			*lflag |= LS_REVERSE_SORT;
398
			break;
399
		case 't':
400
			*lflag &= ~SORT_FLAGS;
401
			*lflag |= LS_TIME_SORT;
402
			break;
403
		default:
404
			error("ls: Invalid flag -%c", ch);
405
			return -1;
452
		}
406
		}
453
454
		if (j == 0) {
455
			error("Empty quotes");
456
			goto fail;
457
		}
458
		*cpp = cp + i + strspn(cp + i, WHITESPACE);
459
	} else {
460
		/* Read to end of filename */
461
		end = strpbrk(cp, WHITESPACE);
462
		if (end == NULL)
463
			end = strchr(cp, '\0');
464
		*cpp = end + strspn(end, WHITESPACE);
465
466
		memcpy(*path, cp, end - cp);
467
		(*path)[end - cp] = '\0';
468
	}
407
	}
469
	return (0);
470
408
471
 fail:
409
	return optind;
472
	xfree(*path);
473
	*path = NULL;
474
	return (-1);
475
}
410
}
476
411
477
static int
412
static int
Lines 854-868 do_globbed_ls(struct sftp_conn *conn, ch Link Here
854
	return (0);
789
	return (0);
855
}
790
}
856
791
792
/*
793
 * Undo escaping of glob sequences in place. Used to undo extra escaping
794
 * applied in makeargv() when the string is destined for a function that
795
 * does not glob it.
796
 */
797
static void
798
undo_glob_escape(char *s)
799
{
800
	size_t i, j;
801
802
	for (i = j = 0;;) {
803
		if (s[i] == '\0') {
804
			s[j] = '\0';
805
			return;
806
		}
807
		if (s[i] != '\\') {
808
			s[j++] = s[i++];
809
			continue;
810
		}
811
		/* s[i] == '\\' */
812
		++i;
813
		switch (s[i]) {
814
		case '?':
815
		case '[':
816
		case '*':
817
			s[j++] = s[i++];
818
			break;
819
		case '\0':
820
			s[j++] = '\\';
821
			s[j] = '\0';
822
			return;
823
		default:
824
			s[j++] = '\\';
825
			s[j++] = s[i++];
826
			break;
827
		}
828
	}
829
}
830
831
/*
832
 * Split a string into an argument vector using sh(1)-style quoting,
833
 * comment and escaping rules, but with some tweaks to handle glob(3)
834
 * wildcards.
835
 * Returns NULL on error or up to MAXARGS in static buffer (NULL terminated)
836
 */
837
#define MAXARGS 	128
838
#define MAXARGLEN	8192
839
static char **
840
makeargv(const char *arg, int *argcp)
841
{
842
	int argc, quot;
843
	size_t i, j;
844
	static char argvs[MAXARGLEN];
845
	static char *argv[MAXARGS + 1];
846
	enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q;
847
848
	*argcp = argc = 0;
849
	if (strlen(arg) > sizeof(argvs) - 1) {
850
 args_too_longs:
851
		error("string too long");
852
		return NULL;
853
	}
854
	state = MA_START;
855
	i = j = 0;
856
	for (;;) {
857
		if (isspace(arg[i])) {
858
			if (state == MA_UNQUOTED) {
859
				/* Terminate current argument */
860
				argvs[j++] = '\0';
861
				argc++;
862
				state = MA_START;
863
			} else if (state != MA_START)
864
				argvs[j++] = arg[i];
865
		} else if (arg[i] == '"' || arg[i] == '\'') {
866
			q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE;
867
			if (state == MA_START) {
868
				argv[argc] = argvs + j;
869
				state = q;
870
			} else if (state == MA_UNQUOTED) 
871
				state = q;
872
			else if (state == q)
873
				state = MA_UNQUOTED;
874
			else
875
				argvs[j++] = arg[i];
876
		} else if (arg[i] == '\\') {
877
			if (state == MA_SQUOTE || state == MA_DQUOTE) {
878
				quot = state == MA_SQUOTE ? '\'' : '"';
879
				/* Unescape backslash or the quote we are in */
880
				/* XXX support \n and friends? */
881
				if (arg[i + 1] == quot || arg[i + 1] == '\\') {
882
					i++;
883
					argvs[j++] = arg[i];
884
				} else if (arg[i + 1] == '?' ||
885
				    arg[i + 1] == '[' || arg[i + 1] == '*') {
886
					/*
887
					 * Special case for sftp: append
888
					 * double-escaped glob sequence -
889
					 * glob will undo one level of
890
					 * escaping. NB. string can grow here.
891
					 */
892
					if (j >= sizeof(argvs) - 5)
893
						goto args_too_longs;
894
					argvs[j++] = '\\';
895
					argvs[j++] = arg[i++];
896
					argvs[j++] = '\\';
897
					argvs[j++] = arg[i];
898
				} else {
899
					argvs[j++] = arg[i++];
900
					argvs[j++] = arg[i];
901
				}
902
			} else {
903
				if (state == MA_START) {
904
					argv[argc] = argvs + j;
905
					state = MA_UNQUOTED;
906
				}
907
				if (arg[i + 1] == '?' || arg[i + 1] == '[' ||
908
				    arg[i + 1] == '*') {
909
					/*
910
					 * Special case for sftp: append
911
					 * escaped glob sequence -
912
					 * glob will undo one level of
913
					 * escaping.
914
					 */
915
					argvs[j++] = arg[i++];
916
					argvs[j++] = arg[i];
917
				} else {
918
					/* Unescape everything */
919
					/* XXX support \n and friends? */
920
					i++;
921
					argvs[j++] = arg[i];
922
				}
923
			}
924
		} else if (arg[i] == '#') {
925
			if (state == MA_SQUOTE || state == MA_DQUOTE)
926
				argvs[j++] = arg[i];
927
			else
928
				goto string_done;
929
		} else if (arg[i] == '\0') {
930
			if (state == MA_SQUOTE || state == MA_DQUOTE) {
931
				error("Unterminated quoted argument");
932
				return NULL;
933
			}
934
 string_done:
935
			if (state == MA_UNQUOTED) {
936
				argvs[j++] = '\0';
937
				argc++;
938
			}
939
			break;
940
		} else {
941
			if (state == MA_START) {
942
				argv[argc] = argvs + j;
943
				state = MA_UNQUOTED;
944
			}
945
			if ((state == MA_SQUOTE || state == MA_DQUOTE) &&
946
			    (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) {
947
				/*
948
				 * Special case for sftp: escape quoted
949
				 * glob(3) wildcards. NB. string can grow
950
				 * here.
951
				 */
952
				if (j >= sizeof(argvs) - 3)
953
					goto args_too_longs;
954
				argvs[j++] = '\\';
955
				argvs[j++] = arg[i];
956
			} else
957
				argvs[j++] = arg[i];
958
		}
959
		i++;
960
	}
961
	*argcp = argc;
962
	return argv;
963
}
964
857
static int
965
static int
858
parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
966
parse_args(const char **cpp, int *pflag, int *lflag, int *iflag,
859
    unsigned long *n_arg, char **path1, char **path2)
967
    unsigned long *n_arg, char **path1, char **path2)
860
{
968
{
861
	const char *cmd, *cp = *cpp;
969
	const char *cmd, *cp = *cpp;
862
	char *cp2;
970
	char *cp2, **argv;
863
	int base = 0;
971
	int base = 0;
864
	long l;
972
	long l;
865
	int i, cmdnum;
973
	int i, cmdnum, optidx, argc;
866
974
867
	/* Skip leading whitespace */
975
	/* Skip leading whitespace */
868
	cp = cp + strspn(cp, WHITESPACE);
976
	cp = cp + strspn(cp, WHITESPACE);
Lines 878-894 parse_args(const char **cpp, int *pflag, Link Here
878
		cp++;
986
		cp++;
879
	}
987
	}
880
988
881
	/* Figure out which command we have */
989
	if ((argv = makeargv(cp, &argc)) == NULL)
882
	for (i = 0; cmds[i].c; i++) {
990
		return -1;
883
		int cmdlen = strlen(cmds[i].c);
884
991
885
		/* Check for command followed by whitespace */
992
	/* Figure out which command we have */
886
		if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
993
	for (i = 0; cmds[i].c != NULL; i++) {
887
		    strchr(WHITESPACE, cp[cmdlen])) {
994
		if (strcasecmp(cmds[i].c, argv[0]) == 0)
888
			cp += cmdlen;
889
			cp = cp + strspn(cp, WHITESPACE);
890
			break;
995
			break;
891
		}
892
	}
996
	}
893
	cmdnum = cmds[i].n;
997
	cmdnum = cmds[i].n;
894
	cmd = cmds[i].c;
998
	cmd = cmds[i].c;
Lines 899-938 parse_args(const char **cpp, int *pflag, Link Here
899
		cmdnum = I_SHELL;
1003
		cmdnum = I_SHELL;
900
	} else if (cmdnum == -1) {
1004
	} else if (cmdnum == -1) {
901
		error("Invalid command.");
1005
		error("Invalid command.");
902
		return (-1);
1006
		return -1;
903
	}
1007
	}
904
1008
905
	/* Get arguments and parse flags */
1009
	/* Get arguments and parse flags */
906
	*lflag = *pflag = *n_arg = 0;
1010
	*lflag = *pflag = *n_arg = 0;
907
	*path1 = *path2 = NULL;
1011
	*path1 = *path2 = NULL;
1012
	optidx = 1;
908
	switch (cmdnum) {
1013
	switch (cmdnum) {
909
	case I_GET:
1014
	case I_GET:
910
	case I_PUT:
1015
	case I_PUT:
911
		if (parse_getput_flags(&cp, pflag))
1016
		if ((optidx = parse_getput_flags(cmd, argv, argc, pflag)) == -1)
912
			return(-1);
1017
			return -1;
913
		/* Get first pathname (mandatory) */
1018
		/* Get first pathname (mandatory) */
914
		if (get_pathname(&cp, path1))
1019
		if (argc - optidx < 1) {
915
			return(-1);
916
		if (*path1 == NULL) {
917
			error("You must specify at least one path after a "
1020
			error("You must specify at least one path after a "
918
			    "%s command.", cmd);
1021
			    "%s command.", cmd);
919
			return(-1);
1022
			return -1;
1023
		}
1024
		*path1 = xstrdup(argv[optidx]);
1025
		/* Get second pathname (optional) */
1026
		if (argc - optidx > 1) {
1027
			*path2 = xstrdup(argv[optidx + 1]);
1028
			/* Destination is not globbed */
1029
			undo_glob_escape(*path2);
920
		}
1030
		}
921
		/* Try to get second pathname (optional) */
922
		if (get_pathname(&cp, path2))
923
			return(-1);
924
		break;
1031
		break;
925
	case I_RENAME:
1032
	case I_RENAME:
926
	case I_SYMLINK:
1033
	case I_SYMLINK:
927
		if (get_pathname(&cp, path1))
1034
		if (argc - optidx < 2) {
928
			return(-1);
929
		if (get_pathname(&cp, path2))
930
			return(-1);
931
		if (!*path1 || !*path2) {
932
			error("You must specify two paths after a %s "
1035
			error("You must specify two paths after a %s "
933
			    "command.", cmd);
1036
			    "command.", cmd);
934
			return(-1);
1037
			return -1;
935
		}
1038
		}
1039
		*path1 = xstrdup(argv[optidx]);
1040
		*path2 = xstrdup(argv[optidx + 1]);
1041
		/* Paths are not globbed */
1042
		undo_glob_escape(*path1);
1043
		undo_glob_escape(*path2);
936
		break;
1044
		break;
937
	case I_RM:
1045
	case I_RM:
938
	case I_MKDIR:
1046
	case I_MKDIR:
Lines 941-999 parse_args(const char **cpp, int *pflag, Link Here
941
	case I_LCHDIR:
1049
	case I_LCHDIR:
942
	case I_LMKDIR:
1050
	case I_LMKDIR:
943
		/* Get pathname (mandatory) */
1051
		/* Get pathname (mandatory) */
944
		if (get_pathname(&cp, path1))
1052
		if (argc - optidx < 1) {
945
			return(-1);
946
		if (*path1 == NULL) {
947
			error("You must specify a path after a %s command.",
1053
			error("You must specify a path after a %s command.",
948
			    cmd);
1054
			    cmd);
949
			return(-1);
1055
			return -1;
950
		}
1056
		}
1057
		*path1 = xstrdup(argv[optidx]);
1058
		/* Only "rm" globs */
1059
		if (cmdnum != I_RM)
1060
			undo_glob_escape(*path1);
951
		break;
1061
		break;
952
	case I_LS:
1062
	case I_LS:
953
		if (parse_ls_flags(&cp, lflag))
1063
		if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1)
954
			return(-1);
1064
			return(-1);
955
		/* Path is optional */
1065
		/* Path is optional */
956
		if (get_pathname(&cp, path1))
1066
		if (argc - optidx > 0)
957
			return(-1);
1067
			*path1 = xstrdup(argv[optidx]);
958
		break;
1068
		break;
959
	case I_LLS:
1069
	case I_LLS:
960
	case I_SHELL:
1070
	case I_SHELL:
961
		/* Uses the rest of the line */
1071
		/* Uses the rest of the line */
962
		break;
1072
		break;
963
	case I_LUMASK:
1073
	case I_LUMASK:
964
		base = 8;
965
	case I_CHMOD:
1074
	case I_CHMOD:
966
		base = 8;
1075
		base = 8;
967
	case I_CHOWN:
1076
	case I_CHOWN:
968
	case I_CHGRP:
1077
	case I_CHGRP:
969
		/* Get numeric arg (mandatory) */
1078
		/* Get numeric arg (mandatory) */
1079
		if (argc - optidx < 1)
1080
			goto need_num_arg;
970
		errno = 0;
1081
		errno = 0;
971
		l = strtol(cp, &cp2, base);
1082
		l = strtol(argv[optidx], &cp2, base);
972
		if (cp2 == cp || ((l == LONG_MIN || l == LONG_MAX) &&
1083
		if (cp2 == argv[optidx] || *cp2 != '\0' ||
973
		    errno == ERANGE) || l < 0) {
1084
		    ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) ||
1085
		    l < 0) {
1086
 need_num_arg:
974
			error("You must supply a numeric argument "
1087
			error("You must supply a numeric argument "
975
			    "to the %s command.", cmd);
1088
			    "to the %s command.", cmd);
976
			return(-1);
1089
			return -1;
977
		}
1090
		}
978
		cp = cp2;
979
		*n_arg = l;
1091
		*n_arg = l;
980
		if (cmdnum == I_LUMASK && strchr(WHITESPACE, *cp))
1092
		if (cmdnum == I_LUMASK)
981
			break;
1093
			break;
982
		if (cmdnum == I_LUMASK || !strchr(WHITESPACE, *cp)) {
983
			error("You must supply a numeric argument "
984
			    "to the %s command.", cmd);
985
			return(-1);
986
		}
987
		cp += strspn(cp, WHITESPACE);
988
989
		/* Get pathname (mandatory) */
1094
		/* Get pathname (mandatory) */
990
		if (get_pathname(&cp, path1))
1095
		if (argc - optidx < 2) {
991
			return(-1);
992
		if (*path1 == NULL) {
993
			error("You must specify a path after a %s command.",
1096
			error("You must specify a path after a %s command.",
994
			    cmd);
1097
			    cmd);
995
			return(-1);
1098
			return -1;
996
		}
1099
		}
1100
		*path1 = xstrdup(argv[optidx + 1]);
997
		break;
1101
		break;
998
	case I_QUIT:
1102
	case I_QUIT:
999
	case I_PWD:
1103
	case I_PWD:

Return to bug 778