blob: 7e09096e9abf7ef66fd20c7798a260c46073388d [file] [log] [blame]
adamdunkelsca9ddcb2003-03-19 14:13:31 +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.
adamdunkels06f897e2004-06-06 05:59:20 +000014 * 3. The name of the author may not be used to endorse or promote
adamdunkelsca9ddcb2003-03-19 14:13:31 +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 *
adamdunkels46cd1432004-07-04 17:50:39 +000032 * $Id: webclient.c,v 1.18 2004/07/04 17:50:39 adamdunkels Exp $
adamdunkelsca9ddcb2003-03-19 14:13:31 +000033 *
34 */
35
adamdunkels46cd1432004-07-04 17:50:39 +000036#include "ek.h"
37#include "tcpip.h"
adamdunkelsca9ddcb2003-03-19 14:13:31 +000038#include "uip.h"
39#include "webclient.h"
40#include "resolv.h"
adamdunkelsdb300d22004-02-24 09:57:49 +000041#include "uiplib.h"
adamdunkelsca9ddcb2003-03-19 14:13:31 +000042
adamdunkels873b6c72003-08-09 13:31:54 +000043#include "www-conf.h"
44
adamdunkelsca9ddcb2003-03-19 14:13:31 +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];
adamdunkels873b6c72003-08-09 13:31:54 +000071 char file[WWW_CONF_MAX_URLLEN];
adamdunkelsca9ddcb2003-03-19 14:13:31 +000072 u16_t getrequestptr;
73 u16_t getrequestleft;
74
adamdunkelsc583d212003-08-24 22:36:31 +000075 char httpheaderline[200];
adamdunkelsca9ddcb2003-03-19 14:13:31 +000076 u16_t httpheaderlineptr;
77
78 char mimetype[32];
79};
80
81static struct webclient_state s;
82
83/*-----------------------------------------------------------------------------------*/
84char *
85webclient_mimetype(void)
86{
adamdunkelsaf908172003-03-28 12:07:54 +000087 return s.mimetype;
adamdunkelsca9ddcb2003-03-19 14:13:31 +000088}
89/*-----------------------------------------------------------------------------------*/
90char *
91webclient_filename(void)
92{
adamdunkelsaf908172003-03-28 12:07:54 +000093 return s.file;
adamdunkelsca9ddcb2003-03-19 14:13:31 +000094}
95/*-----------------------------------------------------------------------------------*/
96char *
97webclient_hostname(void)
98{
adamdunkelsaf908172003-03-28 12:07:54 +000099 return s.host;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000100}
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 +
adamdunkels09c3c762003-07-31 23:32:04 +0000124 strlen(http_user_agent_fields) +
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000125 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;
adamdunkelse6a3f182003-08-22 19:22:58 +0000142 static u16_t addr[2];
adamdunkelse3fa8fa2003-06-30 21:26:42 +0000143
144 /* First check if the host is an IP address. */
145 ipaddr = &addr[0];
adamdunkelsdb300d22004-02-24 09:57:49 +0000146 if(uiplib_ipaddrconv(host, (unsigned char *)addr) == 0) {
adamdunkelse3fa8fa2003-06-30 21:26:42 +0000147 ipaddr = resolv_lookup(host);
148
149 if(ipaddr == NULL) {
150 return 0;
151 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000152 }
153
adamdunkels46cd1432004-07-04 17:50:39 +0000154 conn = tcp_connect(ipaddr, htons(port), NULL);
adamdunkelse3fa8fa2003-06-30 21:26:42 +0000155
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000156 if(conn == NULL) {
157 return 0;
158 }
159
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000160 s.port = port;
161 strncpy(s.file, file, sizeof(s.file));
162 strncpy(s.host, host, sizeof(s.host));
163
164 init_connection();
165 return 1;
166}
167/*-----------------------------------------------------------------------------------*/
adamdunkels873b6c72003-08-09 13:31:54 +0000168static unsigned char * CC_FASTCALL
169copy_string(unsigned char *dest,
adamdunkels8d1e0732003-09-04 19:35:00 +0000170 const unsigned char *src, unsigned char len)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000171{
172 return strcpy(dest, src) + len;
173}
174/*-----------------------------------------------------------------------------------*/
175static void
176senddata(void)
177{
178 u16_t len;
179 char *getrequest;
180 char *cptr;
181
182 if(s.getrequestleft > 0) {
183 cptr = getrequest = (char *)uip_appdata;
184
185 cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
186 cptr = copy_string(cptr, s.file, strlen(s.file));
187 *cptr++ = ISO_space;
188 cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
189
190 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
191
192 cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
193 cptr = copy_string(cptr, s.host, strlen(s.host));
194 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
195
adamdunkelsece21772003-06-30 23:23:12 +0000196 cptr = copy_string(cptr, http_user_agent_fields,
adamdunkels09c3c762003-07-31 23:32:04 +0000197 strlen(http_user_agent_fields));
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000198
199 len = s.getrequestleft > uip_mss()?
200 uip_mss():
201 s.getrequestleft;
202 uip_send(&(getrequest[s.getrequestptr]), len);
203 }
204}
205/*-----------------------------------------------------------------------------------*/
206static void
207acked(void)
208{
209 u16_t len;
210
211 if(s.getrequestleft > 0) {
212 len = s.getrequestleft > uip_mss()?
213 uip_mss():
214 s.getrequestleft;
215 s.getrequestleft -= len;
216 s.getrequestptr += len;
217 }
218}
219/*-----------------------------------------------------------------------------------*/
220static u16_t
221parse_statusline(u16_t len)
222{
223 char *cptr;
224
225 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
226 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
227 ++uip_appdata;
228 --len;
229 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
230
231 if((strncmp(s.httpheaderline, http_10,
232 sizeof(http_10) - 1) == 0) ||
233 (strncmp(s.httpheaderline, http_11,
234 sizeof(http_11) - 1) == 0)) {
235 cptr = &(s.httpheaderline[9]);
236 s.httpflag = HTTPFLAG_NONE;
237 if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
238 /* 200 OK */
239 s.httpflag = HTTPFLAG_OK;
240 } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
241 strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
242 /* 301 Moved permanently or 302 Found. Location: header line
243 will contain thw new location. */
244 s.httpflag = HTTPFLAG_MOVED;
245 } else {
246 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
247 }
248 } else {
249 uip_abort();
250 webclient_aborted();
251 return 0;
252 }
253
254 /* We're done parsing the status line, so we reset the pointer
255 and start parsing the HTTP headers.*/
256 s.httpheaderlineptr = 0;
257 s.state = WEBCLIENT_STATE_HEADERS;
258 break;
259 } else {
260 ++s.httpheaderlineptr;
261 }
262 }
263 return len;
264}
265/*-----------------------------------------------------------------------------------*/
adamdunkelse6a3f182003-08-22 19:22:58 +0000266static char
adamdunkels8d1e0732003-09-04 19:35:00 +0000267casecmp(char *str1, const char *str2, char len)
adamdunkelse6a3f182003-08-22 19:22:58 +0000268{
269 static char c;
270
271 while(len > 0) {
272 c = *str1;
273 /* Force lower-case characters. */
274 if(c & 0x40) {
275 c |= 0x20;
276 }
277 if(*str2 != c) {
278 return 1;
279 }
280 ++str1;
281 ++str2;
282 --len;
283 }
284 return 0;
285}
286/*-----------------------------------------------------------------------------------*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000287static u16_t
288parse_headers(u16_t len)
289{
290 char *cptr;
adamdunkelse937ded2003-10-01 07:53:57 +0000291 static unsigned char i;
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000292
293 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
294 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
295 ++uip_appdata;
296 --len;
297 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
298 /* We have an entire HTTP header line in s.httpheaderline, so
299 we parse it. */
300 if(s.httpheaderline[0] == ISO_cr) {
301 /* This was the last header line (i.e., and empty "\r\n"), so
302 we are done with the headers and proceed with the actual
303 data. */
304 s.state = WEBCLIENT_STATE_DATA;
305 return len;
306 }
307
308 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
adamdunkelse6a3f182003-08-22 19:22:58 +0000309 /* Check for specific HTTP header fields. */
310 if(casecmp(s.httpheaderline, http_content_type,
311 sizeof(http_content_type) - 1) == 0) {
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000312 /* Found Content-type field. */
313 cptr = strchr(s.httpheaderline, ';');
314 if(cptr != NULL) {
315 *cptr = 0;
316 }
317 strncpy(s.mimetype, s.httpheaderline +
318 sizeof(http_content_type) - 1, sizeof(s.mimetype));
adamdunkelse6a3f182003-08-22 19:22:58 +0000319 } else if(casecmp(s.httpheaderline, http_location,
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000320 sizeof(http_location) - 1) == 0) {
adamdunkels37ac5f22003-08-29 20:35:46 +0000321 cptr = s.httpheaderline +
322 sizeof(http_location) - 1;
323
324 if(strncmp(cptr, http_http, 7) == 0) {
325 cptr += 7;
326 for(i = 0; i < s.httpheaderlineptr - 7; ++i) {
327 if(*cptr == 0 ||
328 *cptr == '/' ||
329 *cptr == ' ' ||
330 *cptr == ':') {
331 s.host[i] = 0;
332 break;
333 }
334 s.host[i] = *cptr;
335 ++cptr;
336 }
337 }
338 strncpy(s.file, cptr, sizeof(s.file));
adamdunkels25d2dbf2003-09-04 23:06:53 +0000339 /* s.file[s.httpheaderlineptr - i] = 0;*/
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000340 }
341
342
343 /* We're done parsing, so we reset the pointer and start the
344 next line. */
345 s.httpheaderlineptr = 0;
346 } else {
347 ++s.httpheaderlineptr;
348 }
349 }
350 return len;
351}
352/*-----------------------------------------------------------------------------------*/
353static void
354newdata(void)
355{
356 u16_t len;
357
358 len = uip_datalen();
359
360 if(s.state == WEBCLIENT_STATE_STATUSLINE) {
361 len = parse_statusline(len);
362 }
363
364 if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
365 len = parse_headers(len);
366 }
367
368 if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
369 s.httpflag != HTTPFLAG_MOVED) {
370 webclient_datahandler((char *)uip_appdata, len);
371 }
372}
373/*-----------------------------------------------------------------------------------*/
adamdunkels46cd1432004-07-04 17:50:39 +0000374void
375webclient_appcall(void *state)
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000376{
adamdunkelse3fa8fa2003-06-30 21:26:42 +0000377 if(uip_connected()) {
378 s.timer = 0;
379 s.state = WEBCLIENT_STATE_STATUSLINE;
380 senddata();
381 webclient_connected();
adamdunkels46cd1432004-07-04 17:50:39 +0000382 tcp_markconn(uip_conn, &s);
adamdunkelse3fa8fa2003-06-30 21:26:42 +0000383 return;
384 }
385
adamdunkelse2987b02003-11-27 15:47:11 +0000386 if(uip_timedout()) {
387 webclient_timedout();
388 }
adamdunkelse3fa8fa2003-06-30 21:26:42 +0000389
390 if(state == NULL) {
391 uip_abort();
392 return;
393 }
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000394
395 if(s.state == WEBCLIENT_STATE_CLOSE) {
396 webclient_closed();
397 uip_abort();
398 return;
adamdunkelse2987b02003-11-27 15:47:11 +0000399 }
adamdunkelse3fa8fa2003-06-30 21:26:42 +0000400
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000401 if(uip_aborted()) {
402 webclient_aborted();
403 }
adamdunkelse2987b02003-11-27 15:47:11 +0000404
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000405
406 if(uip_acked()) {
407 s.timer = 0;
408 acked();
409 }
410 if(uip_newdata()) {
411 s.timer = 0;
412 newdata();
413 }
414 if(uip_rexmit() ||
415 uip_newdata() ||
416 uip_acked()) {
417 senddata();
418 } else if(uip_poll()) {
419 ++s.timer;
420 if(s.timer == WEBCLIENT_TIMEOUT) {
421 webclient_timedout();
422 uip_abort();
423 return;
424 }
425 /* senddata();*/
426 }
427
428 if(uip_closed()) {
adamdunkels46cd1432004-07-04 17:50:39 +0000429 tcp_markconn(uip_conn, NULL);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000430 if(s.httpflag != HTTPFLAG_MOVED) {
431 /* Send NULL data to signal EOF. */
432 webclient_datahandler(NULL, 0);
433 } else {
adamdunkels37ac5f22003-08-29 20:35:46 +0000434 /* conn = uip_connect(uip_conn->ripaddr, s.port);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000435 if(conn != NULL) {
436 dispatcher_markconn(conn, NULL);
437 init_connection();
adamdunkels37ac5f22003-08-29 20:35:46 +0000438 }*/
439 if(resolv_lookup(s.host) == NULL) {
440 resolv_query(s.host);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000441 }
adamdunkels37ac5f22003-08-29 20:35:46 +0000442 webclient_get(s.host, s.port, s.file);
adamdunkelsca9ddcb2003-03-19 14:13:31 +0000443 }
444 }
445}
446/*-----------------------------------------------------------------------------------*/