diff options
| author | Joerg Wunsch <joerg@FreeBSD.org> | 1997-02-22 19:14:04 +0000 |
|---|---|---|
| committer | Joerg Wunsch <joerg@FreeBSD.org> | 1997-02-22 19:14:04 +0000 |
| commit | 5f9fe6fb8a279bb4e3b2a92b1e5b7fa8ef7191d6 (patch) | |
| tree | 2c08c19a78b58c37ae5abd7c4eae801b69620e0b /usr.bin/fetch | |
| parent | b0da4f99a316da96968ad7f1c9b4386713af6e9a (diff) | |
Notes
Diffstat (limited to 'usr.bin/fetch')
| -rw-r--r-- | usr.bin/fetch/Makefile | 8 | ||||
| -rw-r--r-- | usr.bin/fetch/fetch.1 | 312 | ||||
| -rw-r--r-- | usr.bin/fetch/main.c | 928 |
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; -} - |
