Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / net / http.c
blob4684f8b333c2a7bca2e1b398f2a9891380fa6696
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/misc.h>
20 #include <grub/net/tcp.h>
21 #include <grub/net/ip.h>
22 #include <grub/net/ethernet.h>
23 #include <grub/net/netbuff.h>
24 #include <grub/net.h>
25 #include <grub/mm.h>
26 #include <grub/dl.h>
27 #include <grub/file.h>
28 #include <grub/i18n.h>
30 GRUB_MOD_LICENSE ("GPLv3+");
32 enum
34 HTTP_PORT = 80
38 typedef struct http_data
40 char *current_line;
41 grub_size_t current_line_len;
42 int headers_recv;
43 int first_line_recv;
44 int size_recv;
45 grub_net_tcp_socket_t sock;
46 char *filename;
47 grub_err_t err;
48 char *errmsg;
49 int chunked;
50 grub_size_t chunk_rem;
51 int in_chunk_len;
52 } *http_data_t;
54 static grub_off_t
55 have_ahead (struct grub_file *file)
57 grub_net_t net = file->device->net;
58 grub_off_t ret = net->offset;
59 struct grub_net_packet *pack;
60 for (pack = net->packs.first; pack; pack = pack->next)
61 ret += pack->nb->tail - pack->nb->data;
62 return ret;
65 static grub_err_t
66 parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len)
68 char *end = ptr + len;
69 while (end > ptr && *(end - 1) == '\r')
70 end--;
71 *end = 0;
72 /* Trailing CRLF. */
73 if (data->in_chunk_len == 1)
75 data->in_chunk_len = 2;
76 return GRUB_ERR_NONE;
78 if (data->in_chunk_len == 2)
80 data->chunk_rem = grub_strtoul (ptr, 0, 16);
81 grub_errno = GRUB_ERR_NONE;
82 if (data->chunk_rem == 0)
84 file->device->net->eof = 1;
85 file->device->net->stall = 1;
86 if (file->size == GRUB_FILE_SIZE_UNKNOWN)
87 file->size = have_ahead (file);
89 data->in_chunk_len = 0;
90 return GRUB_ERR_NONE;
92 if (ptr == end)
94 data->headers_recv = 1;
95 if (data->chunked)
96 data->in_chunk_len = 2;
97 return GRUB_ERR_NONE;
100 if (!data->first_line_recv)
102 int code;
103 if (grub_memcmp (ptr, "HTTP/1.1 ", sizeof ("HTTP/1.1 ") - 1) != 0)
105 data->errmsg = grub_strdup (_("unsupported HTTP response"));
106 data->first_line_recv = 1;
107 return GRUB_ERR_NONE;
109 ptr += sizeof ("HTTP/1.1 ") - 1;
110 code = grub_strtoul (ptr, &ptr, 10);
111 if (grub_errno)
112 return grub_errno;
113 switch (code)
115 case 200:
116 case 206:
117 break;
118 case 404:
119 data->err = GRUB_ERR_FILE_NOT_FOUND;
120 data->errmsg = grub_xasprintf (_("file `%s' not found"), data->filename);
121 return GRUB_ERR_NONE;
122 default:
123 data->err = GRUB_ERR_NET_UNKNOWN_ERROR;
124 /* TRANSLATORS: GRUB HTTP code is pretty young. So even perfectly
125 valid answers like 403 will trigger this very generic message. */
126 data->errmsg = grub_xasprintf (_("unsupported HTTP error %d: %s"),
127 code, ptr);
128 return GRUB_ERR_NONE;
130 data->first_line_recv = 1;
131 return GRUB_ERR_NONE;
133 if (grub_memcmp (ptr, "Content-Length: ", sizeof ("Content-Length: ") - 1)
134 == 0 && !data->size_recv)
136 ptr += sizeof ("Content-Length: ") - 1;
137 file->size = grub_strtoull (ptr, &ptr, 10);
138 data->size_recv = 1;
139 return GRUB_ERR_NONE;
141 if (grub_memcmp (ptr, "Transfer-Encoding: chunked",
142 sizeof ("Transfer-Encoding: chunked") - 1) == 0)
144 data->chunked = 1;
145 return GRUB_ERR_NONE;
148 return GRUB_ERR_NONE;
151 static void
152 http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)),
153 void *f)
155 grub_file_t file = f;
156 http_data_t data = file->data;
158 if (data->sock)
159 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
160 data->sock = 0;
161 if (data->current_line)
162 grub_free (data->current_line);
163 data->current_line = 0;
164 file->device->net->eof = 1;
165 file->device->net->stall = 1;
166 if (file->size == GRUB_FILE_SIZE_UNKNOWN)
167 file->size = have_ahead (file);
170 static grub_err_t
171 http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
172 struct grub_net_buff *nb,
173 void *f)
175 grub_file_t file = f;
176 http_data_t data = file->data;
177 grub_err_t err;
179 if (!data->sock)
181 grub_netbuff_free (nb);
182 return GRUB_ERR_NONE;
185 while (1)
187 char *ptr = (char *) nb->data;
188 if ((!data->headers_recv || data->in_chunk_len) && data->current_line)
190 int have_line = 1;
191 char *t;
192 ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data);
193 if (ptr)
194 ptr++;
195 else
197 have_line = 0;
198 ptr = (char *) nb->tail;
200 t = grub_realloc (data->current_line,
201 data->current_line_len + (ptr - (char *) nb->data));
202 if (!t)
204 grub_netbuff_free (nb);
205 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
206 return grub_errno;
209 data->current_line = t;
210 grub_memcpy (data->current_line + data->current_line_len,
211 nb->data, ptr - (char *) nb->data);
212 data->current_line_len += ptr - (char *) nb->data;
213 if (!have_line)
215 grub_netbuff_free (nb);
216 return GRUB_ERR_NONE;
218 err = parse_line (file, data, data->current_line,
219 data->current_line_len);
220 grub_free (data->current_line);
221 data->current_line = 0;
222 data->current_line_len = 0;
223 if (err)
225 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
226 grub_netbuff_free (nb);
227 return err;
231 while (ptr < (char *) nb->tail && (!data->headers_recv
232 || data->in_chunk_len))
234 char *ptr2;
235 ptr2 = grub_memchr (ptr, '\n', (char *) nb->tail - ptr);
236 if (!ptr2)
238 data->current_line = grub_malloc ((char *) nb->tail - ptr);
239 if (!data->current_line)
241 grub_netbuff_free (nb);
242 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
243 return grub_errno;
245 data->current_line_len = (char *) nb->tail - ptr;
246 grub_memcpy (data->current_line, ptr, data->current_line_len);
247 grub_netbuff_free (nb);
248 return GRUB_ERR_NONE;
250 err = parse_line (file, data, ptr, ptr2 - ptr);
251 if (err)
253 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
254 grub_netbuff_free (nb);
255 return err;
257 ptr = ptr2 + 1;
260 if (((char *) nb->tail - ptr) <= 0)
262 grub_netbuff_free (nb);
263 return GRUB_ERR_NONE;
265 err = grub_netbuff_pull (nb, ptr - (char *) nb->data);
266 if (err)
268 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
269 grub_netbuff_free (nb);
270 return err;
272 if (!(data->chunked && (grub_ssize_t) data->chunk_rem
273 < nb->tail - nb->data))
275 grub_net_put_packet (&file->device->net->packs, nb);
276 if (file->device->net->packs.count >= 20)
277 file->device->net->stall = 1;
279 if (file->device->net->packs.count >= 100)
280 grub_net_tcp_stall (data->sock);
282 if (data->chunked)
283 data->chunk_rem -= nb->tail - nb->data;
284 return GRUB_ERR_NONE;
286 if (data->chunk_rem)
288 struct grub_net_buff *nb2;
289 nb2 = grub_netbuff_alloc (data->chunk_rem);
290 if (!nb2)
291 return grub_errno;
292 grub_netbuff_put (nb2, data->chunk_rem);
293 grub_memcpy (nb2->data, nb->data, data->chunk_rem);
294 if (file->device->net->packs.count >= 20)
296 file->device->net->stall = 1;
297 grub_net_tcp_stall (data->sock);
300 grub_net_put_packet (&file->device->net->packs, nb2);
301 grub_netbuff_pull (nb, data->chunk_rem);
303 data->in_chunk_len = 1;
307 static grub_err_t
308 http_establish (struct grub_file *file, grub_off_t offset, int initial)
310 http_data_t data = file->data;
311 grub_uint8_t *ptr;
312 int i;
313 struct grub_net_buff *nb;
314 grub_err_t err;
316 nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE
317 + sizeof ("GET ") - 1
318 + grub_strlen (data->filename)
319 + sizeof (" HTTP/1.1\r\nHost: ") - 1
320 + grub_strlen (file->device->net->server)
321 + sizeof ("\r\nUser-Agent: " PACKAGE_STRING
322 "\r\n") - 1
323 + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX"
324 "-\r\n\r\n"));
325 if (!nb)
326 return grub_errno;
328 grub_netbuff_reserve (nb, GRUB_NET_TCP_RESERVE_SIZE);
329 ptr = nb->tail;
330 err = grub_netbuff_put (nb, sizeof ("GET ") - 1);
331 if (err)
333 grub_netbuff_free (nb);
334 return err;
336 grub_memcpy (ptr, "GET ", sizeof ("GET ") - 1);
338 ptr = nb->tail;
340 err = grub_netbuff_put (nb, grub_strlen (data->filename));
341 if (err)
343 grub_netbuff_free (nb);
344 return err;
346 grub_memcpy (ptr, data->filename, grub_strlen (data->filename));
348 ptr = nb->tail;
349 err = grub_netbuff_put (nb, sizeof (" HTTP/1.1\r\nHost: ") - 1);
350 if (err)
352 grub_netbuff_free (nb);
353 return err;
355 grub_memcpy (ptr, " HTTP/1.1\r\nHost: ",
356 sizeof (" HTTP/1.1\r\nHost: ") - 1);
358 ptr = nb->tail;
359 err = grub_netbuff_put (nb, grub_strlen (file->device->net->server));
360 if (err)
362 grub_netbuff_free (nb);
363 return err;
365 grub_memcpy (ptr, file->device->net->server,
366 grub_strlen (file->device->net->server));
368 ptr = nb->tail;
369 err = grub_netbuff_put (nb,
370 sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n")
371 - 1);
372 if (err)
374 grub_netbuff_free (nb);
375 return err;
377 grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n",
378 sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1);
379 if (!initial)
381 ptr = nb->tail;
382 grub_snprintf ((char *) ptr,
383 sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX-"
384 "\r\n"
385 "\r\n"),
386 "Range: bytes=%" PRIuGRUB_UINT64_T "-\r\n\r\n",
387 offset);
388 grub_netbuff_put (nb, grub_strlen ((char *) ptr));
390 ptr = nb->tail;
391 grub_netbuff_put (nb, 2);
392 grub_memcpy (ptr, "\r\n", 2);
394 data->sock = grub_net_tcp_open (file->device->net->server,
395 HTTP_PORT, http_receive,
396 http_err, http_err,
397 file);
398 if (!data->sock)
400 grub_netbuff_free (nb);
401 return grub_errno;
404 // grub_net_poll_cards (5000);
406 err = grub_net_send_tcp_packet (data->sock, nb, 1);
407 if (err)
409 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
410 return err;
413 for (i = 0; !data->headers_recv && i < 100; i++)
415 grub_net_tcp_retransmit ();
416 grub_net_poll_cards (300, &data->headers_recv);
419 if (!data->headers_recv)
421 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
422 if (data->err)
424 char *str = data->errmsg;
425 err = grub_error (data->err, "%s", str);
426 grub_free (str);
427 data->errmsg = 0;
428 return data->err;
430 return grub_error (GRUB_ERR_TIMEOUT, N_("time out opening `%s'"), data->filename);
432 return GRUB_ERR_NONE;
435 static grub_err_t
436 http_seek (struct grub_file *file, grub_off_t off)
438 struct http_data *old_data, *data;
439 grub_err_t err;
440 old_data = file->data;
441 /* FIXME: Reuse socket? */
442 if (old_data->sock)
443 grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT);
444 old_data->sock = 0;
446 while (file->device->net->packs.first)
448 grub_netbuff_free (file->device->net->packs.first->nb);
449 grub_net_remove_packet (file->device->net->packs.first);
452 file->device->net->stall = 0;
453 file->device->net->offset = off;
455 data = grub_zalloc (sizeof (*data));
456 if (!data)
457 return grub_errno;
459 data->size_recv = 1;
460 data->filename = old_data->filename;
461 if (!data->filename)
463 grub_free (data);
464 file->data = 0;
465 return grub_errno;
467 grub_free (old_data);
469 file->data = data;
470 err = http_establish (file, off, 0);
471 if (err)
473 grub_free (data->filename);
474 grub_free (data);
475 file->data = 0;
476 return err;
478 return GRUB_ERR_NONE;
481 static grub_err_t
482 http_open (struct grub_file *file, const char *filename)
484 grub_err_t err;
485 struct http_data *data;
487 data = grub_zalloc (sizeof (*data));
488 if (!data)
489 return grub_errno;
490 file->size = GRUB_FILE_SIZE_UNKNOWN;
492 data->filename = grub_strdup (filename);
493 if (!data->filename)
495 grub_free (data);
496 return grub_errno;
499 file->not_easily_seekable = 0;
500 file->data = data;
502 err = http_establish (file, 0, 1);
503 if (err)
505 grub_free (data->filename);
506 grub_free (data);
507 return err;
510 return GRUB_ERR_NONE;
513 static grub_err_t
514 http_close (struct grub_file *file)
516 http_data_t data = file->data;
518 if (!data)
519 return GRUB_ERR_NONE;
521 if (data->sock)
522 grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
523 if (data->current_line)
524 grub_free (data->current_line);
525 grub_free (data->filename);
526 grub_free (data);
527 return GRUB_ERR_NONE;
530 static grub_err_t
531 http_packets_pulled (struct grub_file *file)
533 http_data_t data = file->data;
535 if (file->device->net->packs.count >= 20)
536 return 0;
538 if (!file->device->net->eof)
539 file->device->net->stall = 0;
540 if (data && data->sock)
541 grub_net_tcp_unstall (data->sock);
542 return 0;
545 static struct grub_net_app_protocol grub_http_protocol =
547 .name = "http",
548 .open = http_open,
549 .close = http_close,
550 .seek = http_seek,
551 .packets_pulled = http_packets_pulled
554 GRUB_MOD_INIT (http)
556 grub_net_app_level_register (&grub_http_protocol);
559 GRUB_MOD_FINI (http)
561 grub_net_app_level_unregister (&grub_http_protocol);