diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bdfeea..e487e7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,4 @@ cmake_minimum_required(VERSION 3.5) include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(irblaster) +project(fanctl) diff --git a/DECODED.txt b/DECODED.txt deleted file mode 100644 index ef7b5d4..0000000 --- a/DECODED.txt +++ /dev/null @@ -1,20 +0,0 @@ -hum1 10000000 01111111 00010000 11101111 1 -hum2 10000000 01111111 01010000 10101111 1 -hum3 10000000 01111111 10010000 01101111 1 -mode1 10000000 01111111 00011000 11100111 1 -mode2 10000000 01111111 10011000 01100111 1 -mode3 10000000 01111111 00001000 11110111 1 -mode4 10000000 01111111 10001000 01110111 1 -night 10000000 01111111 10000000 01111111 1 -power 10000000 01111111 00000000 11111111 1 -speed1 10000000 01111111 10101000 01010111 1 -speed2 10000000 01111111 01101000 10010111 1 -speed3 10000000 01111111 00101000 11010111 1 - -Format: - -38.5kHz - -Preamble: 9ms transmit, 4.5ms gap -Zero: 600us transmit, 600us gap. -One: 600us transmit, 1.7ms gap diff --git a/Makefile b/Makefile index 5108515..08f87c8 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,6 @@ # project subdirectory. # -PROJECT_NAME := hello-world +PROJECT_NAME := fanctl include $(IDF_PATH)/make/project.mk diff --git a/components/fileserver/CMakeLists.txt b/components/fileserver/CMakeLists.txt deleted file mode 100644 index 0d00082..0000000 --- a/components/fileserver/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -set(COMPONENT_ADD_INCLUDEDIRS - "include" "files") - -set(COMPONENT_SRCDIRS - "src" "files") - -set(COMPONENT_REQUIRES tcpip_adapter esp_http_server httpd_utils common_utils) - -#begin staticfiles -# generated by rebuild_file_tables -set(COMPONENT_EMBED_FILES - "files/embed/favicon.ico" - "files/embed/index.html") -#end staticfiles - -register_component() diff --git a/components/fileserver/README.txt b/components/fileserver/README.txt deleted file mode 100644 index 588ad42..0000000 --- a/components/fileserver/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -File and template serving support for the http_server bundled with ESP-IDF. - diff --git a/components/fileserver/component.mk b/components/fileserver/component.mk deleted file mode 100644 index 87ae05a..0000000 --- a/components/fileserver/component.mk +++ /dev/null @@ -1,3 +0,0 @@ - -COMPONENT_SRCDIRS := src -COMPONENT_ADD_INCLUDEDIRS := include diff --git a/components/fileserver/files/embed/favicon.ico b/components/fileserver/files/embed/favicon.ico deleted file mode 100644 index 0bd5a51..0000000 Binary files a/components/fileserver/files/embed/favicon.ico and /dev/null differ diff --git a/components/fileserver/files/embed/index.html b/components/fileserver/files/embed/index.html deleted file mode 100644 index d9138bd..0000000 --- a/components/fileserver/files/embed/index.html +++ /dev/null @@ -1,108 +0,0 @@ - - - - -ESP node - - - -

ESP node {version}

