http_server_handlers.c 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361
  1. /*
  2. Copyright (c) 2017-2021 Sebastien L
  3. */
  4. #include "http_server_handlers.h"
  5. #include "cJSON.h"
  6. #include "cmd_system.h"
  7. #include "esp_http_server.h"
  8. #include "esp_system.h"
  9. #include "freertos/FreeRTOS.h"
  10. #include "freertos/task.h"
  11. #include "squeezelite-ota.h"
  12. #include <inttypes.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include "Configurator.h"
  16. #include "accessors.h"
  17. #include "argtable3/argtable3.h"
  18. #include "esp_console.h"
  19. #include "esp_vfs.h"
  20. #include "messaging.h"
  21. #include "network_status.h"
  22. #include "network_wifi.h"
  23. #include "platform_console.h"
  24. #include "platform_esp32.h"
  25. #include "sys/param.h"
  26. #include "tools.h"
  27. #include "pb_encode.h"
  28. #include "pb_decode.h"
  29. #include "Status.pb.h"
  30. #define HTTP_STACK_SIZE (5 * 1024)
  31. const char str_na[] = "N/A";
  32. #define STR_OR_NA(s) s ? s : str_na
  33. /* @brief tag used for ESP serial console messages */
  34. static const char TAG[] = "httpd_handlers";
  35. static const char* www_dir = "/spiffs/www";
  36. SemaphoreHandle_t http_server_config_mutex = NULL;
  37. extern RingbufHandle_t messaging;
  38. #define AUTH_TOKEN_SIZE 50
  39. typedef struct session_context {
  40. char* auth_token;
  41. bool authenticated;
  42. char* sess_ip_address;
  43. u16_t port;
  44. } session_context_t;
  45. extern cJSON* get_gpio_list(bool refresh);
  46. union sockaddr_aligned {
  47. struct sockaddr sa;
  48. struct sockaddr_storage st;
  49. struct sockaddr_in sin;
  50. struct sockaddr_in6 sin6;
  51. } aligned_sockaddr_t;
  52. esp_err_t post_handler_buff_receive(httpd_req_t* req);
  53. static const char redirect_payload1[] = "<html><head><title>Redirecting to Captive "
  54. "Portal</title><meta http-equiv='refresh' content='0; url=";
  55. static const char redirect_payload2[] =
  56. "'></head><body><p>Please wait, refreshing. If page does not refresh, click <a href='";
  57. static const char redirect_payload3[] = "'>here</a> to login.</p></body></html>";
  58. /**
  59. * @brief embedded binary data.
  60. * @see file "component.mk"
  61. * @see
  62. * https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#embedding-binary-data
  63. */
  64. esp_err_t redirect_processor(httpd_req_t* req, httpd_err_code_t error);
  65. char* alloc_get_http_header(httpd_req_t* req, const char* key) {
  66. char* buf = NULL;
  67. size_t buf_len;
  68. /* Get header value string length and allocate memory for length + 1,
  69. * extra byte for null termination */
  70. buf_len = httpd_req_get_hdr_value_len(req, key) + 1;
  71. if (buf_len > 1) {
  72. buf = malloc_init_external(buf_len);
  73. /* Copy null terminated value string into buffer */
  74. if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
  75. ESP_LOGD_LOC(TAG, "Found header => %s: %s", key, buf);
  76. }
  77. }
  78. return buf;
  79. }
  80. char* http_alloc_get_socket_address(httpd_req_t* req, u8_t local, in_port_t* portl) {
  81. socklen_t len;
  82. union sockaddr_aligned addr;
  83. len = sizeof(addr);
  84. ip_addr_t* ip_addr = NULL;
  85. char* ipstr = malloc_init_external(INET6_ADDRSTRLEN);
  86. typedef int (*getaddrname_fn_t)(int s, struct sockaddr* name, socklen_t* namelen);
  87. getaddrname_fn_t get_addr = NULL;
  88. int s = httpd_req_to_sockfd(req);
  89. if (s == -1) {
  90. free(ipstr);
  91. return strdup_psram("httpd_req_to_sockfd error");
  92. }
  93. ESP_LOGV_LOC(TAG, "httpd socket descriptor: %u", s);
  94. get_addr = local ? &lwip_getsockname : &lwip_getpeername;
  95. if (get_addr(s, (struct sockaddr*)&addr, &len) < 0) {
  96. ESP_LOGE_LOC(TAG, "Failed to retrieve socket address");
  97. sprintf(ipstr, "N/A (0.0.0.%u)", local);
  98. } else {
  99. if (addr.sin.sin_family != AF_INET) {
  100. ip_addr = (ip_addr_t*)&(addr.sin6.sin6_addr);
  101. inet_ntop(addr.sa.sa_family, ip_addr, ipstr, INET6_ADDRSTRLEN);
  102. ESP_LOGV_LOC(TAG, "Processing an IPV6 address : %s", ipstr);
  103. *portl = addr.sin6.sin6_port;
  104. unmap_ipv4_mapped_ipv6(ip_2_ip4(ip_addr), ip_2_ip6(ip_addr));
  105. } else {
  106. ip_addr = (ip_addr_t*)&(addr.sin.sin_addr);
  107. inet_ntop(addr.sa.sa_family, ip_addr, ipstr, INET6_ADDRSTRLEN);
  108. ESP_LOGV_LOC(TAG, "Processing an IPV6 address : %s", ipstr);
  109. *portl = addr.sin.sin_port;
  110. }
  111. inet_ntop(AF_INET, ip_addr, ipstr, INET6_ADDRSTRLEN);
  112. ESP_LOGV_LOC(TAG, "Retrieved ip address:port = %s:%u", ipstr, *portl);
  113. }
  114. return ipstr;
  115. }
  116. bool is_captive_portal_host_name(httpd_req_t* req) {
  117. const char* host_name = NULL;
  118. const char* ap_host_name = NULL;
  119. char* ap_ip_address = NULL;
  120. bool request_contains_hostname = false;
  121. esp_err_t hn_err = ESP_OK, err = ESP_OK;
  122. ESP_LOGD_LOC(TAG, "Getting adapter host name");
  123. if ((err = tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &host_name)) != ESP_OK) {
  124. ESP_LOGE_LOC(TAG, "Unable to get host name. Error: %s", esp_err_to_name(err));
  125. } else {
  126. ESP_LOGD_LOC(TAG, "Host name is %s", host_name);
  127. }
  128. ESP_LOGD_LOC(TAG, "Getting host name from request");
  129. char* req_host = alloc_get_http_header(req, "Host");
  130. if (tcpip_adapter_is_netif_up(TCPIP_ADAPTER_IF_AP)) {
  131. ESP_LOGD_LOC(TAG, "Soft AP is enabled. getting ip info");
  132. // Access point is up and running. Get the current IP address
  133. tcpip_adapter_ip_info_t ip_info;
  134. esp_err_t ap_ip_err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip_info);
  135. if (ap_ip_err != ESP_OK) {
  136. ESP_LOGE_LOC(
  137. TAG, "Unable to get local AP ip address. Error: %s", esp_err_to_name(ap_ip_err));
  138. } else {
  139. ESP_LOGD_LOC(TAG, "getting host name for TCPIP_ADAPTER_IF_AP");
  140. if ((hn_err = tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_AP, &ap_host_name)) !=
  141. ESP_OK) {
  142. ESP_LOGE_LOC(TAG, "Unable to get host name. Error: %s", esp_err_to_name(hn_err));
  143. err = err == ESP_OK ? hn_err : err;
  144. } else {
  145. ESP_LOGD_LOC(TAG, "Soft AP Host name is %s", ap_host_name);
  146. }
  147. ap_ip_address = malloc_init_external(IP4ADDR_STRLEN_MAX);
  148. memset(ap_ip_address, 0x00, IP4ADDR_STRLEN_MAX);
  149. if (ap_ip_address) {
  150. ESP_LOGD_LOC(TAG, "Converting soft ip address to string");
  151. ip4addr_ntoa_r(&ip_info.ip, ap_ip_address, IP4ADDR_STRLEN_MAX);
  152. ESP_LOGD_LOC(
  153. TAG, "TCPIP_ADAPTER_IF_AP is up and has ip address %s ", ap_ip_address);
  154. }
  155. }
  156. }
  157. if ((request_contains_hostname = (host_name != NULL) && (req_host != NULL) &&
  158. strcasestr(req_host, host_name)) == true) {
  159. ESP_LOGD_LOC(TAG, "http request host = system host name %s", req_host);
  160. } else if ((request_contains_hostname = (ap_host_name != NULL) && (req_host != NULL) &&
  161. strcasestr(req_host, ap_host_name)) == true) {
  162. ESP_LOGD_LOC(TAG, "http request host = AP system host name %s", req_host);
  163. }
  164. FREE_AND_NULL(ap_ip_address);
  165. FREE_AND_NULL(req_host);
  166. return request_contains_hostname;
  167. }
  168. /* Custom function to free context */
  169. void free_ctx_func(void* ctx) {
  170. session_context_t* context = (session_context_t*)ctx;
  171. if (context) {
  172. ESP_LOGD(TAG, "Freeing up socket context");
  173. FREE_AND_NULL(context->auth_token);
  174. FREE_AND_NULL(context->sess_ip_address);
  175. free(context);
  176. }
  177. }
  178. session_context_t* get_session_context(httpd_req_t* req) {
  179. bool newConnection = false;
  180. if (!req->sess_ctx) {
  181. ESP_LOGD(TAG, "New connection context. Allocating session buffer");
  182. req->sess_ctx = malloc_init_external(sizeof(session_context_t));
  183. req->free_ctx = free_ctx_func;
  184. newConnection = true;
  185. // get the remote IP address only once per session
  186. }
  187. session_context_t* ctx_data = (session_context_t*)req->sess_ctx;
  188. FREE_AND_NULL(ctx_data->sess_ip_address);
  189. ctx_data->sess_ip_address = http_alloc_get_socket_address(req, 0, &ctx_data->port);
  190. if (newConnection) {
  191. ESP_LOGI(TAG, "serving %s to peer %s port %u", req->uri, ctx_data->sess_ip_address,
  192. ctx_data->port);
  193. }
  194. return (session_context_t*)req->sess_ctx;
  195. }
  196. bool is_user_authenticated(httpd_req_t* req) {
  197. session_context_t* ctx_data = get_session_context(req);
  198. if (ctx_data->authenticated) {
  199. ESP_LOGD_LOC(TAG, "User is authenticated.");
  200. return true;
  201. }
  202. ESP_LOGD(TAG, "Heap internal:%zu (min:%zu) external:%zu (min:%zu) dma:%zu (min:%zu)",
  203. heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
  204. heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
  205. heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
  206. heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM), heap_caps_get_free_size(MALLOC_CAP_DMA),
  207. heap_caps_get_minimum_free_size(MALLOC_CAP_DMA));
  208. // todo: ask for user to authenticate
  209. return false;
  210. }
  211. /* Copies the full path into destination buffer and returns
  212. * pointer to requested file name */
  213. static const char* get_path_from_uri(char* dest, const char* uri, size_t destsize) {
  214. size_t pathlen = strlen(uri);
  215. memset(dest, 0x0, destsize);
  216. const char* quest = strchr(uri, '?');
  217. if (quest) {
  218. pathlen = MIN(pathlen, quest - uri);
  219. }
  220. const char* hash = strchr(uri, '#');
  221. if (hash) {
  222. pathlen = MIN(pathlen, hash - uri);
  223. }
  224. if (pathlen + 1 > destsize) {
  225. /* Full path string won't fit into destination buffer */
  226. return NULL;
  227. }
  228. strlcpy(dest, uri, pathlen + 1);
  229. // strip trailing blanks
  230. char* sr = dest + pathlen;
  231. while (*sr == ' ')
  232. *sr-- = '\0';
  233. char* last_fs = strchr(dest, '/');
  234. if (!last_fs) ESP_LOGD_LOC(TAG, "no / found in %s", dest);
  235. char* p = last_fs;
  236. while (p && *(++p) != '\0') {
  237. if (*p == '/') {
  238. last_fs = p;
  239. }
  240. }
  241. /* Return pointer to path, skipping the base */
  242. return last_fs ? ++last_fs : dest;
  243. }
  244. #define IS_FILE_EXT(filename, ext) \
  245. (strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0)
  246. /* Set HTTP response content type according to file extension */
  247. static esp_err_t set_content_type_from_file(httpd_req_t* req, const char* filename) {
  248. if (strlen(filename) == 0) {
  249. // for root page, etc.
  250. return httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
  251. } else if (IS_FILE_EXT(filename, ".pdf")) {
  252. return httpd_resp_set_type(req, "application/pdf");
  253. } else if (IS_FILE_EXT(filename, ".html")) {
  254. return httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
  255. } else if (IS_FILE_EXT(filename, ".jpeg")) {
  256. return httpd_resp_set_type(req, "image/jpeg");
  257. } else if (IS_FILE_EXT(filename, ".png")) {
  258. return httpd_resp_set_type(req, "image/png");
  259. } else if (IS_FILE_EXT(filename, ".ico")) {
  260. return httpd_resp_set_type(req, "image/x-icon");
  261. } else if (IS_FILE_EXT(filename, ".css")) {
  262. return httpd_resp_set_type(req, "text/css");
  263. } else if (IS_FILE_EXT(filename, ".js")) {
  264. return httpd_resp_set_type(req, "text/javascript");
  265. } else if (IS_FILE_EXT(filename, ".json")) {
  266. return httpd_resp_set_type(req, HTTPD_TYPE_JSON);
  267. } else if (IS_FILE_EXT(filename, ".map")) {
  268. return httpd_resp_set_type(req, "map");
  269. } else if (IS_FILE_EXT(filename, ".pro")) {
  270. return httpd_resp_set_type(req, "application/octet-stream");
  271. }
  272. /* This is a limited set only */
  273. /* For any other type always set as plain text */
  274. return httpd_resp_set_type(req, "text/plain");
  275. }
  276. static esp_err_t set_content_type_from_req(httpd_req_t* req) {
  277. char filepath[FILE_PATH_MAX];
  278. const char* filename = get_path_from_uri(filepath, req->uri, sizeof(filepath));
  279. if (!filename) {
  280. ESP_LOGE_LOC(TAG, "Filename is too long");
  281. /* Respond with 500 Internal Server Error */
  282. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
  283. return ESP_FAIL;
  284. }
  285. /* If name has trailing '/', respond with directory contents */
  286. if (filename[strlen(filename) - 1] == '/' && strlen(filename) > 1) {
  287. httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Browsing files forbidden.");
  288. return ESP_FAIL;
  289. }
  290. set_content_type_from_file(req, filename);
  291. return ESP_OK;
  292. }
  293. // esp_err_t root_get_handler(httpd_req_t *req){
  294. // esp_err_t err = ESP_OK;
  295. // ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  296. // httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  297. // httpd_resp_set_hdr(req, "Accept-Encoding", "identity");
  298. // if(!is_user_authenticated(req)){
  299. // // todo: send password entry page and return
  300. // }
  301. // int idx=-1;
  302. // if((idx=resource_get_index("index.html"))>=0){
  303. // const size_t file_size = (resource_map_end[idx] - resource_map_start[idx]);
  304. // httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
  305. // err = set_content_type_from_req(req);
  306. // if(err == ESP_OK){
  307. // httpd_resp_send(req, (const char *)resource_map_start[idx], file_size);
  308. // }
  309. // }
  310. // else{
  311. // httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "index.html not found");
  312. // return ESP_FAIL;
  313. // }
  314. // ESP_LOGD_LOC(TAG, "done serving [%s]", req->uri);
  315. // return err;
  316. // }
  317. static bool resolve_file_path(const char* uri, char* resolvedpath, size_t resolvedsize) {
  318. struct stat file_stat;
  319. // Assume the base path is the directory where files are served from
  320. // Generate the expected file path
  321. snprintf(resolvedpath, resolvedsize, "%s%s", www_dir, uri);
  322. // Check if file exists
  323. if (stat(resolvedpath, &file_stat) == 0) {
  324. // File exists
  325. return true;
  326. } else {
  327. // Check for compressed file
  328. strncat(resolvedpath, ".gz", resolvedsize - strlen(resolvedpath) - 1);
  329. if (stat(resolvedpath, &file_stat) == 0) {
  330. // Compressed file exists
  331. return true;
  332. }
  333. }
  334. // Neither uncompressed nor compressed file exists
  335. return false;
  336. }
  337. esp_err_t file_get_handler(httpd_req_t* req) {
  338. size_t sz;
  339. char filepath[FILE_PATH_MAX];
  340. struct stat file_stat={};
  341. httpd_resp_set_hdr(req, "Content-Encoding", "gzip");
  342. ESP_LOGD_LOC(TAG, "Serving file from [%s]", req->uri);
  343. const char* filename = get_path_from_uri(filepath, req->uri, sizeof(filepath));
  344. if (!filename) {
  345. ESP_LOGE_LOC(TAG, "Filename is too long");
  346. /* Respond with 500 Internal Server Error */
  347. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long");
  348. return ESP_FAIL;
  349. }
  350. /* If name has trailing '/', respond with directory contents */
  351. if (filename[strlen(filename) - 1] == '/') {
  352. httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Browsing files forbidden.");
  353. return ESP_FAIL;
  354. }
  355. if (strlen(filename) != 0 && IS_FILE_EXT(filename, ".map")) {
  356. return httpd_resp_sendstr(req, "");
  357. }
  358. if (!resolve_file_path(filename, filepath, sizeof(filepath))) {
  359. httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File not found");
  360. return ESP_FAIL;
  361. }
  362. esp_err_t err = set_content_type_from_file(req, filepath);
  363. if (err != ESP_OK) {
  364. return ESP_FAIL;
  365. }
  366. char* buffer = load_file(&sz,filepath);
  367. if (buffer) {
  368. if (sz == file_stat.st_size) {
  369. httpd_resp_send(req, buffer, sz);
  370. } else {
  371. ESP_LOGE(TAG, "Failed to read full file : %s", filepath);
  372. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read full file");
  373. err = ESP_FAIL;
  374. }
  375. free(buffer);
  376. } else {
  377. ESP_LOGE(TAG, "Failed to allocate memory for file : %s", filepath);
  378. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Server out of memory");
  379. err = ESP_FAIL;
  380. }
  381. return ESP_OK;
  382. }
  383. esp_err_t root_get_handler(httpd_req_t* req) {
  384. size_t sz;
  385. char filepath[FILE_PATH_MAX];
  386. esp_err_t err = ESP_OK;
  387. ESP_LOGD(TAG, "Serving [%s]", req->uri);
  388. httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
  389. httpd_resp_set_hdr(req, "Accept-Encoding", "identity");
  390. if (!is_user_authenticated(req)) {
  391. // TODO: Send password entry page and return
  392. }
  393. if (!resolve_file_path("index.html", filepath, sizeof(filepath))) {
  394. httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File not found");
  395. return ESP_FAIL;
  396. }
  397. err = set_content_type_from_file(req, filepath);
  398. if (err != ESP_OK) {
  399. return ESP_FAIL;
  400. }
  401. char* buffer = load_file(&sz,filepath);
  402. if (buffer) {
  403. httpd_resp_send(req, buffer, sz);
  404. free(buffer);
  405. } else {
  406. ESP_LOGE(TAG, "Failed to allocate memory for file : %s", filepath);
  407. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Server out of memory");
  408. err = ESP_FAIL;
  409. }
  410. ESP_LOGD(TAG, "Done serving [%s]", req->uri);
  411. return err;
  412. }
  413. esp_err_t resource_filehandler(httpd_req_t* req) {
  414. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  415. esp_err_t err = file_get_handler(req);
  416. ESP_LOGD_LOC(TAG, "Resource sending complete");
  417. return err;
  418. }
  419. esp_err_t ap_scan_handler(httpd_req_t* req) {
  420. const char empty[] = "{}";
  421. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  422. if (!is_user_authenticated(req)) {
  423. // todo: redirect to login page
  424. // return ESP_OK;
  425. }
  426. network_async_scan();
  427. esp_err_t err = set_content_type_from_req(req);
  428. if (err == ESP_OK) {
  429. httpd_resp_send(req, (const char*)empty, HTTPD_RESP_USE_STRLEN);
  430. }
  431. return err;
  432. }
  433. esp_err_t console_cmd_get_handler(httpd_req_t* req) {
  434. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  435. if (!is_user_authenticated(req)) {
  436. // todo: redirect to login page
  437. // return ESP_OK;
  438. }
  439. /* if we can get the mutex, write the last version of the AP list */
  440. esp_err_t err = set_content_type_from_req(req);
  441. cJSON* cmdlist = get_cmd_list();
  442. char* json_buffer = cJSON_Print(cmdlist);
  443. if (json_buffer) {
  444. httpd_resp_send(req, (const char*)json_buffer, HTTPD_RESP_USE_STRLEN);
  445. free(json_buffer);
  446. } else {
  447. ESP_LOGD_LOC(TAG, "Error retrieving command json string. ");
  448. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to format command");
  449. }
  450. cJSON_Delete(cmdlist);
  451. ESP_LOGD_LOC(TAG, "done serving [%s]", req->uri);
  452. return err;
  453. }
  454. esp_err_t console_cmd_post_handler(httpd_req_t* req) {
  455. char success[] = "{\"Result\" : \"Success\" }";
  456. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  457. // bool bOTA=false;
  458. // char * otaURL=NULL;
  459. esp_err_t err = post_handler_buff_receive(req);
  460. if (err != ESP_OK) {
  461. return err;
  462. }
  463. if (!is_user_authenticated(req)) {
  464. // todo: redirect to login page
  465. // return ESP_OK;
  466. }
  467. err = set_content_type_from_req(req);
  468. if (err != ESP_OK) {
  469. return err;
  470. }
  471. char* command = ((rest_server_context_t*)(req->user_ctx))->scratch;
  472. cJSON* root = cJSON_Parse(command);
  473. if (root == NULL) {
  474. ESP_LOGE_LOC(TAG, "Parsing command. Received content was: %s", command);
  475. httpd_resp_send_err(
  476. req, HTTPD_400_BAD_REQUEST, "Malformed command json. Unable to parse content.");
  477. return ESP_FAIL;
  478. }
  479. char* root_str = cJSON_Print(root);
  480. if (root_str != NULL) {
  481. ESP_LOGD(TAG, "Processing command item: \n%s", root_str);
  482. free(root_str);
  483. }
  484. cJSON* item = cJSON_GetObjectItemCaseSensitive(root, "command");
  485. if (!item) {
  486. ESP_LOGE_LOC(TAG, "Command not found. Received content was: %s", command);
  487. httpd_resp_send_err(
  488. req, HTTPD_400_BAD_REQUEST, "Malformed command json. Unable to parse content.");
  489. err = ESP_FAIL;
  490. } else {
  491. // navigate to the first child of the config structure
  492. char* cmd = cJSON_GetStringValue(item);
  493. if (!console_push(cmd, strlen(cmd) + 1)) {
  494. httpd_resp_send_err(
  495. req, HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to push command for execution");
  496. } else {
  497. httpd_resp_send(req, (const char*)success, strlen(success));
  498. }
  499. }
  500. ESP_LOGD_LOC(TAG, "done serving [%s]", req->uri);
  501. return err;
  502. }
  503. esp_err_t config_get_handler(httpd_req_t* req) {
  504. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  505. if (!is_user_authenticated(req)) {
  506. // todo: redirect to login page
  507. // return ESP_OK;
  508. }
  509. esp_err_t err = ESP_OK;
  510. // err= set_content_type_from_req(req);
  511. // if(err == ESP_OK){
  512. // char * json = config_alloc_get_json(false);
  513. // if(json==NULL){
  514. // ESP_LOGD_LOC(TAG, "Error retrieving config json string. ");
  515. // httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Error retrieving configuration
  516. // object"); err=ESP_FAIL;
  517. // }
  518. // else {
  519. // ESP_LOGD_LOC(TAG, "config json : %s",json );
  520. // cJSON * gplist=get_gpio_list(false);
  521. // char * gpliststr=cJSON_PrintUnformatted(gplist);
  522. // httpd_resp_sendstr_chunk(req,"{ \"gpio\":");
  523. // httpd_resp_sendstr_chunk(req,gpliststr);
  524. // httpd_resp_sendstr_chunk(req,", \"config\":");
  525. // httpd_resp_sendstr_chunk(req, (const char *)json);
  526. // httpd_resp_sendstr_chunk(req,"}");
  527. // httpd_resp_sendstr_chunk(req,NULL);
  528. // free(gpliststr);
  529. // free(json);
  530. // }
  531. // }
  532. // TODO: Add support for the commented code
  533. return err;
  534. }
  535. esp_err_t post_handler_buff_receive(httpd_req_t* req) {
  536. esp_err_t err = ESP_OK;
  537. int total_len = req->content_len;
  538. int cur_len = 0;
  539. char* buf = ((rest_server_context_t*)(req->user_ctx))->scratch;
  540. int received = 0;
  541. if (total_len >= SCRATCH_BUFSIZE) {
  542. /* Respond with 500 Internal Server Error */
  543. ESP_LOGE_LOC(TAG, "Received content was too long. ");
  544. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Content too long");
  545. err = ESP_FAIL;
  546. }
  547. while (err == ESP_OK && cur_len < total_len) {
  548. received = httpd_req_recv(req, buf + cur_len, total_len);
  549. if (received <= 0) {
  550. /* Respond with 500 Internal Server Error */
  551. ESP_LOGE_LOC(TAG, "Not all data was received. ");
  552. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Not all data was received");
  553. err = ESP_FAIL;
  554. } else {
  555. cur_len += received;
  556. }
  557. }
  558. if (err == ESP_OK) {
  559. buf[total_len] = '\0';
  560. }
  561. return err;
  562. }
  563. esp_err_t configurator_post_handler(httpd_req_t* req) {
  564. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  565. bool bOTA = false;
  566. char* otaURL = NULL;
  567. esp_err_t err = post_handler_buff_receive(req);
  568. if (err != ESP_OK) {
  569. return err;
  570. }
  571. if (!is_user_authenticated(req)) {
  572. // todo: redirect to login page
  573. // return ESP_OK;
  574. }
  575. err = set_content_type_from_req(req);
  576. if (err != ESP_OK) {
  577. return err;
  578. }
  579. char* buf = ((rest_server_context_t*)(req->user_ctx))->scratch;
  580. cJSON* root = cJSON_Parse(buf);
  581. if (root == NULL) {
  582. ESP_LOGE_LOC(TAG, "Parsing config json failed. Received content was: %s", buf);
  583. httpd_resp_send_err(
  584. req, HTTPD_400_BAD_REQUEST, "Malformed config json. Unable to parse content.");
  585. return ESP_FAIL;
  586. }
  587. char* root_str = cJSON_Print(root);
  588. if (root_str != NULL) {
  589. ESP_LOGD(TAG, "Processing config item: \n%s", root_str);
  590. free(root_str);
  591. }
  592. cJSON* item = cJSON_GetObjectItemCaseSensitive(root, "config");
  593. if (!item) {
  594. ESP_LOGE_LOC(TAG, "Parsing config json failed. Received content was: %s", buf);
  595. httpd_resp_send_err(
  596. req, HTTPD_400_BAD_REQUEST, "Malformed config json. Unable to parse content.");
  597. err = ESP_FAIL;
  598. } else {
  599. // navigate to the first child of the config structure
  600. if (item->child) item = item->child;
  601. }
  602. // while (item && err == ESP_OK)
  603. // {
  604. // cJSON *prev_item = item;
  605. // item=item->next;
  606. // char * entry_str = cJSON_Print(prev_item);
  607. // if(entry_str!=NULL){
  608. // ESP_LOGD_LOC(TAG, "Processing config item: \n%s", entry_str);
  609. // free(entry_str);
  610. // }
  611. // if(prev_item->string==NULL) {
  612. // ESP_LOGD_LOC(TAG,"Config value does not have a name");
  613. // httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Malformed config json. Value does not
  614. // have a name."); err = ESP_FAIL;
  615. // }
  616. // if(err == ESP_OK){
  617. // ESP_LOGD_LOC(TAG,"Found config value name [%s]", prev_item->string);
  618. // nvs_type_t item_type= config_get_item_type(prev_item);
  619. // if(item_type!=0){
  620. // void * val = config_safe_alloc_get_entry_value(item_type, prev_item);
  621. // if(val!=NULL){
  622. // if(strcmp(prev_item->string, "fwurl")==0) {
  623. // if(item_type!=NVS_TYPE_STR){
  624. // ESP_LOGE_LOC(TAG,"Firmware url should be type %d. Found type %d
  625. // instead.",NVS_TYPE_STR,item_type ); httpd_resp_send_err(req,
  626. // HTTPD_400_BAD_REQUEST,
  627. // "Malformed config json. Wrong type for firmware URL."); err = ESP_FAIL;
  628. // }
  629. // else {
  630. // // we're getting a request to do an OTA from that URL
  631. // ESP_LOGW_LOC(TAG, "Found OTA request!");
  632. // otaURL=strdup_psram(val);
  633. // bOTA=true;
  634. // }
  635. // }
  636. // else {
  637. // if(config_set_value(item_type, prev_item->string , val) != ESP_OK){
  638. // ESP_LOGE_LOC(TAG,"Unable to store value for [%s]", prev_item->string);
  639. // httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR , "Unable to store
  640. // config value"); err = ESP_FAIL;
  641. // }
  642. // else {
  643. // ESP_LOGD_LOC(TAG,"Successfully set value for [%s]",prev_item->string);
  644. // }
  645. // }
  646. // free(val);
  647. // }
  648. // else {
  649. // char messageBuffer[101]={};
  650. // ESP_LOGE_LOC(TAG,"Value not found for [%s]", prev_item->string);
  651. // snprintf(messageBuffer,sizeof(messageBuffer),"Malformed config json. Missing value
  652. // for entry %s.",prev_item->string); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
  653. // messageBuffer); err = ESP_FAIL;
  654. // }
  655. // }
  656. // else {
  657. // ESP_LOGE_LOC(TAG,"Unable to determine the type of config value [%s]",
  658. // prev_item->string); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Malformed config
  659. // json. Missing value for entry."); err = ESP_FAIL;
  660. // }
  661. // }
  662. // }
  663. cJSON_Delete(root);
  664. if (bOTA) {
  665. if (is_recovery_running) {
  666. ESP_LOGW_LOC(TAG, "Starting process OTA for url %s", otaURL);
  667. } else {
  668. ESP_LOGW_LOC(TAG, "Restarting system to process OTA for url %s", otaURL);
  669. }
  670. network_reboot_ota(otaURL);
  671. free(otaURL);
  672. }
  673. return err;
  674. }
  675. esp_err_t configurator_get_handler(httpd_req_t* req) {
  676. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  677. if (!is_user_authenticated(req)) {
  678. // todo: redirect to login page
  679. // return ESP_OK;
  680. }
  681. esp_err_t err = ESP_OK;
  682. void* config = NULL;
  683. size_t datalen;
  684. err = set_content_type_from_req(req);
  685. if (err == ESP_OK) {
  686. config = configurator_alloc_get_config(&datalen);
  687. if (!config) {
  688. ESP_LOGE_LOC(TAG, "Unable to serialize configuration");
  689. httpd_resp_send_err(
  690. req, HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to serialize configuration");
  691. err = ESP_FAIL;
  692. } else {
  693. httpd_resp_send(req, (const char*)config, datalen);
  694. free(config);
  695. }
  696. }
  697. // if(err == ESP_OK){
  698. // char * json = config_alloc_get_json(false);
  699. // if(json==NULL){
  700. // ESP_LOGD_LOC(TAG, "Error retrieving config json string. ");
  701. // httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Error retrieving configuration
  702. // object"); err=ESP_FAIL;
  703. // }
  704. // else {
  705. // ESP_LOGD_LOC(TAG, "config json : %s",json );
  706. // cJSON * gplist=get_gpio_list(false);
  707. // char * gpliststr=cJSON_PrintUnformatted(gplist);
  708. // httpd_resp_sendstr_chunk(req,"{ \"gpio\":");
  709. // httpd_resp_sendstr_chunk(req,gpliststr);
  710. // httpd_resp_sendstr_chunk(req,", \"config\":");
  711. // httpd_resp_sendstr_chunk(req, (const char *)json);
  712. // httpd_resp_sendstr_chunk(req,"}");
  713. // httpd_resp_sendstr_chunk(req,NULL);
  714. // free(gpliststr);
  715. // free(json);
  716. // }
  717. // }
  718. // TODO: Add support for the commented code
  719. return err;
  720. }
  721. esp_err_t config_post_handler(httpd_req_t* req) {
  722. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  723. bool bOTA = false;
  724. char* otaURL = NULL;
  725. esp_err_t err = post_handler_buff_receive(req);
  726. if (err != ESP_OK) {
  727. return err;
  728. }
  729. if (!is_user_authenticated(req)) {
  730. // todo: redirect to login page
  731. // return ESP_OK;
  732. }
  733. err = set_content_type_from_req(req);
  734. if (err != ESP_OK) {
  735. return err;
  736. }
  737. char* buf = ((rest_server_context_t*)(req->user_ctx))->scratch;
  738. cJSON* root = cJSON_Parse(buf);
  739. if (root == NULL) {
  740. ESP_LOGE_LOC(TAG, "Parsing config json failed. Received content was: %s", buf);
  741. httpd_resp_send_err(
  742. req, HTTPD_400_BAD_REQUEST, "Malformed config json. Unable to parse content.");
  743. return ESP_FAIL;
  744. }
  745. char* root_str = cJSON_Print(root);
  746. if (root_str != NULL) {
  747. ESP_LOGD(TAG, "Processing config item: \n%s", root_str);
  748. free(root_str);
  749. }
  750. cJSON* item = cJSON_GetObjectItemCaseSensitive(root, "config");
  751. if (!item) {
  752. ESP_LOGE_LOC(TAG, "Parsing config json failed. Received content was: %s", buf);
  753. httpd_resp_send_err(
  754. req, HTTPD_400_BAD_REQUEST, "Malformed config json. Unable to parse content.");
  755. err = ESP_FAIL;
  756. } else {
  757. // navigate to the first child of the config structure
  758. if (item->child) item = item->child;
  759. }
  760. // while (item && err == ESP_OK)
  761. // {
  762. // cJSON *prev_item = item;
  763. // item=item->next;
  764. // char * entry_str = cJSON_Print(prev_item);
  765. // if(entry_str!=NULL){
  766. // ESP_LOGD_LOC(TAG, "Processing config item: \n%s", entry_str);
  767. // free(entry_str);
  768. // }
  769. // if(prev_item->string==NULL) {
  770. // ESP_LOGD_LOC(TAG,"Config value does not have a name");
  771. // httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Malformed config json. Value does not
  772. // have a name."); err = ESP_FAIL;
  773. // }
  774. // if(err == ESP_OK){
  775. // ESP_LOGD_LOC(TAG,"Found config value name [%s]", prev_item->string);
  776. // nvs_type_t item_type= config_get_item_type(prev_item);
  777. // if(item_type!=0){
  778. // void * val = config_safe_alloc_get_entry_value(item_type, prev_item);
  779. // if(val!=NULL){
  780. // if(strcmp(prev_item->string, "fwurl")==0) {
  781. // if(item_type!=NVS_TYPE_STR){
  782. // ESP_LOGE_LOC(TAG,"Firmware url should be type %d. Found type %d
  783. // instead.",NVS_TYPE_STR,item_type ); httpd_resp_send_err(req,
  784. // HTTPD_400_BAD_REQUEST,
  785. // "Malformed config json. Wrong type for firmware URL."); err = ESP_FAIL;
  786. // }
  787. // else {
  788. // // we're getting a request to do an OTA from that URL
  789. // ESP_LOGW_LOC(TAG, "Found OTA request!");
  790. // otaURL=strdup_psram(val);
  791. // bOTA=true;
  792. // }
  793. // }
  794. // else {
  795. // if(config_set_value(item_type, prev_item->string , val) != ESP_OK){
  796. // ESP_LOGE_LOC(TAG,"Unable to store value for [%s]", prev_item->string);
  797. // httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR , "Unable to store
  798. // config value"); err = ESP_FAIL;
  799. // }
  800. // else {
  801. // ESP_LOGD_LOC(TAG,"Successfully set value for [%s]",prev_item->string);
  802. // }
  803. // }
  804. // free(val);
  805. // }
  806. // else {
  807. // char messageBuffer[101]={};
  808. // ESP_LOGE_LOC(TAG,"Value not found for [%s]", prev_item->string);
  809. // snprintf(messageBuffer,sizeof(messageBuffer),"Malformed config json. Missing value
  810. // for entry %s.",prev_item->string); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST,
  811. // messageBuffer); err = ESP_FAIL;
  812. // }
  813. // }
  814. // else {
  815. // ESP_LOGE_LOC(TAG,"Unable to determine the type of config value [%s]",
  816. // prev_item->string); httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Malformed config
  817. // json. Missing value for entry."); err = ESP_FAIL;
  818. // }
  819. // }
  820. // }
  821. // TODO: Add support for the commented code
  822. if (err == ESP_OK) {
  823. httpd_resp_sendstr(req, "{ \"result\" : \"OK\" }");
  824. messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_SYSTEM, "Save Success");
  825. }
  826. cJSON_Delete(root);
  827. if (bOTA) {
  828. if (is_recovery_running) {
  829. ESP_LOGW_LOC(TAG, "Starting process OTA for url %s", otaURL);
  830. } else {
  831. ESP_LOGW_LOC(TAG, "Restarting system to process OTA for url %s", otaURL);
  832. }
  833. network_reboot_ota(otaURL);
  834. free(otaURL);
  835. }
  836. return err;
  837. }
  838. esp_err_t connect_post_handler(httpd_req_t* req) {
  839. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  840. char success[] = "{}";
  841. char* ssid = NULL;
  842. char* password = NULL;
  843. char* host_name = NULL;
  844. esp_err_t err = post_handler_buff_receive(req);
  845. if (err != ESP_OK) {
  846. return err;
  847. }
  848. err = set_content_type_from_req(req);
  849. if (err != ESP_OK) {
  850. return err;
  851. }
  852. char* buf = ((rest_server_context_t*)(req->user_ctx))->scratch;
  853. if (!is_user_authenticated(req)) {
  854. // todo: redirect to login page
  855. // return ESP_OK;
  856. }
  857. cJSON* root = cJSON_Parse(buf);
  858. if (root == NULL) {
  859. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "JSON parsing error.");
  860. return ESP_FAIL;
  861. }
  862. cJSON* ssid_object = cJSON_GetObjectItem(root, "ssid");
  863. if (ssid_object != NULL) {
  864. ssid = strdup_psram(ssid_object->valuestring);
  865. }
  866. cJSON* password_object = cJSON_GetObjectItem(root, "pwd");
  867. if (password_object != NULL) {
  868. password = strdup_psram(password_object->valuestring);
  869. }
  870. cJSON* host_name_object = cJSON_GetObjectItem(root, "host_name");
  871. if (host_name_object != NULL) {
  872. host_name = strdup_psram(host_name_object->valuestring);
  873. }
  874. cJSON_Delete(root);
  875. // if(host_name!=NULL){
  876. // if(config_set_value(NVS_TYPE_STR, "host_name", host_name) != ESP_OK){
  877. // ESP_LOGW_LOC(TAG, "Unable to save host name configuration");
  878. // }
  879. // }
  880. // TODO: Add support for the commented code
  881. if (ssid != NULL && strlen(ssid) <= MAX_SSID_SIZE && strlen(password) <= MAX_PASSWORD_SIZE) {
  882. network_async_connect(ssid, password);
  883. httpd_resp_send(req, (const char*)success, strlen(success));
  884. } else {
  885. httpd_resp_send_err(
  886. req, HTTPD_400_BAD_REQUEST, "Malformed json. Missing or invalid ssid/password.");
  887. err = ESP_FAIL;
  888. }
  889. // FREE_AND_NULL(ssid);
  890. // FREE_AND_NULL(password);
  891. // FREE_AND_NULL(host_name);
  892. // TODO: Add support for the commented code
  893. return err;
  894. }
  895. esp_err_t connect_delete_handler(httpd_req_t* req) {
  896. char success[] = "{}";
  897. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  898. if (!is_user_authenticated(req)) {
  899. // todo: redirect to login page
  900. // return ESP_OK;
  901. }
  902. esp_err_t err = set_content_type_from_req(req);
  903. if (err != ESP_OK) {
  904. return err;
  905. }
  906. httpd_resp_send(req, (const char*)success, strlen(success));
  907. network_async_delete();
  908. return ESP_OK;
  909. }
  910. esp_err_t reboot_ota_post_handler(httpd_req_t* req) {
  911. char success[] = "{}";
  912. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  913. if (!is_user_authenticated(req)) {
  914. // todo: redirect to login page
  915. // return ESP_OK;
  916. }
  917. esp_err_t err = set_content_type_from_req(req);
  918. if (err != ESP_OK) {
  919. return err;
  920. }
  921. httpd_resp_send(req, (const char*)success, strlen(success));
  922. network_async_reboot(OTA);
  923. return ESP_OK;
  924. }
  925. esp_err_t reboot_post_handler(httpd_req_t* req) {
  926. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  927. char success[] = "{}";
  928. if (!is_user_authenticated(req)) {
  929. // todo: redirect to login page
  930. // return ESP_OK;
  931. }
  932. esp_err_t err = set_content_type_from_req(req);
  933. if (err != ESP_OK) {
  934. return err;
  935. }
  936. httpd_resp_send(req, (const char*)success, strlen(success));
  937. network_async_reboot(RESTART);
  938. return ESP_OK;
  939. }
  940. esp_err_t recovery_post_handler(httpd_req_t* req) {
  941. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  942. char success[] = "{}";
  943. if (!is_user_authenticated(req)) {
  944. // todo: redirect to login page
  945. // return ESP_OK;
  946. }
  947. esp_err_t err = set_content_type_from_req(req);
  948. if (err != ESP_OK) {
  949. return err;
  950. }
  951. httpd_resp_send(req, (const char*)success, strlen(success));
  952. network_async_reboot(RECOVERY);
  953. return ESP_OK;
  954. }
  955. esp_err_t flash_post_handler(httpd_req_t* req) {
  956. esp_err_t err = ESP_OK;
  957. if (is_recovery_running) {
  958. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  959. char success[] = "File uploaded. Flashing started.";
  960. if (!is_user_authenticated(req)) {
  961. // todo: redirect to login page
  962. // return ESP_OK;
  963. }
  964. err = httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
  965. if (err != ESP_OK) {
  966. return err;
  967. }
  968. char* binary_buffer = malloc_init_external(req->content_len);
  969. if (binary_buffer == NULL) {
  970. ESP_LOGE(TAG, "File too large : %d bytes", req->content_len);
  971. /* Respond with 400 Bad Request */
  972. httpd_resp_send_err(
  973. req, HTTPD_400_BAD_REQUEST, "Binary file too large. Unable to allocate memory!");
  974. return ESP_FAIL;
  975. }
  976. ESP_LOGI(TAG, "Receiving ota binary file");
  977. /* Retrieve the pointer to scratch buffer for temporary storage */
  978. char* buf = ((rest_server_context_t*)(req->user_ctx))->scratch;
  979. char* head = binary_buffer;
  980. int received;
  981. /* Content length of the request gives
  982. * the size of the file being uploaded */
  983. int remaining = req->content_len;
  984. while (remaining > 0) {
  985. ESP_LOGI(TAG, "Remaining size : %d", remaining);
  986. /* Receive the file part by part into a buffer */
  987. if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) {
  988. if (received == HTTPD_SOCK_ERR_TIMEOUT) {
  989. /* Retry if timeout occurred */
  990. continue;
  991. }
  992. // FREE_RESET(binary_buffer);
  993. // TODO: Add support for the commented code
  994. ESP_LOGE(TAG, "File reception failed!");
  995. /* Respond with 500 Internal Server Error */
  996. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
  997. err = ESP_FAIL;
  998. goto bail_out;
  999. }
  1000. /* Write buffer content to file on storage */
  1001. if (received) {
  1002. memcpy(head, buf, received);
  1003. head += received;
  1004. }
  1005. /* Keep track of remaining size of
  1006. * the file left to be uploaded */
  1007. remaining -= received;
  1008. }
  1009. /* Close file upon upload completion */
  1010. ESP_LOGI(TAG, "File reception complete. Invoking OTA process.");
  1011. err = start_ota(NULL, binary_buffer, req->content_len);
  1012. if (err != ESP_OK) {
  1013. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA processing failed");
  1014. goto bail_out;
  1015. }
  1016. // todo: handle this in ajax. For now, just send the root page
  1017. httpd_resp_send(req, (const char*)success, strlen(success));
  1018. }
  1019. bail_out:
  1020. return err;
  1021. }
  1022. char* get_ap_ip_address() {
  1023. static char ap_ip_address[IP4ADDR_STRLEN_MAX] = {};
  1024. tcpip_adapter_ip_info_t ip_info;
  1025. esp_err_t err = ESP_OK;
  1026. memset(ap_ip_address, 0x00, sizeof(ap_ip_address));
  1027. ESP_LOGD_LOC(TAG, "checking if soft AP is enabled");
  1028. if (tcpip_adapter_is_netif_up(TCPIP_ADAPTER_IF_AP)) {
  1029. ESP_LOGD_LOC(TAG, "Soft AP is enabled. getting ip info");
  1030. // Access point is up and running. Get the current IP address
  1031. err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip_info);
  1032. if (err != ESP_OK) {
  1033. ESP_LOGE_LOC(TAG, "Unable to get local AP ip address. Error: %s", esp_err_to_name(err));
  1034. } else {
  1035. ESP_LOGV_LOC(TAG, "Converting soft ip address to string");
  1036. ip4addr_ntoa_r(&ip_info.ip, ap_ip_address, IP4ADDR_STRLEN_MAX);
  1037. ESP_LOGD_LOC(TAG, "TCPIP_ADAPTER_IF_AP is up and has ip address %s ", ap_ip_address);
  1038. }
  1039. } else {
  1040. ESP_LOGD_LOC(TAG, "AP Is not enabled. Returning blank string");
  1041. }
  1042. return ap_ip_address;
  1043. }
  1044. esp_err_t process_redirect(httpd_req_t* req, const char* status) {
  1045. const char location_prefix[] = "http://";
  1046. char* ap_ip_address = get_ap_ip_address();
  1047. char* remote_ip = NULL;
  1048. in_port_t port = 0;
  1049. char* redirect_url = NULL;
  1050. ESP_LOGD_LOC(TAG, "Getting remote socket address");
  1051. remote_ip = http_alloc_get_socket_address(req, 0, &port);
  1052. size_t buf_size = strlen(redirect_payload1) + strlen(redirect_payload2) +
  1053. strlen(redirect_payload3) +
  1054. 2 * (strlen(location_prefix) + strlen(ap_ip_address)) + 1;
  1055. char* redirect = malloc_init_external(buf_size);
  1056. if (strcasestr(status, "302")) {
  1057. size_t url_buf_size = strlen(location_prefix) + strlen(ap_ip_address) + 1;
  1058. redirect_url = malloc_init_external(url_buf_size);
  1059. memset(redirect_url, 0x00, url_buf_size);
  1060. snprintf(redirect_url, buf_size, "%s%s/", location_prefix, ap_ip_address);
  1061. ESP_LOGW_LOC(
  1062. TAG, "Redirecting host [%s] to %s (from uri %s)", remote_ip, redirect_url, req->uri);
  1063. httpd_resp_set_hdr(req, "Location", redirect_url);
  1064. snprintf(redirect, buf_size, "OK");
  1065. } else {
  1066. snprintf(redirect, buf_size, "%s%s%s%s%s%s%s", redirect_payload1, location_prefix,
  1067. ap_ip_address, redirect_payload2, location_prefix, ap_ip_address, redirect_payload3);
  1068. ESP_LOGW_LOC(TAG, "Responding to host [%s] (from uri %s) with redirect html page %s",
  1069. remote_ip, req->uri, redirect);
  1070. }
  1071. httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
  1072. httpd_resp_set_hdr(req, "Cache-Control", "no-cache");
  1073. httpd_resp_set_status(req, status);
  1074. httpd_resp_send(req, redirect, HTTPD_RESP_USE_STRLEN);
  1075. FREE_AND_NULL(redirect);
  1076. FREE_AND_NULL(redirect_url);
  1077. FREE_AND_NULL(remote_ip);
  1078. return ESP_OK;
  1079. }
  1080. esp_err_t redirect_200_ev_handler(httpd_req_t* req) {
  1081. ESP_LOGD_LOC(TAG, "Processing known redirect url %s", req->uri);
  1082. process_redirect(req, "200 OK");
  1083. return ESP_OK;
  1084. }
  1085. esp_err_t redirect_processor(httpd_req_t* req, httpd_err_code_t error) {
  1086. esp_err_t err = ESP_OK;
  1087. const char* host_name = NULL;
  1088. const char* ap_host_name = NULL;
  1089. char* user_agent = NULL;
  1090. char* remote_ip = NULL;
  1091. char* sta_ip_address = NULL;
  1092. char* ap_ip_address = get_ap_ip_address();
  1093. char* socket_local_address = NULL;
  1094. bool request_contains_hostname = false;
  1095. bool request_contains_ap_ip_address = false;
  1096. bool request_is_sta_ip_address = false;
  1097. bool connected_to_ap_ip_interface = false;
  1098. bool connected_to_sta_ip_interface = false;
  1099. bool useragentiscaptivenetwork = false;
  1100. in_port_t port = 0;
  1101. ESP_LOGV_LOC(TAG, "Getting remote socket address");
  1102. remote_ip = http_alloc_get_socket_address(req, 0, &port);
  1103. ESP_LOGW_LOC(TAG, "%s requested invalid URL: [%s]", remote_ip, req->uri);
  1104. if (network_status_lock_structure(portMAX_DELAY)) {
  1105. sta_ip_address = strdup_psram(status.net.ip.ip);
  1106. network_status_unlock_structure();
  1107. } else {
  1108. ESP_LOGE(TAG, "Unable to obtain local IP address from WiFi Manager.");
  1109. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, NULL);
  1110. }
  1111. ESP_LOGV_LOC(TAG, "Getting host name from request");
  1112. char* req_host = alloc_get_http_header(req, "Host");
  1113. user_agent = alloc_get_http_header(req, "User-Agent");
  1114. if ((useragentiscaptivenetwork =
  1115. (user_agent != NULL && strcasestr(user_agent, "CaptiveNetworkSupport")) == true)) {
  1116. ESP_LOGW_LOC(TAG, "Found user agent that supports captive networks! [%s]", user_agent);
  1117. }
  1118. esp_err_t hn_err = ESP_OK;
  1119. ESP_LOGV_LOC(TAG, "Getting adapter host name");
  1120. if ((hn_err = tcpip_adapter_get_hostname(TCPIP_ADAPTER_IF_STA, &host_name)) != ESP_OK) {
  1121. ESP_LOGE_LOC(TAG, "Unable to get host name. Error: %s", esp_err_to_name(hn_err));
  1122. err = err == ESP_OK ? hn_err : err;
  1123. } else {
  1124. ESP_LOGV_LOC(TAG, "Host name is %s", host_name);
  1125. }
  1126. in_port_t loc_port = 0;
  1127. ESP_LOGV_LOC(TAG, "Getting local socket address");
  1128. socket_local_address = http_alloc_get_socket_address(req, 1, &loc_port);
  1129. ESP_LOGD_LOC(TAG,
  1130. "Peer IP: %s [port %u], System AP IP address: %s, System host: %s. Requested Host: [%s], "
  1131. "uri [%s]",
  1132. STR_OR_NA(remote_ip), port, STR_OR_NA(ap_ip_address), STR_OR_NA(host_name),
  1133. STR_OR_NA(req_host), req->uri);
  1134. /* captive portal functionality: redirect to access point IP for HOST that are not the access
  1135. * point IP OR the STA IP */
  1136. /* determine if Host is from the STA IP address */
  1137. if ((request_contains_hostname = (host_name != NULL) && (req_host != NULL) &&
  1138. strcasestr(req_host, host_name)) == true) {
  1139. ESP_LOGD_LOC(TAG, "http request host = system host name %s", req_host);
  1140. } else if ((request_contains_hostname = (ap_host_name != NULL) && (req_host != NULL) &&
  1141. strcasestr(req_host, ap_host_name)) == true) {
  1142. ESP_LOGD_LOC(TAG, "http request host = AP system host name %s", req_host);
  1143. }
  1144. if ((request_contains_ap_ip_address = (ap_ip_address != NULL) && (req_host != NULL) &&
  1145. strcasestr(req_host, ap_ip_address)) == true) {
  1146. ESP_LOGD_LOC(TAG, "http request host is access point ip address %s", req_host);
  1147. }
  1148. if ((connected_to_ap_ip_interface = (ap_ip_address != NULL) && (socket_local_address != NULL) &&
  1149. strcasestr(socket_local_address, ap_ip_address)) == true) {
  1150. ESP_LOGD_LOC(
  1151. TAG, "http request is connected to access point interface IP %s", ap_ip_address);
  1152. }
  1153. if ((request_is_sta_ip_address = (sta_ip_address != NULL) && (req_host != NULL) &&
  1154. strcasestr(req_host, sta_ip_address)) == true) {
  1155. ESP_LOGD_LOC(TAG, "http request host is WiFi client ip address %s", req_host);
  1156. }
  1157. if ((connected_to_sta_ip_interface =
  1158. (sta_ip_address != NULL) && (socket_local_address != NULL) &&
  1159. strcasestr(sta_ip_address, socket_local_address)) == true) {
  1160. ESP_LOGD_LOC(TAG, "http request is connected to WiFi client ip address %s", sta_ip_address);
  1161. }
  1162. if ((error == 0) || (error == HTTPD_404_NOT_FOUND && connected_to_ap_ip_interface &&
  1163. !(request_contains_ap_ip_address || request_contains_hostname))) {
  1164. process_redirect(req, "302 Found");
  1165. } else {
  1166. ESP_LOGD_LOC(
  1167. TAG, "URL not found, and not processing captive portal so throw regular 404 error");
  1168. httpd_resp_send_err(req, error, NULL);
  1169. }
  1170. FREE_AND_NULL(socket_local_address);
  1171. FREE_AND_NULL(req_host);
  1172. FREE_AND_NULL(user_agent);
  1173. FREE_AND_NULL(sta_ip_address);
  1174. FREE_AND_NULL(remote_ip);
  1175. return err;
  1176. }
  1177. esp_err_t redirect_ev_handler(httpd_req_t* req) { return redirect_processor(req, 0); }
  1178. esp_err_t messages_get_handler(httpd_req_t* req) {
  1179. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  1180. if (!is_user_authenticated(req)) {
  1181. // todo: redirect to login page
  1182. // return ESP_OK;
  1183. }
  1184. esp_err_t err = set_content_type_from_req(req);
  1185. if (err != ESP_OK) {
  1186. return err;
  1187. }
  1188. cJSON* json_messages = messaging_retrieve_messages(messaging);
  1189. if (json_messages != NULL) {
  1190. char* json_text = cJSON_Print(json_messages);
  1191. httpd_resp_send(req, (const char*)json_text, strlen(json_text));
  1192. free(json_text);
  1193. cJSON_Delete(json_messages);
  1194. } else {
  1195. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Unable to retrieve messages");
  1196. }
  1197. return ESP_OK;
  1198. }
  1199. esp_err_t status_get_handler(httpd_req_t* req) {
  1200. ESP_LOGD_LOC(TAG, "serving [%s]", req->uri);
  1201. if (!is_user_authenticated(req)) {
  1202. // todo: redirect to login page
  1203. // return ESP_OK;
  1204. }
  1205. esp_err_t err = httpd_resp_set_type(req, "application/octet-stream");
  1206. if (err != ESP_OK) {
  1207. return err;
  1208. }
  1209. ESP_LOGD(TAG, "Creating binding");
  1210. pb_ostream_t filestream = {&out_http_binding, req, SIZE_MAX, 0};
  1211. ESP_LOGD(TAG, "Starting encode");
  1212. if (!pb_encode(&filestream, sys_Config_fields, (void*)&status)) {
  1213. ESP_LOGE(TAG, "Encoding failed: %s\n", PB_GET_ERROR(&filestream));
  1214. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, PB_GET_ERROR(&filestream));
  1215. }
  1216. else {
  1217. ESP_LOGD(TAG, "Encoded size: %d", filestream.bytes_written);
  1218. if (filestream.bytes_written == 0) {
  1219. ESP_LOGE(TAG, "Empty status!");
  1220. httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Empty status!");
  1221. }
  1222. }
  1223. // update status for next status call
  1224. network_async_update_status();
  1225. return ESP_OK;
  1226. }
  1227. esp_err_t err_handler(httpd_req_t* req, httpd_err_code_t error) {
  1228. esp_err_t err = ESP_OK;
  1229. if (error != HTTPD_404_NOT_FOUND) {
  1230. err = httpd_resp_send_err(req, error, NULL);
  1231. } else {
  1232. err = redirect_processor(req, error);
  1233. }
  1234. return err;
  1235. }