diff options
author | asami <asami@FreeBSD.org> | 1996-06-19 09:32:11 +0000 |
---|---|---|
committer | asami <asami@FreeBSD.org> | 1996-06-19 09:32:11 +0000 |
commit | b0bc5b5357ec35a3b1407b3cfd20bbc0783d238a (patch) | |
tree | b19f4ad765620059cb161556022c9c83892959a9 | |
download | FreeBSD-src-b0bc5b5357ec35a3b1407b3cfd20bbc0783d238a.zip FreeBSD-src-b0bc5b5357ec35a3b1407b3cfd20bbc0783d238a.tar.gz |
Jean-Marc's url fetch program, with Josh MacDonald's patches and
Jordan's ftpio library.
Submitted by: jmz, jkh, jmacd (three-j!)
-rw-r--r-- | usr.bin/fetch/Makefile | 7 | ||||
-rw-r--r-- | usr.bin/fetch/fetch.1 | 1 | ||||
-rw-r--r-- | usr.bin/fetch/main.c | 586 |
3 files changed, 594 insertions, 0 deletions
diff --git a/usr.bin/fetch/Makefile b/usr.bin/fetch/Makefile new file mode 100644 index 0000000..ae40e02 --- /dev/null +++ b/usr.bin/fetch/Makefile @@ -0,0 +1,7 @@ +PROG = fetch +SRCS = main.c + +DPADD= ${LIBFTPIO} +LDADD= -lftpio + +.include <bsd.prog.mk> diff --git a/usr.bin/fetch/fetch.1 b/usr.bin/fetch/fetch.1 new file mode 100644 index 0000000..fa3d009 --- /dev/null +++ b/usr.bin/fetch/fetch.1 @@ -0,0 +1 @@ +.\" under construction :-) diff --git a/usr.bin/fetch/main.c b/usr.bin/fetch/main.c new file mode 100644 index 0000000..470f617 --- /dev/null +++ b/usr.bin/fetch/main.c @@ -0,0 +1,586 @@ +/* $Id$ */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <pwd.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/time.h> +#include <regex.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <err.h> +#include <stdarg.h> + +#include <ftpio.h> + +#define BUFFER_SIZE 32*1024 +#define HTTP_TIMEOUT 60 /* seconds */ +#define FTP_TIMEOUT 300 /* seconds */ + +char buffer[BUFFER_SIZE]; + +char *progname; + +int verbose = 1; +char *outputfile = 0; +char *change_to_dir = 0; +char *host = 0; +int passive_mode = 0; +char *file_to_get = 0; +int http = 0; +int http_port = 80; +int restart = 0; +time_t modtime; + +FILE *file = 0; + +void usage (), die (), rm (), t_out (), ftpget (), httpget (), + display (int, int), parse (char *), output_file_name(), + f_size (char *, int *, time_t *), ftperr (FILE* ftp, char *, ...), + filter (unsigned char *, int); +int match (char *, char *), http_open (); + +void +usage () +{ + fprintf (stderr, "usage: %s [-D:HINPV:Lqpr] [-o outputfile] <-f file -h host [-c dir]| URL>\n", progname); + exit (1); +} + +void +die () +{ + int e = errno; + rm (); + if (errno) + fprintf (stderr, "%s: %s\n", progname, sys_errlist[e]); + else + fprintf (stderr, "%s: Interrupted by signal\n", progname); + exit (1); +} + +void +rm () +{ + struct timeval tv[2]; + if (file) { + fclose (file); + if (file != stdout) { + if (!restart) + remove (outputfile); + else { + tv[0].tv_usec = tv[1].tv_usec = 0; + tv[0].tv_sec = time(0); + tv[1].tv_sec = modtime; + utimes (outputfile, tv); + } + } + } +} + +int +main (int argc, char **argv) +{ + int c; + char *s = strrchr (argv[0], '/'); + + progname = s ? s+1 : argv[0]; + + while ((c = getopt (argc, argv, "D:HINPV:Lqc:f:h:o:pr")) != EOF) { + switch (c) { + case 'D': case 'H': case 'I': case 'N': case 'L': case 'V': + break; /* ncftp compatibility */ + case 'q': + verbose = 0; + case 'c': + change_to_dir = optarg; + break; + case 'f': + file_to_get = optarg; + break; + case 'h': + host = optarg; + break; + case 'o': + outputfile = optarg; + break; + case 'p': case 'P': + passive_mode = 1; + break; + case 'r': + restart = 1; + break; + default: + case '?': + usage (); + } + } + argc -= optind; + argv += optind; + if (argv[0]) { + if (host || change_to_dir || file_to_get) + usage (); + parse (argv[0]); + } else { + if (!host || !file_to_get) + usage (); + } + output_file_name (); + + signal (SIGHUP, die); + signal (SIGINT, die); + signal (SIGQUIT, die); + + if (http) + httpget (); + else + ftpget (); + exit (0); +} + +void +t_out () +{ + fprintf (stderr, "\n%s: Timeout\n", progname); + rm (); + exit (1); +} + +void +ftpget () +{ + FILE *ftp, *fp; + int status, n; + ssize_t size, size0, seekloc; + char ftp_pw[200]; + time_t t; + struct itimerval timer; + + signal (SIGALRM, t_out); + timer.it_interval.tv_sec = timer.it_value.tv_sec = FTP_TIMEOUT; + timer.it_interval.tv_usec = timer.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &timer, 0); + + sprintf (ftp_pw, "%s@", getpwuid (getuid ())->pw_name); + n = strlen (ftp_pw); + gethostname (ftp_pw+n, 200-n); + ftp = ftpLogin(host, "anonymous", ftp_pw, 0); + if (!ftp) + err(1, "Couldn't open FTP connection to %s.", host); + + ftpBinary (ftp, 1); + 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); + if (size < 0) + ftperr (ftp, "%s: ", file_to_get); + + if (restart) { + modtime = ftpGetModtime (ftp, file_to_get); + if (modtime < -1) + err (1, "Unrecoverable error (ftpGetModtime)"); + } else + modtime = (time_t) -1; + + if (!strcmp (outputfile, "-")) + restart = 0; + if (!restart) + size0 = 0; + else { + f_size (outputfile, &size0, &t); + if (size0 && size0 < size && modtime == t) + seekloc = size0; + else + seekloc = size0 = 0; + } + + fp = ftpGet (ftp, file_to_get, &seekloc); + if (fp == NULL) + if (ftpErrno(ftp)) + ftperr (ftp, NULL); + else + die (); + if (size0 && !seekloc) + size0 = 0; + + if (strcmp (outputfile, "-")) { + file = fopen (outputfile, size0 ? "a" : "w"); + if (!file) + err (1, outputfile); + } else + file = stdout; + + display (size, size0); + while (1) { + 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_interval.tv_sec = timer.it_value.tv_sec = FTP_TIMEOUT; + timer.it_interval.tv_usec = timer.it_value.tv_usec = 0; + setitimer(ITIMER_REAL, &timer, 0); + } + if (status < 0) + die (); + fclose(fp); + fclose(file); + display (size, -1); + exit (0); +} + +void +display (int size, int n) +{ + static int bytes, pr, init = 0; + static struct timeval t0, t_start; + static char *s; + struct timezone tz; + struct timeval t; + float d; + + if (!verbose) + return; + if (init == 0) { + init = 1; + gettimeofday(&t0, &tz); + t_start = t0; + bytes = pr = 0; + s = (char *) malloc (strlen(outputfile) + 50); + if (size > 0) + sprintf (s, "Receiving %s (%d bytes)%s", outputfile, size, + size ? "" : " [appending]"); + else + sprintf (s, "Receiving %s", outputfile); + printf ("\n%s", s); + fflush (stdout); + 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); + 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); + d = bytes/d; + if (d < 1000) + printf (" (%d Bytes/s)\n", (int)d); + else { + d /=1024; + printf (" (%.2f K/s)\n", d); + } + return; + } + bytes += n; + d = t.tv_sec + t.tv_usec/1.e6 - t0.tv_sec - t0.tv_usec/1.e6; + if (d < 5) /* display every 5 sec. */ + return; + t0 = t; + pr++; + 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, "ftp://", 6) == 0) { + /* ftp://host.name/file/name */ + s += 6; + p = strchr (s, '/'); + if (!p) { + fprintf (stderr, "%s: no filename??\n", progname); + usage (); + } + } else if (strncasecmp (s, "http://", 7) == 0) { + /* http://host.name/file/name */ + char *q; + s += 7; + p = strchr (s, '/'); + if (!p) { + fprintf (stderr, "%s: no filename??\n", progname); + 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) { + fprintf (stderr, "%s: no filename??\n", progname); + usage (); + } + } + *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) + p = file_to_get; + else + p++; + outputfile = strdup (p); + } +} + +void +f_size (char *name, int *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) { + switch (ftpErrno(ftp)) { + case 421: fprintf (stderr, "Service not available\n"); break; + case 450: fprintf (stderr, "File not available\n"); break; + case 550: fprintf (stderr, "No such file or directory\n"); break; + } + } + rm (); + exit (1); +} + +void +httpget () +{ + char str[1000]; + struct timeval tout; + fd_set fdset; + int i, s; + + restart = 0; + + s = http_open (); + sprintf (str, "GET /%s\n", file_to_get); + i = strlen (str); + if (i != write (s, str, i)) + err (1, 0); + + FD_ZERO (&fdset); + FD_SET (s, &fdset); + tout.tv_sec = HTTP_TIMEOUT; + tout.tv_usec = 0; + + if (strcmp (outputfile, "-")) { + file = fopen (outputfile, "w"); + if (!file) + err (1, 0); + } else { + file = stdout; + verbose = 0; + } + + while (1) { + i = select (s+1, &fdset, 0, 0, &tout); + switch (i) { + case 0: + fprintf (stderr, "%s: Timeout\n", progname); + rm (); + exit (1); + case 1: + i = read (s, buffer, sizeof (buffer)); + filter (buffer, i); + if (i == 0) + exit (0); + break; + default: + err (1, 0); + } + } +} + +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 250 + 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 (".*200.*success", s) == 0) { + /* maybe not found, or document w/o header */ + if (match (".*404.*not found", s)) { + fprintf (stderr, "%s not found\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 (); + display (size, header_len); + /* then p */ + if (p+len > q) { + i = fwrite (q, 1, p+len-q, file); + if (i != p+len-q) + die (); + display (size, i); + } + } else { + unsigned char *t; + /* document begins with a success line. try to get size */ + i = match ("content-length: *([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 (); + display (size, n); + if (p+len > q) { + n = p+len-q; + i = fwrite (q, 1, n, file); + if (i != n) + die (); + display (size, n); + } + } + } else { + i = fwrite (p, 1, len, file); + if (i != len) + die (); + 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, 0); + 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, 0); + 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, 0); + + 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); +} |