#include "web-server.h" #include #include "espfs.h" #include "config.h" #include "cgi.h" #include "cmd.h" #include "serbridge.h" #define WEB_CB "webCb" #define MAX_VARS 20 static char* web_server_reasons[] = { "load", "refresh", "button", "submit" }; char * webServerPages = NULL; char * ICACHE_FLASH_ATTR WEB_UserPages() { return webServerPages; } void ICACHE_FLASH_ATTR WEB_BrowseFiles() { char buffer[1024]; buffer[0] = 0; if( espFsIsValid( userPageCtx ) ) { EspFsIterator it; espFsIteratorInit(userPageCtx, &it); while( espFsIteratorNext(&it) ) { int nlen = os_strlen(it.name); if( nlen >= 6 ) { if( os_strcmp( it.name + nlen-5, ".html" ) == 0 ) { char sh_name[17]; int spos = nlen-5; while( spos > 0 ) { if( it.name[spos+1] == '/' ) break; spos--; } int ps = nlen-5-spos; if( ps > 16 ) ps = 16; os_memcpy(sh_name, it.name + spos, ps); sh_name[ps] = 0; os_strcat(buffer, ", \""); os_strcat(buffer, sh_name); os_strcat(buffer, "\", \"/"); os_strcat(buffer, it.name); os_strcat(buffer, "\""); } } if( os_strlen(buffer) > 600 ) break; } } if( webServerPages != NULL ) os_free( webServerPages ); int len = os_strlen(buffer) + 1; webServerPages = (char *)os_malloc( len ); os_memcpy( webServerPages, buffer, len ); } void ICACHE_FLASH_ATTR WEB_Init() { espFsInit(userPageCtx, (void *)getUserPageSectionStart(), ESPFS_FLASH); if( espFsIsValid( userPageCtx ) ) os_printf("Valid user file system found!\n"); else os_printf("No user file system found!\n"); WEB_BrowseFiles(); } int ICACHE_FLASH_ATTR WEB_CgiJsonHook(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. void * cgiData = connData->cgiData; if( cgiData == NULL ) { if( !flashConfig.slip_enable ) { errorResponse(connData, 400, "Slip processing is disabled!"); return HTTPD_CGI_DONE; } CmdCallback* cb = cmdGetCbByName( WEB_CB ); if( cb == NULL ) { errorResponse(connData, 500, "No MCU callback is registered!"); return HTTPD_CGI_DONE; } if( serbridgeInProgramming() ) { errorResponse(connData, 500, "Slip disabled at programming mode!"); return HTTPD_CGI_DONE; } char reasonBuf[16]; int i; int len = httpdFindArg(connData->getArgs, "reason", reasonBuf, sizeof(reasonBuf)); if( len < 0 ) { errorResponse(connData, 400, "No reason specified!"); return HTTPD_CGI_DONE; } RequestReason reason = INVALID; for(i=0; i < sizeof(web_server_reasons)/sizeof(char *); i++) { if( os_strcmp( web_server_reasons[i], reasonBuf ) == 0 ) reason = (RequestReason)i; } if( reason == INVALID ) { errorResponse(connData, 400, "Invalid reason!"); return HTTPD_CGI_DONE; } char body[1024]; int bodyPtr = 0; int argNum = 0; char *argPos[MAX_VARS]; int argLen[MAX_VARS]; switch(reason) { case BUTTON: argLen[0] = httpdFindArg(connData->getArgs, "id", body, sizeof(body)); if( argLen[0] <= 0 ) { errorResponse(connData, 400, "No button ID specified!"); return HTTPD_CGI_DONE; } argPos[0] = body; argNum++; break; case SUBMIT: { if( connData->post->received < connData->post->len ) { errorResponse(connData, 400, "Post too large!"); return HTTPD_CGI_DONE; } int bptr = 0; while( bptr < connData->post->len ) { if( argNum >= MAX_VARS ) { errorResponse(connData, 400, "Too many variables!"); return HTTPD_CGI_DONE; } char * line = connData->post->buff + bptr; char * eo = os_strchr(line, '&' ); if( eo != NULL ) { *eo = 0; bptr = eo - connData->post->buff + 1; } else { eo = line + os_strlen( line ); bptr = connData->post->len; } int len = os_strlen(line); while( len >= 1 && ( line[len-1] == '\r' || line[len-1] == '\n' )) len--; line[len] = 0; char * val = os_strchr(line, '='); if( val != NULL ) { *val = 0; char * name = line; int vblen = os_strlen(val+1) * 2; char value[vblen]; httpdUrlDecode(val+1, strlen(val+1), value, vblen); int namLen = os_strlen(name); int valLen = os_strlen(value); int totallen = namLen + valLen + 2; if( bodyPtr + totallen > sizeof(body) - 10 ) { errorResponse(connData, 400, "Post too large!"); return HTTPD_CGI_DONE; } argPos[argNum] = body + bodyPtr; body[bodyPtr++] = (char)WEB_STRING; os_strcpy( body + bodyPtr, name ); bodyPtr += namLen; body[bodyPtr++] = 0; os_strcpy( body + bodyPtr, value ); bodyPtr += valLen; argLen[argNum++] = totallen; } } } break; case LOAD: case REFRESH: default: break; } os_printf("Web callback to MCU: %s\n", reasonBuf); cmdResponseStart(CMD_WEB_REQ_CB, (uint32_t)cb->callback, 4 + argNum); uint16_t r = (uint16_t)reason; cmdResponseBody(&r, sizeof(uint16_t)); cmdResponseBody(&connData->conn->proto.tcp->remote_ip, 4); cmdResponseBody(&connData->conn->proto.tcp->remote_port, sizeof(uint16_t)); cmdResponseBody(connData->url, os_strlen(connData->url)); int j; for( j=0; j < argNum; j++ ) cmdResponseBody(argPos[j], argLen[j]); cmdResponseEnd(); if( reason == SUBMIT ) { httpdStartResponse(connData, 204); httpdEndHeaders(connData); return HTTPD_CGI_DONE; } connData->cgiData = (void *)1; } if( connData->cgiArg != NULL ) // arrived data from MCU { char jsonBuf[1500]; int jsonPtr = 0; jsonBuf[jsonPtr++] = '{'; CmdRequest * req = (CmdRequest *)(connData->cgiArg); int c = 2; while( c++ < cmdGetArgc(req) ) { int len = cmdArgLen(req); char buf[len+1]; buf[len] = 0; cmdPopArg(req, buf, len); if(len == 0) break; // last argument if( c > 3 ) // skip the first argument jsonBuf[jsonPtr++] = ','; if( jsonPtr + 20 + len > sizeof(jsonBuf) ) { errorResponse(connData, 500, "Response too large!"); return HTTPD_CGI_DONE; } WebValueType type = (WebValueType)buf[0]; int nameLen = os_strlen(buf+1); jsonBuf[jsonPtr++] = '"'; os_memcpy(jsonBuf + jsonPtr, buf + 1, nameLen); jsonPtr += nameLen; jsonBuf[jsonPtr++] = '"'; jsonBuf[jsonPtr++] = ':'; char * value = buf + 2 + nameLen; switch(type) { case WEB_NULL: os_memcpy(jsonBuf + jsonPtr, "null", 4); jsonPtr += 4; break; case WEB_INTEGER: { int v; os_memcpy( &v, value, 4); char intbuf[20]; os_sprintf(intbuf, "%d", v); os_strcpy(jsonBuf + jsonPtr, intbuf); jsonPtr += os_strlen(intbuf); } break; case WEB_BOOLEAN: if( *value ) { os_memcpy(jsonBuf + jsonPtr, "true", 4); jsonPtr += 4; } else { os_memcpy(jsonBuf + jsonPtr, "false", 5); jsonPtr += 5; } break; case WEB_FLOAT: { float f; os_memcpy( &f, value, 4); char intbuf[20]; os_sprintf(intbuf, "%f", f); os_strcpy(jsonBuf + jsonPtr, intbuf); jsonPtr += os_strlen(intbuf); } break; case WEB_STRING: jsonBuf[jsonPtr++] = '"'; while(*value) { if( *value == '\\' || *value == '"' ) jsonBuf[jsonPtr++] = '\\'; jsonBuf[jsonPtr++] = *(value++); } jsonBuf[jsonPtr++] = '"'; break; case WEB_JSON: os_memcpy(jsonBuf + jsonPtr, value, len - 2 - nameLen); jsonPtr += len - 2 - nameLen; break; } } jsonBuf[jsonPtr++] = '}'; noCacheHeaders(connData, 200); httpdHeader(connData, "Content-Type", "application/json"); char cl[16]; os_sprintf(cl, "%d", jsonPtr); httpdHeader(connData, "Content-Length", cl); httpdEndHeaders(connData); httpdSend(connData, jsonBuf, jsonPtr); return HTTPD_CGI_DONE; } return HTTPD_CGI_MORE; } void ICACHE_FLASH_ATTR WEB_Data(CmdPacket *cmd) { CmdRequest req; cmdRequest(&req, cmd); if (cmdGetArgc(&req) < 2) return; uint8_t ip[4]; cmdPopArg(&req, ip, 4); uint16_t port; cmdPopArg(&req, &port, 2); httpdNotify(ip, port, &req); }