[SCons] define with_krb5 for SCons build
[lighttpd.git] / src / configparser.y
blobf5ba03215c0bfb9defb9d30fa5a7be200b533238
1 %token_prefix TK_
2 %extra_argument {config_t *ctx}
3 %name configparser
5 %include {
6 #include "first.h"
7 #include "configfile.h"
8 #include "buffer.h"
9 #include "array.h"
10 #include "request.h" /* http_request_host_normalize() */
12 #include <assert.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <string.h>
18 static void configparser_push(config_t *ctx, data_config *dc, int isnew) {
19 if (isnew) {
20 dc->context_ndx = ctx->all_configs->used;
21 force_assert(dc->context_ndx > ctx->current->context_ndx);
22 array_insert_unique(ctx->all_configs, (data_unset *)dc);
23 dc->parent = ctx->current;
24 vector_config_weak_push(&dc->parent->children, dc);
26 if (ctx->configs_stack.used > 0 && ctx->current->context_ndx == 0) {
27 fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n");
28 exit(-1);
30 vector_config_weak_push(&ctx->configs_stack, ctx->current);
31 ctx->current = dc;
34 static data_config *configparser_pop(config_t *ctx) {
35 data_config *old = ctx->current;
36 ctx->current = vector_config_weak_pop(&ctx->configs_stack);
37 return old;
40 /* return a copied variable */
41 static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) {
42 data_unset *du;
43 data_config *dc;
45 #if 0
46 fprintf(stderr, "get var %s\n", key->ptr);
47 #endif
48 for (dc = ctx->current; dc; dc = dc->parent) {
49 #if 0
50 fprintf(stderr, "get var on block: %s\n", dc->key->ptr);
51 array_print(dc->value, 0);
52 #endif
53 if (NULL != (du = array_get_element(dc->value, key->ptr))) {
54 return du->copy(du);
57 return NULL;
60 /* op1 is to be eat/return by this function if success, op1->key is not cared
61 op2 is left untouch, unreferenced
63 data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) {
64 /* type mismatch */
65 if (op1->type != op2->type) {
66 if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) {
67 data_string *ds = (data_string *)op1;
68 buffer_append_int(ds->value, ((data_integer*)op2)->value);
69 return op1;
70 } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) {
71 data_string *ds = data_string_init();
72 buffer_append_int(ds->value, ((data_integer*)op1)->value);
73 buffer_append_string_buffer(ds->value, ((data_string*)op2)->value);
74 op1->free(op1);
75 return (data_unset *)ds;
76 } else {
77 fprintf(stderr, "data type mismatch, cannot merge\n");
78 op1->free(op1);
79 return NULL;
83 switch (op1->type) {
84 case TYPE_STRING:
85 buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value);
86 break;
87 case TYPE_INTEGER:
88 ((data_integer *)op1)->value += ((data_integer *)op2)->value;
89 break;
90 case TYPE_ARRAY: {
91 array *dst = ((data_array *)op1)->value;
92 array *src = ((data_array *)op2)->value;
93 data_unset *du;
94 size_t i;
96 for (i = 0; i < src->used; i ++) {
97 du = (data_unset *)src->data[i];
98 if (du) {
99 if (du->is_index_key || buffer_is_empty(du->key) || !array_get_element(dst, du->key->ptr)) {
100 array_insert_unique(dst, du->copy(du));
101 } else {
102 fprintf(stderr, "Duplicate array-key '%s'\n", du->key->ptr);
103 op1->free(op1);
104 return NULL;
108 break;
109 default:
110 force_assert(0);
111 break;
114 return op1;
117 static int configparser_remoteip_normalize_compat(buffer *rvalue) {
118 /* $HTTP["remoteip"] IPv6 accepted with or without '[]' for config compat
119 * http_request_host_normalize() expects IPv6 with '[]',
120 * and config processing at runtime expects COMP_HTTP_REMOTE_IP
121 * compared without '[]', so strip '[]' after normalization */
122 buffer *b = buffer_init();
123 int rc;
125 if (rvalue->ptr[0] != '[') {
126 buffer_append_string_len(b, CONST_STR_LEN("["));
127 buffer_append_string_buffer(b, rvalue);
128 buffer_append_string_len(b, CONST_STR_LEN("]"));
129 } else {
130 buffer_append_string_buffer(b, rvalue);
133 rc = http_request_host_normalize(b);
135 if (0 == rc) {
136 /* remove surrounding '[]' */
137 size_t blen = buffer_string_length(b);
138 if (blen > 1) buffer_copy_string_len(rvalue, b->ptr+1, blen-2);
141 buffer_free(b);
142 return rc;
147 %parse_failure {
148 ctx->ok = 0;
151 input ::= metalines.
152 metalines ::= metalines metaline.
153 metalines ::= .
154 metaline ::= varline.
155 metaline ::= global.
156 metaline ::= condlines(A) EOL. { A = NULL; }
157 metaline ::= include.
158 metaline ::= include_shell.
159 metaline ::= EOL.
161 %type value {data_unset *}
162 %type expression {data_unset *}
163 %type aelement {data_unset *}
164 %type condline {data_config *}
165 %type condlines {data_config *}
166 %type aelements {array *}
167 %type array {array *}
168 %type key {buffer *}
169 %type stringop {buffer *}
171 %type cond {config_cond_t }
173 %destructor value { if ($$) $$->free($$); }
174 %destructor expression { if ($$) $$->free($$); }
175 %destructor aelement { if ($$) $$->free($$); }
176 %destructor aelements { array_free($$); }
177 %destructor array { array_free($$); }
178 %destructor key { buffer_free($$); }
179 %destructor stringop { buffer_free($$); }
181 %token_type {buffer *}
182 %token_destructor { buffer_free($$); }
184 varline ::= key(A) ASSIGN expression(B). {
185 if (ctx->ok) {
186 buffer_copy_buffer(B->key, A);
187 if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
188 fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n",
189 ctx->current->context_ndx,
190 ctx->current->key->ptr, A->ptr);
191 ctx->ok = 0;
192 } else if (NULL == array_get_element(ctx->current->value, B->key->ptr)) {
193 array_insert_unique(ctx->current->value, B);
194 B = NULL;
195 } else {
196 fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n",
197 ctx->current->context_ndx,
198 ctx->current->key->ptr, B->key->ptr);
199 ctx->ok = 0;
200 B->free(B);
201 B = NULL;
204 buffer_free(A);
205 A = NULL;
208 varline ::= key(A) APPEND expression(B). {
209 if (ctx->ok) {
210 array *vars = ctx->current->value;
211 data_unset *du;
213 if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) {
214 fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n",
215 ctx->current->context_ndx,
216 ctx->current->key->ptr, A->ptr);
217 ctx->ok = 0;
218 } else if (NULL != (du = array_extract_element(vars, A->ptr)) || NULL != (du = configparser_get_variable(ctx, A))) {
219 du = configparser_merge_data(du, B);
220 if (NULL == du) {
221 ctx->ok = 0;
223 else {
224 buffer_copy_buffer(du->key, A);
225 array_insert_unique(ctx->current->value, du);
227 B->free(B);
228 } else {
229 buffer_copy_buffer(B->key, A);
230 array_insert_unique(ctx->current->value, B);
232 buffer_free(A);
233 A = NULL;
234 B = NULL;
238 key(A) ::= LKEY(B). {
239 if (strchr(B->ptr, '.') == NULL) {
240 A = buffer_init_string("var.");
241 buffer_append_string_buffer(A, B);
242 buffer_free(B);
243 B = NULL;
244 } else {
245 A = B;
246 B = NULL;
250 expression(A) ::= expression(B) PLUS value(C). {
251 A = NULL;
252 if (ctx->ok) {
253 A = configparser_merge_data(B, C);
254 if (NULL == A) {
255 ctx->ok = 0;
257 B = NULL;
258 C->free(C);
259 C = NULL;
263 expression(A) ::= value(B). {
264 A = B;
265 B = NULL;
268 value(A) ::= key(B). {
269 A = NULL;
270 if (ctx->ok) {
271 if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) {
272 char *env;
274 if (NULL != (env = getenv(B->ptr + 4))) {
275 data_string *ds;
276 ds = data_string_init();
277 buffer_append_string(ds->value, env);
278 A = (data_unset *)ds;
280 else {
281 fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4);
282 ctx->ok = 0;
284 } else if (NULL == (A = configparser_get_variable(ctx, B))) {
285 fprintf(stderr, "Undefined config variable: %s\n", B->ptr);
286 ctx->ok = 0;
288 buffer_free(B);
289 B = NULL;
293 value(A) ::= STRING(B). {
294 A = (data_unset *)data_string_init();
295 buffer_copy_buffer(((data_string *)(A))->value, B);
296 buffer_free(B);
297 B = NULL;
300 value(A) ::= INTEGER(B). {
301 char *endptr;
302 A = (data_unset *)data_integer_init();
303 errno = 0;
304 ((data_integer *)(A))->value = strtol(B->ptr, &endptr, 10);
305 /* skip trailing whitespace */
306 if (endptr != B->ptr) while (isspace(*endptr)) endptr++;
307 if (0 != errno || *endptr != '\0') {
308 fprintf(stderr, "error parsing number: '%s'\n", B->ptr);
309 ctx->ok = 0;
311 buffer_free(B);
312 B = NULL;
314 value(A) ::= array(B). {
315 A = (data_unset *)data_array_init();
316 array_free(((data_array *)(A))->value);
317 ((data_array *)(A))->value = B;
318 B = NULL;
320 array(A) ::= LPARAN RPARAN. {
321 A = array_init();
323 array(A) ::= LPARAN aelements(B) RPARAN. {
324 A = B;
325 B = NULL;
328 aelements(A) ::= aelements(C) COMMA aelement(B). {
329 A = NULL;
330 if (ctx->ok) {
331 if (buffer_is_empty(B->key) ||
332 NULL == array_get_element(C, B->key->ptr)) {
333 array_insert_unique(C, B);
334 B = NULL;
335 } else {
336 fprintf(stderr, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n",
337 B->key->ptr);
338 ctx->ok = 0;
339 B->free(B);
340 B = NULL;
343 A = C;
344 C = NULL;
348 aelements(A) ::= aelements(C) COMMA. {
349 A = C;
350 C = NULL;
353 aelements(A) ::= aelement(B). {
354 A = NULL;
355 if (ctx->ok) {
356 A = array_init();
357 array_insert_unique(A, B);
358 B = NULL;
362 aelement(A) ::= expression(B). {
363 A = B;
364 B = NULL;
366 aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). {
367 A = NULL;
368 if (ctx->ok) {
369 buffer_copy_buffer(C->key, B);
370 buffer_free(B);
371 B = NULL;
373 A = C;
374 C = NULL;
378 eols ::= EOL.
379 eols ::= .
381 globalstart ::= GLOBAL. {
382 data_config *dc;
383 dc = (data_config *)array_get_element(ctx->srv->config_context, "global");
384 force_assert(dc);
385 configparser_push(ctx, dc, 0);
388 global ::= globalstart LCURLY metalines RCURLY. {
389 force_assert(ctx->current);
390 configparser_pop(ctx);
391 force_assert(ctx->current);
394 condlines(A) ::= condlines(B) eols ELSE condline(C). {
395 A = NULL;
396 if (ctx->ok) {
397 if (B->context_ndx >= C->context_ndx) {
398 fprintf(stderr, "unreachable else condition\n");
399 ctx->ok = 0;
401 C->prev = B;
402 B->next = C;
403 A = C;
404 B = NULL;
405 C = NULL;
409 condlines(A) ::= condline(B). {
410 A = B;
411 B = NULL;
414 condline(A) ::= context LCURLY metalines RCURLY. {
415 A = NULL;
416 if (ctx->ok) {
417 data_config *cur;
419 cur = ctx->current;
420 configparser_pop(ctx);
422 force_assert(cur && ctx->current);
424 A = cur;
428 context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). {
429 data_config *dc;
430 buffer *b, *rvalue, *op;
432 if (ctx->ok && D->type != TYPE_STRING) {
433 fprintf(stderr, "rvalue must be string");
434 ctx->ok = 0;
437 if (ctx->ok) {
438 switch(E) {
439 case CONFIG_COND_NE:
440 op = buffer_init_string("!=");
441 break;
442 case CONFIG_COND_EQ:
443 op = buffer_init_string("==");
444 break;
445 case CONFIG_COND_NOMATCH:
446 op = buffer_init_string("!~");
447 break;
448 case CONFIG_COND_MATCH:
449 op = buffer_init_string("=~");
450 break;
451 default:
452 force_assert(0);
453 return; /* unreachable */
456 b = buffer_init();
457 buffer_copy_buffer(b, ctx->current->key);
458 buffer_append_string(b, "/");
459 buffer_append_string_buffer(b, B);
460 buffer_append_string_buffer(b, C);
461 buffer_append_string_buffer(b, op);
462 rvalue = ((data_string*)D)->value;
463 buffer_append_string_buffer(b, rvalue);
465 if (NULL != (dc = (data_config *)array_get_element(ctx->all_configs, b->ptr))) {
466 configparser_push(ctx, dc, 0);
467 } else {
468 static const struct {
469 comp_key_t comp;
470 char *comp_key;
471 size_t len;
472 } comps[] = {
473 { COMP_SERVER_SOCKET, CONST_STR_LEN("SERVER[\"socket\"]" ) },
474 { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) },
475 { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) },
476 { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) },
477 { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) },
478 { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) },
479 { COMP_HTTP_LANGUAGE, CONST_STR_LEN("HTTP[\"language\"]" ) },
480 { COMP_HTTP_COOKIE, CONST_STR_LEN("HTTP[\"cookie\"]" ) },
481 { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) },
482 { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) },
483 { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") },
484 { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") },
485 { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") },
486 { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) },
487 { COMP_UNSET, NULL, 0 },
489 size_t i;
491 dc = data_config_init();
493 buffer_copy_buffer(dc->key, b);
494 buffer_copy_buffer(dc->op, op);
495 buffer_copy_buffer(dc->comp_key, B);
496 buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\""));
497 buffer_append_string_buffer(dc->comp_key, C);
498 buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]"));
499 dc->cond = E;
501 for (i = 0; comps[i].comp_key; i ++) {
502 if (buffer_is_equal_string(
503 dc->comp_key, comps[i].comp_key, comps[i].len)) {
504 dc->comp = comps[i].comp;
505 break;
508 if (COMP_UNSET == dc->comp) {
509 fprintf(stderr, "error comp_key %s", dc->comp_key->ptr);
510 ctx->ok = 0;
512 else if (COMP_HTTP_REMOTE_IP == dc->comp
513 && (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE)) {
514 char * const slash = strchr(rvalue->ptr, '/'); /* CIDR mask */
515 char * const colon = strchr(rvalue->ptr, ':'); /* IPv6 */
516 if (NULL != slash && slash == rvalue->ptr){/*(skip AF_UNIX /path/file)*/
518 else if (NULL != slash) {
519 char *nptr;
520 const unsigned long nm_bits = strtoul(slash + 1, &nptr, 10);
521 if (*nptr || 0 == nm_bits || nm_bits > (NULL != colon ? 128 : 32)) {
522 /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/
523 fprintf(stderr, "invalid or missing netmask: %s\n", rvalue->ptr);
524 ctx->ok = 0;
526 else {
527 int rc;
528 buffer_string_set_length(rvalue, (size_t)(slash - rvalue->ptr)); /*(truncate)*/
529 rc = (NULL == colon)
530 ? http_request_host_normalize(rvalue)
531 : configparser_remoteip_normalize_compat(rvalue);
532 buffer_append_string_len(rvalue, CONST_STR_LEN("/"));
533 buffer_append_int(rvalue, (int)nm_bits);
534 if (0 != rc) {
535 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
536 ctx->ok = 0;
540 else {
541 int rc = (NULL == colon)
542 ? http_request_host_normalize(rvalue)
543 : configparser_remoteip_normalize_compat(rvalue);
544 if (0 != rc) {
545 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
546 ctx->ok = 0;
550 else if (COMP_SERVER_SOCKET == dc->comp) {
551 /*(redundant with parsing in network.c; not actually required here)*/
552 if (rvalue->ptr[0] != ':' /*(network.c special-cases ":" and "[]")*/
553 && !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')) {
554 if (http_request_host_normalize(rvalue)) {
555 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
556 ctx->ok = 0;
560 else if (COMP_HTTP_HOST == dc->comp) {
561 if (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) {
562 if (http_request_host_normalize(rvalue)) {
563 fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
564 ctx->ok = 0;
569 if (ctx->ok) switch(E) {
570 case CONFIG_COND_NE:
571 case CONFIG_COND_EQ:
572 dc->string = buffer_init_buffer(rvalue);
573 break;
574 case CONFIG_COND_NOMATCH:
575 case CONFIG_COND_MATCH: {
576 #ifdef HAVE_PCRE_H
577 const char *errptr;
578 int erroff, captures;
580 if (NULL == (dc->regex =
581 pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) {
582 dc->string = buffer_init_string(errptr);
583 dc->cond = CONFIG_COND_UNSET;
585 fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n",
586 rvalue->ptr, errptr, erroff);
588 ctx->ok = 0;
589 } else if (NULL == (dc->regex_study =
590 pcre_study(dc->regex, 0, &errptr)) &&
591 errptr != NULL) {
592 fprintf(stderr, "studying regex failed: %s -> %s\n",
593 rvalue->ptr, errptr);
594 ctx->ok = 0;
595 } else if (0 != (pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT, &captures))) {
596 fprintf(stderr, "getting capture count for regex failed: %s\n",
597 rvalue->ptr);
598 ctx->ok = 0;
599 } else if (captures > 9) {
600 fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n",
601 rvalue->ptr);
602 ctx->ok = 0;
603 } else {
604 dc->string = buffer_init_buffer(rvalue);
606 #else
607 fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n"
608 "(perhaps just a missing pcre-devel package ?) \n",
609 B->ptr, C->ptr);
610 ctx->ok = 0;
611 #endif
612 break;
615 default:
616 fprintf(stderr, "unknown condition for $%s[%s]\n",
617 B->ptr, C->ptr);
618 ctx->ok = 0;
619 break;
622 if (ctx->ok) {
623 configparser_push(ctx, dc, 1);
624 } else {
625 dc->free((data_unset*) dc);
629 buffer_free(b);
630 buffer_free(op);
631 buffer_free(B);
632 B = NULL;
633 buffer_free(C);
634 C = NULL;
635 D->free(D);
636 D = NULL;
639 cond(A) ::= EQ. {
640 A = CONFIG_COND_EQ;
642 cond(A) ::= MATCH. {
643 A = CONFIG_COND_MATCH;
645 cond(A) ::= NE. {
646 A = CONFIG_COND_NE;
648 cond(A) ::= NOMATCH. {
649 A = CONFIG_COND_NOMATCH;
652 stringop(A) ::= expression(B). {
653 A = NULL;
654 if (ctx->ok) {
655 if (B->type == TYPE_STRING) {
656 A = buffer_init_buffer(((data_string*)B)->value);
657 } else if (B->type == TYPE_INTEGER) {
658 A = buffer_init();
659 buffer_copy_int(A, ((data_integer *)B)->value);
660 } else {
661 fprintf(stderr, "operand must be string");
662 ctx->ok = 0;
665 B->free(B);
666 B = NULL;
669 include ::= INCLUDE stringop(A). {
670 if (ctx->ok) {
671 if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) {
672 ctx->ok = 0;
674 buffer_free(A);
675 A = NULL;
679 include_shell ::= INCLUDE_SHELL stringop(A). {
680 if (ctx->ok) {
681 if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) {
682 ctx->ok = 0;
684 buffer_free(A);
685 A = NULL;