📄 psftp.c
字号:
/*
* In recursive mode, see if we're dealing with a directory.
* (If we're not in recursive mode, we need not even check: the
* subsequent fopen will return an error message.)
*/
if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) {
struct fxp_attrs attrs;
int result;
int nnames, namesize;
char *name, **ournames;
DirHandle *dh;
int i;
/*
* First, attempt to create the destination directory,
* unless it already exists.
*/
sftp_register(req = fxp_stat_send(outfname));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
result = fxp_stat_recv(pktin, rreq, &attrs);
if (!result ||
!(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
!(attrs.permissions & 0040000)) {
sftp_register(req = fxp_mkdir_send(outfname));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
result = fxp_mkdir_recv(pktin, rreq);
if (!result) {
printf("%s: create directory: %s\n",
outfname, fxp_error());
return 0;
}
}
/*
* Now get the list of filenames in the local directory.
*/
nnames = namesize = 0;
ournames = NULL;
dh = open_directory(fname);
if (!dh) {
printf("%s: unable to open directory\n", fname);
return 0;
}
while ((name = read_filename(dh)) != NULL) {
if (nnames >= namesize) {
namesize += 128;
ournames = sresize(ournames, namesize, char *);
}
ournames[nnames++] = name;
}
close_directory(dh);
/*
* Sort the names into a clear order. This ought to make
* things more predictable when we're doing a reput of the
* same directory, just in case two readdirs on the same
* local directory return a different order.
*/
qsort(ournames, nnames, sizeof(*ournames), bare_name_compare);
/*
* If we're in restart mode, find the last filename on this
* list that already exists. We may have to do a reput on
* _that_ file, but shouldn't have to do anything on the
* previous files.
*
* If none of them exists, of course, we start at 0.
*/
i = 0;
if (restart) {
while (i < nnames) {
char *nextoutfname;
nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
sftp_register(req = fxp_stat_send(nextoutfname));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
result = fxp_stat_recv(pktin, rreq, &attrs);
sfree(nextoutfname);
if (!result)
break;
i++;
}
if (i > 0)
i--;
}
/*
* Now we're ready to recurse. Starting at ournames[i]
* and continuing on to the end of the list, we
* construct a new source and target file name, and
* call sftp_put_file again.
*/
for (; i < nnames; i++) {
char *nextfname, *nextoutfname;
int ret;
if (fname)
nextfname = dir_file_cat(fname, ournames[i]);
else
nextfname = dupstr(ournames[i]);
nextoutfname = dupcat(outfname, "/", ournames[i], NULL);
ret = sftp_put_file(nextfname, nextoutfname, recurse, restart);
restart = FALSE; /* after first partial file, do full */
sfree(nextoutfname);
sfree(nextfname);
if (!ret) {
for (i = 0; i < nnames; i++) {
sfree(ournames[i]);
}
sfree(ournames);
return 0;
}
}
/*
* Done this recursion level. Free everything.
*/
for (i = 0; i < nnames; i++) {
sfree(ournames[i]);
}
sfree(ournames);
return 1;
}
file = open_existing_file(fname, NULL, NULL, NULL);
if (!file) {
printf("local: unable to open %s\n", fname);
return 0;
}
if (restart) {
sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE));
} else {
sftp_register(req = fxp_open_send(outfname, SSH_FXF_WRITE |
SSH_FXF_CREAT | SSH_FXF_TRUNC));
}
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
fh = fxp_open_recv(pktin, rreq);
if (!fh) {
printf("%s: open for write: %s\n", outfname, fxp_error());
return 0;
}
if (restart) {
char decbuf[30];
struct fxp_attrs attrs;
int ret;
sftp_register(req = fxp_fstat_send(fh));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
ret = fxp_fstat_recv(pktin, rreq, &attrs);
if (!ret) {
printf("read size of %s: %s\n", outfname, fxp_error());
return 0;
}
if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
printf("read size of %s: size was not given\n", outfname);
return 0;
}
offset = attrs.size;
uint64_decimal(offset, decbuf);
printf("reput: restarting at file position %s\n", decbuf);
if (seek_file((WFile *)file, offset, FROM_START) != 0)
seek_file((WFile *)file, uint64_make(0,0), FROM_END); /* *shrug* */
} else {
offset = uint64_make(0, 0);
}
printf("local:%s => remote:%s\n", fname, outfname);
/*
* FIXME: we can use FXP_FSTAT here to get the file size, and
* thus put up a progress bar.
*/
ret = 1;
xfer = xfer_upload_init(fh, offset);
err = eof = 0;
while ((!err && !eof) || !xfer_done(xfer)) {
char buffer[4096];
int len, ret;
while (xfer_upload_ready(xfer) && !err && !eof) {
len = read_from_file(file, buffer, sizeof(buffer));
if (len == -1) {
printf("error while reading local file\n");
err = 1;
} else if (len == 0) {
eof = 1;
} else {
xfer_upload_data(xfer, buffer, len);
}
}
if (!xfer_done(xfer)) {
pktin = sftp_recv();
ret = xfer_upload_gotpkt(xfer, pktin);
if (!ret) {
printf("error while writing: %s\n", fxp_error());
err = 1;
}
}
}
xfer_cleanup(xfer);
sftp_register(req = fxp_close_send(fh));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
fxp_close_recv(pktin, rreq);
close_rfile(file);
return ret;
}
/* ----------------------------------------------------------------------
* A remote wildcard matcher, providing a similar interface to the
* local one in psftp.h.
*/
typedef struct SftpWildcardMatcher {
struct fxp_handle *dirh;
struct fxp_names *names;
int namepos;
char *wildcard, *prefix;
} SftpWildcardMatcher;
SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name)
{
struct sftp_packet *pktin;
struct sftp_request *req, *rreq;
char *wildcard;
char *unwcdir, *tmpdir, *cdir;
int len, check;
SftpWildcardMatcher *swcm;
struct fxp_handle *dirh;
/*
* We don't handle multi-level wildcards; so we expect to find
* a fully specified directory part, followed by a wildcard
* after that.
*/
wildcard = stripslashes(name, 0);
unwcdir = dupstr(name);
len = wildcard - name;
unwcdir[len] = '\0';
if (len > 0 && unwcdir[len-1] == '/')
unwcdir[len-1] = '\0';
tmpdir = snewn(1 + len, char);
check = wc_unescape(tmpdir, unwcdir);
sfree(tmpdir);
if (!check) {
printf("Multiple-level wildcards are not supported\n");
sfree(unwcdir);
return NULL;
}
cdir = canonify(unwcdir);
sftp_register(req = fxp_opendir_send(cdir));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
dirh = fxp_opendir_recv(pktin, rreq);
if (dirh) {
swcm = snew(SftpWildcardMatcher);
swcm->dirh = dirh;
swcm->names = NULL;
swcm->wildcard = dupstr(wildcard);
swcm->prefix = unwcdir;
} else {
printf("Unable to open %s: %s\n", cdir, fxp_error());
swcm = NULL;
sfree(unwcdir);
}
sfree(cdir);
return swcm;
}
char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
{
struct fxp_name *name;
struct sftp_packet *pktin;
struct sftp_request *req, *rreq;
while (1) {
if (swcm->names && swcm->namepos >= swcm->names->nnames) {
fxp_free_names(swcm->names);
swcm->names = NULL;
}
if (!swcm->names) {
sftp_register(req = fxp_readdir_send(swcm->dirh));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
swcm->names = fxp_readdir_recv(pktin, rreq);
if (!swcm->names) {
if (fxp_error_type() != SSH_FX_EOF)
printf("%s: reading directory: %s\n", swcm->prefix,
fxp_error());
return NULL;
}
swcm->namepos = 0;
}
assert(swcm->names && swcm->namepos < swcm->names->nnames);
name = &swcm->names->names[swcm->namepos++];
if (!strcmp(name->filename, ".") || !strcmp(name->filename, ".."))
continue; /* expected bad filenames */
if (!vet_filename(name->filename)) {
printf("ignoring potentially dangerous server-"
"supplied filename '%s'\n", name->filename);
continue; /* unexpected bad filename */
}
if (!wc_match(swcm->wildcard, name->filename))
continue; /* doesn't match the wildcard */
/*
* We have a working filename. Return it.
*/
return dupprintf("%s%s%s", swcm->prefix,
(!swcm->prefix[0] ||
swcm->prefix[strlen(swcm->prefix)-1]=='/' ?
"" : "/"),
name->filename);
}
}
void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm)
{
struct sftp_packet *pktin;
struct sftp_request *req, *rreq;
sftp_register(req = fxp_close_send(swcm->dirh));
rreq = sftp_find_request(pktin = sftp_recv());
assert(rreq == req);
fxp_close_recv(pktin, rreq);
if (swcm->names)
fxp_free_names(swcm->names);
sfree(swcm->prefix);
sfree(swcm->wildcard);
sfree(swcm);
}
/*
* General function to match a potential wildcard in a filename
* argument and iterate over every matching file. Used in several
* PSFTP commands (rmdir, rm, chmod, mv).
*/
int wildcard_iterate(char *filename, int (*func)(void *, char *), void *ctx)
{
char *unwcfname, *newname, *cname;
int is_wc, ret;
unwcfname = snewn(strlen(filename)+1, char);
is_wc = !wc_unescape(unwcfname, filename);
if (is_wc) {
SftpWildcardMatcher *swcm = sftp_begin_wildcard_matching(filename);
int matched = FALSE;
sfree(unwcfname);
if (!swcm)
return 0;
ret = 1;
while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) {
cname = canonify(newname);
if (!cname) {
printf("%s: canonify: %s\n", newname, fxp_error());
ret = 0;
}
matched = TRUE;
ret &= func(ctx, cname);
sfree(cname);
}
if (!matched) {
/* Politely warn the user that nothing matched. */
printf("%s: nothing matched\n", filename);
}
sftp_finish_wildcard_matching(swcm);
} else {
cname = canonify(unwcfname);
if (!cname) {
printf("%s: canonify: %s\n", filename, fxp_error());
ret = 0;
}
ret = func(ctx, cname);
sfree(cname);
sfree(unwcfname);
}
return ret;
}
/*
* Handy helper function.
*/
int is_wildcard(char *name)
{
char *unwcfname = snewn(strlen(name)+1, char);
int is_wc = !wc_unescape(unwcfname, name);
sfree(unwcfname);
return is_wc;
}
/* ----------------------------------------------------------------------
* Actual sftp commands.
*/
struct sftp_command {
char **words;
int nwords, wordssize;
int (*obey) (struct sftp_command *); /* returns <0 to quit */
};
int sftp_cmd_null(struct sftp_command *cmd)
{
return 1; /* success */
}
int sftp_cmd_unknown(struct sftp_command *cmd)
{
printf("psftp: unknown command \"%s\"\n", cmd->words[0]);
return 0; /* failure */
}
int sftp_cmd_quit(struct sftp_command *cmd)
{
return -1;
}
int sftp_cmd_close(struct sftp_command *cmd)
{
if (back == NULL) {
not_connected();
return 0;
}
if (back != NULL && back->connected(backhandle)) {
char ch;
back->special(backhandle, TS_EOF);
sftp_recvdata(&ch, 1);
}
do_sftp_cleanup();
return 0;
}
/*
* List a directory. If no arguments are given, list pwd; otherwise
* list the directory given in words[1].
*/
int sftp_cmd_ls(struct sftp_command *cmd)
{
struct fxp_handle *dirh;
struct fxp_names *names;
struct fxp_name **ournames;
int nnames, namesize;
char *dir, *cdir, *unwcdir, *wildcard;
struct sftp_packet *pktin;
struct sftp_request *req, *rreq;
int i;
if (back == NULL) {
not_connected();
return 0;
}
if (cmd->nwords < 2)
dir = ".";
else
dir = cmd->words[1];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -