http_server_handlers.c 39 KB

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