- -IR blaster - -Restart node - - diff --git a/components/fileserver/files/rebuild_file_tables.php b/components/fileserver/files/rebuild_file_tables.php deleted file mode 100755 index b0018e2..0000000 --- a/components/fileserver/files/rebuild_file_tables.php +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env php - 'text/plain', - 'htm' => 'text/html', - 'html' => 'text/html', - 'php' => 'text/html', - 'css' => 'text/css', - 'js' => 'application/javascript', - 'json' => 'application/json', - 'xml' => 'application/xml', - 'swf' => 'application/x-shockwave-flash', - 'flv' => 'video/x-flv', - - 'pem' => 'application/x-pem-file', - - // images - 'png' => 'image/png', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'gif' => 'image/gif', - 'bmp' => 'image/bmp', - 'ico' => 'image/vnd.microsoft.icon', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - - // archives - 'zip' => 'application/zip', - 'rar' => 'application/x-rar-compressed', - 'exe' => 'application/x-msdownload', - 'msi' => 'application/x-msdownload', - 'cab' => 'application/vnd.ms-cab-compressed', - - // audio/video - 'mp3' => 'audio/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - - // adobe - 'pdf' => 'application/pdf', - 'psd' => 'image/vnd.adobe.photoshop', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - - // ms office - 'doc' => 'application/msword', - 'rtf' => 'application/rtf', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - - // open office - 'odt' => 'application/vnd.oasis.opendocument.text', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - ); - - $parts = explode('.', $f); - $suffix = end($parts); - $mime = $mimes[$suffix] ?? 'application/octet-stream'; - - $len = filesize('embed/'.$f); - - $struct_array[] = "[FILE_$a] = {{$start}, {$end}, \"{$f}\", \"{$mime}\"},"; - - return - 'extern const uint8_t '.$start.'[];'."\n". - 'extern const uint8_t '.$end.'[];'; -}, $files); - -$externlist = implode("\n", $externs); -$structlist = implode("\n ", $struct_array); - - -file_put_contents('www_files_enum.h', << -#include "www_files_enum.h" - -$externlist - -const struct embedded_file_info EMBEDDED_FILE_LOOKUP[] = { - $structlist -}; - -const size_t EMBEDDED_FILE_LOOKUP_LEN = $files_count; - -FILE -); diff --git a/components/fileserver/files/www_files_enum.c b/components/fileserver/files/www_files_enum.c deleted file mode 100644 index 86b8421..0000000 --- a/components/fileserver/files/www_files_enum.c +++ /dev/null @@ -1,15 +0,0 @@ -// Generated by 'rebuild_file_tables' -#include -#include "www_files_enum.h" - -extern const uint8_t _binary_favicon_ico_start[]; -extern const uint8_t _binary_favicon_ico_end[]; -extern const uint8_t _binary_index_html_start[]; -extern const uint8_t _binary_index_html_end[]; - -const struct embedded_file_info EMBEDDED_FILE_LOOKUP[] = { - [FILE_FAVICON_ICO] = {_binary_favicon_ico_start, _binary_favicon_ico_end, "favicon.ico", "image/vnd.microsoft.icon"}, - [FILE_INDEX_HTML] = {_binary_index_html_start, _binary_index_html_end, "index.html", "text/html"}, -}; - -const size_t EMBEDDED_FILE_LOOKUP_LEN = 2; diff --git a/components/fileserver/files/www_files_enum.h b/components/fileserver/files/www_files_enum.h deleted file mode 100644 index b81113b..0000000 --- a/components/fileserver/files/www_files_enum.h +++ /dev/null @@ -1,13 +0,0 @@ -// Generated by 'rebuild_file_tables' - -#ifndef _EMBEDDED_FILES_ENUM_H -#define _EMBEDDED_FILES_ENUM_H - -#include "fileserver/embedded_files.h" - -enum embedded_file_id { - FILE_FAVICON_ICO = 0, - FILE_INDEX_HTML = 1 -}; - -#endif // _EMBEDDED_FILES_ENUM_H diff --git a/components/fileserver/include/fileserver/embedded_files.h b/components/fileserver/include/fileserver/embedded_files.h deleted file mode 100644 index a86f8ae..0000000 --- a/components/fileserver/include/fileserver/embedded_files.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// Created on 2018/10/17 by Ondrej Hruska -// - -#ifndef FBNODE_EMBEDDED_FILES_H -#define FBNODE_EMBEDDED_FILES_H - -#include -#include -#include - -struct embedded_file_info { - const uint8_t * start; - const uint8_t * end; - const char * name; - const char * mime; -}; - -enum file_access_level { - /** Public = file accessed by a wildcard route */ - FILE_ACCESS_PUBLIC = 0, - /** Protected = file included in a template or explicitly specified in a route */ - FILE_ACCESS_PROTECTED = 1, - /** Files protected against read-out */ - FILE_ACCESS_PRIVATE = 2, -}; - -extern const struct embedded_file_info EMBEDDED_FILE_LOOKUP[]; -extern const size_t EMBEDDED_FILE_LOOKUP_LEN; - -/** - * Find an embedded file by its name. - * - * This function is weak. It crawls the EMBEDDED_FILE_LOOKUP table and checks for exact match, also - * testing with www_get_static_file_access_check if the access is allowed. - * - * @param name - file name - * @param access - access level (public - wildcard fallthrough, protected - files for the server, loaded explicitly) - * @param[out] file - the file struct is stored here if found, unchanged if not found. - * @return status code - */ -esp_err_t www_get_static_file(const char *name, enum file_access_level access, const struct embedded_file_info **file); - -/** - * Check file access permission (if using the default www_get_static_file implementation). - * - * This function is weak. The default implementation returns always true. - */ -bool www_get_static_file_access_check(const struct embedded_file_info *file, enum file_access_level access); - -#endif //FBNODE_EMBEDDED_FILES_H diff --git a/components/fileserver/include/fileserver/token_subs.h b/components/fileserver/include/fileserver/token_subs.h deleted file mode 100644 index 1e396b4..0000000 --- a/components/fileserver/include/fileserver/token_subs.h +++ /dev/null @@ -1,216 +0,0 @@ -// -// This module implements token substitution in files served by the server. -// -// Tokens are in the form {token}, or {escape:token}, where escape can be: -// - h ... html escape (plain text in a html file, attribute value) -// - j ... js escape (for use in JS strings) -// -// When no escape is specified, the token substitution is written verbatim into the response. -// -// var foo = "{j:foo}"; -// -// {generated-html-goes-here} -// -// Token can be made optional by adding '?' at the end (this can't be used for includes). -// Such token then simply becomes empty string when not substituted, as opposed to being included in the page verbatim. -// -// -// -// token names can contain alnum, dash, period and underscore, and are case sensitive. -// -// -// It is further possible to include a static file with optional key-value replacements. These serve as defaults. -// -// {@_subfile.html} -// {@_subfile.html|key=value lalala} -// {@_subfile.html|key=value lalala|other=value} -// -// File inclusion can be nested, and the files can use replacement tokens as specified by the include statement -// -// Created on 2019/01/24 by Ondrej Hruska -// - -#ifndef FBNODE_TOKEN_SUBS_H -#define FBNODE_TOKEN_SUBS_H - -#include "embedded_files.h" -#include -#include -#include - -/** Max length of a token buffer (must suffice for all included filenames) */ -#define MAX_TOKEN_LEN 32 - -/** Max length of a key-value substitution when using tpl_kv_replacer; - * This is also used internally for in-line replacements in file imports. */ -#define TPL_KV_KEY_LEN 24 -/** Max length of a substituion in tpl_kv_replacer */ -#define TPL_KV_SUBST_LEN 64 - -/** - * Escape type - argument for httpd_resp_send_chunk_escaped() - */ -typedef enum { - TPL_ESCAPE_NONE = 0, - TPL_ESCAPE_HTML, - TPL_ESCAPE_JS, -} tpl_escape_t; - -enum { - HTOPT_NONE = 0, - HTOPT_NO_HEADERS = 1 << 0, - HTOPT_NO_CLOSE = 1 << 1, - HTOPT_INCLUDE = HTOPT_NO_HEADERS|HTOPT_NO_CLOSE, -}; - -/** - * Send string using a given escaping scheme - * - * @param r - * @param buf - buf to send - * @param len - buf len, or HTTPD_RESP_USE_STRLEN - * @param escape - escaping type - * @return success - */ -esp_err_t httpd_resp_send_chunk_escaped(httpd_req_t *r, const char *buf, ssize_t len, tpl_escape_t escape); - -/** - * Template substitution callback. Data shall be sent using httpd_resp_send_chunk_escaped(). - * - * @param[in,out] context - user-defined page state data - * @param[in] token - replacement token - * @return ESP_OK if the token was substituted, ESP_ERR_NOT_FOUND if it is unknown, other errors on e.g. send failure - */ -typedef esp_err_t (*template_subst_t)(httpd_req_t *r, void *context, const char *token, tpl_escape_t escape); - -/** - * Send a template file as a response. The content type from the file struct will be used. - * - * Use HTOPT_INCLUDE when used to embed a file inside a template. - * - * @param r - request - * @param file_index - file index in EMBEDDED_FILE_LOOKUP - * @param replacer - substitution callback, can be NULL if only includes are to be processed - * @param context - arbitrary context, will be passed to the replacer function; can be NULL - * @param opts - flag options (HTOPT_*) - */ -esp_err_t httpd_send_template_file(httpd_req_t *r, int file_index, template_subst_t replacer, void *context, uint32_t opts); - -/** - * Same as httpd_send_template_file, but using an `embedded_file_info` struct. - */ -esp_err_t httpd_send_template_file_struct(httpd_req_t *r, const struct embedded_file_info *file, template_subst_t replacer, void *context, uint32_t opts); - -/** - * Process and send a string template. - * The content-type header should be set beforehand, if different from the default (text/html). - * - * Use HTOPT_INCLUDE when used to embed a file inside a template. - * - * @param r - request - * @param template - template string (does not have to be terminated by a null byte) - * @param template_len - length of the template string; -1 to use strlen() - * @param replacer - substitution callback, can be NULL if only includes are to be processed - * @param context - arbitrary context, will be passed to the replacer function; can be NULL - * @param opts - flag options (HTOPT_*) - */ -esp_err_t httpd_send_template(httpd_req_t *r, const char *template, ssize_t template_len, template_subst_t replacer, void *context, uint32_t opts); - -/** - * Send a static file. This can be used to just send a file, or to embed a static template as a token substitution. - * - * Use HTOPT_INCLUDE when used to embed a file inside a template. - * - * Note: use httpd_resp_send_chunk_escaped() or httpd_resp_send_chunk() to send a plain string. - * - * @param r - request - * @param file_index - file index in EMBEDDED_FILE_LOOKUP - * @param escape - escape option - * @param opts - flag options (HTOPT_*) - * @return - */ -esp_err_t httpd_send_static_file(httpd_req_t *r, int file_index, tpl_escape_t escape, uint32_t opts); - -/** - * Same as httpd_send_template_file, but using an `embedded_file_info` struct. - */ -esp_err_t httpd_send_static_file_struct(httpd_req_t *r, const struct embedded_file_info *file, tpl_escape_t escape, uint32_t opts); - -struct tpl_kv_entry { - char key[TPL_KV_KEY_LEN]; // copied here - char subst[TPL_KV_SUBST_LEN]; // copied here - SLIST_ENTRY(tpl_kv_entry) link; -}; - -SLIST_HEAD(tpl_kv_list, tpl_kv_entry); - -/** - * key-value replacer that works with a dynamically allocated SLIST. - * - * @param r - request - * @param context - context - must be a pointer to `struct tpl_kv_list` - * @param token - token to replace - * @param escape - escape option - * @return OK/not found/other - */ -esp_err_t tpl_kv_replacer(httpd_req_t *r, void *context, const char *token, tpl_escape_t escape); - -/** - * Add a pair into the substitutions list - * - * @param head - list head - * @param key - key, copied - * @param subst - value, copied - * @return success (fails if malloc failed) - */ -esp_err_t tpl_kv_add(struct tpl_kv_list *head, const char *key, const char *subst); - -/** - * Convenience function that converts an IP address to string and adds it as a substitution - * - * @param head - list head - * @param key - key, copied - * @param ip4h - host order ipv4 address - * @return success - */ -esp_err_t tpl_kv_add_ipv4str(struct tpl_kv_list *head, const char *key, uint32_t ip4h); - -/** add int as a substitution; key is copied */ -esp_err_t tpl_kv_add_int(struct tpl_kv_list *head, const char *key, int32_t num); - -/** add long as a substitution; key is copied */ -esp_err_t tpl_kv_add_long(struct tpl_kv_list *head, const char *key, int64_t num); - -/** add printf-formatted value; key is copied */ -esp_err_t tpl_kv_sprintf(struct tpl_kv_list *head, const char *key, const char *format, ...) - __attribute__((format(printf,3,4))); - -/** - * Init a substitutions list (on the stack) - * - * @return the list - */ -static inline struct tpl_kv_list tpl_kv_init(void) -{ - return (struct tpl_kv_list) {.slh_first = NULL}; -} - -/** - * Free the list (head is left alone because it was allocated on the stack) - * @param head - */ -void tpl_kv_free(struct tpl_kv_list *head); - -/** - * Send the map as an ASCII table separated by Record Separator (30) and Unit Separator (31). - * Content type is set to application/octet-stream. - * - * key 31 value 30 - * key 31 value 30 - * key 31 value - * - * @param req - */ -esp_err_t tpl_kv_send_as_ascii_map(httpd_req_t *req, struct tpl_kv_list *head); - -#endif //FBNODE_TOKEN_SUBS_H diff --git a/components/fileserver/readme/README.md b/components/fileserver/readme/README.md deleted file mode 100644 index 42d6883..0000000 --- a/components/fileserver/readme/README.md +++ /dev/null @@ -1,29 +0,0 @@ -Place the `rebuild_file_tables.php` script in a `files` subfolder of the main component. -It requires PHP 7 to run. - -This is what the setup should look like - -``` -main/files/embed/index.html -main/files/rebuild_file_tables.php -main/CMakeLists.txt -main/main.c -``` - -Add this to your CMakeLists.txt before `register_component`: - -``` -#begin staticfiles -#end staticfiles -``` - -The script will update CMakeLists.txt and generate `files_enum.c` and `files_enum.h` when run. - -``` -main/files/files_enum.h -main/files/files_enum.c -``` - -Ensure `files/files_enum.c` is included in the build. - -`www_get_static_file()` is implemented as weak to let you provide custom access authentication logic. diff --git a/components/fileserver/readme/rebuild_file_tables.php b/components/fileserver/readme/rebuild_file_tables.php deleted file mode 100755 index 2d81c59..0000000 --- a/components/fileserver/readme/rebuild_file_tables.php +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/env php - 'text/plain', - 'htm' => 'text/html', - 'html' => 'text/html', - 'php' => 'text/html', - 'css' => 'text/css', - 'js' => 'application/javascript', - 'json' => 'application/json', - 'xml' => 'application/xml', - 'swf' => 'application/x-shockwave-flash', - 'flv' => 'video/x-flv', - - 'pem' => 'application/x-pem-file', - - // images - 'png' => 'image/png', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'gif' => 'image/gif', - 'bmp' => 'image/bmp', - 'ico' => 'image/vnd.microsoft.icon', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - - // archives - 'zip' => 'application/zip', - 'rar' => 'application/x-rar-compressed', - 'exe' => 'application/x-msdownload', - 'msi' => 'application/x-msdownload', - 'cab' => 'application/vnd.ms-cab-compressed', - - // audio/video - 'mp3' => 'audio/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - - // adobe - 'pdf' => 'application/pdf', - 'psd' => 'image/vnd.adobe.photoshop', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - - // ms office - 'doc' => 'application/msword', - 'rtf' => 'application/rtf', - 'xls' => 'application/vnd.ms-excel', - 'ppt' => 'application/vnd.ms-powerpoint', - - // open office - 'odt' => 'application/vnd.oasis.opendocument.text', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - ); - - $parts = explode('.', $f); - $suffix = end($parts); - $mime = $mimes[$suffix] ?? 'application/octet-stream'; - - $len = filesize('embed/'.$f); - - $struct_array[] = "[FILE_$a] = {{$start}, {$end}, \"{$f}\", \"{$mime}\"},"; - - return - 'extern const uint8_t '.$start.'[];'."\n". - 'extern const uint8_t '.$end.'[];'; -}, $files); - -$externlist = implode("\n", $externs); -$structlist = implode("\n ", $struct_array); - - -file_put_contents('files_enum.h', << -#include - -enum embedded_file_id { - $keylist, - FILE_MAX -}; - -struct embedded_file_info { - const uint8_t * start; - const uint8_t * end; - const char * name; - const char * mime; -}; - -$externlist - -extern const struct embedded_file_info EMBEDDED_FILE_LOOKUP[]; - -#endif // _EMBEDDED_FILES_ENUM_H - -FILE -); - -file_put_contents("files_enum.c", << -#include "files_enum.h" - -const struct embedded_file_info EMBEDDED_FILE_LOOKUP[] = { - $structlist -}; - -FILE -); diff --git a/components/fileserver/src/embedded_files.c b/components/fileserver/src/embedded_files.c deleted file mode 100644 index e61f348..0000000 --- a/components/fileserver/src/embedded_files.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include "fileserver/embedded_files.h" -#include "string.h" - -esp_err_t __attribute__((weak)) -www_get_static_file(const char *name, enum file_access_level access, const struct embedded_file_info **file) -{ - // simple search by name - for(int i = 0; i < EMBEDDED_FILE_LOOKUP_LEN; i++) { - if (0 == strcmp(EMBEDDED_FILE_LOOKUP[i].name, name)) { - *file = &EMBEDDED_FILE_LOOKUP[i]; - return ESP_OK; - } - } - - return ESP_ERR_NOT_FOUND; -} - -bool __attribute__((weak)) -www_get_static_file_access_check(const struct embedded_file_info *file, enum file_access_level access) { - return true; -} diff --git a/components/fileserver/src/token_subs.c b/components/fileserver/src/token_subs.c deleted file mode 100644 index fd5b67a..0000000 --- a/components/fileserver/src/token_subs.c +++ /dev/null @@ -1,580 +0,0 @@ -//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG - -#include -#include -#include -#include -#include -#include -#include - -#include "fileserver/embedded_files.h" -#include "fileserver/token_subs.h" - -#define ESP_TRY(x) \ - do { \ - esp_err_t try_er = (x); \ - if (try_er != ESP_OK) return try_er; \ - } while(0) - -static const char* TAG = "token_subs"; - -// TODO implement buffering to avoid sending many tiny chunks when escaping - -/* encode for HTML. returns 0 or 1 - 1 = success */ -static esp_err_t send_html_chunk(httpd_req_t *r, const char *data, ssize_t len) -{ - assert(r); - assert(data); - - int start = 0, end = 0; - char c; - if (len < 0) len = (int) strlen(data); - if (len==0) return ESP_OK; - - for (end = 0; end < len; end++) { - c = data[end]; - if (c == 0) { - // we found EOS - break; // not return - the last chunk is printed after the loop - } - - if (c == '"' || c == '\'' || c == '<' || c == '>') { - if (start < end) ESP_TRY(httpd_resp_send_chunk(r, data + start, end - start)); - start = end + 1; - } - - if (c == '"') ESP_TRY(httpd_resp_send_chunk(r, """, 5)); - else if (c == '\'') ESP_TRY(httpd_resp_send_chunk(r, "'", 5)); - else if (c == '<') ESP_TRY(httpd_resp_send_chunk(r, "<", 4)); - else if (c == '>') ESP_TRY(httpd_resp_send_chunk(r, ">", 4)); - } - - if (start < end) ESP_TRY(httpd_resp_send_chunk(r, data + start, end - start)); - return ESP_OK; -} - -/* encode for JS. returns 0 or 1 - 1 = success */ -static esp_err_t send_js_chunk(httpd_req_t *r, const char *data, ssize_t len) -{ - assert(r); - assert(data); - - int start = 0, end = 0; - char c; - if (len < 0) len = (int) strlen(data); - if (len==0) return ESP_OK; - - for (end = 0; end < len; end++) { - c = data[end]; - if (c == 0) { - // we found EOS - break; // not return - the last chunk is printed after the loop - } - - if (c == '"' || c == '\\' || c == '/' || c == '\'' || c == '<' || c == '>' || c == '\n' || c == '\r') { - if (start < end) ESP_TRY(httpd_resp_send_chunk(r, data + start, end - start)); - start = end + 1; - } - - if (c == '"') ESP_TRY(httpd_resp_send_chunk(r, "\\\"", 2)); - else if (c == '\'') ESP_TRY(httpd_resp_send_chunk(r, "\\'", 2)); - else if (c == '\\') ESP_TRY(httpd_resp_send_chunk(r, "\\\\", 2)); - else if (c == '/') ESP_TRY(httpd_resp_send_chunk(r, "\\/", 2)); - else if (c == '<') ESP_TRY(httpd_resp_send_chunk(r, "\\u003C", 6)); - else if (c == '>') ESP_TRY(httpd_resp_send_chunk(r, "\\u003E", 6)); - else if (c == '\n') ESP_TRY(httpd_resp_send_chunk(r, "\\n", 2)); - else if (c == '\r') ESP_TRY(httpd_resp_send_chunk(r, "\\r", 2)); - } - - if (start < end) ESP_TRY(httpd_resp_send_chunk(r, data + start, end - start)); - return ESP_OK; -} - - -esp_err_t httpd_resp_send_chunk_escaped(httpd_req_t *r, const char *buf, ssize_t len, tpl_escape_t escape) -{ - switch (escape) { - default: // this enum should be exhaustive, but in case something went wrong, just print it verbatim - - case TPL_ESCAPE_NONE: - return httpd_resp_send_chunk(r, buf, len); - - case TPL_ESCAPE_HTML: - return send_html_chunk(r, buf, len); - - case TPL_ESCAPE_JS: - return send_js_chunk(r, buf, len); - } -} - -esp_err_t httpd_send_static_file(httpd_req_t *r, int file_index, tpl_escape_t escape, uint32_t opts) -{ - assert(file_index < EMBEDDED_FILE_LOOKUP_LEN); - const struct embedded_file_info *file = &EMBEDDED_FILE_LOOKUP[file_index]; - - return httpd_send_static_file_struct(r, file, escape, opts); -} - -esp_err_t httpd_send_static_file_struct(httpd_req_t *r, const struct embedded_file_info *file, tpl_escape_t escape, uint32_t opts) -{ - if (0 == (opts & HTOPT_NO_HEADERS)) { - ESP_TRY(httpd_resp_set_type(r, file->mime)); - ESP_TRY(httpd_resp_set_hdr(r, "Cache-Control", "max-age=86400, public, must-revalidate")); - } - - ESP_TRY(httpd_resp_send_chunk_escaped(r, (const char *) file->start, (size_t)(file->end - file->start), escape)); - - if (0 == (opts & HTOPT_NO_CLOSE)) { - ESP_TRY(httpd_resp_send_chunk(r, NULL, 0)); - } - - return ESP_OK; -} - -esp_err_t httpd_send_template_file(httpd_req_t *r, - int file_index, - template_subst_t replacer, - void *context, - uint32_t opts) -{ - assert(file_index < EMBEDDED_FILE_LOOKUP_LEN); - const struct embedded_file_info *file = &EMBEDDED_FILE_LOOKUP[file_index]; - return httpd_send_template_file_struct(r,file,replacer,context,opts); -} - -esp_err_t httpd_send_template_file_struct(httpd_req_t *r, - const struct embedded_file_info *file, - template_subst_t replacer, - void *context, - uint32_t opts) -{ - if (0 == (opts & HTOPT_NO_HEADERS)) { - ESP_TRY(httpd_resp_set_type(r, file->mime)); - ESP_TRY(httpd_resp_set_hdr(r, "Cache-Control", "no-cache, no-store, must-revalidate")); - } - - return httpd_send_template(r, (const char *) file->start, (size_t)(file->end - file->start), replacer, context, opts); -} - -esp_err_t tpl_kv_replacer(httpd_req_t *r, void *context, const char *token, tpl_escape_t escape) -{ - assert(context); - assert(token); - - struct tpl_kv_entry *entry; - struct tpl_kv_list *head = context; - SLIST_FOREACH(entry, head, link) { - if (0==strcmp(entry->key, token)) { - return httpd_resp_send_chunk_escaped(r, entry->subst, -1, escape); - } - } - - return ESP_ERR_NOT_FOUND; -} - -struct stacked_replacer_context { - template_subst_t replacer0; - void *context0; - template_subst_t replacer1; - void *context1; -}; - -esp_err_t stacked_replacer(httpd_req_t *r, void *context, const char *token, tpl_escape_t escape) -{ - assert(context); - assert(token); - - struct stacked_replacer_context *combo = context; - - if (ESP_OK == combo->replacer0(r, combo->context0, token, escape)) { - return ESP_OK; - } - - if (ESP_OK == combo->replacer1(r, combo->context1, token, escape)) { - return ESP_OK; - } - - return ESP_ERR_NOT_FOUND; -} - -esp_err_t httpd_send_template(httpd_req_t *r, - const char *template, ssize_t template_len, - template_subst_t replacer, - void *context, - uint32_t opts) -{ - if (template_len < 0) template_len = strlen(template); - - // replacer and context may be NULL - assert(template); - assert(r); - - // data end - const char * const end = template + template_len; - - // start of to-be-processed data - const char * pos = template; - - // start position for finding opening braces, updated after a failed match to avoid infinite loop on the same bad token - const char * searchpos = pos; - - // tokens must be copied to a buffer to allow adding the terminating null byte - char token_buf[MAX_TOKEN_LEN]; - - while (pos < end) { - const char * openbr = strchr(searchpos, '{'); - if (openbr == NULL) { - // no more templates - ESP_TRY(httpd_resp_send_chunk(r, pos, (size_t) (end - pos))); - break; - } - - // this brace could start a valid template. check if it seems valid... - const char * closebr = strchr(openbr, '}'); - if (closebr == NULL) { - // there are no further closing braces, so it can't be a template - - // we also know there can't be any more substitutions, because they would lack a closing } too - ESP_TRY(httpd_resp_send_chunk(r, pos, (size_t) (end - pos))); - break; - } - - // see if the braces content looks like a token - const char *t = openbr + 1; - bool token_valid = true; - struct tpl_kv_list substitutions_head = tpl_kv_init(); - struct tpl_kv_entry *new_subst_pair = NULL; - - // a token can be either a name for replacement by the replacer func, or an include with static kv replacements - bool is_include = false; - bool token_is_optional = false; - const char *token_end = NULL; // points one char after the end of the token - - // parsing the token - { - if (*t == '@') { - ESP_LOGD(TAG, "Parsing an Include token"); - is_include = true; - t++; - } - - enum { - P_NAME, P_KEY, P_VALUE - } state = P_NAME; - - const char *kv_start = NULL; - while (t != closebr || state == P_VALUE) { - char c = *t; - - if (state == P_NAME) { - if (!((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '.' || c == '_' || c == '-' || c == ':')) { - - if (!is_include && c == '?') { - token_end = t; - token_is_optional = true; - } else { - if (is_include && c == '|') { - token_end = t; - state = P_KEY; - kv_start = t + 1; - // pipe separates the include's filename and literal substitutions - // we know there is a closing } somewhere, and {@....| doesn't occur normally, so let's assume it's correct - } - else { - token_valid = false; - break; - } - } - } - } - else if (state == P_KEY) { - if (!((c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '.' || c == '_' || c == '-')) { - if (c == '=') { - new_subst_pair = calloc(sizeof(struct tpl_kv_entry), 1); - const size_t klen = MIN(TPL_KV_KEY_LEN, t - kv_start); - strncpy(new_subst_pair->key, kv_start, klen); - new_subst_pair->key[klen] = 0; - - kv_start = t + 1; - - state = P_VALUE; - // pipe separates the include's filename and literal substitutions - // we know there is a closing } somewhere, and {@....| doesn't occur normally, so let's assume it's correct - } - } - } - else if (state == P_VALUE) { - if (c == '|' || c == '}') { - const size_t vlen = MIN(TPL_KV_SUBST_LEN, t - kv_start); - strncpy(new_subst_pair->subst, kv_start, vlen); - new_subst_pair->subst[vlen] = 0; - - // attach the kv pair to the list - SLIST_INSERT_HEAD(&substitutions_head, new_subst_pair, link); - ESP_LOGD(TAG, "Adding subs kv %s -> %s", new_subst_pair->key, new_subst_pair->subst); - new_subst_pair = NULL; - - kv_start = t + 1; // go past the pipe - state = P_KEY; - - if (t == closebr) { - break; // found the ending brace, so let's quit the kv parse loop - } - } - } - - t++; - } - // clean up after a messed up subs kv pairs syntax - if (new_subst_pair != NULL) { - free(new_subst_pair); - } - } - - if (!token_valid) { - // false match, include it in the block to send before the next token - searchpos = openbr + 1; - ESP_LOGD(TAG, "Skip invalid token near %10s", openbr); - continue; - } - - // now we know it looks like a substitution token - - // flush data before the token - if (pos != openbr) ESP_TRY(httpd_resp_send_chunk(r, pos, (size_t) (openbr - pos))); - - const char *token_start = openbr; - - tpl_escape_t escape = TPL_ESCAPE_NONE; - - // extract and terminate the token - size_t token_len = MIN(MAX_TOKEN_LEN-1, closebr - openbr - 1); - if (token_end) { - token_len = MIN(token_len, token_end - openbr - 1); - } - - if (is_include) { - token_start += 1; // skip the @ - token_len -= 1; - } else { - if (0 == strncmp("h:", openbr + 1, 2)) { - escape = TPL_ESCAPE_HTML; - token_start += 2; - token_len -= 2; - } - else if (0 == strncmp("j:", openbr + 1, 2)) { - escape = TPL_ESCAPE_JS; - token_start += 2; - token_len -= 2; - } - } - - strncpy(token_buf, token_start+1, token_len); - token_buf[token_len] = 0; - - ESP_LOGD(TAG, "Token: %s", token_buf); - - esp_err_t rv; - - if (is_include) { - ESP_LOGD(TAG, "Trying to include a sub-file"); - - const struct embedded_file_info *file = NULL; - rv = www_get_static_file(token_buf, FILE_ACCESS_PROTECTED, &file); - if (rv != ESP_OK) { - ESP_LOGE(TAG, "Failed to statically include \"%s\" in a template - %s", token_buf, esp_err_to_name(rv)); - // this will cause the token to be emitted verbatim - } else { - ESP_LOGD(TAG, "Descending..."); - - // combine the two replacers - struct stacked_replacer_context combo = { - .replacer0 = replacer, - .context0 = context, - .replacer1 = tpl_kv_replacer, - .context1 = &substitutions_head - }; - - rv = httpd_send_template_file_struct(r, file, stacked_replacer, &combo, HTOPT_INCLUDE); - ESP_LOGD(TAG, "...back in parent"); - } - - // tear down the list - tpl_kv_free(&substitutions_head); - - if (rv != ESP_OK) { - // just send it verbatim... - ESP_TRY(httpd_resp_send_chunk(r, openbr, (size_t) (closebr - openbr + 1))); - } - } else { - if (replacer) { - ESP_LOGD(TAG, "Running replacer for \"%s\" with escape %d", token_buf, escape); - rv = replacer(r, context, token_buf, escape); - - if (rv != ESP_OK) { - if (rv == ESP_ERR_NOT_FOUND) { - ESP_LOGD(TAG, "Token rejected"); - // optional token becomes empty string if not replaced - if (!token_is_optional) { - ESP_LOGD(TAG, "Not optional, keeping verbatim"); - // replacer rejected the token, keep it verbatim - ESP_TRY(httpd_resp_send_chunk(r, openbr, (size_t) (closebr - openbr + 1))); - } - } - else { - ESP_LOGE(TAG, "Unexpected error from replacer func: 0x%02x - %s", rv, esp_err_to_name(rv)); - return rv; - } - } - } else { - ESP_LOGD(TAG, "Not replacer"); - // no replacer, only includes - used for 'static' files - if (!token_is_optional) { - ESP_LOGD(TAG, "Token not optional, keeping verbatim"); - ESP_TRY(httpd_resp_send_chunk(r, openbr, (size_t) (closebr - openbr + 1))); - } - } - } - - searchpos = pos = closebr + 1; - } - - if (0 == (opts & HTOPT_NO_CLOSE)) { - ESP_TRY(httpd_resp_send_chunk(r, NULL, 0)); - } - - return ESP_OK; -} - - -esp_err_t tpl_kv_add_int(struct tpl_kv_list *head, const char *key, int32_t num) -{ - char buf[12]; - itoa(num, buf, 10); - return tpl_kv_add(head, key, buf); -} - -esp_err_t tpl_kv_add_long(struct tpl_kv_list *head, const char *key, int64_t num) -{ - char buf[21]; - sprintf(buf, "%"PRIi64, num); - return tpl_kv_add(head, key, buf); -} - -esp_err_t tpl_kv_add_ipv4str(struct tpl_kv_list *head, const char *key, uint32_t ip4h) -{ - char buf[IP4ADDR_STRLEN_MAX]; - ip4_addr_t addr; - addr.addr = lwip_htonl(ip4h); - ip4addr_ntoa_r(&addr, buf, IP4ADDR_STRLEN_MAX); - - return tpl_kv_add(head, key, buf); -} - -esp_err_t tpl_kv_add(struct tpl_kv_list *head, const char *key, const char *subst) -{ - ESP_LOGD(TAG, "kv add subs %s := %s", key, subst); - struct tpl_kv_entry *entry = calloc(sizeof(struct tpl_kv_entry), 1); - if (entry == NULL) return ESP_ERR_NO_MEM; - - assert(strlen(key) < TPL_KV_KEY_LEN); - assert(strlen(subst) < TPL_KV_SUBST_LEN); - - strncpy(entry->key, key, TPL_KV_KEY_LEN); - entry->key[TPL_KV_KEY_LEN - 1] = 0; - - strncpy(entry->subst, subst, TPL_KV_SUBST_LEN - 1); - entry->subst[TPL_KV_KEY_LEN - 1] = 0; - - SLIST_INSERT_HEAD(head, entry, link); - return ESP_OK; -} - -esp_err_t tpl_kv_sprintf(struct tpl_kv_list *head, const char *key, const char *format, ...) -{ - ESP_LOGD(TAG, "kv printf %s := %s", key, format); - struct tpl_kv_entry *entry = calloc(sizeof(struct tpl_kv_entry), 1); - if (entry == NULL) return ESP_ERR_NO_MEM; - - assert(strlen(key) < TPL_KV_KEY_LEN); - - strncpy(entry->key, key, TPL_KV_KEY_LEN); - entry->key[TPL_KV_KEY_LEN - 1] = 0; - - va_list list; - va_start(list, format); - vsnprintf(entry->subst, TPL_KV_SUBST_LEN, format, list); - va_end(list); - entry->subst[TPL_KV_KEY_LEN - 1] = 0; - - SLIST_INSERT_HEAD(head, entry, link); - return ESP_OK; -} - -void tpl_kv_free(struct tpl_kv_list *head) -{ - struct tpl_kv_entry *item, *next; - SLIST_FOREACH_SAFE(item, head, link, next) { - free(item); - } -} - -esp_err_t tpl_kv_send_as_ascii_map(httpd_req_t *req, struct tpl_kv_list *head) -{ - httpd_resp_set_type(req, "text/plain; charset=utf-8"); - -#define BUF_CAP 512 - char *buf_head = calloc(BUF_CAP, 1); - if (!buf_head) { - ESP_LOGE(TAG, "Malloc err"); - return ESP_FAIL; - } - char *buf = buf_head; - size_t cap = BUF_CAP; - struct tpl_kv_entry *entry; - - // GCC nested function - esp_err_t send_part() { - esp_err_t suc = httpd_resp_send_chunk(req, buf_head, BUF_CAP-cap); - buf = buf_head; - cap = BUF_CAP; - if (suc != ESP_OK) { - ESP_LOGE(TAG, "Error sending buffer"); - free(buf_head); - httpd_resp_send_chunk(req, NULL, 0); - } - return suc; - } - - SLIST_FOREACH(entry, head, link) { - buf = append(buf, entry->key, &cap); - if (!buf) ESP_TRY(send_part()); - buf = append(buf, "\x1f", &cap); - if (!buf) ESP_TRY(send_part()); - buf = append(buf, entry->subst, &cap); - if (!buf) ESP_TRY(send_part()); - if (entry->link.sle_next) { - buf = append(buf, "\x1e", &cap); - if (!buf) ESP_TRY(send_part()); - } - } - // send leftovers - if (buf != buf_head) { - esp_err_t suc = httpd_resp_send_chunk(req, buf_head, BUF_CAP-cap); - if (suc != ESP_OK) { - ESP_LOGE(TAG, "Error sending buffer"); - } - } - - // Commit - httpd_resp_send_chunk(req, NULL, 0); - free(buf_head); - return ESP_OK; -} diff --git a/components/httpd_utils/CMakeLists.txt b/components/httpd_utils/CMakeLists.txt deleted file mode 100644 index e5fe530..0000000 --- a/components/httpd_utils/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -set(COMPONENT_ADD_INCLUDEDIRS - "include") - -set(COMPONENT_SRCDIRS - "src") - -set(COMPONENT_REQUIRES tcpip_adapter esp_http_server common_utils) - -register_component() diff --git a/components/httpd_utils/README.txt b/components/httpd_utils/README.txt deleted file mode 100644 index c3e034e..0000000 --- a/components/httpd_utils/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -Functions enriching the HTTP server bundled with ESP-IDF. -This package includes HTTP-related utilities, captive -portal implementation, and a cookie-based session system with a -key-value store and expirations. diff --git a/components/httpd_utils/component.mk b/components/httpd_utils/component.mk deleted file mode 100644 index 87ae05a..0000000 --- a/components/httpd_utils/component.mk +++ /dev/null @@ -1,3 +0,0 @@ - -COMPONENT_SRCDIRS := src -COMPONENT_ADD_INCLUDEDIRS := include diff --git a/components/httpd_utils/include/httpd_utils/captive.h b/components/httpd_utils/include/httpd_utils/captive.h deleted file mode 100644 index d656b41..0000000 --- a/components/httpd_utils/include/httpd_utils/captive.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef HTTPD_UTILS_CAPTIVE_H -#define HTTPD_UTILS_CAPTIVE_H - -#include -#include - -/** - * Redirect if needed when a captive portal capture is detected - * - * @param r - * @return ESP_OK on redirect, ESP_ERR_NOT_FOUND if not needed, other error on failure - */ -esp_err_t httpd_captive_redirect(httpd_req_t *r); - -/** - * Get URL to redirect to. WEAK. - * - * @param r - * @param buf - * @param maxlen - * @return http(s)://foo.bar/ - */ -esp_err_t httpd_captive_redirect_get_url(httpd_req_t *r, char *buf, size_t maxlen); - -/** - * Get captive portal domain. WEAK. - * - * @return foo.bar - */ -const char * httpd_captive_redirect_get_domain(); - -#endif //HTTPD_UTILS_CAPTIVE_H diff --git a/components/httpd_utils/include/httpd_utils/fd_to_ipv4.h b/components/httpd_utils/include/httpd_utils/fd_to_ipv4.h deleted file mode 100644 index 6cb647d..0000000 --- a/components/httpd_utils/include/httpd_utils/fd_to_ipv4.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef HTTPD_FDIPV4_H -#define HTTPD_FDIPV4_H - -/** - * Get IP address for a FD - * - * @param fd - * @param[out] ipv4 - * @return success - */ -esp_err_t fd_to_ipv4(int fd, in_addr_t *ipv4); - -#endif //HTTPD_FDIPV4_H diff --git a/components/httpd_utils/include/httpd_utils/redirect.h b/components/httpd_utils/include/httpd_utils/redirect.h deleted file mode 100644 index 77fdebc..0000000 --- a/components/httpd_utils/include/httpd_utils/redirect.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef HTTPD_UTILS_REDIRECT_H -#define HTTPD_UTILS_REDIRECT_H - -#include -#include - -/** - * Redirect to other URI - sends a HTTP response from the http server - * - * @param r - request - * @param uri - target uri - * @return success - */ -esp_err_t httpd_redirect_to(httpd_req_t *r, const char *uri); - -#endif // HTTPD_UTILS_REDIRECT_H diff --git a/components/httpd_utils/include/httpd_utils/session.h b/components/httpd_utils/include/httpd_utils/session.h deleted file mode 100644 index 1ff409b..0000000 --- a/components/httpd_utils/include/httpd_utils/session.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Session store system main include file - * - * Created on 2019/07/13. - */ - -#ifndef HTTPD_UTILS_SESSION_H -#define HTTPD_UTILS_SESSION_H - -#include "session_kvmap.h" -#include "session_store.h" - -// Customary keys - -/** Session key for OK flash message */ -#define SESS_FLASH_OK "flash_ok" -/** Session key for error flash message */ -#define SESS_FLASH_ERR "flash_err" -/** Session key for a "logged in" flag. Value is 1 if logged in. */ -#define SESS_AUTHED "authed" - -// .. - -/** - * Redirect to /login form if unauthed, - * but also retrieve the session key-value store for further use - */ -#define HTTP_GET_AUTHED_SESSION(kvstore, r) do { \ - kvstore = httpd_req_find_session_and((r), SESS_GET_DATA); \ - if (NULL == kvstore || NULL == sess_kv_map_get(kvstore, SESS_AUTHED)) { \ - return httpd_redirect_to((r), "/login"); \ - } \ -} while(0) - -/** - * Start or resume a session without checking for authed status. - * When started, the session cookie is added to the response immediately. - * - * @param[out] kvstore - a place to store the allocated or retrieved session kvmap - * @param[in] r - request - * @return success - */ -esp_err_t HTTP_GET_SESSION(sess_kv_map_t **kvstore, httpd_req_t *r); - -/** - * Redirect to the login form if unauthed. - * This is the same as `HTTP_GET_AUTHED_SESSION`, except the kvstore variable is - * not needed in the uri handler calling this, so it is declared internally. - */ -#define HTTP_REDIRECT_IF_UNAUTHED(r) do { \ - sess_kv_map_t *_kvstore; \ - HTTP_GET_AUTHED_SESSION(_kvstore, r); \ -} while(0) - -#endif // HTTPD_UTILS_SESSION_H diff --git a/components/httpd_utils/include/httpd_utils/session_kvmap.h b/components/httpd_utils/include/httpd_utils/session_kvmap.h deleted file mode 100644 index e091bd5..0000000 --- a/components/httpd_utils/include/httpd_utils/session_kvmap.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Simple key-value map for session data storage. - * Takes care of dynamic allocation and cleanup. - * - * Created on 2019/01/28. - */ - -#ifndef SESSION_KVMAP_H -#define SESSION_KVMAP_H - -/** - * Prototype for a free() func to clean up session-held objects - */ -typedef void (*sess_kv_free_func_t)(void *obj); - -typedef struct sess_kv_map sess_kv_map_t; - -#define SESS_KVMAP_KEY_LEN 16 - -/** - * Allocate a new session key-value store - * - * @return the store, NULL on error - */ -sess_kv_map_t *sess_kv_map_alloc(void); - -/** - * Free the session kv store. - * - * @param head - store head - */ -void sess_kv_map_free(void *head); - -/** - * Get a value from the session kv store. - * - * @param head - store head - * @param key - key to get a value for - * @return the value, or NULL if not found - */ -void *sess_kv_map_get(sess_kv_map_t *head, const char *key); - -/** - * Get and remove a value from the session store. - * - * The free function is not called in this case and the recipient is - * responsible for cleaning it up correctly. - * - * @param head - store head - * @param key - key to get a value for - * @return the value, or NULL if not found - */ -void * sess_kv_map_take(sess_kv_map_t *head, const char *key); - -/** - * Remove an entry from the session by its key name. - * The slot is not free'd yet, but is made available for reuse. - * - * @param head - store head - * @param key - key to remove - * @return success - */ -esp_err_t sess_kv_map_remove(sess_kv_map_t *head, const char *key); - -/** - * Set a key value. If there is an old value stored, it will be freed by its free function and replaced by the new one. - * Otherwise a new slot is allocated for it, or a previously released one is reused. - * - * @param head - store head - * @param key - key to assign to - * @param value - new value - * @param free_fn - value free func - * @return success - */ -esp_err_t sess_kv_map_set(sess_kv_map_t *head, const char *key, void *value, sess_kv_free_func_t free_fn); - -#endif //SESSION_KVMAP_H diff --git a/components/httpd_utils/include/httpd_utils/session_store.h b/components/httpd_utils/include/httpd_utils/session_store.h deleted file mode 100644 index 2266944..0000000 --- a/components/httpd_utils/include/httpd_utils/session_store.h +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Cookie-based session store - */ - -#ifndef SESSION_STORE_H -#define SESSION_STORE_H - -#include "esp_http_server.h" - -#define SESSION_EXPIRY_TIME_S 60*30 -#define SESSION_COOKIE_NAME "SESSID" - -/** function that frees a session data object */ -typedef void (*sess_data_free_fn_t)(void *); - -enum session_find_action { - SESS_DROP, SESS_GET_DATA -}; - -/** - * Find session and either get data, or drop it. - * - * @param cookie - * @param action - * @return - */ -void *session_find_and(const char *cookie, enum session_find_action action); - -/** - * Initialize the session store. - * Safely empty it if initialized - */ -void session_store_init(void); - -// placeholder for when no data is stored -#define SESSION_DUMMY ((void *) 1) - -/** - * Create a new session. Data must not be NULL, because it wouldn't be possible - * to distinguish between NULL value and session not found in return values. - * It can be e.g. 1 if no data storage is needed. - * - * @param data - data object to attach to the session - * @param free_fn - function that disposes of the data when the session expires - * @return NULL on error, or the new session ID. This is a live pointer into the session structure, - * must be copied if stored, as it can become invalid at any time - */ -const char *session_new(void *data, sess_data_free_fn_t free_fn); - -/** - * Find a session by it's ID (from a cookie) - * - * @param cookie - session ID string - * @return session data (void*), or NULL - */ -void *session_find(const char *cookie); - -/** - * Loop through all sessions and drop these that expired. - */ -void session_drop_expired(void); - -/** - * Drop a session by its ID. Does nothing if not found. - * - * @param cookie - session ID string - */ -void session_drop(const char *cookie); - -/** - * Parse the Cookie header from a request, and do something with the corresponding session. - * - * To also delete the cookie, use req_delete_session_cookie(r) - * - * @param r - request - * @param action - what to do with the session - * @return session data, NULL if removed or not found - */ -void *httpd_req_find_session_and(httpd_req_t *r, enum session_find_action action); - -/** - * Add a header that deletes the session cookie - * - * @param r - request - */ -void httpd_resp_delete_session_cookie(httpd_req_t *r); - -/** - * Add a header that sets the session cookie. - * - * This must be called after creating a session (e.g. user logged in) to make it persistent. - * - * @attention NOT RE-ENTRANT, CAN'T BE USED AGAIN UNTIL THE REQUEST IT WAS CALLED FOR IS DISPATCHED. - * - * @param r - request - * @param cookie - cookie ID - */ -void httpd_resp_set_session_cookie(httpd_req_t *r, const char *cookie); - -#endif //SESSION_STORE_H diff --git a/components/httpd_utils/src/captive.c b/components/httpd_utils/src/captive.c deleted file mode 100644 index 2be20d3..0000000 --- a/components/httpd_utils/src/captive.c +++ /dev/null @@ -1,106 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "httpd_utils/captive.h" -#include "httpd_utils/fd_to_ipv4.h" -#include "httpd_utils/redirect.h" -#include - -static const char *TAG="captive"; - -const char * __attribute__((weak)) -httpd_captive_redirect_get_domain(void) -{ - return "fb_node.captive"; -} - -esp_err_t __attribute__((weak)) -httpd_captive_redirect_get_url(httpd_req_t *r, char *buf, size_t maxlen) -{ - buf = append(buf, "http://", &maxlen); - buf = append(buf, httpd_captive_redirect_get_domain(), &maxlen); - append(buf, "/", &maxlen); - - return ESP_OK; -} - -esp_err_t httpd_captive_redirect(httpd_req_t *r) -{ - // must be static to survive being used in the redirect header - static char s_buf[64]; - - wifi_mode_t mode = 0; - esp_wifi_get_mode(&mode); - - // Check if we have an softap interface. No point checking IPs and hostnames if the client can't be on AP. - if (mode == WIFI_MODE_STA || mode == WIFI_MODE_NULL) { - goto no_redirect; - } - - int fd = httpd_req_to_sockfd(r); - - tcpip_adapter_ip_info_t apip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &apip); - - u32_t client_addr; - if(ESP_OK != fd_to_ipv4(fd, &client_addr)) { - return ESP_FAIL; - } - - ESP_LOGD(TAG, "[captive] Client addr = 0x%08x, ap addr 0x%08x, ap nmask 0x%08x", - client_addr, - apip.ip.addr, - apip.netmask.addr - ); - - // Check if client IP looks like from our AP dhcps - if ((client_addr & apip.netmask.addr) != (apip.ip.addr & apip.netmask.addr)) { - ESP_LOGD(TAG, "[captive] Client not in AP IP range"); - goto no_redirect; - } - - // Get requested hostname from the header - esp_err_t rv = httpd_req_get_hdr_value_str(r, "Host", s_buf, 64); - if (rv != ESP_OK) { - ESP_LOGW(TAG, "[captive] No host in request?"); - goto no_redirect; - } - - ESP_LOGD(TAG, "[captive] Candidate for redirect: %s%s", s_buf, r->uri); - - // Never redirect if host is an IP - if (strlen(s_buf)>7) { - bool isIP = 1; - for (int x = 0; x < strlen(s_buf); x++) { - if (s_buf[x] != '.' && (s_buf[x] < '0' || s_buf[x] > '9')) { - isIP = 0; - break; - } - } - - if (isIP) { - ESP_LOGD(TAG, "[captive] Access via IP, no redirect needed"); - goto no_redirect; - } - } - - // Redirect if host differs - // - this can be e.g. connectivitycheck.gstatic.com or the equivalent for ios - - if (0 != strcmp(s_buf, httpd_captive_redirect_get_domain())) { - ESP_LOGD(TAG, "[captive] Host differs, redirecting..."); - - httpd_captive_redirect_get_url(r, s_buf, 64); - return httpd_redirect_to(r, s_buf); - } else { - ESP_LOGD(TAG, "[captive] Host is OK"); - goto no_redirect; - } - - no_redirect: - return ESP_ERR_NOT_FOUND; -} diff --git a/components/httpd_utils/src/fd_to_ipv4.c b/components/httpd_utils/src/fd_to_ipv4.c deleted file mode 100644 index 7f6377f..0000000 --- a/components/httpd_utils/src/fd_to_ipv4.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include -#include -#include - -#include "httpd_utils/fd_to_ipv4.h" - -static const char *TAG = "fd2ipv4"; - -/** - * Get IP address for a FD - * - * @param fd - * @param[out] ipv4 - * @return success - */ -esp_err_t fd_to_ipv4(int fd, in_addr_t *ipv4) -{ - struct sockaddr_in6 addr; - size_t len = sizeof(addr); - int rv = getpeername(fd, (struct sockaddr *) &addr, &len); - if (rv != 0) { - ESP_LOGE(TAG, "Failed to get IP addr for fd %d", fd); - return ESP_FAIL; - } - - uint32_t client_addr = 0; - if (addr.sin6_family == AF_INET6) { - // this would fail in a real ipv6 network - // with ipv4 the addr is simply in the last ipv6 byte - struct sockaddr_in6 *s = &addr; - client_addr = s->sin6_addr.un.u32_addr[3]; - } - else { - struct sockaddr_in *s = (struct sockaddr_in *) &addr; - client_addr = s->sin_addr.s_addr; - } - - *ipv4 = client_addr; - return ESP_OK; -} diff --git a/components/httpd_utils/src/redirect.c b/components/httpd_utils/src/redirect.c deleted file mode 100644 index 5a174df..0000000 --- a/components/httpd_utils/src/redirect.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include -#include - -#include "httpd_utils/redirect.h" - -static const char *TAG="redirect"; - -esp_err_t httpd_redirect_to(httpd_req_t *r, const char *uri) -{ - ESP_LOGD(TAG, "Redirect to %s", uri); - - httpd_resp_set_hdr(r, "Location", uri); - httpd_resp_set_status(r, "303 See Other"); - httpd_resp_set_type(r, HTTPD_TYPE_TEXT); - const char *msg = "Redirect"; - return httpd_resp_send(r, msg, -1); -} diff --git a/components/httpd_utils/src/session_kvmap.c b/components/httpd_utils/src/session_kvmap.c deleted file mode 100644 index f96638f..0000000 --- a/components/httpd_utils/src/session_kvmap.c +++ /dev/null @@ -1,181 +0,0 @@ -//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG - -#include -#include -#include -#include -#include -#include -#include "httpd_utils/session_kvmap.h" - -static const char *TAG = "sess_kvmap"; - -// this struct is opaque, a stub like this is sufficient for the head pointer. -struct sess_kv_entry; - -/** Session head structure, dynamically allocated */ -SLIST_HEAD(sess_kv_map, sess_kv_entry); - -struct sess_kv_entry { - SLIST_ENTRY(sess_kv_entry) link; - char key[SESS_KVMAP_KEY_LEN]; - void *value; - sess_kv_free_func_t free_fn; -}; - -struct sess_kv_map *sess_kv_map_alloc(void) -{ - ESP_LOGD(TAG, "kv store alloc"); - struct sess_kv_map *map = calloc(sizeof(struct sess_kv_map), 1); - assert(map); - SLIST_INIT(map); - return map; -} - -void sess_kv_map_free(void *head_v) -{ - struct sess_kv_map* head = head_v; - - ESP_LOGD(TAG, "kv store free"); - assert(head); - struct sess_kv_entry *item, *tmp; - SLIST_FOREACH_SAFE(item, head, link, tmp) { - if (item->free_fn) { - item->free_fn(item->value); - free(item); - } - } - free(head); -} - - -void * sess_kv_map_get(struct sess_kv_map *head, const char *key) -{ - assert(head); - assert(key); - ESP_LOGD(TAG, "kv store get %s", key); - - struct sess_kv_entry *item; - SLIST_FOREACH(item, head, link) { - if (0==strcmp(item->key, key)) { - ESP_LOGD(TAG, "got ok"); - return item->value; - } - } - - ESP_LOGD(TAG, "not found in store"); - return NULL; -} - -void * sess_kv_map_take(struct sess_kv_map *head, const char *key) -{ - assert(head); - assert(key); - ESP_LOGD(TAG, "kv store take %s", key); - - struct sess_kv_entry *item; - SLIST_FOREACH(item, head, link) { - if (0==strcmp(item->key, key)) { - item->key[0] = 0; - item->free_fn = NULL; - ESP_LOGD(TAG, "taken ok"); - return item->value; - } - } - - ESP_LOGD(TAG, "not found in store"); - return NULL; -} - -esp_err_t sess_kv_map_remove(struct sess_kv_map *head, const char *key) -{ - assert(head); - assert(key); - ESP_LOGD(TAG, "kv store remove %s", key); - - struct sess_kv_entry *item; - SLIST_FOREACH(item, head, link) { - if (0==strcmp(item->key, key)) { - if (item->free_fn) { - item->free_fn(item->value); - } - item->key[0] = 0; - item->value = NULL; - item->free_fn = NULL; - return ESP_OK; - } - } - - ESP_LOGD(TAG, "couldn't remove, not found: %s", key); - return ESP_ERR_NOT_FOUND; -} - - -esp_err_t sess_kv_map_set(struct sess_kv_map *head, const char *key, void *value, sess_kv_free_func_t free_fn) -{ - assert(head); - assert(key); - ESP_LOGD(TAG, "kv set value for key %s", key); - - size_t key_len = strlen(key); - if (key_len > SESS_KVMAP_KEY_LEN-1) { - ESP_LOGE(TAG, "Key too long: %s", key); - // discard illegal key - return ESP_FAIL; - } - - if (key_len == 0) { - ESP_LOGE(TAG, "Key too short: \"%s\"", key); - // discard illegal key - return ESP_FAIL; - } - - struct sess_kv_entry *item = NULL; - struct sess_kv_entry *empty_item = NULL; // found item with no content - SLIST_FOREACH(item, head, link) { - ESP_LOGD(TAG, "test item with key %s, ptr %p > %p", item->key, item, item->link.sle_next); - if (0 == item->key[0]) { - ESP_LOGD(TAG, "found an empty slot"); - empty_item = item; - } - else if (0==strcmp(item->key, key)) { - ESP_LOGD(TAG, "old value replaced"); - if (item->free_fn) { - item->free_fn(item->value); - } - item->value = value; - item->free_fn = free_fn; - return ESP_OK; - } else { - ESP_LOGD(TAG, "skip this one"); - } - } - - struct sess_kv_entry *new_item = NULL; - - // insert new or reuse an empty item - if (empty_item) { - new_item = empty_item; - ESP_LOGD(TAG, "empty item reused (%p)", new_item); - } else { - ESP_LOGD(TAG, "alloc new item"); - // key not found, add a new entry. - new_item = calloc(sizeof(struct sess_kv_entry), 1); - if (!new_item) { - ESP_LOGE(TAG, "New entry alloc failed"); - return ESP_ERR_NO_MEM; - } - } - - strcpy(new_item->key, key); - new_item->free_fn = free_fn; - new_item->value = value; - - if (!empty_item) { - ESP_LOGD(TAG, "insert new item into list"); - // this item was malloc'd - SLIST_INSERT_HEAD(head, new_item, link); - } - - return ESP_OK; -} diff --git a/components/httpd_utils/src/session_store.c b/components/httpd_utils/src/session_store.c deleted file mode 100644 index 3e00cef..0000000 --- a/components/httpd_utils/src/session_store.c +++ /dev/null @@ -1,220 +0,0 @@ -//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG - -#include -#include -#include -#include -#include -#include - -#include "httpd_utils/session_store.h" - -// TODO add a limit on simultaneously open sessions (can cause memory exhaustion DoS) - -#define COOKIE_LEN 32 -static const char *TAG = "session"; - -struct session { - char cookie[COOKIE_LEN + 1]; - void *data; - TickType_t last_activity_time; - LIST_ENTRY(session) link; - sess_data_free_fn_t free_fn; -}; - -static LIST_HEAD(sessions_, session) s_store; - -static SemaphoreHandle_t sess_store_lock = NULL; -static bool sess_store_inited = false; - - -void session_store_init(void) -{ - if (sess_store_inited) { - xSemaphoreTake(sess_store_lock, portMAX_DELAY); - { - struct session *it, *tit; - LIST_FOREACH_SAFE(it, &s_store, link, tit) { - ESP_LOGW(TAG, "Session cookie expired: \"%s\"", it->cookie); - if (it->free_fn) it->free_fn(it->data); - // no relink, we dont care if the list breaks after this - we're removing all of it - free(it); - } - } - LIST_INIT(&s_store); - xSemaphoreGive(sess_store_lock); - } else { - LIST_INIT(&s_store); - sess_store_lock = xSemaphoreCreateMutex(); - sess_store_inited = true; - } -} - -/** - * Fill buffer with base64 symbols. Does not add a trailing null byte - * - * @param buf - * @param len - */ -static void esp_fill_random_alnum(char *buf, size_t len) -{ -#define alphabet_len 64 - static const char alphabet[alphabet_len] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"; - - unsigned int seed = xTaskGetTickCount(); - - assert(buf != NULL); - for (int i = 0; i < len; i++) { - int index = rand_r(&seed) % alphabet_len; - *buf++ = (uint8_t) alphabet[index]; - } -} - -const char *session_new(void *data, sess_data_free_fn_t free_fn) -{ - assert(data != NULL); - - struct session *item = calloc(sizeof(struct session), 1); - if (item == NULL) return NULL; - - item->data = data; - item->free_fn = free_fn; - esp_fill_random_alnum(item->cookie, COOKIE_LEN); - item->cookie[COOKIE_LEN] = 0; // add the terminator - - xSemaphoreTake(sess_store_lock, portMAX_DELAY); - { - item->last_activity_time = xTaskGetTickCount(); - - LIST_INSERT_HEAD(&s_store, item, link); - } - xSemaphoreGive(sess_store_lock); - - ESP_LOGD(TAG, "New HTTP session: %s", item->cookie); - - return item->cookie; -} - -void *session_find_and(const char *cookie, enum session_find_action action) -{ - // no point in searching if the length is wrong - if (strlen(cookie) != COOKIE_LEN) { - ESP_LOGW(TAG, "Wrong session cookie length: \"%s\"", cookie); - return NULL; - } - - struct session *it = NULL; - - bool found = false; - xSemaphoreTake(sess_store_lock, portMAX_DELAY); - { - LIST_FOREACH(it, &s_store, link) { - if (0==strcmp(it->cookie, cookie)) { - ESP_LOGD(TAG, "Session cookie matched: \"%s\"", cookie); - - it->last_activity_time = xTaskGetTickCount(); - found = true; - break; - } - } - if (found && action == SESS_DROP) { - if (it->free_fn) it->free_fn(it->data); - LIST_REMOVE(it, link); - free(it); - ESP_LOGD(TAG, "Dropped session: \"%s\"", cookie); - } - } - xSemaphoreGive(sess_store_lock); - if (found) { - if (action == SESS_DROP) { - // it was dropped inside the guarded block - // the return value is not used with DROP - return NULL; - } - else if(action == SESS_GET_DATA) { - return it->data; - } - } - - ESP_LOGW(TAG, "Session cookie not found: \"%s\"", cookie); - return NULL; -} - -void *session_find(const char *cookie) -{ - return session_find_and(cookie, SESS_GET_DATA); -} - -void session_drop(const char *cookie) -{ - session_find_and(cookie, SESS_DROP); -} - -void session_drop_expired(void) -{ - struct session *it; - struct session *tit; - - xSemaphoreTake(sess_store_lock, portMAX_DELAY); - { - TickType_t now = xTaskGetTickCount(); - - LIST_FOREACH_SAFE(it, &s_store, link, tit) { - TickType_t elapsed = now - it->last_activity_time; - if (elapsed > pdMS_TO_TICKS(SESSION_EXPIRY_TIME_S*1000)) { - ESP_LOGD(TAG, "Session cookie expired: \"%s\"", it->cookie); - if (it->free_fn) it->free_fn(it->data); - LIST_REMOVE(it, link); - free(it); - } - } - } - xSemaphoreGive(sess_store_lock); -} - - -void *httpd_req_find_session_and(httpd_req_t *r, enum session_find_action action) -{ - // this could be called periodically, but it's sufficient to run it at each request - // it won't slow anything down unless there are hundreds of sessions - session_drop_expired(); - - static char buf[256]; - esp_err_t rv = httpd_req_get_hdr_value_str(r, "Cookie", buf, 256); - if (rv == ESP_OK || rv == ESP_ERR_HTTPD_RESULT_TRUNC) { - ESP_LOGD(TAG, "Cookie header: %s", buf); - - // probably OK, see if we have a cookie - char *start = strstr(buf, SESSION_COOKIE_NAME"="); - if (start != 0) { - start += strlen(SESSION_COOKIE_NAME"="); - char *end = strchr(start, ';'); - if (end != NULL) *end = 0; - - ESP_LOGD(TAG, "Cookie is: %s", start); - return session_find_and(start, action); - } - } else { - ESP_LOGD(TAG, "No cookie."); - } - - return NULL; -} - -void httpd_resp_delete_session_cookie(httpd_req_t *r) -{ - httpd_resp_set_hdr(r, "Set-Cookie", SESSION_COOKIE_NAME"="); -} - - -// Static because the value is passed and stored by reference, so it wouldn't live long enough if it was on stack, -// and there also isn't any hook for freeing it if we used malloc(). This is an SDK bug. -static char cookie_hdr_buf[COOKIE_LEN + 10]; - -// !!! this must not be called concurrently from a different thread. -// That is no problem so long as the server stays single-threaded -void httpd_resp_set_session_cookie(httpd_req_t *r, const char *cookie) -{ - snprintf(cookie_hdr_buf, COOKIE_LEN + 10, "SESSID=%s", cookie); - httpd_resp_set_hdr(r, "Set-Cookie", cookie_hdr_buf); -} diff --git a/components/httpd_utils/src/session_utils.c b/components/httpd_utils/src/session_utils.c deleted file mode 100644 index c1ef068..0000000 --- a/components/httpd_utils/src/session_utils.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * TODO file description - * - * Created on 2019/07/13. - */ - -#ifndef SESSION_UTILS_C_H -#define SESSION_UTILS_C_H - -#include -#include -#include "httpd_utils/session_kvmap.h" -#include "httpd_utils/session_store.h" - -/** - * Start or resume a session. - */ -esp_err_t HTTP_GET_SESSION(sess_kv_map_t **ppkvstore, httpd_req_t *r) -{ - sess_kv_map_t *kvstore; - kvstore = httpd_req_find_session_and((r), SESS_GET_DATA); - if (NULL == kvstore) { - kvstore = sess_kv_map_alloc(); - if (!kvstore) return ESP_ERR_NO_MEM; - - const char *cookie = session_new(kvstore, sess_kv_map_free); - if (cookie == NULL) { - // session alloc failed - sess_kv_map_free(kvstore); - *ppkvstore = NULL; - return ESP_ERR_NO_MEM; - } - httpd_resp_set_session_cookie(r, cookie); - } - - *ppkvstore = kvstore; - return ESP_OK; -} - - -#endif //SESSION_UTILS_C_H diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index fcfeb61..bf7d008 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,16 +1,14 @@ idf_component_register(SRCS - irblaster_main.c + fanctl_main.c settings.c shutdown_handlers.c sntp_cli.c utils.c wifi_conn.c - irblast.c console/console_ioimpl.c console/console_server.c console/register_cmds.c console/telnet_parser.c - web/websrv.c console/commands/cmd_dump.c console/commands/cmd_factory_reset.c console/commands/cmd_heap.c diff --git a/main/irblaster_main.c b/main/fanctl_main.c similarity index 96% rename from main/irblaster_main.c rename to main/fanctl_main.c index bc25ec5..83737ac 100644 --- a/main/irblaster_main.c +++ b/main/fanctl_main.c @@ -15,7 +15,6 @@ #include "console/console_server.h" -#include "web/websrv.h" #include "wifi_conn.h" #include "console/register_cmds.h" @@ -38,8 +37,6 @@ void app_main(void) { ESP_ERROR_CHECK(esp_netif_init()); if (g_Settings.wifi_enabled && (g_Settings.sta_enabled || g_Settings.ap_enabled)) { initialise_wifi(); - - websrv_init(); g_State.wifi_inited = true; } else { // initialise the bare minimum so wifi config can be changed @@ -52,8 +49,6 @@ void app_main(void) { g_State.wifi_inited = true; } - irblast_setup(); - console_init(NULL); register_console_commands(); diff --git a/main/web/websrv.c b/main/web/websrv.c deleted file mode 100644 index d5cd730..0000000 --- a/main/web/websrv.c +++ /dev/null @@ -1,217 +0,0 @@ -#include -#include - -#include -#include -#include - -#include "websrv.h" -#include "esp_http_server.h" -#include "utils.h" -#include "irblast.h" - -#include "www_files_enum.h" - -static const char *TAG = "websrv"; - -static httpd_handle_t s_hServer = NULL; - -// Embedded files (must also be listed in CMakeLists.txt as COMPONENT_EMBED_TXTFILES) -efile(index_file, "index_html"); - -static struct tpl_kv_list build_index_replacements_kv(void) { - //char name[TPL_KV_KEY_LEN]; - struct tpl_kv_list kv = tpl_kv_init(); - tpl_kv_add(&kv, "version", APP_VERSION); - return kv; -} - -/* Main page */ -static esp_err_t handler_index(httpd_req_t *req) { - struct tpl_kv_list kv = build_index_replacements_kv(); - - esp_err_t suc = httpd_send_template_file(req, FILE_INDEX_HTML, tpl_kv_replacer, &kv, 0); - tpl_kv_free(&kv); - return suc; -} - -/* Update XHR for new index page data */ -static esp_err_t handler_update(httpd_req_t *req) { - struct tpl_kv_list kv = build_index_replacements_kv(); - - esp_err_t suc = tpl_kv_send_as_ascii_map(req, &kv); - tpl_kv_free(&kv); - return suc; -} - -/* Set a param */ -static esp_err_t handler_set(httpd_req_t *req) { - char buf[64]; - int n = httpd_req_recv(req, buf, 63); - if (n < 0) { - ESP_LOGW(TAG, "rx er"); - goto err; - } - buf[n] = 0; - - char keybuf[20]; - char valbuf[20]; - if (ESP_OK != httpd_query_key_value(buf, "key", keybuf, 20)) goto err; - if (ESP_OK != httpd_query_key_value(buf, "value", valbuf, 20)) goto err; - - // TODO - ESP_LOGW(TAG, "bad key %s", keybuf); - goto err; - - return httpd_resp_send(req, NULL, 0); - - err: - return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL); -} - -/* Request emulator reboot */ -static esp_err_t handler_reboot(httpd_req_t *req) { - httpd_resp_send(req, "" - "" - "" - "" - "Reboot requested. Reloading in 10 seconds.
" - "Try now.", -1); - - ESP_LOGI(TAG, "Restarting ESP..."); - esp_restart(); -} - -/* Request emulator reboot */ -static esp_err_t handler_irblast(httpd_req_t *req) { - char buf[64]; - esp_err_t err = httpd_req_get_url_query_str(req, buf, 63); - if (err != ESP_OK) { - ESP_LOGW(TAG, "param er: %s", esp_err_to_name(err)); - goto err; - } - ESP_LOGI(TAG, "querystring: %s", buf); - - char valbuf[20]; - if (ESP_OK != httpd_query_key_value(buf, "do", valbuf, 20)) { - ESP_LOGW(TAG, "fail to get \"do\" param"); - goto err; - } - valbuf[19] = 0; - - ESP_LOGI(TAG, "IR cmd to send: %s", valbuf); - - const char *cmdnames[] = { - [IRBLAST_ONOFF] = "onoff", - [IRBLAST_DAYNIGHT] = "daynight", - [IRBLAST_SPEED1] = "speed1", - [IRBLAST_SPEED2] = "speed2", - [IRBLAST_SPEED3] = "speed3", - [IRBLAST_MODE1] = "mode1", - [IRBLAST_MODE2] = "mode2", - [IRBLAST_MODE3] = "mode3", - [IRBLAST_MODE4] = "mode4", - [IRBLAST_HUM1] = "hum1", - [IRBLAST_HUM2] = "hum2", - [IRBLAST_HUM3] = "hum3", - NULL - }; - - for (int i = 0; cmdnames[i]; i++) { - if (strncasecmp(cmdnames[i], valbuf, 20) == 0) { - httpd_resp_send(req, "{\"success\":true}", -1); - irblast_send((enum irblast_cmd) i); - return ESP_OK; - } - } - err: - return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL); -} - -/* An HTTP GET handler */ -static esp_err_t handler_staticfiles(httpd_req_t *r) { - const struct embedded_file_info *file; - const char *fname = r->user_ctx; - enum file_access_level access = FILE_ACCESS_PROTECTED; - - // wildcard files must be public - if (fname == NULL) { - fname = r->uri + 1; // URI always starts with slash, but we dont want a slash in the file name - access = FILE_ACCESS_PUBLIC; - } - -#if USE_CAPTIVE_PORTAL - // First check if this is a phone taken here by the captive portal - esp_err_t rv = httpd_captive_redirect(r); - if (rv != ESP_ERR_NOT_FOUND) return rv; -#endif - - if (ESP_OK != www_get_static_file(fname, access, &file)) { - ESP_LOGW(TAG, "File not found: %s", fname); - return httpd_resp_send_404(r); - } else { - if (streq(file->mime, "text/html")) { - // using the template func to allow includes - return httpd_send_template_file_struct(r, file, /*replacer*/NULL, /*ctx*/NULL, /*opts*/0); - } else { - return httpd_send_static_file_struct(r, file, TPL_ESCAPE_NONE, 0); - } - } -} - -static const httpd_uri_t routes[] = { - { - .uri = "/", - .method = HTTP_GET, - .handler = handler_index, - }, - { - .uri = "/data", - .method = HTTP_GET, - .handler = handler_update, - }, - { - .uri = "/set", - .method = HTTP_POST, - .handler = handler_set, - }, - { - .uri = "/reboot", - .method = HTTP_GET, - .handler = handler_reboot, - }, - { - .uri = "/irblast", - .method = HTTP_GET, - .handler = handler_irblast, - }, - { - .uri = "*", // any file except protected (e.g. not HTML, PEM etc) - .method = HTTP_GET, - .handler = handler_staticfiles, - }, -}; - -esp_err_t websrv_init(void) { - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.max_open_sockets = 3; - - config.uri_match_fn = httpd_uri_match_wildcard; - config.lru_purge_enable = true; - - ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port); - - esp_err_t suc = httpd_start(&s_hServer, &config); - if (suc == ESP_OK) { - ESP_LOGI(TAG, "HTTP server started"); - - for (int i = 0; i < sizeof(routes) / sizeof(httpd_uri_t); i++) { - httpd_register_uri_handler(s_hServer, &routes[i]); - } - - return ESP_OK; - } - - ESP_LOGE(TAG, "Error starting server!"); - return suc; -} diff --git a/main/web/websrv.h b/main/web/websrv.h deleted file mode 100644 index 3bc9679..0000000 --- a/main/web/websrv.h +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Integrated webserver - * - * Created on 2019/07/13. - */ - -#ifndef CSPEMU_WEBSRV_H -#define CSPEMU_WEBSRV_H - -#include - -esp_err_t websrv_init(void); - -#endif //CSPEMU_WEBSRV_H