// // 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 char *subst_heap; 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); /** * Add a heap-allocated string to the replacer. * * @param head - list head * @param key - key, copied * @param subst - value, copied * @return success (fails if malloc failed) */ esp_err_t tpl_kv_add_heapstr(struct tpl_kv_list *head, const char *key, 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