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, "(no-check-certificate)O:", TOYFLAG_USR|TOYFLAG_BIN))
   7
   8config WGET
   9  bool "wget"
  10  default n
  11  help
  12    usage: wget -O filename URL
  13    -O filename: specify output filename
  14    URL: uniform resource location, FTP/HTTP only, not HTTPS
  15
  16    examples:
  17      wget -O index.html http://www.example.com
  18      wget -O sample.jpg ftp://ftp.example.com:21/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  char ftp = !strncmp(url, "ftp://", 6);
  59
  60  if (ftp) i--;
  61  else if (strncmp(url, "http://", i)) error_exit("only FTP/HTTP support");
  62  len = get_hn(url+i, hostname);
  63  i += len;
  64
  65  // get port if exists
  66  if (url[i] == ':') {
  67    i++;
  68    i = get_port(url+i, port, i);
  69  } else strcpy(port, "80");
  70
  71  // get uri in URL
  72  if (url[i] == '\0') strcpy(path, "/");
  73  else if (url[i] == '/') {
  74    if (strlen(url+i) < 1024) strcpy(path, url+i);
  75    else error_exit("too long path in URL");
  76  } else error_exit("wrong URL");
  77
  78  if (ftp) xexec((char *[]){"ftpget", hostname, TT.filename, path, 0});
  79}
  80
  81// connect to any IPv4 or IPv6 server
  82static int conn_svr(const char *hostname, const char *port) {
  83  struct addrinfo hints, *result, *rp;
  84  int sock;
  85
  86  memset(&hints, 0, sizeof(struct addrinfo));
  87  hints.ai_family = AF_UNSPEC;
  88  hints.ai_socktype = SOCK_STREAM;
  89  hints.ai_flags = 0;
  90  hints.ai_protocol = 0;
  91
  92  if ((errno = getaddrinfo(hostname, port, &hints, &result)))
  93    error_exit("getaddrinfo: %s", gai_strerror(errno));
  94
  95  // try all address list(IPv4 or IPv6) until success
  96  for (rp = result; rp; rp = rp->ai_next) {
  97    if ((sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol))
  98        == -1) {
  99      perror_msg("socket error");
 100      continue;
 101    }
 102    if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1)
 103      break; // succeed in connecting to any server IP 
 104    else perror_msg("connect error");
 105    close(sock);
 106  }
 107  freeaddrinfo(result);
 108  if(!rp) error_exit("can't connect");
 109
 110  return sock;
 111}
 112
 113// make HTTP request header field
 114static void mk_fld(char *name, char *value) {
 115  strcat(toybuf, name);
 116  strcat(toybuf, ": ");
 117  strcat(toybuf, value);
 118  strcat(toybuf, "\r\n");
 119}
 120
 121// get http response body starting address and its length
 122static char *get_body(ssize_t len, ssize_t *body_len) {
 123  int i;
 124
 125  for (i = 0; i < len-4; i++)
 126    if (!strncmp(toybuf+i, "\r\n\r\n", 4)) break;
 127
 128  *body_len = len - i - 4;
 129  return toybuf+i+4;
 130}
 131
 132void wget_main(void)
 133{
 134  int sock;
 135  FILE *fp;
 136  ssize_t len, body_len;
 137  char *body, *result, *rc, *r_str;
 138  char ua[18] = "toybox wget", ver[6], hostname[1024], port[6], path[1024];
 139
 140  // TODO extract filename to be saved from URL
 141  if (!(toys.optflags & FLAG_O)) help_exit("no filename");
 142  if (fopen(TT.filename, "r")) error_exit("'%s' already exists", TT.filename);
 143
 144  if(!toys.optargs[0]) help_exit("no URL");
 145  get_info(toys.optargs[0], hostname, port, path);
 146
 147  sock = conn_svr(hostname, port);
 148
 149  // compose HTTP request
 150  sprintf(toybuf, "GET %s HTTP/1.1\r\n", path);
 151  mk_fld("Host", hostname);
 152#ifdef TOYBOX_VERSION
 153  strcat(ua, "/"), strncpy(ver, TOYBOX_VERSION, 5), strcat(ua, ver);
 154#endif
 155  mk_fld("User-Agent", ua); 
 156  mk_fld("Connection", "close");
 157  strcat(toybuf, "\r\n");
 158
 159  // send the HTTP request
 160  len = strlen(toybuf);
 161  if (write(sock, toybuf, len) != len) perror_exit("write error");
 162
 163  // read HTTP response
 164  if ((len = read(sock, toybuf, 4096)) == -1) perror_exit("read error");
 165  if (!strstr(toybuf, "\r\n\r\n")) error_exit("too long HTTP response");
 166  body = get_body(len, &body_len);
 167  result = strtok(toybuf, "\r");
 168  strtok(result, " ");
 169  rc = strtok(NULL, " ");
 170  r_str = strtok(NULL, " ");
 171
 172  // HTTP res code check
 173  // TODO handle HTTP 302 Found(Redirection)
 174  if (strcmp(rc, "200")) error_exit("res: %s(%s)", rc, r_str);
 175
 176  if (!(fp = fopen(TT.filename, "w"))) perror_exit("fopen error");
 177  if (fwrite(body, 1, body_len, fp) != body_len)
 178    error_exit("fwrite error");
 179  while ((len = read(sock, toybuf, 4096)) > 0)
 180    if (fwrite(toybuf, 1, len, fp) != len)
 181      error_exit("fwrite error");
 182  if (fclose(fp) == EOF) perror_exit("fclose error");
 183}
 184