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