1b39628e7SDag-Erling Smørgrav /*- 2b39628e7SDag-Erling Smørgrav * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav 3b39628e7SDag-Erling Smørgrav * All rights reserved. 4b39628e7SDag-Erling Smørgrav * 5b39628e7SDag-Erling Smørgrav * Redistribution and use in source and binary forms, with or without 6b39628e7SDag-Erling Smørgrav * modification, are permitted provided that the following conditions 7b39628e7SDag-Erling Smørgrav * are met: 8b39628e7SDag-Erling Smørgrav * 1. Redistributions of source code must retain the above copyright 9b39628e7SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer 10b39628e7SDag-Erling Smørgrav * in this position and unchanged. 11b39628e7SDag-Erling Smørgrav * 2. Redistributions in binary form must reproduce the above copyright 12b39628e7SDag-Erling Smørgrav * notice, this list of conditions and the following disclaimer in the 13b39628e7SDag-Erling Smørgrav * documentation and/or other materials provided with the distribution. 14b39628e7SDag-Erling Smørgrav * 3. The name of the author may not be used to endorse or promote products 15b39628e7SDag-Erling Smørgrav * derived from this software without specific prior written permission 16b39628e7SDag-Erling Smørgrav * 17b39628e7SDag-Erling Smørgrav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18b39628e7SDag-Erling Smørgrav * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19b39628e7SDag-Erling Smørgrav * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20b39628e7SDag-Erling Smørgrav * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21b39628e7SDag-Erling Smørgrav * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22b39628e7SDag-Erling Smørgrav * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23b39628e7SDag-Erling Smørgrav * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24b39628e7SDag-Erling Smørgrav * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25b39628e7SDag-Erling Smørgrav * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26b39628e7SDag-Erling Smørgrav * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27b39628e7SDag-Erling Smørgrav * 28b39628e7SDag-Erling Smørgrav * $FreeBSD$ 29b39628e7SDag-Erling Smørgrav */ 30b39628e7SDag-Erling Smørgrav 31b39628e7SDag-Erling Smørgrav #include <sys/param.h> 32b39628e7SDag-Erling Smørgrav #include <sys/socket.h> 33469a4570SBruce Evans #include <sys/stat.h> 34469a4570SBruce Evans #include <sys/time.h> 35b39628e7SDag-Erling Smørgrav 36b39628e7SDag-Erling Smørgrav #include <ctype.h> 37b39628e7SDag-Erling Smørgrav #include <err.h> 38b39628e7SDag-Erling Smørgrav #include <errno.h> 3978394463SDag-Erling Smørgrav #include <signal.h> 40b39628e7SDag-Erling Smørgrav #include <stdio.h> 41b39628e7SDag-Erling Smørgrav #include <stdlib.h> 42b39628e7SDag-Erling Smørgrav #include <string.h> 43b39628e7SDag-Erling Smørgrav #include <sysexits.h> 440d60c709SDag-Erling Smørgrav #include <termios.h> 45b39628e7SDag-Erling Smørgrav #include <unistd.h> 46b39628e7SDag-Erling Smørgrav 47b39628e7SDag-Erling Smørgrav #include <fetch.h> 48b39628e7SDag-Erling Smørgrav 49b39628e7SDag-Erling Smørgrav #define MINBUFSIZE 4096 50b39628e7SDag-Erling Smørgrav 51b39628e7SDag-Erling Smørgrav /* Option flags */ 52b39628e7SDag-Erling Smørgrav int A_flag; /* -A: do not follow 302 redirects */ 53b39628e7SDag-Erling Smørgrav int a_flag; /* -a: auto retry */ 5491404f38SDag-Erling Smørgrav off_t B_size; /* -B: buffer size */ 55b39628e7SDag-Erling Smørgrav int b_flag; /*! -b: workaround TCP bug */ 56a8369cd9SDag-Erling Smørgrav char *c_dirname; /* -c: remote directory */ 57b39628e7SDag-Erling Smørgrav int d_flag; /* -d: direct connection */ 58b39628e7SDag-Erling Smørgrav int F_flag; /* -F: restart without checking mtime */ 59b39628e7SDag-Erling Smørgrav char *f_filename; /* -f: file to fetch */ 60b39628e7SDag-Erling Smørgrav char *h_hostname; /* -h: host to fetch from */ 61b39628e7SDag-Erling Smørgrav int l_flag; /* -l: link rather than copy file: URLs */ 62bb11a878SDag-Erling Smørgrav int m_flag; /* -[Mm]: mirror mode */ 63bb11a878SDag-Erling Smørgrav int n_flag; /* -n: do not preserve modification time */ 64b39628e7SDag-Erling Smørgrav int o_flag; /* -o: specify output file */ 65b39628e7SDag-Erling Smørgrav int o_directory; /* output file is a directory */ 66b39628e7SDag-Erling Smørgrav char *o_filename; /* name of output file */ 67b39628e7SDag-Erling Smørgrav int o_stdout; /* output file is stdout */ 68b39628e7SDag-Erling Smørgrav int once_flag; /* -1: stop at first successful file */ 69a5e3ae21SDag-Erling Smørgrav int p_flag; /* -[Pp]: use passive FTP */ 70b39628e7SDag-Erling Smørgrav int R_flag; /* -R: don't delete partially transferred files */ 71b39628e7SDag-Erling Smørgrav int r_flag; /* -r: restart previously interrupted transfer */ 72b39628e7SDag-Erling Smørgrav off_t S_size; /* -S: require size to match */ 737c480c6cSDag-Erling Smørgrav int s_flag; /* -s: show size, don't fetch */ 74eab5a804SDag-Erling Smørgrav u_int T_secs = 120; /* -T: transfer timeout in seconds */ 75b39628e7SDag-Erling Smørgrav int t_flag; /*! -t: workaround TCP bug */ 767c480c6cSDag-Erling Smørgrav int U_flag; /* -U: do not use high ports */ 77b39628e7SDag-Erling Smørgrav int v_level = 1; /* -v: verbosity level */ 78b39628e7SDag-Erling Smørgrav int v_tty; /* stdout is a tty */ 79dbcc1983SDag-Erling Smørgrav pid_t pgrp; /* our process group */ 80b39628e7SDag-Erling Smørgrav u_int w_secs; /* -w: retry delay */ 81b39628e7SDag-Erling Smørgrav int family = PF_UNSPEC; /* -[46]: address family to use */ 82b39628e7SDag-Erling Smørgrav 83ef50a72cSDag-Erling Smørgrav int sigalrm; /* SIGALRM received */ 84cd400b67SDag-Erling Smørgrav int siginfo; /* SIGINFO received */ 85ef50a72cSDag-Erling Smørgrav int sigint; /* SIGINT received */ 86b39628e7SDag-Erling Smørgrav 87b39628e7SDag-Erling Smørgrav u_int ftp_timeout; /* default timeout for FTP transfers */ 88b39628e7SDag-Erling Smørgrav u_int http_timeout; /* default timeout for HTTP transfers */ 89b39628e7SDag-Erling Smørgrav u_char *buf; /* transfer buffer */ 90b39628e7SDag-Erling Smørgrav 91b39628e7SDag-Erling Smørgrav 92db695db7SDag-Erling Smørgrav /* 93db695db7SDag-Erling Smørgrav * Signal handler 94db695db7SDag-Erling Smørgrav */ 9591404f38SDag-Erling Smørgrav static void 96b39628e7SDag-Erling Smørgrav sig_handler(int sig) 97b39628e7SDag-Erling Smørgrav { 98ef50a72cSDag-Erling Smørgrav switch (sig) { 99ef50a72cSDag-Erling Smørgrav case SIGALRM: 100ef50a72cSDag-Erling Smørgrav sigalrm = 1; 101ef50a72cSDag-Erling Smørgrav break; 102cd400b67SDag-Erling Smørgrav case SIGINFO: 103cd400b67SDag-Erling Smørgrav siginfo = 1; 104cd400b67SDag-Erling Smørgrav break; 105ef50a72cSDag-Erling Smørgrav case SIGINT: 106ef50a72cSDag-Erling Smørgrav sigint = 1; 107ef50a72cSDag-Erling Smørgrav break; 108ef50a72cSDag-Erling Smørgrav } 109b39628e7SDag-Erling Smørgrav } 110b39628e7SDag-Erling Smørgrav 111b39628e7SDag-Erling Smørgrav struct xferstat { 112b39628e7SDag-Erling Smørgrav char name[40]; 113b39628e7SDag-Erling Smørgrav struct timeval start; 114b39628e7SDag-Erling Smørgrav struct timeval end; 115b39628e7SDag-Erling Smørgrav struct timeval last; 116b39628e7SDag-Erling Smørgrav off_t size; 117b39628e7SDag-Erling Smørgrav off_t offset; 118b39628e7SDag-Erling Smørgrav off_t rcvd; 119b39628e7SDag-Erling Smørgrav }; 120b39628e7SDag-Erling Smørgrav 121db695db7SDag-Erling Smørgrav /* 122db695db7SDag-Erling Smørgrav * Update the stats display 123db695db7SDag-Erling Smørgrav */ 12491404f38SDag-Erling Smørgrav static void 12549e62d8fSDag-Erling Smørgrav stat_display(struct xferstat *xs, int force) 12649e62d8fSDag-Erling Smørgrav { 12749e62d8fSDag-Erling Smørgrav struct timeval now; 128dbcc1983SDag-Erling Smørgrav int ctty_pgrp; 12949e62d8fSDag-Erling Smørgrav 130d939bf77SDag-Erling Smørgrav if (!v_tty || !v_level) 13149e62d8fSDag-Erling Smørgrav return; 13249e62d8fSDag-Erling Smørgrav 133dbcc1983SDag-Erling Smørgrav /* check if we're the foreground process */ 134dbcc1983SDag-Erling Smørgrav if (ioctl(STDERR_FILENO, TIOCGPGRP, &ctty_pgrp) == -1 || 135dbcc1983SDag-Erling Smørgrav (pid_t)ctty_pgrp != pgrp) 136dbcc1983SDag-Erling Smørgrav return; 137dbcc1983SDag-Erling Smørgrav 13849e62d8fSDag-Erling Smørgrav gettimeofday(&now, NULL); 13949e62d8fSDag-Erling Smørgrav if (!force && now.tv_sec <= xs->last.tv_sec) 14049e62d8fSDag-Erling Smørgrav return; 14149e62d8fSDag-Erling Smørgrav xs->last = now; 14249e62d8fSDag-Erling Smørgrav 14349e62d8fSDag-Erling Smørgrav fprintf(stderr, "\rReceiving %s", xs->name); 1441a4a0639SDag-Erling Smørgrav if (xs->size <= 0) 14591404f38SDag-Erling Smørgrav fprintf(stderr, ": %lld bytes", (long long)xs->rcvd); 14649e62d8fSDag-Erling Smørgrav else 14791404f38SDag-Erling Smørgrav fprintf(stderr, " (%lld bytes): %d%%", (long long)xs->size, 148b3c141fdSDag-Erling Smørgrav (int)((100.0 * xs->rcvd) / xs->size)); 14949e62d8fSDag-Erling Smørgrav } 150aa4b3574SDag-Erling Smørgrav 151db695db7SDag-Erling Smørgrav /* 152db695db7SDag-Erling Smørgrav * Initialize the transfer statistics 153db695db7SDag-Erling Smørgrav */ 15491404f38SDag-Erling Smørgrav static void 15591404f38SDag-Erling Smørgrav stat_start(struct xferstat *xs, const char *name, off_t size, off_t offset) 156b39628e7SDag-Erling Smørgrav { 157b39628e7SDag-Erling Smørgrav snprintf(xs->name, sizeof xs->name, "%s", name); 158aa4b3574SDag-Erling Smørgrav gettimeofday(&xs->start, NULL); 159aa4b3574SDag-Erling Smørgrav xs->last.tv_sec = xs->last.tv_usec = 0; 160aa4b3574SDag-Erling Smørgrav xs->end = xs->last; 161b39628e7SDag-Erling Smørgrav xs->size = size; 162b39628e7SDag-Erling Smørgrav xs->offset = offset; 163b3c141fdSDag-Erling Smørgrav xs->rcvd = offset; 16449e62d8fSDag-Erling Smørgrav stat_display(xs, 1); 165b39628e7SDag-Erling Smørgrav } 166b39628e7SDag-Erling Smørgrav 167db695db7SDag-Erling Smørgrav /* 168db695db7SDag-Erling Smørgrav * Update the transfer statistics 169db695db7SDag-Erling Smørgrav */ 17091404f38SDag-Erling Smørgrav static void 17191404f38SDag-Erling Smørgrav stat_update(struct xferstat *xs, off_t rcvd) 172b39628e7SDag-Erling Smørgrav { 173b39628e7SDag-Erling Smørgrav xs->rcvd = rcvd; 17449e62d8fSDag-Erling Smørgrav stat_display(xs, 0); 175b39628e7SDag-Erling Smørgrav } 176b39628e7SDag-Erling Smørgrav 177db695db7SDag-Erling Smørgrav /* 178db695db7SDag-Erling Smørgrav * Finalize the transfer statistics 179db695db7SDag-Erling Smørgrav */ 18091404f38SDag-Erling Smørgrav static void 181b39628e7SDag-Erling Smørgrav stat_end(struct xferstat *xs) 182b39628e7SDag-Erling Smørgrav { 183b39628e7SDag-Erling Smørgrav double delta; 184b39628e7SDag-Erling Smørgrav double bps; 18532190ef5SDag-Erling Smørgrav 18632190ef5SDag-Erling Smørgrav if (!v_level) 18732190ef5SDag-Erling Smørgrav return; 188b39628e7SDag-Erling Smørgrav 189b39628e7SDag-Erling Smørgrav gettimeofday(&xs->end, NULL); 190b39628e7SDag-Erling Smørgrav 19149e62d8fSDag-Erling Smørgrav stat_display(xs, 1); 192b39628e7SDag-Erling Smørgrav fputc('\n', stderr); 193b39628e7SDag-Erling Smørgrav delta = (xs->end.tv_sec + (xs->end.tv_usec / 1.e6)) 194b39628e7SDag-Erling Smørgrav - (xs->start.tv_sec + (xs->start.tv_usec / 1.e6)); 195b39628e7SDag-Erling Smørgrav fprintf(stderr, "%lld bytes transferred in %.1f seconds ", 19691404f38SDag-Erling Smørgrav (long long)(xs->rcvd - xs->offset), delta); 197aa4b3574SDag-Erling Smørgrav bps = (xs->rcvd - xs->offset) / delta; 198b39628e7SDag-Erling Smørgrav if (bps > 1024*1024) 199b39628e7SDag-Erling Smørgrav fprintf(stderr, "(%.2f MBps)\n", bps / (1024*1024)); 200b39628e7SDag-Erling Smørgrav else if (bps > 1024) 201b39628e7SDag-Erling Smørgrav fprintf(stderr, "(%.2f kBps)\n", bps / 1024); 202b39628e7SDag-Erling Smørgrav else 203b39628e7SDag-Erling Smørgrav fprintf(stderr, "(%.2f Bps)\n", bps); 204b39628e7SDag-Erling Smørgrav } 205b39628e7SDag-Erling Smørgrav 206db695db7SDag-Erling Smørgrav /* 207db695db7SDag-Erling Smørgrav * Ask the user for authentication details 208db695db7SDag-Erling Smørgrav */ 20991404f38SDag-Erling Smørgrav static int 2100d60c709SDag-Erling Smørgrav query_auth(struct url *URL) 2110d60c709SDag-Erling Smørgrav { 2120d60c709SDag-Erling Smørgrav struct termios tios; 2130d60c709SDag-Erling Smørgrav tcflag_t saved_flags; 2140d60c709SDag-Erling Smørgrav int i, nopwd; 2150d60c709SDag-Erling Smørgrav 2160d60c709SDag-Erling Smørgrav 2170d60c709SDag-Erling Smørgrav fprintf(stderr, "Authentication required for <%s://%s:%d/>!\n", 218650b9e0eSIan Dowse URL->scheme, URL->host, URL->port); 2190d60c709SDag-Erling Smørgrav 2200d60c709SDag-Erling Smørgrav fprintf(stderr, "Login: "); 2210d60c709SDag-Erling Smørgrav if (fgets(URL->user, sizeof URL->user, stdin) == NULL) 2220d60c709SDag-Erling Smørgrav return -1; 2230d60c709SDag-Erling Smørgrav for (i = 0; URL->user[i]; ++i) 2240d60c709SDag-Erling Smørgrav if (isspace(URL->user[i])) 2250d60c709SDag-Erling Smørgrav URL->user[i] = '\0'; 2260d60c709SDag-Erling Smørgrav 2270d60c709SDag-Erling Smørgrav fprintf(stderr, "Password: "); 2280d60c709SDag-Erling Smørgrav if (tcgetattr(STDIN_FILENO, &tios) == 0) { 2290d60c709SDag-Erling Smørgrav saved_flags = tios.c_lflag; 2300d60c709SDag-Erling Smørgrav tios.c_lflag &= ~ECHO; 2310d60c709SDag-Erling Smørgrav tios.c_lflag |= ECHONL|ICANON; 2320d60c709SDag-Erling Smørgrav tcsetattr(STDIN_FILENO, TCSAFLUSH|TCSASOFT, &tios); 2330d60c709SDag-Erling Smørgrav nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL); 2340d60c709SDag-Erling Smørgrav tios.c_lflag = saved_flags; 2350d60c709SDag-Erling Smørgrav tcsetattr(STDIN_FILENO, TCSANOW|TCSASOFT, &tios); 2360d60c709SDag-Erling Smørgrav } else { 2370d60c709SDag-Erling Smørgrav nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL); 2380d60c709SDag-Erling Smørgrav } 2390d60c709SDag-Erling Smørgrav if (nopwd) 2400d60c709SDag-Erling Smørgrav return -1; 2410d60c709SDag-Erling Smørgrav 2420d60c709SDag-Erling Smørgrav for (i = 0; URL->pwd[i]; ++i) 2430d60c709SDag-Erling Smørgrav if (isspace(URL->pwd[i])) 2440d60c709SDag-Erling Smørgrav URL->pwd[i] = '\0'; 2450d60c709SDag-Erling Smørgrav return 0; 2460d60c709SDag-Erling Smørgrav } 2470d60c709SDag-Erling Smørgrav 248db695db7SDag-Erling Smørgrav /* 249db695db7SDag-Erling Smørgrav * Fetch a file 250db695db7SDag-Erling Smørgrav */ 25191404f38SDag-Erling Smørgrav static int 25291404f38SDag-Erling Smørgrav fetch(char *URL, const char *path) 253b39628e7SDag-Erling Smørgrav { 254b39628e7SDag-Erling Smørgrav struct url *url; 255b39628e7SDag-Erling Smørgrav struct url_stat us; 256e9dc4f3bSDag-Erling Smørgrav struct stat sb, nsb; 257b39628e7SDag-Erling Smørgrav struct xferstat xs; 258b39628e7SDag-Erling Smørgrav FILE *f, *of; 259cd400b67SDag-Erling Smørgrav size_t size, wr; 260b39628e7SDag-Erling Smørgrav off_t count; 261b39628e7SDag-Erling Smørgrav char flags[8]; 262e9dc4f3bSDag-Erling Smørgrav const char *slash; 263e9dc4f3bSDag-Erling Smørgrav char *tmppath; 2640d60c709SDag-Erling Smørgrav int r; 265b39628e7SDag-Erling Smørgrav u_int timeout; 266cd400b67SDag-Erling Smørgrav u_char *ptr; 267b39628e7SDag-Erling Smørgrav 268b39628e7SDag-Erling Smørgrav f = of = NULL; 269e9dc4f3bSDag-Erling Smørgrav tmppath = NULL; 270b39628e7SDag-Erling Smørgrav 271b39628e7SDag-Erling Smørgrav /* parse URL */ 272b39628e7SDag-Erling Smørgrav if ((url = fetchParseURL(URL)) == NULL) { 273b39628e7SDag-Erling Smørgrav warnx("%s: parse error", URL); 274b39628e7SDag-Erling Smørgrav goto failure; 275b39628e7SDag-Erling Smørgrav } 276b39628e7SDag-Erling Smørgrav 27764638f67SDag-Erling Smørgrav /* if no scheme was specified, take a guess */ 27864638f67SDag-Erling Smørgrav if (!*url->scheme) { 27964638f67SDag-Erling Smørgrav if (!*url->host) 28064638f67SDag-Erling Smørgrav strcpy(url->scheme, SCHEME_FILE); 28174bd3d76SDag-Erling Smørgrav else if (strncasecmp(url->host, "ftp.", 4) == 0) 28264638f67SDag-Erling Smørgrav strcpy(url->scheme, SCHEME_FTP); 28374bd3d76SDag-Erling Smørgrav else if (strncasecmp(url->host, "www.", 4) == 0) 28464638f67SDag-Erling Smørgrav strcpy(url->scheme, SCHEME_HTTP); 28564638f67SDag-Erling Smørgrav } 28664638f67SDag-Erling Smørgrav 287b39628e7SDag-Erling Smørgrav timeout = 0; 288b39628e7SDag-Erling Smørgrav *flags = 0; 289e9a039c2SDag-Erling Smørgrav count = 0; 290b39628e7SDag-Erling Smørgrav 291b39628e7SDag-Erling Smørgrav /* common flags */ 29245b22b2aSDag-Erling Smørgrav if (v_level > 1) 293b39628e7SDag-Erling Smørgrav strcat(flags, "v"); 29462f6d0b8SDag-Erling Smørgrav if (v_level > 2) 29562f6d0b8SDag-Erling Smørgrav fetchDebug = 1; 296b39628e7SDag-Erling Smørgrav switch (family) { 297b39628e7SDag-Erling Smørgrav case PF_INET: 298b39628e7SDag-Erling Smørgrav strcat(flags, "4"); 299b39628e7SDag-Erling Smørgrav break; 300b39628e7SDag-Erling Smørgrav case PF_INET6: 301b39628e7SDag-Erling Smørgrav strcat(flags, "6"); 302b39628e7SDag-Erling Smørgrav break; 303b39628e7SDag-Erling Smørgrav } 304b39628e7SDag-Erling Smørgrav 305b39628e7SDag-Erling Smørgrav /* FTP specific flags */ 306b39628e7SDag-Erling Smørgrav if (strcmp(url->scheme, "ftp") == 0) { 307b39628e7SDag-Erling Smørgrav if (p_flag) 308b39628e7SDag-Erling Smørgrav strcat(flags, "p"); 309b39628e7SDag-Erling Smørgrav if (d_flag) 310b39628e7SDag-Erling Smørgrav strcat(flags, "d"); 3117c480c6cSDag-Erling Smørgrav if (U_flag) 3127c480c6cSDag-Erling Smørgrav strcat(flags, "l"); 313b39628e7SDag-Erling Smørgrav timeout = T_secs ? T_secs : ftp_timeout; 314b39628e7SDag-Erling Smørgrav } 315b39628e7SDag-Erling Smørgrav 316b39628e7SDag-Erling Smørgrav /* HTTP specific flags */ 317b39628e7SDag-Erling Smørgrav if (strcmp(url->scheme, "http") == 0) { 318b39628e7SDag-Erling Smørgrav if (d_flag) 319b39628e7SDag-Erling Smørgrav strcat(flags, "d"); 320b39628e7SDag-Erling Smørgrav if (A_flag) 321b39628e7SDag-Erling Smørgrav strcat(flags, "A"); 322b39628e7SDag-Erling Smørgrav timeout = T_secs ? T_secs : http_timeout; 323b39628e7SDag-Erling Smørgrav } 324b39628e7SDag-Erling Smørgrav 325ef50a72cSDag-Erling Smørgrav /* set the protocol timeout. */ 326b39628e7SDag-Erling Smørgrav fetchTimeout = timeout; 327b39628e7SDag-Erling Smørgrav 328b39628e7SDag-Erling Smørgrav /* just print size */ 329b39628e7SDag-Erling Smørgrav if (s_flag) { 330e9a039c2SDag-Erling Smørgrav if (fetchStat(url, &us, flags) == -1) 331e9a039c2SDag-Erling Smørgrav goto failure; 332b39628e7SDag-Erling Smørgrav if (us.size == -1) 333b39628e7SDag-Erling Smørgrav printf("Unknown\n"); 334b39628e7SDag-Erling Smørgrav else 33591404f38SDag-Erling Smørgrav printf("%lld\n", (long long)us.size); 336b39628e7SDag-Erling Smørgrav goto success; 337b39628e7SDag-Erling Smørgrav } 338b39628e7SDag-Erling Smørgrav 339e9a039c2SDag-Erling Smørgrav /* 34091404f38SDag-Erling Smørgrav * If the -r flag was specified, we have to compare the local 34191404f38SDag-Erling Smørgrav * and remote files, so we should really do a fetchStat() 34291404f38SDag-Erling Smørgrav * first, but I know of at least one HTTP server that only 34391404f38SDag-Erling Smørgrav * sends the content size in response to GET requests, and 34491404f38SDag-Erling Smørgrav * leaves it out of replies to HEAD requests. Also, in the 34591404f38SDag-Erling Smørgrav * (frequent) case that the local and remote files match but 34691404f38SDag-Erling Smørgrav * the local file is truncated, we have sufficient information 34791404f38SDag-Erling Smørgrav * before the compare to issue a correct request. Therefore, 34891404f38SDag-Erling Smørgrav * we always issue a GET request as if we were sure the local 34991404f38SDag-Erling Smørgrav * file was a truncated copy of the remote file; we can drop 35091404f38SDag-Erling Smørgrav * the connection later if we change our minds. 351e9a039c2SDag-Erling Smørgrav */ 352a0c6ec97SDag-Erling Smørgrav sb.st_size = -1; 353e9dc4f3bSDag-Erling Smørgrav if (!o_stdout && stat(path, &sb) == -1 && errno != ENOENT) { 354e9dc4f3bSDag-Erling Smørgrav warnx("%s: stat()", path); 355e9dc4f3bSDag-Erling Smørgrav goto failure; 356a0c6ec97SDag-Erling Smørgrav } 357e9dc4f3bSDag-Erling Smørgrav if (!o_stdout && r_flag && S_ISREG(sb.st_mode)) 358e9dc4f3bSDag-Erling Smørgrav url->offset = sb.st_size; 359e9a039c2SDag-Erling Smørgrav 360e9a039c2SDag-Erling Smørgrav /* start the transfer */ 361e9a039c2SDag-Erling Smørgrav if ((f = fetchXGet(url, &us, flags)) == NULL) { 362e9a039c2SDag-Erling Smørgrav warnx("%s: %s", path, fetchLastErrString); 363e9a039c2SDag-Erling Smørgrav goto failure; 364e9a039c2SDag-Erling Smørgrav } 365e9a039c2SDag-Erling Smørgrav if (sigint) 366e9a039c2SDag-Erling Smørgrav goto signal; 367e9a039c2SDag-Erling Smørgrav 368b39628e7SDag-Erling Smørgrav /* check that size is as expected */ 369e9a039c2SDag-Erling Smørgrav if (S_size) { 370e9a039c2SDag-Erling Smørgrav if (us.size == -1) { 371e9a039c2SDag-Erling Smørgrav warnx("%s: size unknown", path); 372e9a039c2SDag-Erling Smørgrav goto failure; 373e9a039c2SDag-Erling Smørgrav } else if (us.size != S_size) { 374b39628e7SDag-Erling Smørgrav warnx("%s: size mismatch: expected %lld, actual %lld", 37591404f38SDag-Erling Smørgrav path, (long long)S_size, (long long)us.size); 376b39628e7SDag-Erling Smørgrav goto failure; 377b39628e7SDag-Erling Smørgrav } 378e9a039c2SDag-Erling Smørgrav } 379b39628e7SDag-Erling Smørgrav 380b39628e7SDag-Erling Smørgrav /* symlink instead of copy */ 381b39628e7SDag-Erling Smørgrav if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) { 382b39628e7SDag-Erling Smørgrav if (symlink(url->doc, path) == -1) { 383b39628e7SDag-Erling Smørgrav warn("%s: symlink()", path); 384b39628e7SDag-Erling Smørgrav goto failure; 385b39628e7SDag-Erling Smørgrav } 386b39628e7SDag-Erling Smørgrav goto success; 387b39628e7SDag-Erling Smørgrav } 388b39628e7SDag-Erling Smørgrav 38904d12c99SDag-Erling Smørgrav if (us.size == -1 && !o_stdout) 39089a70fbeSDag-Erling Smørgrav warnx("%s: size of remote file is not known", path); 391d939bf77SDag-Erling Smørgrav if (v_level > 1) { 392a0c6ec97SDag-Erling Smørgrav if (sb.st_size != -1) 393a0c6ec97SDag-Erling Smørgrav fprintf(stderr, "local size / mtime: %lld / %ld\n", 39491404f38SDag-Erling Smørgrav (long long)sb.st_size, (long)sb.st_mtime); 39589a70fbeSDag-Erling Smørgrav if (us.size != -1) 396a0c6ec97SDag-Erling Smørgrav fprintf(stderr, "remote size / mtime: %lld / %ld\n", 39791404f38SDag-Erling Smørgrav (long long)us.size, (long)us.mtime); 398d939bf77SDag-Erling Smørgrav } 399d939bf77SDag-Erling Smørgrav 400e9a039c2SDag-Erling Smørgrav /* open output file */ 401b39628e7SDag-Erling Smørgrav if (o_stdout) { 402b39628e7SDag-Erling Smørgrav /* output to stdout */ 403b39628e7SDag-Erling Smørgrav of = stdout; 404e9dc4f3bSDag-Erling Smørgrav } else if (r_flag && sb.st_size != -1) { 405e9a039c2SDag-Erling Smørgrav /* resume mode, local file exists */ 406e9a039c2SDag-Erling Smørgrav if (!F_flag && us.mtime && sb.st_mtime != us.mtime) { 407e9a039c2SDag-Erling Smørgrav /* no match! have to refetch */ 408e9a039c2SDag-Erling Smørgrav fclose(f); 40989a70fbeSDag-Erling Smørgrav /* if precious, warn the user and give up */ 41089a70fbeSDag-Erling Smørgrav if (R_flag) { 41191404f38SDag-Erling Smørgrav warnx("%s: local modification time " 41291404f38SDag-Erling Smørgrav "does not match remote", path); 41389a70fbeSDag-Erling Smørgrav goto failure_keep; 41489a70fbeSDag-Erling Smørgrav } 415e9a039c2SDag-Erling Smørgrav } else { 416e9a039c2SDag-Erling Smørgrav if (us.size == sb.st_size) 417e9a039c2SDag-Erling Smørgrav /* nothing to do */ 418e9a039c2SDag-Erling Smørgrav goto success; 419e9a039c2SDag-Erling Smørgrav if (sb.st_size > us.size) { 420e9a039c2SDag-Erling Smørgrav /* local file too long! */ 421e9a039c2SDag-Erling Smørgrav warnx("%s: local file (%lld bytes) is longer " 42291404f38SDag-Erling Smørgrav "than remote file (%lld bytes)", path, 42391404f38SDag-Erling Smørgrav (long long)sb.st_size, (long long)us.size); 424e9a039c2SDag-Erling Smørgrav goto failure; 425e9a039c2SDag-Erling Smørgrav } 426e9dc4f3bSDag-Erling Smørgrav /* we got it, open local file */ 427b39628e7SDag-Erling Smørgrav if ((of = fopen(path, "a")) == NULL) { 42810e3b1c7SDag-Erling Smørgrav warn("%s: fopen()", path); 429b39628e7SDag-Erling Smørgrav goto failure; 430b39628e7SDag-Erling Smørgrav } 431e9dc4f3bSDag-Erling Smørgrav /* check that it didn't move under our feet */ 432e9dc4f3bSDag-Erling Smørgrav if (fstat(fileno(of), &nsb) == -1) { 433e9dc4f3bSDag-Erling Smørgrav /* can't happen! */ 434e9dc4f3bSDag-Erling Smørgrav warn("%s: fstat()", path); 43510e3b1c7SDag-Erling Smørgrav goto failure; 43610e3b1c7SDag-Erling Smørgrav } 437e9dc4f3bSDag-Erling Smørgrav if (nsb.st_dev != sb.st_dev || 438e9dc4f3bSDag-Erling Smørgrav nsb.st_ino != nsb.st_ino || 439e9dc4f3bSDag-Erling Smørgrav nsb.st_size != sb.st_size) { 440e9dc4f3bSDag-Erling Smørgrav warnx("%s: file has changed", path); 441e9dc4f3bSDag-Erling Smørgrav fclose(of); 442e9dc4f3bSDag-Erling Smørgrav of = NULL; 443e9dc4f3bSDag-Erling Smørgrav sb = nsb; 44410e3b1c7SDag-Erling Smørgrav } 445b39628e7SDag-Erling Smørgrav } 446e9dc4f3bSDag-Erling Smørgrav } else if (m_flag && sb.st_size != -1) { 447e9a039c2SDag-Erling Smørgrav /* mirror mode, local file exists */ 448e9a039c2SDag-Erling Smørgrav if (sb.st_size == us.size && sb.st_mtime == us.mtime) 449e9a039c2SDag-Erling Smørgrav goto success; 450e9a039c2SDag-Erling Smørgrav } 451e9dc4f3bSDag-Erling Smørgrav 452e9dc4f3bSDag-Erling Smørgrav if (of == NULL) { 453e9a039c2SDag-Erling Smørgrav /* 45491404f38SDag-Erling Smørgrav * We don't yet have an output file; either this is a 45591404f38SDag-Erling Smørgrav * vanilla run with no special flags, or the local and 45691404f38SDag-Erling Smørgrav * remote files didn't match. 457e9a039c2SDag-Erling Smørgrav */ 458e9dc4f3bSDag-Erling Smørgrav 459e9dc4f3bSDag-Erling Smørgrav if (url->offset != 0) { 460e9dc4f3bSDag-Erling Smørgrav /* 461e9dc4f3bSDag-Erling Smørgrav * We tried to restart a transfer, but for 462e9dc4f3bSDag-Erling Smørgrav * some reason gave up - so we have to restart 463e9dc4f3bSDag-Erling Smørgrav * from scratch if we want the whole file 464e9dc4f3bSDag-Erling Smørgrav */ 465e9dc4f3bSDag-Erling Smørgrav url->offset = 0; 466e9dc4f3bSDag-Erling Smørgrav if ((f = fetchXGet(url, &us, flags)) == NULL) { 467e9dc4f3bSDag-Erling Smørgrav warnx("%s: %s", path, fetchLastErrString); 468e9dc4f3bSDag-Erling Smørgrav goto failure; 469e9dc4f3bSDag-Erling Smørgrav } 470e9dc4f3bSDag-Erling Smørgrav if (sigint) 471e9dc4f3bSDag-Erling Smørgrav goto signal; 472e9dc4f3bSDag-Erling Smørgrav } 473e9dc4f3bSDag-Erling Smørgrav 474e9dc4f3bSDag-Erling Smørgrav /* construct a temp file name */ 475e9dc4f3bSDag-Erling Smørgrav if (sb.st_size != -1 && S_ISREG(sb.st_mode)) { 476e9dc4f3bSDag-Erling Smørgrav if ((slash = strrchr(path, '/')) == NULL) 477e9dc4f3bSDag-Erling Smørgrav slash = path; 478e9dc4f3bSDag-Erling Smørgrav else 479e9dc4f3bSDag-Erling Smørgrav ++slash; 480e9dc4f3bSDag-Erling Smørgrav asprintf(&tmppath, "%.*s.fetch.XXXXXX.%s", 4811c93d61fSMike Barcroft (int)(slash - path), path, slash); 482e9dc4f3bSDag-Erling Smørgrav } 483e9dc4f3bSDag-Erling Smørgrav 484e9dc4f3bSDag-Erling Smørgrav if (tmppath != NULL) { 485e9dc4f3bSDag-Erling Smørgrav mkstemps(tmppath, strlen(slash) + 1); 486e9dc4f3bSDag-Erling Smørgrav of = fopen(tmppath, "w"); 487e9dc4f3bSDag-Erling Smørgrav } else { 488e9dc4f3bSDag-Erling Smørgrav of = fopen(path, "w"); 489e9dc4f3bSDag-Erling Smørgrav } 490e9dc4f3bSDag-Erling Smørgrav 491e9dc4f3bSDag-Erling Smørgrav if (of == NULL) { 492b39628e7SDag-Erling Smørgrav warn("%s: open()", path); 493b39628e7SDag-Erling Smørgrav goto failure; 494b39628e7SDag-Erling Smørgrav } 495b39628e7SDag-Erling Smørgrav } 496b39628e7SDag-Erling Smørgrav count = url->offset; 497b39628e7SDag-Erling Smørgrav 498b39628e7SDag-Erling Smørgrav /* start the counter */ 499b39628e7SDag-Erling Smørgrav stat_start(&xs, path, us.size, count); 500b39628e7SDag-Erling Smørgrav 501cd400b67SDag-Erling Smørgrav sigalrm = siginfo = sigint = 0; 50249e62d8fSDag-Erling Smørgrav 50349e62d8fSDag-Erling Smørgrav /* suck in the data */ 504cd400b67SDag-Erling Smørgrav signal(SIGINFO, sig_handler); 5050d60c709SDag-Erling Smørgrav while (!sigint && !sigalrm) { 50649e62d8fSDag-Erling Smørgrav if (us.size != -1 && us.size - count < B_size) 507b39628e7SDag-Erling Smørgrav size = us.size - count; 50849e62d8fSDag-Erling Smørgrav else 50949e62d8fSDag-Erling Smørgrav size = B_size; 510b39628e7SDag-Erling Smørgrav if (timeout) 511b39628e7SDag-Erling Smørgrav alarm(timeout); 5120d60c709SDag-Erling Smørgrav if (siginfo) { 5130d60c709SDag-Erling Smørgrav stat_end(&xs); 5140d60c709SDag-Erling Smørgrav siginfo = 0; 5150d60c709SDag-Erling Smørgrav } 516cd400b67SDag-Erling Smørgrav if ((size = fread(buf, 1, size, f)) == 0) { 517cd400b67SDag-Erling Smørgrav if (ferror(f) && errno == EINTR && !sigalrm && !sigint) 518cd400b67SDag-Erling Smørgrav clearerr(f); 519cd400b67SDag-Erling Smørgrav else 520aa4b3574SDag-Erling Smørgrav break; 521b39628e7SDag-Erling Smørgrav } 522cd400b67SDag-Erling Smørgrav if (timeout) 523cd400b67SDag-Erling Smørgrav alarm(0); 52491404f38SDag-Erling Smørgrav stat_update(&xs, count += size); 525cd400b67SDag-Erling Smørgrav for (ptr = buf; size > 0; ptr += wr, size -= wr) 526cd400b67SDag-Erling Smørgrav if ((wr = fwrite(ptr, 1, size, of)) < size) { 52791404f38SDag-Erling Smørgrav if (ferror(of) && errno == EINTR && 52891404f38SDag-Erling Smørgrav !sigalrm && !sigint) 529cd400b67SDag-Erling Smørgrav clearerr(of); 530cd400b67SDag-Erling Smørgrav else 531cd400b67SDag-Erling Smørgrav break; 532cd400b67SDag-Erling Smørgrav } 533cd400b67SDag-Erling Smørgrav if (size != 0) 534cd400b67SDag-Erling Smørgrav break; 535cd400b67SDag-Erling Smørgrav } 536cd400b67SDag-Erling Smørgrav signal(SIGINFO, SIG_DFL); 537b39628e7SDag-Erling Smørgrav 538b39628e7SDag-Erling Smørgrav if (timeout) 539b39628e7SDag-Erling Smørgrav alarm(0); 540b39628e7SDag-Erling Smørgrav 541b39628e7SDag-Erling Smørgrav stat_end(&xs); 542b39628e7SDag-Erling Smørgrav 54330204f98SDag-Erling Smørgrav /* set mtime of local file */ 54474912943SDag-Erling Smørgrav if (!n_flag && us.mtime && !o_stdout 54574912943SDag-Erling Smørgrav && (stat(path, &sb) != -1) && sb.st_mode & S_IFREG) { 546b39628e7SDag-Erling Smørgrav struct timeval tv[2]; 547b39628e7SDag-Erling Smørgrav 54849e62d8fSDag-Erling Smørgrav fflush(of); 54949e62d8fSDag-Erling Smørgrav tv[0].tv_sec = (long)(us.atime ? us.atime : us.mtime); 550b39628e7SDag-Erling Smørgrav tv[1].tv_sec = (long)us.mtime; 551b39628e7SDag-Erling Smørgrav tv[0].tv_usec = tv[1].tv_usec = 0; 5528e7cf3deSDag-Erling Smørgrav if (utimes(tmppath ? tmppath : path, tv)) 5538e7cf3deSDag-Erling Smørgrav warn("%s: utimes()", tmppath ? tmppath : path); 554b39628e7SDag-Erling Smørgrav } 555b39628e7SDag-Erling Smørgrav 55649e62d8fSDag-Erling Smørgrav /* timed out or interrupted? */ 557e9a039c2SDag-Erling Smørgrav signal: 558ef50a72cSDag-Erling Smørgrav if (sigalrm) 559ef50a72cSDag-Erling Smørgrav warnx("transfer timed out"); 5609516ffa7SDag-Erling Smørgrav if (sigint) { 561ef50a72cSDag-Erling Smørgrav warnx("transfer interrupted"); 5629516ffa7SDag-Erling Smørgrav goto failure; 5639516ffa7SDag-Erling Smørgrav } 56449e62d8fSDag-Erling Smørgrav 5659516ffa7SDag-Erling Smørgrav if (!sigalrm) { 56649e62d8fSDag-Erling Smørgrav /* check the status of our files */ 56749e62d8fSDag-Erling Smørgrav if (ferror(f)) 56849e62d8fSDag-Erling Smørgrav warn("%s", URL); 56949e62d8fSDag-Erling Smørgrav if (ferror(of)) 57049e62d8fSDag-Erling Smørgrav warn("%s", path); 57149e62d8fSDag-Erling Smørgrav if (ferror(f) || ferror(of)) 57249e62d8fSDag-Erling Smørgrav goto failure; 57378394463SDag-Erling Smørgrav } 57449e62d8fSDag-Erling Smørgrav 57549e62d8fSDag-Erling Smørgrav /* did the transfer complete normally? */ 57649e62d8fSDag-Erling Smørgrav if (us.size != -1 && count < us.size) { 577bb11a878SDag-Erling Smørgrav warnx("%s appears to be truncated: %lld/%lld bytes", 57891404f38SDag-Erling Smørgrav path, (long long)count, (long long)us.size); 57949e62d8fSDag-Erling Smørgrav goto failure_keep; 580bb11a878SDag-Erling Smørgrav } 581bb11a878SDag-Erling Smørgrav 58230204f98SDag-Erling Smørgrav /* 58330204f98SDag-Erling Smørgrav * If the transfer timed out and we didn't know how much to 58430204f98SDag-Erling Smørgrav * expect, assume the worst (i.e. we didn't get all of it) 58530204f98SDag-Erling Smørgrav */ 58630204f98SDag-Erling Smørgrav if (sigalrm && us.size == -1) { 58730204f98SDag-Erling Smørgrav warnx("%s may be truncated", path); 58830204f98SDag-Erling Smørgrav goto failure_keep; 58930204f98SDag-Erling Smørgrav } 59030204f98SDag-Erling Smørgrav 591b39628e7SDag-Erling Smørgrav success: 59249e62d8fSDag-Erling Smørgrav r = 0; 593e9dc4f3bSDag-Erling Smørgrav if (tmppath != NULL && rename(tmppath, path) == -1) { 594e9dc4f3bSDag-Erling Smørgrav warn("%s: rename()", path); 595e9dc4f3bSDag-Erling Smørgrav goto failure_keep; 596e9dc4f3bSDag-Erling Smørgrav } 597b39628e7SDag-Erling Smørgrav goto done; 598b39628e7SDag-Erling Smørgrav failure: 59949e62d8fSDag-Erling Smørgrav if (of && of != stdout && !R_flag && !r_flag) 600e2b41a62SDag-Erling Smørgrav if (stat(path, &sb) != -1 && (sb.st_mode & S_IFREG)) 601e9dc4f3bSDag-Erling Smørgrav unlink(tmppath ? tmppath : path); 602e9dc4f3bSDag-Erling Smørgrav if (R_flag && tmppath != NULL && sb.st_size == -1) 603e9dc4f3bSDag-Erling Smørgrav rename(tmppath, path); /* ignore errors here */ 60449e62d8fSDag-Erling Smørgrav failure_keep: 605b39628e7SDag-Erling Smørgrav r = -1; 606b39628e7SDag-Erling Smørgrav goto done; 607b39628e7SDag-Erling Smørgrav done: 608b39628e7SDag-Erling Smørgrav if (f) 609b39628e7SDag-Erling Smørgrav fclose(f); 610b39628e7SDag-Erling Smørgrav if (of && of != stdout) 611b39628e7SDag-Erling Smørgrav fclose(of); 612ec850e74SDag-Erling Smørgrav if (url) 613b39628e7SDag-Erling Smørgrav fetchFreeURL(url); 614e9dc4f3bSDag-Erling Smørgrav if (tmppath != NULL) 615e9dc4f3bSDag-Erling Smørgrav free(tmppath); 616b39628e7SDag-Erling Smørgrav return r; 617b39628e7SDag-Erling Smørgrav } 618b39628e7SDag-Erling Smørgrav 61991404f38SDag-Erling Smørgrav static void 620b39628e7SDag-Erling Smørgrav usage(void) 621b39628e7SDag-Erling Smørgrav { 62291404f38SDag-Erling Smørgrav fprintf(stderr, "%s\n%s\n%s\n", 62391404f38SDag-Erling Smørgrav "Usage: fetch [-146AFMPRUadlmnpqrsv] [-o outputfile] [-S bytes]", 62491404f38SDag-Erling Smørgrav " [-B bytes] [-T seconds] [-w seconds]", 62591404f38SDag-Erling Smørgrav " [-h host -f file [-c dir] | URL ...]"); 626b39628e7SDag-Erling Smørgrav } 627b39628e7SDag-Erling Smørgrav 628b39628e7SDag-Erling Smørgrav 629b39628e7SDag-Erling Smørgrav #define PARSENUM(NAME, TYPE) \ 63091404f38SDag-Erling Smørgrav static int \ 63191404f38SDag-Erling Smørgrav NAME(const char *s, TYPE *v) \ 632b39628e7SDag-Erling Smørgrav { \ 633b39628e7SDag-Erling Smørgrav *v = 0; \ 634b39628e7SDag-Erling Smørgrav for (*v = 0; *s; s++) \ 635b39628e7SDag-Erling Smørgrav if (isdigit(*s)) \ 636b39628e7SDag-Erling Smørgrav *v = *v * 10 + *s - '0'; \ 637b39628e7SDag-Erling Smørgrav else \ 638b39628e7SDag-Erling Smørgrav return -1; \ 639b39628e7SDag-Erling Smørgrav return 0; \ 640b39628e7SDag-Erling Smørgrav } 641b39628e7SDag-Erling Smørgrav 642db695db7SDag-Erling Smørgrav PARSENUM(parseint, u_int); 643db695db7SDag-Erling Smørgrav PARSENUM(parseoff, off_t); 644b39628e7SDag-Erling Smørgrav 645db695db7SDag-Erling Smørgrav /* 646db695db7SDag-Erling Smørgrav * Entry point 647db695db7SDag-Erling Smørgrav */ 648b39628e7SDag-Erling Smørgrav int 649b39628e7SDag-Erling Smørgrav main(int argc, char *argv[]) 650b39628e7SDag-Erling Smørgrav { 651b39628e7SDag-Erling Smørgrav struct stat sb; 65278394463SDag-Erling Smørgrav struct sigaction sa; 65391404f38SDag-Erling Smørgrav const char *p, *s; 65491404f38SDag-Erling Smørgrav char *q; 655b39628e7SDag-Erling Smørgrav int c, e, r; 656b39628e7SDag-Erling Smørgrav 657b39628e7SDag-Erling Smørgrav while ((c = getopt(argc, argv, 6587c480c6cSDag-Erling Smørgrav "146AaB:bc:dFf:Hh:lMmnPpo:qRrS:sT:tUvw:")) != EOF) 659b39628e7SDag-Erling Smørgrav switch (c) { 660b39628e7SDag-Erling Smørgrav case '1': 661b39628e7SDag-Erling Smørgrav once_flag = 1; 662b39628e7SDag-Erling Smørgrav break; 663b39628e7SDag-Erling Smørgrav case '4': 664b39628e7SDag-Erling Smørgrav family = PF_INET; 665b39628e7SDag-Erling Smørgrav break; 666b39628e7SDag-Erling Smørgrav case '6': 667b39628e7SDag-Erling Smørgrav family = PF_INET6; 668b39628e7SDag-Erling Smørgrav break; 669b39628e7SDag-Erling Smørgrav case 'A': 670b39628e7SDag-Erling Smørgrav A_flag = 1; 671b39628e7SDag-Erling Smørgrav break; 672b39628e7SDag-Erling Smørgrav case 'a': 673b39628e7SDag-Erling Smørgrav a_flag = 1; 674b39628e7SDag-Erling Smørgrav break; 675b39628e7SDag-Erling Smørgrav case 'B': 67691404f38SDag-Erling Smørgrav if (parseoff(optarg, &B_size) == -1) 6776d64e939SStefan Eßer errx(1, "invalid buffer size (%s)", optarg); 678b39628e7SDag-Erling Smørgrav break; 679b39628e7SDag-Erling Smørgrav case 'b': 680b39628e7SDag-Erling Smørgrav warnx("warning: the -b option is deprecated"); 681b39628e7SDag-Erling Smørgrav b_flag = 1; 682b39628e7SDag-Erling Smørgrav break; 683a8369cd9SDag-Erling Smørgrav case 'c': 684a8369cd9SDag-Erling Smørgrav c_dirname = optarg; 685a8369cd9SDag-Erling Smørgrav break; 686b39628e7SDag-Erling Smørgrav case 'd': 687b39628e7SDag-Erling Smørgrav d_flag = 1; 688b39628e7SDag-Erling Smørgrav break; 689b39628e7SDag-Erling Smørgrav case 'F': 690b39628e7SDag-Erling Smørgrav F_flag = 1; 691b39628e7SDag-Erling Smørgrav break; 692b39628e7SDag-Erling Smørgrav case 'f': 693b39628e7SDag-Erling Smørgrav f_filename = optarg; 694b39628e7SDag-Erling Smørgrav break; 695b39628e7SDag-Erling Smørgrav case 'H': 69691404f38SDag-Erling Smørgrav warnx("The -H option is now implicit, " 69791404f38SDag-Erling Smørgrav "use -U to disable"); 698b39628e7SDag-Erling Smørgrav break; 699b39628e7SDag-Erling Smørgrav case 'h': 700b39628e7SDag-Erling Smørgrav h_hostname = optarg; 701b39628e7SDag-Erling Smørgrav break; 702b39628e7SDag-Erling Smørgrav case 'l': 703b39628e7SDag-Erling Smørgrav l_flag = 1; 704b39628e7SDag-Erling Smørgrav break; 705b39628e7SDag-Erling Smørgrav case 'o': 706b39628e7SDag-Erling Smørgrav o_flag = 1; 707b39628e7SDag-Erling Smørgrav o_filename = optarg; 708b39628e7SDag-Erling Smørgrav break; 709b39628e7SDag-Erling Smørgrav case 'M': 710b39628e7SDag-Erling Smørgrav case 'm': 711e9a039c2SDag-Erling Smørgrav if (r_flag) 71291404f38SDag-Erling Smørgrav errx(1, "the -m and -r flags " 71391404f38SDag-Erling Smørgrav "are mutually exclusive"); 714b39628e7SDag-Erling Smørgrav m_flag = 1; 715b39628e7SDag-Erling Smørgrav break; 716b39628e7SDag-Erling Smørgrav case 'n': 717bb11a878SDag-Erling Smørgrav n_flag = 1; 718b39628e7SDag-Erling Smørgrav break; 719b39628e7SDag-Erling Smørgrav case 'P': 720b39628e7SDag-Erling Smørgrav case 'p': 721b39628e7SDag-Erling Smørgrav p_flag = 1; 722b39628e7SDag-Erling Smørgrav break; 723b39628e7SDag-Erling Smørgrav case 'q': 724b39628e7SDag-Erling Smørgrav v_level = 0; 725b39628e7SDag-Erling Smørgrav break; 726b39628e7SDag-Erling Smørgrav case 'R': 727b39628e7SDag-Erling Smørgrav R_flag = 1; 728b39628e7SDag-Erling Smørgrav break; 729b39628e7SDag-Erling Smørgrav case 'r': 730e9a039c2SDag-Erling Smørgrav if (m_flag) 73191404f38SDag-Erling Smørgrav errx(1, "the -m and -r flags " 73291404f38SDag-Erling Smørgrav "are mutually exclusive"); 733b39628e7SDag-Erling Smørgrav r_flag = 1; 734b39628e7SDag-Erling Smørgrav break; 735b39628e7SDag-Erling Smørgrav case 'S': 736b39628e7SDag-Erling Smørgrav if (parseoff(optarg, &S_size) == -1) 7376d64e939SStefan Eßer errx(1, "invalid size (%s)", optarg); 738b39628e7SDag-Erling Smørgrav break; 739b39628e7SDag-Erling Smørgrav case 's': 740b39628e7SDag-Erling Smørgrav s_flag = 1; 741b39628e7SDag-Erling Smørgrav break; 742b39628e7SDag-Erling Smørgrav case 'T': 743b39628e7SDag-Erling Smørgrav if (parseint(optarg, &T_secs) == -1) 7446d64e939SStefan Eßer errx(1, "invalid timeout (%s)", optarg); 745b39628e7SDag-Erling Smørgrav break; 746b39628e7SDag-Erling Smørgrav case 't': 747b39628e7SDag-Erling Smørgrav t_flag = 1; 748b39628e7SDag-Erling Smørgrav warnx("warning: the -t option is deprecated"); 749b39628e7SDag-Erling Smørgrav break; 7507c480c6cSDag-Erling Smørgrav case 'U': 7517c480c6cSDag-Erling Smørgrav U_flag = 1; 7527c480c6cSDag-Erling Smørgrav break; 753b39628e7SDag-Erling Smørgrav case 'v': 754b39628e7SDag-Erling Smørgrav v_level++; 755b39628e7SDag-Erling Smørgrav break; 756b39628e7SDag-Erling Smørgrav case 'w': 757b39628e7SDag-Erling Smørgrav a_flag = 1; 758b39628e7SDag-Erling Smørgrav if (parseint(optarg, &w_secs) == -1) 7596d64e939SStefan Eßer errx(1, "invalid delay (%s)", optarg); 760b39628e7SDag-Erling Smørgrav break; 761b39628e7SDag-Erling Smørgrav default: 762b39628e7SDag-Erling Smørgrav usage(); 763b39628e7SDag-Erling Smørgrav exit(EX_USAGE); 764b39628e7SDag-Erling Smørgrav } 765b39628e7SDag-Erling Smørgrav 766b39628e7SDag-Erling Smørgrav argc -= optind; 767b39628e7SDag-Erling Smørgrav argv += optind; 768b39628e7SDag-Erling Smørgrav 769a8369cd9SDag-Erling Smørgrav if (h_hostname || f_filename || c_dirname) { 770b39628e7SDag-Erling Smørgrav if (!h_hostname || !f_filename || argc) { 771b39628e7SDag-Erling Smørgrav usage(); 772b39628e7SDag-Erling Smørgrav exit(EX_USAGE); 773b39628e7SDag-Erling Smørgrav } 774b39628e7SDag-Erling Smørgrav /* XXX this is a hack. */ 775b39628e7SDag-Erling Smørgrav if (strcspn(h_hostname, "@:/") != strlen(h_hostname)) 776b39628e7SDag-Erling Smørgrav errx(1, "invalid hostname"); 777a8369cd9SDag-Erling Smørgrav if (asprintf(argv, "ftp://%s/%s/%s", h_hostname, 778a8369cd9SDag-Erling Smørgrav c_dirname ? c_dirname : "", f_filename) == -1) 779a6f339d2SKris Kennaway errx(1, "%s", strerror(ENOMEM)); 780b39628e7SDag-Erling Smørgrav argc++; 781b39628e7SDag-Erling Smørgrav } 782b39628e7SDag-Erling Smørgrav 783b39628e7SDag-Erling Smørgrav if (!argc) { 784b39628e7SDag-Erling Smørgrav usage(); 785b39628e7SDag-Erling Smørgrav exit(EX_USAGE); 786b39628e7SDag-Erling Smørgrav } 787b39628e7SDag-Erling Smørgrav 788b39628e7SDag-Erling Smørgrav /* allocate buffer */ 789b39628e7SDag-Erling Smørgrav if (B_size < MINBUFSIZE) 790b39628e7SDag-Erling Smørgrav B_size = MINBUFSIZE; 791b39628e7SDag-Erling Smørgrav if ((buf = malloc(B_size)) == NULL) 792a6f339d2SKris Kennaway errx(1, "%s", strerror(ENOMEM)); 793b39628e7SDag-Erling Smørgrav 79478394463SDag-Erling Smørgrav /* timeouts */ 795b39628e7SDag-Erling Smørgrav if ((s = getenv("FTP_TIMEOUT")) != NULL) { 796b39628e7SDag-Erling Smørgrav if (parseint(s, &ftp_timeout) == -1) { 7976d64e939SStefan Eßer warnx("FTP_TIMEOUT (%s) is not a positive integer", 7986d64e939SStefan Eßer optarg); 799b39628e7SDag-Erling Smørgrav ftp_timeout = 0; 800b39628e7SDag-Erling Smørgrav } 801b39628e7SDag-Erling Smørgrav } 802b39628e7SDag-Erling Smørgrav if ((s = getenv("HTTP_TIMEOUT")) != NULL) { 803b39628e7SDag-Erling Smørgrav if (parseint(s, &http_timeout) == -1) { 8046d64e939SStefan Eßer warnx("HTTP_TIMEOUT (%s) is not a positive integer", 8056d64e939SStefan Eßer optarg); 806b39628e7SDag-Erling Smørgrav http_timeout = 0; 807b39628e7SDag-Erling Smørgrav } 808b39628e7SDag-Erling Smørgrav } 809b39628e7SDag-Erling Smørgrav 81078394463SDag-Erling Smørgrav /* signal handling */ 81178394463SDag-Erling Smørgrav sa.sa_flags = 0; 81278394463SDag-Erling Smørgrav sa.sa_handler = sig_handler; 81378394463SDag-Erling Smørgrav sigemptyset(&sa.sa_mask); 814e9a039c2SDag-Erling Smørgrav sigaction(SIGALRM, &sa, NULL); 815e9a039c2SDag-Erling Smørgrav sa.sa_flags = SA_RESETHAND; 816e9a039c2SDag-Erling Smørgrav sigaction(SIGINT, &sa, NULL); 817e9a039c2SDag-Erling Smørgrav fetchRestartCalls = 0; 818ef50a72cSDag-Erling Smørgrav 819b39628e7SDag-Erling Smørgrav /* output file */ 820b39628e7SDag-Erling Smørgrav if (o_flag) { 821b39628e7SDag-Erling Smørgrav if (strcmp(o_filename, "-") == 0) { 822b39628e7SDag-Erling Smørgrav o_stdout = 1; 823b39628e7SDag-Erling Smørgrav } else if (stat(o_filename, &sb) == -1) { 824b39628e7SDag-Erling Smørgrav if (errno == ENOENT) { 825b39628e7SDag-Erling Smørgrav if (argc > 1) 82691404f38SDag-Erling Smørgrav errx(EX_USAGE, "%s is not a directory", 82791404f38SDag-Erling Smørgrav o_filename); 828b39628e7SDag-Erling Smørgrav } else { 829b39628e7SDag-Erling Smørgrav err(EX_IOERR, "%s", o_filename); 830b39628e7SDag-Erling Smørgrav } 831b39628e7SDag-Erling Smørgrav } else { 832b39628e7SDag-Erling Smørgrav if (sb.st_mode & S_IFDIR) 833b39628e7SDag-Erling Smørgrav o_directory = 1; 834b39628e7SDag-Erling Smørgrav } 835b39628e7SDag-Erling Smørgrav } 836b39628e7SDag-Erling Smørgrav 837b39628e7SDag-Erling Smørgrav /* check if output is to a tty (for progress report) */ 838bb11a878SDag-Erling Smørgrav v_tty = isatty(STDERR_FILENO); 839dbcc1983SDag-Erling Smørgrav if (v_tty) 840dbcc1983SDag-Erling Smørgrav pgrp = getpgrp(); 841dbcc1983SDag-Erling Smørgrav 842b39628e7SDag-Erling Smørgrav r = 0; 843b39628e7SDag-Erling Smørgrav 8440d60c709SDag-Erling Smørgrav /* authentication */ 8455ef824edSDag-Erling Smørgrav if (v_tty) 8460d60c709SDag-Erling Smørgrav fetchAuthMethod = query_auth; 8470d60c709SDag-Erling Smørgrav 848b39628e7SDag-Erling Smørgrav while (argc) { 849b39628e7SDag-Erling Smørgrav if ((p = strrchr(*argv, '/')) == NULL) 850b39628e7SDag-Erling Smørgrav p = *argv; 851b39628e7SDag-Erling Smørgrav else 852b39628e7SDag-Erling Smørgrav p++; 853b39628e7SDag-Erling Smørgrav 854b39628e7SDag-Erling Smørgrav if (!*p) 855b39628e7SDag-Erling Smørgrav p = "fetch.out"; 856b39628e7SDag-Erling Smørgrav 857b39628e7SDag-Erling Smørgrav fetchLastErrCode = 0; 858b39628e7SDag-Erling Smørgrav 859b39628e7SDag-Erling Smørgrav if (o_flag) { 860b39628e7SDag-Erling Smørgrav if (o_stdout) { 861b39628e7SDag-Erling Smørgrav e = fetch(*argv, "-"); 862b39628e7SDag-Erling Smørgrav } else if (o_directory) { 863b39628e7SDag-Erling Smørgrav asprintf(&q, "%s/%s", o_filename, p); 864b39628e7SDag-Erling Smørgrav e = fetch(*argv, q); 865b39628e7SDag-Erling Smørgrav free(q); 866b39628e7SDag-Erling Smørgrav } else { 867b39628e7SDag-Erling Smørgrav e = fetch(*argv, o_filename); 868b39628e7SDag-Erling Smørgrav } 869b39628e7SDag-Erling Smørgrav } else { 870b39628e7SDag-Erling Smørgrav e = fetch(*argv, p); 871b39628e7SDag-Erling Smørgrav } 872b39628e7SDag-Erling Smørgrav 873ef50a72cSDag-Erling Smørgrav if (sigint) 874e9a039c2SDag-Erling Smørgrav kill(getpid(), SIGINT); 875ef50a72cSDag-Erling Smørgrav 876b39628e7SDag-Erling Smørgrav if (e == 0 && once_flag) 877b39628e7SDag-Erling Smørgrav exit(0); 878b39628e7SDag-Erling Smørgrav 879b39628e7SDag-Erling Smørgrav if (e) { 880b39628e7SDag-Erling Smørgrav r = 1; 881b39628e7SDag-Erling Smørgrav if ((fetchLastErrCode 882b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_UNAVAIL 883b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_MOVED 884b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_URL 885b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_RESOLV 886b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_UNKNOWN)) { 88791404f38SDag-Erling Smørgrav if (w_secs && v_level) 88891404f38SDag-Erling Smørgrav fprintf(stderr, "Waiting %d seconds " 88991404f38SDag-Erling Smørgrav "before retrying\n", w_secs); 89091404f38SDag-Erling Smørgrav if (w_secs) 891b39628e7SDag-Erling Smørgrav sleep(w_secs); 892b39628e7SDag-Erling Smørgrav if (a_flag) 893b39628e7SDag-Erling Smørgrav continue; 894b39628e7SDag-Erling Smørgrav } 895b39628e7SDag-Erling Smørgrav } 896b39628e7SDag-Erling Smørgrav 897b39628e7SDag-Erling Smørgrav argc--, argv++; 898b39628e7SDag-Erling Smørgrav } 899b39628e7SDag-Erling Smørgrav 900b39628e7SDag-Erling Smørgrav exit(r); 901b39628e7SDag-Erling Smørgrav } 902