blob: 0623523e5e243b844849686a656388e681cbc558 [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.
14 * 3. All advertising materials mentioning features or use of this
15 * software must display the following acknowledgement:
16 * This product includes software developed by Adam Dunkels.
17 * 4. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior
19 * written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * This file is part of the "contiki" web browser.
34 *
adamdunkels37b236a2003-08-25 12:38:41 +000035 * $Id: webclient.c,v 1.2 2003/08/25 12:38:41 adamdunkels Exp $
adamdunkels3023dee2003-07-04 10:54:51 +000036 *
37 */
38
39#include "uip.h"
40#include "webclient.h"
41#include "resolv.h"
42#include "uip_main.h"
43
adamdunkels37b236a2003-08-25 12:38:41 +000044#include "www-conf.h"
45
adamdunkels3023dee2003-07-04 10:54:51 +000046#include <string.h>
47
48#define WEBCLIENT_TIMEOUT 100
49
50#define WEBCLIENT_STATE_STATUSLINE 0
51#define WEBCLIENT_STATE_HEADERS 1
52#define WEBCLIENT_STATE_DATA 2
53#define WEBCLIENT_STATE_CLOSE 3
54
55#define HTTPFLAG_NONE 0
56#define HTTPFLAG_OK 1
57#define HTTPFLAG_MOVED 2
58#define HTTPFLAG_ERROR 3
59
60
61#define ISO_nl 0x0a
62#define ISO_cr 0x0d
63#define ISO_space 0x20
64
65struct webclient_state {
66 u8_t timer;
67 u8_t state;
68 u8_t httpflag;
69
70 u16_t port;
71 char host[40];
adamdunkels37b236a2003-08-25 12:38:41 +000072 char file[WWW_CONF_MAX_URLLEN];
adamdunkels3023dee2003-07-04 10:54:51 +000073 u16_t getrequestptr;
74 u16_t getrequestleft;
75
76 char httpheaderline[200];
77 u16_t httpheaderlineptr;
78
79 char mimetype[32];
80};
81
82static struct webclient_state s;
83
84/*-----------------------------------------------------------------------------------*/
85char *
86webclient_mimetype(void)
87{
88 return s.mimetype;
89}
90/*-----------------------------------------------------------------------------------*/
91char *
92webclient_filename(void)
93{
94 return s.file;
95}
96/*-----------------------------------------------------------------------------------*/
97char *
98webclient_hostname(void)
99{
100 return s.host;
101}
102/*-----------------------------------------------------------------------------------*/
103unsigned short
104webclient_port(void)
105{
106 return s.port;
107}
108/*-----------------------------------------------------------------------------------*/
109void
110webclient_init(void)
111{
112
113}
114/*-----------------------------------------------------------------------------------*/
115static void
116init_connection(void)
117{
118 s.state = WEBCLIENT_STATE_STATUSLINE;
119
120 s.getrequestleft = sizeof(http_get) - 1 + 1 +
121 sizeof(http_10) - 1 +
122 sizeof(http_crnl) - 1 +
123 sizeof(http_host) - 1 +
124 sizeof(http_crnl) - 1 +
125 strlen(http_user_agent_fields) +
126 strlen(s.file) + strlen(s.host);
127 s.getrequestptr = 0;
128
129 s.httpheaderlineptr = 0;
130}
131/*-----------------------------------------------------------------------------------*/
132void
133webclient_close(void)
134{
135 s.state = WEBCLIENT_STATE_CLOSE;
136}
137/*-----------------------------------------------------------------------------------*/
138unsigned char
139webclient_get(char *host, u16_t port, char *file)
140{
141 struct uip_conn *conn;
142 u16_t *ipaddr;
adamdunkels37b236a2003-08-25 12:38:41 +0000143 static u16_t addr[2];
adamdunkels3023dee2003-07-04 10:54:51 +0000144
145 /* First check if the host is an IP address. */
146 ipaddr = &addr[0];
147 if(uip_main_ipaddrconv(host, (unsigned char *)addr) == 0) {
148 ipaddr = resolv_lookup(host);
149
150 if(ipaddr == NULL) {
151 return 0;
152 }
153 }
adamdunkels37b236a2003-08-25 12:38:41 +0000154
adamdunkels3023dee2003-07-04 10:54:51 +0000155 /* XXX: here we check so that the server does not try to access any
156 hosts on the SICS networks. */
157 if(ipaddr[0] == HTONS((193 << 8) | 10) &&
158 ((htons(ipaddr[1]) >> 8) == 66 ||
159 (htons(ipaddr[1]) >> 8) == 67)) {
160 return 0;
161 } else {
162 conn = uip_connect(ipaddr, port);
163 }
164
165 if(conn == NULL) {
166 return 0;
167 }
168
169 dispatcher_markconn(conn, NULL);
170
171 s.port = port;
172 strncpy(s.file, file, sizeof(s.file));
173 strncpy(s.host, host, sizeof(s.host));
174
175 init_connection();
176 return 1;
177}
178/*-----------------------------------------------------------------------------------*/
adamdunkels37b236a2003-08-25 12:38:41 +0000179static unsigned char * CC_FASTCALL
180copy_string(unsigned char *dest,
181 unsigned char *src, unsigned char len)
adamdunkels3023dee2003-07-04 10:54:51 +0000182{
183 return strcpy(dest, src) + len;
184}
185/*-----------------------------------------------------------------------------------*/
186static void
187senddata(void)
188{
189 u16_t len;
190 char *getrequest;
191 char *cptr;
192
193 if(s.getrequestleft > 0) {
194 cptr = getrequest = (char *)uip_appdata;
195
196 cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
197 cptr = copy_string(cptr, s.file, strlen(s.file));
198 *cptr++ = ISO_space;
199 cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
200
201 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
202
203 cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
204 cptr = copy_string(cptr, s.host, strlen(s.host));
205 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
206
207 cptr = copy_string(cptr, http_user_agent_fields,
208 strlen(http_user_agent_fields));
209
210 len = s.getrequestleft > uip_mss()?
211 uip_mss():
212 s.getrequestleft;
213 uip_send(&(getrequest[s.getrequestptr]), len);
214 }
215}
216/*-----------------------------------------------------------------------------------*/
217static void
218acked(void)
219{
220 u16_t len;
221
222 if(s.getrequestleft > 0) {
223 len = s.getrequestleft > uip_mss()?
224 uip_mss():
225 s.getrequestleft;
226 s.getrequestleft -= len;
227 s.getrequestptr += len;
228 }
229}
230/*-----------------------------------------------------------------------------------*/
231static u16_t
232parse_statusline(u16_t len)
233{
234 char *cptr;
235
236 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
237 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
238 ++uip_appdata;
239 --len;
240 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
241
242 if((strncmp(s.httpheaderline, http_10,
243 sizeof(http_10) - 1) == 0) ||
244 (strncmp(s.httpheaderline, http_11,
245 sizeof(http_11) - 1) == 0)) {
246 cptr = &(s.httpheaderline[9]);
247 s.httpflag = HTTPFLAG_NONE;
248 if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
249 /* 200 OK */
250 s.httpflag = HTTPFLAG_OK;
251 } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
252 strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
253 /* 301 Moved permanently or 302 Found. Location: header line
254 will contain thw new location. */
255 s.httpflag = HTTPFLAG_MOVED;
256 } else {
257 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
258 }
259 } else {
260 uip_abort();
261 webclient_aborted();
262 return 0;
263 }
264
265 /* We're done parsing the status line, so we reset the pointer
266 and start parsing the HTTP headers.*/
267 s.httpheaderlineptr = 0;
268 s.state = WEBCLIENT_STATE_HEADERS;
269 break;
270 } else {
271 ++s.httpheaderlineptr;
272 }
273 }
274 return len;
275}
276/*-----------------------------------------------------------------------------------*/
adamdunkels37b236a2003-08-25 12:38:41 +0000277static char
278casecmp(char *str1, char *str2, char len)
279{
280 static char c;
281
282 while(len > 0) {
283 c = *str1;
284 /* Force lower-case characters. */
285 if(c & 0x40) {
286 c |= 0x20;
287 }
288 if(*str2 != c) {
289 return 1;
290 }
291 ++str1;
292 ++str2;
293 --len;
294 }
295 return 0;
296}
297/*-----------------------------------------------------------------------------------*/
adamdunkels3023dee2003-07-04 10:54:51 +0000298static u16_t
299parse_headers(u16_t len)
300{
301 char *cptr;
adamdunkels37b236a2003-08-25 12:38:41 +0000302 static char c;
adamdunkels3023dee2003-07-04 10:54:51 +0000303
304 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
305 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
306 ++uip_appdata;
307 --len;
308 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
309 /* We have an entire HTTP header line in s.httpheaderline, so
310 we parse it. */
311 if(s.httpheaderline[0] == ISO_cr) {
312 /* This was the last header line (i.e., and empty "\r\n"), so
313 we are done with the headers and proceed with the actual
314 data. */
315 s.state = WEBCLIENT_STATE_DATA;
316 return len;
317 }
318
319 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
adamdunkels37b236a2003-08-25 12:38:41 +0000320 /* Check for specific HTTP header fields. */
321 if(casecmp(s.httpheaderline, http_content_type,
322 sizeof(http_content_type) - 1) == 0) {
adamdunkels3023dee2003-07-04 10:54:51 +0000323 /* Found Content-type field. */
324 cptr = strchr(s.httpheaderline, ';');
325 if(cptr != NULL) {
326 *cptr = 0;
327 }
328 strncpy(s.mimetype, s.httpheaderline +
329 sizeof(http_content_type) - 1, sizeof(s.mimetype));
adamdunkels37b236a2003-08-25 12:38:41 +0000330 } else if(casecmp(s.httpheaderline, http_location,
adamdunkels3023dee2003-07-04 10:54:51 +0000331 sizeof(http_location) - 1) == 0) {
332 strncpy(s.file, s.httpheaderline +
333 sizeof(http_location) - 1, sizeof(s.file));
334 s.file[s.httpheaderlineptr - 1] = 0;
335 }
336
337
338 /* We're done parsing, so we reset the pointer and start the
339 next line. */
340 s.httpheaderlineptr = 0;
341 } else {
342 ++s.httpheaderlineptr;
343 }
344 }
345 return len;
346}
347/*-----------------------------------------------------------------------------------*/
348static void
349newdata(void)
350{
351 u16_t len;
352
353 len = uip_datalen();
354
355 if(s.state == WEBCLIENT_STATE_STATUSLINE) {
356 len = parse_statusline(len);
357 }
358
359 if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
360 len = parse_headers(len);
361 }
362
363 if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
364 s.httpflag != HTTPFLAG_MOVED) {
365 webclient_datahandler((char *)uip_appdata, len);
366 }
367}
368/*-----------------------------------------------------------------------------------*/
369DISPATCHER_UIPCALL(webclient_appcall, state)
370{
371 struct uip_conn *conn;
372 DISPATCHER_UIPCALL_ARG(state);
373
374 if(uip_connected()) {
375 s.timer = 0;
376 s.state = WEBCLIENT_STATE_STATUSLINE;
377 senddata();
378 webclient_connected();
379 dispatcher_markconn(uip_conn, &s);
380 return;
381 }
382
383
384 if(state == NULL) {
385 uip_abort();
386 return;
387 }
388
389 if(s.state == WEBCLIENT_STATE_CLOSE) {
390 webclient_closed();
391 uip_abort();
392 return;
393 }
394
395
396
397 if(uip_aborted()) {
398 webclient_aborted();
399 }
400 if(uip_timedout()) {
401 webclient_timedout();
402 }
403
404
405 if(uip_acked()) {
406 s.timer = 0;
407 acked();
408 }
409 if(uip_newdata()) {
410 s.timer = 0;
411 newdata();
412 }
413 if(uip_rexmit() ||
414 uip_newdata() ||
415 uip_acked()) {
416 senddata();
417 } else if(uip_poll()) {
418 ++s.timer;
419 if(s.timer == WEBCLIENT_TIMEOUT) {
420 webclient_timedout();
421 uip_abort();
422 return;
423 }
424 /* senddata();*/
425 }
426
427 if(uip_closed()) {
428 dispatcher_markconn(uip_conn, NULL);
429 if(s.httpflag != HTTPFLAG_MOVED) {
430 /* Send NULL data to signal EOF. */
431 webclient_datahandler(NULL, 0);
432 } else {
433 conn = uip_connect(uip_conn->ripaddr, s.port);
434 if(conn != NULL) {
435 dispatcher_markconn(conn, NULL);
436 init_connection();
437 }
438 }
439 }
440}
441/*-----------------------------------------------------------------------------------*/