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