Home | History | Annotate | Download | only in pending
      1 /* wget.c - Simple downloader to get the resource file in HTTP server
      2  *
      3  * Copyright 2016 Lipi C.H. Lee <lipisoft (at) gmail.com>
      4  *
      5 
      6 USE_WGET(NEWTOY(wget, "f:", TOYFLAG_USR|TOYFLAG_BIN))
      7 
      8 config WGET
      9   bool "wget"
     10   default n
     11   help
     12     usage: wget -f filename URL
     13     -f filename: specify the filename to be saved
     14     URL: HTTP uniform resource location and only HTTP, not HTTPS
     15 
     16     examples:
     17       wget -f index.html http://www.example.com
     18       wget -f sample.jpg http://www.example.com:8080/sample.jpg
     19 */
     20 
     21 #define FOR_wget
     22 #include "toys.h"
     23 
     24 GLOBALS(
     25   char *filename;
     26 )
     27 
     28 // extract hostname from url
     29 static unsigned get_hn(const char *url, char *hostname) {
     30   unsigned i;
     31 
     32   for (i = 0; url[i] != '\0' && url[i] != ':' && url[i] != '/'; i++) {
     33     if(i >= 1024) error_exit("too long hostname in URL");
     34     hostname[i] = url[i];
     35   }
     36   hostname[i] = '\0';
     37 
     38   return i;
     39 }
     40 
     41 // extract port number
     42 static unsigned get_port(const char *url, char *port, unsigned url_i) {
     43   unsigned i;
     44 
     45   for (i = 0; url[i] != '\0' && url[i] != '/'; i++, url_i++) {
     46     if('0' <= url[i] && url[i] <= '9') port[i] = url[i];
     47     else error_exit("wrong decimal port number");
     48   }
     49   if(i <= 6) port[i] = '\0';
     50   else error_exit("too long port number");
     51 
     52   return url_i;
     53 }
     54 
     55 // get http infos in URL
     56 static void get_info(const char *url, char* hostname, char *port, char *path) {
     57   unsigned i = 7, len;
     58 
     59   if (strncmp(url, "http://", i)) error_exit("only HTTP support");
     60   len = get_hn(url+i, hostname);
     61   i += len;
     62 
     63   // get port if exists
     64   if (url[i] == ':') {
     65     i++;
     66     i = get_port(url+i, port, i);
     67   } else strcpy(port, "80");
     68 
     69   // get uri in URL
     70   if (url[i] == '\0') strcpy(path, "/");
     71   else if (url[i] == '/') {
     72     if (strlen(url+i) < 1024) strcpy(path, url+i);
     73     else error_exit("too long path in URL");
     74   } else error_exit("wrong URL");
     75 }
     76 
     77 // connect to any IPv4 or IPv6 server
     78 static int conn_svr(const char *hostname, const char *port) {
     79   struct addrinfo hints, *result, *rp;
     80   int sock;
     81 
     82   memset(&hints, 0, sizeof(struct addrinfo));
     83   hints.ai_family = AF_UNSPEC;
     84   hints.ai_socktype = SOCK_STREAM;
     85   hints.ai_flags = 0;
     86   hints.ai_protocol = 0;
     87 
     88   if ((errno = getaddrinfo(hostname, port, &hints, &result)))
     89     error_exit("getaddrinfo: %s", gai_strerror(errno));
     90 
     91   // try all address list(IPv4 or IPv6) until success
     92   for (rp = result; rp; rp = rp->ai_next) {
     93     if ((sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol))
     94         == -1) {
     95       perror_msg("socket error");
     96       continue;
     97     }
     98     if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)
     99       break; // succeed in connecting to any server IP
    100     else perror_msg("connect error");
    101     close(sock);
    102   }
    103   freeaddrinfo(result);
    104   if(!rp) error_exit("can't connect");
    105 
    106   return sock;
    107 }
    108 
    109 // make HTTP request header field
    110 static void mk_fld(char *name, char *value) {
    111   strcat(toybuf, name);
    112   strcat(toybuf, ": ");
    113   strcat(toybuf, value);
    114   strcat(toybuf, "\r\n");
    115 }
    116 
    117 // get http response body starting address and its length
    118 static char *get_body(ssize_t len, ssize_t *body_len) {
    119   int i;
    120 
    121   for (i = 0; i < len-4; i++)
    122     if (!strncmp(toybuf+i, "\r\n\r\n", 4)) break;
    123 
    124   *body_len = len - i - 4;
    125   return toybuf+i+4;
    126 }
    127 
    128 void wget_main(void)
    129 {
    130   int sock;
    131   FILE *fp;
    132   ssize_t len, body_len;
    133   char *body, *result, *rc, *r_str;
    134   char ua[18] = "toybox wget/", ver[6], hostname[1024], port[6], path[1024];
    135 
    136   // TODO extract filename to be saved from URL
    137   if (!(toys.optflags & FLAG_f)) help_exit("no filename");
    138   if (fopen(TT.filename, "r")) perror_exit("file already exists");
    139 
    140   if(!toys.optargs[0]) help_exit("no URL");
    141   get_info(toys.optargs[0], hostname, port, path);
    142 
    143   sock = conn_svr(hostname, port);
    144 
    145   // compose HTTP request
    146   sprintf(toybuf, "GET %s HTTP/1.1\r\n", path);
    147   mk_fld("Host", hostname);
    148   strncpy(ver, TOYBOX_VERSION, 5);
    149   strcat(ua, ver);
    150   mk_fld("User-Agent", ua);
    151   mk_fld("Connection", "close");
    152   strcat(toybuf, "\r\n");
    153 
    154   // send the HTTP request
    155   len = strlen(toybuf);
    156   if (write(sock, toybuf, len) != len) perror_exit("write error");
    157 
    158   // read HTTP response
    159   if ((len = read(sock, toybuf, 4096)) == -1) perror_exit("read error");
    160   if (!strstr(toybuf, "\r\n\r\n")) error_exit("too long HTTP response");
    161   body = get_body(len, &body_len);
    162   result = strtok(toybuf, "\r");
    163   strtok(result, " ");
    164   rc = strtok(NULL, " ");
    165   r_str = strtok(NULL, " ");
    166 
    167   // HTTP res code check
    168   // TODO handle HTTP 302 Found(Redirection)
    169   if (strcmp(rc, "200")) error_exit("res: %s(%s)", rc, r_str);
    170 
    171   if (!(fp = fopen(TT.filename, "w"))) perror_exit("fopen error");
    172   if (fwrite(body, 1, body_len, fp) != body_len)
    173     error_exit("fwrite error");
    174   while ((len = read(sock, toybuf, 4096)) > 0)
    175     if (fwrite(toybuf, 1, len, fp) != len)
    176       error_exit("fwrite error");
    177   if (fclose(fp) == EOF) perror_exit("fclose error");
    178 }
    179