toybox/toys/pending/wget.c
<<
>>
Prefs
   1/* wget.c - Simple downloader to get the resource file in HTTP server
   2 *
   3 * Copyright 2016 Lipi C.H. Lee <lipisoft@gmail.com>
   4 *
   5
   6USE_WGET(NEWTOY(wget, "f:", TOYFLAG_USR|TOYFLAG_BIN))
   7
   8config 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
  24GLOBALS(
  25  char *filename;
  26)
  27
  28// extract hostname from url
  29static 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
  42static 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
  56static 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
  78static 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
 110static 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
 118static 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
 128void 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")) error_exit("'%s' already exists", TT.filename);
 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