blob: eac5400193318361f4a17e46b3d7913aa56dafed [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 *
35 * $Id: webclient.c,v 1.1 2003/07/04 10:54:51 adamdunkels Exp $
36 *
37 */
38
39#include "uip.h"
40#include "webclient.h"
41#include "resolv.h"
42#include "uip_main.h"
43
44#include <string.h>
45
46#define WEBCLIENT_TIMEOUT 100
47
48#define WEBCLIENT_STATE_STATUSLINE 0
49#define WEBCLIENT_STATE_HEADERS 1
50#define WEBCLIENT_STATE_DATA 2
51#define WEBCLIENT_STATE_CLOSE 3
52
53#define HTTPFLAG_NONE 0
54#define HTTPFLAG_OK 1
55#define HTTPFLAG_MOVED 2
56#define HTTPFLAG_ERROR 3
57
58
59#define ISO_nl 0x0a
60#define ISO_cr 0x0d
61#define ISO_space 0x20
62
63struct webclient_state {
64 u8_t timer;
65 u8_t state;
66 u8_t httpflag;
67
68 u16_t port;
69 char host[40];
70 char file[100];
71 u16_t getrequestptr;
72 u16_t getrequestleft;
73
74 char httpheaderline[200];
75 u16_t httpheaderlineptr;
76
77 char mimetype[32];
78};
79
80static struct webclient_state s;
81
82/*-----------------------------------------------------------------------------------*/
83char *
84webclient_mimetype(void)
85{
86 return s.mimetype;
87}
88/*-----------------------------------------------------------------------------------*/
89char *
90webclient_filename(void)
91{
92 return s.file;
93}
94/*-----------------------------------------------------------------------------------*/
95char *
96webclient_hostname(void)
97{
98 return s.host;
99}
100/*-----------------------------------------------------------------------------------*/
101unsigned short
102webclient_port(void)
103{
104 return s.port;
105}
106/*-----------------------------------------------------------------------------------*/
107void
108webclient_init(void)
109{
110
111}
112/*-----------------------------------------------------------------------------------*/
113static void
114init_connection(void)
115{
116 s.state = WEBCLIENT_STATE_STATUSLINE;
117
118 s.getrequestleft = sizeof(http_get) - 1 + 1 +
119 sizeof(http_10) - 1 +
120 sizeof(http_crnl) - 1 +
121 sizeof(http_host) - 1 +
122 sizeof(http_crnl) - 1 +
123 strlen(http_user_agent_fields) +
124 strlen(s.file) + strlen(s.host);
125 s.getrequestptr = 0;
126
127 s.httpheaderlineptr = 0;
128}
129/*-----------------------------------------------------------------------------------*/
130void
131webclient_close(void)
132{
133 s.state = WEBCLIENT_STATE_CLOSE;
134}
135/*-----------------------------------------------------------------------------------*/
136unsigned char
137webclient_get(char *host, u16_t port, char *file)
138{
139 struct uip_conn *conn;
140 u16_t *ipaddr;
141 u16_t addr[2];
142
143 /* First check if the host is an IP address. */
144 ipaddr = &addr[0];
145 if(uip_main_ipaddrconv(host, (unsigned char *)addr) == 0) {
146 ipaddr = resolv_lookup(host);
147
148 if(ipaddr == NULL) {
149 return 0;
150 }
151 }
152
153 /* XXX: here we check so that the server does not try to access any
154 hosts on the SICS networks. */
155 if(ipaddr[0] == HTONS((193 << 8) | 10) &&
156 ((htons(ipaddr[1]) >> 8) == 66 ||
157 (htons(ipaddr[1]) >> 8) == 67)) {
158 return 0;
159 } else {
160 conn = uip_connect(ipaddr, port);
161 }
162
163 if(conn == NULL) {
164 return 0;
165 }
166
167 dispatcher_markconn(conn, NULL);
168
169 s.port = port;
170 strncpy(s.file, file, sizeof(s.file));
171 strncpy(s.host, host, sizeof(s.host));
172
173 init_connection();
174 return 1;
175}
176/*-----------------------------------------------------------------------------------*/
177static unsigned char *
178copy_string(unsigned char *dest, unsigned char *src, unsigned char len)
179{
180 return strcpy(dest, src) + len;
181}
182/*-----------------------------------------------------------------------------------*/
183static void
184senddata(void)
185{
186 u16_t len;
187 char *getrequest;
188 char *cptr;
189
190 if(s.getrequestleft > 0) {
191 cptr = getrequest = (char *)uip_appdata;
192
193 cptr = copy_string(cptr, http_get, sizeof(http_get) - 1);
194 cptr = copy_string(cptr, s.file, strlen(s.file));
195 *cptr++ = ISO_space;
196 cptr = copy_string(cptr, http_10, sizeof(http_10) - 1);
197
198 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
199
200 cptr = copy_string(cptr, http_host, sizeof(http_host) - 1);
201 cptr = copy_string(cptr, s.host, strlen(s.host));
202 cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1);
203
204 cptr = copy_string(cptr, http_user_agent_fields,
205 strlen(http_user_agent_fields));
206
207 len = s.getrequestleft > uip_mss()?
208 uip_mss():
209 s.getrequestleft;
210 uip_send(&(getrequest[s.getrequestptr]), len);
211 }
212}
213/*-----------------------------------------------------------------------------------*/
214static void
215acked(void)
216{
217 u16_t len;
218
219 if(s.getrequestleft > 0) {
220 len = s.getrequestleft > uip_mss()?
221 uip_mss():
222 s.getrequestleft;
223 s.getrequestleft -= len;
224 s.getrequestptr += len;
225 }
226}
227/*-----------------------------------------------------------------------------------*/
228static u16_t
229parse_statusline(u16_t len)
230{
231 char *cptr;
232
233 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
234 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
235 ++uip_appdata;
236 --len;
237 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
238
239 if((strncmp(s.httpheaderline, http_10,
240 sizeof(http_10) - 1) == 0) ||
241 (strncmp(s.httpheaderline, http_11,
242 sizeof(http_11) - 1) == 0)) {
243 cptr = &(s.httpheaderline[9]);
244 s.httpflag = HTTPFLAG_NONE;
245 if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) {
246 /* 200 OK */
247 s.httpflag = HTTPFLAG_OK;
248 } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 ||
249 strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) {
250 /* 301 Moved permanently or 302 Found. Location: header line
251 will contain thw new location. */
252 s.httpflag = HTTPFLAG_MOVED;
253 } else {
254 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
255 }
256 } else {
257 uip_abort();
258 webclient_aborted();
259 return 0;
260 }
261
262 /* We're done parsing the status line, so we reset the pointer
263 and start parsing the HTTP headers.*/
264 s.httpheaderlineptr = 0;
265 s.state = WEBCLIENT_STATE_HEADERS;
266 break;
267 } else {
268 ++s.httpheaderlineptr;
269 }
270 }
271 return len;
272}
273/*-----------------------------------------------------------------------------------*/
274static u16_t
275parse_headers(u16_t len)
276{
277 char *cptr;
278
279 while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) {
280 s.httpheaderline[s.httpheaderlineptr] = *uip_appdata;
281 ++uip_appdata;
282 --len;
283 if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) {
284 /* We have an entire HTTP header line in s.httpheaderline, so
285 we parse it. */
286 if(s.httpheaderline[0] == ISO_cr) {
287 /* This was the last header line (i.e., and empty "\r\n"), so
288 we are done with the headers and proceed with the actual
289 data. */
290 s.state = WEBCLIENT_STATE_DATA;
291 return len;
292 }
293
294 s.httpheaderline[s.httpheaderlineptr - 1] = 0;
295 /* Check for specific HTTP header fields. */
296 if(strncmp(s.httpheaderline, http_content_type,
297 sizeof(http_content_type) - 1) == 0) {
298 /* Found Content-type field. */
299 cptr = strchr(s.httpheaderline, ';');
300 if(cptr != NULL) {
301 *cptr = 0;
302 }
303 strncpy(s.mimetype, s.httpheaderline +
304 sizeof(http_content_type) - 1, sizeof(s.mimetype));
305 } else if(strncmp(s.httpheaderline, http_location,
306 sizeof(http_location) - 1) == 0) {
307 strncpy(s.file, s.httpheaderline +
308 sizeof(http_location) - 1, sizeof(s.file));
309 s.file[s.httpheaderlineptr - 1] = 0;
310 }
311
312
313 /* We're done parsing, so we reset the pointer and start the
314 next line. */
315 s.httpheaderlineptr = 0;
316 } else {
317 ++s.httpheaderlineptr;
318 }
319 }
320 return len;
321}
322/*-----------------------------------------------------------------------------------*/
323static void
324newdata(void)
325{
326 u16_t len;
327
328 len = uip_datalen();
329
330 if(s.state == WEBCLIENT_STATE_STATUSLINE) {
331 len = parse_statusline(len);
332 }
333
334 if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) {
335 len = parse_headers(len);
336 }
337
338 if(len > 0 && s.state == WEBCLIENT_STATE_DATA &&
339 s.httpflag != HTTPFLAG_MOVED) {
340 webclient_datahandler((char *)uip_appdata, len);
341 }
342}
343/*-----------------------------------------------------------------------------------*/
344DISPATCHER_UIPCALL(webclient_appcall, state)
345{
346 struct uip_conn *conn;
347 DISPATCHER_UIPCALL_ARG(state);
348
349 if(uip_connected()) {
350 s.timer = 0;
351 s.state = WEBCLIENT_STATE_STATUSLINE;
352 senddata();
353 webclient_connected();
354 dispatcher_markconn(uip_conn, &s);
355 return;
356 }
357
358
359 if(state == NULL) {
360 uip_abort();
361 return;
362 }
363
364 if(s.state == WEBCLIENT_STATE_CLOSE) {
365 webclient_closed();
366 uip_abort();
367 return;
368 }
369
370
371
372 if(uip_aborted()) {
373 webclient_aborted();
374 }
375 if(uip_timedout()) {
376 webclient_timedout();
377 }
378
379
380 if(uip_acked()) {
381 s.timer = 0;
382 acked();
383 }
384 if(uip_newdata()) {
385 s.timer = 0;
386 newdata();
387 }
388 if(uip_rexmit() ||
389 uip_newdata() ||
390 uip_acked()) {
391 senddata();
392 } else if(uip_poll()) {
393 ++s.timer;
394 if(s.timer == WEBCLIENT_TIMEOUT) {
395 webclient_timedout();
396 uip_abort();
397 return;
398 }
399 /* senddata();*/
400 }
401
402 if(uip_closed()) {
403 dispatcher_markconn(uip_conn, NULL);
404 if(s.httpflag != HTTPFLAG_MOVED) {
405 /* Send NULL data to signal EOF. */
406 webclient_datahandler(NULL, 0);
407 } else {
408 conn = uip_connect(uip_conn->ripaddr, s.port);
409 if(conn != NULL) {
410 dispatcher_markconn(conn, NULL);
411 init_connection();
412 }
413 }
414 }
415}
416/*-----------------------------------------------------------------------------------*/