Disconnect hostapd from building in base
[dragonfly.git] / contrib / hostapd / src / wps / httpread.c
1 /*
2  * httpread - Manage reading file(s) from HTTP/TCP socket
3  * Author: Ted Merrill
4  * Copyright 2008 Atheros Communications
5  *
6  * This software may be distributed under the terms of the BSD license.
7  * See README for more details.
8  *
9  * The files are buffered via internal callbacks from eloop, then presented to
10  * an application callback routine when completely read into memory. May also
11  * be used if no file is expected but just to get the header, including HTTP
12  * replies (e.g. HTTP/1.1 200 OK etc.).
13  *
14  * This does not attempt to be an optimally efficient implementation, but does
15  * attempt to be of reasonably small size and memory consumption; assuming that
16  * only small files are to be read. A maximum file size is provided by
17  * application and enforced.
18  *
19  * It is assumed that the application does not expect any of the following:
20  * -- transfer encoding other than chunked
21  * -- trailer fields
22  * It is assumed that, even if the other side requested that the connection be
23  * kept open, that we will close it (thus HTTP messages sent by application
24  * should have the connection closed field); this is allowed by HTTP/1.1 and
25  * simplifies things for us.
26  *
27  * Other limitations:
28  * -- HTTP header may not exceed a hard-coded size.
29  *
30  * Notes:
31  * This code would be massively simpler without some of the new features of
32  * HTTP/1.1, especially chunked data.
33  */
34
35 #include "includes.h"
36
37 #include "common.h"
38 #include "eloop.h"
39 #include "httpread.h"
40
41
42 /* Tunable parameters */
43 #define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
44 #define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
45 #define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
46
47 #if 0
48 /* httpread_debug -- set this global variable > 0 e.g. from debugger
49  * to enable debugs (larger numbers for more debugs)
50  * Make this a #define of 0 to eliminate the debugging code.
51  */
52 int httpread_debug = 99;
53 #else
54 #define httpread_debug 0        /* eliminates even the debugging code */
55 #endif
56
57
58 /* control instance -- actual definition (opaque to application)
59  */
60 struct httpread {
61         /* information from creation */
62         int sd;         /* descriptor of TCP socket to read from */
63         void (*cb)(struct httpread *handle, void *cookie,
64                     enum httpread_event e);  /* call on event */
65         void *cookie;   /* pass to callback */
66         int max_bytes;          /* maximum file size else abort it */
67         int timeout_seconds;            /* 0 or total duration timeout period */
68
69         /* dynamically used information follows */
70
71         int got_hdr;            /* nonzero when header is finalized */
72         char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
73         int hdr_nbytes;
74
75         enum httpread_hdr_type hdr_type;
76         int version;            /* 1 if we've seen 1.1 */
77         int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
78         int got_content_length; /* true if we know content length for sure */
79         int content_length;     /* body length,  iff got_content_length */
80         int chunked;            /* nonzero for chunked data */
81         char *uri;
82
83         int got_body;           /* nonzero when body is finalized */
84         char *body;
85         int body_nbytes;
86         int body_alloc_nbytes;  /* amount allocated */
87
88         int got_file;           /* here when we are done */
89
90         /* The following apply if data is chunked: */
91         int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
92         int chunk_start;        /* offset in body of chunk hdr or data */
93         int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
94         int in_trailer;         /* in header fields after data (chunked only)*/
95         enum trailer_state {
96                 trailer_line_begin = 0,
97                 trailer_empty_cr,       /* empty line + CR */
98                 trailer_nonempty,
99                 trailer_nonempty_cr,
100         } trailer_state;
101 };
102
103
104 /* Check words for equality, where words consist of graphical characters
105  * delimited by whitespace
106  * Returns nonzero if "equal" doing case insensitive comparison.
107  */
108 static int word_eq(char *s1, char *s2)
109 {
110         int c1;
111         int c2;
112         int end1 = 0;
113         int end2 = 0;
114         for (;;) {
115                 c1 = *s1++;
116                 c2 = *s2++;
117                 if (isalpha(c1) && isupper(c1))
118                         c1 = tolower(c1);
119                 if (isalpha(c2) && isupper(c2))
120                         c2 = tolower(c2);
121                 end1 = !isgraph(c1);
122                 end2 = !isgraph(c2);
123                 if (end1 || end2 || c1 != c2)
124                         break;
125         }
126         return end1 && end2;  /* reached end of both words? */
127 }
128
129
130 static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
131
132 /* httpread_destroy -- if h is non-NULL, clean up
133  * This must eventually be called by the application following
134  * call of the application's callback and may be called
135  * earlier if desired.
136  */
137 void httpread_destroy(struct httpread *h)
138 {
139         if (httpread_debug >= 10)
140                 wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
141         if (!h)
142                 return;
143
144         eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
145         eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
146         os_free(h->body);
147         os_free(h->uri);
148         os_memset(h, 0, sizeof(*h));  /* aid debugging */
149         h->sd = -1;     /* aid debugging */
150         os_free(h);
151 }
152
153
154 /* httpread_timeout_handler -- called on excessive total duration
155  */
156 static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
157 {
158         struct httpread *h = user_ctx;
159         wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
160         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
161 }
162
163
164 /* Analyze options only so far as is needed to correctly obtain the file.
165  * The application can look at the raw header to find other options.
166  */
167 static int httpread_hdr_option_analyze(
168         struct httpread *h,
169         char *hbp       /* pointer to current line in header buffer */
170         )
171 {
172         if (word_eq(hbp, "CONTENT-LENGTH:")) {
173                 while (isgraph(*hbp))
174                         hbp++;
175                 while (*hbp == ' ' || *hbp == '\t')
176                         hbp++;
177                 if (!isdigit(*hbp))
178                         return -1;
179                 h->content_length = atol(hbp);
180                 h->got_content_length = 1;
181                 return 0;
182         }
183         if (word_eq(hbp, "TRANSFER_ENCODING:") ||
184             word_eq(hbp, "TRANSFER-ENCODING:")) {
185                 while (isgraph(*hbp))
186                         hbp++;
187                 while (*hbp == ' ' || *hbp == '\t')
188                         hbp++;
189                 /* There should (?) be no encodings of interest
190                  * other than chunked...
191                  */
192                 if (word_eq(hbp, "CHUNKED")) {
193                         h->chunked = 1;
194                         h->in_chunk_data = 0;
195                         /* ignore possible ;<parameters> */
196                 }
197                 return 0;
198         }
199         /* skip anything we don't know, which is a lot */
200         return 0;
201 }
202
203
204 static int httpread_hdr_analyze(struct httpread *h)
205 {
206         char *hbp = h->hdr;      /* pointer into h->hdr */
207         int standard_first_line = 1;
208
209         /* First line is special */
210         h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
211         if (!isgraph(*hbp))
212                 goto bad;
213         if (os_strncmp(hbp, "HTTP/", 5) == 0) {
214                 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
215                 standard_first_line = 0;
216                 hbp += 5;
217                 if (hbp[0] == '1' && hbp[1] == '.' &&
218                     isdigit(hbp[2]) && hbp[2] != '0')
219                         h->version = 1;
220                 while (isgraph(*hbp))
221                         hbp++;
222                 while (*hbp == ' ' || *hbp == '\t')
223                         hbp++;
224                 if (!isdigit(*hbp))
225                         goto bad;
226                 h->reply_code = atol(hbp);
227         } else if (word_eq(hbp, "GET"))
228                 h->hdr_type = HTTPREAD_HDR_TYPE_GET;
229         else if (word_eq(hbp, "HEAD"))
230                 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
231         else if (word_eq(hbp, "POST"))
232                 h->hdr_type = HTTPREAD_HDR_TYPE_POST;
233         else if (word_eq(hbp, "PUT"))
234                 h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
235         else if (word_eq(hbp, "DELETE"))
236                 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
237         else if (word_eq(hbp, "TRACE"))
238                 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
239         else if (word_eq(hbp, "CONNECT"))
240                 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
241         else if (word_eq(hbp, "NOTIFY"))
242                 h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
243         else if (word_eq(hbp, "M-SEARCH"))
244                 h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
245         else if (word_eq(hbp, "M-POST"))
246                 h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
247         else if (word_eq(hbp, "SUBSCRIBE"))
248                 h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
249         else if (word_eq(hbp, "UNSUBSCRIBE"))
250                 h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
251         else {
252         }
253
254         if (standard_first_line) {
255                 char *rawuri;
256                 char *uri;
257                 /* skip type */
258                 while (isgraph(*hbp))
259                         hbp++;
260                 while (*hbp == ' ' || *hbp == '\t')
261                         hbp++;
262                 /* parse uri.
263                  * Find length, allocate memory for translated
264                  * copy, then translate by changing %<hex><hex>
265                  * into represented value.
266                  */
267                 rawuri = hbp;
268                 while (isgraph(*hbp))
269                         hbp++;
270                 h->uri = os_malloc((hbp - rawuri) + 1);
271                 if (h->uri == NULL)
272                         goto bad;
273                 uri = h->uri;
274                 while (rawuri < hbp) {
275                         int c = *rawuri;
276                         if (c == '%' &&
277                             isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
278                                 *uri++ = hex2byte(rawuri + 1);
279                                 rawuri += 3;
280                         } else {
281                                 *uri++ = c;
282                                 rawuri++;
283                         }
284                 }
285                 *uri = 0;       /* null terminate */
286                 while (isgraph(*hbp))
287                         hbp++;
288                 while (*hbp == ' ' || *hbp == '\t')
289                         hbp++;
290                 /* get version */
291                 if (0 == strncmp(hbp, "HTTP/", 5)) {
292                         hbp += 5;
293                         if (hbp[0] == '1' && hbp[1] == '.' &&
294                             isdigit(hbp[2]) && hbp[2] != '0')
295                                 h->version = 1;
296                 }
297         }
298         /* skip rest of line */
299         while (*hbp)
300                 if (*hbp++ == '\n')
301                         break;
302
303         /* Remainder of lines are options, in any order;
304          * or empty line to terminate
305          */
306         for (;;) {
307                 /* Empty line to terminate */
308                 if (hbp[0] == '\n' ||
309                     (hbp[0] == '\r' && hbp[1] == '\n'))
310                         break;
311                 if (!isgraph(*hbp))
312                         goto bad;
313                 if (httpread_hdr_option_analyze(h, hbp))
314                         goto bad;
315                 /* skip line */
316                 while (*hbp)
317                         if (*hbp++ == '\n')
318                                 break;
319         }
320
321         /* chunked overrides content-length always */
322         if (h->chunked)
323                 h->got_content_length = 0;
324
325         /* For some types, we should not try to read a body
326          * This is in addition to the application determining
327          * that we should not read a body.
328          */
329         switch (h->hdr_type) {
330         case HTTPREAD_HDR_TYPE_REPLY:
331                 /* Some codes can have a body and some not.
332                  * For now, just assume that any other than 200
333                  * do not...
334                  */
335                 if (h->reply_code != 200)
336                         h->max_bytes = 0;
337                 break;
338         case HTTPREAD_HDR_TYPE_GET:
339         case HTTPREAD_HDR_TYPE_HEAD:
340                 /* in practice it appears that it is assumed
341                  * that GETs have a body length of 0... ?
342                  */
343                 if (h->chunked == 0 && h->got_content_length == 0)
344                         h->max_bytes = 0;
345                 break;
346         case HTTPREAD_HDR_TYPE_POST:
347         case HTTPREAD_HDR_TYPE_PUT:
348         case HTTPREAD_HDR_TYPE_DELETE:
349         case HTTPREAD_HDR_TYPE_TRACE:
350         case HTTPREAD_HDR_TYPE_CONNECT:
351         case HTTPREAD_HDR_TYPE_NOTIFY:
352         case HTTPREAD_HDR_TYPE_M_SEARCH:
353         case HTTPREAD_HDR_TYPE_M_POST:
354         case HTTPREAD_HDR_TYPE_SUBSCRIBE:
355         case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
356         default:
357                 break;
358         }
359
360         return 0;
361
362 bad:
363         /* Error */
364         return -1;
365 }
366
367
368 /* httpread_read_handler -- called when socket ready to read
369  *
370  * Note: any extra data we read past end of transmitted file is ignored;
371  * if we were to support keeping connections open for multiple files then
372  * this would have to be addressed.
373  */
374 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
375 {
376         struct httpread *h = sock_ctx;
377         int nread;
378         char *rbp;      /* pointer into read buffer */
379         char *hbp;      /* pointer into header buffer */
380         char *bbp;      /* pointer into body buffer */
381         char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
382
383         if (httpread_debug >= 20)
384                 wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
385
386         /* read some at a time, then search for the interal
387          * boundaries between header and data and etc.
388          */
389         nread = read(h->sd, readbuf, sizeof(readbuf));
390         if (nread < 0)
391                 goto bad;
392         if (nread == 0) {
393                 /* end of transmission... this may be normal
394                  * or may be an error... in some cases we can't
395                  * tell which so we must assume it is normal then.
396                  */
397                 if (!h->got_hdr) {
398                         /* Must at least have completed header */
399                         wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
400                         goto bad;
401                 }
402                 if (h->chunked || h->got_content_length) {
403                         /* Premature EOF; e.g. dropped connection */
404                         wpa_printf(MSG_DEBUG,
405                                    "httpread premature eof(%p) %d/%d",
406                                    h, h->body_nbytes,
407                                    h->content_length);
408                         goto bad;
409                 }
410                 /* No explicit length, hopefully we have all the data
411                  * although dropped connections can cause false
412                  * end
413                  */
414                 if (httpread_debug >= 10)
415                         wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
416                         h->got_body = 1;
417                         goto got_file;
418         }
419         rbp = readbuf;
420
421         /* Header consists of text lines (terminated by both CR and LF)
422          * and an empty line (CR LF only).
423          */
424         if (!h->got_hdr) {
425                 hbp = h->hdr + h->hdr_nbytes;
426                 /* add to headers until:
427                  *      -- we run out of data in read buffer
428                  *      -- or, we run out of header buffer room
429                  *      -- or, we get double CRLF in headers
430                  */
431                 for (;;) {
432                         if (nread == 0)
433                                 goto get_more;
434                         if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
435                                 goto bad;
436                         }
437                         *hbp++ = *rbp++;
438                         nread--;
439                         h->hdr_nbytes++;
440                         if (h->hdr_nbytes >= 4 &&
441                             hbp[-1] == '\n' &&
442                             hbp[-2] == '\r' &&
443                             hbp[-3] == '\n' &&
444                             hbp[-4] == '\r' ) {
445                                 h->got_hdr = 1;
446                                 *hbp = 0;       /* null terminate */
447                                 break;
448                         }
449                 }
450                 /* here we've just finished reading the header */
451                 if (httpread_hdr_analyze(h)) {
452                         wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
453                         goto bad;
454                 }
455                 if (h->max_bytes == 0) {
456                         if (httpread_debug >= 10)
457                                 wpa_printf(MSG_DEBUG,
458                                            "httpread no body hdr end(%p)", h);
459                         goto got_file;
460                 }
461                 if (h->got_content_length && h->content_length == 0) {
462                         if (httpread_debug >= 10)
463                                 wpa_printf(MSG_DEBUG,
464                                            "httpread zero content length(%p)",
465                                            h);
466                         goto got_file;
467                 }
468         }
469
470         /* Certain types of requests never have data and so
471          * must be specially recognized.
472          */
473         if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
474             !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
475             !os_strncasecmp(h->hdr, "HEAD", 4) ||
476             !os_strncasecmp(h->hdr, "GET", 3)) {
477                 if (!h->got_body) {
478                         if (httpread_debug >= 10)
479                                 wpa_printf(MSG_DEBUG,
480                                            "httpread NO BODY for sp. type");
481                 }
482                 h->got_body = 1;
483                 goto got_file;
484         }
485
486         /* Data can be just plain binary data, or if "chunked"
487          * consists of chunks each with a header, ending with
488          * an ending header.
489          */
490         if (nread == 0)
491                 goto get_more;
492         if (!h->got_body) {
493                 /* Here to get (more of) body */
494                 /* ensure we have enough room for worst case for body
495                  * plus a null termination character
496                  */
497                 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
498                         char *new_body;
499                         int new_alloc_nbytes;
500
501                         if (h->body_nbytes >= h->max_bytes)
502                                 goto bad;
503                         new_alloc_nbytes = h->body_alloc_nbytes +
504                                 HTTPREAD_BODYBUF_DELTA;
505                         /* For content-length case, the first time
506                          * through we allocate the whole amount
507                          * we need.
508                          */
509                         if (h->got_content_length &&
510                             new_alloc_nbytes < (h->content_length + 1))
511                                 new_alloc_nbytes = h->content_length + 1;
512                         if ((new_body = os_realloc(h->body, new_alloc_nbytes))
513                             == NULL)
514                                 goto bad;
515
516                         h->body = new_body;
517                         h->body_alloc_nbytes = new_alloc_nbytes;
518                 }
519                 /* add bytes */
520                 bbp = h->body + h->body_nbytes;
521                 for (;;) {
522                         int ncopy;
523                         /* See if we need to stop */
524                         if (h->chunked && h->in_chunk_data == 0) {
525                                 /* in chunk header */
526                                 char *cbp = h->body + h->chunk_start;
527                                 if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
528                                     bbp[-1] == '\n') {
529                                         /* end of chunk hdr line */
530                                         /* hdr line consists solely
531                                          * of a hex numeral and CFLF
532                                          */
533                                         if (!isxdigit(*cbp))
534                                                 goto bad;
535                                         h->chunk_size = strtoul(cbp, NULL, 16);
536                                         /* throw away chunk header
537                                          * so we have only real data
538                                          */
539                                         h->body_nbytes = h->chunk_start;
540                                         bbp = cbp;
541                                         if (h->chunk_size == 0) {
542                                                 /* end of chunking */
543                                                 /* trailer follows */
544                                                 h->in_trailer = 1;
545                                                 if (httpread_debug >= 20)
546                                                         wpa_printf(
547                                                                 MSG_DEBUG,
548                                                                 "httpread end chunks(%p)", h);
549                                                 break;
550                                         }
551                                         h->in_chunk_data = 1;
552                                         /* leave chunk_start alone */
553                                 }
554                         } else if (h->chunked) {
555                                 /* in chunk data */
556                                 if ((h->body_nbytes - h->chunk_start) ==
557                                     (h->chunk_size + 2)) {
558                                         /* end of chunk reached,
559                                          * new chunk starts
560                                          */
561                                         /* check chunk ended w/ CRLF
562                                          * which we'll throw away
563                                          */
564                                         if (bbp[-1] == '\n' &&
565                                             bbp[-2] == '\r') {
566                                         } else
567                                                 goto bad;
568                                         h->body_nbytes -= 2;
569                                         bbp -= 2;
570                                         h->chunk_start = h->body_nbytes;
571                                         h->in_chunk_data = 0;
572                                         h->chunk_size = 0; /* just in case */
573                                 }
574                         } else if (h->got_content_length &&
575                                    h->body_nbytes >= h->content_length) {
576                                 h->got_body = 1;
577                                 if (httpread_debug >= 10)
578                                         wpa_printf(
579                                                 MSG_DEBUG,
580                                                 "httpread got content(%p)", h);
581                                 goto got_file;
582                         }
583                         if (nread <= 0)
584                                 break;
585                         /* Now transfer. Optimize using memcpy where we can. */
586                         if (h->chunked && h->in_chunk_data) {
587                                 /* copy up to remainder of chunk data
588                                  * plus the required CR+LF at end
589                                  */
590                                 ncopy = (h->chunk_start + h->chunk_size + 2) -
591                                         h->body_nbytes;
592                         } else if (h->chunked) {
593                                 /*in chunk header -- don't optimize */
594                                 *bbp++ = *rbp++;
595                                 nread--;
596                                 h->body_nbytes++;
597                                 continue;
598                         } else if (h->got_content_length) {
599                                 ncopy = h->content_length - h->body_nbytes;
600                         } else {
601                                 ncopy = nread;
602                         }
603                         /* Note: should never be 0 */
604                         if (ncopy > nread)
605                                 ncopy = nread;
606                         os_memcpy(bbp, rbp, ncopy);
607                         bbp += ncopy;
608                         h->body_nbytes += ncopy;
609                         rbp += ncopy;
610                         nread -= ncopy;
611                 }       /* body copy loop */
612         }       /* !got_body */
613         if (h->chunked && h->in_trailer) {
614                 /* If "chunked" then there is always a trailer,
615                  * consisting of zero or more non-empty lines
616                  * ending with CR LF and then an empty line w/ CR LF.
617                  * We do NOT support trailers except to skip them --
618                  * this is supported (generally) by the http spec.
619                  */
620                 bbp = h->body + h->body_nbytes;
621                 for (;;) {
622                         int c;
623                         if (nread <= 0)
624                                 break;
625                         c = *rbp++;
626                         nread--;
627                         switch (h->trailer_state) {
628                         case trailer_line_begin:
629                                 if (c == '\r')
630                                         h->trailer_state = trailer_empty_cr;
631                                 else
632                                         h->trailer_state = trailer_nonempty;
633                                 break;
634                         case trailer_empty_cr:
635                                 /* end empty line */
636                                 if (c == '\n') {
637                                         h->trailer_state = trailer_line_begin;
638                                         h->in_trailer = 0;
639                                         if (httpread_debug >= 10)
640                                                 wpa_printf(
641                                                         MSG_DEBUG,
642                                                         "httpread got content(%p)", h);
643                                         h->got_body = 1;
644                                         goto got_file;
645                                 }
646                                 h->trailer_state = trailer_nonempty;
647                                 break;
648                         case trailer_nonempty:
649                                 if (c == '\r')
650                                         h->trailer_state = trailer_nonempty_cr;
651                                 break;
652                         case trailer_nonempty_cr:
653                                 if (c == '\n')
654                                         h->trailer_state = trailer_line_begin;
655                                 else
656                                         h->trailer_state = trailer_nonempty;
657                                 break;
658                         }
659                 }
660         }
661         goto get_more;
662
663 bad:
664         /* Error */
665         wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
666         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
667         return;
668
669 get_more:
670         return;
671
672 got_file:
673         if (httpread_debug >= 10)
674                 wpa_printf(MSG_DEBUG,
675                            "httpread got file %d bytes type %d",
676                            h->body_nbytes, h->hdr_type);
677         /* Null terminate for convenience of some applications */
678         if (h->body)
679                 h->body[h->body_nbytes] = 0; /* null terminate */
680         h->got_file = 1;
681         /* Assume that we do NOT support keeping connection alive,
682          * and just in case somehow we don't get destroyed right away,
683          * unregister now.
684          */
685         eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
686         /* The application can destroy us whenever they feel like...
687          * cancel timeout.
688          */
689         eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
690         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
691 }
692
693
694 /* httpread_create -- start a new reading session making use of eloop.
695  * The new instance will use the socket descriptor for reading (until
696  * it gets a file and not after) but will not close the socket, even
697  * when the instance is destroyed (the application must do that).
698  * Return NULL on error.
699  *
700  * Provided that httpread_create successfully returns a handle,
701  * the callback fnc is called to handle httpread_event events.
702  * The caller should do destroy on any errors or unknown events.
703  *
704  * Pass max_bytes == 0 to not read body at all (required for e.g.
705  * reply to HEAD request).
706  */
707 struct httpread * httpread_create(
708         int sd,  /* descriptor of TCP socket to read from */
709         void (*cb)(struct httpread *handle, void *cookie,
710                    enum httpread_event e),  /* call on event */
711         void *cookie,    /* pass to callback */
712         int max_bytes,    /* maximum body size else abort it */
713         int timeout_seconds     /* 0; or total duration timeout period */
714         )
715 {
716         struct httpread *h = NULL;
717
718         h = os_zalloc(sizeof(*h));
719         if (h == NULL)
720                 goto fail;
721         h->sd = sd;
722         h->cb = cb;
723         h->cookie = cookie;
724         h->max_bytes = max_bytes;
725         h->timeout_seconds = timeout_seconds;
726
727         if (timeout_seconds > 0 &&
728             eloop_register_timeout(timeout_seconds, 0,
729                                    httpread_timeout_handler, NULL, h)) {
730                 /* No way to recover (from malloc failure) */
731                 goto fail;
732         }
733         if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
734                                 NULL, h)) {
735                 /* No way to recover (from malloc failure) */
736                 goto fail;
737         }
738         return h;
739
740 fail:
741
742         /* Error */
743         httpread_destroy(h);
744         return NULL;
745 }
746
747
748 /* httpread_hdr_type_get -- When file is ready, returns header type. */
749 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
750 {
751         return h->hdr_type;
752 }
753
754
755 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
756  * or possibly NULL (which would be an error).
757  */
758 char * httpread_uri_get(struct httpread *h)
759 {
760         return h->uri;
761 }
762
763
764 /* httpread_reply_code_get -- When reply is ready, returns reply code */
765 int httpread_reply_code_get(struct httpread *h)
766 {
767         return h->reply_code;
768 }
769
770
771 /* httpread_length_get -- When file is ready, returns file length. */
772 int httpread_length_get(struct httpread *h)
773 {
774         return h->body_nbytes;
775 }
776
777
778 /* httpread_data_get -- When file is ready, returns file content
779  * with null byte appened.
780  * Might return NULL in some error condition.
781  */
782 void * httpread_data_get(struct httpread *h)
783 {
784         return h->body ? h->body : "";
785 }
786
787
788 /* httpread_hdr_get -- When file is ready, returns header content
789  * with null byte appended.
790  * Might return NULL in some error condition.
791  */
792 char * httpread_hdr_get(struct httpread *h)
793 {
794         return h->hdr;
795 }
796
797
798 /* httpread_hdr_line_get -- When file is ready, returns pointer
799  * to line within header content matching the given tag
800  * (after the tag itself and any spaces/tabs).
801  *
802  * The tag should end with a colon for reliable matching.
803  *
804  * If not found, returns NULL;
805  */
806 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
807 {
808         int tag_len = os_strlen(tag);
809         char *hdr = h->hdr;
810         hdr = os_strchr(hdr, '\n');
811         if (hdr == NULL)
812                 return NULL;
813         hdr++;
814         for (;;) {
815                 if (!os_strncasecmp(hdr, tag, tag_len)) {
816                         hdr += tag_len;
817                         while (*hdr == ' ' || *hdr == '\t')
818                                 hdr++;
819                         return hdr;
820                 }
821                 hdr = os_strchr(hdr, '\n');
822                 if (hdr == NULL)
823                         return NULL;
824                 hdr++;
825         }
826 }