• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
6 USE_WGET(NEWTOY(wget, "(no-check-certificate)O:", TOYFLAG_USR|TOYFLAG_BIN))
7 
8 config 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 
GLOBALS(char * filename;)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
get_port(const char * url,char * port,unsigned url_i)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
get_info(const char * url,char * hostname,char * port,char * path)56 static 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
conn_svr(const char * hostname,const char * port)82 static 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
mk_fld(char * name,char * value)114 static 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
get_body(ssize_t len,ssize_t * body_len)122 static 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 
wget_main(void)132 void 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