1 | /*
|
---|
2 | * Copyright (c) 2013 Martin Sucha
|
---|
3 | * All rights reserved.
|
---|
4 | *
|
---|
5 | * Redistribution and use in source and binary forms, with or without
|
---|
6 | * modification, are permitted provided that the following conditions
|
---|
7 | * are met:
|
---|
8 | *
|
---|
9 | * - Redistributions of source code must retain the above copyright
|
---|
10 | * notice, this list of conditions and the following disclaimer.
|
---|
11 | * - Redistributions in binary form must reproduce the above copyright
|
---|
12 | * notice, this list of conditions and the following disclaimer in the
|
---|
13 | * documentation and/or other materials provided with the distribution.
|
---|
14 | * - The name of the author may not be used to endorse or promote products
|
---|
15 | * derived from this software without specific prior written permission.
|
---|
16 | *
|
---|
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
---|
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
---|
19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
---|
20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
---|
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
---|
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
---|
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
27 | */
|
---|
28 |
|
---|
29 | /** @addtogroup download
|
---|
30 | * @{
|
---|
31 | */
|
---|
32 |
|
---|
33 | /** @file
|
---|
34 | * Download a file from a HTTP server
|
---|
35 | *
|
---|
36 | */
|
---|
37 |
|
---|
38 | #include <errno.h>
|
---|
39 | #include <stdio.h>
|
---|
40 | #include <stdlib.h>
|
---|
41 | #include <str.h>
|
---|
42 | #include <str_error.h>
|
---|
43 | #include <task.h>
|
---|
44 | #include <macros.h>
|
---|
45 |
|
---|
46 | #include <http/http.h>
|
---|
47 | #include <uri.h>
|
---|
48 |
|
---|
49 | #define NAME "download"
|
---|
50 | #ifdef TIMESTAMP_UNIX
|
---|
51 | #define VERSION STRING(HELENOS_RELEASE) "-" STRING(TIMESTAMP_UNIX)
|
---|
52 | #else
|
---|
53 | #define VERSION STRING(HELENOS_RELEASE)
|
---|
54 | #endif
|
---|
55 | #define USER_AGENT "HelenOS-" NAME "/" VERSION
|
---|
56 |
|
---|
57 | static void syntax_print(void)
|
---|
58 | {
|
---|
59 | fprintf(stderr, "Usage: download [-o <outfile>] <url>\n");
|
---|
60 | fprintf(stderr, " Without -o, data will be written to stdout, so you may want\n");
|
---|
61 | fprintf(stderr, " to redirect the output, e.g.\n");
|
---|
62 | fprintf(stderr, "\n");
|
---|
63 | fprintf(stderr, " download http://helenos.org/ | to helenos.html\n\n");
|
---|
64 | }
|
---|
65 |
|
---|
66 | int main(int argc, char *argv[])
|
---|
67 | {
|
---|
68 | int i;
|
---|
69 | char *ofname = NULL;
|
---|
70 | FILE *ofile = NULL;
|
---|
71 | size_t buf_size = 4096;
|
---|
72 | void *buf = NULL;
|
---|
73 | uri_t *uri = NULL;
|
---|
74 | http_t *http = NULL;
|
---|
75 | errno_t rc;
|
---|
76 | int ret;
|
---|
77 |
|
---|
78 | if (argc < 2) {
|
---|
79 | syntax_print();
|
---|
80 | rc = EINVAL;
|
---|
81 | goto error;
|
---|
82 | }
|
---|
83 |
|
---|
84 | i = 1;
|
---|
85 |
|
---|
86 | if (str_cmp(argv[i], "-o") == 0) {
|
---|
87 | ++i;
|
---|
88 | if (argc < i + 1) {
|
---|
89 | syntax_print();
|
---|
90 | rc = EINVAL;
|
---|
91 | goto error;
|
---|
92 | }
|
---|
93 |
|
---|
94 | ofname = argv[i++];
|
---|
95 | ofile = fopen(ofname, "wb");
|
---|
96 | if (ofile == NULL) {
|
---|
97 | fprintf(stderr, "Error creating '%s'.\n", ofname);
|
---|
98 | rc = EINVAL;
|
---|
99 | goto error;
|
---|
100 | }
|
---|
101 | }
|
---|
102 |
|
---|
103 | if (argc != i + 1) {
|
---|
104 | syntax_print();
|
---|
105 | rc = EINVAL;
|
---|
106 | goto error;
|
---|
107 | }
|
---|
108 |
|
---|
109 | uri = uri_parse(argv[i]);
|
---|
110 | if (uri == NULL) {
|
---|
111 | fprintf(stderr, "Failed parsing URI\n");
|
---|
112 | rc = EINVAL;
|
---|
113 | goto error;
|
---|
114 | }
|
---|
115 |
|
---|
116 | if (!uri_validate(uri)) {
|
---|
117 | fprintf(stderr, "The URI is invalid\n");
|
---|
118 | rc = EINVAL;
|
---|
119 | goto error;
|
---|
120 | }
|
---|
121 |
|
---|
122 | /* TODO uri_normalize(uri) */
|
---|
123 |
|
---|
124 | if (str_cmp(uri->scheme, "http") != 0) {
|
---|
125 | fprintf(stderr, "Only http scheme is supported at the moment\n");
|
---|
126 | rc = EINVAL;
|
---|
127 | goto error;
|
---|
128 | }
|
---|
129 |
|
---|
130 | if (uri->host == NULL) {
|
---|
131 | fprintf(stderr, "host not set\n");
|
---|
132 | rc = EINVAL;
|
---|
133 | goto error;
|
---|
134 | }
|
---|
135 |
|
---|
136 | uint16_t port = 80;
|
---|
137 | if (uri->port != NULL) {
|
---|
138 | rc = str_uint16_t(uri->port, NULL, 10, true, &port);
|
---|
139 | if (rc != EOK) {
|
---|
140 | fprintf(stderr, "Invalid port number: %s\n", uri->port);
|
---|
141 | rc = EINVAL;
|
---|
142 | goto error;
|
---|
143 | }
|
---|
144 | }
|
---|
145 |
|
---|
146 | const char *path = uri->path;
|
---|
147 | if (path == NULL || *path == 0)
|
---|
148 | path = "/";
|
---|
149 | char *server_path = NULL;
|
---|
150 | if (uri->query == NULL) {
|
---|
151 | server_path = str_dup(path);
|
---|
152 | if (server_path == NULL) {
|
---|
153 | fprintf(stderr, "Failed allocating path\n");
|
---|
154 | rc = ENOMEM;
|
---|
155 | goto error;
|
---|
156 | }
|
---|
157 | } else {
|
---|
158 | ret = asprintf(&server_path, "%s?%s", path, uri->query);
|
---|
159 | if (ret < 0) {
|
---|
160 | fprintf(stderr, "Failed allocating path\n");
|
---|
161 | rc = ENOMEM;
|
---|
162 | goto error;
|
---|
163 | }
|
---|
164 | }
|
---|
165 |
|
---|
166 | http_request_t *req = http_request_create("GET", server_path);
|
---|
167 | free(server_path);
|
---|
168 | if (req == NULL) {
|
---|
169 | fprintf(stderr, "Failed creating request\n");
|
---|
170 | rc = ENOMEM;
|
---|
171 | goto error;
|
---|
172 | }
|
---|
173 |
|
---|
174 | rc = http_headers_append(&req->headers, "Host", uri->host);
|
---|
175 | if (rc != EOK) {
|
---|
176 | fprintf(stderr, "Failed setting Host header: %s\n", str_error(rc));
|
---|
177 | goto error;
|
---|
178 | }
|
---|
179 |
|
---|
180 | rc = http_headers_append(&req->headers, "User-Agent", USER_AGENT);
|
---|
181 | if (rc != EOK) {
|
---|
182 | fprintf(stderr, "Failed creating User-Agent header: %s\n", str_error(rc));
|
---|
183 | goto error;
|
---|
184 | }
|
---|
185 |
|
---|
186 | http = http_create(uri->host, port);
|
---|
187 | if (http == NULL) {
|
---|
188 | fprintf(stderr, "Failed creating HTTP object\n");
|
---|
189 | rc = ENOMEM;
|
---|
190 | goto error;
|
---|
191 | }
|
---|
192 |
|
---|
193 | rc = http_connect(http);
|
---|
194 | if (rc != EOK) {
|
---|
195 | fprintf(stderr, "Failed connecting: %s\n", str_error(rc));
|
---|
196 | rc = EIO;
|
---|
197 | goto error;
|
---|
198 | }
|
---|
199 |
|
---|
200 | rc = http_send_request(http, req);
|
---|
201 | if (rc != EOK) {
|
---|
202 | fprintf(stderr, "Failed sending request: %s\n", str_error(rc));
|
---|
203 | rc = EIO;
|
---|
204 | goto error;
|
---|
205 | }
|
---|
206 |
|
---|
207 | http_response_t *response = NULL;
|
---|
208 | rc = http_receive_response(&http->recv_buffer, &response, 16 * 1024,
|
---|
209 | 100);
|
---|
210 | if (rc != EOK) {
|
---|
211 | fprintf(stderr, "Failed receiving response: %s\n", str_error(rc));
|
---|
212 | rc = EIO;
|
---|
213 | goto error;
|
---|
214 | }
|
---|
215 |
|
---|
216 | if (response->status != 200) {
|
---|
217 | fprintf(stderr, "Server returned status %d %s\n", response->status,
|
---|
218 | response->message);
|
---|
219 | } else {
|
---|
220 | buf = malloc(buf_size);
|
---|
221 | if (buf == NULL) {
|
---|
222 | fprintf(stderr, "Failed allocating buffer\n)");
|
---|
223 | rc = ENOMEM;
|
---|
224 | goto error;
|
---|
225 | }
|
---|
226 |
|
---|
227 | size_t body_size;
|
---|
228 | while ((rc = recv_buffer(&http->recv_buffer, buf, buf_size, &body_size)) == EOK && body_size > 0) {
|
---|
229 | fwrite(buf, 1, body_size, ofile != NULL ? ofile : stdout);
|
---|
230 | }
|
---|
231 |
|
---|
232 | if (rc != EOK) {
|
---|
233 | fprintf(stderr, "Failed receiving body: %s", str_error(rc));
|
---|
234 | }
|
---|
235 | }
|
---|
236 |
|
---|
237 | free(buf);
|
---|
238 | http_destroy(http);
|
---|
239 | uri_destroy(uri);
|
---|
240 | if (ofile != NULL && fclose(ofile) != 0) {
|
---|
241 | printf("Error writing '%s'.\n", ofname);
|
---|
242 | return EIO;
|
---|
243 | }
|
---|
244 |
|
---|
245 | return EOK;
|
---|
246 | error:
|
---|
247 | free(buf);
|
---|
248 | if (http != NULL)
|
---|
249 | http_destroy(http);
|
---|
250 | if (uri != NULL)
|
---|
251 | uri_destroy(uri);
|
---|
252 | if (ofile != NULL)
|
---|
253 | fclose(ofile);
|
---|
254 | return rc;
|
---|
255 | }
|
---|
256 |
|
---|
257 | /** @}
|
---|
258 | */
|
---|