summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libfetch/common.h11
-rw-r--r--lib/libfetch/fetch.342
-rw-r--r--lib/libfetch/fetch.c69
-rw-r--r--lib/libfetch/fetch.h5
-rw-r--r--lib/libfetch/ftp.c290
-rw-r--r--lib/libfetch/http.c197
6 files changed, 365 insertions, 249 deletions
diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h
index 970153a59499..393010b4b820 100644
--- a/lib/libfetch/common.h
+++ b/lib/libfetch/common.h
@@ -60,4 +60,15 @@ int _fetch_add_entry(struct url_ent **p, int *size, int *len,
#define DEBUG(x) do { } while (0)
#endif
+/*
+ * I don't really like exporting _http_request() from http.c, but ftp.c
+ * occasionally needs to use an HTTP proxy, and this saves me from adding
+ * a lot of special-case code to http.c to handle those cases.
+ *
+ * Note that _http_request() frees purl, which is way ugly but saves us a
+ * whole lot of trouble.
+ */
+FILE *_http_request(struct url *URL, char *op, struct url_stat *us,
+ struct url *purl, char *flags);
+
#endif
diff --git a/lib/libfetch/fetch.3 b/lib/libfetch/fetch.3
index 9c4249f71916..664959436694 100644
--- a/lib/libfetch/fetch.3
+++ b/lib/libfetch/fetch.3
@@ -133,10 +133,16 @@ A regular expression which produces this syntax is:
<scheme>:(//(<user>(:<pwd>)?@)?<host>(:<port>)?)?/(<document>)?
.Ed
.Pp
+If the URL does not seem to begin with a scheme name, the following
+syntax is assumed:
+.Bd -literal
+ ((<user>(:<pwd>)?@)?<host>(:<port>)?)?/(<document>)?
+.Ed
+.Pp
Note that some components of the URL are not necessarily relevant to
all URL schemes.
-For instance, the file scheme only needs the <scheme>
-and <document> components.
+For instance, the file scheme only needs the <scheme> and <document>
+components.
.Pp
.Fn fetchMakeURL
and
@@ -416,8 +422,11 @@ and message, e.g. "File is not available (404 Not Found)"
.Sh ENVIRONMENT
.Bl -tag -width HTTP_PROXY_AUTH
.It Ev FTP_PROXY
-host name of the FTP proxy to use, optionally followed by a port
-number separated from the host name by a colon.
+URL of the proxy to use for FTP requests.
+The document part is ignored.
+FTP and HTTP proxies are supported; if no scheme is specified, FTP is
+assumed.
+If the proxy is an FTP proxy,
.Nm libfetch
will send
.Ql user@host
@@ -426,25 +435,30 @@ as user name to the proxy, where
is the real user name, and
.Ql host
is the name of the FTP server.
+.Pp
If this variable is set to an empty string, no proxy will be used for
FTP requests, even if the
.Ev HTTP_PROXY
variable is set.
.It Ev HTTP_AUTH
-Specifies HTTP authorization parameters, used only if the server
-requires authorization and no user name or password was specified in
-the URL.
+Specifies HTTP authorization parameters as a colon-separated list of
+items.
The first and second item are the authorization scheme and realm
respectively; further items are scheme-dependent.
Currently, only basic authorization is supported.
+.Pp
Basic authorization requires two parameters: the user name and
password, in that order.
+.Pp
+This variable is only used if the server requires authorization and
+no user name or password was specified in the URL.
.It Ev HTTP_PROXY
-host name of the HTTP proxy to use, optionally followed by a port
-number separated from the host name by a colon.
+URL of the proxy to use for HTTP requests.
+The document part is ignored.
+Only HTTP proxies are supported for HTTP requests.
If no port number is specified, the default is 3128.
-Note that the HTTP proxy will also be used for FTP documents, unless
-the
+.Pp
+Note that this proxy will also be used for FTP documents, unless the
.Ev FTP_PROXY
variable is set.
.It Ev HTTP_PROXY_AUTH
@@ -452,8 +466,10 @@ Specifies authorization parameters for the HTTP proxy in the same
format as the
.Ev HTTP_AUTH
variable.
-The value of this variable is used if and only if connected to an HTTP
-proxy.
+.Pp
+This variable is used if and only if connected to an HTTP proxy, and
+is ignored if a user and/or a password were specified in the proxy
+URL.
.El
.Sh SEE ALSO
.Xr fetch 1 ,
diff --git a/lib/libfetch/fetch.c b/lib/libfetch/fetch.c
index 3eb8075fc780..b814336285b3 100644
--- a/lib/libfetch/fetch.c
+++ b/lib/libfetch/fetch.c
@@ -75,11 +75,11 @@ fetchXGet(struct url *URL, struct url_stat *us, char *flags)
int direct;
direct = (flags && strchr(flags, 'd'));
- if (strcasecmp(URL->scheme, "file") == 0)
+ if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
return fetchXGetFile(URL, us, flags);
- else if (strcasecmp(URL->scheme, "http") == 0)
+ else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
return fetchXGetHTTP(URL, us, flags);
- else if (strcasecmp(URL->scheme, "ftp") == 0) {
+ else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
return fetchXGetFTP(URL, us, flags);
} else {
_url_seterr(URL_BAD_SCHEME);
@@ -107,11 +107,11 @@ fetchPut(struct url *URL, char *flags)
int direct;
direct = (flags && strchr(flags, 'd'));
- if (strcasecmp(URL->scheme, "file") == 0)
+ if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
return fetchPutFile(URL, flags);
- else if (strcasecmp(URL->scheme, "http") == 0)
+ else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
return fetchPutHTTP(URL, flags);
- else if (strcasecmp(URL->scheme, "ftp") == 0) {
+ else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
return fetchPutFTP(URL, flags);
} else {
_url_seterr(URL_BAD_SCHEME);
@@ -129,11 +129,11 @@ fetchStat(struct url *URL, struct url_stat *us, char *flags)
int direct;
direct = (flags && strchr(flags, 'd'));
- if (strcasecmp(URL->scheme, "file") == 0)
+ if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
return fetchStatFile(URL, us, flags);
- else if (strcasecmp(URL->scheme, "http") == 0)
+ else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
return fetchStatHTTP(URL, us, flags);
- else if (strcasecmp(URL->scheme, "ftp") == 0) {
+ else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
return fetchStatFTP(URL, us, flags);
} else {
_url_seterr(URL_BAD_SCHEME);
@@ -151,11 +151,11 @@ fetchList(struct url *URL, char *flags)
int direct;
direct = (flags && strchr(flags, 'd'));
- if (strcasecmp(URL->scheme, "file") == 0)
+ if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)
return fetchListFile(URL, flags);
- else if (strcasecmp(URL->scheme, "http") == 0)
+ else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)
return fetchListHTTP(URL, flags);
- else if (strcasecmp(URL->scheme, "ftp") == 0) {
+ else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
return fetchListFTP(URL, flags);
} else {
_url_seterr(URL_BAD_SCHEME);
@@ -288,7 +288,7 @@ fetchMakeURL(char *scheme, char *host, int port, char *doc,
/*
* Split an URL into components. URL syntax is:
- * method:[//[user[:pwd]@]host[:port]]/[document]
+ * [method:/][/[user[:pwd]@]host[:port]/][document]
* This almost, but not quite, RFC1738 URL syntax.
*/
struct url *
@@ -305,19 +305,18 @@ fetchParseURL(char *URL)
}
/* scheme name */
- for (i = 0; *URL && (*URL != ':'); URL++)
- if (i < URL_SCHEMELEN)
- u->scheme[i++] = *URL;
- if (!URL[0] || (URL[1] != '/')) {
- _url_seterr(URL_BAD_SCHEME);
- goto ouch;
+ if ((p = strstr(URL, ":/"))) {
+ snprintf(u->scheme, URL_SCHEMELEN+1, "%.*s", p - URL, URL);
+ URL = ++p;
+ /*
+ * Only one slash: no host, leave slash as part of document
+ * Two slashes: host follows, strip slashes
+ */
+ if (URL[1] == '/')
+ URL = (p += 2);
}
- else URL++;
- if (URL[1] != '/') {
- p = URL;
+ if (!*URL || *URL == '/')
goto nohost;
- }
- else URL += 2;
p = strpbrk(URL, "/@");
if (p && *p == '@') {
@@ -368,7 +367,27 @@ nohost:
if (!*p)
p = "/";
- if ((u->doc = strdup(p)) == NULL) {
+ if (strcmp(u->scheme, "http") == 0 || strcmp(u->scheme, "https") == 0) {
+ const char hexnums[] = "0123456789abcdef";
+ char *doc;
+
+ /* Perform %hh encoding of white space. */
+ if ((doc = u->doc = malloc(strlen(p) * 3 + 1)) == NULL) {
+ _fetch_syserr();
+ goto ouch;
+ }
+ while (*p != '\0') {
+ if (!isspace(*p)) {
+ *doc++ = *p++;
+ } else {
+ *doc++ = '%';
+ *doc++ = hexnums[((unsigned int)*p) >> 4];
+ *doc++ = hexnums[((unsigned int)*p) & 0xf];
+ p++;
+ }
+ }
+ *doc = '\0';
+ } else if ((u->doc = strdup(p)) == NULL) {
_fetch_syserr();
goto ouch;
}
diff --git a/lib/libfetch/fetch.h b/lib/libfetch/fetch.h
index 9d7fa9ba1a2b..aedd472cb1a8 100644
--- a/lib/libfetch/fetch.h
+++ b/lib/libfetch/fetch.h
@@ -59,6 +59,11 @@ struct url_ent {
struct url_stat stat;
};
+/* Recognized schemes */
+#define SCHEME_FTP "ftp"
+#define SCHEME_HTTP "http"
+#define SCHEME_FILE "file"
+
/* Error codes */
#define FETCH_ABORT 1
#define FETCH_AUTH 2
diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c
index 78e2de1edf5f..5c28804641c1 100644
--- a/lib/libfetch/ftp.c
+++ b/lib/libfetch/ftp.c
@@ -61,6 +61,7 @@
#include <ctype.h>
#include <errno.h>
+#include <fcntl.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
@@ -304,11 +305,124 @@ _ftp_stat(int cd, char *file, struct url_stat *us)
}
/*
+ * I/O functions for FTP
+ */
+struct ftpio {
+ int csd; /* Control socket descriptor */
+ int dsd; /* Data socket descriptor */
+ int dir; /* Direction */
+ int eof; /* EOF reached */
+ int err; /* Error code */
+};
+
+static int _ftp_readfn(void *, char *, int);
+static int _ftp_writefn(void *, const char *, int);
+static fpos_t _ftp_seekfn(void *, fpos_t, int);
+static int _ftp_closefn(void *);
+
+static int
+_ftp_readfn(void *v, char *buf, int len)
+{
+ struct ftpio *io;
+ int r;
+
+ io = (struct ftpio *)v;
+ if (io->csd == -1 || io->dsd == -1 || io->dir == O_WRONLY) {
+ errno = EBADF;
+ return -1;
+ }
+ if (io->err) {
+ errno = io->err;
+ return -1;
+ }
+ if (io->eof)
+ return 0;
+ r = read(io->dsd, buf, len);
+ if (r > 0)
+ return r;
+ if (r == 0) {
+ io->eof = 1;
+ return _ftp_closefn(v);
+ }
+ io->err = errno;
+ return -1;
+}
+
+static int
+_ftp_writefn(void *v, const char *buf, int len)
+{
+ struct ftpio *io;
+ int w;
+
+ io = (struct ftpio *)v;
+ if (io->csd == -1 || io->dsd == -1 || io->dir == O_RDONLY) {
+ errno = EBADF;
+ return -1;
+ }
+ if (io->err) {
+ errno = io->err;
+ return -1;
+ }
+ w = write(io->dsd, buf, len);
+ if (w >= 0)
+ return w;
+ io->err = errno;
+ return -1;
+}
+
+static fpos_t
+_ftp_seekfn(void *v, fpos_t pos, int whence)
+{
+ errno = ESPIPE;
+ return -1;
+}
+
+static int
+_ftp_closefn(void *v)
+{
+ struct ftpio *io;
+
+ io = (struct ftpio *)v;
+ if (io->dir == -1)
+ return 0;
+ if (io->csd == -1 || io->dsd == -1) {
+ errno = EBADF;
+ return -1;
+ }
+ io->err = _ftp_chkerr(io->csd);
+ io->dir = -1;
+ if (close(io->dsd) == -1)
+ return -1;
+ io->dsd = -1;
+ close(io->csd);
+ io->csd = -1;
+ return io->err ? -1 : 0;
+}
+
+static FILE *
+_ftp_setup(int csd, int dsd, int mode)
+{
+ struct ftpio *io;
+ FILE *f;
+
+ if ((io = malloc(sizeof *io)) == NULL)
+ return NULL;
+ io->csd = dup(csd);
+ io->dsd = dsd;
+ io->dir = mode;
+ io->eof = io->err = 0;
+ f = funopen(io, _ftp_readfn, _ftp_writefn, _ftp_seekfn, _ftp_closefn);
+ if (f == NULL)
+ free(io);
+ return f;
+}
+
+/*
* Transfer file
*/
static FILE *
_ftp_transfer(int cd, char *oper, char *file,
- char *mode, off_t offset, char *flags)
+ int mode, off_t offset, char *flags)
{
struct sockaddr_storage sin;
struct sockaddr_in6 *sin6;
@@ -325,8 +439,9 @@ _ftp_transfer(int cd, char *oper, char *file,
verbose = (flags && strchr(flags, 'v'));
/* passive mode */
- if (!pasv && (s = getenv("FTP_PASSIVE_MODE")) != NULL)
- pasv = (strncasecmp(s, "no", 2) != 0);
+ if (!pasv)
+ pasv = ((s = getenv("FTP_PASSIVE_MODE")) == NULL ||
+ strncasecmp(s, "no", 2) != 0);
/* find our own address, bind, and listen */
l = sizeof sin;
@@ -553,7 +668,7 @@ _ftp_transfer(int cd, char *oper, char *file,
sd = d;
}
- if ((df = fdopen(sd, mode)) == NULL)
+ if ((df = _ftp_setup(cd, sd, mode)) == NULL)
goto sysouch;
return df;
@@ -579,7 +694,7 @@ _ftp_default_port(void)
{
struct servent *se;
- if ((se = getservbyname("ftp", "tcp")) != NULL)
+ if ((se = getservbyname(SCHEME_FTP, "tcp")) != NULL)
return ntohs(se->s_port);
return FTP_DEFAULT_PORT;
}
@@ -588,16 +703,16 @@ _ftp_default_port(void)
* Log on to FTP server
*/
static int
-_ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
+_ftp_connect(struct url *url, struct url *purl, char *flags)
{
- int cd, e, pp = 0, direct, verbose;
+ int cd, e, direct, verbose;
#ifdef INET6
int af = AF_UNSPEC;
#else
int af = AF_INET;
#endif
- char *p, *q;
const char *logname;
+ char *user, *pwd;
char localhost[MAXHOSTNAMELEN];
char pbuf[MAXHOSTNAMELEN + MAXLOGNAME + 1];
@@ -608,43 +723,17 @@ _ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
else if ((flags && strchr(flags, '6')))
af = AF_INET6;
+ if (direct)
+ purl = NULL;
+
/* check for proxy */
- if (!direct && (p = getenv("FTP_PROXY")) != NULL && *p) {
- char c = 0;
-
-#ifdef INET6
- if (*p != '[' || (q = strchr(p + 1, ']')) == NULL ||
- (*++q != '\0' && *q != ':'))
-#endif
- q = strchr(p, ':');
- if (q != NULL && *q == ':') {
- if (strspn(q+1, "0123456789") != strlen(q+1) || strlen(q+1) > 5) {
- /* XXX we should emit some kind of warning */
- }
- pp = atoi(q+1);
- if (pp < 1 || pp > 65535) {
- /* XXX we should emit some kind of warning */
- }
- }
- if (!pp)
- pp = _ftp_default_port();
- if (q) {
-#ifdef INET6
- if (q > p && *p == '[' && *(q - 1) == ']') {
- p++;
- q--;
- }
-#endif
- c = *q;
- *q = 0;
- }
- cd = _fetch_connect(p, pp, af, verbose);
- if (q)
- *q = c;
+ if (purl) {
+ /* XXX proxy authentication! */
+ cd = _fetch_connect(purl->host, purl->port, af, verbose);
} else {
/* no proxy, go straight to target */
- cd = _fetch_connect(host, port, af, verbose);
- p = NULL;
+ cd = _fetch_connect(url->host, url->port, af, verbose);
+ purl = NULL;
}
/* check connection */
@@ -656,19 +745,23 @@ _ftp_connect(char *host, int port, char *user, char *pwd, char *flags)
/* expect welcome message */
if ((e = _ftp_chkerr(cd)) != FTP_SERVICE_READY)
goto fouch;
+
+ /* XXX FTP_AUTH, and maybe .netrc */
/* send user name and password */
+ user = url->user;
if (!user || !*user)
user = FTP_ANONYMOUS_USER;
- if (p && port == FTP_DEFAULT_PORT)
- e = _ftp_cmd(cd, "USER %s@%s", user, host);
- else if (p)
- e = _ftp_cmd(cd, "USER %s@%s@%d", user, host, port);
+ if (purl && url->port == FTP_DEFAULT_PORT)
+ e = _ftp_cmd(cd, "USER %s@%s", user, url->host);
+ else if (purl)
+ e = _ftp_cmd(cd, "USER %s@%s@%d", user, url->host, url->port);
else
e = _ftp_cmd(cd, "USER %s", user);
/* did the server request a password? */
if (e == FTP_NEED_PASSWORD) {
+ pwd = url->pwd;
if (!pwd || !*pwd)
pwd = getenv("FTP_PASSWORD");
if (!pwd || !*pwd) {
@@ -734,7 +827,7 @@ _ftp_isconnected(struct url *url)
* Check the cache, reconnect if no luck
*/
static int
-_ftp_cached_connect(struct url *url, char *flags)
+_ftp_cached_connect(struct url *url, struct url *purl, char *flags)
{
int e, cd;
@@ -748,32 +841,40 @@ _ftp_cached_connect(struct url *url, char *flags)
if (_ftp_isconnected(url)) {
e = _ftp_cmd(cached_socket, "NOOP");
if (e == FTP_OK || e == FTP_SYNTAX_ERROR)
- cd = cached_socket;
+ return cached_socket;
}
/* connect to server */
- if (cd == -1) {
- cd = _ftp_connect(url->host, url->port, url->user, url->pwd, flags);
- if (cd == -1)
- return -1;
- if (cached_socket)
- _ftp_disconnect(cached_socket);
- cached_socket = cd;
- memcpy(&cached_host, url, sizeof *url);
- }
-
+ if ((cd = _ftp_connect(url, purl, flags)) == -1)
+ return -1;
+ if (cached_socket)
+ _ftp_disconnect(cached_socket);
+ cached_socket = cd;
+ memcpy(&cached_host, url, sizeof *url);
return cd;
}
/*
- * Check to see if we should use an HTTP proxy instead
+ * Check the proxy settings
*/
-static int
-_ftp_use_http_proxy(void)
+static struct url *
+_ftp_get_proxy(void)
{
+ struct url *purl;
char *p;
-
- return ((p = getenv("HTTP_PROXY")) && *p && !getenv("FTP_PROXY"));
+
+ if (((p = getenv("FTP_PROXY")) || (p = getenv("HTTP_PROXY"))) &&
+ *p && (purl = fetchParseURL(p)) != NULL) {
+ if (!*purl->scheme)
+ strcpy(purl->scheme, SCHEME_FTP);
+ if (!purl->port)
+ purl->port = _ftp_default_port();
+ if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||
+ strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
+ return purl;
+ fetchFreeURL(purl);
+ }
+ return NULL;
}
/*
@@ -782,13 +883,22 @@ _ftp_use_http_proxy(void)
FILE *
fetchXGetFTP(struct url *url, struct url_stat *us, char *flags)
{
+ struct url *purl;
int cd;
- if (_ftp_use_http_proxy())
- return fetchXGetHTTP(url, us, flags);
+ /* get the proxy URL, and check if we should use HTTP instead */
+ if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) {
+ if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
+ return _http_request(url, "GET", us, purl, flags);
+ } else {
+ purl = NULL;
+ }
/* connect to server */
- if ((cd = _ftp_cached_connect(url, flags)) == NULL)
+ cd = _ftp_cached_connect(url, purl, flags);
+ if (purl)
+ fetchFreeURL(purl);
+ if (cd == NULL)
return NULL;
/* change directory */
@@ -802,7 +912,7 @@ fetchXGetFTP(struct url *url, struct url_stat *us, char *flags)
return NULL;
/* initiate the transfer */
- return _ftp_transfer(cd, "RETR", url->doc, "r", url->offset, flags);
+ return _ftp_transfer(cd, "RETR", url->doc, O_RDONLY, url->offset, flags);
}
/*
@@ -820,13 +930,23 @@ fetchGetFTP(struct url *url, char *flags)
FILE *
fetchPutFTP(struct url *url, char *flags)
{
+ struct url *purl;
int cd;
- if (_ftp_use_http_proxy())
- return fetchPutHTTP(url, flags);
+ /* get the proxy URL, and check if we should use HTTP instead */
+ if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) {
+ if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
+ /* XXX HTTP PUT is not implemented, so try without the proxy */
+ purl = NULL;
+ } else {
+ purl = NULL;
+ }
/* connect to server */
- if ((cd = _ftp_cached_connect(url, flags)) == NULL)
+ cd = _ftp_cached_connect(url, purl, flags);
+ if (purl)
+ fetchFreeURL(purl);
+ if (cd == NULL)
return NULL;
/* change directory */
@@ -835,7 +955,7 @@ fetchPutFTP(struct url *url, char *flags)
/* initiate the transfer */
return _ftp_transfer(cd, (flags && strchr(flags, 'a')) ? "APPE" : "STOR",
- url->doc, "w", url->offset, flags);
+ url->doc, O_WRONLY, url->offset, flags);
}
/*
@@ -844,15 +964,30 @@ fetchPutFTP(struct url *url, char *flags)
int
fetchStatFTP(struct url *url, struct url_stat *us, char *flags)
{
+ struct url *purl;
int cd;
- if (_ftp_use_http_proxy())
- return fetchStatHTTP(url, us, flags);
+ /* get the proxy URL, and check if we should use HTTP instead */
+ if (!strchr(flags, 'd') && (purl = _ftp_get_proxy()) != NULL) {
+ if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {
+ FILE *f;
+
+ if ((f = _http_request(url, "HEAD", us, purl, flags)) == NULL)
+ return -1;
+ fclose(f);
+ return 0;
+ }
+ } else {
+ purl = NULL;
+ }
/* connect to server */
- if ((cd = _ftp_cached_connect(url, flags)) == NULL)
- return -1;
-
+ cd = _ftp_cached_connect(url, purl, flags);
+ if (purl)
+ fetchFreeURL(purl);
+ if (cd == NULL)
+ return NULL;
+
/* change directory */
if (_ftp_cwd(cd, url->doc) == -1)
return -1;
@@ -868,9 +1003,6 @@ extern void warnx(char *, ...);
struct url_ent *
fetchListFTP(struct url *url, char *flags)
{
- if (_ftp_use_http_proxy())
- return fetchListHTTP(url, flags);
-
warnx("fetchListFTP(): not implemented");
return NULL;
}
diff --git a/lib/libfetch/http.c b/lib/libfetch/http.c
index 527be48e3460..a386364f6a80 100644
--- a/lib/libfetch/http.c
+++ b/lib/libfetch/http.c
@@ -618,151 +618,66 @@ _http_default_port(char *scheme)
if ((se = getservbyname(scheme, "tcp")) != NULL)
return ntohs(se->s_port);
- if (strcasecmp(scheme, "ftp") == 0)
+ if (strcasecmp(scheme, SCHEME_FTP) == 0)
return FTP_DEFAULT_PORT;
- if (strcasecmp(scheme, "http") == 0)
+ if (strcasecmp(scheme, SCHEME_HTTP) == 0)
return HTTP_DEFAULT_PORT;
return 0;
}
/*
- * Connect to the specified HTTP proxy server.
- */
-static int
-_http_proxy_connect(char *proxy, int af, int verbose)
-{
- char *hostname, *p;
- int fd, port;
-
- /* get hostname */
- hostname = NULL;
-#ifdef INET6
- /* host part can be an IPv6 address enclosed in square brackets */
- if (*proxy == '[') {
- if ((p = strchr(proxy, ']')) == NULL) {
- /* no terminating bracket */
- /* XXX should set an error code */
- goto ouch;
- }
- if (p[1] != '\0' && p[1] != ':') {
- /* garbage after address */
- /* XXX should set an error code */
- goto ouch;
- }
- if ((hostname = malloc(p - proxy)) == NULL) {
- errno = ENOMEM;
- _fetch_syserr();
- goto ouch;
- }
- strncpy(hostname, proxy + 1, p - proxy - 1);
- hostname[p - proxy - 1] = '\0';
- ++p;
- } else {
-#endif /* INET6 */
- if ((p = strchr(proxy, ':')) == NULL)
- p = strchr(proxy, '\0');
- if ((hostname = malloc(p - proxy + 1)) == NULL) {
- errno = ENOMEM;
- _fetch_syserr();
- goto ouch;
- }
- strncpy(hostname, proxy, p - proxy);
- hostname[p - proxy] = '\0';
-#ifdef INET6
- }
-#endif /* INET6 */
- DEBUG(fprintf(stderr, "proxy name: [%s]\n", hostname));
-
- /* get port number */
- port = 0;
- if (*p == ':') {
- ++p;
- if (strspn(p, "0123456789") != strlen(p) || strlen(p) > 5) {
- /* port number is non-numeric or too long */
- /* XXX should set an error code */
- goto ouch;
- }
- port = atoi(p);
- if (port < 1 || port > 65535) {
- /* port number is out of range */
- /* XXX should set an error code */
- goto ouch;
- }
- }
-
- if (!port) {
-#if 0
- /*
- * commented out, since there is currently no service name
- * for HTTP proxies
- */
- struct servent *se;
-
- if ((se = getservbyname("xxxx", "tcp")) != NULL)
- port = ntohs(se->s_port);
- else
-#endif
- port = 3128;
- }
- DEBUG(fprintf(stderr, "proxy port: %d\n", port));
-
- /* connect */
- if ((fd = _fetch_connect(hostname, port, af, verbose)) == -1)
- _fetch_syserr();
- return fd;
-
- ouch:
- if (hostname)
- free(hostname);
- return -1;
-}
-
-/*
* Connect to the correct HTTP server or proxy.
*/
static int
-_http_connect(struct url *URL, int *proxy, char *flags)
+_http_connect(struct url *URL, struct url *purl, char *flags)
{
- int direct, verbose;
+ int verbose;
int af, fd;
- char *p;
#ifdef INET6
af = AF_UNSPEC;
#else
af = AF_INET;
#endif
-
- direct = (flags && strchr(flags, 'd'));
+
verbose = (flags && strchr(flags, 'v'));
if (flags && strchr(flags, '4'))
af = AF_INET;
+#ifdef INET6
else if (flags && strchr(flags, '6'))
af = AF_INET6;
+#endif
+
+ if (purl) {
+ URL = purl;
+ } else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0) {
+ /* can't talk http to an ftp server */
+ /* XXX should set an error code */
+ return -1;
+ }
- /* check port */
- if (!URL->port)
- URL->port = _http_default_port(URL->scheme);
+ if ((fd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1)
+ /* _fetch_connect() has already set an error code */
+ return -1;
+ return fd;
+}
+
+static struct url *
+_http_get_proxy()
+{
+ struct url *purl;
+ char *p;
- if (!direct && (p = getenv("HTTP_PROXY")) != NULL && *p != '\0') {
- /* attempt to connect to proxy server */
- if ((fd = _http_proxy_connect(p, af, verbose)) == -1)
- return -1;
- *proxy = 1;
- } else {
- /* if no proxy is configured, try direct */
- if (strcasecmp(URL->scheme, "ftp") == 0) {
- /* can't talk http to an ftp server */
- /* XXX should set an error code */
- return -1;
- }
- if ((fd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1)
- /* _fetch_connect() has already set an error code */
- return -1;
- *proxy = 0;
+ if ((p = getenv("HTTP_PROXY")) && (purl = fetchParseURL(p))) {
+ if (!*purl->scheme)
+ strcpy(purl->scheme, SCHEME_HTTP);
+ if (!purl->port)
+ purl->port = _http_default_port(SCHEME_HTTP);
+ if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)
+ return purl;
+ fetchFreeURL(purl);
}
-
- return fd;
+ return NULL;
}
@@ -773,11 +688,12 @@ _http_connect(struct url *URL, int *proxy, char *flags)
/*
* Send a request and process the reply
*/
-static FILE *
-_http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
+FILE *
+_http_request(struct url *URL, char *op, struct url_stat *us,
+ struct url *purl, char *flags)
{
struct url *url, *new;
- int chunked, need_auth, noredirect, proxy, verbose;
+ int chunked, direct, need_auth, noredirect, verbose;
int code, fd, i, n;
off_t offset, clength, length, size;
time_t mtime;
@@ -789,9 +705,15 @@ _http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
char hbuf[MAXHOSTNAMELEN + 1];
#endif
+ direct = (flags && strchr(flags, 'd'));
noredirect = (flags && strchr(flags, 'A'));
verbose = (flags && strchr(flags, 'v'));
+ if (direct && purl) {
+ fetchFreeURL(purl);
+ purl = NULL;
+ }
+
/* try the provided URL first */
url = URL;
@@ -809,8 +731,12 @@ _http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
size = -1;
mtime = 0;
retry:
+ /* check port */
+ if (!url->port)
+ url->port = _http_default_port(url->scheme);
+
/* connect to server or proxy */
- if ((fd = _http_connect(url, &proxy, flags)) == -1)
+ if ((fd = _http_connect(url, purl, flags)) == -1)
goto ouch;
host = url->host;
@@ -825,7 +751,7 @@ _http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
if (verbose)
_fetch_info("requesting %s://%s:%d%s",
url->scheme, host, url->port, url->doc);
- if (proxy) {
+ if (purl) {
_http_cmd(fd, "%s %s://%s:%d%s HTTP/1.1",
op, url->scheme, host, url->port, url->doc);
} else {
@@ -834,15 +760,18 @@ _http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
}
/* proxy authorization */
- if (proxy && (p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')
- _http_authorize(fd, "Proxy-Authorization", p);
+ if (purl) {
+ if (*purl->user || *purl->pwd)
+ _http_basic_auth(fd, "Proxy-Authorization",
+ purl->user, purl->pwd);
+ else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')
+ _http_authorize(fd, "Proxy-Authorization", p);
+ }
/* server authorization */
if (need_auth) {
if (*url->user || *url->pwd)
- _http_basic_auth(fd, "Authorization",
- url->user ? url->user : "",
- url->pwd ? url->pwd : "");
+ _http_basic_auth(fd, "Authorization", url->user, url->pwd);
else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0')
_http_authorize(fd, "Authorization", p);
else {
@@ -1023,12 +952,16 @@ _http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
if (url != URL)
fetchFreeURL(url);
+ if (purl)
+ fetchFreeURL(purl);
return f;
ouch:
if (url != URL)
fetchFreeURL(url);
+ if (purl)
+ fetchFreeURL(purl);
if (fd != -1)
close(fd);
return NULL;
@@ -1045,7 +978,7 @@ _http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
FILE *
fetchXGetHTTP(struct url *URL, struct url_stat *us, char *flags)
{
- return _http_request(URL, "GET", us, flags);
+ return _http_request(URL, "GET", us, _http_get_proxy(), flags);
}
/*
@@ -1075,7 +1008,7 @@ fetchStatHTTP(struct url *URL, struct url_stat *us, char *flags)
{
FILE *f;
- if ((f = _http_request(URL, "HEAD", us, flags)) == NULL)
+ if ((f = _http_request(URL, "HEAD", us, _http_get_proxy(), flags)) == NULL)
return -1;
fclose(f);
return 0;