#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 httpMimeTypeJson[] = "application/json" ; // TEXT MIME type const char httpMimeTypeBinary[] = "application/octet-stream" ; // binary/file MIME type const char htmlDoctype[] = ""; const char htmlHeadMain[] = "" "" ""; const char htmlHeadStyle[] = ""; 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 | " "Support project
"; // make sure that USER_SW_VER is set on all platforms // Automatic Github builds are setting it externally, // but it may not be set while doing a test build on developer PC #ifndef USER_SW_VER #ifdef WINDOWS #define USER_SW_VER "Win_Test" #elif PLATFORM_XR809 #define USER_SW_VER "XR809_Test" #elif defined(PLATFORM_BK7231N) #define USER_SW_VER "BK7231N_Test" #elif defined(PLATFORM_BK7231T) #define USER_SW_VER "BK7231T_Test" #elif defined(PLATFORM_BL602) #define USER_SW_VER "BL602_Test" #elif defined(PLATFORM_W600) #define USER_SW_VER "W600_Test" #elif defined(PLATFORM_W800) #define USER_SW_VER "W800_Test" #elif defined(PLATFORM_BL602) #define USER_SW_VER "BL602_Test" #else #define USER_SW_VER "unknown" #endif #endif 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){ if (!url || !callback){ return -1; } if (numCallbacks >= MAX_HTTP_CALLBACKS){ return -4; } 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(char *a, 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; } 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){ hprintf128(request, httpHeader, request->responseCode, type); poststr(request,"\r\n"); // next header poststr(request,httpCorsHeaders); poststr(request,"\r\n"); // end headers with double CRLF poststr(request,"\r\n"); } void http_html_start(http_request_t *request, const char *pagename) { // void HTTP_AddHeader(http_request_t *request) { poststr(request, htmlDoctype); poststr(request, ""); poststr(request, CFG_GetDeviceName()); // todo: check escaping if (pagename) { poststr(request, " - "); poststr(request, pagename); } poststr(request, ""); poststr(request, htmlHeadMain); poststr(request, htmlHeadStyle); poststr(request, htmlBodyStart); poststr(request, CFG_GetDeviceName()); // todo: check escaping poststr(request, htmlBodyStart2); } void http_html_end(http_request_t *request) { // was void HTTP_AddBuildFooter(http_request_t *request) { char upTimeStr[128]; unsigned char mac[32]; poststr(request, " | "); poststr(request, htmlFooterInfo); poststr(request, "
"); poststr(request, g_build_str); poststr(request, "
Online for "); misc_formatUpTimeString(Time_getUpTimeSeconds(), upTimeStr); poststr(request, upTimeStr); WiFI_GetMacAddress((char *)mac); sprintf(upTimeStr, "
Device MAC: %02X:%02X:%02X:%02X:%02X:%02X",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]); poststr(request, upTimeStr); sprintf(upTimeStr, "
Short name: %s, Chipset %s",CFG_GetShortDeviceName(),PLATFORM_MCU_NAME); poststr(request, upTimeStr); poststr(request, htmlBodyEnd); } 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; } void http_copyCarg(const char *atin, char *to, int maxSize) { int a, b; const unsigned char *at = (unsigned char *)atin; while(*at != 0 && *at != '&' && *at != ' ' && maxSize > 1) { #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'; *to++ = 16*a+b; at+=3; } else if (*at == '+') { *to++ = ' '; at++; } else { *to++ = *at++; } maxSize--; #endif } *to = 0; } 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++; http_copyCarg(at,o,maxSize); return 1; } 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", "PWM_n", "error", "error", "error", "error", "error", "error", "error", }; 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){ 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)); } void misc_formatUpTimeString(int totalSeconds, char *o) { int rem_days; int rem_hours; int rem_minutes; int rem_seconds; rem_days = totalSeconds / (24*60*60); totalSeconds = totalSeconds % (24*60*60); rem_hours = totalSeconds / (60*60); totalSeconds = totalSeconds % (60*60); rem_minutes = totalSeconds / (60); rem_seconds = totalSeconds % 60; *o = 0; if(rem_days > 0) { sprintf(o,"%i days, %i hours, %i minutes and %i seconds ",rem_days,rem_hours,rem_minutes,rem_seconds); } else if(rem_hours > 0) { sprintf(o,"%i hours, %i minutes and %i seconds ",rem_hours,rem_minutes,rem_seconds); } else if(rem_minutes > 0) { sprintf(o,"%i minutes and %i seconds ",rem_minutes,rem_seconds); } else { sprintf(o,"just %i seconds ",rem_seconds); } } int hprintf128(http_request_t *request, const char *fmt, ...){ va_list argList; //BaseType_t taken; char tmp[256]; va_start(argList, fmt); vsprintf(tmp, 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 = ""; if (request->received == 0){ ADDLOGF_ERROR("You gave request with NULL input"); return 0; } char *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) { *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) { *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; do { p = strchr(headers, '\r'); if (p != headers){ if (p){ if (request->numheaders < 16){ 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); 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,"config_dump_table")) return http_fn_config_dump_table(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); }