Wed, 27 Nov 2024 23:00:07 +0100
add TODO to use a future ucx feature
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2013 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "objconf.h" #include "logging.h" #include <string.h> #include <cx/utils.h> /* dev notes: * * to free ObjectConfig, free: * line dlist * mempool * object */ ObjectConfig *load_object_config(char *file) { FILE *in = fopen(file, "r"); if(in == NULL) { return NULL; } ObjectConfig *conf = malloc(sizeof(ObjectConfig)); conf->parser.parse = objconf_parse; conf->file = file; //conf->conditions = NULL; conf->levels = NULL; conf->objects = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); //conf->lines = NULL; int r = cfg_parse_basic_file((ConfigParser*)conf, in); if(r != 0) { // TODO: free return NULL; } fclose(in); return conf; } ObjectConfig2* objectconf_load(const char *file) { CxMempool *mp = cxBasicMempoolCreate(512); if(!mp) { return NULL; } // setup parser ConfigParser2 parser; memset(&parser, 0, sizeof(ConfigParser2)); parser.mp = mp; parser.filename = file; parser.delim = "()/*%"; parser.validateDirective = objectconf_validate_directive; parser.validateObjBegin = objectconf_validate_objbegin; parser.validateObjEnd = objectconf_validate_objend; parser.allow_hierarchy = 1; ConfigNode *obj_config = serverconfig_load_file(&parser, file); if(!obj_config) { cxMempoolDestroy(mp); return NULL; } ObjectConfig2 *conf = cxMalloc(mp->allocator, sizeof(ObjectConfig2)); if(!conf) { cxMempoolDestroy(mp); return NULL; } conf->mp = mp; conf->root = obj_config; return conf; } void objectconf_free(ObjectConfig2 *objconf) { cxMempoolDestroy(objconf->mp); } int objectconf_validate_directive(ConfigParser2 *parser, ConfigNode *node) { // It is possible that this function is called for Objects // if { is on the next line // It may be possible to fix the parser to not do that, but it is fine // to just deal with that here // ignore if node->name is an Object name like // Object, Client, If, Else IfElse const char *objnames[] = { "Object", "Client", "If", "ElseIf", "Else" }; size_t typeindex; if(!serverconfig_validate_directive_name(node, objnames, 5, &typeindex)) { return 0; } // now check directive type const char *dirtypes[] = { "AuthTrans", "NameTrans", "PathCheck", "ObjectType", "Service", "AddLog", "Error" }; if(serverconfig_validate_directive_name(node, dirtypes, 7, &typeindex)) { return 1; } // directives must have parameter names ConfigParam *param_err; if(serverconfig_check_param_names(node, ¶m_err)) { return 1; } // check if the fn parameter exists cxstring fn = serverconfig_directive_get_arg(node, cx_str("fn")); if(fn.length == 0) { return 1; } return 0; } static int validate_else_node(ConfigNode *node) { // previous node must be "If" or "ElseIf" ConfigNode *prev = serverconfig_previous_dir_or_obj(node); const char *allowed_prev_types[] = { "If", "ElseIf" }; size_t typeindex; if(serverconfig_validate_directive_name(prev, allowed_prev_types, 2, &typeindex)) { return 1; // prevous node (not counting space, comments) is not "If" or "Else" } // "Else" must have no args return node->args ? 1 : 0; } static int validate_elseif_node(ConfigNode *node) { // the previous node must be "If" or "IfElse" // in case if "IfElse", we also have to check all previous nodes, until // we find the starting "If" node const char *allowed_prev_types[] = { "If", "ElseIf" }; size_t typeindex; ConfigNode *prev = serverconfig_previous_dir_or_obj(node); while(prev) { if(serverconfig_validate_directive_name(prev, allowed_prev_types, 2, &typeindex)) { return 1; // previous node was not "If" or "ElseIf" } // we found a valid "If" node, the whole construct is valid if(typeindex == 0) { return 0; } prev = serverconfig_previous_dir_or_obj(node); } // at this point we found only "IfElse" nodes, but no "If" node return 1; } int objectconf_validate_objbegin(ConfigParser2 *parser, ConfigNode *node) { // get object type const char *objnames[] = { "Object", "Client", "If", "ElseIf", "Else" }; size_t typeindex; if(serverconfig_validate_directive_name(node, objnames, 5, &typeindex)) { return 1; // unknown object type } // some special requirements for "Else" node if(typeindex == 4) { // type is "Else" return validate_else_node(node); } // check "ElseIf" was used after "If" or "IfElse" if(typeindex == 3 && validate_elseif_node(node)) { return 1; } // only Object must have a name/value arg, other types can have more // complex parameters if(typeindex == 0) { // type is "Object" ConfigParam *err; if(serverconfig_check_param_names(node, &err)) { return 1; } return 0; } // all remaining object types must have arguments, but don't check more return node->args ? 0 : 1; } int objectconf_validate_objend(ConfigParser2 *parser, ConfigNode *node) { return 0; } void free_object_config(ObjectConfig *conf) { // free other lists if(conf->levels) { //ucx_list_free(conf->levels); } // free mempool //ucx_mempool_destroy(conf->parser.mp->pool); free(conf); } int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) { ObjectConfig *conf = p; begin->type = cfg_get_line_type(line); switch(begin->type) { case LINE_BEGIN_TAG: { ConfigTag *tag = cfg_parse_begin_tag(line, conf->parser.mp); if(tag == NULL) { ws_cfg_log(LOG_FAILURE, "Parse error in %s", conf->file); exit(-1); // TODO: better error handling } tag->begin = begin; tag->end = end; tag->type_num = cfg_get_tag_type(cx_strcast(tag->name)); //printf("line {%s}\n", cx_strdub(ll).ptr); if(objconf_on_begin_tag(conf, tag) != 0) { fprintf(stderr, "1error\n"); exit(-1); } break; } case LINE_END_TAG: { cxmutstr tag = cfg_get_end_tag_name(line); if(objconf_on_end_tag(conf, tag) != 0) { fprintf(stderr, "2error\n"); exit(-1); } break; } case LINE_DIRECTIVE: { ConfigDirective *dir = cfg_parse_directive( line, conf->parser.mp); dir->begin = begin; dir->end = end; if(objconf_on_directive(conf, dir) != 0) { fprintf(stderr, "3error\n"); exit(-1); } } } return 0; } int objconf_on_begin_tag(ObjectConfig *conf, ConfigTag *tag) { CxAllocator *mp = conf->parser.mp; if(tag->type_num != TAG_OBJECT) { ConfigParserLevel *l = conf->levels; if(l->tag->type_num != TAG_OBJECT) { tag->parent = l->tag; } } switch(tag->type_num) { case TAG_OBJECT: { ConfigObject *obj = OBJ_NEW_N(mp, ConfigObject); obj->begin = tag->begin; obj->end = tag->end; obj->name = cfg_param_get(tag->param, cx_str("name")); obj->ppath = cfg_param_get(tag->param, cx_str("ppath")); conf->obj = obj; //conf->objects = ucx_list_append_a(mp, conf->objects, obj); cxListAdd(conf->objects, obj); // create tree level object ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel); lvl->iftag = NULL; lvl->levelnum = 1; lvl->tag = tag; lvl->next = NULL; //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); CFG_LEVEL_PREPEND(&conf->levels, lvl); break; } case TAG_IF: { // create tree level object ConfigParserLevel *last_lvl = conf->levels; ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel); lvl->iftag = NULL; lvl->levelnum = last_lvl->levelnum + 1; lvl->tag = tag; //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); CFG_LEVEL_PREPEND(&conf->levels, lvl); last_lvl->iftag = tag; break; } case TAG_ELSEIF: { } case TAG_ELSE: { // create tree level object ConfigParserLevel *last_lvl = conf->levels; tag->iftag = last_lvl->iftag; ConfigParserLevel *lvl = OBJ_NEW( conf->parser.mp, ConfigParserLevel); lvl->iftag = last_lvl->tag; lvl->levelnum = last_lvl->levelnum + 1; lvl->tag = tag; //conf->levels = ucx_list_prepend(conf->levels, lvl); CFG_LEVEL_PREPEND(&conf->levels, lvl); break; } case TAG_CLIENT: { // create tree level object // TODO break; } default: { ws_cfg_log(LOG_FAILURE, "objconf: unknown tag"); return 1; } } return 0; } int objconf_on_end_tag(ObjectConfig *conf, cxmutstr tagname) { int type = cfg_get_tag_type(cx_strcast(tagname)); if(type == -1) { ws_cfg_log(LOG_FAILURE, "objconf: unknown tag"); return 1; } else { if(type == TAG_OBJECT) { conf->obj = NULL; } // remove level /* conf->levels = ucx_list_remove_a( conf->parser.mp, conf->levels, conf->levels); */ conf->levels = conf->levels->next; } return 0; } int objconf_on_directive(ObjectConfig *conf, ConfigDirective *dir) { ConfigParserLevel *lvl = conf->levels; // check if we have a condition for the directive // if the level tag is not an object tag, use it as condition if(lvl->tag->type_num != TAG_OBJECT) { dir->condition = lvl->tag; } // add directive to current object /* conf->obj->directives[dir->type_num] = ucx_list_append_a( conf->parser.mp, conf->obj->directives[dir->type_num], dir); */ ConfigDirectiveList *dir_entry = cxMalloc(conf->parser.mp, sizeof(ConfigDirectiveList)); dir_entry->directive = dir; dir_entry->next = NULL; CFG_DIRECTIVES_ADD(&conf->obj->directives[dir->type_num], dir_entry); return 0; }