2 %extra_argument
{config_t
*ctx
}
7 #include "configfile.h"
10 #include "request.h" /* http_request_host_normalize() */
18 static void configparser_push
(config_t
*ctx
, data_config
*dc
, int 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");
30 vector_config_weak_push
(&ctx
->configs_stack
, ctx
->current
);
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
);
40 /* return a copied variable */
41 static data_unset
*configparser_get_variable
(config_t
*ctx
, const buffer
*key
) {
46 fprintf
(stderr
, "get var %s\n", key
->ptr
);
48 for
(dc
= ctx
->current
; dc
; dc
= dc
->parent
) {
50 fprintf
(stderr
, "get var on block: %s\n", dc
->key
->ptr
);
51 array_print
(dc
->value
, 0);
53 if
(NULL
!= (du
= array_get_element
(dc
->value
, key
->ptr
))) {
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
) {
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
);
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
);
75 return
(data_unset
*)ds
;
77 fprintf
(stderr
, "data type mismatch, cannot merge\n");
85 buffer_append_string_buffer
(((data_string
*)op1
)->value
, ((data_string
*)op2
)->value
);
88 ((data_integer
*)op1
)->value
+= ((data_integer
*)op2
)->value
;
91 array
*dst
= ((data_array
*)op1
)->value
;
92 array
*src
= ((data_array
*)op2
)->value
;
96 for
(i
= 0; i
< src
->used
; i
++) {
97 du
= (data_unset
*)src
->data
[i
];
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
));
102 fprintf
(stderr
, "Duplicate array-key '%s'\n", du
->key
->ptr
);
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
();
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
("]"));
130 buffer_append_string_buffer
(b
, rvalue
);
133 rc
= http_request_host_normalize
(b
);
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);
152 metalines
::= metalines metaline.
154 metaline
::= varline.
156 metaline
::= condlines
(A
) EOL.
{ A
= NULL
; }
157 metaline
::= include.
158 metaline
::= include_shell.
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
*}
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
).
{
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
);
192 } else if
(NULL
== array_get_element
(ctx
->current
->value
, B
->key
->ptr
)) {
193 array_insert_unique
(ctx
->current
->value
, B
);
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
);
208 varline
::= key
(A
) APPEND expression
(B
).
{
210 array
*vars
= ctx
->current
->value
;
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
);
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
);
224 buffer_copy_buffer
(du
->key
, A
);
225 array_insert_unique
(ctx
->current
->value
, du
);
229 buffer_copy_buffer
(B
->key
, A
);
230 array_insert_unique
(ctx
->current
->value
, B
);
238 key
(A
) ::= LKEY
(B
).
{
239 if
(strchr
(B
->ptr
, '.') == NULL
) {
240 A
= buffer_init_string
("var.");
241 buffer_append_string_buffer
(A
, B
);
250 expression
(A
) ::= expression
(B
) PLUS value
(C
).
{
253 A
= configparser_merge_data
(B
, C
);
263 expression
(A
) ::= value
(B
).
{
268 value
(A
) ::= key
(B
).
{
271 if
(strncmp
(B
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
274 if
(NULL
!= (env
= getenv
(B
->ptr
+ 4))) {
276 ds
= data_string_init
();
277 buffer_append_string
(ds
->value
, env
);
278 A
= (data_unset
*)ds
;
281 fprintf
(stderr
, "Undefined env variable: %s\n", B
->ptr
+ 4);
284 } else if
(NULL
== (A
= configparser_get_variable
(ctx
, B
))) {
285 fprintf
(stderr
, "Undefined config variable: %s\n", B
->ptr
);
293 value
(A
) ::= STRING
(B
).
{
294 A
= (data_unset
*)data_string_init
();
295 buffer_copy_buffer
(((data_string
*)(A
))->value
, B
);
300 value
(A
) ::= INTEGER
(B
).
{
302 A
= (data_unset
*)data_integer_init
();
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
);
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
;
320 array
(A
) ::= LPARAN RPARAN.
{
323 array
(A
) ::= LPARAN aelements
(B
) RPARAN.
{
328 aelements
(A
) ::= aelements
(C
) COMMA aelement
(B
).
{
331 if
(buffer_is_empty
(B
->key
) ||
332 NULL
== array_get_element
(C
, B
->key
->ptr
)) {
333 array_insert_unique
(C
, B
);
336 fprintf
(stderr
, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n",
348 aelements
(A
) ::= aelements
(C
) COMMA.
{
353 aelements
(A
) ::= aelement
(B
).
{
357 array_insert_unique
(A
, B
);
362 aelement
(A
) ::= expression
(B
).
{
366 aelement
(A
) ::= stringop
(B
) ARRAY_ASSIGN expression
(C
).
{
369 buffer_copy_buffer
(C
->key
, B
);
381 globalstart
::= GLOBAL.
{
383 dc
= (data_config
*)array_get_element
(ctx
->srv
->config_context
, "global");
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
).
{
397 if
(B
->context_ndx
>= C
->context_ndx
) {
398 fprintf
(stderr
, "unreachable else condition\n");
409 condlines
(A
) ::= condline
(B
).
{
414 condline
(A
) ::= context LCURLY metalines RCURLY.
{
420 configparser_pop
(ctx
);
422 force_assert
(cur
&& ctx
->current
);
428 context
::= DOLLAR SRVVARNAME
(B
) LBRACKET stringop
(C
) RBRACKET cond
(E
) expression
(D
).
{
430 buffer
*b
, *rvalue
, *op
;
432 if
(ctx
->ok
&& D
->type
!= TYPE_STRING
) {
433 fprintf
(stderr
, "rvalue must be string");
440 op
= buffer_init_string
("!=");
443 op
= buffer_init_string
("==");
445 case CONFIG_COND_NOMATCH
:
446 op
= buffer_init_string
("!~");
448 case CONFIG_COND_MATCH
:
449 op
= buffer_init_string
("=~");
453 return
; /* unreachable */
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);
468 static const struct {
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 },
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
("\"]"));
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
;
508 if
(COMP_UNSET
== dc
->comp
) {
509 fprintf
(stderr
, "error comp_key %s", dc
->comp_key
->ptr
);
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
) {
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
);
528 buffer_string_set_length
(rvalue
, (size_t)(slash
- rvalue
->ptr
)); /*(truncate)*/
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
);
535 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
541 int rc
= (NULL
== colon
)
542 ? http_request_host_normalize
(rvalue
)
543 : configparser_remoteip_normalize_compat
(rvalue
);
545 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
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
);
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
);
569 if
(ctx
->ok
) switch
(E
) {
572 dc
->string = buffer_init_buffer
(rvalue
);
574 case CONFIG_COND_NOMATCH
:
575 case CONFIG_COND_MATCH
: {
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
);
589 } else if
(NULL
== (dc
->regex_study
=
590 pcre_study
(dc
->regex
, 0, &errptr
)) &&
592 fprintf
(stderr
, "studying regex failed: %s -> %s\n",
593 rvalue
->ptr
, errptr
);
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",
599 } else if
(captures
> 9) {
600 fprintf
(stderr
, "Too many captures in regex, use (?:...) instead of (...): %s\n",
604 dc
->string = buffer_init_buffer
(rvalue
);
607 fprintf
(stderr
, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n"
608 "(perhaps just a missing pcre-devel package ?) \n",
616 fprintf
(stderr
, "unknown condition for $%s[%s]\n",
623 configparser_push
(ctx
, dc
, 1);
625 dc
->free
((data_unset
*) dc
);
643 A
= CONFIG_COND_MATCH
;
648 cond
(A
) ::= NOMATCH.
{
649 A
= CONFIG_COND_NOMATCH
;
652 stringop
(A
) ::= expression
(B
).
{
655 if
(B
->type
== TYPE_STRING
) {
656 A
= buffer_init_buffer
(((data_string
*)B
)->value
);
657 } else if
(B
->type
== TYPE_INTEGER
) {
659 buffer_copy_int
(A
, ((data_integer
*)B
)->value
);
661 fprintf
(stderr
, "operand must be string");
669 include
::= INCLUDE stringop
(A
).
{
671 if
(0 != config_parse_file
(ctx
->srv
, ctx
, A
->ptr
)) {
679 include_shell
::= INCLUDE_SHELL stringop
(A
).
{
681 if
(0 != config_parse_cmd
(ctx
->srv
, ctx
, A
->ptr
)) {