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/stat.h> 33b39628e7SDag-Erling Smørgrav #include <sys/socket.h> 34b39628e7SDag-Erling Smørgrav 35b39628e7SDag-Erling Smørgrav #include <ctype.h> 36b39628e7SDag-Erling Smørgrav #include <err.h> 37b39628e7SDag-Erling Smørgrav #include <errno.h> 3878394463SDag-Erling Smørgrav #include <signal.h> 39b39628e7SDag-Erling Smørgrav #include <stdio.h> 40b39628e7SDag-Erling Smørgrav #include <stdlib.h> 41b39628e7SDag-Erling Smørgrav #include <string.h> 42b39628e7SDag-Erling Smørgrav #include <sysexits.h> 430d60c709SDag-Erling Smørgrav #include <termios.h> 44b39628e7SDag-Erling Smørgrav #include <unistd.h> 45b39628e7SDag-Erling Smørgrav 46b39628e7SDag-Erling Smørgrav #include <fetch.h> 47b39628e7SDag-Erling Smørgrav 48b39628e7SDag-Erling Smørgrav #define MINBUFSIZE 4096 49b39628e7SDag-Erling Smørgrav 50b39628e7SDag-Erling Smørgrav /* Option flags */ 51b39628e7SDag-Erling Smørgrav int A_flag; /* -A: do not follow 302 redirects */ 52b39628e7SDag-Erling Smørgrav int a_flag; /* -a: auto retry */ 5391404f38SDag-Erling Smørgrav off_t B_size; /* -B: buffer size */ 54b39628e7SDag-Erling Smørgrav int b_flag; /*! -b: workaround TCP bug */ 55a8369cd9SDag-Erling Smørgrav char *c_dirname; /* -c: remote directory */ 56b39628e7SDag-Erling Smørgrav int d_flag; /* -d: direct connection */ 57b39628e7SDag-Erling Smørgrav int F_flag; /* -F: restart without checking mtime */ 58b39628e7SDag-Erling Smørgrav char *f_filename; /* -f: file to fetch */ 59b39628e7SDag-Erling Smørgrav char *h_hostname; /* -h: host to fetch from */ 60b39628e7SDag-Erling Smørgrav int l_flag; /* -l: link rather than copy file: URLs */ 61bb11a878SDag-Erling Smørgrav int m_flag; /* -[Mm]: mirror mode */ 62bb11a878SDag-Erling Smørgrav int n_flag; /* -n: do not preserve modification time */ 63b39628e7SDag-Erling Smørgrav int o_flag; /* -o: specify output file */ 64b39628e7SDag-Erling Smørgrav int o_directory; /* output file is a directory */ 65b39628e7SDag-Erling Smørgrav char *o_filename; /* name of output file */ 66b39628e7SDag-Erling Smørgrav int o_stdout; /* output file is stdout */ 67b39628e7SDag-Erling Smørgrav int once_flag; /* -1: stop at first successful file */ 68a5e3ae21SDag-Erling Smørgrav int p_flag; /* -[Pp]: use passive FTP */ 69b39628e7SDag-Erling Smørgrav int R_flag; /* -R: don't delete partially transferred files */ 70b39628e7SDag-Erling Smørgrav int r_flag; /* -r: restart previously interrupted transfer */ 71b39628e7SDag-Erling Smørgrav off_t S_size; /* -S: require size to match */ 727c480c6cSDag-Erling Smørgrav int s_flag; /* -s: show size, don't fetch */ 737c480c6cSDag-Erling Smørgrav u_int T_secs = 0; /* -T: transfer timeout in seconds */ 74b39628e7SDag-Erling Smørgrav int t_flag; /*! -t: workaround TCP bug */ 757c480c6cSDag-Erling Smørgrav int U_flag; /* -U: do not use high ports */ 76b39628e7SDag-Erling Smørgrav int v_level = 1; /* -v: verbosity level */ 77b39628e7SDag-Erling Smørgrav int v_tty; /* stdout is a tty */ 78b39628e7SDag-Erling Smørgrav u_int w_secs; /* -w: retry delay */ 79b39628e7SDag-Erling Smørgrav int family = PF_UNSPEC; /* -[46]: address family to use */ 80b39628e7SDag-Erling Smørgrav 81ef50a72cSDag-Erling Smørgrav int sigalrm; /* SIGALRM received */ 82cd400b67SDag-Erling Smørgrav int siginfo; /* SIGINFO received */ 83ef50a72cSDag-Erling Smørgrav int sigint; /* SIGINT received */ 84b39628e7SDag-Erling Smørgrav 85b39628e7SDag-Erling Smørgrav u_int ftp_timeout; /* default timeout for FTP transfers */ 86b39628e7SDag-Erling Smørgrav u_int http_timeout; /* default timeout for HTTP transfers */ 87b39628e7SDag-Erling Smørgrav u_char *buf; /* transfer buffer */ 88b39628e7SDag-Erling Smørgrav 89b39628e7SDag-Erling Smørgrav 9091404f38SDag-Erling Smørgrav static void 91b39628e7SDag-Erling Smørgrav sig_handler(int sig) 92b39628e7SDag-Erling Smørgrav { 93ef50a72cSDag-Erling Smørgrav switch (sig) { 94ef50a72cSDag-Erling Smørgrav case SIGALRM: 95ef50a72cSDag-Erling Smørgrav sigalrm = 1; 96ef50a72cSDag-Erling Smørgrav break; 97cd400b67SDag-Erling Smørgrav case SIGINFO: 98cd400b67SDag-Erling Smørgrav siginfo = 1; 99cd400b67SDag-Erling Smørgrav break; 100ef50a72cSDag-Erling Smørgrav case SIGINT: 101ef50a72cSDag-Erling Smørgrav sigint = 1; 102ef50a72cSDag-Erling Smørgrav break; 103ef50a72cSDag-Erling Smørgrav } 104b39628e7SDag-Erling Smørgrav } 105b39628e7SDag-Erling Smørgrav 106b39628e7SDag-Erling Smørgrav struct xferstat { 107b39628e7SDag-Erling Smørgrav char name[40]; 108b39628e7SDag-Erling Smørgrav struct timeval start; 109b39628e7SDag-Erling Smørgrav struct timeval end; 110b39628e7SDag-Erling Smørgrav struct timeval last; 111b39628e7SDag-Erling Smørgrav off_t size; 112b39628e7SDag-Erling Smørgrav off_t offset; 113b39628e7SDag-Erling Smørgrav off_t rcvd; 114b39628e7SDag-Erling Smørgrav }; 115b39628e7SDag-Erling Smørgrav 11691404f38SDag-Erling Smørgrav static void 11749e62d8fSDag-Erling Smørgrav stat_display(struct xferstat *xs, int force) 11849e62d8fSDag-Erling Smørgrav { 11949e62d8fSDag-Erling Smørgrav struct timeval now; 12049e62d8fSDag-Erling Smørgrav 121d939bf77SDag-Erling Smørgrav if (!v_tty || !v_level) 12249e62d8fSDag-Erling Smørgrav return; 12349e62d8fSDag-Erling Smørgrav 12449e62d8fSDag-Erling Smørgrav gettimeofday(&now, NULL); 12549e62d8fSDag-Erling Smørgrav if (!force && now.tv_sec <= xs->last.tv_sec) 12649e62d8fSDag-Erling Smørgrav return; 12749e62d8fSDag-Erling Smørgrav xs->last = now; 12849e62d8fSDag-Erling Smørgrav 12949e62d8fSDag-Erling Smørgrav fprintf(stderr, "\rReceiving %s", xs->name); 1301a4a0639SDag-Erling Smørgrav if (xs->size <= 0) 13191404f38SDag-Erling Smørgrav fprintf(stderr, ": %lld bytes", (long long)xs->rcvd); 13249e62d8fSDag-Erling Smørgrav else 13391404f38SDag-Erling Smørgrav fprintf(stderr, " (%lld bytes): %d%%", (long long)xs->size, 134b3c141fdSDag-Erling Smørgrav (int)((100.0 * xs->rcvd) / xs->size)); 13549e62d8fSDag-Erling Smørgrav } 136aa4b3574SDag-Erling Smørgrav 13791404f38SDag-Erling Smørgrav static void 13891404f38SDag-Erling Smørgrav stat_start(struct xferstat *xs, const char *name, off_t size, off_t offset) 139b39628e7SDag-Erling Smørgrav { 140b39628e7SDag-Erling Smørgrav snprintf(xs->name, sizeof xs->name, "%s", name); 141aa4b3574SDag-Erling Smørgrav gettimeofday(&xs->start, NULL); 142aa4b3574SDag-Erling Smørgrav xs->last.tv_sec = xs->last.tv_usec = 0; 143aa4b3574SDag-Erling Smørgrav xs->end = xs->last; 144b39628e7SDag-Erling Smørgrav xs->size = size; 145b39628e7SDag-Erling Smørgrav xs->offset = offset; 146b3c141fdSDag-Erling Smørgrav xs->rcvd = offset; 14749e62d8fSDag-Erling Smørgrav stat_display(xs, 1); 148b39628e7SDag-Erling Smørgrav } 149b39628e7SDag-Erling Smørgrav 15091404f38SDag-Erling Smørgrav static void 15191404f38SDag-Erling Smørgrav stat_update(struct xferstat *xs, off_t rcvd) 152b39628e7SDag-Erling Smørgrav { 153b39628e7SDag-Erling Smørgrav xs->rcvd = rcvd; 15449e62d8fSDag-Erling Smørgrav stat_display(xs, 0); 155b39628e7SDag-Erling Smørgrav } 156b39628e7SDag-Erling Smørgrav 15791404f38SDag-Erling Smørgrav static void 158b39628e7SDag-Erling Smørgrav stat_end(struct xferstat *xs) 159b39628e7SDag-Erling Smørgrav { 160b39628e7SDag-Erling Smørgrav double delta; 161b39628e7SDag-Erling Smørgrav double bps; 16232190ef5SDag-Erling Smørgrav 16332190ef5SDag-Erling Smørgrav if (!v_level) 16432190ef5SDag-Erling Smørgrav return; 165b39628e7SDag-Erling Smørgrav 166b39628e7SDag-Erling Smørgrav gettimeofday(&xs->end, NULL); 167b39628e7SDag-Erling Smørgrav 16849e62d8fSDag-Erling Smørgrav stat_display(xs, 1); 169b39628e7SDag-Erling Smørgrav fputc('\n', stderr); 170b39628e7SDag-Erling Smørgrav delta = (xs->end.tv_sec + (xs->end.tv_usec / 1.e6)) 171b39628e7SDag-Erling Smørgrav - (xs->start.tv_sec + (xs->start.tv_usec / 1.e6)); 172b39628e7SDag-Erling Smørgrav fprintf(stderr, "%lld bytes transferred in %.1f seconds ", 17391404f38SDag-Erling Smørgrav (long long)(xs->rcvd - xs->offset), delta); 174aa4b3574SDag-Erling Smørgrav bps = (xs->rcvd - xs->offset) / delta; 175b39628e7SDag-Erling Smørgrav if (bps > 1024*1024) 176b39628e7SDag-Erling Smørgrav fprintf(stderr, "(%.2f MBps)\n", bps / (1024*1024)); 177b39628e7SDag-Erling Smørgrav else if (bps > 1024) 178b39628e7SDag-Erling Smørgrav fprintf(stderr, "(%.2f kBps)\n", bps / 1024); 179b39628e7SDag-Erling Smørgrav else 180b39628e7SDag-Erling Smørgrav fprintf(stderr, "(%.2f Bps)\n", bps); 181b39628e7SDag-Erling Smørgrav } 182b39628e7SDag-Erling Smørgrav 18391404f38SDag-Erling Smørgrav static int 1840d60c709SDag-Erling Smørgrav query_auth(struct url *URL) 1850d60c709SDag-Erling Smørgrav { 1860d60c709SDag-Erling Smørgrav struct termios tios; 1870d60c709SDag-Erling Smørgrav tcflag_t saved_flags; 1880d60c709SDag-Erling Smørgrav int i, nopwd; 1890d60c709SDag-Erling Smørgrav 1900d60c709SDag-Erling Smørgrav 1910d60c709SDag-Erling Smørgrav fprintf(stderr, "Authentication required for <%s://%s:%d/>!\n", 1920d60c709SDag-Erling Smørgrav URL->scheme, URL->host, URL->port, URL->doc); 1930d60c709SDag-Erling Smørgrav 1940d60c709SDag-Erling Smørgrav fprintf(stderr, "Login: "); 1950d60c709SDag-Erling Smørgrav if (fgets(URL->user, sizeof URL->user, stdin) == NULL) 1960d60c709SDag-Erling Smørgrav return -1; 1970d60c709SDag-Erling Smørgrav for (i = 0; URL->user[i]; ++i) 1980d60c709SDag-Erling Smørgrav if (isspace(URL->user[i])) 1990d60c709SDag-Erling Smørgrav URL->user[i] = '\0'; 2000d60c709SDag-Erling Smørgrav 2010d60c709SDag-Erling Smørgrav fprintf(stderr, "Password: "); 2020d60c709SDag-Erling Smørgrav if (tcgetattr(STDIN_FILENO, &tios) == 0) { 2030d60c709SDag-Erling Smørgrav saved_flags = tios.c_lflag; 2040d60c709SDag-Erling Smørgrav tios.c_lflag &= ~ECHO; 2050d60c709SDag-Erling Smørgrav tios.c_lflag |= ECHONL|ICANON; 2060d60c709SDag-Erling Smørgrav tcsetattr(STDIN_FILENO, TCSAFLUSH|TCSASOFT, &tios); 2070d60c709SDag-Erling Smørgrav nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL); 2080d60c709SDag-Erling Smørgrav tios.c_lflag = saved_flags; 2090d60c709SDag-Erling Smørgrav tcsetattr(STDIN_FILENO, TCSANOW|TCSASOFT, &tios); 2100d60c709SDag-Erling Smørgrav } else { 2110d60c709SDag-Erling Smørgrav nopwd = (fgets(URL->pwd, sizeof URL->pwd, stdin) == NULL); 2120d60c709SDag-Erling Smørgrav } 2130d60c709SDag-Erling Smørgrav if (nopwd) 2140d60c709SDag-Erling Smørgrav return -1; 2150d60c709SDag-Erling Smørgrav 2160d60c709SDag-Erling Smørgrav for (i = 0; URL->pwd[i]; ++i) 2170d60c709SDag-Erling Smørgrav if (isspace(URL->pwd[i])) 2180d60c709SDag-Erling Smørgrav URL->pwd[i] = '\0'; 2190d60c709SDag-Erling Smørgrav return 0; 2200d60c709SDag-Erling Smørgrav } 2210d60c709SDag-Erling Smørgrav 22291404f38SDag-Erling Smørgrav static int 22391404f38SDag-Erling Smørgrav fetch(char *URL, const char *path) 224b39628e7SDag-Erling Smørgrav { 225b39628e7SDag-Erling Smørgrav struct url *url; 226b39628e7SDag-Erling Smørgrav struct url_stat us; 227b39628e7SDag-Erling Smørgrav struct stat sb; 228b39628e7SDag-Erling Smørgrav struct xferstat xs; 229b39628e7SDag-Erling Smørgrav FILE *f, *of; 230cd400b67SDag-Erling Smørgrav size_t size, wr; 231b39628e7SDag-Erling Smørgrav off_t count; 232b39628e7SDag-Erling Smørgrav char flags[8]; 2330d60c709SDag-Erling Smørgrav int r; 234b39628e7SDag-Erling Smørgrav u_int timeout; 235cd400b67SDag-Erling Smørgrav u_char *ptr; 236b39628e7SDag-Erling Smørgrav 237b39628e7SDag-Erling Smørgrav f = of = NULL; 238b39628e7SDag-Erling Smørgrav 239b39628e7SDag-Erling Smørgrav /* parse URL */ 240b39628e7SDag-Erling Smørgrav if ((url = fetchParseURL(URL)) == NULL) { 241b39628e7SDag-Erling Smørgrav warnx("%s: parse error", URL); 242b39628e7SDag-Erling Smørgrav goto failure; 243b39628e7SDag-Erling Smørgrav } 244b39628e7SDag-Erling Smørgrav 24564638f67SDag-Erling Smørgrav /* if no scheme was specified, take a guess */ 24664638f67SDag-Erling Smørgrav if (!*url->scheme) { 24764638f67SDag-Erling Smørgrav if (!*url->host) 24864638f67SDag-Erling Smørgrav strcpy(url->scheme, SCHEME_FILE); 24974bd3d76SDag-Erling Smørgrav else if (strncasecmp(url->host, "ftp.", 4) == 0) 25064638f67SDag-Erling Smørgrav strcpy(url->scheme, SCHEME_FTP); 25174bd3d76SDag-Erling Smørgrav else if (strncasecmp(url->host, "www.", 4) == 0) 25264638f67SDag-Erling Smørgrav strcpy(url->scheme, SCHEME_HTTP); 25364638f67SDag-Erling Smørgrav } 25464638f67SDag-Erling Smørgrav 255b39628e7SDag-Erling Smørgrav timeout = 0; 256b39628e7SDag-Erling Smørgrav *flags = 0; 257e9a039c2SDag-Erling Smørgrav count = 0; 258b39628e7SDag-Erling Smørgrav 259b39628e7SDag-Erling Smørgrav /* common flags */ 26045b22b2aSDag-Erling Smørgrav if (v_level > 1) 261b39628e7SDag-Erling Smørgrav strcat(flags, "v"); 262b39628e7SDag-Erling Smørgrav switch (family) { 263b39628e7SDag-Erling Smørgrav case PF_INET: 264b39628e7SDag-Erling Smørgrav strcat(flags, "4"); 265b39628e7SDag-Erling Smørgrav break; 266b39628e7SDag-Erling Smørgrav case PF_INET6: 267b39628e7SDag-Erling Smørgrav strcat(flags, "6"); 268b39628e7SDag-Erling Smørgrav break; 269b39628e7SDag-Erling Smørgrav } 270b39628e7SDag-Erling Smørgrav 271b39628e7SDag-Erling Smørgrav /* FTP specific flags */ 272b39628e7SDag-Erling Smørgrav if (strcmp(url->scheme, "ftp") == 0) { 273b39628e7SDag-Erling Smørgrav if (p_flag) 274b39628e7SDag-Erling Smørgrav strcat(flags, "p"); 275b39628e7SDag-Erling Smørgrav if (d_flag) 276b39628e7SDag-Erling Smørgrav strcat(flags, "d"); 2777c480c6cSDag-Erling Smørgrav if (U_flag) 2787c480c6cSDag-Erling Smørgrav strcat(flags, "l"); 279b39628e7SDag-Erling Smørgrav timeout = T_secs ? T_secs : ftp_timeout; 280b39628e7SDag-Erling Smørgrav } 281b39628e7SDag-Erling Smørgrav 282b39628e7SDag-Erling Smørgrav /* HTTP specific flags */ 283b39628e7SDag-Erling Smørgrav if (strcmp(url->scheme, "http") == 0) { 284b39628e7SDag-Erling Smørgrav if (d_flag) 285b39628e7SDag-Erling Smørgrav strcat(flags, "d"); 286b39628e7SDag-Erling Smørgrav if (A_flag) 287b39628e7SDag-Erling Smørgrav strcat(flags, "A"); 288b39628e7SDag-Erling Smørgrav timeout = T_secs ? T_secs : http_timeout; 289b39628e7SDag-Erling Smørgrav } 290b39628e7SDag-Erling Smørgrav 291ef50a72cSDag-Erling Smørgrav /* set the protocol timeout. */ 292b39628e7SDag-Erling Smørgrav fetchTimeout = timeout; 293b39628e7SDag-Erling Smørgrav 294b39628e7SDag-Erling Smørgrav /* just print size */ 295b39628e7SDag-Erling Smørgrav if (s_flag) { 296e9a039c2SDag-Erling Smørgrav if (fetchStat(url, &us, flags) == -1) 297e9a039c2SDag-Erling Smørgrav goto failure; 298b39628e7SDag-Erling Smørgrav if (us.size == -1) 299b39628e7SDag-Erling Smørgrav printf("Unknown\n"); 300b39628e7SDag-Erling Smørgrav else 30191404f38SDag-Erling Smørgrav printf("%lld\n", (long long)us.size); 302b39628e7SDag-Erling Smørgrav goto success; 303b39628e7SDag-Erling Smørgrav } 304b39628e7SDag-Erling Smørgrav 305e9a039c2SDag-Erling Smørgrav /* 30691404f38SDag-Erling Smørgrav * If the -r flag was specified, we have to compare the local 30791404f38SDag-Erling Smørgrav * and remote files, so we should really do a fetchStat() 30891404f38SDag-Erling Smørgrav * first, but I know of at least one HTTP server that only 30991404f38SDag-Erling Smørgrav * sends the content size in response to GET requests, and 31091404f38SDag-Erling Smørgrav * leaves it out of replies to HEAD requests. Also, in the 31191404f38SDag-Erling Smørgrav * (frequent) case that the local and remote files match but 31291404f38SDag-Erling Smørgrav * the local file is truncated, we have sufficient information 31391404f38SDag-Erling Smørgrav * before the compare to issue a correct request. Therefore, 31491404f38SDag-Erling Smørgrav * we always issue a GET request as if we were sure the local 31591404f38SDag-Erling Smørgrav * file was a truncated copy of the remote file; we can drop 31691404f38SDag-Erling Smørgrav * the connection later if we change our minds. 317e9a039c2SDag-Erling Smørgrav */ 318a0c6ec97SDag-Erling Smørgrav if ((r_flag || m_flag) && !o_stdout && stat(path, &sb) != -1) { 319a0c6ec97SDag-Erling Smørgrav if (r_flag) 320e9a039c2SDag-Erling Smørgrav url->offset = sb.st_size; 321a0c6ec97SDag-Erling Smørgrav } else { 322a0c6ec97SDag-Erling Smørgrav sb.st_size = -1; 323a0c6ec97SDag-Erling Smørgrav } 324e9a039c2SDag-Erling Smørgrav 325e9a039c2SDag-Erling Smørgrav /* start the transfer */ 326e9a039c2SDag-Erling Smørgrav if ((f = fetchXGet(url, &us, flags)) == NULL) { 327e9a039c2SDag-Erling Smørgrav warnx("%s: %s", path, fetchLastErrString); 328e9a039c2SDag-Erling Smørgrav goto failure; 329e9a039c2SDag-Erling Smørgrav } 330e9a039c2SDag-Erling Smørgrav if (sigint) 331e9a039c2SDag-Erling Smørgrav goto signal; 332e9a039c2SDag-Erling Smørgrav 333b39628e7SDag-Erling Smørgrav /* check that size is as expected */ 334e9a039c2SDag-Erling Smørgrav if (S_size) { 335e9a039c2SDag-Erling Smørgrav if (us.size == -1) { 336e9a039c2SDag-Erling Smørgrav warnx("%s: size unknown", path); 337e9a039c2SDag-Erling Smørgrav goto failure; 338e9a039c2SDag-Erling Smørgrav } else if (us.size != S_size) { 339b39628e7SDag-Erling Smørgrav warnx("%s: size mismatch: expected %lld, actual %lld", 34091404f38SDag-Erling Smørgrav path, (long long)S_size, (long long)us.size); 341b39628e7SDag-Erling Smørgrav goto failure; 342b39628e7SDag-Erling Smørgrav } 343e9a039c2SDag-Erling Smørgrav } 344b39628e7SDag-Erling Smørgrav 345b39628e7SDag-Erling Smørgrav /* symlink instead of copy */ 346b39628e7SDag-Erling Smørgrav if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) { 347b39628e7SDag-Erling Smørgrav if (symlink(url->doc, path) == -1) { 348b39628e7SDag-Erling Smørgrav warn("%s: symlink()", path); 349b39628e7SDag-Erling Smørgrav goto failure; 350b39628e7SDag-Erling Smørgrav } 351b39628e7SDag-Erling Smørgrav goto success; 352b39628e7SDag-Erling Smørgrav } 353b39628e7SDag-Erling Smørgrav 35404d12c99SDag-Erling Smørgrav if (us.size == -1 && !o_stdout) 35589a70fbeSDag-Erling Smørgrav warnx("%s: size of remote file is not known", path); 356d939bf77SDag-Erling Smørgrav if (v_level > 1) { 357a0c6ec97SDag-Erling Smørgrav if (sb.st_size != -1) 358a0c6ec97SDag-Erling Smørgrav fprintf(stderr, "local size / mtime: %lld / %ld\n", 35991404f38SDag-Erling Smørgrav (long long)sb.st_size, (long)sb.st_mtime); 36089a70fbeSDag-Erling Smørgrav if (us.size != -1) 361a0c6ec97SDag-Erling Smørgrav fprintf(stderr, "remote size / mtime: %lld / %ld\n", 36291404f38SDag-Erling Smørgrav (long long)us.size, (long)us.mtime); 363d939bf77SDag-Erling Smørgrav } 364d939bf77SDag-Erling Smørgrav 365e9a039c2SDag-Erling Smørgrav /* open output file */ 366b39628e7SDag-Erling Smørgrav if (o_stdout) { 367b39628e7SDag-Erling Smørgrav /* output to stdout */ 368b39628e7SDag-Erling Smørgrav of = stdout; 369a0c6ec97SDag-Erling Smørgrav } else if (sb.st_size != -1) { 370e9a039c2SDag-Erling Smørgrav /* resume mode, local file exists */ 371e9a039c2SDag-Erling Smørgrav if (!F_flag && us.mtime && sb.st_mtime != us.mtime) { 372e9a039c2SDag-Erling Smørgrav /* no match! have to refetch */ 373e9a039c2SDag-Erling Smørgrav fclose(f); 37489a70fbeSDag-Erling Smørgrav /* if precious, warn the user and give up */ 37589a70fbeSDag-Erling Smørgrav if (R_flag) { 37691404f38SDag-Erling Smørgrav warnx("%s: local modification time " 37791404f38SDag-Erling Smørgrav "does not match remote", path); 37889a70fbeSDag-Erling Smørgrav goto failure_keep; 37989a70fbeSDag-Erling Smørgrav } 380e9a039c2SDag-Erling Smørgrav url->offset = 0; 381e9a039c2SDag-Erling Smørgrav if ((f = fetchXGet(url, &us, flags)) == NULL) { 382e9a039c2SDag-Erling Smørgrav warnx("%s: %s", path, fetchLastErrString); 383b39628e7SDag-Erling Smørgrav goto failure; 384b39628e7SDag-Erling Smørgrav } 385e9a039c2SDag-Erling Smørgrav if (sigint) 386e9a039c2SDag-Erling Smørgrav goto signal; 387e9a039c2SDag-Erling Smørgrav } else { 388e9a039c2SDag-Erling Smørgrav if (us.size == sb.st_size) 389e9a039c2SDag-Erling Smørgrav /* nothing to do */ 390e9a039c2SDag-Erling Smørgrav goto success; 391e9a039c2SDag-Erling Smørgrav if (sb.st_size > us.size) { 392e9a039c2SDag-Erling Smørgrav /* local file too long! */ 393e9a039c2SDag-Erling Smørgrav warnx("%s: local file (%lld bytes) is longer " 39491404f38SDag-Erling Smørgrav "than remote file (%lld bytes)", path, 39591404f38SDag-Erling Smørgrav (long long)sb.st_size, (long long)us.size); 396e9a039c2SDag-Erling Smørgrav goto failure; 397e9a039c2SDag-Erling Smørgrav } 39891404f38SDag-Erling Smørgrav /* we got it, open local file and seek to offset */ 399e9a039c2SDag-Erling Smørgrav /* 40091404f38SDag-Erling Smørgrav * XXX there's a race condition here - the 40191404f38SDag-Erling Smørgrav * file we open is not necessarily the same as 40291404f38SDag-Erling Smørgrav * the one we stat()'ed earlier... 403e9a039c2SDag-Erling Smørgrav */ 404b39628e7SDag-Erling Smørgrav if ((of = fopen(path, "a")) == NULL) { 40510e3b1c7SDag-Erling Smørgrav warn("%s: fopen()", path); 406b39628e7SDag-Erling Smørgrav goto failure; 407b39628e7SDag-Erling Smørgrav } 40810e3b1c7SDag-Erling Smørgrav if (fseek(of, url->offset, SEEK_SET) == -1) { 40910e3b1c7SDag-Erling Smørgrav warn("%s: fseek()", path); 41010e3b1c7SDag-Erling Smørgrav goto failure; 41110e3b1c7SDag-Erling Smørgrav } 41210e3b1c7SDag-Erling Smørgrav } 413b39628e7SDag-Erling Smørgrav } 414a0c6ec97SDag-Erling Smørgrav if (m_flag && sb.st_size != -1) { 415e9a039c2SDag-Erling Smørgrav /* mirror mode, local file exists */ 416e9a039c2SDag-Erling Smørgrav if (sb.st_size == us.size && sb.st_mtime == us.mtime) 417e9a039c2SDag-Erling Smørgrav goto success; 418e9a039c2SDag-Erling Smørgrav } 419e9a039c2SDag-Erling Smørgrav if (!of) { 420e9a039c2SDag-Erling Smørgrav /* 42191404f38SDag-Erling Smørgrav * We don't yet have an output file; either this is a 42291404f38SDag-Erling Smørgrav * vanilla run with no special flags, or the local and 42391404f38SDag-Erling Smørgrav * remote files didn't match. 424e9a039c2SDag-Erling Smørgrav */ 425b39628e7SDag-Erling Smørgrav if ((of = fopen(path, "w")) == NULL) { 426b39628e7SDag-Erling Smørgrav warn("%s: open()", path); 427b39628e7SDag-Erling Smørgrav goto failure; 428b39628e7SDag-Erling Smørgrav } 429b39628e7SDag-Erling Smørgrav } 430b39628e7SDag-Erling Smørgrav count = url->offset; 431b39628e7SDag-Erling Smørgrav 432b39628e7SDag-Erling Smørgrav /* start the counter */ 433b39628e7SDag-Erling Smørgrav stat_start(&xs, path, us.size, count); 434b39628e7SDag-Erling Smørgrav 435cd400b67SDag-Erling Smørgrav sigalrm = siginfo = sigint = 0; 43649e62d8fSDag-Erling Smørgrav 43749e62d8fSDag-Erling Smørgrav /* suck in the data */ 438cd400b67SDag-Erling Smørgrav signal(SIGINFO, sig_handler); 4390d60c709SDag-Erling Smørgrav while (!sigint && !sigalrm) { 44049e62d8fSDag-Erling Smørgrav if (us.size != -1 && us.size - count < B_size) 441b39628e7SDag-Erling Smørgrav size = us.size - count; 44249e62d8fSDag-Erling Smørgrav else 44349e62d8fSDag-Erling Smørgrav size = B_size; 444b39628e7SDag-Erling Smørgrav if (timeout) 445b39628e7SDag-Erling Smørgrav alarm(timeout); 4460d60c709SDag-Erling Smørgrav if (siginfo) { 4470d60c709SDag-Erling Smørgrav stat_end(&xs); 4480d60c709SDag-Erling Smørgrav siginfo = 0; 4490d60c709SDag-Erling Smørgrav } 450cd400b67SDag-Erling Smørgrav if ((size = fread(buf, 1, size, f)) == 0) { 451cd400b67SDag-Erling Smørgrav if (ferror(f) && errno == EINTR && !sigalrm && !sigint) 452cd400b67SDag-Erling Smørgrav clearerr(f); 453cd400b67SDag-Erling Smørgrav else 454aa4b3574SDag-Erling Smørgrav break; 455b39628e7SDag-Erling Smørgrav } 456cd400b67SDag-Erling Smørgrav if (timeout) 457cd400b67SDag-Erling Smørgrav alarm(0); 45891404f38SDag-Erling Smørgrav stat_update(&xs, count += size); 459cd400b67SDag-Erling Smørgrav for (ptr = buf; size > 0; ptr += wr, size -= wr) 460cd400b67SDag-Erling Smørgrav if ((wr = fwrite(ptr, 1, size, of)) < size) { 46191404f38SDag-Erling Smørgrav if (ferror(of) && errno == EINTR && 46291404f38SDag-Erling Smørgrav !sigalrm && !sigint) 463cd400b67SDag-Erling Smørgrav clearerr(of); 464cd400b67SDag-Erling Smørgrav else 465cd400b67SDag-Erling Smørgrav break; 466cd400b67SDag-Erling Smørgrav } 467cd400b67SDag-Erling Smørgrav if (size != 0) 468cd400b67SDag-Erling Smørgrav break; 469cd400b67SDag-Erling Smørgrav } 470cd400b67SDag-Erling Smørgrav signal(SIGINFO, SIG_DFL); 471b39628e7SDag-Erling Smørgrav 472b39628e7SDag-Erling Smørgrav if (timeout) 473b39628e7SDag-Erling Smørgrav alarm(0); 474b39628e7SDag-Erling Smørgrav 475b39628e7SDag-Erling Smørgrav stat_end(&xs); 476b39628e7SDag-Erling Smørgrav 47730204f98SDag-Erling Smørgrav /* set mtime of local file */ 47874912943SDag-Erling Smørgrav if (!n_flag && us.mtime && !o_stdout 47974912943SDag-Erling Smørgrav && (stat(path, &sb) != -1) && sb.st_mode & S_IFREG) { 480b39628e7SDag-Erling Smørgrav struct timeval tv[2]; 481b39628e7SDag-Erling Smørgrav 48249e62d8fSDag-Erling Smørgrav fflush(of); 48349e62d8fSDag-Erling Smørgrav tv[0].tv_sec = (long)(us.atime ? us.atime : us.mtime); 484b39628e7SDag-Erling Smørgrav tv[1].tv_sec = (long)us.mtime; 485b39628e7SDag-Erling Smørgrav tv[0].tv_usec = tv[1].tv_usec = 0; 486b39628e7SDag-Erling Smørgrav if (utimes(path, tv)) 487b39628e7SDag-Erling Smørgrav warn("%s: utimes()", path); 488b39628e7SDag-Erling Smørgrav } 489b39628e7SDag-Erling Smørgrav 49049e62d8fSDag-Erling Smørgrav /* timed out or interrupted? */ 491e9a039c2SDag-Erling Smørgrav signal: 492ef50a72cSDag-Erling Smørgrav if (sigalrm) 493ef50a72cSDag-Erling Smørgrav warnx("transfer timed out"); 4949516ffa7SDag-Erling Smørgrav if (sigint) { 495ef50a72cSDag-Erling Smørgrav warnx("transfer interrupted"); 4969516ffa7SDag-Erling Smørgrav goto failure; 4979516ffa7SDag-Erling Smørgrav } 49849e62d8fSDag-Erling Smørgrav 4999516ffa7SDag-Erling Smørgrav if (!sigalrm) { 50049e62d8fSDag-Erling Smørgrav /* check the status of our files */ 50149e62d8fSDag-Erling Smørgrav if (ferror(f)) 50249e62d8fSDag-Erling Smørgrav warn("%s", URL); 50349e62d8fSDag-Erling Smørgrav if (ferror(of)) 50449e62d8fSDag-Erling Smørgrav warn("%s", path); 50549e62d8fSDag-Erling Smørgrav if (ferror(f) || ferror(of)) 50649e62d8fSDag-Erling Smørgrav goto failure; 50778394463SDag-Erling Smørgrav } 50849e62d8fSDag-Erling Smørgrav 50949e62d8fSDag-Erling Smørgrav /* did the transfer complete normally? */ 51049e62d8fSDag-Erling Smørgrav if (us.size != -1 && count < us.size) { 511bb11a878SDag-Erling Smørgrav warnx("%s appears to be truncated: %lld/%lld bytes", 51291404f38SDag-Erling Smørgrav path, (long long)count, (long long)us.size); 51349e62d8fSDag-Erling Smørgrav goto failure_keep; 514bb11a878SDag-Erling Smørgrav } 515bb11a878SDag-Erling Smørgrav 51630204f98SDag-Erling Smørgrav /* 51730204f98SDag-Erling Smørgrav * If the transfer timed out and we didn't know how much to 51830204f98SDag-Erling Smørgrav * expect, assume the worst (i.e. we didn't get all of it) 51930204f98SDag-Erling Smørgrav */ 52030204f98SDag-Erling Smørgrav if (sigalrm && us.size == -1) { 52130204f98SDag-Erling Smørgrav warnx("%s may be truncated", path); 52230204f98SDag-Erling Smørgrav goto failure_keep; 52330204f98SDag-Erling Smørgrav } 52430204f98SDag-Erling Smørgrav 525b39628e7SDag-Erling Smørgrav success: 52649e62d8fSDag-Erling Smørgrav r = 0; 527b39628e7SDag-Erling Smørgrav goto done; 528b39628e7SDag-Erling Smørgrav failure: 52949e62d8fSDag-Erling Smørgrav if (of && of != stdout && !R_flag && !r_flag) 530e2b41a62SDag-Erling Smørgrav if (stat(path, &sb) != -1 && (sb.st_mode & S_IFREG)) 53149e62d8fSDag-Erling Smørgrav unlink(path); 53249e62d8fSDag-Erling Smørgrav failure_keep: 533b39628e7SDag-Erling Smørgrav r = -1; 534b39628e7SDag-Erling Smørgrav goto done; 535b39628e7SDag-Erling Smørgrav done: 536b39628e7SDag-Erling Smørgrav if (f) 537b39628e7SDag-Erling Smørgrav fclose(f); 538b39628e7SDag-Erling Smørgrav if (of && of != stdout) 539b39628e7SDag-Erling Smørgrav fclose(of); 540ec850e74SDag-Erling Smørgrav if (url) 541b39628e7SDag-Erling Smørgrav fetchFreeURL(url); 542b39628e7SDag-Erling Smørgrav return r; 543b39628e7SDag-Erling Smørgrav } 544b39628e7SDag-Erling Smørgrav 54591404f38SDag-Erling Smørgrav static void 546b39628e7SDag-Erling Smørgrav usage(void) 547b39628e7SDag-Erling Smørgrav { 54891404f38SDag-Erling Smørgrav fprintf(stderr, "%s\n%s\n%s\n", 54991404f38SDag-Erling Smørgrav "Usage: fetch [-146AFMPRUadlmnpqrsv] [-o outputfile] [-S bytes]", 55091404f38SDag-Erling Smørgrav " [-B bytes] [-T seconds] [-w seconds]", 55191404f38SDag-Erling Smørgrav " [-h host -f file [-c dir] | URL ...]"); 552b39628e7SDag-Erling Smørgrav } 553b39628e7SDag-Erling Smørgrav 554b39628e7SDag-Erling Smørgrav 555b39628e7SDag-Erling Smørgrav #define PARSENUM(NAME, TYPE) \ 55691404f38SDag-Erling Smørgrav static int \ 55791404f38SDag-Erling Smørgrav NAME(const char *s, TYPE *v) \ 558b39628e7SDag-Erling Smørgrav { \ 559b39628e7SDag-Erling Smørgrav *v = 0; \ 560b39628e7SDag-Erling Smørgrav for (*v = 0; *s; s++) \ 561b39628e7SDag-Erling Smørgrav if (isdigit(*s)) \ 562b39628e7SDag-Erling Smørgrav *v = *v * 10 + *s - '0'; \ 563b39628e7SDag-Erling Smørgrav else \ 564b39628e7SDag-Erling Smørgrav return -1; \ 565b39628e7SDag-Erling Smørgrav return 0; \ 566b39628e7SDag-Erling Smørgrav } 567b39628e7SDag-Erling Smørgrav 568b39628e7SDag-Erling Smørgrav PARSENUM(parseint, u_int) 569b39628e7SDag-Erling Smørgrav PARSENUM(parseoff, off_t) 570b39628e7SDag-Erling Smørgrav 571b39628e7SDag-Erling Smørgrav int 572b39628e7SDag-Erling Smørgrav main(int argc, char *argv[]) 573b39628e7SDag-Erling Smørgrav { 574b39628e7SDag-Erling Smørgrav struct stat sb; 57578394463SDag-Erling Smørgrav struct sigaction sa; 57691404f38SDag-Erling Smørgrav const char *p, *s; 57791404f38SDag-Erling Smørgrav char *q; 578b39628e7SDag-Erling Smørgrav int c, e, r; 579b39628e7SDag-Erling Smørgrav 580b39628e7SDag-Erling Smørgrav while ((c = getopt(argc, argv, 5817c480c6cSDag-Erling Smørgrav "146AaB:bc:dFf:Hh:lMmnPpo:qRrS:sT:tUvw:")) != EOF) 582b39628e7SDag-Erling Smørgrav switch (c) { 583b39628e7SDag-Erling Smørgrav case '1': 584b39628e7SDag-Erling Smørgrav once_flag = 1; 585b39628e7SDag-Erling Smørgrav break; 586b39628e7SDag-Erling Smørgrav case '4': 587b39628e7SDag-Erling Smørgrav family = PF_INET; 588b39628e7SDag-Erling Smørgrav break; 589b39628e7SDag-Erling Smørgrav case '6': 590b39628e7SDag-Erling Smørgrav family = PF_INET6; 591b39628e7SDag-Erling Smørgrav break; 592b39628e7SDag-Erling Smørgrav case 'A': 593b39628e7SDag-Erling Smørgrav A_flag = 1; 594b39628e7SDag-Erling Smørgrav break; 595b39628e7SDag-Erling Smørgrav case 'a': 596b39628e7SDag-Erling Smørgrav a_flag = 1; 597b39628e7SDag-Erling Smørgrav break; 598b39628e7SDag-Erling Smørgrav case 'B': 59991404f38SDag-Erling Smørgrav if (parseoff(optarg, &B_size) == -1) 600b39628e7SDag-Erling Smørgrav errx(1, "invalid buffer size"); 601b39628e7SDag-Erling Smørgrav break; 602b39628e7SDag-Erling Smørgrav case 'b': 603b39628e7SDag-Erling Smørgrav warnx("warning: the -b option is deprecated"); 604b39628e7SDag-Erling Smørgrav b_flag = 1; 605b39628e7SDag-Erling Smørgrav break; 606a8369cd9SDag-Erling Smørgrav case 'c': 607a8369cd9SDag-Erling Smørgrav c_dirname = optarg; 608a8369cd9SDag-Erling Smørgrav break; 609b39628e7SDag-Erling Smørgrav case 'd': 610b39628e7SDag-Erling Smørgrav d_flag = 1; 611b39628e7SDag-Erling Smørgrav break; 612b39628e7SDag-Erling Smørgrav case 'F': 613b39628e7SDag-Erling Smørgrav F_flag = 1; 614b39628e7SDag-Erling Smørgrav break; 615b39628e7SDag-Erling Smørgrav case 'f': 616b39628e7SDag-Erling Smørgrav f_filename = optarg; 617b39628e7SDag-Erling Smørgrav break; 618b39628e7SDag-Erling Smørgrav case 'H': 61991404f38SDag-Erling Smørgrav warnx("The -H option is now implicit, " 62091404f38SDag-Erling Smørgrav "use -U to disable"); 621b39628e7SDag-Erling Smørgrav break; 622b39628e7SDag-Erling Smørgrav case 'h': 623b39628e7SDag-Erling Smørgrav h_hostname = optarg; 624b39628e7SDag-Erling Smørgrav break; 625b39628e7SDag-Erling Smørgrav case 'l': 626b39628e7SDag-Erling Smørgrav l_flag = 1; 627b39628e7SDag-Erling Smørgrav break; 628b39628e7SDag-Erling Smørgrav case 'o': 629b39628e7SDag-Erling Smørgrav o_flag = 1; 630b39628e7SDag-Erling Smørgrav o_filename = optarg; 631b39628e7SDag-Erling Smørgrav break; 632b39628e7SDag-Erling Smørgrav case 'M': 633b39628e7SDag-Erling Smørgrav case 'm': 634e9a039c2SDag-Erling Smørgrav if (r_flag) 63591404f38SDag-Erling Smørgrav errx(1, "the -m and -r flags " 63691404f38SDag-Erling Smørgrav "are mutually exclusive"); 637b39628e7SDag-Erling Smørgrav m_flag = 1; 638b39628e7SDag-Erling Smørgrav break; 639b39628e7SDag-Erling Smørgrav case 'n': 640bb11a878SDag-Erling Smørgrav n_flag = 1; 641b39628e7SDag-Erling Smørgrav break; 642b39628e7SDag-Erling Smørgrav case 'P': 643b39628e7SDag-Erling Smørgrav case 'p': 644b39628e7SDag-Erling Smørgrav p_flag = 1; 645b39628e7SDag-Erling Smørgrav break; 646b39628e7SDag-Erling Smørgrav case 'q': 647b39628e7SDag-Erling Smørgrav v_level = 0; 648b39628e7SDag-Erling Smørgrav break; 649b39628e7SDag-Erling Smørgrav case 'R': 650b39628e7SDag-Erling Smørgrav R_flag = 1; 651b39628e7SDag-Erling Smørgrav break; 652b39628e7SDag-Erling Smørgrav case 'r': 653e9a039c2SDag-Erling Smørgrav if (m_flag) 65491404f38SDag-Erling Smørgrav errx(1, "the -m and -r flags " 65591404f38SDag-Erling Smørgrav "are mutually exclusive"); 656b39628e7SDag-Erling Smørgrav r_flag = 1; 657b39628e7SDag-Erling Smørgrav break; 658b39628e7SDag-Erling Smørgrav case 'S': 659b39628e7SDag-Erling Smørgrav if (parseoff(optarg, &S_size) == -1) 660b39628e7SDag-Erling Smørgrav errx(1, "invalid size"); 661b39628e7SDag-Erling Smørgrav break; 662b39628e7SDag-Erling Smørgrav case 's': 663b39628e7SDag-Erling Smørgrav s_flag = 1; 664b39628e7SDag-Erling Smørgrav break; 665b39628e7SDag-Erling Smørgrav case 'T': 666b39628e7SDag-Erling Smørgrav if (parseint(optarg, &T_secs) == -1) 667b39628e7SDag-Erling Smørgrav errx(1, "invalid timeout"); 668b39628e7SDag-Erling Smørgrav break; 669b39628e7SDag-Erling Smørgrav case 't': 670b39628e7SDag-Erling Smørgrav t_flag = 1; 671b39628e7SDag-Erling Smørgrav warnx("warning: the -t option is deprecated"); 672b39628e7SDag-Erling Smørgrav break; 6737c480c6cSDag-Erling Smørgrav case 'U': 6747c480c6cSDag-Erling Smørgrav U_flag = 1; 6757c480c6cSDag-Erling Smørgrav break; 676b39628e7SDag-Erling Smørgrav case 'v': 677b39628e7SDag-Erling Smørgrav v_level++; 678b39628e7SDag-Erling Smørgrav break; 679b39628e7SDag-Erling Smørgrav case 'w': 680b39628e7SDag-Erling Smørgrav a_flag = 1; 681b39628e7SDag-Erling Smørgrav if (parseint(optarg, &w_secs) == -1) 682b39628e7SDag-Erling Smørgrav errx(1, "invalid delay"); 683b39628e7SDag-Erling Smørgrav break; 684b39628e7SDag-Erling Smørgrav default: 685b39628e7SDag-Erling Smørgrav usage(); 686b39628e7SDag-Erling Smørgrav exit(EX_USAGE); 687b39628e7SDag-Erling Smørgrav } 688b39628e7SDag-Erling Smørgrav 689b39628e7SDag-Erling Smørgrav argc -= optind; 690b39628e7SDag-Erling Smørgrav argv += optind; 691b39628e7SDag-Erling Smørgrav 692a8369cd9SDag-Erling Smørgrav if (h_hostname || f_filename || c_dirname) { 693b39628e7SDag-Erling Smørgrav if (!h_hostname || !f_filename || argc) { 694b39628e7SDag-Erling Smørgrav usage(); 695b39628e7SDag-Erling Smørgrav exit(EX_USAGE); 696b39628e7SDag-Erling Smørgrav } 697b39628e7SDag-Erling Smørgrav /* XXX this is a hack. */ 698b39628e7SDag-Erling Smørgrav if (strcspn(h_hostname, "@:/") != strlen(h_hostname)) 699b39628e7SDag-Erling Smørgrav errx(1, "invalid hostname"); 700a8369cd9SDag-Erling Smørgrav if (asprintf(argv, "ftp://%s/%s/%s", h_hostname, 701a8369cd9SDag-Erling Smørgrav c_dirname ? c_dirname : "", f_filename) == -1) 702a6f339d2SKris Kennaway errx(1, "%s", strerror(ENOMEM)); 703b39628e7SDag-Erling Smørgrav argc++; 704b39628e7SDag-Erling Smørgrav } 705b39628e7SDag-Erling Smørgrav 706b39628e7SDag-Erling Smørgrav if (!argc) { 707b39628e7SDag-Erling Smørgrav usage(); 708b39628e7SDag-Erling Smørgrav exit(EX_USAGE); 709b39628e7SDag-Erling Smørgrav } 710b39628e7SDag-Erling Smørgrav 711b39628e7SDag-Erling Smørgrav /* allocate buffer */ 712b39628e7SDag-Erling Smørgrav if (B_size < MINBUFSIZE) 713b39628e7SDag-Erling Smørgrav B_size = MINBUFSIZE; 714b39628e7SDag-Erling Smørgrav if ((buf = malloc(B_size)) == NULL) 715a6f339d2SKris Kennaway errx(1, "%s", strerror(ENOMEM)); 716b39628e7SDag-Erling Smørgrav 71778394463SDag-Erling Smørgrav /* timeouts */ 718b39628e7SDag-Erling Smørgrav if ((s = getenv("FTP_TIMEOUT")) != NULL) { 719b39628e7SDag-Erling Smørgrav if (parseint(s, &ftp_timeout) == -1) { 720b39628e7SDag-Erling Smørgrav warnx("FTP_TIMEOUT is not a positive integer"); 721b39628e7SDag-Erling Smørgrav ftp_timeout = 0; 722b39628e7SDag-Erling Smørgrav } 723b39628e7SDag-Erling Smørgrav } 724b39628e7SDag-Erling Smørgrav if ((s = getenv("HTTP_TIMEOUT")) != NULL) { 725b39628e7SDag-Erling Smørgrav if (parseint(s, &http_timeout) == -1) { 726b39628e7SDag-Erling Smørgrav warnx("HTTP_TIMEOUT is not a positive integer"); 727b39628e7SDag-Erling Smørgrav http_timeout = 0; 728b39628e7SDag-Erling Smørgrav } 729b39628e7SDag-Erling Smørgrav } 730b39628e7SDag-Erling Smørgrav 73178394463SDag-Erling Smørgrav /* signal handling */ 73278394463SDag-Erling Smørgrav sa.sa_flags = 0; 73378394463SDag-Erling Smørgrav sa.sa_handler = sig_handler; 73478394463SDag-Erling Smørgrav sigemptyset(&sa.sa_mask); 735e9a039c2SDag-Erling Smørgrav sigaction(SIGALRM, &sa, NULL); 736e9a039c2SDag-Erling Smørgrav sa.sa_flags = SA_RESETHAND; 737e9a039c2SDag-Erling Smørgrav sigaction(SIGINT, &sa, NULL); 738e9a039c2SDag-Erling Smørgrav fetchRestartCalls = 0; 739ef50a72cSDag-Erling Smørgrav 740b39628e7SDag-Erling Smørgrav /* output file */ 741b39628e7SDag-Erling Smørgrav if (o_flag) { 742b39628e7SDag-Erling Smørgrav if (strcmp(o_filename, "-") == 0) { 743b39628e7SDag-Erling Smørgrav o_stdout = 1; 744b39628e7SDag-Erling Smørgrav } else if (stat(o_filename, &sb) == -1) { 745b39628e7SDag-Erling Smørgrav if (errno == ENOENT) { 746b39628e7SDag-Erling Smørgrav if (argc > 1) 74791404f38SDag-Erling Smørgrav errx(EX_USAGE, "%s is not a directory", 74891404f38SDag-Erling Smørgrav o_filename); 749b39628e7SDag-Erling Smørgrav } else { 750b39628e7SDag-Erling Smørgrav err(EX_IOERR, "%s", o_filename); 751b39628e7SDag-Erling Smørgrav } 752b39628e7SDag-Erling Smørgrav } else { 753b39628e7SDag-Erling Smørgrav if (sb.st_mode & S_IFDIR) 754b39628e7SDag-Erling Smørgrav o_directory = 1; 755b39628e7SDag-Erling Smørgrav } 756b39628e7SDag-Erling Smørgrav } 757b39628e7SDag-Erling Smørgrav 758b39628e7SDag-Erling Smørgrav /* check if output is to a tty (for progress report) */ 759bb11a878SDag-Erling Smørgrav v_tty = isatty(STDERR_FILENO); 760b39628e7SDag-Erling Smørgrav r = 0; 761b39628e7SDag-Erling Smørgrav 7620d60c709SDag-Erling Smørgrav /* authentication */ 7635ef824edSDag-Erling Smørgrav if (v_tty) 7640d60c709SDag-Erling Smørgrav fetchAuthMethod = query_auth; 7650d60c709SDag-Erling Smørgrav 766b39628e7SDag-Erling Smørgrav while (argc) { 767b39628e7SDag-Erling Smørgrav if ((p = strrchr(*argv, '/')) == NULL) 768b39628e7SDag-Erling Smørgrav p = *argv; 769b39628e7SDag-Erling Smørgrav else 770b39628e7SDag-Erling Smørgrav p++; 771b39628e7SDag-Erling Smørgrav 772b39628e7SDag-Erling Smørgrav if (!*p) 773b39628e7SDag-Erling Smørgrav p = "fetch.out"; 774b39628e7SDag-Erling Smørgrav 775b39628e7SDag-Erling Smørgrav fetchLastErrCode = 0; 776b39628e7SDag-Erling Smørgrav 777b39628e7SDag-Erling Smørgrav if (o_flag) { 778b39628e7SDag-Erling Smørgrav if (o_stdout) { 779b39628e7SDag-Erling Smørgrav e = fetch(*argv, "-"); 780b39628e7SDag-Erling Smørgrav } else if (o_directory) { 781b39628e7SDag-Erling Smørgrav asprintf(&q, "%s/%s", o_filename, p); 782b39628e7SDag-Erling Smørgrav e = fetch(*argv, q); 783b39628e7SDag-Erling Smørgrav free(q); 784b39628e7SDag-Erling Smørgrav } else { 785b39628e7SDag-Erling Smørgrav e = fetch(*argv, o_filename); 786b39628e7SDag-Erling Smørgrav } 787b39628e7SDag-Erling Smørgrav } else { 788b39628e7SDag-Erling Smørgrav e = fetch(*argv, p); 789b39628e7SDag-Erling Smørgrav } 790b39628e7SDag-Erling Smørgrav 791ef50a72cSDag-Erling Smørgrav if (sigint) 792e9a039c2SDag-Erling Smørgrav kill(getpid(), SIGINT); 793ef50a72cSDag-Erling Smørgrav 794b39628e7SDag-Erling Smørgrav if (e == 0 && once_flag) 795b39628e7SDag-Erling Smørgrav exit(0); 796b39628e7SDag-Erling Smørgrav 797b39628e7SDag-Erling Smørgrav if (e) { 798b39628e7SDag-Erling Smørgrav r = 1; 799b39628e7SDag-Erling Smørgrav if ((fetchLastErrCode 800b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_UNAVAIL 801b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_MOVED 802b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_URL 803b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_RESOLV 804b39628e7SDag-Erling Smørgrav && fetchLastErrCode != FETCH_UNKNOWN)) { 80591404f38SDag-Erling Smørgrav if (w_secs && v_level) 80691404f38SDag-Erling Smørgrav fprintf(stderr, "Waiting %d seconds " 80791404f38SDag-Erling Smørgrav "before retrying\n", w_secs); 80891404f38SDag-Erling Smørgrav if (w_secs) 809b39628e7SDag-Erling Smørgrav sleep(w_secs); 810b39628e7SDag-Erling Smørgrav if (a_flag) 811b39628e7SDag-Erling Smørgrav continue; 812b39628e7SDag-Erling Smørgrav } 813b39628e7SDag-Erling Smørgrav } 814b39628e7SDag-Erling Smørgrav 815b39628e7SDag-Erling Smørgrav argc--, argv++; 816b39628e7SDag-Erling Smørgrav } 817b39628e7SDag-Erling Smørgrav 818b39628e7SDag-Erling Smørgrav exit(r); 819b39628e7SDag-Erling Smørgrav } 820