#include #include #include /* vim:set ts=4: vim:set shiftwidth=4: */ /* this is a simple sample structure that we use to keep track of the current context in the configfile when reading it. It just has a field for the currently granted permissions. Later, when the option-table is defined, we'll specify the the needed permissions for each option. */ struct mycontext { int permissions; const char *current_end_token; pool_t *pool; }; enum permissions { O_ROOT = 1, O_SOMESECTION = 2, O_OTHERSECTION = 4, O_LAST = 8 }; static DOTCONF_CB(option_SomeSection_open); static DOTCONF_CB(option_OtherSection_open); static DOTCONF_CB(common_section_close); static DOTCONF_CB(common_option); /* the constant Section End tokens. We define these separately to be able to reuse pointers. We need this for our context 'sensitivity' system. */ static const char end_SomeSection[] = ""; static const char end_OtherSection[] = ""; /* the last field is used to specify the permissions needed for each option. These will be checked at runtime by our contextchecker */ static const configoption_t options[] = { { "", ARG_NONE, option_SomeSection_open, NULL, CTX_ALL }, { end_SomeSection, ARG_NONE, common_section_close, NULL, O_SOMESECTION }, { "", ARG_NONE, option_OtherSection_open, NULL, CTX_ALL }, { end_OtherSection, ARG_NONE, common_section_close, NULL, O_OTHERSECTION }, { "RootOption", ARG_NONE, common_option, NULL, CTX_ALL }, { "SomeOption", ARG_NONE, common_option, NULL, O_SOMESECTION }, { "OtherOption", ARG_NONE, common_option, NULL, O_OTHERSECTION }, { "Hybrid", ARG_NONE, common_option, NULL, O_SOMESECTION | O_OTHERSECTION }, LAST_OPTION }; const char *context_checker(command_t *cmd, unsigned long mask) { struct mycontext *context = cmd->context; /* * this test is quite simple: if the permissions needed for the * to-be-called command are not granted, we'll deny service */ /* Root Context granted and Root Context given? */ if (!mask && !context->permissions) return NULL; if ( !(context->permissions & mask) ) { return pool_strcat(context->pool, "Option '", cmd->name, "' not allowed in <", context->current_end_token ? context->current_end_token + 2 : "global>", " context", NULL); } return NULL; } FUNC_ERRORHANDLER(error_handler) { fprintf(stderr, "[error] %s\n", msg); /* continue reading the configfile ; return 1 stop after first error found */ return 0; } int main(void) { configfile_t *configfile; struct mycontext context; context.current_end_token = 0; context.permissions = 0; context.pool = pool_new(NULL); configfile = dotconf_create("./context.conf", options, (void *)&context, CASE_INSENSITIVE); if (!configfile) { fprintf(stderr, "Error opening configuration file\n"); exit(1); } configfile->errorhandler = (dotconf_errorhandler_t) error_handler; configfile->contextchecker = (dotconf_contextchecker_t) context_checker; if (dotconf_command_loop(configfile) == 0) fprintf(stderr, "Error reading configuration file\n"); dotconf_cleanup(configfile); pool_free(context.pool); return 0; } DOTCONF_CB(common_section_close) { struct mycontext *context = (struct mycontext *)ctx; if (!context->current_end_token) return pool_strcat(context->pool, cmd->name, " without matching <", cmd->name + 2, " section", NULL); if (context->current_end_token != cmd->name) return pool_strcat(context->pool, "Expected '", context->current_end_token, "' but saw ", cmd->name, NULL); return context->current_end_token; } DOTCONF_CB(common_option) { printf("Option %s called\n", cmd->name); return NULL; } DOTCONF_CB(option_SomeSection_open) { struct mycontext *context = (struct mycontext *)ctx; const char *old_end_token = context->current_end_token; int old_override = context->permissions; const char *err = 0; if (context->permissions & O_SOMESECTION) return " cannot be nested"; context->permissions |= O_SOMESECTION; context->current_end_token = end_SomeSection; while (!cmd->configfile->eof) { err = dotconf_command_loop_until_error(cmd->configfile); if (!err) { err = " is missing"; break; } if (err == context->current_end_token) break; dotconf_warning(cmd->configfile, DCLOG_ERR, 0, err); } context->current_end_token = old_end_token; context->permissions = old_override; if (err != end_SomeSection) return err; return NULL; } DOTCONF_CB(option_OtherSection_open) { struct mycontext *context = (struct mycontext *)ctx; const char *old_end_token = context->current_end_token; int old_override = context->permissions ; const char *err = 0; if (context->permissions & O_OTHERSECTION) return " Cannot be nested"; context->permissions |= O_OTHERSECTION; context->current_end_token = end_OtherSection; while (!cmd->configfile->eof) { err = dotconf_command_loop_until_error(cmd->configfile); if (!err) { err = " is missing"; break; } if (err == context->current_end_token) break; dotconf_warning(cmd->configfile, DCLOG_ERR, 0, err); } context->current_end_token = old_end_token; context->permissions = old_override; if (err != end_OtherSection) return err; return NULL; }