#include "../new_common.h" #include "../logging/logging.h" #include "ctype.h" #include "new_http.h" #include "http_fns.h" #include "../new_pins.h" #include "../new_cfg.h" #include "../ota/ota.h" #include "../hal/hal_wifi.h" // define the feature ADDLOGF_XXX will use #define LOG_FEATURE LOG_FEATURE_HTTP const char httpHeader[] = "HTTP/1.1 %d OK\nContent-type: %s"; // HTTP header const char httpMimeTypeHTML[] = "text/html"; // HTML MIME type const char httpMimeTypeText[] = "text/plain"; // TEXT MIME type const char httpMimeTypeXML[] = "text/xml"; // TEXT MIME type const char httpMimeTypeJson[] = "application/json"; // TEXT MIME type const char httpMimeTypeBinary[] = "application/octet-stream"; // binary/file MIME type const char htmlShortcutIcon[] = ""; const char htmlDoctype[] = ""; const char htmlHeadMeta[] = "" "" ""; const char htmlBodyStart[] = "" "" "
" "

" ""; const char htmlBodyStart2[] = "

"; const char htmlBodyEnd[] = "
"; const char htmlFooterReturnToMenu[] = "Return to menu"; const char htmlFooterRefreshLink[] = "Refresh"; const char htmlFooterReturnToCfgLink[] = "Return to cfg"; const char htmlFooterInfo[] = "Read more | " "Devices List | " "Commands | " "Support project
"; const char* g_build_str = "Build on " __DATE__ " " __TIME__ " version " USER_SW_VER; // Show GIT version at Build line; const char httpCorsHeaders[] = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept"; // TEXT MIME type const char* methodNames[] = { "GET", "PUT", "POST", "OPTIONS" }; #if WINDOWS #define os_free free #define os_malloc malloc #endif void misc_formatUpTimeString(int totalSeconds, char* o); int Time_getUpTimeSeconds(); typedef struct http_callback_tag { char* url; int method; http_callback_fn callback; } http_callback_t; #define MAX_HTTP_CALLBACKS 32 static http_callback_t* callbacks[MAX_HTTP_CALLBACKS]; static int numCallbacks = 0; int HTTP_RegisterCallback(const char* url, int method, http_callback_fn callback) { int i; if (!url || !callback) { return -1; } if (numCallbacks >= MAX_HTTP_CALLBACKS) { return -4; } for (i = 0; i < MAX_HTTP_CALLBACKS; i++) { if (callbacks[i]) { if (callbacks[i]->callback == callback && !strcmp(callbacks[i]->url, url) && callbacks[i]->method == method) { return i; } } } callbacks[numCallbacks] = (http_callback_t*)os_malloc(sizeof(http_callback_t)); if (!callbacks[numCallbacks]) { return -2; } callbacks[numCallbacks]->url = (char*)os_malloc(strlen(url) + 1); if (!callbacks[numCallbacks]->url) { os_free(callbacks[numCallbacks]); return -3; } strcpy(callbacks[numCallbacks]->url, url); callbacks[numCallbacks]->callback = callback; callbacks[numCallbacks]->method = method; numCallbacks++; // success return 0; } int my_strnicmp(const char* a, const char* b, int len) { int i; for (i = 0; i < len; i++) { char x = *a; char y = *b; if (!x || !y) return 1; if ((x | 0x20) != (y | 0x20)) return 1; a++; b++; } return 0; } /// @brief Write escaped data to the response. /// @param request /// @param str void poststr_escaped(http_request_t* request, char* str) { if (str == NULL) { postany(request, NULL, 0); return; } int i; bool foundChar = false; int len = strlen(str); //Do a quick check if escaping is necessary for (i = 0; (foundChar == false) && (i < len); i++) { switch (str[i]) { case '<': foundChar = true; break; case '>': foundChar = true; break; case '&': foundChar = true; break; case '"': foundChar = true; break; } } if (foundChar) { for (i = 0; i < len; i++) { switch (str[i]) { case '<': postany(request, "<", 4); break; case '>': postany(request, ">", 4); break; case '&': postany(request, "&", 5); break; case '"': postany(request, """, 6); break; default: postany(request, str + i, 1); break; } } } else { postany(request, str, strlen(str)); } } bool http_startsWith(const char* base, const char* substr) { while (*substr != 0) { if (*base != *substr) return false; if (*base == 0) return false; base++; substr++; } return true; } bool http_checkUrlBase(const char* base, const char* fileName) { while (*base != 0 && *base != '?' && *base != ' ') { if (*base != *fileName) return false; if (*base == 0) return false; base++; fileName++; } if (*fileName != 0) return false; return true; } void http_setup(http_request_t* request, const char* type) { hprintf255(request, httpHeader, request->responseCode, type); poststr(request, "\r\n"); // next header poststr(request, httpCorsHeaders); #if 0 poststr(request, "Server: Tasmota/10.1.0 (ESP8266EX)"); poststr(request, "\r\n"); poststr(request, "Cache-Control: no-cache, no-store, must-revalidate"); poststr(request, "\r\n"); poststr(request, "Pragma: no-cache"); poststr(request, "\r\n"); poststr(request, "Expires: -1"); poststr(request, "\r\n"); poststr(request, "Accept-Ranges: none"); poststr(request, "\r\n"); poststr(request, "Transfer-Encoding: chunked"); #endif poststr(request, "\r\n"); poststr(request, "Connection: close"); poststr(request, "\r\n"); // end headers with double CRLF poststr(request, "\r\n"); } void http_html_start(http_request_t* request, const char* pagename) { poststr(request, htmlDoctype); poststr(request, ""); poststr(request, CFG_GetDeviceName()); if (pagename) { hprintf255(request, " - %s", pagename); } poststr(request, ""); poststr(request, htmlShortcutIcon); poststr(request, htmlHeadMeta); poststr(request, htmlHeadStyle); poststr(request, ""); poststr(request, htmlBodyStart); poststr(request, CFG_GetDeviceName()); poststr(request, htmlBodyStart2); } void http_html_end(http_request_t* request) { char upTimeStr[128]; unsigned char mac[32]; poststr(request, " | "); poststr(request, htmlFooterInfo); poststr(request, "
"); poststr(request, g_build_str); hprintf255(request, "
Online for -", Time_getUpTimeSeconds()); WiFI_GetMacAddress((char*)mac); snprintf(upTimeStr, sizeof(upTimeStr), "
Device MAC: %02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); poststr(request, upTimeStr); snprintf(upTimeStr, sizeof(upTimeStr), "
Short name: %s, Chipset %s", CFG_GetShortDeviceName(), PLATFORM_MCU_NAME); poststr(request, upTimeStr); poststr(request, htmlBodyEnd); poststr(request, pageScript); } const char* http_checkArg(const char* p, const char* n) { while (1) { if (*n == 0 && (*p == 0 || *p == '=')) return p; if (*p != *n) return 0; p++; n++; } return p; } int http_copyCarg(const char* atin, char* to, int maxSize) { int a, b; int realSize; const unsigned char* at = (unsigned char*)atin; realSize = 0; while (*at != 0 && *at != '&' && *at != ' ') { #if 0 * to = *at; to++; at++; maxSize--; #else if ((*at == '%') && ((a = at[1]) && (b = at[2])) && (isxdigit(a) && isxdigit(b))) { if (a >= 'a') a -= 'a' - 'A'; if (a >= 'A') a -= ('A' - 10); else a -= '0'; if (b >= 'a') b -= 'a' - 'A'; if (b >= 'A') b -= ('A' - 10); else b -= '0'; // can we afford to place this char in the target? if (maxSize > 1) { maxSize--; *to++ = 16 * a + b; } realSize++; at += 3; } else if (*at == '+') { // can we afford to place this char in the target? if (maxSize > 1) { maxSize--; *to++ = ' '; } realSize++; at++; } else { // can we afford to place this char in the target? if (maxSize > 1) { maxSize--; *to++ = *at; } realSize++; at++; } #endif } *to = 0; return realSize; } int http_getArg(const char* base, const char* name, char* o, int maxSize) { *o = '\0'; while (*base != '?') { if (*base == 0) return 0; base++; } base++; while (*base) { const char* at = http_checkArg(base, name); if (at) { at++; return http_copyCarg(at, o, maxSize); } while (*base != '&') { if (*base == 0) { return 0; } base++; } base++; } return 0; } int http_getArgInteger(const char* base, const char* name) { char tmp[16]; if (http_getArg(base, name, tmp, sizeof(tmp)) == 0) return 0; return atoi(tmp); } const char* htmlPinRoleNames[] = { " ", "Rel", "Rel_n", "Btn", "Btn_n", "LED", "LED_n", "PWM", "WifiLED", "WifiLED_n", "Btn_Tgl_All", "Btn_Tgl_All_n", "dInput", "dInput_n", "TglChanOnTgl", "dInput_NoPullUp", "dInput_NoPullUp_n", "BL0937SEL", "BL0937CF", "BL0937CF1", "ADC", "SM2135DAT", "SM2135CLK", "BP5758D_DAT", "BP5758D_CLK", "BP1658CJ_DAT", "BP1658CJ_CLK", "PWM_n", "IRRecv", "IRSend", "Btn_NextColor", "Btn_NextColor_n", "Btn_NextDimmer", "Btn_NextDimmer_n", "AlwaysHigh", "AlwaysLow", "UCS1912_DIN", "SM16703P_DIN", "Btn_NextTemperature", "Btn_NextTemperature_n", "Btn_ScriptOnly", "Btn_ScriptOnly_n", "DHT11", "DHT12", "DHT21", "DHT22", "CHT8305_SDA", "CHT8305_SCK", "SHT3X_SDA", "SHT3X_SCK", "SoftSDA", "SoftSCL", "SM2235DAT", "SM2235CLK", "BridgeFWD", "BridgeREV", "error", "error", "error", "error", "error", }; const char *PIN_RoleToString(int role) { return htmlPinRoleNames[role]; } int PIN_ParsePinRoleName(const char* name) { int i; for (i = 0; i < IOR_Total_Options; i++) { if (!stricmp(name, htmlPinRoleNames[i])) return i; } return IOR_Total_Options; } void setupAllWB2SPinsAsButtons() { PIN_SetPinRoleForPinIndex(6, IOR_Button); PIN_SetPinChannelForPinIndex(6, 1); PIN_SetPinRoleForPinIndex(7, IOR_Button); PIN_SetPinChannelForPinIndex(7, 1); PIN_SetPinRoleForPinIndex(8, IOR_Button); PIN_SetPinChannelForPinIndex(8, 1); PIN_SetPinRoleForPinIndex(23, IOR_Button); PIN_SetPinChannelForPinIndex(23, 1); PIN_SetPinRoleForPinIndex(24, IOR_Button); PIN_SetPinChannelForPinIndex(24, 1); PIN_SetPinRoleForPinIndex(26, IOR_Button); PIN_SetPinChannelForPinIndex(26, 1); PIN_SetPinRoleForPinIndex(27, IOR_Button); PIN_SetPinChannelForPinIndex(27, 1); } // add some more output safely, sending if necessary. // call with str == NULL to force send. - can be binary. // supply length int postany(http_request_t* request, const char* str, int len) { #if PLATFORM_BL602 send(request->fd, str, len, 0); return 0; #else int currentlen; int addlen = len; if (NULL == str) { // fd will be NULL for unit tests where HTTP packet is faked locally if (request->fd == 0) { return request->replylen; } if (request->replylen > 0) { send(request->fd, request->reply, request->replylen, 0); } request->reply[0] = 0; request->replylen = 0; return 0; } currentlen = request->replylen; if (currentlen + addlen >= request->replymaxlen) { send(request->fd, request->reply, request->replylen, 0); request->reply[0] = 0; request->replylen = 0; currentlen = 0; } while (addlen >= request->replymaxlen) { if (request->replylen > 0) { send(request->fd, request->reply, request->replylen, 0); request->replylen = 0; } send(request->fd, str, (request->replymaxlen - 1), 0); addlen -= (request->replymaxlen - 1); str += (request->replymaxlen - 1); rtos_delay_milliseconds(1); } memcpy(request->reply + request->replylen, str, addlen); request->replylen += addlen; return (currentlen + addlen); #endif } // add some more output safely, sending if necessary. // call with str == NULL to force send. int poststr(http_request_t* request, const char* str) { if (str == NULL) { return postany(request, NULL, 0); } return postany(request, str, strlen(str)); } int hprintf255(http_request_t* request, const char* fmt, ...) { va_list argList; //BaseType_t taken; char tmp[256]; memset(tmp, 0, sizeof(tmp)); va_start(argList, fmt); vsnprintf(tmp, 255, fmt, argList); va_end(argList); return postany(request, tmp, strlen(tmp)); } int HTTP_ProcessPacket(http_request_t* request) { int i; char* p; char* headers; char* protocol; //int bChanged = 0; char* urlStr = ""; char* recvbuf; if (request->received == 0) { ADDLOGF_ERROR("You gave request with NULL input"); return 0; } recvbuf = request->received; for (i = 0; i < sizeof(methodNames) / sizeof(*methodNames); i++) { if (http_startsWith(recvbuf, methodNames[i])) { urlStr = recvbuf + strlen(methodNames[i]) + 2; // skip method name plus space, plus slash request->method = i; break; } } if (request->method == -1) { ADDLOGF_ERROR("unsupported method %7s", recvbuf); return 0; } if (request->reply == 0) { ADDLOGF_ERROR("You gave request with NULL buffer"); return 0; } if (request->method == HTTP_GET) { //ADDLOG_INFO(LOG_FEATURE_HTTP, "HTTP request\n"); } else { //ADDLOG_INFO(LOG_FEATURE_HTTP, "Other request\n"); } // if OPTIONS, return now - for CORS if (request->method == HTTP_OPTIONS) { http_setup(request, httpMimeTypeHTML); i = strlen(request->reply); return i; } // chop URL at space p = strchr(urlStr, ' '); if (p != 0) { if (*p) { *p = '\0'; p++; // past space } else { ADDLOGF_ERROR("invalid request\n"); return 0; } } request->url = urlStr; // protocol is next, termed by \r\n protocol = p; p = strchr(protocol, '\r'); if (p != 0) { if (*p) { *p = '\0'; p++; // past \r p++; // past \n } else { ADDLOGF_ERROR("invalid request\n"); return 0; } } // i.e. not received request->contentLength = -1; headers = p; if (headers != 0) { do { p = strchr(headers, '\r'); if (p != headers) { if (p) { if (request->numheaders < MAX_HEADERS) { request->headers[request->numheaders] = headers; request->numheaders++; } // pick out contentLength if (!my_strnicmp(headers, "Content-Length:", 15)) { request->contentLength = atoi(headers + 15); } *p = 0; p++; // past \r p++; // past \n headers = p; } else { break; } } if (*p == '\r') { // end of headers *p = 0; p++; p++; break; } } while (1); } if (p == 0) { request->bodystart = 0; request->bodylen = 0; } else { request->bodystart = p; request->bodylen = request->receivedLen - (p - request->received); } #if 0 postany(request, "test", 4); return 0; #elif 0 return http_fn_empty_url(request); #endif // look for a callback with this URL and method, or HTTP_ANY for (i = 0; i < numCallbacks; i++) { char* url = callbacks[i]->url; if (http_startsWith(urlStr, &url[1])) { int method = callbacks[i]->method; if (method == HTTP_ANY || method == request->method) { return callbacks[i]->callback(request); } } } if (http_checkUrlBase(urlStr, "")) return http_fn_empty_url(request); if (http_checkUrlBase(urlStr, "testmsg")) return http_fn_testmsg(request); if (http_checkUrlBase(urlStr, "index")) return http_fn_index(request); if (http_checkUrlBase(urlStr, "about")) return http_fn_about(request); if (http_checkUrlBase(urlStr, "cfg_mqtt")) return http_fn_cfg_mqtt(request); if (http_checkUrlBase(urlStr, "cfg_mqtt_set")) return http_fn_cfg_mqtt_set(request); if (http_checkUrlBase(urlStr, "cfg_webapp")) return http_fn_cfg_webapp(request); if (http_checkUrlBase(urlStr, "cfg_webapp_set")) return http_fn_cfg_webapp_set(request); if (http_checkUrlBase(urlStr, "cfg_wifi")) return http_fn_cfg_wifi(request); if (http_checkUrlBase(urlStr, "cfg_name")) return http_fn_cfg_name(request); if (http_checkUrlBase(urlStr, "cfg_wifi_set")) return http_fn_cfg_wifi_set(request); if (http_checkUrlBase(urlStr, "cfg_loglevel_set")) return http_fn_cfg_loglevel_set(request); if (http_checkUrlBase(urlStr, "cfg_mac")) return http_fn_cfg_mac(request); if (http_checkUrlBase(urlStr, "flash_read_tool")) return http_fn_flash_read_tool(request); if (http_checkUrlBase(urlStr, "uart_tool")) return http_fn_uart_tool(request); if (http_checkUrlBase(urlStr, "cmd_tool")) return http_fn_cmd_tool(request); if (http_checkUrlBase(urlStr, "startup_command")) return http_fn_startup_command(request); if (http_checkUrlBase(urlStr, "cfg_generic")) return http_fn_cfg_generic(request); if (http_checkUrlBase(urlStr, "cfg_startup")) return http_fn_cfg_startup(request); if (http_checkUrlBase(urlStr, "cfg_dgr")) return http_fn_cfg_dgr(request); if (http_checkUrlBase(urlStr, "cfg_quick")) return http_fn_cfg_quick(request); if (http_checkUrlBase(urlStr, "ha_cfg")) return http_fn_ha_cfg(request); if (http_checkUrlBase(urlStr, "ha_discovery")) return http_fn_ha_discovery(request); if (http_checkUrlBase(urlStr, "cfg")) return http_fn_cfg(request); if (http_checkUrlBase(urlStr, "cfg_pins")) return http_fn_cfg_pins(request); if (http_checkUrlBase(urlStr, "cfg_ping")) return http_fn_cfg_ping(request); if (http_checkUrlBase(urlStr, "ota")) return http_fn_ota(request); if (http_checkUrlBase(urlStr, "ota_exec")) return http_fn_ota_exec(request); if (http_checkUrlBase(urlStr, "cm")) return http_fn_cm(request); return http_fn_other(request); } /* NOTE: The following fields should not be manually edited. Instead, edit the script/css files in this folder and then re-generate the fields by running gulp. Gulp tasks should automatically appear in Visual Code or can be invoked from console. See https://github.com/openshwprojects/OpenBK7231T_App/blob/main/BUILDING.md for gulp setup. */ //region_start htmlHeadStyle const char htmlHeadStyle[] = ""; //region_end htmlHeadStyle //region_start pageScript const char pageScript[] = ""; //region_end pageScript //region_start ha_discovery_script const char ha_discovery_script[] = ""; //region_end ha_discovery_script