1 %token_prefix TK_
2 %extra_argument {config_t *ctx}
3 %name configparser
4 
5 %include {
6 #include "configfile.h"
7 #include "buffer.h"
8 #include "array.h"
9 
10 #include <assert.h>
11 #include <stdio.h>
12 #include <string.h>
13 
configparser_push(config_t * ctx,data_config * dc,int isnew)14 static void configparser_push(config_t *ctx, data_config *dc, int isnew) {
15   if (isnew) {
16     dc->context_ndx = ctx->all_configs->used;
17     assert(dc->context_ndx > ctx->current->context_ndx);
18     array_insert_unique(ctx->all_configs, (data_unset *)dc);
19     dc->parent = ctx->current;
20     array_insert_unique(dc->parent->childs, (data_unset *)dc);
21   }
22   if (ctx->configs_stack->used > 0 && ctx->current->context_ndx == 0) {
23     fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n");
24     exit(-1);
25   }
26   array_insert_unique(ctx->configs_stack, (data_unset *)ctx->current);
27   ctx->current = dc;
28 }
29 
configparser_pop(config_t * ctx)30 static data_config *configparser_pop(config_t *ctx) {
31   data_config *old = ctx->current;
32   ctx->current = (data_config *) array_pop(ctx->configs_stack);
33   return old;
34 }
35 
36 /* return a copied variable */
configparser_get_variable(config_t * ctx,const buffer * key)37 static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) {
38   data_unset *du;
39   data_config *dc;
40 
41 #if 0
42   fprintf(stderr, "get var %s\n", key->ptr);
43 #endif
44   for (dc = ctx->current; dc; dc = dc->parent) {
45 #if 0
46     fprintf(stderr, "get var on block: %s\n", dc->key->ptr);
47     array_print(dc->value, 0);
48 #endif
49     if (NULL != (du = array_get_element(dc->value, key->ptr))) {
50       return du->copy(du);
51     }
52   }
53   return NULL;
54 }
55 
56 /* op1 is to be eat/return by this function if success, op1->key is not cared
57    op2 is left untouch, unreferenced
58  */
configparser_merge_data(data_unset * op1,const data_unset * op2)59 data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) {
60   /* type mismatch */
61   if (op1->type != op2->type) {
62     if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) {
63       data_string *ds = (data_string *)op1;
64       buffer_append_long(ds->value, ((data_integer*)op2)->value);
65       return op1;
66     } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) {
67       data_string *ds = data_string_init();
68       buffer_append_long(ds->value, ((data_integer*)op1)->value);
69       buffer_append_string_buffer(ds->value, ((data_string*)op2)->value);
70       op1->free(op1);
71       return (data_unset *)ds;
72     } else {
73       fprintf(stderr, "data type mismatch, cannot merge\n");
74       return NULL;
75     }
76   }
77 
78   switch (op1->type) {
79     case TYPE_STRING:
80       buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value);
81       break;
82     case TYPE_INTEGER:
83       ((data_integer *)op1)->value += ((data_integer *)op2)->value;
84       break;
85     case TYPE_ARRAY: {
86       array *dst = ((data_array *)op1)->value;
87       array *src = ((data_array *)op2)->value;
88       data_unset *du;
89       size_t i;
90 
91       for (i = 0; i < src->used; i ++) {
92         du = (data_unset *)src->data[i];
93         if (du) {
94           array_insert_unique(dst, du->copy(du));
95         }
96       }
97       break;
98     default:
99       assert(0);
100       break;
101     }
102   }
103   return op1;
104 }
105 
106 }
107 
108 %parse_failure {
109   ctx->ok = 0;
110 }
111 
112 input ::= metalines.
113 metalines ::= metalines metaline.
114 metalines ::= .
115 metaline ::= varline.
116 metaline ::= global.
condlines(A)117 metaline ::= condlines(A) EOL. { A = NULL; }
118 metaline ::= include.
119 metaline ::= include_shell.
120 metaline ::= EOL.
121 
122 %type       value                  {data_unset *}
123 %type       expression             {data_unset *}
124 %type       aelement               {data_unset *}
125 %type       condline               {data_config *}
126 %type       condlines              {data_config *}
127 %type       global                 {data_config *}
128 %type       aelements              {array *}
129 %type       array                  {array *}
130 %type       key                    {buffer *}
131 %type       stringop               {buffer *}
132 
133 %type       cond                   {config_cond_t }
134 
135 %destructor value                  { $$->free($$); }
136 %destructor expression             { $$->free($$); }
137 %destructor aelement               { $$->free($$); }
138 %destructor aelements              { array_free($$); }
139 %destructor array                  { array_free($$); }
140 %destructor key                    { buffer_free($$); }
141 %destructor stringop               { buffer_free($$); }
142 
143 %token_type                        {buffer *}
144 %token_destructor                  { buffer_free($$); }
145 
key(A)146 varline ::= key(A) ASSIGN expression(B). {
147   if (ctx->ok) {
148     buffer_copy_string_buffer(B->key, A);
149     if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
150       fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n",
151           ctx->current->context_ndx,
152           ctx->current->key->ptr, A->ptr);
153       ctx->ok = 0;
154     } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) {
155       array_insert_unique(ctx->current->value, B);
156       B = NULL;
157     } else {
158       fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n",
159               ctx->current->context_ndx,
160               ctx->current->key->ptr, B->key->ptr);
161       ctx->ok = 0;
162       B->free(B);
163       B = NULL;
164     }
165   }
166   buffer_free(A);
167   A = NULL;
168 }
169 
key(A)170 varline ::= key(A) APPEND expression(B). {
171   array *vars = ctx->current->value;
172   data_unset *du;
173 
174   if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
175     fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n",
176         ctx->current->context_ndx,
177         ctx->current->key->ptr, A->ptr);
178     ctx->ok = 0;
179   } else if (NULL != (du = array_get_element(vars, A->ptr))) {
180     /* exists in current block */
181     du = configparser_merge_data(du, B);
182     if (NULL == du) {
183       ctx->ok = 0;
184     }
185     else {
186       buffer_copy_string_buffer(du->key, A);
187       array_replace(vars, du);
188     }
189     B->free(B);
190   } else if (NULL != (du = configparser_get_variable(ctx, A))) {
191     du = configparser_merge_data(du, B);
192     if (NULL == du) {
193       ctx->ok = 0;
194     }
195     else {
196       buffer_copy_string_buffer(du->key, A);
197       array_insert_unique(ctx->current->value, du);
198     }
199     B->free(B);
200   } else {
201     buffer_copy_string_buffer(B->key, A);
202     array_insert_unique(ctx->current->value, B);
203   }
204   buffer_free(A);
205   A = NULL;
206   B = NULL;
207 }
208 
key(A)209 key(A) ::= LKEY(B). {
210   if (strchr(B->ptr, '.') == NULL) {
211     A = buffer_init_string("var.");
212     buffer_append_string_buffer(A, B);
213     buffer_free(B);
214     B = NULL;
215   } else {
216     A = B;
217     B = NULL;
218   }
219 }
220 
expression(A)221 expression(A) ::= expression(B) PLUS value(C). {
222   A = configparser_merge_data(B, C);
223   if (NULL == A) {
224     ctx->ok = 0;
225   }
226   B = NULL;
227   C->free(C);
228   C = NULL;
229 }
230 
expression(A)231 expression(A) ::= value(B). {
232   A = B;
233   B = NULL;
234 }
235 
value(A)236 value(A) ::= key(B). {
237   A = NULL;
238   if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) {
239     char *env;
240 
241     if (NULL != (env = getenv(B->ptr + 4))) {
242       data_string *ds;
243       ds = data_string_init();
244       buffer_append_string(ds->value, env);
245       A = (data_unset *)ds;
246     }
247     else {
248       fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4);
249       ctx->ok = 0;
250     }
251   } else if (NULL == (A = configparser_get_variable(ctx, B))) {
252     fprintf(stderr, "Undefined config variable: %s\n", B->ptr);
253     ctx->ok = 0;
254   }
255   if (!A) {
256     /* make a dummy so it won't crash */
257     A = (data_unset *)data_string_init();
258   }
259   buffer_free(B);
260   B = NULL;
261 }
262 
value(A)263 value(A) ::= STRING(B). {
264   A = (data_unset *)data_string_init();
265   buffer_copy_string_buffer(((data_string *)(A))->value, B);
266   buffer_free(B);
267   B = NULL;
268 }
269 
value(A)270 value(A) ::= INTEGER(B). {
271   A = (data_unset *)data_integer_init();
272   ((data_integer *)(A))->value = strtol(B->ptr, NULL, 10);
273   buffer_free(B);
274   B = NULL;
275 }
value(A)276 value(A) ::= array(B). {
277   A = (data_unset *)data_array_init();
278   array_free(((data_array *)(A))->value);
279   ((data_array *)(A))->value = B;
280   B = NULL;
281 }
array(A)282 array(A) ::= LPARAN RPARAN. {
283   A = array_init();
284 }
array(A)285 array(A) ::= LPARAN aelements(B) RPARAN. {
286   A = B;
287   B = NULL;
288 }
289 
aelements(A)290 aelements(A) ::= aelements(C) COMMA aelement(B). {
291   if (buffer_is_empty(B->key) ||
292       NULL == array_get_element(C, B->key->ptr)) {
293     array_insert_unique(C, B);
294     B = NULL;
295   } else {
296     fprintf(stderr, "Duplicate array-key: %s\n",
297             B->key->ptr);
298     ctx->ok = 0;
299     B->free(B);
300     B = NULL;
301   }
302 
303   A = C;
304   C = NULL;
305 }
306 
aelements(A)307 aelements(A) ::= aelements(C) COMMA. {
308   A = C;
309   C = NULL;
310 }
311 
aelements(A)312 aelements(A) ::= aelement(B). {
313   A = array_init();
314   array_insert_unique(A, B);
315   B = NULL;
316 }
317 
aelement(A)318 aelement(A) ::= expression(B). {
319   A = B;
320   B = NULL;
321 }
aelement(A)322 aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). {
323   buffer_copy_string_buffer(C->key, B);
324   buffer_free(B);
325   B = NULL;
326 
327   A = C;
328   C = NULL;
329 }
330 
331 eols ::= EOL.
332 eols ::= .
333 
334 globalstart ::= GLOBAL. {
335   data_config *dc;
336   dc = (data_config *)array_get_element(ctx->srv->config_context, "global");
337   assert(dc);
338   configparser_push(ctx, dc, 0);
339 }
340 
global(A)341 global(A) ::= globalstart LCURLY metalines RCURLY. {
342   data_config *cur;
343 
344   cur = ctx->current;
345   configparser_pop(ctx);
346 
347   assert(cur && ctx->current);
348 
349   A = cur;
350 }
351 
condlines(A)352 condlines(A) ::= condlines(B) eols ELSE condline(C). {
353   if (B->context_ndx >= C->context_ndx) {
354     fprintf(stderr, "unreachable else condition\n");
355     ctx->ok = 0;
356   }
357   C->prev = B;
358   B->next = C;
359   A = C;
360   B = NULL;
361   C = NULL;
362 }
363 
condlines(A)364 condlines(A) ::= condline(B). {
365   A = B;
366   B = NULL;
367 }
368 
condline(A)369 condline(A) ::= context LCURLY metalines RCURLY. {
370   data_config *cur;
371 
372   cur = ctx->current;
373   configparser_pop(ctx);
374 
375   assert(cur && ctx->current);
376 
377   A = cur;
378 }
379 
SRVVARNAME(B)380 context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). {
381   data_config *dc;
382   buffer *b, *rvalue, *op;
383 
384   if (ctx->ok && D->type != TYPE_STRING) {
385     fprintf(stderr, "rvalue must be string");
386     ctx->ok = 0;
387   }
388 
389   switch(E) {
390   case CONFIG_COND_NE:
391     op = buffer_init_string("!=");
392     break;
393   case CONFIG_COND_EQ:
394     op = buffer_init_string("==");
395     break;
396   case CONFIG_COND_NOMATCH:
397     op = buffer_init_string("!~");
398     break;
399   case CONFIG_COND_MATCH:
400     op = buffer_init_string("=~");
401     break;
402   default:
403     assert(0);
404     return;
405   }
406 
407   b = buffer_init();
408   buffer_copy_string_buffer(b, ctx->current->key);
409   buffer_append_string(b, "/");
410   buffer_append_string_buffer(b, B);
411   buffer_append_string_buffer(b, C);
412   buffer_append_string_buffer(b, op);
413   rvalue = ((data_string*)D)->value;
414   buffer_append_string_buffer(b, rvalue);
415 
416   if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) {
417     configparser_push(ctx, dc, 0);
418   } else {
419     struct {
420       comp_key_t comp;
421       char *comp_key;
422       size_t len;
423     } comps[] = {
424       { COMP_SERVER_SOCKET,      CONST_STR_LEN("SERVER[\"socket\"]"   ) },
425       { COMP_HTTP_URL,           CONST_STR_LEN("HTTP[\"url\"]"        ) },
426       { COMP_HTTP_HOST,          CONST_STR_LEN("HTTP[\"host\"]"       ) },
427       { COMP_HTTP_REFERER,       CONST_STR_LEN("HTTP[\"referer\"]"    ) },
428       { COMP_HTTP_USER_AGENT,    CONST_STR_LEN("HTTP[\"useragent\"]"  ) },
429       { COMP_HTTP_USER_AGENT,    CONST_STR_LEN("HTTP[\"user-agent\"]"  ) },
430       { COMP_HTTP_LANGUAGE,      CONST_STR_LEN("HTTP[\"language\"]"   ) },
431       { COMP_HTTP_COOKIE,        CONST_STR_LEN("HTTP[\"cookie\"]"     ) },
432       { COMP_HTTP_REMOTE_IP,     CONST_STR_LEN("HTTP[\"remoteip\"]"   ) },
433       { COMP_HTTP_REMOTE_IP,     CONST_STR_LEN("HTTP[\"remote-ip\"]"   ) },
434       { COMP_HTTP_QUERY_STRING,  CONST_STR_LEN("HTTP[\"querystring\"]") },
435       { COMP_HTTP_QUERY_STRING,  CONST_STR_LEN("HTTP[\"query-string\"]") },
436       { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") },
437       { COMP_HTTP_SCHEME,        CONST_STR_LEN("HTTP[\"scheme\"]"     ) },
438       { COMP_UNSET, NULL, 0 },
439     };
440     size_t i;
441 
442     dc = data_config_init();
443 
444     buffer_copy_string_buffer(dc->key, b);
445     buffer_copy_string_buffer(dc->op, op);
446     buffer_copy_string_buffer(dc->comp_key, B);
447     buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\""));
448     buffer_append_string_buffer(dc->comp_key, C);
449     buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]"));
450     dc->cond = E;
451 
452     for (i = 0; comps[i].comp_key; i ++) {
453       if (buffer_is_equal_string(
454             dc->comp_key, comps[i].comp_key, comps[i].len)) {
455         dc->comp = comps[i].comp;
456         break;
457       }
458     }
459     if (COMP_UNSET == dc->comp) {
460       fprintf(stderr, "error comp_key %s", dc->comp_key->ptr);
461       ctx->ok = 0;
462     }
463 
464     switch(E) {
465     case CONFIG_COND_NE:
466     case CONFIG_COND_EQ:
467       dc->string = buffer_init_buffer(rvalue);
468       break;
469     case CONFIG_COND_NOMATCH:
470     case CONFIG_COND_MATCH: {
471 #ifdef HAVE_PCRE_H
472       const char *errptr;
473       int erroff, captures;
474 
475       if (NULL == (dc->regex =
476           pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) {
477         dc->string = buffer_init_string(errptr);
478         dc->cond = CONFIG_COND_UNSET;
479 
480         fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n",
481             rvalue->ptr, errptr, erroff);
482 
483         ctx->ok = 0;
484       } else if (NULL == (dc->regex_study =
485           pcre_study(dc->regex, 0, &errptr)) &&
486                  errptr != NULL) {
487         fprintf(stderr, "studying regex failed: %s -> %s\n",
488             rvalue->ptr, errptr);
489         ctx->ok = 0;
490       } else if (0 != (pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT, &captures))) {
491         fprintf(stderr, "getting capture count for regex failed: %s\n",
492             rvalue->ptr);
493         ctx->ok = 0;
494       } else if (captures > 9) {
495         fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n",
496             rvalue->ptr);
497         ctx->ok = 0;
498       } else {
499         dc->string = buffer_init_buffer(rvalue);
500       }
501 #else
502       fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n"
503 		      "(perhaps just a missing pcre-devel package ?) \n",
504                       B->ptr, C->ptr);
505       ctx->ok = 0;
506 #endif
507       break;
508     }
509 
510     default:
511       fprintf(stderr, "unknown condition for $%s[%s]\n",
512                       B->ptr, C->ptr);
513       ctx->ok = 0;
514       break;
515     }
516 
517     configparser_push(ctx, dc, 1);
518   }
519 
520   buffer_free(b);
521   buffer_free(op);
522   buffer_free(B);
523   B = NULL;
524   buffer_free(C);
525   C = NULL;
526   D->free(D);
527   D = NULL;
528 }
cond(A)529 cond(A) ::= EQ. {
530   A = CONFIG_COND_EQ;
531 }
cond(A)532 cond(A) ::= MATCH. {
533   A = CONFIG_COND_MATCH;
534 }
cond(A)535 cond(A) ::= NE. {
536   A = CONFIG_COND_NE;
537 }
cond(A)538 cond(A) ::= NOMATCH. {
539   A = CONFIG_COND_NOMATCH;
540 }
541 
stringop(A)542 stringop(A) ::= expression(B). {
543   A = NULL;
544   if (ctx->ok) {
545     if (B->type == TYPE_STRING) {
546       A = buffer_init_buffer(((data_string*)B)->value);
547     } else if (B->type == TYPE_INTEGER) {
548       A = buffer_init();
549       buffer_copy_long(A, ((data_integer *)B)->value);
550     } else {
551       fprintf(stderr, "operand must be string");
552       ctx->ok = 0;
553     }
554   }
555   B->free(B);
556   B = NULL;
557 }
558 
stringop(A)559 include ::= INCLUDE stringop(A). {
560   if (ctx->ok) {
561     if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) {
562       ctx->ok = 0;
563     }
564     buffer_free(A);
565     A = NULL;
566   }
567 }
568 
stringop(A)569 include_shell ::= INCLUDE_SHELL stringop(A). {
570   if (ctx->ok) {
571     if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) {
572       ctx->ok = 0;
573     }
574     buffer_free(A);
575     A = NULL;
576   }
577 }
578