| /* |
| * Copyright (c) 2002, Adam Dunkels. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * 3. The name of the author may not be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * This file is part of the "contiki" web browser. |
| * |
| * $Id: webclient.c,v 1.18 2004/07/04 17:50:39 adamdunkels Exp $ |
| * |
| */ |
| |
| #include "ek.h" |
| #include "tcpip.h" |
| #include "uip.h" |
| #include "webclient.h" |
| #include "resolv.h" |
| #include "uiplib.h" |
| |
| #include "www-conf.h" |
| |
| #include <string.h> |
| |
| #define WEBCLIENT_TIMEOUT 100 |
| |
| #define WEBCLIENT_STATE_STATUSLINE 0 |
| #define WEBCLIENT_STATE_HEADERS 1 |
| #define WEBCLIENT_STATE_DATA 2 |
| #define WEBCLIENT_STATE_CLOSE 3 |
| |
| #define HTTPFLAG_NONE 0 |
| #define HTTPFLAG_OK 1 |
| #define HTTPFLAG_MOVED 2 |
| #define HTTPFLAG_ERROR 3 |
| |
| |
| #define ISO_nl 0x0a |
| #define ISO_cr 0x0d |
| #define ISO_space 0x20 |
| |
| struct webclient_state { |
| u8_t timer; |
| u8_t state; |
| u8_t httpflag; |
| |
| u16_t port; |
| char host[40]; |
| char file[WWW_CONF_MAX_URLLEN]; |
| u16_t getrequestptr; |
| u16_t getrequestleft; |
| |
| char httpheaderline[200]; |
| u16_t httpheaderlineptr; |
| |
| char mimetype[32]; |
| }; |
| |
| static struct webclient_state s; |
| |
| /*-----------------------------------------------------------------------------------*/ |
| char * |
| webclient_mimetype(void) |
| { |
| return s.mimetype; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| char * |
| webclient_filename(void) |
| { |
| return s.file; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| char * |
| webclient_hostname(void) |
| { |
| return s.host; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| unsigned short |
| webclient_port(void) |
| { |
| return s.port; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| webclient_init(void) |
| { |
| |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| init_connection(void) |
| { |
| s.state = WEBCLIENT_STATE_STATUSLINE; |
| |
| s.getrequestleft = sizeof(http_get) - 1 + 1 + |
| sizeof(http_10) - 1 + |
| sizeof(http_crnl) - 1 + |
| sizeof(http_host) - 1 + |
| sizeof(http_crnl) - 1 + |
| strlen(http_user_agent_fields) + |
| strlen(s.file) + strlen(s.host); |
| s.getrequestptr = 0; |
| |
| s.httpheaderlineptr = 0; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| webclient_close(void) |
| { |
| s.state = WEBCLIENT_STATE_CLOSE; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| unsigned char |
| webclient_get(char *host, u16_t port, char *file) |
| { |
| struct uip_conn *conn; |
| u16_t *ipaddr; |
| static u16_t addr[2]; |
| |
| /* First check if the host is an IP address. */ |
| ipaddr = &addr[0]; |
| if(uiplib_ipaddrconv(host, (unsigned char *)addr) == 0) { |
| ipaddr = resolv_lookup(host); |
| |
| if(ipaddr == NULL) { |
| return 0; |
| } |
| } |
| |
| conn = tcp_connect(ipaddr, htons(port), NULL); |
| |
| if(conn == NULL) { |
| return 0; |
| } |
| |
| s.port = port; |
| strncpy(s.file, file, sizeof(s.file)); |
| strncpy(s.host, host, sizeof(s.host)); |
| |
| init_connection(); |
| return 1; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static unsigned char * CC_FASTCALL |
| copy_string(unsigned char *dest, |
| const unsigned char *src, unsigned char len) |
| { |
| return strcpy(dest, src) + len; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| senddata(void) |
| { |
| u16_t len; |
| char *getrequest; |
| char *cptr; |
| |
| if(s.getrequestleft > 0) { |
| cptr = getrequest = (char *)uip_appdata; |
| |
| cptr = copy_string(cptr, http_get, sizeof(http_get) - 1); |
| cptr = copy_string(cptr, s.file, strlen(s.file)); |
| *cptr++ = ISO_space; |
| cptr = copy_string(cptr, http_10, sizeof(http_10) - 1); |
| |
| cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1); |
| |
| cptr = copy_string(cptr, http_host, sizeof(http_host) - 1); |
| cptr = copy_string(cptr, s.host, strlen(s.host)); |
| cptr = copy_string(cptr, http_crnl, sizeof(http_crnl) - 1); |
| |
| cptr = copy_string(cptr, http_user_agent_fields, |
| strlen(http_user_agent_fields)); |
| |
| len = s.getrequestleft > uip_mss()? |
| uip_mss(): |
| s.getrequestleft; |
| uip_send(&(getrequest[s.getrequestptr]), len); |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| acked(void) |
| { |
| u16_t len; |
| |
| if(s.getrequestleft > 0) { |
| len = s.getrequestleft > uip_mss()? |
| uip_mss(): |
| s.getrequestleft; |
| s.getrequestleft -= len; |
| s.getrequestptr += len; |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static u16_t |
| parse_statusline(u16_t len) |
| { |
| char *cptr; |
| |
| while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) { |
| s.httpheaderline[s.httpheaderlineptr] = *uip_appdata; |
| ++uip_appdata; |
| --len; |
| if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) { |
| |
| if((strncmp(s.httpheaderline, http_10, |
| sizeof(http_10) - 1) == 0) || |
| (strncmp(s.httpheaderline, http_11, |
| sizeof(http_11) - 1) == 0)) { |
| cptr = &(s.httpheaderline[9]); |
| s.httpflag = HTTPFLAG_NONE; |
| if(strncmp(cptr, http_200, sizeof(http_200) - 1) == 0) { |
| /* 200 OK */ |
| s.httpflag = HTTPFLAG_OK; |
| } else if(strncmp(cptr, http_301, sizeof(http_301) - 1) == 0 || |
| strncmp(cptr, http_302, sizeof(http_302) - 1) == 0) { |
| /* 301 Moved permanently or 302 Found. Location: header line |
| will contain thw new location. */ |
| s.httpflag = HTTPFLAG_MOVED; |
| } else { |
| s.httpheaderline[s.httpheaderlineptr - 1] = 0; |
| } |
| } else { |
| uip_abort(); |
| webclient_aborted(); |
| return 0; |
| } |
| |
| /* We're done parsing the status line, so we reset the pointer |
| and start parsing the HTTP headers.*/ |
| s.httpheaderlineptr = 0; |
| s.state = WEBCLIENT_STATE_HEADERS; |
| break; |
| } else { |
| ++s.httpheaderlineptr; |
| } |
| } |
| return len; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static char |
| casecmp(char *str1, const char *str2, char len) |
| { |
| static char c; |
| |
| while(len > 0) { |
| c = *str1; |
| /* Force lower-case characters. */ |
| if(c & 0x40) { |
| c |= 0x20; |
| } |
| if(*str2 != c) { |
| return 1; |
| } |
| ++str1; |
| ++str2; |
| --len; |
| } |
| return 0; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static u16_t |
| parse_headers(u16_t len) |
| { |
| char *cptr; |
| static unsigned char i; |
| |
| while(len > 0 && s.httpheaderlineptr < sizeof(s.httpheaderline)) { |
| s.httpheaderline[s.httpheaderlineptr] = *uip_appdata; |
| ++uip_appdata; |
| --len; |
| if(s.httpheaderline[s.httpheaderlineptr] == ISO_nl) { |
| /* We have an entire HTTP header line in s.httpheaderline, so |
| we parse it. */ |
| if(s.httpheaderline[0] == ISO_cr) { |
| /* This was the last header line (i.e., and empty "\r\n"), so |
| we are done with the headers and proceed with the actual |
| data. */ |
| s.state = WEBCLIENT_STATE_DATA; |
| return len; |
| } |
| |
| s.httpheaderline[s.httpheaderlineptr - 1] = 0; |
| /* Check for specific HTTP header fields. */ |
| if(casecmp(s.httpheaderline, http_content_type, |
| sizeof(http_content_type) - 1) == 0) { |
| /* Found Content-type field. */ |
| cptr = strchr(s.httpheaderline, ';'); |
| if(cptr != NULL) { |
| *cptr = 0; |
| } |
| strncpy(s.mimetype, s.httpheaderline + |
| sizeof(http_content_type) - 1, sizeof(s.mimetype)); |
| } else if(casecmp(s.httpheaderline, http_location, |
| sizeof(http_location) - 1) == 0) { |
| cptr = s.httpheaderline + |
| sizeof(http_location) - 1; |
| |
| if(strncmp(cptr, http_http, 7) == 0) { |
| cptr += 7; |
| for(i = 0; i < s.httpheaderlineptr - 7; ++i) { |
| if(*cptr == 0 || |
| *cptr == '/' || |
| *cptr == ' ' || |
| *cptr == ':') { |
| s.host[i] = 0; |
| break; |
| } |
| s.host[i] = *cptr; |
| ++cptr; |
| } |
| } |
| strncpy(s.file, cptr, sizeof(s.file)); |
| /* s.file[s.httpheaderlineptr - i] = 0;*/ |
| } |
| |
| |
| /* We're done parsing, so we reset the pointer and start the |
| next line. */ |
| s.httpheaderlineptr = 0; |
| } else { |
| ++s.httpheaderlineptr; |
| } |
| } |
| return len; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| newdata(void) |
| { |
| u16_t len; |
| |
| len = uip_datalen(); |
| |
| if(s.state == WEBCLIENT_STATE_STATUSLINE) { |
| len = parse_statusline(len); |
| } |
| |
| if(s.state == WEBCLIENT_STATE_HEADERS && len > 0) { |
| len = parse_headers(len); |
| } |
| |
| if(len > 0 && s.state == WEBCLIENT_STATE_DATA && |
| s.httpflag != HTTPFLAG_MOVED) { |
| webclient_datahandler((char *)uip_appdata, len); |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| webclient_appcall(void *state) |
| { |
| if(uip_connected()) { |
| s.timer = 0; |
| s.state = WEBCLIENT_STATE_STATUSLINE; |
| senddata(); |
| webclient_connected(); |
| tcp_markconn(uip_conn, &s); |
| return; |
| } |
| |
| if(uip_timedout()) { |
| webclient_timedout(); |
| } |
| |
| if(state == NULL) { |
| uip_abort(); |
| return; |
| } |
| |
| if(s.state == WEBCLIENT_STATE_CLOSE) { |
| webclient_closed(); |
| uip_abort(); |
| return; |
| } |
| |
| if(uip_aborted()) { |
| webclient_aborted(); |
| } |
| |
| |
| if(uip_acked()) { |
| s.timer = 0; |
| acked(); |
| } |
| if(uip_newdata()) { |
| s.timer = 0; |
| newdata(); |
| } |
| if(uip_rexmit() || |
| uip_newdata() || |
| uip_acked()) { |
| senddata(); |
| } else if(uip_poll()) { |
| ++s.timer; |
| if(s.timer == WEBCLIENT_TIMEOUT) { |
| webclient_timedout(); |
| uip_abort(); |
| return; |
| } |
| /* senddata();*/ |
| } |
| |
| if(uip_closed()) { |
| tcp_markconn(uip_conn, NULL); |
| if(s.httpflag != HTTPFLAG_MOVED) { |
| /* Send NULL data to signal EOF. */ |
| webclient_datahandler(NULL, 0); |
| } else { |
| /* conn = uip_connect(uip_conn->ripaddr, s.port); |
| if(conn != NULL) { |
| dispatcher_markconn(conn, NULL); |
| init_connection(); |
| }*/ |
| if(resolv_lookup(s.host) == NULL) { |
| resolv_query(s.host); |
| } |
| webclient_get(s.host, s.port, s.file); |
| } |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |