libretro: adjust psxclock description
[pcsx_rearmed.git] / deps / libretro-common / net / net_http.c
CommitLineData
3719602c
PC
1/* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (net_http.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <ctype.h>
26
27#include <net/net_http.h>
28#include <net/net_compat.h>
29#include <net/net_socket.h>
30#ifdef HAVE_SSL
31#include <net/net_socket_ssl.h>
32#endif
33#include <compat/strl.h>
34#include <string/stdstring.h>
35#include <string.h>
36#include <retro_common_api.h>
37#include <retro_miscellaneous.h>
38
39enum
40{
41 P_HEADER_TOP = 0,
42 P_HEADER,
43 P_BODY,
44 P_BODY_CHUNKLEN,
45 P_DONE,
46 P_ERROR
47};
48
49enum
50{
51 T_FULL = 0,
52 T_LEN,
53 T_CHUNK
54};
55
56struct http_socket_state_t
57{
58 void *ssl_ctx;
59 int fd;
60 bool ssl;
61};
62
63struct http_t
64{
65 char *data;
66 struct http_socket_state_t sock_state; /* ptr alignment */
67 size_t pos;
68 size_t len;
69 size_t buflen;
70 int status;
71 char part;
72 char bodytype;
73 bool error;
74};
75
76struct http_connection_t
77{
78 char *domain;
79 char *location;
80 char *urlcopy;
81 char *scan;
82 char *methodcopy;
83 char *contenttypecopy;
84 char *postdatacopy;
85 char *useragentcopy;
86 char *headerscopy;
87 struct http_socket_state_t sock_state; /* ptr alignment */
88 int port;
89};
90
91/**
92 * net_http_urlencode:
93 *
94 * URL Encode a string
95 * caller is responsible for deleting the destination buffer
96 **/
97void net_http_urlencode(char **dest, const char *source)
98{
99 static const char urlencode_lut[256] =
100 {
101 0, /* 0 */
102 0, /* 1 */
103 0, /* 2 */
104 0, /* 3 */
105 0, /* 4 */
106 0, /* 5 */
107 0, /* 6 */
108 0, /* 7 */
109 0, /* 8 */
110 0, /* 9 */
111 0, /* 10 */
112 0, /* 11 */
113 0, /* 12 */
114 0, /* 13 */
115 0, /* 14 */
116 0, /* 15 */
117 0, /* 16 */
118 0, /* 17 */
119 0, /* 18 */
120 0, /* 19 */
121 0, /* 20 */
122 0, /* 21 */
123 0, /* 22 */
124 0, /* 23 */
125 0, /* 24 */
126 0, /* 25 */
127 0, /* 26 */
128 0, /* 27 */
129 0, /* 28 */
130 0, /* 29 */
131 0, /* 30 */
132 0, /* 31 */
133 0, /* 32 */
134 0, /* 33 */
135 0, /* 34 */
136 0, /* 35 */
137 0, /* 36 */
138 0, /* 37 */
139 0, /* 38 */
140 0, /* 39 */
141 0, /* 40 */
142 0, /* 41 */
143 '*', /* 42 */
144 0, /* 43 */
145 0, /* 44 */
146 '-', /* 45 */
147 '.', /* 46 */
148 '/', /* 47 */
149 '0', /* 48 */
150 '1', /* 49 */
151 '2', /* 50 */
152 '3', /* 51 */
153 '4', /* 52 */
154 '5', /* 53 */
155 '6', /* 54 */
156 '7', /* 55 */
157 '8', /* 56 */
158 '9', /* 57 */
159 0, /* 58 */
160 0, /* 59 */
161 0, /* 60 */
162 0, /* 61 */
163 0, /* 62 */
164 0, /* 63 */
165 0, /* 64 */
166 'A', /* 65 */
167 'B', /* 66 */
168 'C', /* 67 */
169 'D', /* 68 */
170 'E', /* 69 */
171 'F', /* 70 */
172 'G', /* 71 */
173 'H', /* 72 */
174 'I', /* 73 */
175 'J', /* 74 */
176 'K', /* 75 */
177 'L', /* 76 */
178 'M', /* 77 */
179 'N', /* 78 */
180 'O', /* 79 */
181 'P', /* 80 */
182 'Q', /* 81 */
183 'R', /* 82 */
184 'S', /* 83 */
185 'T', /* 84 */
186 'U', /* 85 */
187 'V', /* 86 */
188 'W', /* 87 */
189 'X', /* 88 */
190 'Y', /* 89 */
191 'Z', /* 90 */
192 0, /* 91 */
193 0, /* 92 */
194 0, /* 93 */
195 0, /* 94 */
196 '_', /* 95 */
197 0, /* 96 */
198 'a', /* 97 */
199 'b', /* 98 */
200 'c', /* 99 */
201 'd', /* 100 */
202 'e', /* 101 */
203 'f', /* 102 */
204 'g', /* 103 */
205 'h', /* 104 */
206 'i', /* 105 */
207 'j', /* 106 */
208 'k', /* 107 */
209 'l', /* 108 */
210 'm', /* 109 */
211 'n', /* 110 */
212 'o', /* 111 */
213 'p', /* 112 */
214 'q', /* 113 */
215 'r', /* 114 */
216 's', /* 115 */
217 't', /* 116 */
218 'u', /* 117 */
219 'v', /* 118 */
220 'w', /* 119 */
221 'x', /* 120 */
222 'y', /* 121 */
223 'z', /* 122 */
224 0, /* 123 */
225 0, /* 124 */
226 0, /* 125 */
227 0, /* 126 */
228 0, /* 127 */
229 0, /* 128 */
230 0, /* 129 */
231 0, /* 130 */
232 0, /* 131 */
233 0, /* 132 */
234 0, /* 133 */
235 0, /* 134 */
236 0, /* 135 */
237 0, /* 136 */
238 0, /* 137 */
239 0, /* 138 */
240 0, /* 139 */
241 0, /* 140 */
242 0, /* 141 */
243 0, /* 142 */
244 0, /* 143 */
245 0, /* 144 */
246 0, /* 145 */
247 0, /* 146 */
248 0, /* 147 */
249 0, /* 148 */
250 0, /* 149 */
251 0, /* 150 */
252 0, /* 151 */
253 0, /* 152 */
254 0, /* 153 */
255 0, /* 154 */
256 0, /* 155 */
257 0, /* 156 */
258 0, /* 157 */
259 0, /* 158 */
260 0, /* 159 */
261 0, /* 160 */
262 0, /* 161 */
263 0, /* 162 */
264 0, /* 163 */
265 0, /* 164 */
266 0, /* 165 */
267 0, /* 166 */
268 0, /* 167 */
269 0, /* 168 */
270 0, /* 169 */
271 0, /* 170 */
272 0, /* 171 */
273 0, /* 172 */
274 0, /* 173 */
275 0, /* 174 */
276 0, /* 175 */
277 0, /* 176 */
278 0, /* 177 */
279 0, /* 178 */
280 0, /* 179 */
281 0, /* 180 */
282 0, /* 181 */
283 0, /* 182 */
284 0, /* 183 */
285 0, /* 184 */
286 0, /* 185 */
287 0, /* 186 */
288 0, /* 187 */
289 0, /* 188 */
290 0, /* 189 */
291 0, /* 190 */
292 0, /* 191 */
293 0, /* 192 */
294 0, /* 193 */
295 0, /* 194 */
296 0, /* 195 */
297 0, /* 196 */
298 0, /* 197 */
299 0, /* 198 */
300 0, /* 199 */
301 0, /* 200 */
302 0, /* 201 */
303 0, /* 202 */
304 0, /* 203 */
305 0, /* 204 */
306 0, /* 205 */
307 0, /* 206 */
308 0, /* 207 */
309 0, /* 208 */
310 0, /* 209 */
311 0, /* 210 */
312 0, /* 211 */
313 0, /* 212 */
314 0, /* 213 */
315 0, /* 214 */
316 0, /* 215 */
317 0, /* 216 */
318 0, /* 217 */
319 0, /* 218 */
320 0, /* 219 */
321 0, /* 220 */
322 0, /* 221 */
323 0, /* 222 */
324 0, /* 223 */
325 0, /* 224 */
326 0, /* 225 */
327 0, /* 226 */
328 0, /* 227 */
329 0, /* 228 */
330 0, /* 229 */
331 0, /* 230 */
332 0, /* 231 */
333 0, /* 232 */
334 0, /* 233 */
335 0, /* 234 */
336 0, /* 235 */
337 0, /* 236 */
338 0, /* 237 */
339 0, /* 238 */
340 0, /* 239 */
341 0, /* 240 */
342 0, /* 241 */
343 0, /* 242 */
344 0, /* 243 */
345 0, /* 244 */
346 0, /* 245 */
347 0, /* 246 */
348 0, /* 247 */
349 0, /* 248 */
350 0, /* 249 */
351 0, /* 250 */
352 0, /* 251 */
353 0, /* 252 */
354 0, /* 253 */
355 0, /* 254 */
356 0 /* 255 */
357 };
358
359 /* Assume every character will be encoded, so we need 3 times the space. */
360 size_t len = strlen(source) * 3 + 1;
361 size_t count = len;
362 char *enc = (char*)calloc(1, len);
363 *dest = enc;
364
365 for (; *source; source++)
366 {
367 int written = 0;
368
369 /* any non-ASCII character will just be encoded without question */
370 if ((unsigned)*source < sizeof(urlencode_lut) && urlencode_lut[(unsigned)*source])
371 written = snprintf(enc, count, "%c", urlencode_lut[(unsigned)*source]);
372 else
373 written = snprintf(enc, count, "%%%02X", *source & 0xFF);
374
375 if (written > 0)
376 count -= written;
377
378 while (*++enc);
379 }
380
381 (*dest)[len - 1] = '\0';
382}
383
384/**
385 * net_http_urlencode_full:
386 *
387 * Re-encode a full URL
388 **/
389void net_http_urlencode_full(char *dest,
390 const char *source, size_t size)
391{
392 size_t tmp_len;
393 size_t url_domain_len;
394 char url_domain[256];
395 char url_path[PATH_MAX_LENGTH];
396 size_t buf_pos = 0;
397 char *tmp = NULL;
398 int count = 0;
399
400 strlcpy(url_path, source, sizeof(url_path));
401 tmp = url_path;
402
403 while (count < 3 && tmp[0] != '\0')
404 {
405 tmp = strchr(tmp, '/');
406 count++;
407 tmp++;
408 }
409
410 tmp_len = strlen(tmp);
411 url_domain_len = ((strlcpy(url_domain, source, tmp - url_path)) - tmp_len) - 1;
412 strlcpy(url_path,
413 source + url_domain_len + 1,
414 tmp_len + 1
415 );
416
417 tmp = NULL;
418 net_http_urlencode(&tmp, url_path);
419 buf_pos = strlcpy(dest, url_domain, size);
420 dest[buf_pos] = '/';
421 dest[buf_pos+1] = '\0';
422 strlcat(dest, tmp, size);
423 free (tmp);
424}
425
426static int net_http_new_socket(struct http_connection_t *conn)
427{
428 struct addrinfo *addr = NULL, *next_addr = NULL;
429 int fd = socket_init(
430 (void**)&addr, conn->port, conn->domain, SOCKET_TYPE_STREAM, 0);
431#ifdef HAVE_SSL
432 if (conn->sock_state.ssl)
433 {
434 if (fd < 0)
435 goto done;
436
437 if (!(conn->sock_state.ssl_ctx = ssl_socket_init(fd, conn->domain)))
438 {
439 socket_close(fd);
440 fd = -1;
441 goto done;
442 }
443
444 /* TODO: Properly figure out what's going wrong when the newer
445 timeout/poll code interacts with mbed and winsock
446 https://github.com/libretro/RetroArch/issues/14742 */
447
448 /* Temp fix, don't use new timeout/poll code for cheevos http requests */
449 bool timeout = true;
450#ifdef __WIN32
451 if (!strcmp(conn->domain, "retroachievements.org"))
452 timeout = false;
453#endif
454
455 if (ssl_socket_connect(conn->sock_state.ssl_ctx, addr, timeout, true)
456 < 0)
457 {
458 fd = -1;
459 goto done;
460 }
461 }
462 else
463#endif
464 for (next_addr = addr; fd >= 0; fd = socket_next((void**)&next_addr))
465 {
466 if (socket_connect_with_timeout(fd, next_addr, 5000))
467 break;
468
469 socket_close(fd);
470 }
471
472#ifdef HAVE_SSL
473done:
474#endif
475 if (addr)
476 freeaddrinfo_retro(addr);
477
478 conn->sock_state.fd = fd;
479
480 return fd;
481}
482
483static void net_http_send_str(
484 struct http_socket_state_t *sock_state, bool *error,
485 const char *text, size_t text_size)
486{
487 if (*error)
488 return;
489#ifdef HAVE_SSL
490 if (sock_state->ssl)
491 {
492 if (!ssl_socket_send_all_blocking(
493 sock_state->ssl_ctx, text, text_size, true))
494 *error = true;
495 }
496 else
497#endif
498 {
499 if (!socket_send_all_blocking(
500 sock_state->fd, text, text_size, true))
501 *error = true;
502 }
503}
504
505struct http_connection_t *net_http_connection_new(const char *url,
506 const char *method, const char *data)
507{
508 struct http_connection_t *conn = NULL;
509
510 if (!url)
511 return NULL;
512 if (!(conn = (struct http_connection_t*)malloc(
513 sizeof(*conn))))
514 return NULL;
515
516 conn->domain = NULL;
517 conn->location = NULL;
518 conn->urlcopy = NULL;
519 conn->scan = NULL;
520 conn->methodcopy = NULL;
521 conn->contenttypecopy = NULL;
522 conn->postdatacopy = NULL;
523 conn->useragentcopy = NULL;
524 conn->headerscopy = NULL;
525 conn->port = 0;
526 conn->sock_state.fd = 0;
527 conn->sock_state.ssl = false;
528 conn->sock_state.ssl_ctx= NULL;
529
530 if (method)
531 conn->methodcopy = strdup(method);
532
533 if (data)
534 conn->postdatacopy = strdup(data);
535
536 if (!(conn->urlcopy = strdup(url)))
537 goto error;
538
539 if (!strncmp(url, "http://", STRLEN_CONST("http://")))
540 conn->scan = conn->urlcopy + STRLEN_CONST("http://");
541 else if (!strncmp(url, "https://", STRLEN_CONST("https://")))
542 {
543 conn->scan = conn->urlcopy + STRLEN_CONST("https://");
544 conn->sock_state.ssl = true;
545 }
546 else
547 goto error;
548
549 if (string_is_empty(conn->scan))
550 goto error;
551
552 conn->domain = conn->scan;
553
554 return conn;
555
556error:
557 if (conn->urlcopy)
558 free(conn->urlcopy);
559 if (conn->methodcopy)
560 free(conn->methodcopy);
561 if (conn->postdatacopy)
562 free(conn->postdatacopy);
563 conn->urlcopy = NULL;
564 conn->methodcopy = NULL;
565 conn->postdatacopy = NULL;
566 free(conn);
567 return NULL;
568}
569
570/**
571 * net_http_connection_iterate:
572 *
573 * Leaf function.
574 **/
575bool net_http_connection_iterate(struct http_connection_t *conn)
576{
577 if (!conn)
578 return false;
579
580 while (*conn->scan != '/' && *conn->scan != ':' && *conn->scan != '\0')
581 conn->scan++;
582
583 return true;
584}
585
586bool net_http_connection_done(struct http_connection_t *conn)
587{
588 int has_port = 0;
589
590 if (!conn || !conn->domain || !*conn->domain)
591 return false;
592
593 if (*conn->scan == ':')
594 {
595 /* domain followed by port, split off the port */
596 *conn->scan++ = '\0';
597
598 if (!isdigit((int)(*conn->scan)))
599 return false;
600
601 conn->port = (int)strtoul(conn->scan, &conn->scan, 10);
602 has_port = 1;
603 }
604 else if (conn->port == 0)
605 {
606 /* port not specified, default to standard HTTP or HTTPS port */
607 if (conn->sock_state.ssl)
608 conn->port = 443;
609 else
610 conn->port = 80;
611 }
612
613 if (*conn->scan == '/')
614 {
615 /* domain followed by location - split off the location */
616 /* site.com/path.html or site.com:80/path.html */
617 *conn->scan = '\0';
618 conn->location = conn->scan + 1;
619 return true;
620 }
621 else if (!*conn->scan)
622 {
623 /* domain with no location - point location at empty string */
624 /* site.com or site.com:80 */
625 conn->location = conn->scan;
626 return true;
627 }
628 else if (*conn->scan == '?')
629 {
630 /* domain with no location, but still has query parms - point location at the query parms */
631 /* site.com?param=3 or site.com:80?param=3 */
632 if (!has_port)
633 {
634 /* if there wasn't a port, we have to expand the urlcopy so we can separate the two parts */
635 size_t domain_len = strlen(conn->domain);
636 size_t location_len = strlen(conn->scan);
637 char* urlcopy = (char*)malloc(domain_len + location_len + 2);
638 memcpy(urlcopy, conn->domain, domain_len);
639 urlcopy[domain_len] = '\0';
640 memcpy(urlcopy + domain_len + 1, conn->scan, location_len + 1);
641
642 free(conn->urlcopy);
643 conn->domain = conn->urlcopy = urlcopy;
644 conn->location = conn->scan = urlcopy + domain_len + 1;
645 }
646 else /* There was a port, so overwriting the : will terminate the domain and we can just point at the ? */
647 conn->location = conn->scan;
648
649 return true;
650 }
651
652 /* invalid character after domain/port */
653 return false;
654}
655
656void net_http_connection_free(struct http_connection_t *conn)
657{
658 if (!conn)
659 return;
660
661 if (conn->urlcopy)
662 free(conn->urlcopy);
663
664 if (conn->methodcopy)
665 free(conn->methodcopy);
666
667 if (conn->contenttypecopy)
668 free(conn->contenttypecopy);
669
670 if (conn->postdatacopy)
671 free(conn->postdatacopy);
672
673 if (conn->useragentcopy)
674 free(conn->useragentcopy);
675
676 if (conn->headerscopy)
677 free(conn->headerscopy);
678
679 conn->urlcopy = NULL;
680 conn->methodcopy = NULL;
681 conn->contenttypecopy = NULL;
682 conn->postdatacopy = NULL;
683 conn->useragentcopy = NULL;
684 conn->headerscopy = NULL;
685
686 free(conn);
687}
688
689void net_http_connection_set_user_agent(
690 struct http_connection_t *conn, const char *user_agent)
691{
692 if (conn->useragentcopy)
693 free(conn->useragentcopy);
694
695 conn->useragentcopy = user_agent ? strdup(user_agent) : NULL;
696}
697
698void net_http_connection_set_headers(
699 struct http_connection_t *conn, const char *headers)
700{
701 if (conn->headerscopy)
702 free(conn->headerscopy);
703
704 conn->headerscopy = headers ? strdup(headers) : NULL;
705}
706
707const char *net_http_connection_url(struct http_connection_t *conn)
708{
709 return conn->urlcopy;
710}
711
712const char* net_http_connection_method(struct http_connection_t* conn)
713{
714 return conn->methodcopy;
715}
716
717struct http_t *net_http_new(struct http_connection_t *conn)
718{
719 bool error = false;
720 int fd = -1;
721 struct http_t *state = NULL;
722
723 if (!conn)
724 goto error;
725
726 if ((fd = net_http_new_socket(conn)) < 0)
727 goto error;
728
729 error = false;
730
731 /* This is a bit lazy, but it works. */
732 if (conn->methodcopy)
733 {
734 net_http_send_str(&conn->sock_state, &error, conn->methodcopy,
735 strlen(conn->methodcopy));
736 net_http_send_str(&conn->sock_state, &error, " /",
737 STRLEN_CONST(" /"));
738 }
739 else
740 {
741 net_http_send_str(&conn->sock_state, &error, "GET /",
742 STRLEN_CONST("GET /"));
743 }
744
745 net_http_send_str(&conn->sock_state, &error, conn->location,
746 strlen(conn->location));
747 net_http_send_str(&conn->sock_state, &error, " HTTP/1.1\r\n",
748 STRLEN_CONST(" HTTP/1.1\r\n"));
749
750 net_http_send_str(&conn->sock_state, &error, "Host: ",
751 STRLEN_CONST("Host: "));
752 net_http_send_str(&conn->sock_state, &error, conn->domain,
753 strlen(conn->domain));
754
755 if (conn->port)
756 {
757 char portstr[16];
758
759 portstr[0] = '\0';
760
761 snprintf(portstr, sizeof(portstr), ":%i", conn->port);
762 net_http_send_str(&conn->sock_state, &error, portstr,
763 strlen(portstr));
764 }
765
766 net_http_send_str(&conn->sock_state, &error, "\r\n",
767 STRLEN_CONST("\r\n"));
768
769 /* Pre-formatted headers */
770 if (conn->headerscopy)
771 net_http_send_str(&conn->sock_state, &error, conn->headerscopy,
772 strlen(conn->headerscopy));
773 /* This is not being set anywhere yet */
774 else if (conn->contenttypecopy)
775 {
776 net_http_send_str(&conn->sock_state, &error, "Content-Type: ",
777 STRLEN_CONST("Content-Type: "));
778 net_http_send_str(&conn->sock_state, &error,
779 conn->contenttypecopy, strlen(conn->contenttypecopy));
780 net_http_send_str(&conn->sock_state, &error, "\r\n",
781 STRLEN_CONST("\r\n"));
782 }
783
784 if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
785 {
786 size_t post_len, len;
787 char *len_str = NULL;
788
789 if (!conn->postdatacopy)
790 goto error;
791
792 if (!conn->headerscopy)
793 {
794 if (!conn->contenttypecopy)
795 net_http_send_str(&conn->sock_state, &error,
796 "Content-Type: application/x-www-form-urlencoded\r\n",
797 STRLEN_CONST(
798 "Content-Type: application/x-www-form-urlencoded\r\n"
799 ));
800 }
801
802 net_http_send_str(&conn->sock_state, &error, "Content-Length: ",
803 STRLEN_CONST("Content-Length: "));
804
805 post_len = strlen(conn->postdatacopy);
806#ifdef _WIN32
807 len = snprintf(NULL, 0, "%" PRIuPTR, post_len);
808 len_str = (char*)malloc(len + 1);
809 snprintf(len_str, len + 1, "%" PRIuPTR, post_len);
810#else
811 len = snprintf(NULL, 0, "%llu", (long long unsigned)post_len);
812 len_str = (char*)malloc(len + 1);
813 snprintf(len_str, len + 1, "%llu", (long long unsigned)post_len);
814#endif
815
816 len_str[len] = '\0';
817
818 net_http_send_str(&conn->sock_state, &error, len_str,
819 strlen(len_str));
820 net_http_send_str(&conn->sock_state, &error, "\r\n",
821 STRLEN_CONST("\r\n"));
822
823 free(len_str);
824 }
825
826 net_http_send_str(&conn->sock_state, &error, "User-Agent: ",
827 STRLEN_CONST("User-Agent: "));
828 if (conn->useragentcopy)
829 net_http_send_str(&conn->sock_state, &error,
830 conn->useragentcopy, strlen(conn->useragentcopy));
831 else
832 net_http_send_str(&conn->sock_state, &error, "libretro",
833 STRLEN_CONST("libretro"));
834 net_http_send_str(&conn->sock_state, &error, "\r\n",
835 STRLEN_CONST("\r\n"));
836
837 net_http_send_str(&conn->sock_state, &error,
838 "Connection: close\r\n", STRLEN_CONST("Connection: close\r\n"));
839 net_http_send_str(&conn->sock_state, &error, "\r\n",
840 STRLEN_CONST("\r\n"));
841
842 if (conn->methodcopy && (string_is_equal(conn->methodcopy, "POST")))
843 net_http_send_str(&conn->sock_state, &error, conn->postdatacopy,
844 strlen(conn->postdatacopy));
845
846 if (error)
847 goto error;
848
849 state = (struct http_t*)malloc(sizeof(struct http_t));
850 state->sock_state = conn->sock_state;
851 state->status = -1;
852 state->data = NULL;
853 state->part = P_HEADER_TOP;
854 state->bodytype = T_FULL;
855 state->error = false;
856 state->pos = 0;
857 state->len = 0;
858 state->buflen = 512;
859
860 if (!(state->data = (char*)malloc(state->buflen)))
861 goto error;
862
863 return state;
864
865error:
866 if (conn)
867 {
868 if (conn->methodcopy)
869 free(conn->methodcopy);
870 if (conn->contenttypecopy)
871 free(conn->contenttypecopy);
872 conn->methodcopy = NULL;
873 conn->contenttypecopy = NULL;
874 conn->postdatacopy = NULL;
875 }
876#ifdef HAVE_SSL
877 if (conn && conn->sock_state.ssl_ctx)
878 {
879 ssl_socket_close(conn->sock_state.ssl_ctx);
880 ssl_socket_free(conn->sock_state.ssl_ctx);
881 conn->sock_state.ssl_ctx = NULL;
882 }
883#else
884 if (fd >= 0)
885 socket_close(fd);
886#endif
887 if (state)
888 free(state);
889 return NULL;
890}
891
892/**
893 * net_http_fd:
894 *
895 * Leaf function.
896 *
897 * You can use this to call net_http_update
898 * only when something will happen; select() it for reading.
899 **/
900int net_http_fd(struct http_t *state)
901{
902 if (!state)
903 return -1;
904 return state->sock_state.fd;
905}
906
907/**
908 * net_http_update:
909 *
910 * @return true if it's done, or if something broke.
911 * @total will be 0 if it's not known.
912 **/
913bool net_http_update(struct http_t *state, size_t* progress, size_t* total)
914{
915 ssize_t newlen = 0;
916
917 if (!state)
918 return true;
919 if (state->error)
920 {
921 state->part = P_ERROR;
922 state->status = -1;
923 return true;
924 }
925
926 if (state->part < P_BODY)
927 {
928 if (state->error)
929 {
930 state->part = P_ERROR;
931 state->status = -1;
932 return true;
933 }
934
935#ifdef HAVE_SSL
936 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
937 newlen = ssl_socket_receive_all_nonblocking(state->sock_state.ssl_ctx, &state->error,
938 (uint8_t*)state->data + state->pos,
939 state->buflen - state->pos);
940 else
941#endif
942 newlen = socket_receive_all_nonblocking(state->sock_state.fd, &state->error,
943 (uint8_t*)state->data + state->pos,
944 state->buflen - state->pos);
945
946 if (newlen < 0)
947 {
948 state->error = true;
949 state->part = P_ERROR;
950 state->status = -1;
951 return true;
952 }
953
954 if (state->pos + newlen >= state->buflen - 64)
955 {
956 state->buflen *= 2;
957 state->data = (char*)realloc(state->data, state->buflen);
958 }
959 state->pos += newlen;
960
961 while (state->part < P_BODY)
962 {
963 char *dataend = state->data + state->pos;
964 char *lineend = (char*)memchr(state->data, '\n', state->pos);
965
966 if (!lineend)
967 break;
968
969 *lineend='\0';
970
971 if (lineend != state->data && lineend[-1]=='\r')
972 lineend[-1]='\0';
973
974 if (state->part == P_HEADER_TOP)
975 {
976 if (strncmp(state->data, "HTTP/1.", STRLEN_CONST("HTTP/1."))!=0)
977 {
978 state->error = true;
979 state->part = P_ERROR;
980 state->status = -1;
981 return true;
982 }
983 state->status = (int)strtoul(state->data
984 + STRLEN_CONST("HTTP/1.1 "), NULL, 10);
985 state->part = P_HEADER;
986 }
987 else
988 {
989 if (string_starts_with_case_insensitive(state->data, "Content-Length:"))
990 {
991 char* ptr = state->data + STRLEN_CONST("Content-Length:");
992 while (ISSPACE(*ptr))
993 ++ptr;
994
995 state->bodytype = T_LEN;
996 state->len = strtol(ptr, NULL, 10);
997 }
998 if (string_is_equal_case_insensitive(state->data, "Transfer-Encoding: chunked"))
999 state->bodytype = T_CHUNK;
1000
1001 /* TODO: save headers somewhere */
1002 if (state->data[0]=='\0')
1003 {
1004 state->part = P_BODY;
1005 if (state->bodytype == T_CHUNK)
1006 state->part = P_BODY_CHUNKLEN;
1007 }
1008 }
1009
1010 memmove(state->data, lineend + 1, dataend-(lineend+1));
1011 state->pos = (dataend-(lineend + 1));
1012 }
1013
1014 if (state->part >= P_BODY)
1015 {
1016 newlen = state->pos;
1017 state->pos = 0;
1018 }
1019 }
1020
1021 if (state->part >= P_BODY && state->part < P_DONE)
1022 {
1023 if (!newlen)
1024 {
1025 if (state->error)
1026 newlen = -1;
1027 else
1028 {
1029#ifdef HAVE_SSL
1030 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
1031 newlen = ssl_socket_receive_all_nonblocking(
1032 state->sock_state.ssl_ctx,
1033 &state->error,
1034 (uint8_t*)state->data + state->pos,
1035 state->buflen - state->pos);
1036 else
1037#endif
1038 newlen = socket_receive_all_nonblocking(
1039 state->sock_state.fd,
1040 &state->error,
1041 (uint8_t*)state->data + state->pos,
1042 state->buflen - state->pos);
1043 }
1044
1045 if (newlen < 0)
1046 {
1047 if (state->bodytype != T_FULL)
1048 {
1049 state->error = true;
1050 state->part = P_ERROR;
1051 state->status = -1;
1052 return true;
1053 }
1054 state->part = P_DONE;
1055 state->data = (char*)realloc(state->data, state->len);
1056 newlen = 0;
1057 }
1058
1059 if (state->pos + newlen >= state->buflen - 64)
1060 {
1061 state->buflen *= 2;
1062 state->data = (char*)realloc(state->data, state->buflen);
1063 }
1064 }
1065
1066parse_again:
1067 if (state->bodytype == T_CHUNK)
1068 {
1069 if (state->part == P_BODY_CHUNKLEN)
1070 {
1071 state->pos += newlen;
1072
1073 if (state->pos - state->len >= 2)
1074 {
1075 /*
1076 * len=start of chunk including \r\n
1077 * pos=end of data
1078 */
1079
1080 char *fullend = state->data + state->pos;
1081 char *end = (char*)memchr(state->data + state->len + 2, '\n',
1082 state->pos - state->len - 2);
1083
1084 if (end)
1085 {
1086 size_t chunklen = strtoul(state->data+state->len, NULL, 16);
1087 state->pos = state->len;
1088 end++;
1089
1090 memmove(state->data+state->len, end, fullend-end);
1091
1092 state->len = chunklen;
1093 newlen = (fullend - end);
1094
1095 /*
1096 len=num bytes
1097 newlen=unparsed bytes after \n
1098 pos=start of chunk including \r\n
1099 */
1100
1101 state->part = P_BODY;
1102 if (state->len == 0)
1103 {
1104 state->part = P_DONE;
1105 state->len = state->pos;
1106 state->data = (char*)realloc(state->data, state->len);
1107 }
1108 goto parse_again;
1109 }
1110 }
1111 }
1112 else if (state->part == P_BODY)
1113 {
1114 if ((size_t)newlen >= state->len)
1115 {
1116 state->pos += state->len;
1117 newlen -= state->len;
1118 state->len = state->pos;
1119 state->part = P_BODY_CHUNKLEN;
1120 goto parse_again;
1121 }
1122 state->pos += newlen;
1123 state->len -= newlen;
1124 }
1125 }
1126 else
1127 {
1128 state->pos += newlen;
1129
1130 if (state->pos > state->len)
1131 {
1132 state->error = true;
1133 state->part = P_ERROR;
1134 state->status = -1;
1135 return true;
1136 }
1137 else if (state->pos == state->len)
1138 {
1139 state->part = P_DONE;
1140 state->data = (char*)realloc(state->data, state->len);
1141 }
1142 }
1143 }
1144
1145 if (progress)
1146 *progress = state->pos;
1147
1148 if (total)
1149 {
1150 if (state->bodytype == T_LEN)
1151 *total = state->len;
1152 else
1153 *total = 0;
1154 }
1155
1156 return (state->part == P_DONE);
1157}
1158
1159/**
1160 * net_http_status:
1161 *
1162 * Report HTTP status. 200, 404, or whatever.
1163 *
1164 * Leaf function.
1165 *
1166 * @return HTTP status code.
1167 **/
1168int net_http_status(struct http_t *state)
1169{
1170 if (!state)
1171 return -1;
1172 return state->status;
1173}
1174
1175/**
1176 * net_http_data:
1177 *
1178 * Leaf function.
1179 *
1180 * @return the downloaded data. The returned buffer is owned by the
1181 * HTTP handler; it's freed by net_http_delete().
1182 * If the status is not 20x and accept_error is false, it returns NULL.
1183 **/
1184uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error)
1185{
1186 if (!state)
1187 return NULL;
1188
1189 if (!accept_error && (state->error || state->status < 200 || state->status > 299))
1190 {
1191 if (len)
1192 *len = 0;
1193 return NULL;
1194 }
1195
1196 if (len)
1197 *len = state->len;
1198
1199 return (uint8_t*)state->data;
1200}
1201
1202/**
1203 * net_http_delete:
1204 *
1205 * Cleans up all memory.
1206 **/
1207void net_http_delete(struct http_t *state)
1208{
1209 if (!state)
1210 return;
1211
1212 if (state->sock_state.fd >= 0)
1213 {
1214#ifdef HAVE_SSL
1215 if (state->sock_state.ssl && state->sock_state.ssl_ctx)
1216 {
1217 ssl_socket_close(state->sock_state.ssl_ctx);
1218 ssl_socket_free(state->sock_state.ssl_ctx);
1219 state->sock_state.ssl_ctx = NULL;
1220 }
1221 else
1222#endif
1223 socket_close(state->sock_state.fd);
1224 }
1225 free(state);
1226}
1227
1228/**
1229 * net_http_error:
1230 *
1231 * Leaf function
1232 **/
1233bool net_http_error(struct http_t *state)
1234{
1235 return (state->error || state->status < 200 || state->status > 299);
1236}