Diff

Differences From Artifact [47e3780e0a]:

To Artifact [0aff0a0bb2]:


681
682
683
684
685
686
687
688

689
690

691
692
693
694
695
696

697
698



699
700
701
702
703
704
705
681
682
683
684
685
686
687

688
689

690
691
692
693
694
695
696
697


698
699
700
701
702
703
704
705
706
707







-
+

-
+






+
-
-
+
+
+







	return (str);
    else
	return (NULL);
}

/*
 * Return a pointer to the portion of str that comes after the last
 * slash or backslash.
 * slash (or backslash, if `local' is TRUE).
 */
static char *stripslashes(char *str)
static char *stripslashes(char *str, int local)
{
    char *p;

    p = strrchr(str, '/');
    if (p) str = p+1;

    if (local) {
    p = strrchr(str, '\\');
    if (p) str = p+1;
	p = strrchr(str, '\\');
	if (p) str = p+1;
    }

    return str;
}

/*
 * Determine whether a string is entirely composed of dots.
 */
829
830
831
832
833
834
835

836
837

838
839
840
841
842
843
844
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848







+


+







 */

static struct scp_sftp_dirstack {
    struct scp_sftp_dirstack *next;
    struct fxp_name *names;
    int namepos, namelen;
    char *dirpath;
    char *wildcard;
} *scp_sftp_dirstack_head;
static char *scp_sftp_remotepath, *scp_sftp_currentname;
static char *scp_sftp_wildcard;
static int scp_sftp_targetisdir, scp_sftp_donethistarget;
static int scp_sftp_preserve, scp_sftp_recursive;
static unsigned long scp_sftp_mtime, scp_sftp_atime;
static int scp_has_times;
static struct fxp_handle *scp_sftp_filehandle;
static uint64 scp_sftp_fileoffset;

1054
1055
1056
1057
1058
1059
1060
1061

1062
1063

















































1064







1065
1066
1067
1068
1069

1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082

1083
1084
1085
1086
1087
1088
1089
1058
1059
1060
1061
1062
1063
1064

1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116

1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150







-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+





+













+








/*
 * Yes, I know; I have an scp_sink_setup _and_ an scp_sink_init.
 * That's bad. The difference is that scp_sink_setup is called once
 * right at the start, whereas scp_sink_init is called to
 * initialise every level of recursion in the protocol.
 */
void scp_sink_setup(char *source, int preserve, int recursive)
int scp_sink_setup(char *source, int preserve, int recursive)
{
    if (using_sftp) {
	char *newsource;
	/*
	 * It's possible that the source string we've been given
	 * contains a wildcard. If so, we must split the directory
	 * away from the wildcard itself (throwing an error if any
	 * wildcardness comes before the final slash) and arrange
	 * things so that a dirstack entry will be set up.
	 */
	newsource = smalloc(1+strlen(source));
	if (!wc_unescape(newsource, source)) {
	    /* Yes, here we go; it's a wildcard. Bah. */
	    char *dupsource, *lastpart, *dirpart, *wildcard;
	    dupsource = dupstr(source);
	    lastpart = stripslashes(dupsource, 0);
	    wildcard = dupstr(lastpart);
	    *lastpart = '\0';
	    if (*dupsource && dupsource[1]) {
		/*
		 * The remains of dupsource are at least two
		 * characters long, meaning the pathname wasn't
		 * empty or just `/'. Hence, we remove the trailing
		 * slash.
		 */
		lastpart[-1] = '\0';
	    }

	    /*
	     * Now we have separated our string into dupsource (the
	     * directory part) and wildcard. Both of these will
	     * need freeing at some point. Next step is to remove
	     * wildcard escapes from the directory part, throwing
	     * an error if it contains a real wildcard.
	     */
	    dirpart = smalloc(1+strlen(dupsource));
	    if (!wc_unescape(dirpart, dupsource)) {
		tell_user(stderr, "%s: multiple-level wildcards unsupported",
			  source);
		errs++;
		sfree(dirpart);
		sfree(wildcard);
		sfree(dupsource);
		return 1;
	    }

	    /*
	     * Now we have dirpart (unescaped, ie a valid remote
	     * path), and wildcard (a wildcard). This will be
	     * sufficient to arrange a dirstack entry.
	     */
	scp_sftp_remotepath = dupstr(source);
	    scp_sftp_remotepath = dirpart;
	    scp_sftp_wildcard = wildcard;
	    sfree(dupsource);
	} else {
	    scp_sftp_remotepath = newsource;
	    scp_sftp_wildcard = NULL;
	}
	scp_sftp_preserve = preserve;
	scp_sftp_recursive = recursive;
	scp_sftp_donethistarget = 0;
	scp_sftp_dirstack_head = NULL;
    }
    return 0;
}

