Add missing includes
[contiki-2.x.git] / apps / webbrowser / webclient.c
blob5f8f93fa91898709caa2392d80fe1b3c3b32a18c
1 /*
2 * Copyright (c) 2002, Adam Dunkels.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following
12 * disclaimer in the documentation and/or other materials provided
13 * with the distribution.
14 * 3. The name of the author may not be used to endorse or promote
15 * products derived from this software without specific prior
16 * written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * This file is part of the "contiki" web browser.
32 * $Id: webclient.c,v 1.11 2010/10/19 18:29:03 adamdunkels Exp $
36 #include <string.h>
38 #include "contiki-net.h"
39 #include "www.h"
41 #include "webclient.h"
43 #define WEBCLIENT_TIMEOUT 100
45 #define WEBCLIENT_STATE_STATUSLINE 0
46 #define WEBCLIENT_STATE_HEADERS 1
47 #define WEBCLIENT_STATE_DATA 2
48 #define WEBCLIENT_STATE_CLOSE 3
50 #define HTTPFLAG_NONE 0
51 #define HTTPFLAG_OK 1
52 #define HTTPFLAG_MOVED 2
53 #define HTTPFLAG_ERROR 3
56 #define ISO_nl 0x0a
57 #define ISO_cr 0x0d
58 #define ISO_space 0x20
60 struct webclient_state {
61 u8_t timer;
62 u8_t state;
63 u8_t httpflag;
65 u16_t port;
66 char host[40];
67 char file[WWW_CONF_MAX_URLLEN];
68 u16_t getrequestptr;
69 u16_t getrequestleft;
71 char httpheaderline[200];
72 u16_t httpheaderlineptr;
74 char mimetype[32];
77 static struct webclient_state s;
79 /*-----------------------------------------------------------------------------------*/
80 char *
81 webclient_mimetype(void)
83 return s.mimetype;
85 /*-----------------------------------------------------------------------------------*/
86 char *
87 webclient_filename(void)
89 return s.file;
91 /*-----------------------------------------------------------------------------------*/
92 char *
93 webclient_hostname(void)
95 return s.host;
97 /*-----------------------------------------------------------------------------------*/
98 unsigned short
99 webclient_port(void)
101 return s.port;
103 /*-----------------------------------------------------------------------------------*/
104 void
105 webclient_init(void)
109 /*-----------------------------------------------------------------------------------*/
110 static void
111 init_connection(void)
113 s.state = WEBCLIENT_STATE_STATUSLINE;
115 s.getrequestleft = sizeof(http_get) - 1 + 1 +
116 sizeof(http_10) - 1 +
117 sizeof(http_crnl) - 1 +
118 sizeof(http_host) - 1 +
119 sizeof(http_crnl) - 1 +
120 (u16_t)strlen(http_user_agent_fields) +
121 (u16_t)strlen(s.file) + (u16_t)strlen(s.host);
122 s.getrequestptr = 0;
124 s.httpheaderlineptr = 0;
126 /*-----------------------------------------------------------------------------------*/
127 void
128 webclient_close(void)
130 s.state = WEBCLIENT_STATE_CLOSE;
132 /*-----------------------------------------------------------------------------------*/
133 unsigned char
134 webclient_get(const char *host, u16_t port, const char *file)
136 uip_ipaddr_t addr;
137 struct uip_conn *conn;
138 uip_ipaddr_t *ipaddr;
140 /* First check if the host is an IP address. */
141 ipaddr = &addr;
142 if(uiplib_ipaddrconv(host, &addr) == 0) {
143 #if UIP_UDP
144 ipaddr = resolv_lookup(host);
146 if(ipaddr == NULL) {
147 return 0;
149 #else /* UIP_UDP */
150 return 0;
151 #endif /* UIP_UDP */
154 conn = tcp_connect(ipaddr, uip_htons(port), NULL);
156 if(conn == NULL) {
157 return 0;
160 s.port = port;
161 strncpy(s.file, file, sizeof(s.file));
162 strncpy(s.host, host, sizeof(s.host));
164 init_connection();
165 return 1;
167 /*-----------------------------------------------------------------------------------*/
168 /* Copy data into a "window", specified by the windowstart and
169 windowend variables. Only data that fits within the window is
170 copied. This function is used to copy data into the uIP buffer, which
171 typically is smaller than the data that is to be copied.
173 static unsigned char *windowptr;
174 static int windowstart, windowend;
175 static int
176 window_copy(int curptr, const char *data, unsigned char datalen)
178 int len;
180 if(windowstart == windowend) {
181 return curptr + datalen;
184 if(curptr + datalen < windowstart) {
185 /* If all the data is before the window, we do not copy the
186 data. */
187 return curptr + datalen;
190 if(curptr > windowend) {
191 /* If all the data is after the window, we do not copy the data. */
192 return curptr + datalen;
195 len = datalen;
197 /* Trim off data before the window. */
198 data += windowstart - curptr;
199 len -= windowstart - curptr;
201 /* Trim off data after the window. */
202 if(len > windowend - windowstart) {
203 len = windowend - windowstart;
206 strncpy(windowptr + windowstart, data, len);
207 windowstart += len;
209 return curptr + datalen;
211 /*-----------------------------------------------------------------------------------*/
212 static void
213 senddata(void)
215 u16_t len;
216 int curptr;
218 if(s.getrequestleft > 0) {
220 windowstart = s.getrequestptr;
221 curptr = 0;
222 windowend = windowstart + uip_mss();
223 windowptr = (char *)uip_appdata - windowstart;
225 curptr = window_copy(curptr, http_get, sizeof(http_get) - 1);
226 curptr = window_copy(curptr, s.file, (unsigned char)strlen(s.file));
227 curptr = window_copy(curptr, " ", 1);
228 curptr = window_copy(curptr, http_10, sizeof(http_10) - 1);
230 curptr = window_copy(curptr, http_crnl, sizeof(http_crnl) - 1);
232 curptr = window_copy(curptr, http_host, sizeof(http_host) - 1);
233 curptr = window_copy(curptr, s.host, (unsigned char)strlen(s.host));
234 curptr = window_copy(curptr, http_crnl, sizeof(http_crnl) - 1);
236 curptr = window_copy(curptr, http_user_agent_fields,
237 (unsigned char)strlen(http_user_agent_fields));
239 len = s.getrequestleft > uip_mss()?
240 uip_mss():
241 s.getrequestleft;
242 uip_send(uip_appdata, len);
245 /*-----------------------------------------------------------------------------------*/
246 static void
247 acked(void)
249 u16_t len;
251 if(s.getrequestleft > 0) {
252 len = s.getrequestleft > uip_mss()?
253 uip_mss():
254 s.getrequestleft;
255 s.getrequestleft -= len;
256 s.getrequestptr += len;
259 /*-----------------------------------------------------------------------------------*/
260 static u16_t
261 parse_statusline(u16_t len)
263 char *cptr;
265 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
266 s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
267 uip_appdata = (char *)uip_appdata + 1;
268 --len;
269 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
271 if((strncmp(s.httpheaderline, http_10,
272 sizeof(http_10) - 1) == 0) ||
273 (strncmp(s.httpheaderline, http_11,
274 sizeof(http_11) - 1) == 0)) {
275 cptr = &(s.httpheaderline[9]);
276 s.httpflag = HTTPFLAG_NONE;
277 if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
278 /* 200 OK */
279 s.httpflag = HTTPFLAG_OK;
280 } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
281 strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
282 /* 301 Moved permanently or 302 Found. Location: header line
283 will contain thw new location. */
284 s.httpflag = HTTPFLAG_MOVED;
285 } else {
286 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
288 } else {
289 uip_abort();
290 webclient_aborted();
291 return 0;
294 /* We're done parsing the status line, so we reset the pointer
295 and start parsing the HTTP headers.*/
296 s.httpheaderlineptr = 0;
297 s.state = WEBCLIENT_STATE_HEADERS;
298 break;
299 } else {
300 ++s.httpheaderlineptr;
303 return len;
305 /*-----------------------------------------------------------------------------------*/
306 static char
307 casecmp(char *str1, const char *str2, char len)
309 static char c;
311 while(len > 0) {
312 c = *str1;
313 /* Force lower-case characters. */
314 if(c & 0x40) {
315 c |= 0x20;
317 if(*str2 != c) {
318 return 1;
320 ++str1;
321 ++str2;
322 --len;
324 return 0;
326 /*-----------------------------------------------------------------------------------*/
327 static u16_t
328 parse_headers(u16_t len)
330 char *cptr;
331 static unsigned char i;
333 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
334 s.httpheaderline[s.httpheaderlineptr] = *(char *)uip_appdata;
335 uip_appdata = (char *)uip_appdata + 1;
336 --len;
337 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
338 /* We have an entire HTTP header line in s.httpheaderline, so
339 we parse it. */
340 if(s.httpheaderline[0] == ISO_cr) {
341 /* This was the last header line (i.e., and empty "\r\n"), so
342 we are done with the headers and proceed with the actual
343 data. */
344 s.state = WEBCLIENT_STATE_DATA;
345 return len;
348 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
349 /* Check for specific HTTP header fields. */
350 if(casecmp(s.httpheaderline, http_content_type,
351 sizeof(http_content_type) - 1) == 0) {
352 /* Found Content-type field. */
353 cptr = strchr(s.httpheaderline, ';');
354 if(cptr != NULL) {
355 *cptr = 0;
357 strncpy(s.mimetype, s.httpheaderline +
358 sizeof(http_content_type) - 1, sizeof(s.mimetype));
359 } else if(casecmp(s.httpheaderline, http_location,
360 sizeof(http_location) - 1) == 0) {
361 cptr = s.httpheaderline +
362 sizeof(http_location) - 1;
364 if(strncmp(cptr, http_http, 7) == 0) {
365 cptr += 7;
366 for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
367 if(*cptr == 0 ||
368 *cptr == '/' ||
369 *cptr == ' ' ||
370 *cptr == ':') {
371 s.host[i] = 0;
372 break;
374 s.host[i] = *cptr;
375 ++cptr;
378 strncpy(s.file, cptr, sizeof(s.file));
379 /* s.file[s.httpheaderlineptr - i] = 0;*/
383 /* We're done parsing, so we reset the pointer and start the
384 next line. */
385 s.httpheaderlineptr = 0;
386 } else {
387 ++s.httpheaderlineptr;
390 return len;
392 /*-----------------------------------------------------------------------------------*/
393 static void
394 newdata(void)
396 u16_t len;
398 len = uip_datalen();
400 if(s.state == WEBCLIENT_STATE_STATUSLINE) {
401 len = parse_statusline(len);
404 if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
405 len = parse_headers(len);
408 if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
409 s.httpflag != HTTPFLAG_MOVED) {
410 webclient_datahandler((char *)uip_appdata, len);
413 /*-----------------------------------------------------------------------------------*/
414 void
415 webclient_appcall(void *state)
417 char *dataptr;
419 if(uip_connected()) {
420 s.timer = 0;
421 s.state = WEBCLIENT_STATE_STATUSLINE;
422 senddata();
423 webclient_connected();
424 tcp_markconn(uip_conn, &s);
425 return;
428 if(uip_timedout()) {
429 webclient_timedout();
432 if(uip_aborted()) {
433 webclient_aborted();
436 if(state == NULL) {
437 uip_abort();
438 return;
441 if(s.state == WEBCLIENT_STATE_CLOSE) {
442 webclient_closed();
443 uip_abort();
444 return;
448 /* The acked() and newdata() functions may alter the uip_appdata
449 ptr, so we need to store it in the "dataptr" variable so that we
450 can restore it before the senddata() function is called. */
451 dataptr = uip_appdata;
453 if(uip_acked()) {
454 s.timer = 0;
455 acked();
457 if(uip_newdata()) {
458 s.timer = 0;
459 newdata();
462 uip_appdata = dataptr;
464 if(uip_rexmit() ||
465 uip_newdata() ||
466 uip_acked()) {
467 senddata();
468 } else if(uip_poll()) {
469 ++s.timer;
470 if(s.timer == WEBCLIENT_TIMEOUT) {
471 webclient_timedout();
472 uip_abort();
473 return;
475 /* senddata();*/
478 if(uip_closed()) {
479 tcp_markconn(uip_conn, NULL);
480 if(s.httpflag != HTTPFLAG_MOVED) {
481 /* Send NULL data to signal EOF. */
482 webclient_datahandler(NULL, 0);
483 } else {
484 /* conn = uip_connect(uip_conn->ripaddr, s.port);
485 if(conn != NULL) {
486 dispatcher_markconn(conn, NULL);
487 init_connection();
489 #if UIP_UDP
490 if(resolv_lookup(s.host) == NULL) {
491 resolv_query(s.host);
493 #endif /* UIP_UDP */
494 webclient_get(s.host, s.port, s.file);
498 /*-----------------------------------------------------------------------------------*/