Fix error reporting in app/download.
[helenos.git] / uspace / app / download / main.c
blob7ff891acd7ffaf6a5c3bc85e289bc1f5617d6a0a
1 /*
2 * Copyright (c) 2013 Martin Sucha
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup download
30 * @{
33 /** @file
34 * Download a file from a HTTP server
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>
46 #include <http/http.h>
47 #include <uri.h>
49 #define NAME "download"
50 #ifdef TIMESTAMP_UNIX
51 #define VERSION STRING(RELEASE) "-" STRING(TIMESTAMP_UNIX)
52 #else
53 #define VERSION STRING(RELEASE)
54 #endif
55 #define USER_AGENT "HelenOS-" NAME "/" VERSION
57 static void syntax_print(void)
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");
66 int main(int argc, char *argv[])
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 int rc;
77 if (argc < 2) {
78 syntax_print();
79 rc = EINVAL;
80 goto error;
83 i = 1;
85 if (str_cmp(argv[i], "-o") == 0) {
86 ++i;
87 if (argc < i + 1) {
88 syntax_print();
89 rc = EINVAL;
90 goto error;
93 ofname = argv[i++];
94 ofile = fopen(ofname, "wb");
95 if (ofile == NULL) {
96 fprintf(stderr, "Error creating '%s'.\n", ofname);
97 rc = EINVAL;
98 goto error;
102 if (argc != i + 1) {
103 syntax_print();
104 rc = EINVAL;
105 goto error;
108 uri = uri_parse(argv[i]);
109 if (uri == NULL) {
110 fprintf(stderr, "Failed parsing URI\n");
111 rc = EINVAL;
112 goto error;
115 if (!uri_validate(uri)) {
116 fprintf(stderr, "The URI is invalid\n");
117 rc = EINVAL;
118 goto error;
121 /* TODO uri_normalize(uri) */
123 if (str_cmp(uri->scheme, "http") != 0) {
124 fprintf(stderr, "Only http scheme is supported at the moment\n");
125 rc = EINVAL;
126 goto error;
129 if (uri->host == NULL) {
130 fprintf(stderr, "host not set\n");
131 rc = EINVAL;
132 goto error;
135 uint16_t port = 80;
136 if (uri->port != NULL) {
137 rc = str_uint16_t(uri->port, NULL, 10, true, &port);
138 if (rc != EOK) {
139 fprintf(stderr, "Invalid port number: %s\n", uri->port);
140 rc = EINVAL;
141 goto error;
145 const char *path = uri->path;
146 if (path == NULL || *path == 0)
147 path = "/";
148 char *server_path = NULL;
149 if (uri->query == NULL) {
150 server_path = str_dup(path);
151 if (server_path == NULL) {
152 fprintf(stderr, "Failed allocating path\n");
153 rc = ENOMEM;
154 goto error;
156 } else {
157 rc = asprintf(&server_path, "%s?%s", path, uri->query);
158 if (rc < 0) {
159 fprintf(stderr, "Failed allocating path\n");
160 rc = ENOMEM;
161 goto error;
165 http_request_t *req = http_request_create("GET", server_path);
166 free(server_path);
167 if (req == NULL) {
168 fprintf(stderr, "Failed creating request\n");
169 rc = ENOMEM;
170 goto error;
173 rc = http_headers_append(&req->headers, "Host", uri->host);
174 if (rc != EOK) {
175 fprintf(stderr, "Failed setting Host header: %s\n", str_error(rc));
176 goto error;
179 rc = http_headers_append(&req->headers, "User-Agent", USER_AGENT);
180 if (rc != EOK) {
181 fprintf(stderr, "Failed creating User-Agent header: %s\n", str_error(rc));
182 goto error;
185 http = http_create(uri->host, port);
186 if (http == NULL) {
187 fprintf(stderr, "Failed creating HTTP object\n");
188 rc = ENOMEM;
189 goto error;
192 rc = http_connect(http);
193 if (rc != EOK) {
194 fprintf(stderr, "Failed connecting: %s\n", str_error(rc));
195 rc = EIO;
196 goto error;
199 rc = http_send_request(http, req);
200 if (rc != EOK) {
201 fprintf(stderr, "Failed sending request: %s\n", str_error(rc));
202 rc = EIO;
203 goto error;
206 http_response_t *response = NULL;
207 rc = http_receive_response(&http->recv_buffer, &response, 16 * 1024,
208 100);
209 if (rc != EOK) {
210 fprintf(stderr, "Failed receiving response: %s\n", str_error(rc));
211 rc = EIO;
212 goto error;
215 if (response->status != 200) {
216 fprintf(stderr, "Server returned status %d %s\n", response->status,
217 response->message);
218 } else {
219 buf = malloc(buf_size);
220 if (buf == NULL) {
221 fprintf(stderr, "Failed allocating buffer\n)");
222 rc = ENOMEM;
223 goto error;
226 size_t body_size;
227 while ((rc = recv_buffer(&http->recv_buffer, buf, buf_size, &body_size)) == EOK && body_size > 0) {
228 fwrite(buf, 1, body_size, ofile != NULL ? ofile : stdout);
231 if (rc != EOK) {
232 fprintf(stderr, "Failed receiving body: %s", str_error(rc));
236 free(buf);
237 http_destroy(http);
238 uri_destroy(uri);
239 if (ofile != NULL && fclose(ofile) != 0) {
240 printf("Error writing '%s'.\n", ofname);
241 return EIO;
244 return EOK;
245 error:
246 free(buf);
247 if (http != NULL)
248 http_destroy(http);
249 if (uri != NULL)
250 uri_destroy(uri);
251 if (ofile != NULL)
252 fclose(ofile);
253 return rc;
256 /** @}