download-curl 0.1.3 → 0.1.4
raw patch · 12 files changed
+2/−3931 lines, 12 filesdep ~tagsoupnew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: tagsoup
API changes (from Hackage documentation)
Files
- cbits/common.c +0/−717
- cbits/common.h +0/−115
- cbits/download.c +0/−360
- cbits/download.h +0/−141
- cbits/file.c +0/−93
- cbits/ftp.c +0/−1175
- cbits/ftperr.h +0/−45
- cbits/hs_download_utils.c +0/−9
- cbits/hs_download_utils.h +0/−4
- cbits/http.c +0/−1226
- cbits/httperr.h +0/−44
- download-curl.cabal +2/−2
− cbits/common.c
@@ -1,717 +0,0 @@-/*-- * Copyright (c) 2006 Aaron Griffin <aaronmgriffin@gmail.com>- *- * Based on original libfetch code from:- * Copyright (c) 1998-2004 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.- */--#include <sys/cdefs.h>--#include <sys/param.h>-#include <sys/socket.h>-#include <sys/time.h>-#include <sys/uio.h>-#include <netinet/in.h>-#include <signal.h>--#include <errno.h>-#include <netdb.h>-#include <pwd.h>-#include <stdarg.h>-#include <stdlib.h>-#include <stdio.h>-#include <string.h>-#include <unistd.h>--#include "download.h"-#include "common.h"---/*** Local data **************************************************************/--/*- * Error messages for resolver errors- */-static struct downloaderr _netdb_errlist[] = {-#ifdef EAI_NODATA- { EAI_NODATA, DLERR_RESOLV, "Host not found" },-#endif- { EAI_AGAIN, DLERR_TEMP, "Transient resolver failure" },- { EAI_FAIL, DLERR_RESOLV, "Non-recoverable resolver failure" },- { EAI_NONAME, DLERR_RESOLV, "No address record" },- { -1, DLERR_UNKNOWN, "Unknown resolver error" }-};--/* End-of-Line */-static const char ENDL[2] = "\r\n";---/*** Error-reporting functions ***********************************************/--/*- * Map error code to string- */-static struct downloaderr *-_download_finderr(struct downloaderr *p, int e)-{- while (p->num != -1 && p->num != e)- p++;- return (p);-}--/*- * Set error code- */-void-_download_seterr(struct downloaderr *p, int e)-{- p = _download_finderr(p, e);- downloadLastErrCode = p->cat;- snprintf(downloadLastErrString, MAXERRSTRING, "%s", p->string);- DBG(fprintf(stderr, "ERR %d: %s\n", downloadLastErrCode, downloadLastErrString));-}--/*- * Unset error code- */-void-_download_unseterr(void)-{- downloadLastErrCode = DLERR_NONE;- memset(downloadLastErrString, 0, MAXERRSTRING);-}--/*- * Set error code according to errno- */-void-_download_syserr(void)-{- switch (errno) {- case 0:- downloadLastErrCode = DLERR_OK;- break;- case EPERM:- case EACCES:- case EROFS:- downloadLastErrCode = DLERR_AUTH;- break;- case ENOENT:- case EISDIR: /* XXX */- downloadLastErrCode = DLERR_UNAVAIL;- break;- case ENOMEM:- downloadLastErrCode = DLERR_MEMORY;- break;- case EBUSY:- case EAGAIN:- downloadLastErrCode = DLERR_TEMP;- break;- case EEXIST:- downloadLastErrCode = DLERR_EXISTS;- break;- case ENOSPC:- downloadLastErrCode = DLERR_FULL;- break;- case EADDRINUSE:- case EADDRNOTAVAIL:- case ENETDOWN:- case ENETUNREACH:- case ENETRESET:- case EHOSTUNREACH:- downloadLastErrCode = DLERR_NETWORK;- break;- case ECONNABORTED:- case ECONNRESET:- downloadLastErrCode = DLERR_ABORT;- break;- case ETIMEDOUT:- downloadLastErrCode = DLERR_TIMEOUT;- break;- case ECONNREFUSED:- case EHOSTDOWN:- downloadLastErrCode = DLERR_DOWN;- break;- default:- downloadLastErrCode = DLERR_UNKNOWN;- }- snprintf(downloadLastErrString, MAXERRSTRING, "%s", strerror(errno));-}---/*- * Emit status message- */-void-_download_info(const char *fmt, ...)-{- va_list ap;-- va_start(ap, fmt);- vfprintf(stderr, fmt, ap);- va_end(ap);- fputc('\n', stderr);-}---/*** Network-related utility functions ***************************************/--/*- * Return the default port for a scheme- */-int-_download_default_port(const char *scheme)-{- struct servent *se;-- if ((se = getservbyname(scheme, "tcp")) != NULL)- return (ntohs(se->s_port));- if (strcasecmp(scheme, SCHEME_FTP) == 0)- return (FTP_DEFAULT_PORT);- if (strcasecmp(scheme, SCHEME_HTTP) == 0)- return (HTTP_DEFAULT_PORT);- return (0);-}--/*- * Return the default proxy port for a scheme- */-int-_download_default_proxy_port(const char *scheme)-{- if (strcasecmp(scheme, SCHEME_FTP) == 0)- return (FTP_DEFAULT_PROXY_PORT);- if (strcasecmp(scheme, SCHEME_HTTP) == 0)- return (HTTP_DEFAULT_PROXY_PORT);- return (0);-}---/*- * Create a connection for an existing descriptor.- */-conn_t *-_download_reopen(int sd)-{- conn_t *conn;-- /* allocate and fill connection structure */- if ((conn = calloc(1, sizeof(*conn))) == NULL)- return (NULL);- conn->sd = sd;- ++conn->ref;- return (conn);-}---/*- * Bump a connection's reference count.- */-conn_t *-_download_ref(conn_t *conn)-{- ++conn->ref;- return (conn);-}---/*- * Bind a socket to a specific local address- */-int-_download_bind(int sd, int af, const char *addr)-{- struct addrinfo hints, *res, *res0;- int err;-- memset(&hints, 0, sizeof(hints));- hints.ai_family = af;- hints.ai_socktype = SOCK_STREAM;- hints.ai_protocol = 0;- if ((err = getaddrinfo(addr, NULL, &hints, &res0)) != 0)- return (-1);- for (res = res0; res; res = res->ai_next)- if (bind(sd, res->ai_addr, res->ai_addrlen) == 0)- return (0);- return (-1);-}---/*- * Establish a TCP connection to the specified port on the specified host.- */-conn_t *-_download_connect(const char *host, int port, int af, int verbose)-{- conn_t *conn;- char pbuf[10];- const char *bindaddr;- struct addrinfo hints, *res, *res0;- int sd, err;-- DBG(fprintf(stderr, "---> %s:%d\n", host, port));-- if (verbose)- _download_info("looking up %s", host);-- /* look up host name and set up socket address structure */- snprintf(pbuf, sizeof(pbuf), "%d", port);- memset(&hints, 0, sizeof(hints));- hints.ai_family = af;- hints.ai_socktype = SOCK_STREAM;- hints.ai_protocol = 0;- if ((err = getaddrinfo(host, pbuf, &hints, &res0)) != 0) {- _netdb_seterr(err);- return (NULL);- }- bindaddr = getenv("DOWNLOAD_BIND_ADDRESS");-- if (verbose)- _download_info("connecting to %s:%d", host, port);-- /* try to connect */- for (sd = -1, res = res0; res; sd = -1, res = res->ai_next) {- if ((sd = socket(res->ai_family, res->ai_socktype,- res->ai_protocol)) == -1)- continue;- if (bindaddr != NULL && *bindaddr != '\0' &&- _download_bind(sd, res->ai_family, bindaddr) != 0) {- _download_info("failed to bind to '%s'", bindaddr);- close(sd);- continue;- }- if (connect(sd, res->ai_addr, res->ai_addrlen) == 0)- break;- close(sd);- }- freeaddrinfo(res0);- if (sd == -1) {- _download_syserr();- return (NULL);- }-- if ((conn = _download_reopen(sd)) == NULL) {- _download_syserr();- close(sd);- }- return (conn);-}---/*- * Enable SSL on a connection.- */-int-_download_ssl(conn_t *conn, int verbose)-{--#ifdef WITH_SSL- /* Init the SSL library and context */- if (!SSL_library_init()){- fprintf(stderr, "SSL library init failed\n");- return (-1);- }-- SSL_load_error_strings();-- conn->ssl_meth = SSLv23_client_method();- conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth);- SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY);-- conn->ssl = SSL_new(conn->ssl_ctx);- if (conn->ssl == NULL){- fprintf(stderr, "SSL context creation failed\n");- return (-1);- }- SSL_set_fd(conn->ssl, conn->sd);- if (SSL_connect(conn->ssl) == -1){- ERR_print_errors_fp(stderr);- return (-1);- }-- if (verbose) {- X509_NAME *name;- char *str;-- fprintf(stderr, "SSL connection established using %s\n",- SSL_get_cipher(conn->ssl));- conn->ssl_cert = SSL_get_peer_certificate(conn->ssl);- name = X509_get_subject_name(conn->ssl_cert);- str = X509_NAME_oneline(name, 0, 0);- printf("Certificate subject: %s\n", str);- free(str);- name = X509_get_issuer_name(conn->ssl_cert);- str = X509_NAME_oneline(name, 0, 0);- printf("Certificate issuer: %s\n", str);- free(str);- }-- return (0);-#else- (void)conn;- (void)verbose;- DBG(fprintf(stderr, "SSL support disabled\n"));- return (-1);-#endif-}--/*- * Read a character from a connection w/ timeout- */-ssize_t-_download_read(conn_t *conn, char *buf, size_t len)-{- struct timeval wait;- fd_set readfds;- ssize_t rlen, total;- int r;-- if (downloadTimeout) {- FD_ZERO(&readfds);- }-- total = 0;- while (len > 0) {- while (downloadTimeout && !FD_ISSET(conn->sd, &readfds)) {- FD_SET(conn->sd, &readfds);- wait.tv_sec = downloadTimeout / 1000;- wait.tv_usec = (downloadTimeout % 1000) * 1000;- errno = 0;- r = select(conn->sd + 1, &readfds, NULL, NULL, &wait);- if (r == -1) {- if (errno == EINTR && downloadRestartCalls)- continue;- _download_syserr();- return (-1);- } else if (r == 0) {- errno = ETIMEDOUT;- _download_syserr();- return (-1);- }- }-#ifdef WITH_SSL- if (conn->ssl != NULL)- rlen = SSL_read(conn->ssl, buf, len);- else-#endif- rlen = read(conn->sd, buf, len);- if (rlen == 0)- break;- if (rlen < 0) {- if (errno == EINTR && downloadRestartCalls)- continue;- return (-1);- }- len -= rlen;- buf += rlen;- total += rlen;- }- return (total);-}---/*- * Read a line of text from a connection w/ timeout- */-#define MIN_BUF_SIZE 1024--int-_download_getln(conn_t *conn)-{- char *tmp;- size_t tmpsize;- ssize_t len;- char c;-- if (conn->buf == NULL) {- if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) {- errno = ENOMEM;- return (-1);- }- conn->bufsize = MIN_BUF_SIZE;- }-- conn->buf[0] = '\0';- conn->buflen = 0;-- do {- len = _download_read(conn, &c, 1);- if (len == -1)- return (-1);- if (len == 0)- break;- conn->buf[conn->buflen++] = c;- if (conn->buflen == conn->bufsize) {- tmp = conn->buf;- tmpsize = conn->bufsize * 2 + 1;- if ((tmp = realloc(tmp, tmpsize)) == NULL) {- errno = ENOMEM;- return (-1);- }- conn->buf = tmp;- conn->bufsize = tmpsize;- }- } while (c != '\n');-- conn->buf[conn->buflen] = '\0';- DBG(fprintf(stderr, "<<< %s", conn->buf));- return (0);-}---/*- * Write to a connection w/ timeout- */-ssize_t-_download_write(conn_t *conn, const char *buf, size_t len)-{- struct iovec iov;-- iov.iov_base = __DECONST(char *, buf);- iov.iov_len = len;- return _download_writev(conn, &iov, 1);-}--/*- * Write a vector to a connection w/ timeout- * Note: can modify the iovec.- */-ssize_t-_download_writev(conn_t *conn, struct iovec *iov, int iovcnt)-{- struct timeval wait;- fd_set writefds;- ssize_t wlen, total;- int r;-- if (downloadTimeout) {- FD_ZERO(&writefds);- /*- gettimeofday(&timeout, NULL);- timeout.tv_sec += downloadTimeout;- */- }-- total = 0;- while (iovcnt > 0) {- while (downloadTimeout && !FD_ISSET(conn->sd, &writefds)) {- FD_SET(conn->sd, &writefds);- /*- gettimeofday(&now, NULL);- wait.tv_sec = timeout.tv_sec - now.tv_sec;- wait.tv_usec = timeout.tv_usec - now.tv_usec;- if (wait.tv_usec < 0) {- wait.tv_usec += 1000000;- wait.tv_sec--;- }- if (wait.tv_sec < 0) {- errno = ETIMEDOUT;- _download_syserr();- return (-1);- }- */- wait.tv_sec = downloadTimeout / 1000;- wait.tv_usec = (downloadTimeout % 1000) * 1000;- errno = 0;- r = select(conn->sd + 1, NULL, &writefds, NULL, &wait);- if (r == -1) {- if (errno == EINTR && downloadRestartCalls)- continue;- return (-1);- } else if (r == 0) {- errno = ETIMEDOUT;- _download_syserr();- return (-1);- }- }- errno = 0;-- /* Protect against SIGPIPE in the case of a timeout */- signal(SIGPIPE, SIG_IGN);-#ifdef WITH_SSL- if (conn->ssl != NULL)- wlen = SSL_write(conn->ssl,- iov->iov_base, iov->iov_len);- else-#endif- wlen = writev(conn->sd, iov, iovcnt);- signal(SIGPIPE, SIG_DFL);-- if (wlen == 0) {- /* we consider a short write a failure */- errno = EPIPE;- _download_syserr();- return (-1);- }- if (wlen < 0) {- if (errno == EINTR && downloadRestartCalls)- continue;- return (-1);- }- total += wlen;- while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {- wlen -= iov->iov_len;- iov++;- iovcnt--;- }- if (iovcnt > 0) {- iov->iov_len -= wlen;- iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;- }- }- return (total);-}---/*- * Write a line of text to a connection w/ timeout- */-int-_download_putln(conn_t *conn, const char *str, size_t len)-{- struct iovec iov[2];- int ret;-- DBG(fprintf(stderr, ">>> %s\n", str));- iov[0].iov_base = __DECONST(char *, str);- iov[0].iov_len = len;- iov[1].iov_base = __DECONST(char *, ENDL);- iov[1].iov_len = sizeof(ENDL);- if (len == 0)- ret = _download_writev(conn, &iov[1], 1);- else- ret = _download_writev(conn, iov, 2);- if (ret == -1)- return (-1);- return (0);-}---/*- * Close connection- */-int-_download_close(conn_t *conn)-{- int ret;-- if (--conn->ref > 0)- return (0);- ret = close(conn->sd);- free(conn->buf);- free(conn);- return (ret);-}--/*** Authentication-related utility functions ********************************/--static const char *-_download_read_word(FILE *f)-{- static char word[1024];-- if (fscanf(f, " %1024s ", word) != 1)- return (NULL);- return (word);-}--/*- * Get authentication data for a URL from .netrc- */-int-_download_netrc_auth(struct url *url)-{- char fn[PATH_MAX];- const char *word;- char *p;- FILE *f;-- if ((p = getenv("NETRC")) != NULL) {- if (snprintf(fn, sizeof(fn), "%s", p) >= (int)sizeof(fn)) {- _download_info("$NETRC specifies a file name "- "longer than PATH_MAX");- return (-1);- }- } else {- if ((p = getenv("HOME")) != NULL) {- struct passwd *pwd;-- if ((pwd = getpwuid(getuid())) == NULL ||- (p = pwd->pw_dir) == NULL)- return (-1);- }- if (snprintf(fn, sizeof(fn), "%s/.netrc", p) >= (int)sizeof(fn))- return (-1);- }-- if ((f = fopen(fn, "r")) == NULL)- return (-1);- while ((word = _download_read_word(f)) != NULL) {- if (strcmp(word, "default") == 0) {- DBG(_download_info("Using default .netrc settings"));- break;- }- if (strcmp(word, "machine") == 0 &&- (word = _download_read_word(f)) != NULL &&- strcasecmp(word, url->host) == 0) {- DBG(_download_info("Using .netrc settings for %s", word));- break;- }- }- if (word == NULL)- goto ferr;- while ((word = _download_read_word(f)) != NULL) {- if (strcmp(word, "login") == 0) {- if ((word = _download_read_word(f)) == NULL)- goto ferr;- if (snprintf(url->user, sizeof(url->user),- "%s", word) > (int)sizeof(url->user)) {- _download_info("login name in .netrc is too long");- url->user[0] = '\0';- }- } else if (strcmp(word, "password") == 0) {- if ((word = _download_read_word(f)) == NULL)- goto ferr;- if (snprintf(url->pwd, sizeof(url->pwd),- "%s", word) > (int)sizeof(url->pwd)) {- _download_info("password in .netrc is too long");- url->pwd[0] = '\0';- }- } else if (strcmp(word, "account") == 0) {- if ((word = _download_read_word(f)) == NULL)- goto ferr;- /* XXX not supported! */- } else {- break;- }- }- fclose(f);- return (0);-ferr:- fclose(f);- return (-1);-}
− cbits/common.h
@@ -1,115 +0,0 @@-/*-- * Copyright (c) 2006 Aaron Griffin <aaronmgriffin@gmail.com>- *- * Based on original libfetch code from:- * Copyright (c) 1998-2004 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.- *- */--#ifndef COMMON_H_INCLUDED-#define COMMON_H_INCLUDED--/* BSD seems to use this alot - we don't have it */-#define __DECONST(type, v) (type)((uintptr_t)(void *)(v))--#define FTP_DEFAULT_PORT 21-#define HTTP_DEFAULT_PORT 80-#define FTP_DEFAULT_PROXY_PORT 21-#define HTTP_DEFAULT_PROXY_PORT 3128--#ifdef WITH_SSL-#include <openssl/crypto.h>-#include <openssl/x509.h>-#include <openssl/pem.h>-#include <openssl/ssl.h>-#include <openssl/err.h>-#endif--/* Connection */-struct connection {- int sd; /* socket descriptor */- char *buf; /* buffer */- size_t bufsize; /* buffer size */- size_t buflen; /* length of buffer contents */- int err; /* last protocol reply code */- fd_set readfds;-#ifdef WITH_SSL- SSL *ssl; /* SSL handle */- SSL_CTX *ssl_ctx; /* SSL context */- X509 *ssl_cert; /* server certificate */- SSL_METHOD *ssl_meth; /* SSL method */-#endif- int ref; /* reference count */-};-typedef struct connection conn_t;--/* Structure used for error message lists */-struct downloaderr {- const int num;- const int cat;- const char *string;-};--/* for _download_writev */-struct iovec;--void _download_seterr(struct downloaderr *, int);-void _download_unseterr(void);-void _download_syserr(void);-void _download_info(const char *, ...);-int _download_default_port(const char *);-int _download_default_proxy_port(const char *);-int _download_bind(int, int, const char *);-conn_t *_download_connect(const char *, int, int, int);-conn_t *_download_reopen(int);-conn_t *_download_ref(conn_t *);-int _download_ssl(conn_t *, int);-ssize_t _download_read(conn_t *, char *, size_t);-int _download_getln(conn_t *);-ssize_t _download_write(conn_t *, const char *, size_t);-ssize_t _download_writev(conn_t *, struct iovec *, int);-int _download_putln(conn_t *, const char *, size_t);-int _download_close(conn_t *);-int _download_netrc_auth(struct url *url);--#define _ftp_seterr(n) _download_seterr(_ftp_errlist, n)-#define _http_seterr(n) _download_seterr(_http_errlist, n)-#define _netdb_seterr(n) _download_seterr(_netdb_errlist, n)-#define _url_seterr(n) _download_seterr(_url_errlist, n)--#ifdef DEBUG-#define DBG(x) do { if (downloadDebug) { x; } } while (0)-#else-#define DBG(x) do { } while (0)-#endif--/*- * Check whether a particular flag is set- */-#define CHECK_FLAG(x) (flags && strchr(flags, (x)))--#endif /*COMMON_H_INCLUDED*/
− cbits/download.c
@@ -1,360 +0,0 @@-/*-- * Copyright (c) 2006 Aaron Griffin <aaronmgriffin@gmail.com>- *- * Based on original libfetch code from:- * Copyright (c) 1998-2004 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.- */--#include <sys/cdefs.h>--#include <sys/param.h>-#include <sys/errno.h>--#include <ctype.h>-#include <stdio.h>-#include <stdlib.h>-#include <string.h>--#include "download.h"-#include "common.h"--auth_t downloadAuthMethod;-int downloadLastErrCode;-char downloadLastErrString[MAXERRSTRING];-int downloadTimeout;-int downloadRestartCalls = 1;-int downloadDebug;---/*** Local data **************************************************************/--/*- * Error messages for parser errors- */-#define URL_MALFORMED 1-#define URL_BAD_SCHEME 2-#define URL_BAD_PORT 3-static struct downloaderr _url_errlist[] = {- { URL_MALFORMED, DLERR_URL, "Malformed URL" },- { URL_BAD_SCHEME, DLERR_URL, "Invalid URL scheme" },- { URL_BAD_PORT, DLERR_URL, "Invalid server port" },- { -1, DLERR_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.- * Also fill out the struct url_stat.- */-FILE *-downloadXGet(struct url *URL, struct url_stat *us, const char *flags)-{- int direct;-- direct = CHECK_FLAG('d');- if (us != NULL) {- us->size = -1;- us->atime = us->mtime = 0;- }- if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)- return (downloadXGetFile(URL, us, flags));- else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)- return (downloadXGetFTP(URL, us, flags));- else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)- return (downloadXGetHTTP(URL, us, flags));- else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)- return (downloadXGetHTTP(URL, us, flags));- _url_seterr(URL_BAD_SCHEME);- return (NULL);-}--/*- * Select the appropriate protocol for the URL scheme, and return a- * read-only stream connected to the document referenced by the URL.- */-FILE *-downloadGet(struct url *URL, const char *flags)-{- return (downloadXGet(URL, NULL, flags));-}--/*- * Select the appropriate protocol for the URL scheme, and return the- * size of the document referenced by the URL if it exists.- */-int-downloadStat(struct url *URL, struct url_stat *us, const char *flags)-{- int direct;-- direct = CHECK_FLAG('d');- if (us != NULL) {- us->size = -1;- us->atime = us->mtime = 0;- }- if (strcasecmp(URL->scheme, SCHEME_FILE) == 0)- return (downloadStatFile(URL, us, flags));- else if (strcasecmp(URL->scheme, SCHEME_FTP) == 0)- return (downloadStatFTP(URL, us, flags));- else if (strcasecmp(URL->scheme, SCHEME_HTTP) == 0)- return (downloadStatHTTP(URL, us, flags));- else if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0)- return (downloadStatHTTP(URL, us, flags));- _url_seterr(URL_BAD_SCHEME);- return (-1);-}--/*- * Attempt to parse the given URL; if successful, call downloadXGet().- */-FILE *-downloadXGetURL(const char *URL, struct url_stat *us, const char *flags)-{- struct url *u;- FILE *f;-- if ((u = downloadParseURL(URL)) == NULL)- return (NULL);-- f = downloadXGet(u, us, flags);-- downloadFreeURL(u);- return (f);-}--/*- * Attempt to parse the given URL; if successful, call downloadGet().- */-FILE *-downloadGetURL(const char *URL, const char *flags)-{- return (downloadXGetURL(URL, NULL, flags));-}--/*- * Attempt to parse the given URL; if successful, call downloadStat().- */-int-downloadStatURL(const char *URL, struct url_stat *us, const char *flags)-{- struct url *u;- int s;-- if ((u = downloadParseURL(URL)) == NULL)- return (-1);-- s = downloadStat(u, us, flags);-- downloadFreeURL(u);- return (s);-}--/*- * Make a URL- */-struct url *-downloadMakeURL(const char *scheme, const char *host, int port, const char *doc,- const char *user, const char *pwd)-{- struct url *u;-- if (!scheme || (!host && !doc)) {- _url_seterr(URL_MALFORMED);- return (NULL);- }-- if (port < 0 || port > 65535) {- _url_seterr(URL_BAD_PORT);- return (NULL);- }-- /* allocate struct url */- if ((u = calloc(1, sizeof(*u))) == NULL) {- _download_syserr();- return (NULL);- }-- if ((u->doc = strdup(doc ? doc : "/")) == NULL) {- _download_syserr();- free(u);- return (NULL);- }--#define seturl(x) snprintf(u->x, sizeof(u->x), "%s", x)- seturl(scheme);- seturl(host);- seturl(user);- seturl(pwd);-#undef seturl- u->port = port;-- return (u);-}--/*- * Split an URL into components. URL syntax is:- * [method:/][/[user[:pwd]@]host[:port]/][document]- * This almost, but not quite, RFC1738 URL syntax.- */-struct url *-downloadParseURL(const char *URL)-{- char *doc;- const char *p, *q;- struct url *u;- int i;-- /* allocate struct url */- if ((u = calloc(1, sizeof(*u))) == NULL) {- _download_syserr();- return (NULL);- }-- /* scheme name */- if ((p = strstr(URL, ":/"))) {- snprintf(u->scheme, URL_SCHEMELEN+1,- "%.*s", (int)(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 {- p = URL;- }- if (!*URL || *URL == '/' || *URL == '.' ||- (u->scheme[0] == '\0' &&- strchr(URL, '/') == NULL && strchr(URL, ':') == NULL))- goto nohost;-- 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 */-#ifdef INET6- if (*p == '[' && (q = strchr(p + 1, ']')) != NULL &&- (*++q == '\0' || *q == '/' || *q == ':')) {- if ((i = q - p - 2) > MAXHOSTNAMELEN)- i = MAXHOSTNAMELEN;- strncpy(u->host, ++p, i);- p = q;- } else-#endif- 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;- }- p = q;- }--nohost:- /* document */- if (!*p)- p = "/";-- if (strcasecmp(u->scheme, SCHEME_HTTP) == 0 ||- strcasecmp(u->scheme, SCHEME_HTTPS) == 0) {- const char hexnums[] = "0123456789abcdef";-- /* percent-escape whitespace. */- if ((doc = malloc(strlen(p) * 3 + 1)) == NULL) {- _download_syserr();- goto ouch;- }- u->doc = doc;- 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) {- _download_syserr();- goto ouch;- }-- DBG(fprintf(stderr,- "scheme: [%s]\n"- "user: [%s]\n"- "password: [%s]\n"- "host: [%s]\n"- "port: [%d]\n"- "document: [%s]\n",- u->scheme, u->user, u->pwd,- u->host, u->port, u->doc));-- return (u);--ouch:- free(u);- return (NULL);-}--/*- * Free a URL- */-void-downloadFreeURL(struct url *u)-{- free(u->doc);- free(u);-}
− cbits/download.h
@@ -1,141 +0,0 @@-/*-- * Copyright (c) 2006 Aaron Griffin <aaronmgriffin@gmail.com>- *- * Based on original libfetch code from:- * Copyright (c) 1998-2004 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.- *- */--#ifndef DOWNLOAD_H_INCLUDED-#define DOWNLOAD_H_INCLUDED--#include <stdio.h>-#include <sys/param.h> /* MAXHOSTNAMELEN */--#define USER_AGENT_STRING "libdownload/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;- off_t offset;- size_t length;-};--struct url_stat {- off_t size;- time_t atime;- time_t mtime;-};--/* Recognized schemes */-#define SCHEME_FTP "ftp"-#define SCHEME_HTTP "http"-#define SCHEME_HTTPS "https"-#define SCHEME_FILE "file"--/* Error codes */-#define DLERR_NONE 0-#define DLERR_ABORT 1-#define DLERR_AUTH 2-#define DLERR_DOWN 3-#define DLERR_EXISTS 4-#define DLERR_FULL 5-#define DLERR_INFO 6-#define DLERR_MEMORY 7-#define DLERR_MOVED 8-#define DLERR_NETWORK 9-#define DLERR_OK 10-#define DLERR_PROTO 11-#define DLERR_RESOLV 12-#define DLERR_SERVER 13-#define DLERR_TEMP 14-#define DLERR_TIMEOUT 15-#define DLERR_UNAVAIL 16-#define DLERR_UNKNOWN 17-#define DLERR_URL 18-#define DLERR_VERBOSE 19--__BEGIN_DECLS--/* FILE-specific functions */-FILE *downloadXGetFile(struct url *, struct url_stat *, const char *);-FILE *downloadGetFile(struct url *, const char *);-int downloadStatFile(struct url *, struct url_stat *, const char *);--/* HTTP-specific functions */-FILE *downloadXGetHTTP(struct url *, struct url_stat *, const char *);-FILE *downloadGetHTTP(struct url *, const char *);-int downloadStatHTTP(struct url *, struct url_stat *, const char *);--/* FTP-specific functions */-FILE *downloadXGetFTP(struct url *, struct url_stat *, const char *);-FILE *downloadGetFTP(struct url *, const char *);-int downloadStatFTP(struct url *, struct url_stat *, const char *);--/* Generic functions */-FILE *downloadXGetURL(const char *, struct url_stat *, const char *);-FILE *downloadGetURL(const char *, const char *);-int downloadStatURL(const char *, struct url_stat *, const char *);-FILE *downloadXGet(struct url *, struct url_stat *, const char *);-FILE *downloadGet(struct url *, const char *);-int downloadStat(struct url *, struct url_stat *, const char *);--/* URL parsing */-struct url *downloadMakeURL(const char *, const char *, int, const char *,- const char *, const char *);-struct url *downloadParseURL(const char *);-void downloadFreeURL(struct url *);--__END_DECLS--/* Authentication */-typedef int (*auth_t)(struct url *);-extern auth_t downloadAuthMethod;--/* Last error code */-extern int downloadLastErrCode;-#define MAXERRSTRING 256-extern char downloadLastErrString[MAXERRSTRING];--/* I/O timeout */-extern int downloadTimeout;--/* Restart interrupted syscalls */-extern int downloadRestartCalls;--/* Extra verbosity */-extern int downloadDebug;--#endif /*DOWNLOAD_H_INCLUDED*/
− cbits/file.c
@@ -1,93 +0,0 @@-/*-- * Copyright (c) 2006 Aaron Griffin <aaronmgriffin@gmail.com>- *- * Based on original libfetch code from:- * Copyright (c) 1998-2004 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.- */--#include <sys/cdefs.h>--#include <sys/param.h>-#include <sys/stat.h>-#include <unistd.h>--#include <dirent.h>-#include <stdio.h>-#include <string.h>--#include "download.h"-#include "common.h"--FILE *-downloadXGetFile(struct url *u, struct url_stat *us, const char *flags)-{- FILE *f;-- if (us && downloadStatFile(u, us, flags) == -1)- return (NULL);-- f = fopen(u->doc, "r");-- if (f == NULL)- _download_syserr();-- if (u->offset && fseeko(f, u->offset, SEEK_SET) == -1) {- fclose(f);- _download_syserr();- }-- return (f);-}--FILE *-downloadGetFile(struct url *u, const char *flags)-{- return (downloadXGetFile(u, NULL, flags));-}--static int-_download_stat_file(const char *fn, struct url_stat *us)-{- struct stat sb;-- us->size = -1;- us->atime = us->mtime = 0;- if (stat(fn, &sb) == -1) {- _download_syserr();- return (-1);- }- us->size = sb.st_size;- us->atime = sb.st_atime;- us->mtime = sb.st_mtime;- return (0);-}--int-downloadStatFile(struct url *u, struct url_stat *us, const char *flags)-{- return (_download_stat_file(u->doc, us));-}
− cbits/ftp.c
@@ -1,1175 +0,0 @@-/*-- * Copyright (c) 2006 Aaron Griffin <aaronmgriffin@gmail.com>- *- * Based on original libfetch code from:- * Copyright (c) 1998-2004 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.- */--#include <sys/cdefs.h>--/*- * Portions of this code were taken from or based on ftpio.c:- *- * ----------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42):- * <phk@FreeBSD.org> 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 <libio.h>-#include <err.h>-#include <errno.h>-#include <fcntl.h>-#include <netdb.h>-#include <stdarg.h>-#include <stdint.h>-#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#include <time.h>-#include <unistd.h>--#include "download.h"-#include "common.h"-#include "ftperr.h"--#define FTP_ANONYMOUS_USER "anonymous"--#define FTP_CONNECTION_ALREADY_OPEN 125-#define FTP_OPEN_DATA_CONNECTION 150-#define FTP_OK 200-#define FTP_FILE_STATUS 213-#define FTP_SERVICE_READY 220-#define FTP_TRANSFER_COMPLETE 226-#define FTP_PASSIVE_MODE 227-#define FTP_LPASSIVE_MODE 228-#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-#define FTP_SYNTAX_ERROR 500-#define FTP_PROTOCOL_ERROR 999--static struct url cached_host;-static conn_t *cached_connection;--#define isftpreply(foo) (isdigit(foo[0]) && isdigit(foo[1]) \- && isdigit(foo[2]) \- && (foo[3] == ' ' || foo[3] == '\0'))-#define isftpinfo(foo) (isdigit(foo[0]) && isdigit(foo[1]) \- && isdigit(foo[2]) && foo[3] == '-')--/* for easy redirection */-FILE *_http_request(struct url *, const char *, struct url_stat *,- struct url *, const char *);--/*- * Translate IPv4 mapped IPv6 address to IPv4 address- */-static void-unmappedaddr(struct sockaddr_in6 *sin6)-{- struct sockaddr_in *sin4;- u_int32_t addr;- int port;-- if (sin6->sin6_family != AF_INET6 ||- !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))- return;- sin4 = (struct sockaddr_in *)sin6;- addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];- port = sin6->sin6_port;- memset(sin4, 0, sizeof(struct sockaddr_in));- sin4->sin_addr.s_addr = addr;- sin4->sin_port = port;- sin4->sin_family = AF_INET;- //sin6->sin_len = sizeof(struct sockaddr_in);-}--/*- * Get server response- */-static int-_ftp_chkerr(conn_t *conn)-{- if (_download_getln(conn) == -1) {- _download_syserr();- return (-1);- }- if (isftpinfo(conn->buf)) {- while (conn->buflen && !isftpreply(conn->buf)) {- if (_download_getln(conn) == -1) {- _download_syserr();- return (-1);- }- }- }-- while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))- conn->buflen--;- conn->buf[conn->buflen] = '\0';-- if (!isftpreply(conn->buf)) {- _ftp_seterr(FTP_PROTOCOL_ERROR);- return (-1);- }-- conn->err = (conn->buf[0] - '0') * 100- + (conn->buf[1] - '0') * 10- + (conn->buf[2] - '0');-- return (conn->err);-}--/*- * Send a command and check reply- */-static int-_ftp_cmd(conn_t *conn, const char *fmt, ...)-{- va_list ap;- size_t len;- char *msg;- int r;-- va_start(ap, fmt);- len = vasprintf(&msg, fmt, ap);- va_end(ap);-- if (msg == NULL) {- errno = ENOMEM;- _download_syserr();- return (-1);- }-- r = _download_putln(conn, msg, len);- free(msg);-- if (r == -1) {- _download_syserr();- return (-1);- }-- return (_ftp_chkerr(conn));-}--/*- * Return a pointer to the filename part of a path- */-static const char *-_ftp_filename(const char *file, int *len, int *type)-{- const char *s;-- if ((s = strrchr(file, '/')) == NULL)- s = file;- else- 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';- return (FTP_OK);-}--/*- * Change working directory to the directory that contains the specified- * file.- */-static int-_ftp_cwd(conn_t *conn, const char *file)-{- const char *beg, *end;- char pwd[PATH_MAX];- int e, i, len;-- 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 (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) {- while (*beg == '/')- ++beg, ++i;- for (++i; file + i < end && file[i] != '/'; ++i)- /* nothing */;- if(beg < end)- 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) {- if (mode == 'S') {- /*- * Stream mode is supposed to be the default - so- * much so that some servers not only do not- * support any other mode, but do not support the- * MODE command at all.- *- * If "MODE S" fails, it is unlikely that we- * previously succeeded in setting a different- * mode. Therefore, we simply hope that the- * server is already in the correct mode, and- * silently ignore the failure.- */- } else {- 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- */-static int-_ftp_stat(conn_t *conn, const char *file, struct url_stat *us)-{- char *ln;- const char *filename;- int filenamelen, type;- struct tm tm;- time_t t;- int e;-- us->size = -1;- us->atime = us->mtime = 0;-- filename = _ftp_filename(file, &filenamelen, &type);-- 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);- }- for (ln = conn->buf + 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(FTP_PROTOCOL_ERROR);- us->size = -1;- return (-1);- }- if (us->size == 0)- us->size = -1;- DBG(fprintf(stderr, "size: [%lld]\n", (long long)us->size));-- e = _ftp_cmd(conn, "MDTM %.*s", filenamelen, filename);- if (e != FTP_FILE_STATUS) {- _ftp_seterr(e);- return (-1);- }- for (ln = conn->buf + 4; *ln && isspace(*ln); ln++)- /* nothing */ ;- switch (strspn(ln, "0123456789")) {- case 14:- break;- case 15:- ln++;- ln[0] = '2';- ln[1] = '0';- break;- default:- _ftp_seterr(FTP_PROTOCOL_ERROR);- return (-1);- }- if (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) != 6) {- _ftp_seterr(FTP_PROTOCOL_ERROR);- return (-1);- }- tm.tm_mon--;- tm.tm_year -= 1900;- tm.tm_isdst = -1;- t = timegm(&tm);- if (t == (time_t)-1)- t = time(NULL);- us->mtime = t;- us->atime = t;- DBG(fprintf(stderr,- "last modified: [%04d-%02d-%02d %02d:%02d:%02d]\n",- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,- tm.tm_hour, tm.tm_min, tm.tm_sec));- return (0);-}--/*- * I/O functions for FTP- */-struct ftpio {- conn_t *cconn; /* Control connection */- conn_t *dconn; /* Data connection */- 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 == NULL) {- errno = EBADF;- return (-1);- }- if (io->cconn == NULL || io->dconn == NULL || io->dir == O_WRONLY) {- errno = EBADF;- return (-1);- }- if (io->err) {- errno = io->err;- return (-1);- }- if (io->eof)- return (0);- r = _download_read(io->dconn, buf, len);- if (r > 0)- return (r);- if (r == 0) {- io->eof = 1;- return (0);- }- if (errno != EINTR)- 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 == NULL) {- errno = EBADF;- return (-1);- }- if (io->cconn == NULL || io->dconn == NULL || io->dir == O_RDONLY) {- errno = EBADF;- return (-1);- }- if (io->err) {- errno = io->err;- return (-1);- }- w = _download_write(io->dconn, buf, len);- if (w >= 0)- return (w);- if (errno != EINTR)- io->err = errno;- return (-1);-}--static fpos_t-_ftp_seekfn(void *v, fpos_t pos, int whence)-{- fpos_t invalid = {0};- struct ftpio *io;-- io = (struct ftpio *)v;- if (io == NULL) {- errno = EBADF;- return invalid;- }- errno = ESPIPE;- return invalid;-}--static int-_ftp_closefn(void *v)-{- struct ftpio *io;- int r;-- io = (struct ftpio *)v;- if (io == NULL) {- errno = EBADF;- return (-1);- }- if (io->dir == -1)- return (0);- if (io->cconn == NULL || io->dconn == NULL) {- errno = EBADF;- return (-1);- }- _download_close(io->dconn);- io->dir = -1;- io->dconn = NULL;- DBG(fprintf(stderr, "Waiting for final status\n"));- r = _ftp_chkerr(io->cconn);- if (io->cconn == cached_connection && io->cconn->ref == 1)- cached_connection = NULL;- _download_close(io->cconn);- free(io);- return (r == FTP_TRANSFER_COMPLETE) ? 0 : -1;-}--static FILE *-_ftp_setup(conn_t *cconn, conn_t *dconn, int mode)-{- struct ftpio *io;- FILE *f;-- if (cconn == NULL || dconn == NULL)- return (NULL);- if ((io = malloc(sizeof(*io))) == NULL)- return (NULL);- io->cconn = cconn;- io->dconn = dconn;- io->dir = mode;- io->eof = io->err = 0;- cookie_io_functions_t cookies;- cookies.read = (cookie_read_function_t *)_ftp_readfn;- cookies.write = (cookie_write_function_t *)_ftp_writefn;- cookies.seek = (cookie_seek_function_t *)_ftp_seekfn;- cookies.close = (cookie_close_function_t *)_ftp_closefn;- f = fopencookie(io, "rw", cookies);- if (f == NULL)- free(io);- return (f);-}--/*- * Transfer file- */-static FILE *-_ftp_transfer(conn_t *conn, const char *oper, const char *file,- int mode, off_t offset, const char *flags)-{- struct sockaddr_storage sa;- struct sockaddr_in6 *sin6;- struct sockaddr_in *sin4;- const char *bindaddr;- const char *filename;- int filenamelen, type;- int low, pasv, verbose;- int e, sd = -1;- socklen_t l;- char *s;- FILE *df;-- /* check flags */- low = CHECK_FLAG('l');- pasv = CHECK_FLAG('p');- verbose = CHECK_FLAG('v');-- /* passive mode */- if (!pasv)- 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)- goto sysouch;- if (sa.ss_family == AF_INET6)- unmappedaddr((struct sockaddr_in6 *)&sa);-- /* open data socket */- if ((sd = socket(sa.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {- _download_syserr();- return (NULL);- }-- if (pasv) {- u_char addr[64];- char *ln, *p;- unsigned int i;- int port;-- /* send PASV command */- if (verbose)- _download_info("setting passive mode");- switch (sa.ss_family) {- case AF_INET:- if ((e = _ftp_cmd(conn, "PASV")) != FTP_PASSIVE_MODE)- goto ouch;- break;- case AF_INET6:- if ((e = _ftp_cmd(conn, "EPSV")) != FTP_EPASSIVE_MODE) {- if (e == -1)- goto ouch;- if ((e = _ftp_cmd(conn, "LPSV")) !=- FTP_LPASSIVE_MODE)- goto ouch;- }- break;- default:- e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */- 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 = conn->buf;- switch (e) {- case FTP_PASSIVE_MODE:- case FTP_LPASSIVE_MODE:- for (p = ln + 3; *p && !isdigit(*p); p++)- /* nothing */ ;- if (!*p) {- e = FTP_PROTOCOL_ERROR;- goto ouch;- }- l = (e == FTP_PASSIVE_MODE ? 6 : 21);- for (i = 0; *p && i < l; i++, p++)- addr[i] = strtol(p, &p, 10);- if (i < l) {- e = FTP_PROTOCOL_ERROR;- goto ouch;- }- break;- case FTP_EPASSIVE_MODE:- for (p = ln + 3; *p && *p != '('; p++)- /* nothing */ ;- if (!*p) {- e = FTP_PROTOCOL_ERROR;- goto ouch;- }- ++p;- if (sscanf(p, "%c%c%c%d%c", &addr[0], &addr[1], &addr[2],- &port, &addr[3]) != 5 ||- addr[0] != addr[1] ||- addr[0] != addr[2] || addr[0] != addr[3]) {- e = FTP_PROTOCOL_ERROR;- goto ouch;- }- break;- }-- /* seek to required offset */- if (offset)- if (_ftp_cmd(conn, "REST %lu", (u_long)offset) != FTP_FILE_OK)- goto sysouch;-- /* construct sockaddr for data socket */- l = sizeof(sa);- if (getpeername(conn->sd, (struct sockaddr *)&sa, &l) == -1)- goto sysouch;- if (sa.ss_family == AF_INET6)- unmappedaddr((struct sockaddr_in6 *)&sa);- switch (sa.ss_family) {- case AF_INET6:- sin6 = (struct sockaddr_in6 *)&sa;- if (e == FTP_EPASSIVE_MODE)- sin6->sin6_port = htons(port);- else {- bcopy(addr + 2, (char *)&sin6->sin6_addr, 16);- bcopy(addr + 19, (char *)&sin6->sin6_port, 2);- }- break;- case AF_INET:- sin4 = (struct sockaddr_in *)&sa;- if (e == FTP_EPASSIVE_MODE)- sin4->sin_port = htons(port);- else {- bcopy(addr, (char *)&sin4->sin_addr, 4);- bcopy(addr + 4, (char *)&sin4->sin_port, 2);- }- break;- default:- e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */- break;- }-- /* connect to data port */- if (verbose)- _download_info("opening data connection");- bindaddr = getenv("DOWNLOAD_BIND_ADDRESS");- if (bindaddr != NULL && *bindaddr != '\0' &&- _download_bind(sd, sa.ss_family, bindaddr) != 0)- goto sysouch;- if (connect(sd, (struct sockaddr *)&sa, sizeof(struct sockaddr_storage)) == -1)- goto sysouch;-- /* make the server initiate the transfer */- if (verbose)- _download_info("initiating transfer");- e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);- if (e != FTP_CONNECTION_ALREADY_OPEN && e != FTP_OPEN_DATA_CONNECTION)- goto ouch;-- } else {- u_int32_t a;- u_short p;- int d;-#ifdef IPV6_PORTRANGE- int arg;-#endif- char *ap;- char hname[INET6_ADDRSTRLEN];-- switch (sa.ss_family) {- case AF_INET6:- ((struct sockaddr_in6 *)&sa)->sin6_port = 0;-#ifdef IPV6_PORTRANGE- arg = low ? IPV6_PORTRANGE_DEFAULT : IPV6_PORTRANGE_HIGH;- if (setsockopt(sd, IPPROTO_IPV6, IPV6_PORTRANGE,- (char *)&arg, sizeof(arg)) == -1)- goto sysouch;-#endif- break;- case AF_INET:- ((struct sockaddr_in *)&sa)->sin_port = 0;-#ifdef IP_PORTRANGE- arg = low ? IP_PORTRANGE_DEFAULT : IP_PORTRANGE_HIGH;- if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,- (char *)&arg, sizeof(arg)) == -1)- goto sysouch;-#endif- break;- }- if (verbose)- _download_info("binding data socket");- if (bind(sd, (struct sockaddr *)&sa, sizeof(struct sockaddr_storage)) == -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 *)&sa, &l) == -1)- goto sysouch;- switch (sa.ss_family) {- case AF_INET:- sin4 = (struct sockaddr_in *)&sa;- a = ntohl(sin4->sin_addr.s_addr);- p = ntohs(sin4->sin_port);- e = _ftp_cmd(conn, "PORT %d,%d,%d,%d,%d,%d",- (a >> 24) & 0xff, (a >> 16) & 0xff,- (a >> 8) & 0xff, a & 0xff,- (p >> 8) & 0xff, p & 0xff);- break;- case AF_INET6:-#define UC(b) (((int)b)&0xff)- e = -1;- sin6 = (struct sockaddr_in6 *)&sa;- sin6->sin6_scope_id = 0;- if (getnameinfo((struct sockaddr *)&sa,sizeof(struct sockaddr_storage),- hname, sizeof(hname),- NULL, 0, NI_NUMERICHOST) == 0) {- e = _ftp_cmd(conn, "EPRT |%d|%s|%d|", 2, hname,- htons(sin6->sin6_port));- if (e == -1)- goto ouch;- }- if (e != FTP_OK) {- ap = (char *)&sin6->sin6_addr;- e = _ftp_cmd(conn,- "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",- 6, 16,- UC(ap[0]), UC(ap[1]), UC(ap[2]), UC(ap[3]),- UC(ap[4]), UC(ap[5]), UC(ap[6]), UC(ap[7]),- UC(ap[8]), UC(ap[9]), UC(ap[10]), UC(ap[11]),- UC(ap[12]), UC(ap[13]), UC(ap[14]), UC(ap[15]),- 2,- (ntohs(sin6->sin6_port) >> 8) & 0xff,- ntohs(sin6->sin6_port) & 0xff);- }- break;- default:- e = FTP_PROTOCOL_ERROR; /* XXX: error code should be prepared */- goto ouch;- }- if (e != FTP_OK)- goto ouch;-- /* seek to required offset */- if (offset)- if (_ftp_cmd(conn, "REST %ju", (uintmax_t)offset) != FTP_FILE_OK)- goto sysouch;-- /* make the server initiate the transfer */- if (verbose)- _download_info("initiating transfer");- e = _ftp_cmd(conn, "%s %.*s", oper, filenamelen, filename);- if (e != FTP_CONNECTION_ALREADY_OPEN && 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 = _ftp_setup(conn, _download_reopen(sd), mode)) == NULL)- goto sysouch;- return (df);--sysouch:- _download_syserr();- if (sd >= 0)- close(sd);- return (NULL);--ouch:- if (e != -1)- _ftp_seterr(e);- if (sd >= 0)- close(sd);- return (NULL);-}--/*- * Authenticate- */-static int-_ftp_authenticate(conn_t *conn, struct url *url, struct url *purl)-{- const char *user, *pwd, *logname;- char pbuf[MAXHOSTNAMELEN + MAXPATHLEN + 1];- int e, len;-- /* XXX FTP_AUTH, and maybe .netrc */-- /* send user name and password */- if (url->user[0] == '\0')- _download_netrc_auth(url);- user = url->user;- if (*user == '\0')- user = getenv("FTP_LOGIN");- if (user == NULL || *user == '\0')- user = FTP_ANONYMOUS_USER;- if (purl && url->port == _download_default_port(url->scheme))- e = _ftp_cmd(conn, "USER %s@%s", user, url->host);- else if (purl)- e = _ftp_cmd(conn, "USER %s@%s@%d", user, url->host, url->port);- else- e = _ftp_cmd(conn, "USER %s", user);-- /* did the server request a password? */- if (e == FTP_NEED_PASSWORD) {- pwd = url->pwd;- if (*pwd == '\0')- pwd = getenv("FTP_PASSWORD");- if (pwd == NULL || *pwd == '\0') {- if ((logname = getlogin()) == 0)- logname = FTP_ANONYMOUS_USER;- if ((len = snprintf(pbuf, MAXPATHLEN + 1, "%s@", logname)) < 0)- len = 0;- else if (len > MAXPATHLEN)- len = MAXPATHLEN;- gethostname(pbuf + len, sizeof(pbuf) - len);- pwd = pbuf;- }- e = _ftp_cmd(conn, "PASS %s", pwd);- }-- return (e);-}--/*- * Log on to FTP server- */-static conn_t *-_ftp_connect(struct url *url, struct url *purl, const char *flags)-{- conn_t *conn;- int e, direct, verbose;-#ifdef INET6- int af = AF_UNSPEC;-#else- int af = AF_INET;-#endif-- direct = CHECK_FLAG('d');- verbose = CHECK_FLAG('v');- if (CHECK_FLAG('4'))- af = AF_INET;- else if (CHECK_FLAG('6'))- af = AF_INET6;-- if (direct)- purl = NULL;-- /* check for proxy */- if (purl) {- /* XXX proxy authentication! */- conn = _download_connect(purl->host, purl->port, af, verbose);- } else {- /* no proxy, go straight to target */- conn = _download_connect(url->host, url->port, af, verbose);- purl = NULL;- }-- /* check connection */- if (conn == NULL)- /* _download_connect() has already set an error code */- return (NULL);-- /* expect welcome message */- if ((e = _ftp_chkerr(conn)) != FTP_SERVICE_READY)- goto fouch;-- /* authenticate */- if ((e = _ftp_authenticate(conn, url, purl)) != FTP_LOGGED_IN)- goto fouch;-- /* done */- return (conn);--fouch:- if (e != -1)- _ftp_seterr(e);- _download_close(conn);- return (NULL);-}--/*- * Disconnect from server- */-static void-_ftp_disconnect(conn_t *conn)-{- (void)_ftp_cmd(conn, "QUIT");- _download_unseterr(); /* we don't care about errors on disconnect */- if (conn == cached_connection && conn->ref == 1)- cached_connection = NULL;- _download_close(conn);-}--/*- * Check if we're already connected- */-static int-_ftp_isconnected(struct url *url)-{- return (cached_connection- && (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 conn_t *-_ftp_cached_connect(struct url *url, struct url *purl, const char *flags)-{- conn_t *conn;- int e;-- /* set default port */- if (!url->port)- url->port = _download_default_port(url->scheme);-- /* try to use previously cached connection */- if (_ftp_isconnected(url)) {- e = _ftp_cmd(cached_connection, "NOOP");- if (e == FTP_OK || e == FTP_SYNTAX_ERROR)- return (_download_ref(cached_connection));- }-- /* connect to server */- if ((conn = _ftp_connect(url, purl, flags)) == NULL)- return (NULL);- if (cached_connection)- _ftp_disconnect(cached_connection);- cached_connection = _download_ref(conn);- memcpy(&cached_host, url, sizeof(*url));- return (conn);-}--/*- * Check the proxy settings- */-static struct url *-_ftp_get_proxy(const char *flags)-{- struct url *purl;- char *p;-- if (flags != NULL && strchr(flags, 'd') != NULL)- return (NULL);- if (((p = getenv("FTP_PROXY")) || (p = getenv("ftp_proxy")) ||- (p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&- *p && (purl = downloadParseURL(p)) != NULL) {- if (!*purl->scheme) {- if (getenv("FTP_PROXY") || getenv("ftp_proxy"))- strcpy(purl->scheme, SCHEME_FTP);- else- strcpy(purl->scheme, SCHEME_HTTP);- }- if (!purl->port)- purl->port = _download_default_proxy_port(purl->scheme);- if (strcasecmp(purl->scheme, SCHEME_FTP) == 0 ||- strcasecmp(purl->scheme, SCHEME_HTTP) == 0)- return (purl);- downloadFreeURL(purl);- }- return (NULL);-}--/*- * Process an FTP request- */-FILE *-_ftp_request(struct url *url, const char *op, struct url_stat *us,- struct url *purl, const char *flags)-{- conn_t *conn;- int oflag;-- char *no_proxy, *no_proxy_item;- size_t b, s;-- if ((no_proxy = getenv("NO_PROXY")) != NULL) {- b = strlen(url->host);- while ((no_proxy_item = strsep(&no_proxy, ",")) != NULL) {- s = strlen(no_proxy_item);- if (b > s && strcasecmp(url->host + b - s,- no_proxy_item) == 0) {- /* disable proxy */- downloadFreeURL(purl);- purl = NULL;- break;- }- }- }-- /* check if we should use HTTP instead */- if (purl && strcasecmp(purl->scheme, SCHEME_HTTP) == 0) {- if (strcmp(op, "STAT") == 0)- return (_http_request(url, "HEAD", us, purl, flags));- else if (strcmp(op, "RETR") == 0)- return (_http_request(url, "GET", us, purl, flags));- /*- * Our HTTP code doesn't support PUT requests yet, so try- * a direct connection.- */- }-- /* connect to server */- conn = _ftp_cached_connect(url, purl, flags);- if (purl)- downloadFreeURL(purl);- if (conn == NULL)- return (NULL);-- /* change directory */- if (_ftp_cwd(conn, url->doc) == -1)- return (NULL);-- /* stat file */- if (us && _ftp_stat(conn, url->doc, us) == -1- && downloadLastErrCode != DLERR_PROTO- && downloadLastErrCode != DLERR_UNAVAIL)- return (NULL);-- /* just a stat */- if (strcmp(op, "STAT") == 0)- return (FILE *)1; /* bogus return value */- if (strcmp(op, "STOR") == 0 || strcmp(op, "APPE") == 0)- oflag = O_WRONLY;- else- oflag = O_RDONLY;-- /* initiate the transfer */- return (_ftp_transfer(conn, op, url->doc, oflag, url->offset, flags));-}--/*- * Get and stat file- */-FILE *-downloadXGetFTP(struct url *url, struct url_stat *us, const char *flags)-{- return (_ftp_request(url, "RETR", us, _ftp_get_proxy(flags), flags));-}--/*- * Get file- */-FILE *-downloadGetFTP(struct url *url, const char *flags)-{- return (downloadXGetFTP(url, NULL, flags));-}--/*- * Get file stats- */-int-downloadStatFTP(struct url *url, struct url_stat *us, const char *flags)-{- FILE *f;-- f = _ftp_request(url, "STAT", us, _ftp_get_proxy(flags), flags);- if (f == NULL)- return (-1);- return (0);-}
− cbits/ftperr.h
@@ -1,45 +0,0 @@-static struct downloaderr _ftp_errlist[] = {- { 110, DLERR_OK, "Restart marker reply" },- { 120, DLERR_TEMP, "Service ready in a few minutes" },- { 125, DLERR_OK, "Data connection already open; transfer starting" },- { 150, DLERR_OK, "File status okay; about to open data connection" },- { 200, DLERR_OK, "Command okay" },- { 202, DLERR_PROTO, "Command not implemented, superfluous at this site" },- { 211, DLERR_INFO, "System status, or system help reply" },- { 212, DLERR_INFO, "Directory status" },- { 213, DLERR_INFO, "File status" },- { 214, DLERR_INFO, "Help message" },- { 215, DLERR_INFO, "Set system type" },- { 220, DLERR_OK, "Service ready for new user" },- { 221, DLERR_OK, "Service closing control connection" },- { 225, DLERR_OK, "Data connection open; no transfer in progress" },- { 226, DLERR_OK, "Requested file action successful" },- { 227, DLERR_OK, "Entering Passive Mode" },- { 229, DLERR_OK, "Entering Extended Passive Mode" },- { 230, DLERR_OK, "User logged in, proceed" },- { 250, DLERR_OK, "Requested file action okay, completed" },- { 257, DLERR_OK, "File/directory created" },- { 331, DLERR_AUTH, "User name okay, need password" },- { 332, DLERR_AUTH, "Need account for login" },- { 350, DLERR_OK, "Requested file action pending further information" },- { 421, DLERR_DOWN, "Service not available, closing control connection" },- { 425, DLERR_NETWORK, "Can't open data connection" },- { 426, DLERR_ABORT, "Connection closed; transfer aborted" },- { 450, DLERR_UNAVAIL, "File unavailable (e.g., file busy)" },- { 451, DLERR_SERVER, "Requested action aborted: local error in processing" },- { 452, DLERR_FULL, "Insufficient storage space in system" },- { 500, DLERR_PROTO, "Syntax error, command unrecognized" },- { 501, DLERR_PROTO, "Syntax error in parameters or arguments" },- { 502, DLERR_PROTO, "Command not implemented" },- { 503, DLERR_PROTO, "Bad sequence of commands" },- { 504, DLERR_PROTO, "Command not implemented for that parameter" },- { 530, DLERR_AUTH, "Not logged in" },- { 532, DLERR_AUTH, "Need account for storing files" },- { 535, DLERR_PROTO, "Bug in MediaHawk Video Kernel FTP server" },- { 550, DLERR_UNAVAIL, "File unavailable (e.g., file not found, no access)" },- { 551, DLERR_PROTO, "Requested action aborted. Page type unknown" },- { 552, DLERR_FULL, "Exceeded storage allocation" },- { 553, DLERR_EXISTS, "File name not allowed" },- { 999, DLERR_PROTO, "Protocol error" },- { -1, DLERR_UNKNOWN, "Unknown FTP error" }-};
− cbits/hs_download_utils.c
@@ -1,9 +0,0 @@-#include "hs_download_utils.h"--char * hs_download_last_error () {- return downloadLastErrString;-}--int hs_download_last_error_code () {- return downloadLastErrCode;-}
− cbits/hs_download_utils.h
@@ -1,4 +0,0 @@-#include "download.h"--char * hs_download_last_error ();-int hs_download_last_error_code ();
− cbits/http.c
@@ -1,1226 +0,0 @@-/*-- * Copyright (c) 2006 Aaron Griffin <aaronmgriffin@gmail.com>- *- * Based on original libfetch code from:- * Copyright (c) 2000-2004 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.- */--#include <sys/cdefs.h>--/*- * The following copyright applies to the base64 code:- *- *-- * 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 <sys/socket.h>--#include <ctype.h>-#include <libio.h>-#include <err.h>-#include <errno.h>-#include <locale.h>-#include <netdb.h>-#include <stdarg.h>-#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#include <time.h>-#include <unistd.h>--#include <netinet/in.h>-#include <netinet/tcp.h>--#include "download.h"-#include "common.h"-#include "httperr.h"--/* Maximum number of redirects to follow */-#define MAX_REDIRECT 5--/* Symbolic names for reply codes we care about */-#define HTTP_OK 200-#define HTTP_PARTIAL 206-#define HTTP_MOVED_PERM 301-#define HTTP_MOVED_TEMP 302-#define HTTP_SEE_OTHER 303-#define HTTP_NEED_AUTH 401-#define HTTP_NEED_PROXY_AUTH 407-#define HTTP_BAD_RANGE 416-#define HTTP_PROTOCOL_ERROR 999--#define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \- || (xyz) == HTTP_MOVED_TEMP \- || (xyz) == HTTP_SEE_OTHER)--#define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)--/* for easy redirection */-FILE *_ftp_request(struct url *, const char *, struct url_stat *,- struct url *, const char *);--/*****************************************************************************- * I/O functions for decoding chunked streams- */--struct httpio-{- conn_t *conn; /* connection */- int chunked; /* chunked mode */- char *buf; /* chunk buffer */- size_t bufsize; /* size of chunk buffer */- ssize_t buflen; /* amount of data currently in buffer */- int bufpos; /* current read offset in buffer */- int eof; /* end-of-file flag */- int error; /* error flag */- size_t chunksize; /* remaining size of current chunk */-#ifdef DEBUG- size_t total;-#endif-};--/*- * Get next chunk header- */-static int-_http_new_chunk(struct httpio *io)-{- char hexdigits[23] = "0123456789abcdefABCDEF\0";- char *p = NULL;-- if (_download_getln(io->conn) == -1)- return (-1);-- if (io->conn->buflen < 2 || !strchr(hexdigits, *io->conn->buf))- return (-1);-- for (p = io->conn->buf; *p && !isspace(*p); ++p) {- if (*p == ';')- break;- if (!strchr(hexdigits, *p))- return (-1);- if (isdigit(*p)) {- io->chunksize = io->chunksize * 16 +- *p - '0';- } else {- io->chunksize = io->chunksize * 16 +- 10 + tolower(*p) - 'a';- }- }--#ifdef DEBUG- if (downloadDebug) {- io->total += io->chunksize;- if (io->chunksize == 0)- fprintf(stderr, "%s(): end of last chunk\n", __func__);- else- fprintf(stderr, "%s(): new chunk: %lu (%lu)\n",- __func__, (unsigned long)io->chunksize,- (unsigned long)io->total);- }-#endif-- return (io->chunksize);-}--/*- * Grow the input buffer to at least len bytes- */-static inline int-_http_growbuf(struct httpio *io, size_t len)-{- char *tmp;-- if (io->bufsize >= len)- return (0);-- if ((tmp = realloc(io->buf, len)) == NULL)- return (-1);- io->buf = tmp;- io->bufsize = len;- return (0);-}--/*- * Fill the input buffer, do chunk decoding on the fly- */-static int-_http_fillbuf(struct httpio *io, size_t len)-{- if (io->error)- return (-1);- if (io->eof)- return (0);-- if (io->chunked == 0) {- if (_http_growbuf(io, len) == -1)- return (-1);- if ((io->buflen = _download_read(io->conn, io->buf, len)) == -1) {- io->error = 1;- return (-1);- }- io->bufpos = 0;- return (io->buflen);- }-- if (io->chunksize == 0) {- switch (_http_new_chunk(io)) {- case -1:- io->error = 1;- return (-1);- case 0:- io->eof = 1;- return (0);- }- }-- if (len > io->chunksize)- len = io->chunksize;- if (_http_growbuf(io, len) == -1)- return (-1);- if ((io->buflen = _download_read(io->conn, io->buf, len)) == -1) {- io->error = 1;- return (-1);- }- io->chunksize -= io->buflen;-- if (io->chunksize == 0) {- char endl[2];-- if (_download_read(io->conn, endl, 2) != 2 ||- endl[0] != '\r' || endl[1] != '\n')- return (-1);- }-- io->bufpos = 0;-- return (io->buflen);-}--/*- * Read function- */-static int-_http_readfn(void *v, char *buf, int len)-{- struct httpio *io = (struct httpio *)v;- int l, pos;-- if (io->error)- return (-1);- if (io->eof)- return (0);-- for (pos = 0; len > 0; pos += l, len -= l) {- /* empty buffer */- if (!io->buf || io->bufpos == io->buflen)- if (_http_fillbuf(io, len) < 1)- break;- l = io->buflen - io->bufpos;- if (len < l)- l = len;- bcopy(io->buf + io->bufpos, buf + pos, l);- io->bufpos += l;- }-- if (!pos && io->error)- return (-1);- return (pos);-}--/*- * Write function- */-static int-_http_writefn(void *v, const char *buf, int len)-{- struct httpio *io = (struct httpio *)v;-- return (_download_write(io->conn, buf, len));-}--/*- * Close function- */-static int-_http_closefn(void *v)-{- struct httpio *io = (struct httpio *)v;- int r;-- r = _download_close(io->conn);- if (io->buf)- free(io->buf);- free(io);- return (r);-}--/*- * Wrap a file descriptor up- */-static FILE *-_http_funopen(conn_t *conn, int chunked)-{- struct httpio *io;- FILE *f;-- if ((io = calloc(1, sizeof(*io))) == NULL) {- _download_syserr();- return (NULL);- }- io->conn = conn;- io->chunked = chunked;- cookie_io_functions_t cookie;- cookie.read = (cookie_read_function_t *)_http_readfn;- cookie.write = (cookie_write_function_t *)_http_writefn;- cookie.seek = (cookie_seek_function_t *)NULL;- cookie.close = (cookie_close_function_t *)_http_closefn;- f = fopencookie(io, "rw", cookie);- if (f == NULL) {- _download_syserr();- free(io);- return (NULL);- }- return (f);-}---/*****************************************************************************- * Helper functions for talking to the server and parsing its replies- */--/* Header types */-typedef enum {- hdr_syserror = -2,- hdr_error = -1,- hdr_end = 0,- hdr_unknown = 1,- hdr_content_length,- hdr_content_range,- hdr_last_modified,- hdr_location,- hdr_transfer_encoding,- hdr_www_authenticate-} hdr_t;--/* Names of interesting headers */-static struct {- hdr_t num;- const char *name;-} hdr_names[] = {- { hdr_content_length, "Content-Length" },- { hdr_content_range, "Content-Range" },- { hdr_last_modified, "Last-Modified" },- { hdr_location, "Location" },- { hdr_transfer_encoding, "Transfer-Encoding" },- { hdr_www_authenticate, "WWW-Authenticate" },- { hdr_unknown, NULL },-};--/*- * Send a formatted line; optionally echo to terminal- */-static int-_http_cmd(conn_t *conn, const char *fmt, ...)-{- va_list ap;- size_t len;- char *msg;- int r;-- va_start(ap, fmt);- len = vasprintf(&msg, fmt, ap);- va_end(ap);-- if (msg == NULL) {- errno = ENOMEM;- _download_syserr();- return (-1);- }-- r = _download_putln(conn, msg, len);- free(msg);-- if (r == -1) {- _download_syserr();- return (-1);- }-- return (0);-}--/*- * Get and parse status line- */-static int-_http_get_reply(conn_t *conn)-{- char *p;-- if (_download_getln(conn) == -1)- return (-1);- /*- * A valid status line looks like "HTTP/m.n xyz reason" where m- * and n are the major and minor protocol version numbers and xyz- * is the reply code.- * Unfortunately, there are servers out there (NCSA 1.5.1, to name- * just one) that do not send a version number, so we can't rely- * on finding one, but if we do, insist on it being 1.0 or 1.1.- * We don't care about the reason phrase.- */- if (strncmp(conn->buf, "HTTP", 4) != 0)- return (HTTP_PROTOCOL_ERROR);- p = conn->buf + 4;- if (*p == '/') {- if (p[1] != '1' || p[2] != '.' || (p[3] != '0' && p[3] != '1'))- return (HTTP_PROTOCOL_ERROR);- p += 4;- }- if (*p != ' ' || !isdigit(p[1]) || !isdigit(p[2]) || !isdigit(p[3]))- return (HTTP_PROTOCOL_ERROR);-- conn->err = (p[1] - '0') * 100 + (p[2] - '0') * 10 + (p[3] - '0');- return (conn->err);-}--/*- * Check a header; if the type matches the given string, return a pointer- * to the beginning of the value.- */-static const char *-_http_match(const char *str, const char *hdr)-{- while (*str && *hdr && tolower(*str++) == tolower(*hdr++))- /* nothing */;- if (*str || *hdr != ':')- return (NULL);- while (*hdr && isspace(*++hdr))- /* nothing */;- return (hdr);-}--/*- * Get the next header and return the appropriate symbolic code.- */-static hdr_t-_http_next_header(conn_t *conn, const char **p)-{- int i;-- if (_download_getln(conn) == -1)- return (hdr_syserror);- while (conn->buflen && isspace(conn->buf[conn->buflen - 1]))- conn->buflen--;- conn->buf[conn->buflen] = '\0';- if (conn->buflen == 0)- return (hdr_end);- /*- * We could check for malformed headers but we don't really care.- * A valid header starts with a token immediately followed by a- * colon; a token is any sequence of non-control, non-whitespace- * characters except "()<>@,;:\\\"{}".- */- for (i = 0; hdr_names[i].num != hdr_unknown; i++)- if ((*p = _http_match(hdr_names[i].name, conn->buf)) != NULL)- return (hdr_names[i].num);- return (hdr_unknown);-}--/*- * Parse a last-modified header- */-static int-_http_parse_mtime(const char *p, time_t *mtime)-{- char locale[64], *r;- struct tm tm;-- strncpy(locale, setlocale(LC_TIME, NULL), sizeof(locale));- setlocale(LC_TIME, "C");- r = (char *)strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm);- /* XXX should add support for date-2 and date-3 */- setlocale(LC_TIME, locale);- if (r == NULL)- return (-1);- DBG(fprintf(stderr, "last modified: [%04d-%02d-%02d "- "%02d:%02d:%02d]\n",- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,- tm.tm_hour, tm.tm_min, tm.tm_sec));- *mtime = timegm(&tm);- return (0);-}--/*- * Parse a content-length header- */-static int-_http_parse_length(const char *p, off_t *length)-{- off_t len;-- for (len = 0; *p && isdigit(*p); ++p)- len = len * 10 + (*p - '0');- if (*p)- return (-1);- DBG(fprintf(stderr, "content length: [%lld]\n",- (long long)len));- *length = len;- return (0);-}--/*- * Parse a content-range header- */-static int-_http_parse_range(const char *p, off_t *offset, off_t *length, off_t *size)-{- off_t first, last, len;-- if (strncasecmp(p, "bytes ", 6) != 0)- return (-1);- p += 6;- if (*p == '*') {- first = last = -1;- ++p;- } else {- for (first = 0; *p && isdigit(*p); ++p)- first = first * 10 + *p - '0';- if (*p != '-')- return (-1);- for (last = 0, ++p; *p && isdigit(*p); ++p)- last = last * 10 + *p - '0';- }- if (first > last || *p != '/')- return (-1);- for (len = 0, ++p; *p && isdigit(*p); ++p)- len = len * 10 + *p - '0';- if (*p || len < last - first + 1)- return (-1);- if (first == -1) {- DBG(fprintf(stderr, "content range: [*/%lld]\n",- (long long)len));- *length = 0;- } else {- DBG(fprintf(stderr, "content range: [%lld-%lld/%lld]\n",- (long long)first, (long long)last, (long long)len));- *length = last - first + 1;- }- *offset = first;- *size = len;- return (0);-}---/*****************************************************************************- * Helper functions for authorization- */--/*- * Base64 encoding- */-static char *-_http_base64(const char *src)-{- static const char base64[] =- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"- "abcdefghijklmnopqrstuvwxyz"- "0123456789+/";- char *str, *dst;- size_t l;- int t, r;-- l = strlen(src);- if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL)- return (NULL);- dst = str;- 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 (str);-}--/*- * Encode username and password- */-static int-_http_basic_auth(conn_t *conn, const char *hdr, const char *usr, const char *pwd)-{- char *upw, *auth;- int r;-- DBG(fprintf(stderr, "usr: [%s]\n", usr));- DBG(fprintf(stderr, "pwd: [%s]\n", pwd));- if (asprintf(&upw, "%s:%s", usr, pwd) == -1)- return (-1);- auth = _http_base64(upw);- free(upw);- if (auth == NULL)- return (-1);- r = _http_cmd(conn, "%s: Basic %s", hdr, auth);- free(auth);- return (r);-}--/*- * Send an authorization header- */-static int-_http_authorize(conn_t *conn, const char *hdr, const char *p)-{- /* basic authorization */- if (strncasecmp(p, "basic:", 6) == 0) {- char *user, *pwd, *str;- int r;-- /* skip realm */- for (p += 6; *p && *p != ':'; ++p)- /* nothing */ ;- if (!*p || strchr(++p, ':') == NULL)- return (-1);- if ((str = strdup(p)) == NULL)- return (-1); /* XXX */- user = str;- pwd = strchr(str, ':');- *pwd++ = '\0';- r = _http_basic_auth(conn, hdr, user, pwd);- free(str);- return (r);- }- return (-1);-}---/*****************************************************************************- * Helper functions for connecting to a server or proxy- */--/*- * Connect to the correct HTTP server or proxy.- */-static conn_t *-_http_connect(struct url *URL, struct url *purl, const char *flags)-{- conn_t *conn;- int verbose;- int af;--#ifdef INET6- af = AF_UNSPEC;-#else- af = AF_INET;-#endif-- verbose = CHECK_FLAG('v');- if (CHECK_FLAG('4'))- af = AF_INET;-#ifdef INET6- else if (CHECK_FLAG('6'))- af = AF_INET6;-#endif-- if (purl && strcasecmp(URL->scheme, SCHEME_HTTPS) != 0) {- 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 (NULL);- }-- if ((conn = _download_connect(URL->host, URL->port, af, verbose)) == NULL)- /* _download_connect() has already set an error code */- return (NULL);- if (strcasecmp(URL->scheme, SCHEME_HTTPS) == 0 &&- _download_ssl(conn, verbose) == -1) {- _download_close(conn);- /* grrr */- _download_syserr();- return (NULL);- }--#ifdef TCP_NOPUSH- int val = 1;- setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val));-#endif-- return (conn);-}--static struct url *-_http_get_proxy(const char *flags)-{- struct url *purl;- char *p;-- if (flags != NULL && strchr(flags, 'd') != NULL)- return (NULL);- if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) &&- *p && (purl = downloadParseURL(p))) {- if (!*purl->scheme)- strcpy(purl->scheme, SCHEME_HTTP);- if (!purl->port)- purl->port = _download_default_proxy_port(purl->scheme);- if (strcasecmp(purl->scheme, SCHEME_HTTP) == 0)- return (purl);- downloadFreeURL(purl);- }- return (NULL);-}--static void-_http_print_html(FILE *out, FILE *in)-{- size_t len;- unsigned int sz = 1024;- char *line, *p, *q;- int comment, tag;-- line = (char *)calloc(1024, sizeof(char));- comment = tag = 0;- while ((len = getline(&line, &sz, in)) > 0) {- while (len && isspace(line[len - 1]))- --len;- for (p = q = line; q < line + len; ++q) {- if (comment && *q == '-') {- if (q + 2 < line + len &&- strcmp(q, "-->") == 0) {- tag = comment = 0;- q += 2;- }- } else if (tag && !comment && *q == '>') {- p = q + 1;- tag = 0;- } else if (!tag && *q == '<') {- if (q > p)- fwrite(p, q - p, 1, out);- tag = 1;- if (q + 3 < line + len &&- strcmp(q, "<!--") == 0) {- comment = 1;- q += 3;- }- }- }- if (!tag && q > p)- fwrite(p, q - p, 1, out);- fputc('\n', out);- }-}---/*****************************************************************************- * Core- */--/*- * Send a request and process the reply- *- * XXX This function is way too long, the do..while loop should be split- * XXX off into a separate function.- */-FILE *-_http_request(struct url *URL, const char *op, struct url_stat *us,- struct url *purl, const char *flags)-{- conn_t *conn;- struct url *url, *new;- int chunked, direct, need_auth, noredirect, verbose;- int e, i, n, val;- off_t offset, clength, length, size;- time_t mtime;- const char *p;- FILE *f;- hdr_t h;- char hbuf[MAXHOSTNAMELEN + 7], *host;-- char *no_proxy, *no_proxy_item;- size_t b, s;-- if ((no_proxy = getenv("NO_PROXY")) != NULL) {- b = strlen(URL->host);- while ((no_proxy_item = strsep(&no_proxy, ",")) != NULL) {- s = strlen(no_proxy_item);- if (b > s && strcasecmp(URL->host + b - s,- no_proxy_item) == 0) {- /* disable proxy */- downloadFreeURL(purl);- purl = NULL;- break;- }- }- }-- direct = CHECK_FLAG('d');- noredirect = CHECK_FLAG('A');- verbose = CHECK_FLAG('v');-- if (direct && purl) {- downloadFreeURL(purl);- purl = NULL;- }-- /* try the provided URL first */- url = URL;-- /* if the A flag is set, we only get one try */- n = noredirect ? 1 : MAX_REDIRECT;- i = 0;-- e = HTTP_PROTOCOL_ERROR;- need_auth = 0;- do {- new = NULL;- chunked = 0;- offset = 0;- clength = -1;- length = -1;- size = -1;- mtime = 0;-- /* check port */- if (!url->port)- url->port = _download_default_port(url->scheme);-- /* were we redirected to an FTP URL? */- if (purl == NULL && strcmp(url->scheme, SCHEME_FTP) == 0) {- if (strcmp(op, "GET") == 0)- return (_ftp_request(url, "RETR", us, purl, flags));- else if (strcmp(op, "HEAD") == 0)- return (_ftp_request(url, "STAT", us, purl, flags));- }-- /* connect to server or proxy */- if ((conn = _http_connect(url, purl, flags)) == NULL)- goto ouch;-- host = url->host;-#ifdef INET6- if (strchr(url->host, ':')) {- snprintf(hbuf, sizeof(hbuf), "[%s]", url->host);- host = hbuf;- }-#endif- if (url->port != _download_default_port(url->scheme)) {- if (host != hbuf) {- strcpy(hbuf, host);- host = hbuf;- }- snprintf(hbuf + strlen(hbuf),- sizeof(hbuf) - strlen(hbuf), ":%d", url->port);- }-- /* send request */- if (verbose)- _download_info("requesting %s://%s%s",- url->scheme, host, url->doc);- if (purl) {- _http_cmd(conn, "%s %s://%s%s HTTP/1.1",- op, url->scheme, host, url->doc);- } else {- _http_cmd(conn, "%s %s HTTP/1.1",- op, url->doc);- }-- /* virtual host */- _http_cmd(conn, "Host: %s", host);-- /* proxy authorization */- if (purl) {- if (*purl->user || *purl->pwd)- _http_basic_auth(conn, "Proxy-Authorization",- purl->user, purl->pwd);- else if ((p = getenv("HTTP_PROXY_AUTH")) != NULL && *p != '\0')- _http_authorize(conn, "Proxy-Authorization", p);- }-- /* server authorization */- if (need_auth || *url->user || *url->pwd) {- if (*url->user || *url->pwd)- _http_basic_auth(conn, "Authorization", url->user, url->pwd);- else if ((p = getenv("HTTP_AUTH")) != NULL && *p != '\0')- _http_authorize(conn, "Authorization", p);- else if (downloadAuthMethod && downloadAuthMethod(url) == 0) {- _http_basic_auth(conn, "Authorization", url->user, url->pwd);- } else {- _http_seterr(HTTP_NEED_AUTH);- goto ouch;- }- }-- /* other headers */- if ((p = getenv("HTTP_REFERER")) != NULL && *p != '\0') {- if (strcasecmp(p, "auto") == 0)- _http_cmd(conn, "Referer: %s://%s%s",- url->scheme, host, url->doc);- else- _http_cmd(conn, "Referer: %s", p);- }- if ((p = getenv("HTTP_USER_AGENT")) != NULL && *p != '\0')- _http_cmd(conn, "User-Agent: %s", p);- else- _http_cmd(conn, "User-Agent: " USER_AGENT_STRING);- if (url->offset > 0)- _http_cmd(conn, "Range: bytes=%lld-", (long long)url->offset);- _http_cmd(conn, "Connection: close");- _http_cmd(conn, "");-- /*- * Force the queued request to be dispatched. Normally, one- * would do this with shutdown(2) but squid proxies can be- * configured to disallow such half-closed connections. To- * be compatible with such configurations, fiddle with socket- * options to force the pending data to be written.- */-#ifdef TCP_NOPUSH- val = 0;- setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val,- sizeof(val));-#endif- val = 1;- setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val,- sizeof(val));-- /* get reply */- switch (_http_get_reply(conn)) {- case HTTP_OK:- case HTTP_PARTIAL:- /* fine */- break;- case HTTP_MOVED_PERM:- case HTTP_MOVED_TEMP:- case HTTP_SEE_OTHER:- /*- * Not so fine, but we still have to read the- * headers to get the new location.- */- break;- case HTTP_NEED_AUTH:- if (need_auth) {- /*- * We already sent out authorization code,- * so there's nothing more we can do.- */- _http_seterr(conn->err);- goto ouch;- }- /* try again, but send the password this time */- if (verbose)- _download_info("server requires authorization");- break;- case HTTP_NEED_PROXY_AUTH:- /*- * If we're talking to a proxy, we already sent- * our proxy authorization code, so there's- * nothing more we can do.- */- _http_seterr(conn->err);- goto ouch;- case HTTP_BAD_RANGE:- /*- * This can happen if we ask for 0 bytes because- * we already have the whole file. Consider this- * a success for now, and check sizes later.- */- break;- case HTTP_PROTOCOL_ERROR:- /* fall through */- case -1:- _download_syserr();- goto ouch;- default:- _http_seterr(conn->err);- goto ouch;- /* fall through so we can get the full error message */- }-- /* get headers */- do {- switch ((h = _http_next_header(conn, &p))) {- case hdr_syserror:- _download_syserr();- goto ouch;- case hdr_error:- _http_seterr(HTTP_PROTOCOL_ERROR);- goto ouch;- case hdr_content_length:- _http_parse_length(p, &clength);- break;- case hdr_content_range:- _http_parse_range(p, &offset, &length, &size);- break;- case hdr_last_modified:- _http_parse_mtime(p, &mtime);- break;- case hdr_location:- if (!HTTP_REDIRECT(conn->err))- break;- if (new)- free(new);- if (verbose)- _download_info("%d redirect to %s", conn->err, p);- if (*p == '/')- /* absolute path */- new = downloadMakeURL(url->scheme, url->host, url->port, p,- url->user, url->pwd);- else- new = downloadParseURL(p);- if (new == NULL) {- /* XXX should set an error code */- DBG(fprintf(stderr, "failed to parse new URL\n"));- goto ouch;- }- if (!*new->user && !*new->pwd) {- strcpy(new->user, url->user);- strcpy(new->pwd, url->pwd);- }- new->offset = url->offset;- new->length = url->length;- break;- case hdr_transfer_encoding:- /* XXX weak test*/- chunked = (strcasecmp(p, "chunked") == 0);- break;- case hdr_www_authenticate:- if (conn->err != HTTP_NEED_AUTH)- break;- /* if we were smarter, we'd check the method and realm */- break;- case hdr_end:- /* fall through */- case hdr_unknown:- /* ignore */- break;- }- } while (h > hdr_end);-- /* we need to provide authentication */- if (conn->err == HTTP_NEED_AUTH) {- e = conn->err;- need_auth = 1;- _download_close(conn);- conn = NULL;- continue;- }-- /* requested range not satisfiable */- if (conn->err == HTTP_BAD_RANGE) {- if (url->offset == size && url->length == 0) {- /* asked for 0 bytes; fake it */- offset = url->offset;- conn->err = HTTP_OK;- break;- } else {- _http_seterr(conn->err);- goto ouch;- }- }-- /* we have a hit or an error */- if (conn->err == HTTP_OK || conn->err == HTTP_PARTIAL || HTTP_ERROR(conn->err))- break;-- /* all other cases: we got a redirect */- e = conn->err;- need_auth = 0;- _download_close(conn);- conn = NULL;- if (!new) {- DBG(fprintf(stderr, "redirect with no new location\n"));- break;- }- if (url != URL)- downloadFreeURL(url);- url = new;- } while (++i < n);-- /* we failed, or ran out of retries */- if (conn == NULL) {- _http_seterr(e);- goto ouch;- }-- DBG(fprintf(stderr, "offset %lld, length %lld,"- " size %lld, clength %lld\n",- (long long)offset, (long long)length,- (long long)size, (long long)clength));-- /* check for inconsistencies */- if (clength != -1 && length != -1 && clength != length) {- _http_seterr(HTTP_PROTOCOL_ERROR);- goto ouch;- }- if (clength == -1)- clength = length;- if (clength != -1)- length = offset + clength;- if (length != -1 && size != -1 && length != size) {- _http_seterr(HTTP_PROTOCOL_ERROR);- goto ouch;- }- if (size == -1)- size = length;-- /* fill in stats */- if (us) {- us->size = size;- us->atime = us->mtime = mtime;- }-- /* too far? */- if (URL->offset > 0 && offset > URL->offset) {- _http_seterr(HTTP_PROTOCOL_ERROR);- goto ouch;- }-- /* report back real offset and size */- URL->offset = offset;- URL->length = clength;-- /* wrap it up in a FILE */- if ((f = _http_funopen(conn, chunked)) == NULL) {- _download_syserr();- goto ouch;- }-- if (url != URL)- downloadFreeURL(url);- if (purl)- downloadFreeURL(purl);-- if (HTTP_ERROR(conn->err)) {- _http_print_html(stderr, f);- fclose(f);- f = NULL;- }-- return (f);--ouch:- if (url != URL)- downloadFreeURL(url);- if (purl)- downloadFreeURL(purl);- if (conn != NULL)- _download_close(conn);- return (NULL);-}---/*****************************************************************************- * Entry points- */--/*- * Retrieve and stat a file by HTTP- */-FILE *-downloadXGetHTTP(struct url *URL, struct url_stat *us, const char *flags)-{- return (_http_request(URL, "GET", us, _http_get_proxy(flags), flags));-}--/*- * Retrieve a file by HTTP- */-FILE *-downloadGetHTTP(struct url *URL, const char *flags)-{- return (downloadXGetHTTP(URL, NULL, flags));-}--/*- * Get an HTTP document's metadata- */-int-downloadStatHTTP(struct url *URL, struct url_stat *us, const char *flags)-{- FILE *f;-- f = _http_request(URL, "HEAD", us, _http_get_proxy(flags), flags);- if (f == NULL)- return (-1);- fclose(f);- return (0);-}
− cbits/httperr.h
@@ -1,44 +0,0 @@-static struct downloaderr _http_errlist[] = {- { 100, DLERR_OK, "Continue" },- { 101, DLERR_OK, "Switching Protocols" },- { 200, DLERR_OK, "OK" },- { 201, DLERR_OK, "Created" },- { 202, DLERR_OK, "Accepted" },- { 203, DLERR_INFO, "Non-Authoritative Information" },- { 204, DLERR_OK, "No Content" },- { 205, DLERR_OK, "Reset Content" },- { 206, DLERR_OK, "Partial Content" },- { 300, DLERR_MOVED, "Multiple Choices" },- { 301, DLERR_MOVED, "Moved Permanently" },- { 302, DLERR_MOVED, "Moved Temporarily" },- { 303, DLERR_MOVED, "See Other" },- { 304, DLERR_OK, "Not Modified" },- { 305, DLERR_INFO, "Use Proxy" },- { 307, DLERR_MOVED, "Temporary Redirect" },- { 400, DLERR_PROTO, "Bad Request" },- { 401, DLERR_AUTH, "Unauthorized" },- { 402, DLERR_AUTH, "Payment Required" },- { 403, DLERR_AUTH, "Forbidden" },- { 404, DLERR_UNAVAIL, "Not Found" },- { 405, DLERR_PROTO, "Method Not Allowed" },- { 406, DLERR_PROTO, "Not Acceptable" },- { 407, DLERR_AUTH, "Proxy Authentication Required" },- { 408, DLERR_TIMEOUT, "Request Time-out" },- { 409, DLERR_EXISTS, "Conflict" },- { 410, DLERR_UNAVAIL, "Gone" },- { 411, DLERR_PROTO, "Length Required" },- { 412, DLERR_SERVER, "Precondition Failed" },- { 413, DLERR_PROTO, "Request Entity Too Large" },- { 414, DLERR_PROTO, "Request-URI Too Large" },- { 415, DLERR_PROTO, "Unsupported Media Type" },- { 416, DLERR_UNAVAIL, "Requested Range Not Satisfiable" },- { 417, DLERR_SERVER, "Expectation Failed" },- { 500, DLERR_SERVER, "Internal Server Error" },- { 501, DLERR_PROTO, "Not Implemented" },- { 502, DLERR_SERVER, "Bad Gateway" },- { 503, DLERR_TEMP, "Service Unavailable" },- { 504, DLERR_TIMEOUT, "Gateway Time-out" },- { 505, DLERR_PROTO, "HTTP Version not supported" },- { 999, DLERR_PROTO, "Protocol error" },- { -1, DLERR_UNKNOWN, "Unknown HTTP error" }-};
download-curl.cabal view
@@ -1,5 +1,5 @@ name: download-curl-version: 0.1.3+version: 0.1.4 homepage: http://code.haskell.org/~dons/code/download-curl synopsis: High-level file download based on URLs description: High-level file download based on URLs@@ -56,4 +56,4 @@ build-depends: base >= 3 && < 6, bytestring else build-depends: base > 3 && < 4- build-depends: curl, tagsoup >= 0.8 && < 0.11, feed, xml+ build-depends: curl, tagsoup >= 0.8 && < 0.13, feed, xml