summaryrefslogtreecommitdiff
path: root/usr.bin/fetch
diff options
context:
space:
mode:
authorJoerg Wunsch <joerg@FreeBSD.org>1997-02-22 19:14:04 +0000
committerJoerg Wunsch <joerg@FreeBSD.org>1997-02-22 19:14:04 +0000
commit5f9fe6fb8a279bb4e3b2a92b1e5b7fa8ef7191d6 (patch)
tree2c08c19a78b58c37ae5abd7c4eae801b69620e0b /usr.bin/fetch
parentb0da4f99a316da96968ad7f1c9b4386713af6e9a (diff)
Notes
Diffstat (limited to 'usr.bin/fetch')
-rw-r--r--usr.bin/fetch/Makefile8
-rw-r--r--usr.bin/fetch/fetch.1312
-rw-r--r--usr.bin/fetch/main.c928
3 files changed, 477 insertions, 771 deletions
diff --git a/usr.bin/fetch/Makefile b/usr.bin/fetch/Makefile
index 6e86642107cd..31479bdec0a4 100644
--- a/usr.bin/fetch/Makefile
+++ b/usr.bin/fetch/Makefile
@@ -1,9 +1,9 @@
PROG = fetch
-SRCS = main.c
+SRCS = file.c ftp.c http.c main.c util.c uri.c
-CFLAGS+= -Wall
+CFLAGS+= -Wall -Wwrite-strings -Wmissing-prototypes
-DPADD= ${LIBFTPIO}
-LDADD= -lftpio
+DPADD= ${LIBFTPIO} ${LIBMD}
+LDADD= -lftpio -lmd
.include <bsd.prog.mk>
diff --git a/usr.bin/fetch/fetch.1 b/usr.bin/fetch/fetch.1
index f2309ecf4877..8223cbd7deda 100644
--- a/usr.bin/fetch/fetch.1
+++ b/usr.bin/fetch/fetch.1
@@ -1,17 +1,18 @@
-.\" $Id: fetch.1,v 1.8 1996/09/19 18:07:24 peter Exp $
+.\" $FreeBSD$
.Dd July 2, 1996
.Dt FETCH 1
-.Os
+.Os FreeBSD 3.0
.Sh NAME
.Nm fetch
.Nd retrieve a file by Uniform Resource Locator
.Sh SYNOPSIS
.Nm fetch
-.Op Fl MPmnpqr
+.Op Fl MPamnpqr
.Op Fl o Ar file
.Ar URL
+.Op Ar ...
.Nm fetch
-.Op Fl MPmnpqr
+.Op Fl MPRmnpqr
.Op Fl o Ar file
.Op Fl c Ar dir
.Fl f Ar file
@@ -20,28 +21,23 @@
.Nm fetch
allows a user to transfer files from a remote network site using
either the
-.Em ftp
+.Tn FTP
or the
-.Em http
+.Tn HTTP
protocol. In the first form of the command, the
.Ar URL
may be of the form
-.Em http://site.domain/path/to/the/file
+.Li http://site.domain/path/to/the/file
or
-.Em ftp://site.domain/path/to/the/file.
-For compatibility with
-.Xr tftp 1
-the form
-.Em site.domain:/path/to/the/file
-is also accepted.
-To denote a local filename to be copied or linked to (see
+.Li ftp://site.domain/path/to/the/file.
+To denote a local filename to be copied or linked to (see the
.Fl l
-flag), the
+flag below), the
.Em file:/path/to/the/file
URL form is used.
-
+.Pp
The second form of the command can be used to get a file using the
-.Em ftp
+.Tn FTP
protocol, specifying the file name and the remote host with the
.Fl h
and the
@@ -49,35 +45,54 @@ and the
flags.
.Pp
The following options are available:
-.Bl -tag -width Fl -compact
-.It Fl M
-.It Fl m
-Mirror mode: Set the modification time of the file so that it is
-identical to the modification time of the file at the remote host.
-If the file already exists on the local host and is identical (as
-gauged by size and modification time), no transfer is done.
-.It Fl n
-Don't preserve the modtime of the transfered file, use the current time.
-.It Fl P
-.It Fl p
-Use passive mode if you are behind a firewall.
+.Bl -tag -width Fl
+.It Fl a
+Automatically retry the transfer upon soft failures.
.It Fl c Ar dir
-Change to directory
+The file to retrieve is in directory
.Ar dir
-at remote host before starting the transfer.
+on the remote host.
.It Fl f Ar file
-Retrieve
+The file to retrieve is named
.Ar file
on the remote host.
.It Fl h Ar host
-Set the
-.Ar host
-for transfer.
+The file to retrieve is located on the host
+.Ar host .
.It Fl l
If target is a
.Ar file:/
style of URL, make a link to the target rather than trying
to copy it.
+.It Fl M
+.It Fl m
+Mirror mode: Set the modification time of the file so that it is
+identical to the modification time of the file at the remote host.
+If the file already exists on the local host and is identical (as
+gauged by size and modification time), no transfer is done.
+.It Fl n
+Don't preserve the modtime of the transfered file, use the current time.
+.It Fl o Ar file
+Set the output file name to
+.Ar file .
+By default, a ``pathname'' is extracted from the specified URI, and
+its basename is used as the name of the output file. A
+.Ar file
+argument of
+.Sq Li \&-
+indicates that results are to be directed to the standard output.
+.It Fl P
+.It Fl p
+Use the passive mode of the
+.Tn FTP
+protocol. This is useful for crossing certain sorts of firewalls.
+.It Fl q
+Quiet mode. Do not report transfer progress on the terminal.
+.It Fl R
+The filenames specified are ``precious'', and should not be deleted
+under any circumstances, even if the transfer failed or was incomplete.
+.It Fl r
+Restart a previously interrupted transfer.
.It Fl T Ar seconds
Set timeout value to
.Ar seconds.
@@ -86,52 +101,199 @@ Overrides the environment variables
for ftp transfers or
.Ev HTTP_TIMEOUT
for http transfers if set.
-.It Fl q
-Quiet mode. Do not report transfer progress on the terminal.
.It Fl v
-Verbose mode - display FTP connection information in painful detail.
-.It Fl r
-Reget. Use this flag to restart an interrupted transfer.
-.It Fl o Ar file
-Set the output file name to
-.Ar file
+Increase verbosity. More
+.Fl v Ns \&'s
+result in more information.
.El
+.Pp
+Many options are also controlled solely by the environment (this is a
+bug).
+.Sh PROXY SERVERS
+Many sites use application gateways (``proxy servers'') in their
+firewalls in order to allow communication across the firewall using a
+trusted protocol. The
+.Nm fetch
+program can use both the
+.Tn FTP
+and the
+.Tn HTTP
+protocol with a proxy server.
+.Tn FTP
+proxy servers can only relay
+.Tn FTP
+requests;
+.Tn HTTP
+proxy servers can relay both
+.Tn FTP
+and
+.Tn HTTP
+requests.
+A proxy server can be configured by defining an environment variable
+named
+.Dq Va PROTO Ns Ev _PROXY ,
+where
+.Va PROTO
+is the name of the protocol in upper case. The value of the
+environment variable specifies a hostname, optionally followed by a
+colon and a port number.
+.Pp
+The
+.Tn FTP
+proxy client specifies
+.Dq anonymous
+as its user name, and passes the remote user name and host as the
+.Tn FTP
+session's password, in the form
+.Dq Va remoteuser Ns Li \&@ Ns Va remotehost .
+The
+.Tn HTTP
+proxy client simply passes the originally-requested URI to the remote
+server in an
+.Tn HTTP
+.Dq Li GET
+request. HTTP proxy authentication is not yet implemented.
+When multiple proxy protcols are configured,
+.Nm
+will prefer
+.Tn HTTP .
+.Sh HTTP AUTHENTICATION
+The
+.Tn HTTP
+protocol includes support for various methods of authentication.
+Currently, the
+.Dq basic
+method, which provides no security from packet-sniffing or
+man-in-the-middle attacks, is the only method supported in
+.Nm fetch .
+Authentication is enabled by the
+.Ev HTTP_AUTH
+and
+.Ev HTTP_PROXY_AUTH
+environment variables. Both variables have the same format, which
+consists of space-separated list of parameter settings, where each
+setting consists of a colon-separated list of parameters. The first
+two parameters are always the (case-insensitive) authentication scheme
+name and the realm in which authentication is to be performed. If the
+realm is specified as
+.Sq Li \&* ,
+then it will match all realms not specified otherwise.
+.Pp
+For the
+.Li basic
+authentication scheme uses two additional optional parameters; the
+first is a user name, and the second is the password associated with
+it. If either the password or both parameters are not specified in
+the environment, and the standard input of
+.Nm
+is connected to a terminal, then
+.Nm
+will prompt the user to enter the missing parameters. Thus, if the
+user is known as
+.Dq Li jane
+in the
+.Dq Li WallyWorld
+realm, and has a password of
+.Dq Li QghiLx79
+there, then she might set her
+.Ev HTTP_AUTH
+variable to:
+.Bl -enum -offset indent
+.It
+.Dq Li basic:WallyWorld:jane:QghiLx79
+.It
+.Dq Li basic:WallyWorld:jane ,
+or
+.It
+.Dq Li basic:WallyWorld
+.El
+.Pp
+and
+.Nm
+will prompt for the missing information if it is required. She might
+also specify a realm of
+.Dq Li \&*
+instead of
+.Dq Li WallyWorld
+to indicate that the parameters can be applied to any realm. (This is
+most commonly used in a construction such as
+.Dq Li basic:* ,
+which indicates to
+.Nm
+that it may offer to do
+.Li basic
+authentication for any realm.
+.Sh ERRORS
+The
+.Nm
+command returns zero on success, or a non-zero value from
+.Aq Pa sysexits.h
+on failure. If multiple URIs are given for retrieval,
+.Nm
+will attempt all of them and return zero only if all succeeded
+(otherwise it will return the error from the last failure).
.Sh ENVIRONMENT
-A transfer using the
-.Em ftp
-protocol will be aborted after the delay specified by the
-.Ev FTP_TIMEOUT
-variable. The default is 300 (seconds)
-
-A transfer using the
-.Em http
-protocol will be aborted after the delay specified by the
-.Ev HTTP_TIMEOUT
-variable. The default is 300 (seconds)
-
-.Ev FTP_LOGIN
-is the login name for the remote host. Default is
-.Em anonymous
-
-.Ev FTP_PASSWORD
-is the password for the remote host. Default is
-.Em <yourname>@
-
-.Ev FTP_PASSIVE_MODE
-will force the use of passive mode FTP for firewalls.
-
-If
-.Ev HTTP_PROXY
-is set to a value of the form
-.Em host:port
-it specifies the address of a http proxy. The proxy will be used
-for all ftp and http requests. This is useful if you are behind
-an application firewall.
+.Bl -tag -width FTP_PASSIVE_MODE -offset indent
+.It Ev FTP_TIMEOUT
+maximum time, in seconds, to wait before aborting an
+.Tn FTP
+connection.
+.It Ev FTP_LOGIN
+the login name used for
+.Tn FTP
+transfers (default
+.Dq Li anonymous )
+.It Ev FTP_PASSIVE_MODE
+force the use of passive mode FTP
+.It Ev FTP_PASSWORD
+the password used for
+.Tn FTP
+transfers (default
+.Dq Va yourname Ns Li \&@ Ns Va yourhost )
+.It Ev FTP_PROXY
+the address of a proxy server which understands
+.Tn FTP
+.It Ev HTTP_AUTH
+defines authentication parameters for
+.Tn HTTP
+.It Ev HTTP_PROXY
+the address of a proxy server which understands
+.Tn HTTP
+.It Ev HTTP_PROXY_AUTH
+defines authentication parameters for
+.Tn HTTP
+proxy servers
+.It Ev HTTP_TIMEOUT
+maximum time, in seconds, to wait before aborting an
+.Tn HTTP
+connection.
.Sh SEE ALSO
-.Xr tftp 1 ,
-.Xr ftp 1
+.Xr ftp 1 ,
+.Xr tftp 1
.Sh HISTORY
The
.Nm fetch
command appeared in
.Fx 2.1.5 .
+.Sh AUTHORS
+The original implementation of
+.Nm
+was done by Jean-Marc Zucconi. It was extensively re-worked for
+.Fx 3.0
+by Garrett Wollman.
+.Sh BUGS
+There are too many environment variables and command-line options.
+.Pp
+The
+.Fl a
+option is only implemented for certain kinds of
+.Tn HTTP
+failures, and no
+.Tn FTP
+failures.
+.Pp
+Only the
+.Dq basic
+authentication mode is implemented for
+.Tn HTTP .
+This should be replaced by digest authentication.
diff --git a/usr.bin/fetch/main.c b/usr.bin/fetch/main.c
index d4e8c482b65e..5a457cd46306 100644
--- a/usr.bin/fetch/main.c
+++ b/usr.bin/fetch/main.c
@@ -24,426 +24,315 @@
* SUCH DAMAGE.
*/
-/* $Id: main.c,v 1.26.2.1 1996/11/12 09:10:35 phk Exp $ */
+/* $FreeBSD$ */
#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <pwd.h>
-#include <unistd.h>
-
-#include <netinet/in.h>
-
-#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <regex.h>
+#include <limits.h> /* needed for INT_MAX */
+#include <setjmp.h>
#include <signal.h>
-#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sysexits.h>
#include <unistd.h>
-#include <ftpio.h>
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#include <sys/time.h> /* for struct timeval, gettimeofday */
-#define BUFFER_SIZE 1024
-#define HTTP_TIMEOUT 300 /* seconds */
-#define FTP_TIMEOUT 300 /* seconds */
+#include "fetch.h"
-char buffer[BUFFER_SIZE];
+static struct fetch_state clean_fetch_state;
+static sigjmp_buf sigbuf;
+static int get(struct fetch_state *volatile fs);
-extern char *__progname; /* from crt0.o */
-
-int verbose = 1;
-int ftp_verbose = 0;
-int linkfile = 0;
-char *outputfile = 0;
-char *change_to_dir = 0;
-char *host = 0;
-int passive_mode = 0;
-char *file_to_get = 0;
-int ftp = 0;
-int http_proxy = 0;
-int http = 0;
-int http_port = 80;
-int mirror = 0;
-int newtime = 0;
-int restart = 0;
-time_t modtime;
-FILE *file = 0;
-int timeout_ival = 0;
-
-void usage(void), die(int), rm(void), timeout(int), ftpget(void),
- httpget(void), fileget(void),
- display(int, int), parse(char *), output_file_name(void),
- f_size(char *, off_t *, time_t *), ftperr(FILE* ftp, char *, ...),
- filter(unsigned char *, int),
- setup_http_proxy(void);
-
-int match(char *, char *), http_open(void);
-
-void
-usage()
+static void
+usage(const char *argv0)
{
- fprintf(stderr, "usage: %s [-DHINPMTVLqlmnprv] [-o outputfile] <-f file -h host [-c dir]| URL>\n", __progname);
- exit(1);
+ fprintf(stderr,
+ "%s: usage:\n\t%s [-DHILMNPRTValmnpqrv] [-o outputfile] "
+ "[-f file -h host [-c dir] | URL]\n", argv0, argv0);
+ exit(EX_USAGE);
}
-void
-die(int sig)
-{
- int e = errno;
-
- rm();
- if (!sig)
- fprintf (stderr, "%s: %s\n", __progname, strerror(e));
- else
- warnx ("Interrupted by signal %d", sig);
- exit(1);
-}
-
-void
-adjmodtime()
-{
- struct timeval tv[2];
-
- if (!newtime) {
- tv[0].tv_usec = tv[1].tv_usec = 0;
- tv[0].tv_sec = time(0);
- tv[1].tv_sec = modtime;
- utimes (outputfile, tv);
- }
-}
-
-void
-rm()
-{
- if (file) {
- fclose(file);
- if (file != stdout) {
- if (!restart && !mirror)
- remove(outputfile);
- adjmodtime();
- }
- }
-}
int
-main(int argc, char **argv)
+main(int argc, char *const *argv)
{
int c;
- char *s;
+ char *ep;
+ struct fetch_state fs;
+ const char *change_to_dir, *file_to_get, *hostname;
+ int error, rv;
+ unsigned long l;
- while ((c = getopt (argc, argv, "D:HINPMT:V:Lqc:f:h:o:plmnrv")) != -1) {
- switch (c) {
- case 'D': case 'H': case 'I': case 'N': case 'L': case 'V':
- break; /* ncftp compatibility */
-
- case 'q':
- verbose = 0;
+ init_schemes();
+ fs = clean_fetch_state;
+ fs.fs_verbose = 1;
+ change_to_dir = file_to_get = hostname = 0;
+
+ while ((c = getopt(argc, argv, "ac:D:f:h:HilLmMnNo:pPqRrT:vV:")) != -1) {
+ switch (c) {
+ case 'D': case 'H': case 'I': case 'N': case 'L': case 'V':
+ break; /* ncftp compatibility */
- case 'c':
- change_to_dir = optarg;
- break;
+ case 'a':
+ fs.fs_auto_retry = 1;
+ break;
+ case 'c':
+ change_to_dir = optarg;
+ break;
- case 'f':
- file_to_get = optarg;
- break;
+ case 'f':
+ file_to_get = optarg;
+ break;
- case 'h':
- host = optarg;
- ftp = 1;
- break;
+ case 'h':
+ hostname = optarg;
+ break;
- case 'l':
- linkfile = 1;
- break;
+ case 'l':
+ fs.fs_linkfile = 1;
+ break;
- case 'o':
- outputfile = optarg;
- break;
+ case 'm': case 'M':
+ fs.fs_mirror = 1;
+ break;
- case 'p': case 'P':
- passive_mode = 1;
- break;
+ case 'n':
+ fs.fs_newtime = 1;
+ break;
- case 'm': case 'M':
- mirror = 1;
- break;
-
- case 'n':
- newtime = 1;
- break;
+ case 'o':
+ fs.fs_outputfile = optarg;
+ break;
+
+ case 'p': case 'P':
+ fs.fs_passive_mode = 1;
+ break;
- case 'r':
- restart = 1;
- break;
+ case 'q':
+ fs.fs_verbose = 0;
+ break;
+
+ case 'r':
+ fs.fs_restart = 1;
+ break;
+
+ case 'R':
+ fs.fs_precious = 1;
+ break;
- case 'v':
- ftp_verbose = 1;
- break;
+ case 'T':
+ /* strtol sets errno to ERANGE in the case of overflow */
+ errno = 0;
+ l = strtoul(optarg, &ep, 0);
+ if (!optarg[0] || *ep || errno != 0 || l > INT_MAX)
+ errx(EX_USAGE, "invalid timeout value: `%s'",
+ optarg);
+ fs.fs_timeout = l;
+ break;
- case 'T':
- timeout_ival = atoi(optarg);
- break;
+ case 'v':
+ if (fs.fs_verbose < 2)
+ fs.fs_verbose = 2;
+ else
+ fs.fs_verbose++;
+ break;
- default:
- case '?':
- usage();
- }
+ default:
+ case '?':
+ usage(argv[0]);
+ }
}
- argc -= optind;
- argv += optind;
- if (argv[0]) {
- if (host || change_to_dir || file_to_get)
- usage();
- s = strdup(argv[0]);
- if (s == NULL)
- s = argv[0]; /* optomistic, I know.. malloc just failed. */
- parse(s);
- } else {
- if (!host || !file_to_get)
- usage();
+
+ clean_fetch_state = fs; /* preserve option settings */
+
+ if (argv[optind] && (hostname || change_to_dir || file_to_get)) {
+ warnx("cannot use -h, -c, or -f with a URI argument");
+ usage(argv[0]);
}
-
- if (mirror && restart)
- errx(1, "-m and -r are mutually exclusive.");
-
- output_file_name();
-
- signal(SIGHUP, die);
- signal(SIGINT, die);
- signal(SIGQUIT, die);
- signal(SIGTERM, die);
-
- setup_http_proxy();
- if (http)
- httpget();
- else if (ftp)
- ftpget();
- else
- fileget();
- exit(0);
-}
+ if (fs.fs_mirror && fs.fs_restart)
+ errx(EX_USAGE, "-m and -r are mutually exclusive.");
+
+ if (argv[optind] == 0) {
+ char *uri;
-void
-timeout(int sig)
-{
- fprintf (stderr, "\n%s: Timeout\n", __progname);
- rm();
- exit(1);
-}
+ if (hostname == 0) hostname = "localhost";
+ if (change_to_dir == 0) change_to_dir = "";
+ if (file_to_get == 0) {
+ usage(argv[0]);
+ }
-void
-fileget()
-{
- char *basename;
+ uri = alloca(sizeof("ftp://") + strlen(hostname) +
+ strlen(change_to_dir) + 2 + strlen(file_to_get));
+ strcpy(uri, "ftp://");
+ strcat(uri, hostname);
+ /*
+ * XXX - we should %-map a leading `/' into `%2f', but for
+ * anonymous FTP it is unlikely to matter. Still, it would
+ * be better to follow the spec.
+ */
+ if (change_to_dir[0] != '/')
+ strcat(uri, "/");
+ strcat(uri, change_to_dir);
+ if (file_to_get[0] != '/' && uri[strlen(uri) - 1] != '/')
+ strcat(uri, "/");
+ strcat(uri, file_to_get);
- if (access(file_to_get, R_OK)) {
- fprintf(stderr, "unable to access local file `%s'\n", file_to_get);
- exit(1);
- }
- basename = strrchr(file_to_get, '/');
- if (!basename) {
- fprintf(stderr, "malformed filename `%s' - expected full path.\n",
- file_to_get);
- exit(1);
- }
- ++basename; /* move over the / */
- if (!access(basename, F_OK)) {
- fprintf(stderr, "%s: file already exists.\n", basename);
- exit(1);
+ error = parse_uri(&fs, uri);
+ if (error)
+ return error;
+ return get(&fs);
}
- if (linkfile) {
- if (symlink(file_to_get, basename) == -1) {
- perror("symlink");
- exit(1);
- }
- }
- else {
- char *buf = alloca(strlen(file_to_get) + strlen(basename) + 15);
- sprintf(buf, "/bin/cp -p %s %s", file_to_get, basename);
- if (system(buf)) {
- fprintf(stderr, "failed to copy %s successfully.", file_to_get);
- exit(1);
- }
- }
-}
+ for (rv = 0; argv[optind] != 0; optind++) {
+ error = parse_uri(&fs, argv[optind]);
+ if (error) {
+ rv = error;
+ continue;
+ }
-void
-ftpget()
-{
- FILE *ftp, *fp;
- char *cp, *lp;
- int status, n;
- off_t size, size0, seekloc;
- char ftp_pw[200];
- time_t t;
- time_t tout;
- struct itimerval timer;
-
- if ((cp = getenv("FTP_PASSWORD")) != NULL)
- strcpy(ftp_pw, cp);
- else {
- sprintf (ftp_pw, "%s@", getpwuid (getuid ())->pw_name);
- n = strlen (ftp_pw);
- gethostname (ftp_pw + n, 200 - n);
- }
- if ((lp = getenv("FTP_LOGIN")) == NULL)
- lp = "anonymous";
- ftp = ftpLogin(host, lp, ftp_pw, 0, ftp_verbose, &status);
- if (!ftp) {
- if (status)
- errx(1, "%s: %s", host, ftpErrString(status));
- else
- errx(1, "couldn't open FTP connection to %s: %s",
- host, hstrerror(h_errno));
- }
-
- /* Time to set our defaults */
- ftpBinary (ftp);
- ftpPassive (ftp, passive_mode);
-
- if (change_to_dir) {
- status = ftpChdir (ftp, change_to_dir);
- if (status)
- ftperr (ftp, "couldn't cd to %s: ", change_to_dir);
- }
- size = ftpGetSize (ftp, file_to_get);
- modtime = ftpGetModtime (ftp, file_to_get);
- if (modtime < -1) {
- warnx ("Couldn't get file time for %s - using current time", file_to_get);
- modtime = (time_t) -1;
+ error = get(&fs);
+ if (error) {
+ rv = error;
+ }
+ fs = clean_fetch_state;
}
+ return rv;
+}
- if (!strcmp (outputfile, "-"))
- restart = 0;
- if (restart || mirror) {
- f_size (outputfile, &size0, &t);
- if (mirror && size0 == size && modtime <= t) {
- fclose(ftp);
- return;
- }
- else if (restart) {
- if (size0 && size0 < size)
- seekloc = size0;
- else
- seekloc = size0 = 0;
+/*
+ * The signal handling is probably more complex than it needs to be,
+ * but it doesn't cost a lot, so we'll be extra-careful. Using
+ * siglongjmp() to get out of the signal handler allows us to
+ * call rm() without having to store the state variable in some global
+ * spot where the signal handler can get at it. We also obviate the need
+ * for a separate timeout signal handler.
+ */
+static int
+get(struct fetch_state *volatile fs)
+{
+ volatile int error;
+ struct sigaction oldhup, oldint, oldquit, oldterm;
+ struct sigaction catch;
+ volatile sigset_t omask;
+
+ sigemptyset(&catch.sa_mask);
+ sigaddset(&catch.sa_mask, SIGHUP);
+ sigaddset(&catch.sa_mask, SIGINT);
+ sigaddset(&catch.sa_mask, SIGQUIT);
+ sigaddset(&catch.sa_mask, SIGTERM);
+ sigaddset(&catch.sa_mask, SIGALRM);
+ catch.sa_handler = catchsig;
+ catch.sa_flags = 0;
+
+ sigprocmask(SIG_BLOCK, &catch.sa_mask, (sigset_t *)&omask);
+ sigaction(SIGHUP, &catch, &oldhup);
+ sigaction(SIGINT, &catch, &oldint);
+ sigaction(SIGQUIT, &catch, &oldquit);
+ sigaction(SIGTERM, &catch, &oldterm);
+
+ error = sigsetjmp(sigbuf, 0);
+ if (error == SIGALRM) {
+ rm(fs);
+ unsetup_sigalrm();
+ fprintf(stderr, "\n"); /* just in case */
+ warnx("%s: %s: timed out", fs->fs_outputfile, fs->fs_status);
+ goto close;
+ } else if (error) {
+ rm(fs);
+ fprintf(stderr, "\n"); /* just in case */
+ warnx("%s: interrupted by signal: %s", fs->fs_status,
+ sys_signame[error]);
+ sigdelset(&omask, error);
+ signal(error, SIG_DFL);
+ sigprocmask(SIG_SETMASK, (sigset_t *)&omask, 0);
+ raise(error); /* so that it gets reported as such */
}
- }
- else if (!restart)
- seekloc = size0 = 0;
-
- fp = ftpGet (ftp, file_to_get, &seekloc);
- if (fp == NULL)
- if (ftpErrno(ftp))
- ftperr (ftp, NULL);
- else
- die(0);
- if (size0 && !seekloc)
- size0 = 0;
-
- if (strcmp (outputfile, "-")) {
- file = fopen (outputfile, size0 ? "a" : "w");
- if (!file)
- err (1, "could not open output file %s.", outputfile);
- } else
- file = stdout;
-
- signal (SIGALRM, timeout);
- if (timeout_ival)
- tout = timeout_ival;
- else if ((cp = getenv("FTP_TIMEOUT")) != NULL)
- tout = atoi(cp);
- else
- tout = FTP_TIMEOUT;
-
- timer.it_interval.tv_sec = 0; /* Reload value */
- timer.it_interval.tv_usec = 0;
+ sigprocmask(SIG_SETMASK, (sigset_t *)&omask, 0);
+ error = fs->fs_retrieve(fs);
- timer.it_value.tv_sec = tout; /* One-Shot value */
- timer.it_value.tv_usec = 0;
+close:
+ sigaction(SIGHUP, &oldhup, 0);
+ sigaction(SIGINT, &oldint, 0);
+ sigaction(SIGQUIT, &oldquit, 0);
+ sigaction(SIGTERM, &oldterm, 0);
+ fs->fs_close(fs);
- display (size, size0);
- while (1) {
- setitimer(ITIMER_REAL, &timer, 0); /* reset timeout */
+ return error;
+}
- n = status = fread (buffer, 1, BUFFER_SIZE, fp);
- if (status <= 0)
- break;
- display (size, n);
- status = fwrite (buffer, 1, n, file);
- if (status != n)
- break;
- }
- timer.it_value.tv_sec = 0;
- timer.it_value.tv_usec = 0;
- setitimer(ITIMER_REAL, &timer, 0); /* disable timeout */
+
+/*
+ * Utility functions
+ */
- if (status < 0)
- die(0);
- fclose(fp);
- fclose(file);
- display (size, -1);
- if (file != stdout)
- adjmodtime();
- exit (0);
+/*
+ * Handle all signals by jumping back into get().
+ */
+void
+catchsig(int sig)
+{
+ siglongjmp(sigbuf, sig);
}
+/* Used to generate the progress display when not in quiet mode. */
void
-display (int size, int n)
+display(struct fetch_state *fs, off_t size, ssize_t n)
{
- static int bytes, pr, init = 0;
+ static off_t bytes;
+ static off_t bytestart;
+ static int pr, stdoutatty, init = 0;
static struct timeval t0, t_start;
static char *s;
struct timezone tz;
struct timeval t;
float d;
- if (!verbose)
+ if (!fs->fs_verbose)
return;
if (init == 0) {
init = 1;
gettimeofday(&t0, &tz);
t_start = t0;
bytes = pr = 0;
- s = (char *) malloc (strlen(outputfile) + 50);
+ stdoutatty = isatty(STDOUT_FILENO);
if (size > 0)
- sprintf (s, "Receiving %s (%d bytes)%s", outputfile, size,
+ asprintf (&s, "Receiving %s (%qd bytes)%s", fs->fs_outputfile,
+ (quad_t)size,
size ? "" : " [appending]");
else
- sprintf (s, "Receiving %s", outputfile);
- printf ("%s", s);
- fflush (stdout);
- bytes = n;
+ asprintf (&s, "Receiving %s", fs->fs_outputfile);
+ fprintf (stderr, "%s", s);
+ bytestart = bytes = n;
return;
}
gettimeofday(&t, &tz);
if (n == -1) {
- if (size > 0)
- printf ("\r%s: 100%%", s);
- else
- printf ("\r%s: %d Kbytes", s, bytes/1024);
+ if(stdoutatty) {
+ if (size > 0)
+ fprintf (stderr, "\r%s: 100%%", s);
+ else
+ fprintf (stderr, "\r%s: %qd Kbytes", s, (quad_t)bytes/1024);
+ }
+ bytes -= bytestart;
d = t.tv_sec + t.tv_usec/1.e6 - t_start.tv_sec - t_start.tv_usec/1.e6;
- printf ("\n%d bytes transfered in %.1f seconds", bytes, d);
+ fprintf (stderr, "\n%qd bytes transfered in %.1f seconds",
+ (quad_t)bytes, d);
d = bytes/d;
if (d < 1000)
- printf (" (%d Bytes/s)\n", (int)d);
+ fprintf (stderr, " (%.0f bytes/s)\n", d);
else {
d /=1024;
- printf (" (%.2f K/s)\n", d);
+ fprintf (stderr, " (%.2f kB/s)\n", d);
}
+ free(s);
+ init = 0;
return;
}
bytes += n;
@@ -452,358 +341,13 @@ display (int size, int n)
return;
t0 = t;
pr++;
- if (size > 1000000)
- printf ("\r%s: %2d%%", s, bytes/(size/100));
- else if (size > 0)
- printf ("\r%s: %2d%%", s, 100*bytes/size);
- else
- printf ("\r%s: %d Kbytes", s, bytes/1024);
- fflush (stdout);
-}
-
-void
-parse (char *s)
-{
- char *p;
-
- if (strncasecmp (s, "file:", 5) == 0) {
- /* file:filename */
- s += 4;
- *s++ = '\0';
- host = NULL;
- ftp = http = 0;
- file_to_get = s;
- return;
- }
- else if (strncasecmp (s, "ftp://", 6) == 0) {
- /* ftp://host.name/file/name */
- s += 6;
- p = strchr(s, '/');
- if (!p) {
- warnx("no filename??");
- usage();
- }
- }
- else if (strncasecmp (s, "http://", 7) == 0) {
- /* http://host.name/file/name */
- char *q;
- s += 7;
- p = strchr(s, '/');
- if (!p) {
- warnx ("no filename??");
- usage ();
- }
- *p++ = 0;
- q = strchr (s, ':');
- if (q && q < p) {
- *q++ = 0;
- http_port = atoi (q);
- }
- host = s;
- file_to_get = p;
- http = 1;
- return;
- }
- else {
- /* assume host.name:/file/name */
- p = strchr (s, ':');
- if (!p) {
- /* assume /file/name */
- host = NULL;
- ftp = http = 0;
- file_to_get = s;
- return;
- }
- }
- ftp = 1;
- *p++ = 0;
- host = s;
- s = strrchr (p, '/');
- if (s) {
- *s++ = 0;
- change_to_dir = p;
- file_to_get = s;
- } else {
- change_to_dir = 0;
- file_to_get = p;
- }
-}
-
-void
-output_file_name ()
-{
- char *p;
-
- if (!outputfile) {
- p = strrchr (file_to_get, '/');
- if (!p || (!ftp && !http))
- p = file_to_get;
+ if(stdoutatty) {
+ if (size > 1000000)
+ fprintf (stderr, "\r%s: %2qd%%", s, (quad_t)bytes/(size/100));
+ else if (size > 0)
+ fprintf (stderr, "\r%s: %2qd%%", s, (quad_t)100*bytes/size);
else
- p++;
- outputfile = strdup (p);
+ fprintf (stderr, "\r%s: %qd kB", s, (quad_t)bytes/1024);
}
}
-void
-f_size (char *name, off_t *size, time_t *time)
-{
- struct stat s;
-
- *size = 0;
-
- if (stat (name, &s))
- return;
- *size = s.st_size;
- *time = s.st_mtime;
-}
-
-void
-ftperr (FILE* ftp, char *fmt, ...)
-{
- va_list ap;
- va_start (ap, fmt);
-
- if (fmt)
- vfprintf(stderr, fmt, ap);
- if(ftp) {
- const char *str = ftpErrString(ftpErrno(ftp));
-
- if (str)
- fprintf(stderr, "%s\n", str);
- }
- rm ();
- exit (1);
-}
-
-void
-httpget ()
-{
- char *cp, str[1000];
- struct timeval tv;
- time_t tout;
- fd_set fdset;
- int i, s;
-
- restart = 0;
-
- s = http_open ();
- sprintf (str, "GET %s%s HTTP/1.0\r\n\r\n",
- http_proxy? "" : "/", file_to_get);
- i = strlen (str);
- if (i != write (s, str, i))
- err (1, "could not send GET command to HTTP server.");
-
- FD_ZERO (&fdset);
- FD_SET (s, &fdset);
- if (timeout_ival)
- tout = timeout_ival;
- else if ((cp = getenv("HTTP_TIMEOUT")) != NULL)
- tout = atoi(cp);
- else
- tout = HTTP_TIMEOUT;
-
- if (strcmp (outputfile, "-")) {
- file = fopen (outputfile, "w");
- if (!file)
- err (1, "could not open output file %s.", outputfile);
- } else {
- file = stdout;
- verbose = 0;
- }
-
- while (1) {
- tv.tv_sec = tout;
- tv.tv_usec = 0;
- i = select (s+1, &fdset, 0, 0, &tv);
- switch (i) {
- case 0:
- warnx ("Timeout");
- rm ();
- exit (1);
- case 1:
- i = read (s, buffer, sizeof (buffer));
- filter (buffer, i);
- if (i == 0)
- exit (0);
- break;
- default:
- err (1, "communication error with HTTP server.");
- }
- }
-}
-
-int
-match (char *pat, char *s)
-{
- regex_t preg;
- regmatch_t pmatch[2];
-
- regcomp (&preg, pat, REG_EXTENDED|REG_ICASE);
- if (regexec(&preg, s, 2, pmatch, 0))
- return 0;
- return pmatch[1].rm_so ? pmatch[1].rm_so : -1;
-}
-
-void
-filter (unsigned char *p, int len)
-{
-#define S 512
- static unsigned char s[S+2];
- static int header_len = 0, size = -1, n;
- int i = len;
- unsigned char *q = p;
-
- if (header_len < S) {
- while (header_len < S && i--)
- s[header_len++] = *q++;
- s[header_len] = 0;
- if (len && (header_len < S))
- return;
- if (match ("^HTTP/[0-9]+\\.[0-9]+[ \t]+200[^0-9]", s) == 0) {
- /* maybe not found, or document w/o header */
- if (match ("^HTTP/[0-9]+\\.[0-9]+[ \t]+[0-9]+", s)) {
- fprintf (stderr, "%s fetching failed, header so far:\n%s\n", file_to_get, s);
- rm ();
- exit (1);
- }
- /* assume no header */
- /* write s */
- display (size, 0);
- i = fwrite (s, 1, header_len, file);
- if (i != header_len)
- die(0);
- display (size, header_len);
- /* then p */
- if (p+len > q) {
- i = fwrite (q, 1, p+len-q, file);
- if (i != p+len-q)
- die(0);
- display (size, i);
- }
- } else {
- unsigned char *t;
- /* document begins with a success line. try to get size */
- i = match ("content-length:[ \t]*([0-9]+)", s);
- if (i > 0)
- size = atoi (s+i);
- /* assume that the file to get begins after an empty line */
- i = match ("(\n\n|\r\n\r\n)", s);
- if (i > 0) {
- if (s[i] == '\r')
- t = s+i+4;
- else
- t = s+i+2;
- } else {
- fprintf (stderr, "Can't decode the header!\n");
- rm ();
- exit (1);
- }
- display (size, 0);
- n = (s-t)+header_len;
- i = fwrite (t, 1, n, file);
- if (i != n)
- die(0);
- display (size, n);
- if (p+len > q) {
- n = p+len-q;
- i = fwrite (q, 1, n, file);
- if (i != n)
- die(0);
- display (size, n);
- }
- }
- } else {
- i = fwrite (p, 1, len, file);
- if (i != len)
- die(0);
- if (len)
- display (size, i);
- }
- if (len == 0)
- display (size, -1);
-}
-
-int
-http_open()
-{
- unsigned long a;
- struct sockaddr_in sin, sin2;
- struct hostent *h;
- int s;
-
- a = inet_addr (host);
- if (a != INADDR_NONE) {
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = a;
- } else {
- h = gethostbyname (host);
- if (!h)
- err (1, "could not lookup host %s.", host);
- sin.sin_family = h->h_addrtype;
- bcopy(h->h_addr, (char *)&sin.sin_addr, h->h_length);
- }
- sin.sin_port = htons (http_port);
- if ((s = socket (sin.sin_family, SOCK_STREAM, 0)) < 0)
- err (1, "socket");
- bzero ((char *)&sin2, sizeof (sin2));
- sin2.sin_family = AF_INET;
- sin2.sin_port = 0;
- sin2.sin_addr.s_addr = htonl (INADDR_ANY);
- if (bind (s, (struct sockaddr *)&sin2, sizeof (sin2)))
- err (1, "could not bind to socket.");
-
- if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
- err (1, "connection failed");
- return s;
-}
-
-int
-isDebug ()
-{
- return 0;
-}
-
-void msgDebug (char *p)
-{
- printf ("%s", p);
-}
-
-void
-setup_http_proxy()
-{
- char *e;
- char *p;
- char *url;
- unsigned short port;
-
- if (!(e = getenv("HTTP_PROXY"))
- || !(p = strchr(e, ':'))
- || (port = atoi(p+1)) == 0)
- return;
-
- if (!(url = (char *) malloc (strlen(file_to_get)
- + strlen(host)
- + (change_to_dir ? strlen(change_to_dir) : 0)
- + 50)))
- return;
-
- if (http) {
- sprintf(url, "http://%s:%d/%s",
- host, http_port, file_to_get);
- } else {
- if (change_to_dir) {
- sprintf(url, "ftp://%s/%s/%s",
- host, change_to_dir, file_to_get);
- } else {
- sprintf(url, "ftp://%s/%s", host, file_to_get);
- }
- }
- file_to_get = url;
-
- *p = 0;
- host = strdup(e);
- http_port = port;
- http = 1;
- http_proxy = 1;
-}
-