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 cond_else
{data_config
*}
166 %type condlines
{data_config
*}
167 %type aelements
{array
*}
168 %type array
{array
*}
170 %type stringop
{buffer
*}
172 %type cond
{config_cond_t
}
174 %destructor value
{ if
($$
) $$
->free
($$
); }
175 %destructor expression
{ if
($$
) $$
->free
($$
); }
176 %destructor aelement
{ if
($$
) $$
->free
($$
); }
177 %destructor aelements
{ array_free
($$
); }
178 %destructor array
{ array_free
($$
); }
179 %destructor key
{ buffer_free
($$
); }
180 %destructor stringop
{ buffer_free
($$
); }
182 %token_type
{buffer
*}
183 %token_destructor
{ buffer_free
($$
); }
185 varline
::= key
(A
) ASSIGN expression
(B
).
{
187 buffer_copy_buffer
(B
->key
, A
);
188 if
(strncmp
(A
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
189 fprintf
(stderr
, "Setting env variable is not supported in conditional %d %s: %s\n",
190 ctx
->current
->context_ndx
,
191 ctx
->current
->key
->ptr
, A
->ptr
);
193 } else if
(NULL
== array_get_element
(ctx
->current
->value
, B
->key
->ptr
)) {
194 array_insert_unique
(ctx
->current
->value
, B
);
197 fprintf
(stderr
, "Duplicate config variable in conditional %d %s: %s\n",
198 ctx
->current
->context_ndx
,
199 ctx
->current
->key
->ptr
, B
->key
->ptr
);
209 varline
::= key
(A
) APPEND expression
(B
).
{
211 array
*vars
= ctx
->current
->value
;
214 if
(strncmp
(A
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
215 fprintf
(stderr
, "Appending env variable is not supported in conditional %d %s: %s\n",
216 ctx
->current
->context_ndx
,
217 ctx
->current
->key
->ptr
, A
->ptr
);
219 } else if
(NULL
!= (du
= array_extract_element
(vars
, A
->ptr
)) || NULL
!= (du
= configparser_get_variable
(ctx
, A
))) {
220 du
= configparser_merge_data
(du
, B
);
225 buffer_copy_buffer
(du
->key
, A
);
226 array_insert_unique
(ctx
->current
->value
, du
);
230 buffer_copy_buffer
(B
->key
, A
);
231 array_insert_unique
(ctx
->current
->value
, B
);
239 key
(A
) ::= LKEY
(B
).
{
240 if
(strchr
(B
->ptr
, '.') == NULL
) {
241 A
= buffer_init_string
("var.");
242 buffer_append_string_buffer
(A
, B
);
251 expression
(A
) ::= expression
(B
) PLUS value
(C
).
{
254 A
= configparser_merge_data
(B
, C
);
264 expression
(A
) ::= value
(B
).
{
269 value
(A
) ::= key
(B
).
{
272 if
(strncmp
(B
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
275 if
(NULL
!= (env
= getenv
(B
->ptr
+ 4))) {
277 ds
= data_string_init
();
278 buffer_append_string
(ds
->value
, env
);
279 A
= (data_unset
*)ds
;
282 fprintf
(stderr
, "Undefined env variable: %s\n", B
->ptr
+ 4);
285 } else if
(NULL
== (A
= configparser_get_variable
(ctx
, B
))) {
286 fprintf
(stderr
, "Undefined config variable: %s\n", B
->ptr
);
294 value
(A
) ::= STRING
(B
).
{
295 A
= (data_unset
*)data_string_init
();
296 buffer_copy_buffer
(((data_string
*)(A
))->value
, B
);
301 value
(A
) ::= INTEGER
(B
).
{
303 A
= (data_unset
*)data_integer_init
();
305 ((data_integer
*)(A
))->value
= strtol
(B
->ptr
, &endptr
, 10);
306 /* skip trailing whitespace */
307 if
(endptr
!= B
->ptr
) while
(isspace
(*endptr
)) endptr
++;
308 if
(0 != errno ||
*endptr
!= '\0') {
309 fprintf
(stderr
, "error parsing number: '%s'\n", B
->ptr
);
315 value
(A
) ::= array
(B
).
{
316 A
= (data_unset
*)data_array_init
();
317 array_free
(((data_array
*)(A
))->value
);
318 ((data_array
*)(A
))->value
= B
;
321 array
(A
) ::= LPARAN RPARAN.
{
324 array
(A
) ::= LPARAN aelements
(B
) RPARAN.
{
329 aelements
(A
) ::= aelements
(C
) COMMA aelement
(B
).
{
332 if
(buffer_is_empty
(B
->key
) ||
333 NULL
== array_get_element
(C
, B
->key
->ptr
)) {
334 array_insert_unique
(C
, B
);
337 fprintf
(stderr
, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n",
349 aelements
(A
) ::= aelements
(C
) COMMA.
{
354 aelements
(A
) ::= aelement
(B
).
{
358 array_insert_unique
(A
, B
);
363 aelement
(A
) ::= expression
(B
).
{
367 aelement
(A
) ::= stringop
(B
) ARRAY_ASSIGN expression
(C
).
{
370 buffer_copy_buffer
(C
->key
, B
);
382 globalstart
::= GLOBAL.
{
384 dc
= (data_config
*)array_get_element
(ctx
->srv
->config_context
, "global");
386 configparser_push
(ctx
, dc
, 0);
389 global
::= globalstart LCURLY metalines RCURLY.
{
390 force_assert
(ctx
->current
);
391 configparser_pop
(ctx
);
392 force_assert
(ctx
->current
);
395 condlines
(A
) ::= condlines
(B
) eols ELSE condline
(C
).
{
398 if
(B
->context_ndx
>= C
->context_ndx
) {
399 fprintf
(stderr
, "unreachable else condition\n");
402 if
(B
->cond
== CONFIG_COND_ELSE
) {
403 fprintf
(stderr
, "unreachable condition following else catch-all\n");
414 condlines
(A
) ::= condlines
(B
) eols ELSE cond_else
(C
).
{
417 if
(B
->context_ndx
>= C
->context_ndx
) {
418 fprintf
(stderr
, "unreachable else condition\n");
421 if
(B
->cond
== CONFIG_COND_ELSE
) {
422 fprintf
(stderr
, "unreachable condition following else catch-all\n");
429 dc
= (data_config
*)array_extract_element
(ctx
->all_configs
, C
->key
->ptr
);
430 force_assert
(C
== dc
);
431 buffer_copy_buffer
(C
->key
, B
->key
);
432 /*buffer_copy_buffer(C->comp_key, B->comp_key);*/
433 /*C->string = buffer_init_buffer(B->string);*/
434 pos
= buffer_string_length
(C
->key
)-buffer_string_length
(B
->string)-2;
437 C
->key
->ptr
[pos
] = '='; /* opposite cond */
438 /*buffer_copy_string_len(C->op, CONST_STR_LEN("=="));*/
441 C
->key
->ptr
[pos
] = '!'; /* opposite cond */
442 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!="));*/
444 case CONFIG_COND_NOMATCH
:
445 C
->key
->ptr
[pos
] = '='; /* opposite cond */
446 /*buffer_copy_string_len(C->op, CONST_STR_LEN("=~"));*/
448 case CONFIG_COND_MATCH
:
449 C
->key
->ptr
[pos
] = '!'; /* opposite cond */
450 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!~"));*/
452 default
: /* should not happen; CONFIG_COND_ELSE checked further above */
456 if
(NULL
== (dc
= (data_config
*)array_get_element
(ctx
->all_configs
, C
->key
->ptr
))) {
457 /* re-insert into ctx->all_configs with new C->key */
458 array_insert_unique
(ctx
->all_configs
, (data_unset
*)C
);
462 fprintf
(stderr
, "unreachable else condition\n");
464 C
->free
((data_unset
*)C
);
474 condlines
(A
) ::= condline
(B
).
{
479 condline
(A
) ::= context LCURLY metalines RCURLY.
{
485 configparser_pop
(ctx
);
487 force_assert
(cur
&& ctx
->current
);
493 cond_else
(A
) ::= context_else LCURLY metalines RCURLY.
{
499 configparser_pop
(ctx
);
501 force_assert
(cur
&& ctx
->current
);
507 context
::= DOLLAR SRVVARNAME
(B
) LBRACKET stringop
(C
) RBRACKET cond
(E
) expression
(D
).
{
509 buffer
*b
, *rvalue
, *op
;
511 if
(ctx
->ok
&& D
->type
!= TYPE_STRING
) {
512 fprintf
(stderr
, "rvalue must be string");
519 op
= buffer_init_string
("!=");
522 op
= buffer_init_string
("==");
524 case CONFIG_COND_NOMATCH
:
525 op
= buffer_init_string
("!~");
527 case CONFIG_COND_MATCH
:
528 op
= buffer_init_string
("=~");
532 return
; /* unreachable */
536 buffer_copy_buffer
(b
, ctx
->current
->key
);
537 buffer_append_string
(b
, "/");
538 buffer_append_string_buffer
(b
, B
);
539 buffer_append_string_buffer
(b
, C
);
540 buffer_append_string_buffer
(b
, op
);
541 rvalue
= ((data_string
*)D
)->value
;
542 buffer_append_string_buffer
(b
, rvalue
);
544 if
(NULL
!= (dc
= (data_config
*)array_get_element
(ctx
->all_configs
, b
->ptr
))) {
545 configparser_push
(ctx
, dc
, 0);
547 static const struct {
552 { COMP_SERVER_SOCKET
, CONST_STR_LEN
("SERVER[\"socket\"]" ) },
553 { COMP_HTTP_URL
, CONST_STR_LEN
("HTTP[\"url\"]" ) },
554 { COMP_HTTP_HOST
, CONST_STR_LEN
("HTTP[\"host\"]" ) },
555 { COMP_HTTP_REFERER
, CONST_STR_LEN
("HTTP[\"referer\"]" ) },
556 { COMP_HTTP_USER_AGENT
, CONST_STR_LEN
("HTTP[\"useragent\"]" ) },
557 { COMP_HTTP_USER_AGENT
, CONST_STR_LEN
("HTTP[\"user-agent\"]" ) },
558 { COMP_HTTP_LANGUAGE
, CONST_STR_LEN
("HTTP[\"language\"]" ) },
559 { COMP_HTTP_COOKIE
, CONST_STR_LEN
("HTTP[\"cookie\"]" ) },
560 { COMP_HTTP_REMOTE_IP
, CONST_STR_LEN
("HTTP[\"remoteip\"]" ) },
561 { COMP_HTTP_REMOTE_IP
, CONST_STR_LEN
("HTTP[\"remote-ip\"]" ) },
562 { COMP_HTTP_QUERY_STRING
, CONST_STR_LEN
("HTTP[\"querystring\"]") },
563 { COMP_HTTP_QUERY_STRING
, CONST_STR_LEN
("HTTP[\"query-string\"]") },
564 { COMP_HTTP_REQUEST_METHOD
, CONST_STR_LEN
("HTTP[\"request-method\"]") },
565 { COMP_HTTP_SCHEME
, CONST_STR_LEN
("HTTP[\"scheme\"]" ) },
566 { COMP_UNSET
, NULL
, 0 },
570 dc
= data_config_init
();
572 buffer_copy_buffer
(dc
->key
, b
);
573 buffer_copy_buffer
(dc
->op
, op
);
574 buffer_copy_buffer
(dc
->comp_key
, B
);
575 buffer_append_string_len
(dc
->comp_key
, CONST_STR_LEN
("[\""));
576 buffer_append_string_buffer
(dc
->comp_key
, C
);
577 buffer_append_string_len
(dc
->comp_key
, CONST_STR_LEN
("\"]"));
580 for
(i
= 0; comps
[i
].comp_key
; i
++) {
581 if
(buffer_is_equal_string
(
582 dc
->comp_key
, comps
[i
].comp_key
, comps
[i
].len
)) {
583 dc
->comp
= comps
[i
].comp
;
587 if
(COMP_UNSET
== dc
->comp
) {
588 fprintf
(stderr
, "error comp_key %s", dc
->comp_key
->ptr
);
591 else if
(COMP_HTTP_REMOTE_IP
== dc
->comp
592 && (dc
->cond
== CONFIG_COND_EQ || dc
->cond
== CONFIG_COND_NE
)) {
593 char * const slash
= strchr
(rvalue
->ptr
, '/'); /* CIDR mask */
594 char * const colon
= strchr
(rvalue
->ptr
, ':'); /* IPv6 */
595 if
(NULL
!= slash
&& slash
== rvalue
->ptr
){/*(skip AF_UNIX /path/file)*/
597 else if
(NULL
!= slash
) {
599 const unsigned long nm_bits
= strtoul
(slash
+ 1, &nptr
, 10);
600 if
(*nptr ||
0 == nm_bits || nm_bits
> (NULL
!= colon ?
128 : 32)) {
601 /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/
602 fprintf
(stderr
, "invalid or missing netmask: %s\n", rvalue
->ptr
);
607 buffer_string_set_length
(rvalue
, (size_t)(slash
- rvalue
->ptr
)); /*(truncate)*/
609 ? http_request_host_normalize
(rvalue
)
610 : configparser_remoteip_normalize_compat
(rvalue
);
611 buffer_append_string_len
(rvalue
, CONST_STR_LEN
("/"));
612 buffer_append_int
(rvalue
, (int)nm_bits
);
614 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
620 int rc
= (NULL
== colon
)
621 ? http_request_host_normalize
(rvalue
)
622 : configparser_remoteip_normalize_compat
(rvalue
);
624 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
629 else if
(COMP_SERVER_SOCKET
== dc
->comp
) {
630 /*(redundant with parsing in network.c; not actually required here)*/
631 if
(rvalue
->ptr
[0] != ':' /*(network.c special-cases ":" and "[]")*/
632 && !(rvalue
->ptr
[0] == '[' && rvalue
->ptr
[1] == ']')) {
633 if
(http_request_host_normalize
(rvalue
)) {
634 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
639 else if
(COMP_HTTP_HOST
== dc
->comp
) {
640 if
(dc
->cond
== CONFIG_COND_EQ || dc
->cond
== CONFIG_COND_NE
) {
641 if
(http_request_host_normalize
(rvalue
)) {
642 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
648 if
(ctx
->ok
) switch
(E
) {
651 dc
->string = buffer_init_buffer
(rvalue
);
653 case CONFIG_COND_NOMATCH
:
654 case CONFIG_COND_MATCH
: {
657 int erroff
, captures
;
659 if
(NULL
== (dc
->regex
=
660 pcre_compile
(rvalue
->ptr
, 0, &errptr
, &erroff
, NULL
))) {
661 dc
->string = buffer_init_string
(errptr
);
662 dc
->cond
= CONFIG_COND_UNSET
;
664 fprintf
(stderr
, "parsing regex failed: %s -> %s at offset %d\n",
665 rvalue
->ptr
, errptr
, erroff
);
668 } else if
(NULL
== (dc
->regex_study
=
669 pcre_study
(dc
->regex
, 0, &errptr
)) &&
671 fprintf
(stderr
, "studying regex failed: %s -> %s\n",
672 rvalue
->ptr
, errptr
);
674 } else if
(0 != (pcre_fullinfo
(dc
->regex
, dc
->regex_study
, PCRE_INFO_CAPTURECOUNT
, &captures
))) {
675 fprintf
(stderr
, "getting capture count for regex failed: %s\n",
678 } else if
(captures
> 9) {
679 fprintf
(stderr
, "Too many captures in regex, use (?:...) instead of (...): %s\n",
683 dc
->string = buffer_init_buffer
(rvalue
);
686 fprintf
(stderr
, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n"
687 "(perhaps just a missing pcre-devel package ?) \n",
695 fprintf
(stderr
, "unknown condition for $%s[%s]\n",
702 configparser_push
(ctx
, dc
, 1);
704 dc
->free
((data_unset
*) dc
);
721 data_config
*dc
= data_config_init
();
722 buffer_copy_buffer
(dc
->key
, ctx
->current
->key
);
723 buffer_append_string_len
(dc
->key
, CONST_STR_LEN
("/"));
724 buffer_append_string_len
(dc
->key
, CONST_STR_LEN
("else_tmp_token"));
725 dc
->cond
= CONFIG_COND_ELSE
;
726 configparser_push
(ctx
, dc
, 1);
734 A
= CONFIG_COND_MATCH
;
739 cond
(A
) ::= NOMATCH.
{
740 A
= CONFIG_COND_NOMATCH
;
743 stringop
(A
) ::= expression
(B
).
{
746 if
(B
->type
== TYPE_STRING
) {
747 A
= buffer_init_buffer
(((data_string
*)B
)->value
);
748 } else if
(B
->type
== TYPE_INTEGER
) {
750 buffer_copy_int
(A
, ((data_integer
*)B
)->value
);
752 fprintf
(stderr
, "operand must be string");
760 include
::= INCLUDE stringop
(A
).
{
762 if
(0 != config_parse_file
(ctx
->srv
, ctx
, A
->ptr
)) {
770 include_shell
::= INCLUDE_SHELL stringop
(A
).
{
772 if
(0 != config_parse_cmd
(ctx
->srv
, ctx
, A
->ptr
)) {