/* Some random cgi routines. Used in the LED example and the page that returns the entire flash as a binary. Also handles the hit counter on the main page. */ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * Jeroen Domburg wrote this file. As long as you retain * this notice you can do whatever you want with this stuff. If we meet some day, * and you think this stuff is worth it, you can buy me a beer in return. * ---------------------------------------------------------------------------- */ #include #include #include "user_interface.h" #include "mem.h" #include "httpd.h" #include "cgi.h" #include "io.h" #include #include "espmissingincludes.h" //cause I can't be bothered to write an ioGetLed() static char currLedState=0; //Cgi that turns the LED on or off according to the 'led' param in the POST data int ICACHE_FLASH_ATTR cgiLed(HttpdConnData *connData) { int len; char buff[1024]; if (connData->conn==NULL) { //Connection aborted. Clean up. return HTTPD_CGI_DONE; } len=httpdFindArg(connData->postBuff, "led", buff, sizeof(buff)); if (len!=0) { currLedState=atoi(buff); ioLed(currLedState); } httpdRedirect(connData, "led.tpl"); return HTTPD_CGI_DONE; } //Template code for the led page. int ICACHE_FLASH_ATTR tplLed(HttpdConnData *connData, char *token, void **arg) { char buff[128]; if (token==NULL) return HTTPD_CGI_DONE; os_strcpy(buff, "Unknown"); if (os_strcmp(token, "ledstate")==0) { if (currLedState) { os_strcpy(buff, "on"); } else { os_strcpy(buff, "off"); } } httpdSend(connData, buff, -1); return HTTPD_CGI_DONE; } static long hitCounter=0; //Template code for the counter on the index page. int ICACHE_FLASH_ATTR tplCounter(HttpdConnData *connData, char *token, void **arg) { char buff[128]; if (token==NULL) return HTTPD_CGI_DONE; if (os_strcmp(token, "counter")==0) { hitCounter++; os_sprintf(buff, "%ld", hitCounter); } httpdSend(connData, buff, -1); return HTTPD_CGI_DONE; } //Cgi that reads the SPI flash. Assumes 512KByte flash. int ICACHE_FLASH_ATTR cgiReadFlash(HttpdConnData *connData) { int *pos=(int *)&connData->cgiData; if (connData->conn==NULL) { //Connection aborted. Clean up. return HTTPD_CGI_DONE; } if (*pos==0) { os_printf("Start flash download.\n"); httpdStartResponse(connData, 200); httpdHeader(connData, "Content-Type", "application/bin"); httpdEndHeaders(connData); *pos=0x40200000; return HTTPD_CGI_MORE; } //Send 1K of flash per call. We will get called again if we haven't sent 512K yet. espconn_sent(connData->conn, (uint8 *)(*pos), 1024); *pos+=1024; if (*pos>=0x40200000+(512*1024)) return HTTPD_CGI_DONE; else return HTTPD_CGI_MORE; } #define LOOKING_FOR_SECTION 1 #define CHECKING_HEADERS 2 #define IN_DATA 3 typedef struct updateState_t { char delimiter[61]; int step; } updateState_t; char* bin_strstr(char *haystack, char *needle, int haystackLen, int needleLen){ if(needleLen < 0){ needleLen = strlen(needle); } char * end = haystack + haystackLen; for(; haystack < end; haystack++){ if(*haystack == *needle){ int match = true; for(int i = 1; i< needleLen; i++){ if(*(needle + i) != *(haystack + i)){ match = false; break; } } if(match){ return haystack; } } } return NULL; } //Cgi to allow user to upload a new espfs image int ICACHE_FLASH_ATTR updateWeb(HttpdConnData *connData) { os_printf("data size : %d\r\n", connData->postBuffLen); updateState_t *state; if (connData->conn==NULL) { //Connection aborted. Clean up. return HTTPD_CGI_DONE; } if(connData->requestType == GET){ httpdStartResponse(connData, 200); httpdHeader(connData, "Content-Length", "135"); httpdEndHeaders(connData); httpdSend(connData, "
", 135); }else if(connData->requestType == POST){ if(connData->postReceived == connData->postBuffLen){ //it's the first time this handler has been called for this request connData->cgiPrivData = (int*)os_malloc(sizeof(updateState_t)); state = connData->cgiPrivData; state->step = LOOKING_FOR_SECTION; }else{ state = connData->cgiPrivData; } char *b; char *p; char * end = connData->postBuff + connData->postBuffLen; for(b = connData->postBuff; b < end; b++){ if(state->step == LOOKING_FOR_SECTION){ if((p = bin_strstr(b, connData->multipartBoundary, connData->postBuffLen - (b - connData->postBuff), -1)) != NULL){ os_printf("Found section\r\n"); // We've found one of the sections, now make sure it's the file b = p + strlen(connData->multipartBoundary) + 2; state->step = CHECKING_HEADERS; }else{ os_printf("Not Found section\r\n"); break; } } if(state->step == CHECKING_HEADERS){ //os_printf("next data: %s\n", b); if(!os_strncmp(b, "Content-Disposition: form-data", 30)){ os_printf("Correct header\r\n"); // It's the Content-Disposition header // Find the end of the line p = os_strstr(b, "\r\n"); // turn the end of the line into a string end so we can search within the line *p = 0; if(os_strstr(b, "name=\"file\"") != NULL){ os_printf("Correct file\r\n"); b = p + 2; // it's the correct section, skip to the data if((p = os_strstr(b, "\r\n\r\n")) != NULL){ os_printf("Skipping to data\r\n"); b = p + 4; state->step = IN_DATA; }else{ os_printf("Couldn't find line endings\r\n"); os_printf("data: %s\n", b); return HTTPD_CGI_DONE; } }else{ // it's the wrong section, skip to the next boundary b = p + 1; state->step = LOOKING_FOR_SECTION; } }else{ // Skip to the next header p = os_strstr(b, "\r\n"); b = p + 2; } } if(state->step == IN_DATA){ //os_printf("In data\r\n"); // Make sure it doesn't contain the boundary, but we can't rely on there being no zeroes so can't use strstr if((p = bin_strstr(b, connData->multipartBoundary, connData->postBuffLen - (b - connData->postBuff), -1)) == NULL){ // all of the data is file data }else{ if(b < (p - 2)){ // -2 bytes for the \r\n // This is a byte that's part of the file os_printf("%c", *b); }else{ os_printf("\r\nComplete\r\n"); } } } } /* os_printf("postReceived: %d\r\n", connData->postReceived); os_printf("postLen : %d\r\n", connData->postLen); os_printf("data size : %d\r\n", connData->postBuffLen); */ if(connData->postReceived >= connData->postLen){ httpdStartResponse(connData, 204); if (connData->cgiPrivData!=NULL) os_free(connData->cgiPrivData); return HTTPD_CGI_DONE; }else{ return HTTPD_CGI_MORE; } } return HTTPD_CGI_DONE; }