1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
#include <stdio.h>
#include <dotconf.h>
#include <libpool.h>
/* 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[] = "</SomeSection>";
static const char end_OtherSection[] = "</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[] = {
{"<SomeSection>", ARG_NONE, option_SomeSection_open, NULL, CTX_ALL},
{end_SomeSection, ARG_NONE, common_section_close, NULL, O_SOMESECTION},
{"<OtherSection>", 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");
return 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 "<SomeSection> 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 = "</SomeSection> 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 "<OtherSection> 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 = "</SomeSection> 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;
}
|