blob: a6cd44555d5bfdd7f9243ad78b769fc5335269a1 [file] [log] [blame]
adamdunkels3023dee2003-07-04 10:54:51 +00001/*
2 * Copyright (c) 2002, Adam Dunkels.
3 * All rights reserved.
4 *
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.
adamdunkelsd311bf12004-07-04 20:17:37 +000014 * 3. The name of the author may not be used to endorse or promote
adamdunkels3023dee2003-07-04 10:54:51 +000015 * products derived from this software without specific prior
16 * written permission.
17 *
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.
29 *
30 * This file is part of the "contiki" web browser.
31 *
adamdunkels0ebd76c2004-08-09 22:25:36 +000032 * $Id: webclient.c,v 1.5 2004/08/09 22:25:36 adamdunkels Exp $
adamdunkels3023dee2003-07-04 10:54:51 +000033 *
34 */
35
adamdunkels0ebd76c2004-08-09 22:25:36 +000036#include "ek.h"
37#include "tcpip.h"
adamdunkels3023dee2003-07-04 10:54:51 +000038#include "uip.h"
39#include "webclient.h"
40#include "resolv.h"
adamdunkelsd311bf12004-07-04 20:17:37 +000041#include "uiplib.h"
adamdunkels3023dee2003-07-04 10:54:51 +000042
adamdunkels37b236a2003-08-25 12:38:41 +000043#include "www-conf.h"
44
adamdunkels3023dee2003-07-04 10:54:51 +000045#include <string.h>
46
47#define WEBCLIENT_TIMEOUT 100
48
49#define WEBCLIENT_STATE_STATUSLINE 0
50#define WEBCLIENT_STATE_HEADERS 1
51#define WEBCLIENT_STATE_DATA 2
52#define WEBCLIENT_STATE_CLOSE 3
53
54#define HTTPFLAG_NONE 0
55#define HTTPFLAG_OK 1
56#define HTTPFLAG_MOVED 2
57#define HTTPFLAG_ERROR 3
58
59
60#define ISO_nl 0x0a
61#define ISO_cr 0x0d
62#define ISO_space 0x20
63
64struct webclient_state {
65 u8_t timer;
66 u8_t state;
67 u8_t httpflag;
68
69 u16_t port;
70 char host[40];
adamdunkels37b236a2003-08-25 12:38:41 +000071 char file[WWW_CONF_MAX_URLLEN];
adamdunkels3023dee2003-07-04 10:54:51 +000072 u16_t getrequestptr;
73 u16_t getrequestleft;
74
75 char httpheaderline[200];
76 u16_t httpheaderlineptr;
77
78 char mimetype[32];
79};
80
81static struct webclient_state s;
82
83/*-----------------------------------------------------------------------------------*/
84char *
85webclient_mimetype(void)
86{
87 return s.mimetype;
88}
89/*-----------------------------------------------------------------------------------*/
90char *
91webclient_filename(void)
92{
93 return s.file;
94}
95/*-----------------------------------------------------------------------------------*/
96char *
97webclient_hostname(void)
98{
99 return s.host;
100}
101/*-----------------------------------------------------------------------------------*/
102unsigned short
103webclient_port(void)
104{
105 return s.port;
106}
107/*-----------------------------------------------------------------------------------*/
108void
109webclient_init(void)
110{
111
112}
113/*-----------------------------------------------------------------------------------*/
114static void
115init_connection(void)
116{
117 s.state = WEBCLIENT_STATE_STATUSLINE;
118
119 s.getrequestleft = sizeof(http_get) - 1 + 1 +
120 sizeof(http_10) - 1 +
121 sizeof(http_crnl) - 1 +
122 sizeof(http_host) - 1 +
123 sizeof(http_crnl) - 1 +
124 strlen(http_user_agent_fields) +
125 strlen(s.file) + strlen(s.host);
126 s.getrequestptr = 0;
127
128 s.httpheaderlineptr = 0;
129}
130/*-----------------------------------------------------------------------------------*/
131void
132webclient_close(void)
133{
134 s.state = WEBCLIENT_STATE_CLOSE;
135}
136/*-----------------------------------------------------------------------------------*/
137unsigned char
138webclient_get(char *host, u16_t port, char *file)
139{
140 struct uip_conn *conn;
141 u16_t *ipaddr;
adamdunkels37b236a2003-08-25 12:38:41 +0000142 static u16_t addr[2];
adamdunkels3023dee2003-07-04 10:54:51 +0000143
144 /* First check if the host is an IP address. */
145 ipaddr = &addr[0];
adamdunkelsd311bf12004-07-04 20:17:37 +0000146 if(uiplib_ipaddrconv(host, (unsigned char *)addr) == 0) {
adamdunkels3023dee2003-07-04 10:54:51 +0000147 ipaddr = resolv_lookup(host);
148
149 if(ipaddr == NULL) {
150 return 0;
151 }
152 }
adamdunkels0ebd76c2004-08-09 22:25:36 +0000153
adamdunkels3023dee2003-07-04 10:54:51 +0000154 /* XXX: here we check so that the server does not try to access any
155 hosts on the SICS networks. */
adamdunkels0ebd76c2004-08-09 22:25:36 +0000156 if(!(ipaddr[0] == HTONS((193 << 8) | 10) &&
157 ipaddr[1] == HTONS((64 << 8) | 99)) &&
158 (ipaddr[0] == HTONS((193 << 8) | 10) &&
159 ((htons(ipaddr[1]) >> 8) == 66 ||
160 (htons(ipaddr[1]) >> 8) == 67))) {
adamdunkels3023dee2003-07-04 10:54:51 +0000161 return 0;
162 } else {
adamdunkelsd311bf12004-07-04 20:17:37 +0000163 conn = tcp_connect(ipaddr, htons(port), NULL);
adamdunkels3023dee2003-07-04 10:54:51 +0000164 }
165
166 if(conn == NULL) {
167 return 0;
168 }
169
adamdunkels3023dee2003-07-04 10:54:51 +0000170 s.port = port;
171 strncpy(s.file, file, sizeof(s.file));
172 strncpy(s.host, host, sizeof(s.host));
173
174 init_connection();
175 return 1;
176}
177/*-----------------------------------------------------------------------------------*/
adamdunkels37b236a2003-08-25 12:38:41 +0000178static unsigned char * CC_FASTCALL
179copy_string(unsigned char *dest,
adamdunkels0ebd76c2004-08-09 22:25:36 +0000180 const unsigned char *src, unsigned char len)
adamdunkels3023dee2003-07-04 10:54:51 +0000181{
182 return strcpy(dest, src) + len;
183}
184/*-----------------------------------------------------------------------------------*/
185static void
186senddata(void)
187{
188 u16_t len;
189 char *getrequest;
190 char *cptr;
191
192 if(s.getrequestleft > 0) {
193 cptr = getrequest = (char *)uip_appdata;
194
195 cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
196 cptr = copy_string(cptr, s.file, strlen(s.file));
197 *cptr++ = ISO_space;
198 cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
199
200 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
201
202 cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
203 cptr = copy_string(cptr, s.host, strlen(s.host));
204 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
205
206 cptr = copy_string(cptr, http_user_agent_fields,
207 strlen(http_user_agent_fields));
208
209 len = s.getrequestleft > uip_mss()?
210 uip_mss():
211 s.getrequestleft;
212 uip_send(&(getrequest[s.getrequestptr]), len);
213 }
214}
215/*-----------------------------------------------------------------------------------*/
216static void
217acked(void)
218{
219 u16_t len;
220
221 if(s.getrequestleft > 0) {
222 len = s.getrequestleft > uip_mss()?
223 uip_mss():
224 s.getrequestleft;
225 s.getrequestleft -= len;
226 s.getrequestptr += len;
227 }
228}
229/*-----------------------------------------------------------------------------------*/
230static u16_t
231parse_statusline(u16_t len)
232{
233 char *cptr;
234
235 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
236 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
237 ++uip_appdata;
238 --len;
239 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
240
241 if((strncmp(s.httpheaderline, http_10,
242 sizeof(http_10) - 1) == 0) ||
243 (strncmp(s.httpheaderline, http_11,
244 sizeof(http_11) - 1) == 0)) {
245 cptr = &(s.httpheaderline[9]);
246 s.httpflag = HTTPFLAG_NONE;
247 if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
248 /* 200 OK */
249 s.httpflag = HTTPFLAG_OK;
250 } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
251 strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
252 /* 301 Moved permanently or 302 Found. Location: header line
253 will contain thw new location. */
254 s.httpflag = HTTPFLAG_MOVED;
255 } else {
256 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
257 }
258 } else {
259 uip_abort();
260 webclient_aborted();
261 return 0;
262 }
263
264 /* We're done parsing the status line, so we reset the pointer
265 and start parsing the HTTP headers.*/
266 s.httpheaderlineptr = 0;
267 s.state = WEBCLIENT_STATE_HEADERS;
268 break;
269 } else {
270 ++s.httpheaderlineptr;
271 }
272 }
273 return len;
274}
275/*-----------------------------------------------------------------------------------*/
adamdunkels37b236a2003-08-25 12:38:41 +0000276static char
adamdunkels0ebd76c2004-08-09 22:25:36 +0000277casecmp(char *str1, const char *str2, char len)
adamdunkels37b236a2003-08-25 12:38:41 +0000278{
279 static char c;
280
281 while(len > 0) {
282 c = *str1;
283 /* Force lower-case characters. */
284 if(c & 0x40) {
285 c |= 0x20;
286 }
287 if(*str2 != c) {
288 return 1;
289 }
290 ++str1;
291 ++str2;
292 --len;
293 }
294 return 0;
295}
296/*-----------------------------------------------------------------------------------*/
adamdunkels3023dee2003-07-04 10:54:51 +0000297static u16_t
298parse_headers(u16_t len)
299{
300 char *cptr;
adamdunkels0ebd76c2004-08-09 22:25:36 +0000301 static unsigned char i;
adamdunkels3023dee2003-07-04 10:54:51 +0000302
303 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
304 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
305 ++uip_appdata;
306 --len;
307 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
308 /* We have an entire HTTP header line in s.httpheaderline, so
309 we parse it. */
310 if(s.httpheaderline[0] == ISO_cr) {
311 /* This was the last header line (i.e., and empty "\r\n"), so
312 we are done with the headers and proceed with the actual
313 data. */
314 s.state = WEBCLIENT_STATE_DATA;
315 return len;
316 }
317
318 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
adamdunkels37b236a2003-08-25 12:38:41 +0000319 /* Check for specific HTTP header fields. */
320 if(casecmp(s.httpheaderline, http_content_type,
321 sizeof(http_content_type) - 1) == 0) {
adamdunkels3023dee2003-07-04 10:54:51 +0000322 /* Found Content-type field. */
323 cptr = strchr(s.httpheaderline, ';');
324 if(cptr != NULL) {
325 *cptr = 0;
326 }
327 strncpy(s.mimetype, s.httpheaderline +
328 sizeof(http_content_type) - 1, sizeof(s.mimetype));
adamdunkels37b236a2003-08-25 12:38:41 +0000329 } else if(casecmp(s.httpheaderline, http_location,
adamdunkels3023dee2003-07-04 10:54:51 +0000330 sizeof(http_location) - 1) == 0) {
adamdunkels0ebd76c2004-08-09 22:25:36 +0000331 cptr = s.httpheaderline +
332 sizeof(http_location) - 1;
333
334 if(strncmp(cptr, http_http, 7) == 0) {
335 cptr += 7;
336 for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
337 if(*cptr == 0 ||
338 *cptr == '/' ||
339 *cptr == ' ' ||
340 *cptr == ':') {
341 s.host[i] = 0;
342 break;
343 }
344 s.host[i] = *cptr;
345 ++cptr;
346 }
347 }
348 strncpy(s.file, cptr, sizeof(s.file));
349 /* s.file[s.httpheaderlineptr - i] = 0;*/
adamdunkels3023dee2003-07-04 10:54:51 +0000350 }
351
352
353 /* We're done parsing, so we reset the pointer and start the
354 next line. */
355 s.httpheaderlineptr = 0;
356 } else {
357 ++s.httpheaderlineptr;
358 }
359 }
360 return len;
361}
362/*-----------------------------------------------------------------------------------*/
363static void
364newdata(void)
365{
366 u16_t len;
367
368 len = uip_datalen();
369
370 if(s.state == WEBCLIENT_STATE_STATUSLINE) {
371 len = parse_statusline(len);
372 }
373
374 if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
375 len = parse_headers(len);
376 }
377
378 if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
379 s.httpflag != HTTPFLAG_MOVED) {
380 webclient_datahandler((char *)uip_appdata, len);
381 }
382}
383/*-----------------------------------------------------------------------------------*/
adamdunkelsd311bf12004-07-04 20:17:37 +0000384void
385webclient_appcall(void *state)
adamdunkels3023dee2003-07-04 10:54:51 +0000386{
adamdunkels3023dee2003-07-04 10:54:51 +0000387 if(uip_connected()) {
388 s.timer = 0;
389 s.state = WEBCLIENT_STATE_STATUSLINE;
390 senddata();
391 webclient_connected();
adamdunkelsd311bf12004-07-04 20:17:37 +0000392 tcp_markconn(uip_conn, &s);
adamdunkels3023dee2003-07-04 10:54:51 +0000393 return;
394 }
395
adamdunkels0ebd76c2004-08-09 22:25:36 +0000396 if(uip_timedout()) {
397 webclient_timedout();
398 }
adamdunkels3023dee2003-07-04 10:54:51 +0000399
400 if(state == NULL) {
401 uip_abort();
402 return;
403 }
404
405 if(s.state == WEBCLIENT_STATE_CLOSE) {
406 webclient_closed();
407 uip_abort();
408 return;
adamdunkels0ebd76c2004-08-09 22:25:36 +0000409 }
adamdunkels3023dee2003-07-04 10:54:51 +0000410
411 if(uip_aborted()) {
412 webclient_aborted();
413 }
adamdunkels0ebd76c2004-08-09 22:25:36 +0000414
adamdunkels3023dee2003-07-04 10:54:51 +0000415
416 if(uip_acked()) {
417 s.timer = 0;
418 acked();
419 }
420 if(uip_newdata()) {
421 s.timer = 0;
422 newdata();
423 }
424 if(uip_rexmit() ||
425 uip_newdata() ||
426 uip_acked()) {
427 senddata();
428 } else if(uip_poll()) {
429 ++s.timer;
430 if(s.timer == WEBCLIENT_TIMEOUT) {
431 webclient_timedout();
432 uip_abort();
433 return;
434 }
435 /* senddata();*/
436 }
437
438 if(uip_closed()) {
adamdunkelsd311bf12004-07-04 20:17:37 +0000439 tcp_markconn(uip_conn, NULL);
adamdunkels3023dee2003-07-04 10:54:51 +0000440 if(s.httpflag != HTTPFLAG_MOVED) {
441 /* Send NULL data to signal EOF. */
442 webclient_datahandler(NULL, 0);
443 } else {
adamdunkels0ebd76c2004-08-09 22:25:36 +0000444 /* conn = uip_connect(uip_conn->ripaddr, s.port);
adamdunkels3023dee2003-07-04 10:54:51 +0000445 if(conn != NULL) {
adamdunkels0ebd76c2004-08-09 22:25:36 +0000446 dispatcher_markconn(conn, NULL);
adamdunkels3023dee2003-07-04 10:54:51 +0000447 init_connection();
adamdunkels0ebd76c2004-08-09 22:25:36 +0000448 }*/
449 if(resolv_lookup(s.host) == NULL) {
450 resolv_query(s.host);
adamdunkels3023dee2003-07-04 10:54:51 +0000451 }
adamdunkels0ebd76c2004-08-09 22:25:36 +0000452 webclient_get(s.host, s.port, s.file);
adamdunkels3023dee2003-07-04 10:54:51 +0000453 }
454 }
455}
456/*-----------------------------------------------------------------------------------*/