#include "uip.h"
#include "httpd.h"
#include "fs.h"
#include "fsdata.h"
#include "common.h"
DECLARE_GLOBAL_DATA_PTR;
#define DEV_NAME_ADDR 0x1da02
#define HW_VER_ADDR 0x1da40
#define MAX_DEV_NAME_SIZE 55
#define MAX_HW_VER_SIZE 14
#define STATE_NONE 0 // empty state (waiting for request...)
#define STATE_FILE_REQUEST 1 // remote host sent GET request
#define STATE_UPLOAD_REQUEST 2 // remote host sent POST request
// ASCII characters
#define ISO_G 0x47 // GET
#define ISO_E 0x45
#define ISO_T 0x54
#define ISO_P 0x50 // POST
#define ISO_O 0x4f
#define ISO_S 0x53
#define ISO_T 0x54
#define ISO_slash 0x2f // control and other characters
#define ISO_space 0x20
#define ISO_nl 0x0a
#define ISO_cr 0x0d
#define ISO_tab 0x09
// we use this so that we can do without the ctype library
#define is_digit(c) ((c) >= '0' && (c) <= '9')
// debug
//#define DEBUG_UIP
// html files
extern const struct fsdata_file file_index_html;
extern const struct fsdata_file file_404_html;
extern const struct fsdata_file file_flashing_html;
extern const struct fsdata_file file_fail_html;
extern int webfailsafe_ready_for_upgrade;
extern int webfailsafe_firmware_chk_success;
#ifdef CONFIG_RTS3913
extern u32 net_boot_file_size;
#else
extern ulong NetBootFileXferSize;
#endif
extern unsigned char *webfailsafe_data_pointer;
// http app state
struct httpd_state *hs;
static int webfailsafe_post_done = 0;
static int webfailsafe_upload_failed = 0;
static unsigned char post_packet_counter = 0;
// 0x0D -> CR 0x0A -> LF
static char eol[3] = { 0x0d, 0x0a, 0x00 };
static char eol2[5] = { 0x0d, 0x0a, 0x0d, 0x0a, 0x00 };
static char *boundary_value;
// str to int
static int atoi(const char *s){
int i = 0;
while(is_digit(*s)){
i = i * 10 + *(s++) - '0';
}
return(i);
}
// print downloading progress
static void httpd_download_progress(void){
if(post_packet_counter == 194){
puts("\n ");
post_packet_counter = 0;
}
if (post_packet_counter%3 == 0)
puts("#");
post_packet_counter++;
}
// http server init
void httpd_init(void)
{
fs_init();
upgrade_init();
uip_listen(HTONS(HTTPD_LISTEN_PORT));
}
// reset app state
static void httpd_state_reset(void){
hs->state = STATE_NONE;
hs->count = 0;
hs->dataptr = 0;
hs->upload = 0;
hs->upload_total = 0;
hs->data_start_found = 0;
post_packet_counter = 0;
if(boundary_value){
free(boundary_value);
boundary_value = NULL;
}
}
// find and get first chunk of data
static int httpd_findandstore_firstchunk(void){
char *start = NULL;
char *end = NULL;
if(!boundary_value){
return(0);
}
// chek if we have data in packet
start = (char *)strstr((char *)uip_appdata, (char *)boundary_value);
if(start){
// ok, we have data in this packet!
// find start position of the data!
end = (char *)strstr((char *)start, eol2);
if(end){
if((end - (char *)uip_appdata) < uip_len){
// move pointer over CR LF CR LF
end += 4;
// how much data we expect?
// last part (magic value 6): [CR][LF](boundary length)[-][-][CR][LF]
hs->upload_total = hs->upload_total - (int)(end - start) - strlen(boundary_value) - 6;
printf("Upload file size: %d bytes\n", hs->upload_total);
printf("Loading: ");
// how much data we are storing now?
hs->upload = (unsigned int)(uip_len - (end - (char *)uip_appdata));
memcpy((void *)webfailsafe_data_pointer, (void *)end, hs->upload);
webfailsafe_data_pointer += hs->upload;
httpd_download_progress();
return(1);
}
} else {
printf("## Error: couldn't find start of data!\n");
}
}
return(0);
}
static int httpd_get_model(char model[MAX_DEV_NAME_SIZE + MAX_HW_VER_SIZE + 2])
{
if (model == NULL)
{
printf("## Error: null parameter when get model\n");
return -1;
}
char dev_name[MAX_DEV_NAME_SIZE + 1] = {0};
char hw_ver[MAX_HW_VER_SIZE + 1] = {0};
if (readFlash(DEV_NAME_ADDR, dev_name, MAX_DEV_NAME_SIZE) || !strlen(dev_name))
{
printf("## Error: couldn't find dev name!\n");
return -1;
}
if (readFlash(HW_VER_ADDR, hw_ver, MAX_HW_VER_SIZE) || !strlen(hw_ver))
{
printf("## Error: couldn't find hw ver!\n");
return -1;
}
sprintf(model, "%s %s", dev_name, hw_ver);
return 0;
}
static int httpd_replace_model(struct fs_file *fsfile, char *model)
{
if ((fsfile == NULL) || (model == NULL))
{
printf("## Error: null parameter replace mode\n");
return -1;
}
int i = 0;
int j = 0;
char tag[] = "hardwareVer";
int tag_len = strlen(tag);
int model_len = strlen(model);
for (i = 0; i < fsfile->len; i++)
{
j = (tag[j] == fsfile->data[i])? (j + 1): 0;
if (j == tag_len)
{
break;
}
}
i += 1 + strlen("\">");
j = 0;
while ((i < fsfile->len) && (fsfile->data[i] != '<'))
{
if (j < model_len)
{
fsfile->data[i++] = model[j++];
}
else
{
fsfile->data[i++] = ISO_space;
}
}
return 0;
}
// called for http server app
void httpd_appcall(void){
struct fs_file fsfile;
unsigned int i;
switch(uip_conn->lport){
case HTONS(80):
// app state
hs = (struct httpd_state *)(uip_conn->appstate);
// closed connection
if(uip_closed()){
httpd_state_reset();
uip_close();
return;
}
// aborted connection or time out occured
if(uip_aborted() || uip_timedout()){
httpd_state_reset();
uip_abort();
return;
}
// if we are pooled
if(uip_poll()){
if(hs->count++ >= 100){
httpd_state_reset();
uip_abort();
}
return;
}
// new connection
if(uip_connected()){
httpd_state_reset();
return;
}
// new data in STATE_NONE
if(uip_newdata() && hs->state == STATE_NONE){
// GET or POST request?
if( ((u8_t*)uip_appdata)[0] == ISO_G
&& ((u8_t*)uip_appdata)[1] == ISO_E
&& ((u8_t*)uip_appdata)[2] == ISO_T
&& (((u8_t*)uip_appdata)[3] == ISO_space
|| ((u8_t*)uip_appdata)[3] == ISO_tab))
{
hs->state = STATE_FILE_REQUEST;
} else if(((u8_t*)uip_appdata)[0] == ISO_P
&& ((u8_t*)uip_appdata)[1] == ISO_O
&& ((u8_t*)uip_appdata)[2] == ISO_S
&& ((u8_t*)uip_appdata)[3] == ISO_T
&& (((u8_t*)uip_appdata)[4] == ISO_space
|| ((u8_t*)uip_appdata)[4] == ISO_tab)){
hs->state = STATE_UPLOAD_REQUEST;
}
// anything else -> abort the connection!
if(hs->state == STATE_NONE)
{
httpd_state_reset();
uip_abort();
return;
}
// get file or firmware upload?
if(hs->state == STATE_FILE_REQUEST){
// we are looking for GET file name
for(i = 4; i < 30; i++){
if( ((u8_t*)uip_appdata)[i] == ISO_space
|| ((u8_t*)uip_appdata)[i] == ISO_cr
|| ((u8_t*)uip_appdata)[i] == ISO_nl
|| ((u8_t*)uip_appdata)[i] == ISO_tab){
((u8_t*)uip_appdata)[i] = 0;
i = 0;
break;
}
}
if(i != 0){
printf("## Error: request file name too long!\n");
httpd_state_reset();
uip_abort();
return;
}
printf("rport:%d; Request for: ", uip_conn->rport);
printf("%s\n", &((u8_t*)uip_appdata)[4]);
// request for /
if(((u8_t*)uip_appdata)[4] == ISO_slash && ((u8_t*)uip_appdata)[5] == 0){
i = fs_open(file_index_html.name, &fsfile);
char model[MAX_DEV_NAME_SIZE + MAX_HW_VER_SIZE + 2] = {0};
if (!httpd_get_model(model))
{
httpd_replace_model(&fsfile, model);
}
}
else {
// check if we have requested file
if(!fs_open((const char *)&((u8_t*)uip_appdata)[4], &fsfile)){
printf("## Error: file not found!\n");
fs_open(file_404_html.name, &fsfile);
}
}
hs->state = STATE_FILE_REQUEST;
hs->dataptr = (u8_t *)fsfile.data;
hs->upload = fsfile.len;
// send first (and maybe the last) chunk of data
uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
return;
}
else if(hs->state == STATE_UPLOAD_REQUEST)
{
char *start = NULL;
char *end = NULL;
// end bufor data with NULL
((u8_t*)uip_appdata)[uip_len] = '\0';
/*
* We got first packet with POST request
*
* Some browsers don't include first chunk of data in the first
* POST request packet (like Google Chrome, IE and Safari)!
* So we must now find two values:
* - Content-Length
* - boundary
* Headers with these values can be in any order!
* If we don't find these values in first packet, connection will be aborted!
*
*/
// Content-Length pos
start = (char *)strstr((char*)uip_appdata, "Content-Length:");
if(start){
start += sizeof("Content-Length:");
// find end of the line with "Content-Length:"
end = (char *)strstr(start, eol);
if(end){
hs->upload_total = atoi(start);
#ifdef DEBUG_UIP
printf("Expecting %d bytes in body request message\n", hs->upload_total);
#endif
} else {
printf("## Error: couldn't find \"Content-Length\"!\n");
httpd_state_reset();
uip_abort();
return;
}
}
else {
printf("## Error: couldn't find \"Content-Length\"!\n");
httpd_state_reset();
uip_abort();
return;
}
// we don't support very small files (< 10 KB)
if(hs->upload_total < 10240){
printf("## Error: request for upload < 10 KB data!\n");
httpd_state_reset();
uip_abort();
return;
}
// boundary value
start = NULL;
end = NULL;
start = (char *)strstr((char *)uip_appdata, "boundary=");
if(start){
// move pointer over "boundary="
start += 9;
// find end of line with boundary value
end = (char *)strstr((char *)start, eol);
if(end){
// malloc space for boundary value + '--' and '\0'
boundary_value = (char*)malloc(end - start + 3);
if(boundary_value){
memcpy(&boundary_value[2], start, end - start);
// add -- at the begin and 0 at the end
boundary_value[0] = '-';
boundary_value[1] = '-';
boundary_value[end - start + 2] = 0;
#ifdef DEBUG_UIP
printf("Found boundary value: \"%s\"\n", boundary_value);
#endif
} else {
printf("## Error: couldn't allocate memory for boundary!\n");
httpd_state_reset();
uip_abort();
return;
}
} else {
printf("## Error: couldn't find boundary!\n");
httpd_state_reset();
uip_abort();
return;
}
} else {
printf("## Error: couldn't find boundary!\n");
httpd_state_reset();
uip_abort();
return;
}
/*
* OK, if we are here, it means that we found
* Content-Length and boundary values in headers
*
* We can now try to 'allocate memory' and
* find beginning of the data in first packet
*/
webfailsafe_data_pointer = (u8_t *)WEBFAILSAFE_UPLOAD_RAM_ADDRESS;
if(!webfailsafe_data_pointer){
printf("## Error: couldn't allocate RAM for data!\n");
httpd_state_reset();
uip_abort();
return;
} else {
printf("Data will be downloaded at 0x%X in RAM\n", WEBFAILSAFE_UPLOAD_RAM_ADDRESS);
}
if(httpd_findandstore_firstchunk()){
hs->data_start_found = 1;
} else {
hs->data_start_found = 0;
}
return;
} /* else if(hs->state == STATE_UPLOAD_REQUEST) */
} /* uip_newdata() && hs->state == STATE_NONE */
// if we got ACK from remote host
if(uip_acked()){
// if we are in STATE_FILE_REQUEST state
if(hs->state == STATE_FILE_REQUEST){
// data which we send last time was received (we got ACK)
// if we send everything last time -> gently close the connection
if(hs->upload <= uip_mss()){
// post upload completed?
if(webfailsafe_post_done){
if(!webfailsafe_upload_failed){
webfailsafe_ready_for_upgrade = 1;
}
webfailsafe_post_done = 0;
webfailsafe_upload_failed = 0;
}
httpd_state_reset();
uip_close();
return;
}
// otherwise, send another chunk of data
// last time we sent uip_conn->len size of data
hs->dataptr += uip_conn->len;
hs->upload -= uip_conn->len;
UIP_LOG("line: %d==:rport:%d;hs->upload %d; "
"last send uip_conn->len %d;"
"now will send %d;"
" --debug by HouXB\n",
__LINE__, uip_conn->rport, hs->upload, uip_conn->len,
(hs->upload > uip_mss() ? uip_mss() : hs->upload));
uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
}
return;
}
// if we need to retransmit
if(uip_rexmit()){
// if we are in STATE_FILE_REQUEST state
if(hs->state == STATE_FILE_REQUEST){
// send again chunk of data without changing pointer and length of data left to send
uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
}
return;
}
// if we got new data frome remote host
if(uip_newdata()){
// if we are in STATE_UPLOAD_REQUEST state
if(hs->state == STATE_UPLOAD_REQUEST){
// end bufor data with NULL
((u8_t*)uip_appdata)[uip_len] = '\0';
// do we have to find start of data?
if(!hs->data_start_found){
if(!httpd_findandstore_firstchunk()){
printf("## Error: couldn't find start of data in next packet!\n");
httpd_state_reset();
uip_abort();
return;
} else {
hs->data_start_found = 1;
}
return;
}
hs->upload += (unsigned int)uip_len;
if(!webfailsafe_upload_failed){
memcpy((void *)webfailsafe_data_pointer, (void *)uip_appdata, uip_len);
webfailsafe_data_pointer += uip_len;
}
httpd_download_progress();
// if we have collected all data
if(hs->upload >= hs->upload_total){
printf("\n\n");
// end of post upload
webfailsafe_post_done = 1;
#ifdef CONFIG_RTS3913
net_boot_file_size = (ulong)hs->upload_total;
if(do_http_check(net_boot_file_size)<0)
#else
NetBootFileXferSize = (ulong)hs->upload_total;
if(do_http_check(NetBootFileXferSize)<0)
#endif
{
webfailsafe_upload_failed = 1;
}
else
{
webfailsafe_firmware_chk_success = 1;
}
// which website will be returned
if(!webfailsafe_upload_failed){
fs_open(file_flashing_html.name, &fsfile);
} else {
fs_open(file_fail_html.name, &fsfile);
}
httpd_state_reset();
hs->state = STATE_FILE_REQUEST;
hs->dataptr = (u8_t *)fsfile.data;
hs->upload = fsfile.len;
uip_send(hs->dataptr, (hs->upload > uip_mss() ? uip_mss() : hs->upload));
}
}
return;
}
break;
default:
// we shouldn't get here... we are listening only on port 80
uip_abort();
break;
}
}