UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "objconf.h" 30 #include "logging.h" 31 32 #include <string.h> 33 34 #include <cx/utils.h> 35 36 /* dev notes: 37 * 38 * to free ObjectConfig, free: 39 * line dlist 40 * mempool 41 * object 42 */ 43 44 ObjectConfig *load_object_config(char *file) { 45 FILE *in = fopen(file, "r"); 46 if(in == NULL) { 47 return NULL; 48 } 49 50 ObjectConfig *conf = malloc(sizeof(ObjectConfig)); 51 conf->parser.parse = objconf_parse; 52 conf->file = file; 53 //conf->conditions = NULL; 54 conf->levels = NULL; 55 conf->objects = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); 56 //conf->lines = NULL; 57 58 int r = cfg_parse_basic_file((ConfigParser*)conf, in); 59 if(r != 0) { 60 // TODO: free 61 return NULL; 62 } 63 64 fclose(in); 65 66 return conf; 67 } 68 69 ObjectConfig2* objectconf_load(const char *file) { 70 CxMempool *mp = cxBasicMempoolCreate(512); 71 if(!mp) { 72 return NULL; 73 } 74 75 // setup parser 76 ConfigParser2 parser; 77 memset(&parser, 0, sizeof(ConfigParser2)); 78 parser.mp = mp; 79 parser.filename = file; 80 parser.delim = "()/*%"; 81 parser.validateDirective = objectconf_validate_directive; 82 parser.validateObjBegin = objectconf_validate_objbegin; 83 parser.validateObjEnd = objectconf_validate_objend; 84 parser.allow_hierarchy = 1; 85 86 ConfigNode *obj_config = serverconfig_load_file(&parser, file); 87 if(!obj_config) { 88 cxMempoolDestroy(mp); 89 return NULL; 90 } 91 92 ObjectConfig2 *conf = cxMalloc(mp->allocator, sizeof(ObjectConfig2)); 93 if(!conf) { 94 cxMempoolDestroy(mp); 95 return NULL; 96 } 97 98 conf->mp = mp; 99 conf->root = obj_config; 100 101 return conf; 102 } 103 104 void objectconf_free(ObjectConfig2 *objconf) { 105 cxMempoolDestroy(objconf->mp); 106 } 107 108 int objectconf_validate_directive(ConfigParser2 *parser, ConfigNode *node) { 109 // It is possible that this function is called for Objects 110 // if { is on the next line 111 // It may be possible to fix the parser to not do that, but it is fine 112 // to just deal with that here 113 114 // ignore if node->name is an Object name like 115 // Object, Client, If, Else IfElse 116 const char *objnames[] = { "Object", "Client", "If", "ElseIf", "Else" }; 117 size_t typeindex; 118 if(!serverconfig_validate_directive_name(node, objnames, 5, &typeindex)) { 119 return 0; 120 } 121 122 // now check directive type 123 const char *dirtypes[] = { "AuthTrans", "NameTrans", "PathCheck", "ObjectType", "Service", "AddLog", "Error" }; 124 if(serverconfig_validate_directive_name(node, dirtypes, 7, &typeindex)) { 125 return 1; 126 } 127 128 // directives must have parameter names 129 ConfigParam *param_err; 130 if(serverconfig_check_param_names(node, &param_err)) { 131 return 1; 132 } 133 134 // check if the fn parameter exists 135 cxstring fn = serverconfig_directive_get_arg(node, cx_str("fn")); 136 if(fn.length == 0) { 137 return 1; 138 } 139 140 return 0; 141 } 142 143 static int validate_else_node(ConfigNode *node) { 144 // previous node must be "If" or "ElseIf" 145 ConfigNode *prev = serverconfig_previous_dir_or_obj(node); 146 const char *allowed_prev_types[] = { "If", "ElseIf" }; 147 size_t typeindex; 148 if(serverconfig_validate_directive_name(prev, allowed_prev_types, 2, &typeindex)) { 149 return 1; // prevous node (not counting space, comments) is not "If" or "Else" 150 } 151 152 // "Else" must have no args 153 return node->args ? 1 : 0; 154 } 155 156 static int validate_elseif_node(ConfigNode *node) { 157 // the previous node must be "If" or "IfElse" 158 // in case if "IfElse", we also have to check all previous nodes, until 159 // we find the starting "If" node 160 const char *allowed_prev_types[] = { "If", "ElseIf" }; 161 size_t typeindex; 162 ConfigNode *prev = serverconfig_previous_dir_or_obj(node); 163 while(prev) { 164 if(serverconfig_validate_directive_name(prev, allowed_prev_types, 2, &typeindex)) { 165 return 1; // previous node was not "If" or "ElseIf" 166 } 167 // we found a valid "If" node, the whole construct is valid 168 if(typeindex == 0) { 169 return 0; 170 } 171 172 prev = serverconfig_previous_dir_or_obj(node); 173 } 174 // at this point we found only "IfElse" nodes, but no "If" node 175 return 1; 176 } 177 178 int objectconf_validate_objbegin(ConfigParser2 *parser, ConfigNode *node) { 179 // get object type 180 const char *objnames[] = { "Object", "Client", "If", "ElseIf", "Else" }; 181 size_t typeindex; 182 if(serverconfig_validate_directive_name(node, objnames, 5, &typeindex)) { 183 return 1; // unknown object type 184 } 185 186 // some special requirements for "Else" node 187 if(typeindex == 4) { 188 // type is "Else" 189 return validate_else_node(node); 190 } 191 192 // check "ElseIf" was used after "If" or "IfElse" 193 if(typeindex == 3 && validate_elseif_node(node)) { 194 return 1; 195 } 196 197 // only Object must have a name/value arg, other types can have more 198 // complex parameters 199 if(typeindex == 0) { 200 // type is "Object" 201 ConfigParam *err; 202 if(serverconfig_check_param_names(node, &err)) { 203 return 1; 204 } 205 return 0; 206 } 207 208 // all remaining object types must have arguments, but don't check more 209 return node->args ? 0 : 1; 210 } 211 212 int objectconf_validate_objend(ConfigParser2 *parser, ConfigNode *node) { 213 return 0; 214 } 215 216 217 void free_object_config(ObjectConfig *conf) { 218 // free other lists 219 if(conf->levels) { 220 //ucx_list_free(conf->levels); 221 } 222 223 // free mempool 224 //ucx_mempool_destroy(conf->parser.mp->pool); 225 free(conf); 226 } 227 228 229 230 int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) { 231 ObjectConfig *conf = p; 232 233 begin->type = cfg_get_line_type(line); 234 switch(begin->type) { 235 case LINE_BEGIN_TAG: { 236 ConfigTag *tag = cfg_parse_begin_tag(line, conf->parser.mp); 237 if(tag == NULL) { 238 ws_cfg_log(LOG_FAILURE, "Parse error in %s", conf->file); 239 exit(-1); // TODO: better error handling 240 } 241 tag->begin = begin; 242 tag->end = end; 243 tag->type_num = cfg_get_tag_type(cx_strcast(tag->name)); 244 //printf("line {%s}\n", cx_strdub(ll).ptr); 245 if(objconf_on_begin_tag(conf, tag) != 0) { 246 fprintf(stderr, "1error\n"); 247 exit(-1); 248 } 249 break; 250 } 251 case LINE_END_TAG: { 252 cxmutstr tag = cfg_get_end_tag_name(line); 253 if(objconf_on_end_tag(conf, tag) != 0) { 254 fprintf(stderr, "2error\n"); 255 exit(-1); 256 } 257 258 break; 259 } 260 case LINE_DIRECTIVE: { 261 ConfigDirective *dir = cfg_parse_directive( 262 line, 263 conf->parser.mp); 264 dir->begin = begin; 265 dir->end = end; 266 if(objconf_on_directive(conf, dir) != 0) { 267 fprintf(stderr, "3error\n"); 268 exit(-1); 269 } 270 } 271 } 272 return 0; 273 } 274 275 int objconf_on_begin_tag(ObjectConfig *conf, ConfigTag *tag) { 276 CxAllocator *mp = conf->parser.mp; 277 if(tag->type_num != TAG_OBJECT) { 278 ConfigParserLevel *l = conf->levels; 279 if(l->tag->type_num != TAG_OBJECT) { 280 tag->parent = l->tag; 281 } 282 } 283 284 285 switch(tag->type_num) { 286 case TAG_OBJECT: { 287 ConfigObject *obj = OBJ_NEW_N(mp, ConfigObject); 288 obj->begin = tag->begin; 289 obj->end = tag->end; 290 291 obj->name = cfg_param_get(tag->param, cx_str("name")); 292 obj->ppath = cfg_param_get(tag->param, cx_str("ppath")); 293 294 conf->obj = obj; 295 //conf->objects = ucx_list_append_a(mp, conf->objects, obj); 296 cxListAdd(conf->objects, obj); 297 298 // create tree level object 299 ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel); 300 lvl->iftag = NULL; 301 lvl->levelnum = 1; 302 lvl->tag = tag; 303 lvl->next = NULL; 304 //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); 305 CFG_LEVEL_PREPEND(&conf->levels, lvl); 306 307 break; 308 } 309 case TAG_IF: { 310 // create tree level object 311 ConfigParserLevel *last_lvl = conf->levels; 312 313 ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel); 314 315 lvl->iftag = NULL; 316 lvl->levelnum = last_lvl->levelnum + 1; 317 lvl->tag = tag; 318 //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl); 319 CFG_LEVEL_PREPEND(&conf->levels, lvl); 320 last_lvl->iftag = tag; 321 322 break; 323 } 324 case TAG_ELSEIF: { 325 } 326 case TAG_ELSE: { 327 // create tree level object 328 ConfigParserLevel *last_lvl = conf->levels; 329 tag->iftag = last_lvl->iftag; 330 331 ConfigParserLevel *lvl = OBJ_NEW( 332 conf->parser.mp, 333 ConfigParserLevel); 334 335 lvl->iftag = last_lvl->tag; 336 lvl->levelnum = last_lvl->levelnum + 1; 337 lvl->tag = tag; 338 //conf->levels = ucx_list_prepend(conf->levels, lvl); 339 CFG_LEVEL_PREPEND(&conf->levels, lvl); 340 341 break; 342 } 343 case TAG_CLIENT: { 344 // create tree level object 345 346 // TODO 347 348 break; 349 } 350 default: { 351 ws_cfg_log(LOG_FAILURE, "objconf: unknown tag"); 352 return 1; 353 } 354 } 355 356 return 0; 357 } 358 359 int objconf_on_end_tag(ObjectConfig *conf, cxmutstr tagname) { 360 int type = cfg_get_tag_type(cx_strcast(tagname)); 361 if(type == -1) { 362 ws_cfg_log(LOG_FAILURE, "objconf: unknown tag"); 363 return 1; 364 } else { 365 if(type == TAG_OBJECT) { 366 conf->obj = NULL; 367 } 368 369 // remove level 370 /* 371 conf->levels = ucx_list_remove_a( 372 conf->parser.mp, 373 conf->levels, 374 conf->levels); 375 */ 376 conf->levels = conf->levels->next; 377 } 378 379 return 0; 380 } 381 382 int objconf_on_directive(ObjectConfig *conf, ConfigDirective *dir) { 383 ConfigParserLevel *lvl = conf->levels; 384 385 // check if we have a condition for the directive 386 // if the level tag is not an object tag, use it as condition 387 if(lvl->tag->type_num != TAG_OBJECT) { 388 dir->condition = lvl->tag; 389 } 390 391 // add directive to current object 392 /* 393 conf->obj->directives[dir->type_num] = ucx_list_append_a( 394 conf->parser.mp, 395 conf->obj->directives[dir->type_num], 396 dir); 397 */ 398 399 ConfigDirectiveList *dir_entry = cxMalloc(conf->parser.mp, sizeof(ConfigDirectiveList)); 400 dir_entry->directive = dir; 401 dir_entry->next = NULL; 402 CFG_DIRECTIVES_ADD(&conf->obj->directives[dir->type_num], dir_entry); 403 404 return 0; 405 } 406 407