/* * Copyright 2024 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CFG_RCL_H_ #define CFG_RCL_H_ #include "config.h" #include "cfg_file.h" #include "ucl.h" #include "mem_pool.h" #define CFG_RCL_ERROR cfg_rcl_error_quark() static inline GQuark cfg_rcl_error_quark(void) { return g_quark_from_static_string("cfg-rcl-error-quark"); } #ifdef __cplusplus extern "C" { #endif struct rspamd_rcl_section; struct rspamd_rcl_sections_map; struct rspamd_config; struct rspamd_rcl_default_handler_data; enum rspamd_rcl_flag { RSPAMD_CL_FLAG_TIME_FLOAT = 0x1 << 0, RSPAMD_CL_FLAG_TIME_TIMEVAL = 0x1 << 1, RSPAMD_CL_FLAG_TIME_TIMESPEC = 0x1 << 2, RSPAMD_CL_FLAG_TIME_INTEGER = 0x1 << 3, RSPAMD_CL_FLAG_TIME_UINT_32 = 0x1 << 4, RSPAMD_CL_FLAG_INT_16 = 0x1 << 5, RSPAMD_CL_FLAG_INT_32 = 0x1 << 6, RSPAMD_CL_FLAG_INT_64 = 0x1 << 7, RSPAMD_CL_FLAG_UINT = 0x1 << 8, RSPAMD_CL_FLAG_INT_SIZE = 0x1 << 9, RSPAMD_CL_FLAG_STRING_PATH = 0x1 << 10, RSPAMD_CL_FLAG_BOOLEAN_INVERSE = 0x1 << 11, RSPAMD_CL_FLAG_STRING_LIST_HASH = 0x1 << 12, RSPAMD_CL_FLAG_MULTIPLE = 0x1 << 13, RSPAMD_CL_FLAG_SIGNKEY = 0x1 << 14, }; struct rspamd_rcl_struct_parser { struct rspamd_config *cfg; gpointer user_struct; goffset offset; int flags; /* enum rspamd_rcl_flag */ }; /** * Common handler type * @param cfg configuration * @param obj object to parse * @param ud user data (depends on section) * @param err error object * @return TRUE if a section has been parsed */ typedef gboolean (*rspamd_rcl_handler_t)(rspamd_mempool_t *pool, const ucl_object_t *obj, const char *key, gpointer ud, struct rspamd_rcl_section *section, GError **err); typedef gboolean (*rspamd_rcl_default_handler_t)(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * A handler type that is called at the end of section parsing * @param cfg configuration * @param ud user data */ typedef void (*rspamd_rcl_section_fin_t)(rspamd_mempool_t *pool, gpointer ud); /** * Add a default handler for a section * @param section section pointer * @param name name of param * @param handler handler of param * @param offset offset in a structure * @param flags flags for the parser * @return newly created structure */ struct rspamd_rcl_default_handler_data *rspamd_rcl_add_default_handler( struct rspamd_rcl_section *section, const char *name, rspamd_rcl_default_handler_t handler, goffset offset, int flags, const char *doc_string); /** * Add new section to the configuration * @param top top section * @param name the name of the section * @param key_attr name of the attribute that should be used as key attribute * @param handler handler function for all attributes * @param type type of object handled by a handler * @param required whether at least one of these sections is required * @param strict_type turn on strict check for types for this section * @return newly created structure */ struct rspamd_rcl_section *rspamd_rcl_add_section( struct rspamd_rcl_sections_map **top, struct rspamd_rcl_section *parent_section, const char *name, const char *key_attr, rspamd_rcl_handler_t handler, enum ucl_type type, gboolean required, gboolean strict_type); struct rspamd_rcl_section *rspamd_rcl_add_section_doc( struct rspamd_rcl_sections_map **top, struct rspamd_rcl_section *parent_section, const char *name, const char *key_attr, rspamd_rcl_handler_t handler, enum ucl_type type, gboolean required, gboolean strict_type, ucl_object_t *doc_target, const char *doc_string); /** * Init common sections known to rspamd * @return top section */ struct rspamd_rcl_sections_map *rspamd_rcl_config_init(struct rspamd_config *cfg, GHashTable *skip_sections); /** * Parse configuration * @param top top section * @param cfg rspamd configuration * @param ptr pointer to the target * @param pool pool object * @param obj ucl object to parse * @param err error pointer * @return */ gboolean rspamd_rcl_parse(struct rspamd_rcl_sections_map *top, struct rspamd_config *cfg, gpointer ptr, rspamd_mempool_t *pool, const ucl_object_t *obj, GError **err); /** * Here is a section of common handlers that accepts rcl_struct_parser * which itself contains a struct pointer and the offset of a member in a * specific structure */ /** * Parse a string field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure * @param section the current section * @param err error pointer * @return TRUE if a string value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_string(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse an integer field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_integer(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a float field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_double(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a time field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_time(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a string list field of a structure presented by a GList* object * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_string_list(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a boolean field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_boolean(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a keypair field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_keypair(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a pubkey field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_pubkey(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a inet addr field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_addr(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a gmime inet address field of a structure * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_mime_addr(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Parse a raw ucl object * @param cfg config pointer * @param obj object to parse * @param ud struct_parser structure (flags mean the exact structure used) * @param section the current section * @param err error pointer * @return TRUE if a value has been successfully parsed */ gboolean rspamd_rcl_parse_struct_ucl(rspamd_mempool_t *pool, const ucl_object_t *obj, gpointer ud, struct rspamd_rcl_section *section, GError **err); /** * Utility functions */ /** * Register new parser for a worker type of an option with the specified name * @param cfg config structure * @param type type of worker (GQuark) * @param name name of option * @param handler handler of option * @param target opaque target structure, note it **MUST** be worker ctx due to some reasons I don't really remember * @param offset offset inside a structure */ void rspamd_rcl_register_worker_option(struct rspamd_config *cfg, GQuark type, const char *name, rspamd_rcl_default_handler_t handler, gpointer target, glong offset, int flags, const char *doc_string); /** * Adds new documentation object to the configuration * @param doc_target target object where to insert documentation (top object is used if this is NULL) * @param doc_object documentation object to insert */ ucl_object_t *rspamd_rcl_add_doc_obj(ucl_object_t *doc_target, const char *doc_string, const char *doc_name, ucl_type_t type, rspamd_rcl_default_handler_t handler, int flags, const char *default_value, gboolean required); /** * Adds new documentation option specified by path `doc_path` that should be * split by dots */ ucl_object_t *rspamd_rcl_add_doc_by_path(struct rspamd_config *cfg, const char *doc_path, const char *doc_string, const char *doc_name, ucl_type_t type, rspamd_rcl_default_handler_t handler, int flags, const char *default_value, gboolean required); /** * Parses example and adds documentation according to the example: * * ``` * section { * param1 = value; # explanation * param2 = value; # explanation * } * ``` * * will produce the following documentation strings: * section -> * section.param1 : explanation * section.param2 : explanation * * @param cfg * @param root_path * @param example_data * @param example_len * @return */ ucl_object_t *rspamd_rcl_add_doc_by_example(struct rspamd_config *cfg, const char *root_path, const char *doc_string, const char *doc_name, const char *example_data, gsize example_len); /** * Add lua modules path * @param cfg * @param path * @param err * @return */ gboolean rspamd_rcl_add_lua_plugins_path(struct rspamd_rcl_sections_map *sections, struct rspamd_config *cfg, const char *path, gboolean main_path, GError **err); /** * Calls for an external lua function to apply potential config transformations * if needed. This function can change the cfg->rcl_obj. * * Example of transformation function: * * function(obj) * if obj.something == 'foo' then * obj.something = "bla" * return true, obj * end * * return false, nil * end * * If function returns 'false' then rcl_obj is not touched. Otherwise, * it is changed, then rcl_obj is imported from lua. Old config is dereferenced. * @param cfg */ void rspamd_rcl_maybe_apply_lua_transform(struct rspamd_config *cfg); void rspamd_rcl_sections_free(struct rspamd_rcl_sections_map *sections); void rspamd_config_calculate_cksum(struct rspamd_config *cfg); /* * Read configuration file */ gboolean rspamd_config_parse_ucl(struct rspamd_config *cfg, const char *filename, GHashTable *vars, ucl_include_trace_func_t inc_trace, void *trace_data, gboolean skip_jinja, GError **err); gboolean rspamd_config_read(struct rspamd_config *cfg, const char *filename, rspamd_rcl_section_fin_t logger_fin, gpointer logger_ud, GHashTable *vars, gboolean skip_jinja, char **lua_env); #ifdef __cplusplus } #endif #endif /* CFG_RCL_H_ */