diff options
Diffstat (limited to 'lib/libfetch')
-rw-r--r-- | lib/libfetch/Makefile | 50 | ||||
-rw-r--r-- | lib/libfetch/README | 10 | ||||
-rw-r--r-- | lib/libfetch/common.c | 242 | ||||
-rw-r--r-- | lib/libfetch/common.h | 57 | ||||
-rw-r--r-- | lib/libfetch/fetch.3 | 367 | ||||
-rw-r--r-- | lib/libfetch/fetch.c | 277 | ||||
-rw-r--r-- | lib/libfetch/fetch.h | 85 | ||||
-rw-r--r-- | lib/libfetch/fetch_err.et | 50 | ||||
-rw-r--r-- | lib/libfetch/file.c | 79 | ||||
-rw-r--r-- | lib/libfetch/ftp.c | 509 | ||||
-rw-r--r-- | lib/libfetch/ftp.errors | 44 | ||||
-rw-r--r-- | lib/libfetch/http.c | 461 | ||||
-rw-r--r-- | lib/libfetch/http.errors | 41 |
13 files changed, 2272 insertions, 0 deletions
diff --git a/lib/libfetch/Makefile b/lib/libfetch/Makefile new file mode 100644 index 0000000..0be36ff --- /dev/null +++ b/lib/libfetch/Makefile @@ -0,0 +1,50 @@ +# $Id: Makefile,v 1.10 1998/12/16 10:24:52 des Exp $ + +LIB= fetch +CFLAGS+= -I. -Wall -pedantic +.if !defined(DEBUG) +CFLAGS+= -DNDEBUG +.endif +SRCS= fetch.c common.c ftp.c http.c file.c fetch_err.c \ + fetch_err.h ftperr.h httperr.h +MAN3= fetch.3 +CLEANFILES= fetch_err.c fetch_err.h ftperr.h httperr.h + +SHLIB_MAJOR= 1 +SHLIB_MINOR= 0 + +beforeinstall: + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/fetch.h \ + ${DESTDIR}/usr/include + ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 fetch_err.h \ + ${DESTDIR}/usr/include + +ftperr.h: ftp.errors + @echo "static struct fetcherr _ftp_errlist[] = {" > ${.TARGET} + @cat ${.ALLSRC} \ + | grep -v ^# \ + | sort \ + | while read NUM CAT STRING; do \ + echo " { $${NUM}, FETCH_$${CAT}, \"$${STRING}\" },"; \ + done >> ${.TARGET} + @echo " { -1, FETCH_UNKNOWN, \"Unknown FTP error\" }" >> ${.TARGET} + @echo "};" >> ${.TARGET} + +httperr.h: http.errors + @echo "static struct fetcherr _http_errlist[] = {" > ${.TARGET} + @cat ${.ALLSRC} \ + | grep -v ^# \ + | sort \ + | while read NUM CAT STRING; do \ + echo " { $${NUM}, FETCH_$${CAT}, \"$${STRING}\" },"; \ + done >> ${.TARGET} + @echo " { -1, FETCH_UNKNOWN, \"Unknown HTTP error\" }" >> ${.TARGET} + @echo "};" >> ${.TARGET} + +hdrs: fetch_err.h + +.ORDER: fetch_err.c fetch_err.h +fetch_err.c fetch_err.h: fetch_err.et + compile_et ${.ALLSRC} + +.include <bsd.lib.mk> diff --git a/lib/libfetch/README b/lib/libfetch/README new file mode 100644 index 0000000..7b85376 --- /dev/null +++ b/lib/libfetch/README @@ -0,0 +1,10 @@ +This is the new fetch(3) library, which is to replace the ftpio(3) +library and provide a new, unified backend for all fetch(1), +pkg_add(1) and sysinstall(8). + +Note that this is very much work in progress. It compiles (with a few +warnings), but there is much left to be implemented. Comments, patches +etc. of all kinds are welcome, but please don't commit anything +without talking to me first. + + -- Dag-Erling C. Smørgrav (des@FreeBSD.org) diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c new file mode 100644 index 0000000..a04cb0b --- /dev/null +++ b/lib/libfetch/common.c @@ -0,0 +1,242 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: common.c,v 1.3 1998/12/16 10:24:52 des Exp $ + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <com_err.h> +#include <errno.h> +#include <netdb.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "fetch.h" +#include "common.h" + + +/*** Local data **************************************************************/ + +/* + * Error messages for resolver errors + */ +static struct fetcherr _netdb_errlist[] = { + { HOST_NOT_FOUND, FETCH_RESOLV, "Host not found" }, + { TRY_AGAIN, FETCH_TEMP, "Transient resolver failure" }, + { NO_RECOVERY, FETCH_RESOLV, "Non-recoverable resolver failure" }, + { NO_DATA, FETCH_RESOLV, "No address record" }, + { -1, FETCH_UNKNOWN, "Unknown resolver error" } +}; + +static int com_err_initialized; + +/*** Error-reporting functions ***********************************************/ + +/* + * Initialize the common error library + */ +static void +_fetch_init_com_err(void) +{ + initialize_ftch_error_table(); + com_err_initialized = 1; +} + +/* + * Map error code to string + */ +static int +_fetch_finderr(struct fetcherr *p, int e) +{ + int i; + for (i = 0; p[i].num != -1; i++) + if (p[i].num == e) + break; + return i; +} + +/* + * Set error code + */ +void +_fetch_seterr(struct fetcherr *p, int e) +{ + int n; + + if (!com_err_initialized) + _fetch_init_com_err(); + + n = _fetch_finderr(p, e); + fetchLastErrCode = p[n].cat; + com_err("libfetch", fetchLastErrCode, "(%03d %s)", e, p[n].string); +} + +/* + * Set error code according to errno + */ +void +_fetch_syserr(void) +{ + int e; + e = errno; + + if (!com_err_initialized) + _fetch_init_com_err(); + + switch (errno) { + case 0: + fetchLastErrCode = FETCH_OK; + break; + case EPERM: + case EACCES: + case EROFS: + case EAUTH: + case ENEEDAUTH: + fetchLastErrCode = FETCH_AUTH; + break; + case ENOENT: + case EISDIR: /* XXX */ + fetchLastErrCode = FETCH_UNAVAIL; + break; + case ENOMEM: + fetchLastErrCode = FETCH_MEMORY; + break; + case EBUSY: + case EAGAIN: + fetchLastErrCode = FETCH_TEMP; + break; + case EEXIST: + fetchLastErrCode = FETCH_EXISTS; + break; + case ENOSPC: + fetchLastErrCode = FETCH_FULL; + break; + case EADDRINUSE: + case EADDRNOTAVAIL: + case ENETDOWN: + case ENETUNREACH: + case ENETRESET: + case EHOSTUNREACH: + fetchLastErrCode = FETCH_NETWORK; + break; + case ECONNABORTED: + case ECONNRESET: + fetchLastErrCode = FETCH_ABORT; + break; + case ETIMEDOUT: + fetchLastErrCode = FETCH_TIMEOUT; + break; + case ECONNREFUSED: + case EHOSTDOWN: + fetchLastErrCode = FETCH_DOWN; + break; + default: + fetchLastErrCode = FETCH_UNKNOWN; + } + com_err("libfetch", fetchLastErrCode, "(%03d %s)", e, strerror(e)); +} + + +/* + * Emit status message + */ +int +_fetch_info(char *fmt, ...) +{ + va_list ap; + char *s; + + if (!com_err_initialized) + _fetch_init_com_err(); + + va_start(ap, fmt); + vasprintf(&s, fmt, ap); + va_end(ap); + + if (s == NULL) { + com_err("libfetch", FETCH_MEMORY, ""); + return -1; + } else { + com_err("libfetch", FETCH_VERBOSE, "%s", s); + free(s); + return 0; + } +} + + +/*** Network-related utility functions ***************************************/ + +/* + * Establish a TCP connection to the specified port on the specified host. + */ +int +_fetch_connect(char *host, int port, int verbose) +{ + struct sockaddr_in sin; + struct hostent *he; + int sd; + +#ifndef NDEBUG + fprintf(stderr, "\033[1m---> %s:%d\033[m\n", host, port); +#endif + + if (verbose) + _fetch_info("looking up %s", host); + + /* look up host name */ + if ((he = gethostbyname(host)) == NULL) { + _netdb_seterr(h_errno); + return -1; + } + + if (verbose) + _fetch_info("connecting to %s:%d", host, port); + + /* set up socket address structure */ + bzero(&sin, sizeof(sin)); + bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length); + sin.sin_family = he->h_addrtype; + sin.sin_port = htons(port); + + /* try to connect */ + if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + _fetch_syserr(); + return -1; + } + if (connect(sd, (struct sockaddr *)&sin, sizeof sin) == -1) { + _fetch_syserr(); + close(sd); + return -1; + } + + return sd; +} diff --git a/lib/libfetch/common.h b/lib/libfetch/common.h new file mode 100644 index 0000000..eb34033 --- /dev/null +++ b/lib/libfetch/common.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: common.h,v 1.3 1998/12/16 10:24:53 des Exp $ + */ + +#ifndef _COMMON_H_INCLUDED +#define _COMMON_H_INCLUDED + +/* Structure used for error message lists */ +#define ERRCAT_ +struct fetcherr { + const int num, cat; + const char *string; +}; + +void _fetch_seterr(struct fetcherr *, int); +void _fetch_syserr(void); +int _fetch_info(char *fmt, ...); +int _fetch_connect(char *, int, int); + +#define _ftp_seterr(n) _fetch_seterr(_ftp_errlist, n) +#define _http_seterr(n) _fetch_seterr(_http_errlist, n) +#define _netdb_seterr(n) _fetch_seterr(_netdb_errlist, n) +#define _url_seterr(n) _fetch_seterr(_url_errlist, n) + +#ifndef NDEBUG +#define DEBUG(x) do x; while (0) +#else +#define DEBUG(x) do { } while (0) +#endif + +#endif diff --git a/lib/libfetch/fetch.3 b/lib/libfetch/fetch.3 new file mode 100644 index 0000000..0c6aa7d --- /dev/null +++ b/lib/libfetch/fetch.3 @@ -0,0 +1,367 @@ +.\" Copyright (c) 1998 Dag-Erling Coïdan Smørgrav +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: fetch.3,v 1.5 1998/12/16 10:24:54 des Exp $ +.\" +.Dd July 1, 1998 +.Dt FETCH 3 +.Os +.Sh NAME +.Nm fetchGetURL , +.Nm fetchPutURL , +.Nm fetchStatURL , +.Nm fetchParseURL , +.Nm fetchGet , +.Nm fetchPut , +.Nm fetchStat , +.Nm fetchGetFile , +.Nm fetchPutFile , +.Nm fetchStatFile , +.Nm fetchGetHTTP , +.Nm fetchPutHTTP , +.Nm fetchStatHTTP , +.Nm fetchGetFTP , +.Nm fetchPutFTP +.Nm fetchStatFTP +.Nd file transfer library +.Sh SYNOPSIS +.Fd #include <sys/param.h> +.Fd #include <stdio.h> +.Fd #include <fetch.h> +.Ft FILE * +.Fn fetchGetURL "char *URL" "char *flags" +.Ft FILE * +.Fn fetchPutURL "char *URL" "char *flags" +.Ft int +.Fn fetchStatURL "char *URL" "struct url_stat *us" "char *flags" +.Ft struct url * +.Fn fetchParseURL "char *URL" "char *flags" +.Ft FILE * +.Fn fetchGet "struct url *URL" "char *flags" +.Ft FILE * +.Fn fetchPut "struct url *URL" "char *flags" +.Ft int +.Fn fetchStat "struct url *URL" "struct url_stat *us" "char *flags" +.Ft FILE * +.Fn fetchGetFile "struct url *u" "char *flags" +.Ft FILE * +.Fn fetchPutFile "struct url *u" "char *flags" +.Ft int +.Fn fetchStatFile "struct url *URL" "struct url_stat *us" "char *flags" +.Ft FILE * +.Fn fetchGetHTTP "struct url *u" "char *flags" +.Ft FILE * +.Fn fetchPutHTTP "struct url *u" "char *flags" +.Ft int +.Fn fetchStatHTTP "struct url *URL" "struct url_stat *us" "char *flags" +.Ft FILE * +.Fn fetchGetFTP "struct url *u" "char *flags" +.Ft FILE * +.Fn fetchPutFTP "struct url *u" "char *flags" +.Ft int +.Fn fetchStatFTP "struct url *URL" "struct url_stat *us" "char *flags" +.Sh DESCRIPTION +.Pp +These functions implement a high-level library for retrieving and +uploading files using Uniform Resource Locators (URLs). +.Pp +.Fn fetchGetURL +and +.Fn fetchPutURL +constitute the recommended interface to the +.Nm fetch +library. They examine the URL passed to them to determine the transfer +method, and call the appropriate lower-level functions to perform the +actual transfer. The +.Fa flags +argument is a string of characters which specify transfer options. The +meaning of the individual flags is scheme-dependent, and is detailed +in the appropriate section below. +.Pp +.Fn fetchStatURL +attempts to obtain the requested document's metadata and fill in the +structure pointed to by it's second argument. The +.Fa url_stat +structure is defined as follows in +.Aq Pa fetch.h : +.Bd -literal +struct url_stat { + off_t size; + time_t atime; + time_t mtime; +}; +.Ed +.Pp +.Fn fetchParseURL +takes a URL in the form of a null-terminated string and splits it into +its components function according to the Common Internet Scheme Syntax +detailed in RFC1738. A regular expression which produces this syntax +is: +.Bd -literal + <scheme>:(//(<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. +.Pp +The pointer returned by +.Fn fetchParseURL +should be freed using +.Fn free . +.Pp +.Fn fetchGet , +.Fn fetchPut +and +.Fn fetchStat +are similar to +.Fn fetchGetURL , +.Fn fetchPutURL +and +.Fn fetchStatURL , +except that they expect a pre-parsed URL in the form of a pointer to +a +.Fa struct url +rather than a string. +.Pp +All of the +.Fn fetchGetXXX +and +.Fn fetchPutXXX +functions return a pointer to a stream which can be used to read or +write data from or to the requested document, respectively. Note that +although the implementation details of the individual access methods +vary, it can generally be assumed that a stream returned by one of the +.Fn fetchGetXXX +functions is read-only, and that a stream returned by one of the +.Fn fetchPutXXX +functions is write-only. +.Sh FILE SCHEME +.Fn fetchGetFile +and +.Fn fetchPutFile +provide access to documents which are files in a locally mounted file +system. Only the <document> component of the URL is used. +.Pp +.Fn fetchGetFile +does not accept any flags. +.Pp +.Fn fetchPutFile +accepts the +.Fa a +(append to file) flag. If that flag is specified, the data written to +the stream returned by +.Fn fetchPutFile +will be appended to the previous contents of the file, instead of +replacing them. +.Sh FTP SCHEME +.Fn fetchGetFTP +and +.Fn fetchPutFTP +implement the FTP protocol as described in RFC959. +.Pp +If the +.Fa p +(passive) flag is specified, a passive (rather than active) connection +will be attempted. +.Pp +If no user name or password is given, the +.Nm fetch +library will attempt an anonymous login, with user name "ftp" and +password "ftp". +.Sh HTTP SCHEME +The +.Fn fetchGetHTTP +and +.Fn fetchPutHTTP +functions implement the HTTP/1.1 protocol. With a little luck, there's +even a chance that they comply with RFC2068. +.Pp +Since there seems to be no good way of implementing the HTTP PUT +method in a manner consistent with the rest of the +.Nm fetch +library, +.Fn fetchPutHTTP +is currently unimplemented. +.Sh RETURN VALUES +.Fn fetchParseURL +returns a pointer to a +.Fa struct url +containing the individual components of the URL. If it is +unable to allocate memory, or the URL is syntactically incorrect, +.Fn fetchParseURL +returns a NULL pointer. +.Pp +The +.Fn fetchStat +functions return 0 on success and -1 on failure. +.Pp +All other functions return a stream pointer which may be used to +access the requested document, or NULL if an error occurred. +.Pp +.Nm Libfetch +uses the Common Error Library +.Nm ( libcom_err ) +to report errors. The error code passed to +.Fn com_err +is one of: +.Bl -tag -width Er +.It Bq Er FETCH_ABORT +Operation aborted +.It Bq Er FETCH_AUTH +Authentication failed +.It Bq Er FETCH_DOWN +Service unavailable +.It Bq Er FETCH_EXISTS +File exists +.It Bq Er FETCH_FULL +File system full +.It Bq Er FETCH_INFO +Informational response +.It Bq Er FETCH_MEMORY +Insufficient memory +.It Bq Er FETCH_MOVED +File has moved +.It Bq Er FETCH_NETWORK +Network error +.It Bq Er FETCH_OK +No error +.It Bq Er FETCH_PROTO +Protocol error +.It Bq Er FETCH_RESOLV +Resolver error +.It Bq Er FETCH_SERVER +Server error +.It Bq Er FETCH_TEMP +Temporary error +.It Bq Er FETCH_TIMEOUT +Operation timed out +.It Bq Er FETCH_UNAVAIL +File is not available +.It Bq Er FETCH_UNKNOWN +Unknown error +.It Bq Er FETCH_URL +Invalid URL +.El +.Pp +The accompanying error message includes a protocol-specific error code +and message, e.g. "File is not available (404 Not Found)" +.Sh ENVIRONMENT +The FTP and HTTP related functions use the +.Ev HTTP_PROXY +and +.Ev FTP_PROXY +environment variables, respectively, as the address of a proxy server +to use for transferring files. +.Sh SEE ALSO +.Xr com_err 3 , +.Xr fetch 1 , +.Xr ftpio 3 +.Rs +.%A T. Berners-Lee, L. Masinter & M. McCahill +.%D December 1994 +.%T Uniform Resource Locators (URL) +.%O RFC1738 +.Re +.Rs +.%A R. Fielding, J. Gettys, J. Mogul, H. Frystyk, T. Berners-Lee +.%D Januray 1997 +.%B Hypertext Transfer Protocol -- HTTP/1.1 +.%O RFC2068 +.Re +.Rs +.%A J. Postel, J. K. Reynolds +.%D October 1985 +.%B File Transfer Protocol +.%O RFC959 +.Re +.Sh NOTES +The +.Nm fetch +library uses the Common Error library, and applications which link +with +.Nm libfetch +must therefore also link with +.Nm libcom_err . +.Sh HISTORY +The +.Nm fetch +library first appeared in +.Fx 3.0 . +.Sh AUTHORS +The +.Nm fetch +library was mostly written by +.An Dag-Erling Coïdan Smørgrav Aq des@FreeBSD.org +with numerous suggestions from +.An Jordan K. Hubbard Aq jkh@FreeBSD.org , +.An Eugene Skepner Aq eu@qub.com +and other FreeBSD developers. +It replaces the older +.Nm ftpio +library written by +.An Poul-Henning Kamp Aq pkh@FreeBSD.org +and +.An Jordan K. Hubbard Aq jkh@FreeBSD.org . +.Pp +This manual page was written by +.An Dag-Erling Coïdan Smørgrav Aq des@FreeBSD.org +.Sh BUGS +Some parts of the library are not yet implemented. The most notable +examples of this are +.Fn fetchPutHTTP , +.Fn fetchStatHTTP +and FTP proxy support. +.Pp +There's no way to select a proxy at run-time other than setting the +.Ev HTTP_PROXY +or +.Ev FTP_PROXY +environment variables as appropriate. There is also no way to stop the +FTP and HTTP functions from trying to use a proxy if these variables +are set. +.Pp +HTTP authentication doesn't work. I'm not sure that's a bug in my +code; as far as I can determine, +.Nm libfetch +handles HTTP/1.1 basic authentication correctly as outlined in +RFC2068, but I haven't been able to find an HTTP server that honors +the Authentication: header field. Also, +.Nm libfetch +does not attempt to interpret and respond to authentication requests +from the HTTP server. +.Pp +No attempt is made to encode spaces etc. within URLs. Spaces in the +document part of an URLshould be replaced with "%20" in HTTP URLs and +"\\ " in FTP URLs. +.Pp +Error numbers are unique only within a certain context; the error +codes used for FTP and HTTP overlap, as do those used for resolver and +system errors. For instance, error code 202 means "Command not +implemented, superfluous at this site" in an FTP context and +"Accepted" in an HTTP context. +.Pp +The man page is poorly written and produces badly formatted text. +.Pp +Tons of other stuff. diff --git a/lib/libfetch/fetch.c b/lib/libfetch/fetch.c new file mode 100644 index 0000000..1dba89e --- /dev/null +++ b/lib/libfetch/fetch.c @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: fetch.c,v 1.6 1998/11/06 22:14:08 des Exp $ + */ + +#include <sys/param.h> +#include <sys/errno.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "fetch.h" +#include "common.h" + + +int fetchLastErrCode; + + +/*** Local data **************************************************************/ + +/* + * Error messages for parser errors + */ +#define URL_MALFORMED 1 +#define URL_BAD_SCHEME 2 +#define URL_BAD_PORT 3 +static struct fetcherr _url_errlist[] = { + { URL_MALFORMED, FETCH_URL, "Malformed URL" }, + { URL_BAD_SCHEME, FETCH_URL, "Invalid URL scheme" }, + { URL_BAD_PORT, FETCH_URL, "Invalid server port" }, + { -1, FETCH_UNKNOWN, "Unknown parser error" } +}; + + +/*** Public API **************************************************************/ + +/* + * Select the appropriate protocol for the URL scheme, and return a + * read-only stream connected to the document referenced by the URL. + */ +FILE * +fetchGet(struct url *URL, char *flags) +{ + if (strcasecmp(URL->scheme, "file") == 0) + return fetchGetFile(URL, flags); + else if (strcasecmp(URL->scheme, "http") == 0) + return fetchGetHTTP(URL, flags); + else if (strcasecmp(URL->scheme, "ftp") == 0) + return fetchGetFTP(URL, flags); + else { + _url_seterr(URL_BAD_SCHEME); + return NULL; + } +} + +/* + * Select the appropriate protocol for the URL scheme, and return a + * write-only stream connected to the document referenced by the URL. + */ +FILE * +fetchPut(struct url *URL, char *flags) +{ + if (strcasecmp(URL->scheme, "file") == 0) + return fetchPutFile(URL, flags); + else if (strcasecmp(URL->scheme, "http") == 0) + return fetchPutHTTP(URL, flags); + else if (strcasecmp(URL->scheme, "ftp") == 0) + return fetchPutFTP(URL, flags); + else { + _url_seterr(URL_BAD_SCHEME); + return NULL; + } +} + +/* + * Select the appropriate protocol for the URL scheme, and return the + * size of the document referenced by the URL if it exists. + */ +int +fetchStat(struct url *URL, struct url_stat *us, char *flags) +{ + if (strcasecmp(URL->scheme, "file") == 0) + return fetchStatFile(URL, us, flags); + else if (strcasecmp(URL->scheme, "http") == 0) + return fetchStatHTTP(URL, us, flags); + else if (strcasecmp(URL->scheme, "ftp") == 0) + return fetchStatFTP(URL, us, flags); + else { + _url_seterr(URL_BAD_SCHEME); + return -1; + } +} + +/* + * Attempt to parse the given URL; if successful, call fetchGet(). + */ +FILE * +fetchGetURL(char *URL, char *flags) +{ + struct url *u; + FILE *f; + + if ((u = fetchParseURL(URL)) == NULL) + return NULL; + + f = fetchGet(u, flags); + + free(u); + return f; +} + + +/* + * Attempt to parse the given URL; if successful, call fetchPut(). + */ +FILE * +fetchPutURL(char *URL, char *flags) +{ + struct url *u; + FILE *f; + + if ((u = fetchParseURL(URL)) == NULL) + return NULL; + + f = fetchPut(u, flags); + + free(u); + return f; +} + +/* + * Attempt to parse the given URL; if successful, call fetchStat(). + */ +int +fetchStatURL(char *URL, struct url_stat *us, char *flags) +{ + struct url *u; + int s; + + if ((u = fetchParseURL(URL)) == NULL) + return -1; + + s = fetchStat(u, us, flags); + + free(u); + return s; +} + +/* + * Split an URL into components. URL syntax is: + * method:[//[user[:pwd]@]host[:port]]/[document] + * This almost, but not quite, RFC1738 URL syntax. + */ +struct url * +fetchParseURL(char *URL) +{ + char *p, *q; + struct url *u; + int i; + + /* allocate struct url */ + if ((u = calloc(1, sizeof(struct url))) == NULL) { + errno = ENOMEM; + _fetch_syserr(); + return NULL; + } + + /* 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; + } + else URL++; + if (URL[1] != '/') { + p = URL; + goto nohost; + } + else URL += 2; + + p = strpbrk(URL, "/@"); + if (p && *p == '@') { + /* username */ + for (q = URL, i = 0; (*q != ':') && (*q != '@'); q++) + if (i < URL_USERLEN) + u->user[i++] = *q; + + /* password */ + if (*q == ':') + for (q++, i = 0; (*q != ':') && (*q != '@'); q++) + if (i < URL_PWDLEN) + u->pwd[i++] = *q; + + p++; + } else p = URL; + + /* hostname */ + for (i = 0; *p && (*p != '/') && (*p != ':'); p++) + if (i < MAXHOSTNAMELEN) + u->host[i++] = *p; + + /* port */ + if (*p == ':') { + for (q = ++p; *q && (*q != '/'); q++) + if (isdigit(*q)) + u->port = u->port * 10 + (*q - '0'); + else { + /* invalid port */ + _url_seterr(URL_BAD_PORT); + goto ouch; + } + while (*p && (*p != '/')) + p++; + } + +nohost: + /* document */ + if (*p) { + struct url *t; + t = realloc(u, sizeof(*u)+strlen(p)-1); + if (t == NULL) { + errno = ENOMEM; + _fetch_syserr(); + goto ouch; + } + u = t; + strcpy(u->doc, p); + } else { + u->doc[0] = '/'; + u->doc[1] = 0; + } + + DEBUG(fprintf(stderr, + "scheme: [\033[1m%s\033[m]\n" + "user: [\033[1m%s\033[m]\n" + "password: [\033[1m%s\033[m]\n" + "host: [\033[1m%s\033[m]\n" + "port: [\033[1m%d\033[m]\n" + "document: [\033[1m%s\033[m]\n", + u->scheme, u->user, u->pwd, + u->host, u->port, u->doc)); + + return u; + +ouch: + free(u); + return NULL; +} diff --git a/lib/libfetch/fetch.h b/lib/libfetch/fetch.h new file mode 100644 index 0000000..6ec7d67 --- /dev/null +++ b/lib/libfetch/fetch.h @@ -0,0 +1,85 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: fetch.h,v 1.6 1998/11/06 22:14:08 des Exp $ + */ + +#ifndef _FETCH_H_INCLUDED +#define _FETCH_H_INCLUDED + +#include <fetch_err.h> + +#define _LIBFETCH_VER "libfetch/1.0" + +#define URL_SCHEMELEN 16 +#define URL_USERLEN 256 +#define URL_PWDLEN 256 + +struct url { + char scheme[URL_SCHEMELEN+1]; + char user[URL_USERLEN+1]; + char pwd[URL_PWDLEN+1]; + char host[MAXHOSTNAMELEN+1]; + int port; + char doc[2]; +}; + +struct url_stat { + off_t size; + time_t atime; + time_t mtime; +}; + +/* FILE-specific functions */ +FILE *fetchGetFile(struct url *, char *); +FILE *fetchPutFile(struct url *, char *); +int fetchStatFile(struct url *, struct url_stat *, char *); + +/* HTTP-specific functions */ +char *fetchContentType(FILE *); +FILE *fetchGetHTTP(struct url *, char *); +FILE *fetchPutHTTP(struct url *, char *); +int fetchStatHTTP(struct url *, struct url_stat *, char *); + +/* FTP-specific functions */ +FILE *fetchGetFTP(struct url *, char *); +FILE *fetchPutFTP(struct url *, char *); +int fetchStatFTP(struct url *, struct url_stat *, char *); + +/* Generic functions */ +struct url *fetchParseURL(char *); +FILE *fetchGetURL(char *, char *); +FILE *fetchPutURL(char *, char *); +int fetchStatURL(char *, struct url_stat *, char *); +FILE *fetchGet(struct url *, char *); +FILE *fetchPut(struct url *, char *); +int fetchStat(struct url *, struct url_stat *, char *); + +/* Last error code */ +extern int fetchLastErrCode; + +#endif diff --git a/lib/libfetch/fetch_err.et b/lib/libfetch/fetch_err.et new file mode 100644 index 0000000..2e247a6 --- /dev/null +++ b/lib/libfetch/fetch_err.et @@ -0,0 +1,50 @@ +#- +# Copyright (c) 1998 Dag-Erling Coïdan Smørgrav +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer +# in this position and unchanged. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $Id: fetch_err.et,v 1.1 1998/11/06 22:14:08 des Exp $ +# +et ftch + ec FETCH_ABORT, "Operation aborted" + ec FETCH_AUTH, "Authentication failed" + ec FETCH_DOWN, "Service unavailable" + ec FETCH_EXISTS, "File exists" + ec FETCH_FULL, "File system full" + ec FETCH_INFO, "Informational response" + ec FETCH_MEMORY, "Insufficient memory" + ec FETCH_MOVED, "File has moved" + ec FETCH_NETWORK, "Network error" + ec FETCH_OK, "No error" + ec FETCH_PROTO, "Protocol error" + ec FETCH_RESOLV, "Resolver error" + ec FETCH_SERVER, "Server error" + ec FETCH_TEMP, "Temporary error" + ec FETCH_TIMEOUT, "Operation timed out" + ec FETCH_UNAVAIL, "File is not available" + ec FETCH_UNKNOWN, "Unknown error" + ec FETCH_URL, "Invalid URL" + ec FETCH_VERBOSE, "" +end diff --git a/lib/libfetch/file.c b/lib/libfetch/file.c new file mode 100644 index 0000000..fb10f8a --- /dev/null +++ b/lib/libfetch/file.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: file.c,v 1.2 1998/11/06 22:14:08 des Exp $ + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> + +#include "fetch.h" +#include "common.h" + +FILE * +fetchGetFile(struct url *u, char *flags) +{ + FILE *f; + + f = fopen(u->doc, "r"); + + if (f == NULL) + _fetch_syserr(); + return f; +} + +FILE * +fetchPutFile(struct url *u, char *flags) +{ + FILE *f; + + if (strchr(flags, 'a')) + f = fopen(u->doc, "a"); + else + f = fopen(u->doc, "w"); + + if (f == NULL) + _fetch_syserr(); + return f; +} + +int +fetchStatFile(struct url *u, struct url_stat *us, char *flags) +{ + struct stat sb; + + if (stat(u->doc, &sb) == -1) { + _fetch_syserr(); + return -1; + } + us->size = sb.st_size; + us->atime = sb.st_atime; + us->mtime = sb.st_mtime; + return 0; +} diff --git a/lib/libfetch/ftp.c b/lib/libfetch/ftp.c new file mode 100644 index 0000000..22993ee7 --- /dev/null +++ b/lib/libfetch/ftp.c @@ -0,0 +1,509 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: ftp.c,v 1.10 1998/12/16 15:29:03 des Exp $ + */ + +/* + * Portions of this code were taken from or based on ftpio.c: + * + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * Major Changelog: + * + * Dag-Erling Coïdan Smørgrav + * 9 Jun 1998 + * + * Incorporated into libfetch + * + * Jordan K. Hubbard + * 17 Jan 1996 + * + * Turned inside out. Now returns xfers as new file ids, not as a special + * `state' of FTP_t + * + * $ftpioId: ftpio.c,v 1.30 1998/04/11 07:28:53 phk Exp $ + * + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "fetch.h" +#include "common.h" +#include "ftperr.h" + +#define FTP_ANONYMOUS_USER "ftp" +#define FTP_ANONYMOUS_PASSWORD "ftp" +#define FTP_DEFAULT_PORT 21 + +#define FTP_OPEN_DATA_CONNECTION 150 +#define FTP_OK 200 +#define FTP_FILE_STATUS 213 +#define FTP_SERVICE_READY 220 +#define FTP_PASSIVE_MODE 227 +#define FTP_LOGGED_IN 230 +#define FTP_FILE_ACTION_OK 250 +#define FTP_NEED_PASSWORD 331 +#define FTP_NEED_ACCOUNT 332 + +#define ENDL "\r\n" + +static struct url cached_host; +static FILE *cached_socket; + +static char _ftp_last_reply[1024]; + +/* + * Get server response, check that first digit is a '2' + */ +static int +_ftp_chkerr(FILE *s) +{ + char *line; + size_t len; + + do { + if (((line = fgetln(s, &len)) == NULL) || (len < 4)) { + _fetch_syserr(); + return -1; + } + } while (len >= 4 && line[3] == '-'); + + while (len && isspace(line[len-1])) + len--; + snprintf(_ftp_last_reply, sizeof(_ftp_last_reply), + "%*.*s", (int)len, (int)len, line); + +#ifndef NDEBUG + fprintf(stderr, "\033[1m<<< "); + fprintf(stderr, "%*.*s\n", (int)len, (int)len, line); + fprintf(stderr, "\033[m"); +#endif + + if (len < 4 || !isdigit(line[1]) || !isdigit(line[1]) + || !isdigit(line[2]) || (line[3] != ' ')) { + return -1; + } + + return (line[0] - '0') * 100 + (line[1] - '0') * 10 + (line[2] - '0'); +} + +/* + * Send a command and check reply + */ +static int +_ftp_cmd(FILE *f, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(f, fmt, ap); +#ifndef NDEBUG + fprintf(stderr, "\033[1m>>> "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\033[m"); +#endif + va_end(ap); + + return _ftp_chkerr(f); +} + +/* + * Transfer file + */ +static FILE * +_ftp_transfer(FILE *cf, char *oper, char *file, char *mode, int pasv) +{ + struct sockaddr_in sin; + int e, sd = -1, l; + char *s; + FILE *df; + + /* change directory */ + if (((s = strrchr(file, '/')) != NULL) && (s != file)) { + *s = 0; + if ((e = _ftp_cmd(cf, "CWD %s" ENDL, file)) != FTP_FILE_ACTION_OK) { + *s = '/'; + _ftp_seterr(e); + return NULL; + } + *s++ = '/'; + } else { + if ((e = _ftp_cmd(cf, "CWD /" ENDL)) != FTP_FILE_ACTION_OK) { + _ftp_seterr(e); + return NULL; + } + } + + /* s now points to file name */ + + /* open data socket */ + if ((sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + _fetch_syserr(); + return NULL; + } + + if (pasv) { + u_char addr[6]; + char *ln, *p; + int i; + + /* send PASV command */ + if ((e = _ftp_cmd(cf, "PASV" ENDL)) != FTP_PASSIVE_MODE) + goto ouch; + + /* find address and port number. The reply to the PASV command + is IMHO the one and only weak point in the FTP protocol. */ + ln = _ftp_last_reply; + for (p = ln + 3; !isdigit(*p); p++) + /* nothing */ ; + for (p--, i = 0; i < 6; i++) { + p++; /* skip the comma */ + addr[i] = strtol(p, &p, 10); + } + + /* construct sockaddr for data socket */ + l = sizeof(sin); + if (getpeername(fileno(cf), (struct sockaddr *)&sin, &l) == -1) + goto sysouch; + bcopy(addr, (char *)&sin.sin_addr, 4); + bcopy(addr + 4, (char *)&sin.sin_port, 2); + + /* connect to data port */ + if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) + goto sysouch; + + /* make the server initiate the transfer */ + e = _ftp_cmd(cf, "%s %s" ENDL, oper, s); + if (e != FTP_OPEN_DATA_CONNECTION) + goto ouch; + + } else { + u_int32_t a; + u_short p; + int d; + + /* find our own address, bind, and listen */ + l = sizeof(sin); + if (getsockname(fileno(cf), (struct sockaddr *)&sin, &l) == -1) + goto sysouch; + sin.sin_port = 0; + if (bind(sd, (struct sockaddr *)&sin, l) == -1) + goto sysouch; + if (listen(sd, 1) == -1) + goto sysouch; + + /* find what port we're on and tell the server */ + if (getsockname(sd, (struct sockaddr *)&sin, &l) == -1) + goto sysouch; + a = ntohl(sin.sin_addr.s_addr); + p = ntohs(sin.sin_port); + e = _ftp_cmd(cf, "PORT %d,%d,%d,%d,%d,%d" ENDL, + (a >> 24) & 0xff, (a >> 16) & 0xff, + (a >> 8) & 0xff, a & 0xff, + (p >> 8) & 0xff, p & 0xff); + if (e != FTP_OK) + goto ouch; + + /* make the server initiate the transfer */ + e = _ftp_cmd(cf, "%s %s" ENDL, oper, s); + if (e != FTP_OPEN_DATA_CONNECTION) + goto ouch; + + /* accept the incoming connection and go to town */ + if ((d = accept(sd, NULL, NULL)) == -1) + goto sysouch; + close(sd); + sd = d; + } + + if ((df = fdopen(sd, mode)) == NULL) + goto sysouch; + return df; + +sysouch: + _fetch_syserr(); + close(sd); + return NULL; + +ouch: + _ftp_seterr(e); + close(sd); + return NULL; +} + +/* + * Log on to FTP server + */ +static FILE * +_ftp_connect(char *host, int port, char *user, char *pwd, int verbose) +{ + int sd, e, pp = FTP_DEFAULT_PORT; + char *p, *q; + FILE *f; + + /* check for proxy */ + if ((p = getenv("FTP_PROXY")) != NULL) { + if ((q = strchr(p, ':')) != NULL) { + /* XXX check that it's a valid number */ + pp = atoi(q+1); + } + if (q) + *q = 0; + sd = _fetch_connect(p, pp, verbose); + if (q) + *q = ':'; + } else { + /* no proxy, go straight to target */ + sd = _fetch_connect(host, port, verbose); + } + + /* check connection */ + if (sd == -1) { + _fetch_syserr(); + return NULL; + } + + /* streams make life easier */ + if ((f = fdopen(sd, "r+")) == NULL) { + _fetch_syserr(); + close(sd); + return NULL; + } + + /* expect welcome message */ + if ((e = _ftp_chkerr(f)) != FTP_SERVICE_READY) + goto fouch; + + /* send user name and password */ + if (!user || !*user) + user = FTP_ANONYMOUS_USER; + e = p ? _ftp_cmd(f, "USER %s@%s@%d" ENDL, user, host, port) + : _ftp_cmd(f, "USER %s" ENDL, user); + + /* did the server request a password? */ + if (e == FTP_NEED_PASSWORD) { + if (!pwd || !*pwd) + pwd = FTP_ANONYMOUS_PASSWORD; + e = _ftp_cmd(f, "PASS %s" ENDL, pwd); + } + + /* did the server request an account? */ + if (e == FTP_NEED_ACCOUNT) + goto fouch; + + /* we should be done by now */ + if (e != FTP_LOGGED_IN) + goto fouch; + + /* might as well select mode and type at once */ +#ifdef FTP_FORCE_STREAM_MODE + if ((e = _ftp_cmd(f, "MODE S" ENDL)) != FTP_OK) /* default is S */ + goto fouch; +#endif + if ((e = _ftp_cmd(f, "TYPE I" ENDL)) != FTP_OK) /* default is A */ + goto fouch; + + /* done */ + return f; + +fouch: + _ftp_seterr(e); + fclose(f); + return NULL; +} + +/* + * Disconnect from server + */ +static void +_ftp_disconnect(FILE *f) +{ + (void)_ftp_cmd(f, "QUIT" ENDL); + fclose(f); +} + +/* + * Check if we're already connected + */ +static int +_ftp_isconnected(struct url *url) +{ + return (cached_socket + && (strcmp(url->host, cached_host.host) == 0) + && (strcmp(url->user, cached_host.user) == 0) + && (strcmp(url->pwd, cached_host.pwd) == 0) + && (url->port == cached_host.port)); +} + +/* + * Check the cache, reconnect if no luck + */ +static FILE * +_ftp_cached_connect(struct url *url, char *flags) +{ + FILE *cf; + + cf = NULL; + + /* set default port */ + if (!url->port) + url->port = FTP_DEFAULT_PORT; + + /* try to use previously cached connection */ + if (_ftp_isconnected(url)) + if (_ftp_cmd(cached_socket, "NOOP" ENDL) != -1) + cf = cached_socket; + + /* connect to server */ + if (!cf) { + cf = _ftp_connect(url->host, url->port, url->user, url->pwd, + (strchr(flags, 'v') != NULL)); + if (!cf) + return NULL; + if (cached_socket) + _ftp_disconnect(cached_socket); + cached_socket = cf; + memcpy(&cached_host, url, sizeof(struct url)); + } + + return cf; +} + +/* + * Get file + */ +FILE * +fetchGetFTP(struct url *url, char *flags) +{ + FILE *cf; + + /* connect to server */ + if ((cf = _ftp_cached_connect(url, flags)) == NULL) + return NULL; + + /* initiate the transfer */ + return _ftp_transfer(cf, "RETR", url->doc, "r", + (flags && strchr(flags, 'p'))); +} + +/* + * Put file + */ +FILE * +fetchPutFTP(struct url *url, char *flags) +{ + FILE *cf; + + /* connect to server */ + if ((cf = _ftp_cached_connect(url, flags)) == NULL) + return NULL; + + /* initiate the transfer */ + return _ftp_transfer(cf, (flags && strchr(flags, 'a')) ? "APPE" : "STOR", + url->doc, "w", (flags && strchr(flags, 'p'))); +} + +/* + * Get file stats + */ +int +fetchStatFTP(struct url *url, struct url_stat *us, char *flags) +{ + FILE *cf; + char *ln, *s; + struct tm tm; + time_t t; + int e; + + /* connect to server */ + if ((cf = _ftp_cached_connect(url, flags)) == NULL) + return -1; + + /* change directory */ + if (((s = strrchr(url->doc, '/')) != NULL) && (s != url->doc)) { + *s = 0; + if ((e = _ftp_cmd(cf, "CWD %s" ENDL, url->doc)) != FTP_FILE_ACTION_OK) { + *s = '/'; + goto ouch; + } + *s++ = '/'; + } else { + if ((e = _ftp_cmd(cf, "CWD /" ENDL)) != FTP_FILE_ACTION_OK) + goto ouch; + } + + /* s now points to file name */ + + if (_ftp_cmd(cf, "SIZE %s" ENDL, s) != FTP_FILE_STATUS) + goto ouch; + for (ln = _ftp_last_reply + 4; *ln && isspace(*ln); ln++) + /* nothing */ ; + for (us->size = 0; *ln && isdigit(*ln); ln++) + us->size = us->size * 10 + *ln - '0'; + if (*ln && !isspace(*ln)) { + _ftp_seterr(999); /* XXX should signal a FETCH_PROTO error */ + return -1; + } + + if ((e = _ftp_cmd(cf, "MDTM %s" ENDL, s)) != FTP_FILE_STATUS) + goto ouch; + for (ln = _ftp_last_reply + 4; *ln && isspace(*ln); ln++) + /* nothing */ ; + t = time(NULL); + us->mtime = localtime(&t)->tm_gmtoff; + sscanf(ln, "%04d%02d%02d%02d%02d%02d", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + /* XXX should check the return value from sscanf */ + tm.tm_mon--; + tm.tm_year -= 1900; + tm.tm_isdst = -1; + tm.tm_gmtoff = 0; + us->mtime += mktime(&tm); + us->atime = us->mtime; + return 0; + +ouch: + _ftp_seterr(e); + return -1; +} diff --git a/lib/libfetch/ftp.errors b/lib/libfetch/ftp.errors new file mode 100644 index 0000000..d7816d2 --- /dev/null +++ b/lib/libfetch/ftp.errors @@ -0,0 +1,44 @@ +# $Id: ftp.errors,v 1.1.1.1 1998/07/09 16:52:43 des Exp $ +# +# This list is taken from RFC 959. +# It probably needs a going over. +# +110 OK Restart marker reply +120 TEMP Service ready in a few minutes +125 OK Data connection already open; transfer starting +150 OK File status okay; about to open data connection +200 OK Command okay +202 PROTO Command not implemented, superfluous at this site +211 INFO System status, or system help reply +212 INFO Directory status +213 INFO File status +214 INFO Help message +215 INFO Set system type +220 OK Service ready for new user +221 OK Service closing control connection +225 OK Data connection open; no transfer in progress +226 OK Requested file action successful +227 OK Entering Passive Mode +230 OK User logged in, proceed +250 OK Requested file action okay, completed +257 OK File/directory created +331 AUTH User name okay, need password +332 AUTH Need account for login +350 OK Requested file action pending further information +421 DOWN Service not available, closing control connection +425 NETWORK Can't open data connection +426 ABORT Connection closed; transfer aborted +450 UNAVAIL File unavailable (e.g., file busy) +451 SERVER Requested action aborted: local error in processing +452 FULL Insufficient storage space in system +500 PROTO Syntax error, command unrecognized +501 PROTO Syntax error in parameters or arguments +502 PROTO Command not implemented +503 PROTO Bad sequence of commands +504 PROTO Command not implemented for that parameter +530 AUTH Not logged in +532 AUTH Need account for storing files +550 UNAVAIL File unavailable (e.g., file not found, no access) +551 PROTO Requested action aborted. Page type unknown +552 FULL Exceeded storage allocation +553 EXISTS File name not allowed diff --git a/lib/libfetch/http.c b/lib/libfetch/http.c new file mode 100644 index 0000000..5c49ec5 --- /dev/null +++ b/lib/libfetch/http.c @@ -0,0 +1,461 @@ +/*- + * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: http.c,v 1.9 1998/12/16 11:44:31 des Exp $ + */ + +/* + * The base64 code in this file is based on code from MIT fetch, which + * has the following copyright and license: + * + *- + * Copyright 1997 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ + +#include <sys/param.h> + +#include <err.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "fetch.h" +#include "common.h" +#include "httperr.h" + +extern char *__progname; + +#define ENDL "\r\n" + +struct cookie +{ + FILE *real_f; +#define ENC_NONE 0 +#define ENC_CHUNKED 1 + int encoding; /* 1 = chunked, 0 = none */ +#define HTTPCTYPELEN 59 + char content_type[HTTPCTYPELEN+1]; + char *buf; + int b_cur, eof; + unsigned b_len, chunksize; +}; + +/* + * Send a formatted line; optionally echo to terminal + */ +static int +_http_cmd(FILE *f, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(f, fmt, ap); +#ifndef NDEBUG + fprintf(stderr, "\033[1m>>> "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\033[m"); +#endif + va_end(ap); + + return 0; /* XXX */ +} + +/* + * Fill the input buffer, do chunk decoding on the fly + */ +static char * +_http_fillbuf(struct cookie *c) +{ + char *ln; + unsigned int len; + + if (c->eof) + return NULL; + + if (c->encoding == ENC_NONE) { + c->buf = fgetln(c->real_f, &(c->b_len)); + c->b_cur = 0; + } else if (c->encoding == ENC_CHUNKED) { + if (c->chunksize == 0) { + ln = fgetln(c->real_f, &len); + DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): new chunk: " + "%*.*s\033[m\n", (int)len-2, (int)len-2, ln)); + sscanf(ln, "%x", &(c->chunksize)); + if (!c->chunksize) { + DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " + "end of last chunk\033[m\n")); + c->eof = 1; + return NULL; + } + DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " + "new chunk: %X\033[m\n", c->chunksize)); + } + c->buf = fgetln(c->real_f, &(c->b_len)); + if (c->b_len > c->chunksize) + c->b_len = c->chunksize; + c->chunksize -= c->b_len; + c->b_cur = 0; + } + else return NULL; /* unknown encoding */ + return c->buf; +} + +/* + * Read function + */ +static int +_http_readfn(struct cookie *c, char *buf, int len) +{ + int l, pos = 0; + while (len) { + /* empty buffer */ + if (!c->buf || (c->b_cur == c->b_len)) + if (!_http_fillbuf(c)) + break; + + l = c->b_len - c->b_cur; + if (len < l) l = len; + memcpy(buf + pos, c->buf + c->b_cur, l); + c->b_cur += l; + pos += l; + len -= l; + } + + if (ferror(c->real_f)) + return -1; + else return pos; +} + +/* + * Write function + */ +static int +_http_writefn(struct cookie *c, const char *buf, int len) +{ + size_t r = fwrite(buf, 1, (size_t)len, c->real_f); + return r ? r : -1; +} + +/* + * Close function + */ +static int +_http_closefn(struct cookie *c) +{ + int r = fclose(c->real_f); + free(c); + return (r == EOF) ? -1 : 0; +} + +/* + * Extract content type from cookie + */ +char * +fetchContentType(FILE *f) +{ + /* + * We have no way of making sure this really *is* one of our cookies, + * so just check for a null pointer and hope for the best. + */ + return f->_cookie ? (((struct cookie *)f->_cookie)->content_type) : NULL; +} + +/* + * Base64 encoding + */ +int +_http_base64(char *dst, char *src, int l) +{ + static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + int t, r = 0; + + while (l >= 3) { + t = (src[0] << 16) | (src[1] << 8) | src[2]; + dst[0] = base64[(t >> 18) & 0x3f]; + dst[1] = base64[(t >> 12) & 0x3f]; + dst[2] = base64[(t >> 6) & 0x3f]; + dst[3] = base64[(t >> 0) & 0x3f]; + src += 3; l -= 3; + dst += 4; r += 4; + } + + switch (l) { + case 2: + t = (src[0] << 16) | (src[1] << 8); + dst[0] = base64[(t >> 18) & 0x3f]; + dst[1] = base64[(t >> 12) & 0x3f]; + dst[2] = base64[(t >> 6) & 0x3f]; + dst[3] = '='; + dst += 4; + r += 4; + break; + case 1: + t = src[0] << 16; + dst[0] = base64[(t >> 18) & 0x3f]; + dst[1] = base64[(t >> 12) & 0x3f]; + dst[2] = dst[3] = '='; + dst += 4; + r += 4; + break; + case 0: + break; + } + + *dst = 0; + return r; +} + +/* + * Encode username and password + */ +char * +_http_auth(char *usr, char *pwd) +{ + int len, lu, lp; + char *str, *s; + + lu = strlen(usr); + lp = strlen(pwd); + + len = (lu * 4 + 2) / 3 /* user name, round up */ + + 1 /* colon */ + + (lp * 4 + 2) / 3 /* password, round up */ + + 1; /* null */ + + if ((s = str = (char *)malloc(len)) == NULL) + return NULL; + + s += _http_base64(s, usr, lu); + *s++ = ':'; + s += _http_base64(s, pwd, lp); + *s = 0; + + return str; +} + +/* + * Retrieve a file by HTTP + */ +FILE * +fetchGetHTTP(struct url *URL, char *flags) +{ + int sd = -1, e, i, enc = ENC_NONE, verbose; + struct cookie *c; + char *ln, *p, *px, *q; + FILE *f, *cf; + size_t len; + + verbose = (strchr(flags, 'v') != NULL); + + /* allocate cookie */ + if ((c = calloc(1, sizeof(struct cookie))) == NULL) + return NULL; + + /* check port */ + if (!URL->port) + URL->port = 80; /* default HTTP port */ + + /* attempt to connect to proxy server */ + if ((px = getenv("HTTP_PROXY")) != NULL) { + char host[MAXHOSTNAMELEN]; + int port = 3128; /* XXX I think 3128 is default... check? */ + + /* measure length */ + len = strcspn(px, ":"); + + /* get port (atoi is a little too tolerant perhaps?) */ + if (px[len] == ':') + port = atoi(px+len+1); + + /* get host name */ + if (len >= MAXHOSTNAMELEN) + len = MAXHOSTNAMELEN - 1; + strncpy(host, px, len); + host[len] = 0; + + /* connect */ + sd = _fetch_connect(host, port, verbose); + } + + /* if no proxy is configured or could be contacted, try direct */ + if (sd == -1) { + if ((sd = _fetch_connect(URL->host, URL->port, verbose)) == -1) + goto ouch; + } + + /* reopen as stream */ + if ((f = fdopen(sd, "r+")) == NULL) + goto ouch; + c->real_f = f; + + /* send request (proxies require absolute form, so use that) */ + if (verbose) + _fetch_info("requesting http://%s:%d%s", + URL->host, URL->port, URL->doc); + _http_cmd(f, "GET http://%s:%d%s HTTP/1.1" ENDL, + URL->host, URL->port, URL->doc); + + /* start sending headers away */ + if (URL->user[0] || URL->pwd[0]) { + char *auth_str = _http_auth(URL->user, URL->pwd); + if (!auth_str) + goto fouch; + _http_cmd(f, "Authorization: Basic %s" ENDL, auth_str); + free(auth_str); + } + _http_cmd(f, "Host: %s:%d" ENDL, URL->host, URL->port); + _http_cmd(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname); + _http_cmd(f, "Connection: close" ENDL ENDL); + + /* get response */ + if ((ln = fgetln(f, &len)) == NULL) + goto fouch; + DEBUG(fprintf(stderr, "response: [\033[1m%*.*s\033[m]\n", + (int)len-2, (int)len-2, ln)); + + /* we can't use strchr() and friends since ln isn't NUL-terminated */ + p = ln; + while ((p < ln + len) && !isspace(*p)) + p++; + while ((p < ln + len) && !isdigit(*p)) + p++; + if (!isdigit(*p)) + goto fouch; + e = atoi(p); + DEBUG(fprintf(stderr, "code: [\033[1m%d\033[m]\n", e)); + + /* add code to handle redirects later */ + if (e != 200) { + _http_seterr(e); + goto fouch; + } + + /* browse through header */ + while (1) { + if ((ln = fgetln(f, &len)) == NULL) + goto fouch; + if ((ln[0] == '\r') || (ln[0] == '\n')) + break; + DEBUG(fprintf(stderr, "header: [\033[1m%*.*s\033[m]\n", + (int)len-2, (int)len-2, ln)); +#define XFERENC "Transfer-Encoding:" + if (strncasecmp(ln, XFERENC, sizeof(XFERENC)-1) == 0) { + p = ln + sizeof(XFERENC) - 1; + while ((p < ln + len) && isspace(*p)) + p++; + for (q = p; (q < ln + len) && !isspace(*q); q++) + /* VOID */ ; + *q = 0; + if (strcasecmp(p, "chunked") == 0) + enc = ENC_CHUNKED; + DEBUG(fprintf(stderr, "xferenc: [\033[1m%s\033[m]\n", p)); +#undef XFERENC +#define CONTTYPE "Content-Type:" + } else if (strncasecmp(ln, CONTTYPE, sizeof(CONTTYPE)-1) == 0) { + p = ln + sizeof(CONTTYPE) - 1; + while ((p < ln + len) && isspace(*p)) + p++; + for (i = 0; p < ln + len; p++) + if (i < HTTPCTYPELEN) + c->content_type[i++] = *p; + do c->content_type[i--] = 0; while (isspace(c->content_type[i])); + DEBUG(fprintf(stderr, "conttype: [\033[1m%s\033[m]\n", + c->content_type)); +#undef CONTTYPE + } + } + + /* only body remains */ + c->encoding = enc; + cf = funopen(c, + (int (*)(void *, char *, int))_http_readfn, + (int (*)(void *, const char *, int))_http_writefn, + (fpos_t (*)(void *, fpos_t, int))NULL, + (int (*)(void *))_http_closefn); + if (cf == NULL) + goto fouch; + return cf; + +ouch: + if (sd >= 0) + close(sd); + free(c); + _http_seterr(999); /* XXX do this properly RSN */ + return NULL; +fouch: + fclose(f); + free(c); + _http_seterr(999); /* XXX do this properly RSN */ + return NULL; +} + +FILE * +fetchPutHTTP(struct url *URL, char *flags) +{ + warnx("fetchPutHTTP(): not implemented"); + return NULL; +} + +/* + * Get an HTTP document's metadata + */ +int +fetchStatHTTP(struct url *url, struct url_stat *us, char *flags) +{ + warnx("fetchStatHTTP(): not implemented"); + return -1; +} diff --git a/lib/libfetch/http.errors b/lib/libfetch/http.errors new file mode 100644 index 0000000..26907d5 --- /dev/null +++ b/lib/libfetch/http.errors @@ -0,0 +1,41 @@ +# $Id: http.errors,v 1.1.1.1 1998/07/09 16:52:44 des Exp $ +# +# This list is taken from RFC 2068. +# +100 OK Continue +101 OK Switching Protocols +200 OK OK +201 OK Created +202 OK Accepted +203 INFO Non-Authoritative Information +204 OK No Content +205 OK Reset Content +206 OK Partial Content +300 MOVED Multiple Choices +301 MOVED Moved Permanently +302 MOVED Moved Temporarily +303 MOVED See Other +304 OK Not Modified +305 INFO Use Proxy +400 PROTO Bad Request +401 AUTH Unauthorized +402 AUTH Payment Required +403 AUTH Forbidden +404 UNAVAIL Not Found +405 PROTO Method Not Allowed +406 PROTO Not Acceptable +407 AUTH Proxy Authentication Required +408 TIMEOUT Request Time-out +409 EXISTS Conflict +410 UNAVAIL Gone +411 PROTO Length Required +412 SERVER Precondition Failed +413 PROTO Request Entity Too Large +414 PROTO Request-URI Too Large +415 PROTO Unsupported Media Type +500 SERVER Internal Server Error +501 PROTO Not Implemented +502 SERVER Bad Gateway +503 TEMP Service Unavailable +504 TIMEOUT Gateway Time-out +505 PROTO HTTP Version not supported |