int scp_sink_init(void)
{
    if (!using_sftp) {
	back->send("", 1);
    }
    return 0;
}

#define SCP_SINK_FILE   1
#define SCP_SINK_DIR    2
#define SCP_SINK_ENDDIR 3
#define SCP_SINK_RETRY  4	       /* not an action; just try again */
struct scp_sink_action {
    int action;			       /* FILE, DIR, ENDDIR */
    char *buf;			       /* will need freeing after use */
    char *name;			       /* filename or dirname (not ENDDIR) */
    int mode;			       /* access mode (not ENDDIR) */
    unsigned long size;		       /* file size (not ENDDIR) */
    int settime;		       /* 1 if atime and mtime are filled */
1117
1118
1119
1120
1121
1122
1123
1124




1125
1126
1127
1128
1129
1130
1131
1132
1133
1134


1135





1136


1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1178
1179
1180
1181
1182
1183
1184

1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197

1198
1199
1200
1201
1202
1203
1204
1205

1206
1207
1208
1209
1210
1211
1212

1213
1214
1215
1216
1217
1218
1219







-
+
+
+
+









-
+
+

+
+
+
+
+
-
+
+





-







	    /*
	     * We're now in the middle of stepping through a list
	     * of names returned from fxp_readdir(); so let's carry
	     * on.
	     */
	    struct scp_sftp_dirstack *head = scp_sftp_dirstack_head;
	    while (head->namepos < head->namelen &&
		   is_dots(head->names[head->namepos].filename))
		   (is_dots(head->names[head->namepos].filename) ||
		    (head->wildcard &&
		     !wc_match(head->wildcard,
			       head->names[head->namepos].filename))))
		head->namepos++;       /* skip . and .. */
	    if (head->namepos < head->namelen) {
		fname = dupcat(head->dirpath, "/",
			       head->names[head->namepos++].filename,
			       NULL);
		must_free_fname = 1;
	    } else {
		/*
		 * We've come to the end of the list; pop it off
		 * the stack and return an ENDDIR action.
		 * the stack and return an ENDDIR action (or RETRY
		 * if this was a wildcard match).
		 */
		if (head->wildcard) {
		    act->action = SCP_SINK_RETRY;
		    sfree(head->wildcard);
		} else {
		    act->action = SCP_SINK_ENDDIR;
		
		}

		sfree(head->dirpath);
		sfree(head->names);
		scp_sftp_dirstack_head = head->next;
		sfree(head);

		act->action = SCP_SINK_ENDDIR;
		return 0;
	    }
	}

	/*
	 * Now we have a filename. Stat it, and see if it's a file
	 * or a directory.
1160
1161
1162
1163
1164
1165
1166
1167
1168



1169
1170

1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182




1183
1184
1185
1186
1187
1188
1189
1230
1231
1232
1233
1234
1235
1236


1237
1238
1239
1240

1241
1242
1243
1244
1245
1246
1247
1248
1249
1250



1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261







-
-
+
+
+

-
+









-
-
-
+
+
+
+







	    struct scp_sftp_dirstack *newitem;
	    struct fxp_handle *dirhandle;
	    int nnames, namesize;
	    struct fxp_name *ournames;
	    struct fxp_names *names;

	    /*
	     * It's a directory. If we're not in recursive
	     * mode, this just merits a complaint.
	     * It's a directory. If we're not in recursive mode and
	     * we haven't been passed a wildcard from
	     * scp_sink_setup, this just merits a complaint.
	     */
	    if (!scp_sftp_recursive) {
	    if (!scp_sftp_recursive && !scp_sftp_wildcard) {
		tell_user(stderr, "pscp: %s: is a directory", fname);
		errs++;
		if (must_free_fname) sfree(fname);
		return 1;
	    }

	    /*
	     * Otherwise, the fun begins. We must fxp_opendir() the
	     * directory, slurp the filenames into memory, return
	     * SCP_SINK_DIR, and set targetisdir. The next time
	     * we're called, we will run through the list of
	     * filenames one by one.
	     * SCP_SINK_DIR (unless this is a wildcard match), and
	     * set targetisdir. The next time we're called, we will
	     * run through the list of filenames one by one,
	     * matching them against a wildcard if present.
	     * 
	     * If targetisdir is _already_ set (meaning we're
	     * already in the middle of going through another such
	     * list), we must push the other (target,namelist) pair
	     * on a stack.
	     */
	    dirhandle = fxp_opendir(fname);
1231
1232
1233
1234
1235
1236
1237






1238
1239



1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251













1252
1253
1254
1255
1256
1257
1258
1259

1260
1261
1262
1263
1264
1265
1266
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320












1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340

1341
1342
1343
1344
1345
1346
1347
1348







+
+
+
+
+
+


+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+







	    newitem->names = ournames;
	    newitem->namepos = 0;
	    newitem->namelen = nnames;
	    if (must_free_fname)
		newitem->dirpath = fname;
	    else
		newitem->dirpath = dupstr(fname);
	    if (scp_sftp_wildcard) {
		newitem->wildcard = scp_sftp_wildcard;
		scp_sftp_wildcard = NULL;
	    } else {
		newitem->wildcard = NULL;
	    }
	    scp_sftp_dirstack_head = newitem;

	    if (newitem->wildcard) {
		act->action = SCP_SINK_RETRY;
	    } else {
	    act->action = SCP_SINK_DIR;
	    act->buf = dupstr(stripslashes(fname));
	    act->name = act->buf;
	    act->size = 0;	       /* duhh, it's a directory */
	    act->mode = 07777 & attrs.permissions;
	    if (scp_sftp_preserve &&
		(attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
		act->atime = attrs.atime;
		act->mtime = attrs.mtime;
		act->settime = 1;
	    } else
		act->settime = 0;
		act->action = SCP_SINK_DIR;
		act->buf = dupstr(stripslashes(fname, 0));
		act->name = act->buf;
		act->size = 0;	       /* duhh, it's a directory */
		act->mode = 07777 & attrs.permissions;
		if (scp_sftp_preserve &&
		    (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) {
		    act->atime = attrs.atime;
		    act->mtime = attrs.mtime;
		    act->settime = 1;
		} else
		    act->settime = 0;
	    }
	    return 0;

	} else {
	    /*
	     * It's a file. Return SCP_SINK_FILE.
	     */
	    act->action = SCP_SINK_FILE;
	    act->buf = dupstr(stripslashes(fname));
	    act->buf = dupstr(stripslashes(fname, 0));
	    act->name = act->buf;
	    if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) {
		if (uint64_compare(attrs.size,
				   uint64_make(0, ULONG_MAX)) > 0) {
		    act->size = ULONG_MAX;   /* *boggle* */
		} else
		    act->size = attrs.size.lo;
1609
1610
1611
1612
1613
1614
1615



1616
1617
1618
1619
1620
1621
1622
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707







+
+
+







    while (1) {
	struct scp_sink_action act;
	if (scp_get_sink_action(&act))
	    return;

	if (act.action == SCP_SINK_ENDDIR)
	    return;

	if (act.action == SCP_SINK_RETRY)
	    continue;

	if (targisdir) {
	    /*
	     * Prevent the remote side from maliciously writing to
	     * files outside the target area by sending a filename
	     * containing `../'. In fact, it shouldn't be sending
	     * filenames with any slashes in at all; so we'll find
1640
1641
1642
1643
1644
1645
1646
1647

1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664

1665
1666
1667
1668
1669
1670
1671
1725
1726
1727
1728
1729
1730
1731

1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748

1749
1750
1751
1752
1753
1754
1755
1756







-
+
















-
+







	     * SCP's protocol infelicities is that wildcard
	     * matching is done at the server end _by the server's
	     * rules_ and so in general this is infeasible. Live
	     * with it, or upgrade to SFTP.)
	     */
	    char *striptarget, *stripsrc;

	    striptarget = stripslashes(act.name);
	    striptarget = stripslashes(act.name, 1);
	    if (striptarget != act.name) {
		tell_user(stderr, "warning: remote host sent a compound"
			  " pathname - possibly malicious! (ignored)");
	    }

	    /*
	     * Also check to see if the target filename is '.' or
	     * '..', or indeed '...' and so on because Windows
	     * appears to interpret those like '..'.
	     */
	    if (is_dots(striptarget)) {
		bump("security violation: remote host attempted to write to"
		     " a '.' or '..' path!");
	    }

	    if (src) {
		stripsrc = stripslashes(src);
		stripsrc = stripslashes(src, 1);
		if (!stripsrc[strcspn(stripsrc, "*?[]")] &&
		    strcmp(striptarget, stripsrc)) {
		    tell_user(stderr, "warning: remote host attempted to"
			      " write to a different filename: disallowing");
		    /* Override the name the server provided with our own. */
		    striptarget = stripsrc;
		}
1711
1712
1713
1714
1715
1716
1717
1718

1719
1720
1721
1722
1723
1724
1725
1796
1797
1798
1799
1800
1801
1802

1803
1804
1805
1806
1807
1808
1809
1810







-
+








	if (scp_accept_filexfer())
	    return;

	stat_bytes = 0;
	stat_starttime = time(NULL);
	stat_lasttime = 0;
	stat_name = stripslashes(destfname);
	stat_name = stripslashes(destfname, 1);

	received = 0;
	while (received < act.size) {
	    char transbuf[4096];
	    DWORD blksize, read, written;
	    blksize = 4096;
	    if (blksize > act.size - received)
1840
1841
1842
1843
1844
1845
1846
1847

1848
1849
1850
1851
1852
1853
1854
1925
1926
1927
1928
1929
1930
1931

1932
1933
1934
1935
1936
1937
1938
1939







-
+








	/*
	 * Trim off the last pathname component of `src', to
	 * provide the base pathname which will be prepended to
	 * filenames returned from Find{First,Next}File.
	 */
	srcpath = dupstr(src);
	last = stripslashes(srcpath);
	last = stripslashes(srcpath, 1);
	if (last == srcpath) {
	    last = strchr(srcpath, ':');
	    if (last)
		last++;
	    else
		last = srcpath;
	}
1933
1934
1935
1936
1937
1938
1939
1940


1941
1942
1943
1944
1945
1946
1947
2018
2019
2020
2021
2022
2023
2024

2025
2026
2027
2028
2029
2030
2031
2032
2033







-
+
+







	    verbose ? " -v" : "",
	    recursive ? " -r" : "",
	    preserve ? " -p" : "",
	    targetshouldbedirectory ? " -d" : "", src);
    do_cmd(host, user, cmd);
    sfree(cmd);

    scp_sink_setup(src, preserve, recursive);
    if (scp_sink_setup(src, preserve, recursive))
	return;

    sink(targ, src);
}

/*
 *  We will issue a list command to get a remote directory.
 */