From fd04cc776685ad9f01a5311cc6c4aa9570c63a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag-Erling=20Sm=C3=B8rgrav?= Date: Fri, 12 Aug 2005 12:48:50 +0000 Subject: [PATCH] Change directory one level at a time, and use CDUP to back out. This is a work in progress; it partially fixed bin/83278 and is a prerequisite to fixing bin/83277. PR: bin/83277, bin/83278 --- lib/libfetch/ftp.c | 181 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 152 insertions(+), 29 deletions(-) diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c index f59b2f481b03..c504cbea2ea1 100644 --- a/lib/libfetch/ftp.c +++ b/lib/libfetch/ftp.c @@ -90,6 +90,9 @@ __FBSDID("$FreeBSD$"); #define FTP_EPASSIVE_MODE 229 #define FTP_LOGGED_IN 230 #define FTP_FILE_ACTION_OK 250 +#define FTP_DIRECTORY_CREATED 257 /* multiple meanings */ +#define FTP_FILE_CREATED 257 /* multiple meanings */ +#define FTP_WORKING_DIRECTORY 257 /* multiple meanings */ #define FTP_NEED_PASSWORD 331 #define FTP_NEED_ACCOUNT 332 #define FTP_FILE_OK 350 @@ -199,14 +202,58 @@ _ftp_cmd(conn_t *conn, const char *fmt, ...) * Return a pointer to the filename part of a path */ static const char * -_ftp_filename(const char *file) +_ftp_filename(const char *file, int *len, int *type) { - char *s; + const char *s; if ((s = strrchr(file, '/')) == NULL) - return (file); + s = file; else - return (s + 1); + s = s + 1; + *len = strlen(s); + if (*len > 7 && strncmp(s + *len - 7, ";type=", 6) == 0) { + *type = s[*len - 1]; + *len -= 7; + } else { + *type = '\0'; + } + return (s); +} + +/* + * Get current working directory from the reply to a CWD, PWD or CDUP + * command. + */ +static int +_ftp_pwd(conn_t *conn, char *pwd, size_t pwdlen) +{ + char *src, *dst, *end; + int q; + + if (conn->err != FTP_WORKING_DIRECTORY && + conn->err != FTP_FILE_ACTION_OK) + return (FTP_PROTOCOL_ERROR); + end = conn->buf + conn->buflen; + src = conn->buf + 4; + if (src >= end || *src++ != '"') + return (FTP_PROTOCOL_ERROR); + for (q = 0, dst = pwd; src < end && pwdlen--; ++src) { + if (!q && *src == '"') + q = 1; + else if (q && *src != '"') + break; + else if (q) + *dst++ = '"', q = 0; + else + *dst++ = *src; + } + if (!pwdlen) + return (FTP_PROTOCOL_ERROR); + *dst = '\0'; +#if 0 + DEBUG(fprintf(stderr, "pwd: [%s]\n", pwd)); +#endif + return (FTP_OK); } /* @@ -216,21 +263,91 @@ _ftp_filename(const char *file) static int _ftp_cwd(conn_t *conn, const char *file) { - char *s; - int e; + const char *beg, *end; + char pwd[PATH_MAX]; + int e, i, len; - if ((s = strrchr(file, '/')) == NULL || s == file) { - e = _ftp_cmd(conn, "CWD /"); - } else { - e = _ftp_cmd(conn, "CWD %.*s", s - file, file); - } - if (e != FTP_FILE_ACTION_OK) { + if ((end = strrchr(file, '/')) == NULL) + return (0); + if ((e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || + (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { _ftp_seterr(e); return (-1); } + for (;;) { + len = strlen(pwd); + /* look for a common prefix */ + for (i = 0; i <= len && i <= end - file; ++i) + if (pwd[i] != file[i]) + break; +#if 0 + DEBUG(fprintf(stderr, "have: [%.*s|%s]\n", i, pwd, pwd + i)); + DEBUG(fprintf(stderr, "want: [%.*s|%s]\n", i, file, file + i)); +#endif + if (pwd[i] == '\0' && (file[i - 1] == '/' || file[i] == '/')) + break; + if ((e = _ftp_cmd(conn, "CDUP")) != FTP_FILE_ACTION_OK || + (e = _ftp_cmd(conn, "PWD")) != FTP_WORKING_DIRECTORY || + (e = _ftp_pwd(conn, pwd, sizeof(pwd))) != FTP_OK) { + _ftp_seterr(e); + return (-1); + } + } + for (beg = file + i; beg < end; beg = file + i + 1) { + for (++i; file + i < end && file[i] != '/'; ++i) + /* nothing */ ; + e = _ftp_cmd(conn, "CWD %.*s", file + i - beg, beg); + if (e != FTP_FILE_ACTION_OK) { + _ftp_seterr(e); + return (-1); + } + } return (0); } +/* + * Set transfer mode and data type + */ +static int +_ftp_mode_type(conn_t *conn, int mode, int type) +{ + int e; + + switch (mode) { + case 0: + case 's': + mode = 'S'; + case 'S': + break; + default: + return (FTP_PROTOCOL_ERROR); + } + if ((e = _ftp_cmd(conn, "MODE %c", mode)) != FTP_OK) + return (e); + + switch (type) { + case 0: + case 'i': + type = 'I'; + case 'I': + break; + case 'a': + type = 'A'; + case 'A': + break; + case 'd': + type = 'D'; + case 'D': + /* can't handle yet */ + default: + return (FTP_PROTOCOL_ERROR); + } + if ((e = _ftp_cmd(conn, "TYPE %c", type)) != FTP_OK) + return (e); + + return (FTP_OK); +} + /* * Request and parse file stats */ @@ -238,7 +355,8 @@ static int _ftp_stat(conn_t *conn, const char *file, struct url_stat *us) { char *ln; - const char *s; + const char *filename; + int filenamelen, type; struct tm tm; time_t t; int e; @@ -246,12 +364,15 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us) us->size = -1; us->atime = us->mtime = 0; - if ((s = strrchr(file, '/')) == NULL) - s = file; - else - ++s; + filename = _ftp_filename(file, &filenamelen, &type); - if ((e = _ftp_cmd(conn, "SIZE %s", s)) != FTP_FILE_STATUS) { + if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK) { + _ftp_seterr(e); + return (-1); + } + + e = _ftp_cmd(conn, "SIZE %.*s", filenamelen, filename); + if (e != FTP_FILE_STATUS) { _ftp_seterr(e); return (-1); } @@ -268,7 +389,8 @@ _ftp_stat(conn_t *conn, const char *file, struct url_stat *us) us->size = -1; DEBUG(fprintf(stderr, "size: [%lld]\n", (long long)us->size)); - if ((e = _ftp_cmd(conn, "MDTM %s", s)) != FTP_FILE_STATUS) { + e = _ftp_cmd(conn, "MDTM %.*s", filenamelen, filename); + if (e != FTP_FILE_STATUS) { _ftp_seterr(e); return (-1); } @@ -456,6 +578,8 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file, struct sockaddr_storage sa; struct sockaddr_in6 *sin6; struct sockaddr_in *sin4; + const char *filename; + int filenamelen, type; int low, pasv, verbose; int e, sd = -1; socklen_t l; @@ -472,6 +596,13 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file, pasv = ((s = getenv("FTP_PASSIVE_MODE")) != NULL && strncasecmp(s, "no", 2) != 0); + /* isolate filename */ + filename = _ftp_filename(file, &filenamelen, &type); + + /* set transfer mode and data type */ + if ((e = _ftp_mode_type(conn, 0, type)) != FTP_OK) + goto ouch; + /* find our own address, bind, and listen */ l = sizeof(sa); if (getsockname(conn->sd, (struct sockaddr *)&sa, &l) == -1) @@ -597,7 +728,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file, /* make the server initiate the transfer */ if (verbose) _fetch_info("initiating transfer"); - e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file)); + e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename); if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) goto ouch; @@ -688,7 +819,7 @@ _ftp_transfer(conn_t *conn, const char *oper, const char *file, /* make the server initiate the transfer */ if (verbose) _fetch_info("initiating transfer"); - e = _ftp_cmd(conn, "%s %s", oper, _ftp_filename(file)); + e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename); if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION) goto ouch; @@ -812,14 +943,6 @@ _ftp_connect(struct url *url, struct url *purl, const char *flags) if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN) goto fouch; - /* might as well select mode and type at once */ -#ifdef FTP_FORCE_STREAM_MODE - if ((e = _ftp_cmd(conn, "MODE S")) != FTP_OK) /* default is S */ - goto fouch; -#endif - if ((e = _ftp_cmd(conn, "TYPE I")) != FTP_OK) /* default is A */ - goto fouch; - /* done */ return (conn);