2 %extra_argument
{config_t
*ctx
}
8 #include "configfile.h"
11 #include "request.h" /* http_request_host_normalize() */
19 static void configparser_push
(config_t
*ctx
, data_config
*dc
, int isnew
) {
21 dc
->context_ndx
= ctx
->all_configs
->used
;
22 force_assert
(dc
->context_ndx
> ctx
->current
->context_ndx
);
23 array_insert_unique
(ctx
->all_configs
, (data_unset
*)dc
);
24 dc
->parent
= ctx
->current
;
25 vector_config_weak_push
(&dc
->parent
->children
, dc
);
27 if
(ctx
->configs_stack.used
> 0 && ctx
->current
->context_ndx
== 0) {
28 fprintf
(stderr
, "Cannot use conditionals inside a global { ... } block\n");
31 vector_config_weak_push
(&ctx
->configs_stack
, ctx
->current
);
35 static data_config
*configparser_pop
(config_t
*ctx
) {
36 data_config
*old
= ctx
->current
;
37 ctx
->current
= vector_config_weak_pop
(&ctx
->configs_stack
);
41 /* return a copied variable */
42 static data_unset
*configparser_get_variable
(config_t
*ctx
, const buffer
*key
) {
47 fprintf
(stderr
, "get var %s\n", key
->ptr
);
49 for
(dc
= ctx
->current
; dc
; dc
= dc
->parent
) {
51 fprintf
(stderr
, "get var on block: %s\n", dc
->key
->ptr
);
52 array_print
(dc
->value
, 0);
54 if
(NULL
!= (du
= array_get_element_klen
(dc
->value
, CONST_BUF_LEN
(key
)))) {
55 du
= du
->fn
->copy
(du
);
56 buffer_clear
(du
->key
);
63 /* op1 is to be eat/return by this function if success, op1->key is not cared
64 op2 is left untouch, unreferenced
66 static data_unset
*configparser_merge_data
(data_unset
*op1
, const data_unset
*op2
) {
68 if
(op1
->type
!= op2
->type
) {
69 if
(op1
->type
== TYPE_STRING
&& op2
->type
== TYPE_INTEGER
) {
70 data_string
*ds
= (data_string
*)op1
;
71 buffer_append_int
(ds
->value
, ((data_integer
*)op2
)->value
);
73 } else if
(op1
->type
== TYPE_INTEGER
&& op2
->type
== TYPE_STRING
) {
74 data_string
*ds
= data_string_init
();
75 buffer_append_int
(ds
->value
, ((data_integer
*)op1
)->value
);
76 buffer_append_string_buffer
(ds
->value
, ((data_string
*)op2
)->value
);
78 return
(data_unset
*)ds
;
80 fprintf
(stderr
, "data type mismatch, cannot merge\n");
88 buffer_append_string_buffer
(((data_string
*)op1
)->value
, ((data_string
*)op2
)->value
);
91 ((data_integer
*)op1
)->value
+= ((data_integer
*)op2
)->value
;
94 array
*dst
= ((data_array
*)op1
)->value
;
95 array
*src
= ((data_array
*)op2
)->value
;
99 for
(i
= 0; i
< src
->used
; i
++) {
100 du
= (data_unset
*)src
->data
[i
];
102 if
(du
->is_index_key || buffer_is_empty
(du
->key
) ||
!array_get_element_klen
(dst
, CONST_BUF_LEN
(du
->key
))) {
103 array_insert_unique
(dst
, du
->fn
->copy
(du
));
105 fprintf
(stderr
, "Duplicate array-key '%s'\n", du
->key
->ptr
);
120 static int configparser_remoteip_normalize_compat
(buffer
*rvalue
) {
121 /* $HTTP["remoteip"] IPv6 accepted with or without '[]' for config compat
122 * http_request_host_normalize() expects IPv6 with '[]',
123 * and config processing at runtime expects COMP_HTTP_REMOTE_IP
124 * compared without '[]', so strip '[]' after normalization */
125 buffer
*b
= buffer_init
();
128 if
(rvalue
->ptr
[0] != '[') {
129 buffer_append_string_len
(b
, CONST_STR_LEN
("["));
130 buffer_append_string_buffer
(b
, rvalue
);
131 buffer_append_string_len
(b
, CONST_STR_LEN
("]"));
133 buffer_append_string_buffer
(b
, rvalue
);
136 rc
= http_request_host_normalize
(b
, 0);
139 /* remove surrounding '[]' */
140 size_t blen
= buffer_string_length
(b
);
141 if
(blen
> 1) buffer_copy_string_len
(rvalue
, b
->ptr
+1, blen
-2);
155 metalines
::= metalines metaline.
157 metaline
::= varline.
159 metaline
::= condlines
(A
) EOL.
{ A
= NULL
; }
160 metaline
::= include.
161 metaline
::= include_shell.
164 %type value
{data_unset
*}
165 %type expression
{data_unset
*}
166 %type aelement
{data_unset
*}
167 %type condline
{data_config
*}
168 %type cond_else
{data_config
*}
169 %type condlines
{data_config
*}
170 %type aelements
{array
*}
171 %type array
{array
*}
173 %type stringop
{buffer
*}
175 %type cond
{config_cond_t
}
177 %destructor value
{ if
($$
) $$
->fn
->free
($$
); }
178 %destructor expression
{ if
($$
) $$
->fn
->free
($$
); }
179 %destructor aelement
{ if
($$
) $$
->fn
->free
($$
); }
180 %destructor aelements
{ array_free
($$
); }
181 %destructor array
{ array_free
($$
); }
182 %destructor key
{ buffer_free
($$
); }
183 %destructor stringop
{ buffer_free
($$
); }
185 %token_type
{buffer
*}
186 %token_destructor
{ buffer_free
($$
); }
188 varline
::= key
(A
) ASSIGN expression
(B
).
{
190 buffer_copy_buffer
(B
->key
, A
);
191 if
(strncmp
(A
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
192 fprintf
(stderr
, "Setting env variable is not supported in conditional %d %s: %s\n",
193 ctx
->current
->context_ndx
,
194 ctx
->current
->key
->ptr
, A
->ptr
);
196 } else if
(NULL
== array_get_element_klen
(ctx
->current
->value
, CONST_BUF_LEN
(B
->key
))) {
197 array_insert_unique
(ctx
->current
->value
, B
);
200 fprintf
(stderr
, "Duplicate config variable in conditional %d %s: %s\n",
201 ctx
->current
->context_ndx
,
202 ctx
->current
->key
->ptr
, B
->key
->ptr
);
208 if
(B
) B
->fn
->free
(B
);
212 varline
::= key
(A
) FORCE_ASSIGN expression
(B
).
{
214 if
(strncmp
(A
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
215 fprintf
(stderr
, "Setting env variable is not supported in conditional %d %s: %s\n",
216 ctx
->current
->context_ndx
,
217 ctx
->current
->key
->ptr
, A
->ptr
);
220 buffer_copy_buffer
(B
->key
, A
);
221 array_replace
(ctx
->current
->value
, B
);
227 if
(B
) B
->fn
->free
(B
);
231 varline
::= key
(A
) APPEND expression
(B
).
{
233 array
*vars
= ctx
->current
->value
;
236 if
(strncmp
(A
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
237 fprintf
(stderr
, "Appending env variable is not supported in conditional %d %s: %s\n",
238 ctx
->current
->context_ndx
,
239 ctx
->current
->key
->ptr
, A
->ptr
);
241 } else if
(NULL
!= (du
= array_extract_element_klen
(vars
, CONST_BUF_LEN
(A
))) || NULL
!= (du
= configparser_get_variable
(ctx
, A
))) {
242 du
= configparser_merge_data
(du
, B
);
247 buffer_copy_buffer
(du
->key
, A
);
248 array_insert_unique
(ctx
->current
->value
, du
);
251 buffer_copy_buffer
(B
->key
, A
);
252 array_insert_unique
(ctx
->current
->value
, B
);
258 if
(B
) B
->fn
->free
(B
);
262 key
(A
) ::= LKEY
(B
).
{
263 if
(strchr
(B
->ptr
, '.') == NULL
) {
264 A
= buffer_init_string
("var.");
265 buffer_append_string_buffer
(A
, B
);
274 expression
(A
) ::= expression
(B
) PLUS value
(C
).
{
277 A
= configparser_merge_data
(B
, C
);
283 if
(B
) B
->fn
->free
(B
);
285 if
(C
) C
->fn
->free
(C
);
289 expression
(A
) ::= value
(B
).
{
294 value
(A
) ::= key
(B
).
{
297 if
(strncmp
(B
->ptr
, "env.", sizeof
("env.") - 1) == 0) {
300 if
(NULL
!= (env
= getenv
(B
->ptr
+ 4))) {
302 ds
= data_string_init
();
303 buffer_append_string
(ds
->value
, env
);
304 A
= (data_unset
*)ds
;
307 fprintf
(stderr
, "Undefined env variable: %s\n", B
->ptr
+ 4);
310 } else if
(NULL
== (A
= configparser_get_variable
(ctx
, B
))) {
311 fprintf
(stderr
, "Undefined config variable: %s\n", B
->ptr
);
319 value
(A
) ::= STRING
(B
).
{
321 A
= (data_unset
*)data_string_init
();
322 b
= ((data_string
*)(A
))->value
;
324 ((data_string
*)(A
))->value
= B
;
328 value
(A
) ::= INTEGER
(B
).
{
330 A
= (data_unset
*)data_integer_init
();
332 ((data_integer
*)(A
))->value
= strtol
(B
->ptr
, &endptr
, 10);
333 /* skip trailing whitespace */
334 if
(endptr
!= B
->ptr
) while
(isspace
(*endptr
)) endptr
++;
335 if
(0 != errno ||
*endptr
!= '\0') {
336 fprintf
(stderr
, "error parsing number: '%s'\n", B
->ptr
);
342 value
(A
) ::= array
(B
).
{
343 A
= (data_unset
*)data_array_init
();
344 array_free
(((data_array
*)(A
))->value
);
345 ((data_array
*)(A
))->value
= B
;
348 array
(A
) ::= LPARAN RPARAN.
{
351 array
(A
) ::= LPARAN aelements
(B
) RPARAN.
{
356 aelements
(A
) ::= aelements
(C
) COMMA aelement
(B
).
{
359 if
(buffer_is_empty
(B
->key
) ||
360 NULL
== array_get_element_klen
(C
, CONST_BUF_LEN
(B
->key
))) {
361 array_insert_unique
(C
, B
);
364 fprintf
(stderr
, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n",
374 if
(B
) B
->fn
->free
(B
);
378 aelements
(A
) ::= aelements
(C
) COMMA.
{
383 aelements
(A
) ::= aelement
(B
).
{
387 array_insert_unique
(A
, B
);
390 if
(B
) B
->fn
->free
(B
);
394 aelement
(A
) ::= expression
(B
).
{
398 aelement
(A
) ::= stringop
(B
) ARRAY_ASSIGN expression
(C
).
{
401 buffer_copy_buffer
(C
->key
, B
);
406 if
(C
) C
->fn
->free
(C
);
415 globalstart
::= GLOBAL.
{
417 dc
= (data_config
*)array_get_element_klen
(ctx
->srv
->config_context
, CONST_STR_LEN
("global"));
419 configparser_push
(ctx
, dc
, 0);
422 global
::= globalstart LCURLY metalines RCURLY.
{
423 force_assert
(ctx
->current
);
424 configparser_pop
(ctx
);
425 force_assert
(ctx
->current
);
428 condlines
(A
) ::= condlines
(B
) eols ELSE condline
(C
).
{
431 if
(B
->context_ndx
>= C
->context_ndx
) {
432 fprintf
(stderr
, "unreachable else condition\n");
435 if
(B
->cond
== CONFIG_COND_ELSE
) {
436 fprintf
(stderr
, "unreachable condition following else catch-all\n");
447 condlines
(A
) ::= condlines
(B
) eols ELSE cond_else
(C
).
{
450 if
(B
->context_ndx
>= C
->context_ndx
) {
451 fprintf
(stderr
, "unreachable else condition\n");
454 if
(B
->cond
== CONFIG_COND_ELSE
) {
455 fprintf
(stderr
, "unreachable condition following else catch-all\n");
462 dc
= (data_config
*)array_extract_element_klen
(ctx
->all_configs
, CONST_BUF_LEN
(C
->key
));
463 force_assert
(C
== dc
);
464 buffer_copy_buffer
(C
->key
, B
->key
);
466 /*buffer_copy_buffer(C->comp_key, B->comp_key);*/
467 /*C->string = buffer_init_buffer(B->string);*/
468 pos
= buffer_string_length
(C
->key
)-buffer_string_length
(B
->string)-2;
471 C
->key
->ptr
[pos
] = '='; /* opposite cond */
472 /*buffer_copy_string_len(C->op, CONST_STR_LEN("=="));*/
475 C
->key
->ptr
[pos
] = '!'; /* opposite cond */
476 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!="));*/
478 case CONFIG_COND_NOMATCH
:
479 C
->key
->ptr
[pos
] = '='; /* opposite cond */
480 /*buffer_copy_string_len(C->op, CONST_STR_LEN("=~"));*/
482 case CONFIG_COND_MATCH
:
483 C
->key
->ptr
[pos
] = '!'; /* opposite cond */
484 /*buffer_copy_string_len(C->op, CONST_STR_LEN("!~"));*/
486 default
: /* should not happen; CONFIG_COND_ELSE checked further above */
490 if
(NULL
== (dc
= (data_config
*)array_get_element_klen
(ctx
->all_configs
, CONST_BUF_LEN
(C
->key
)))) {
491 /* re-insert into ctx->all_configs with new C->key */
492 array_insert_unique
(ctx
->all_configs
, (data_unset
*)C
);
496 fprintf
(stderr
, "unreachable else condition\n");
498 C
->fn
->free
((data_unset
*)C
);
508 condlines
(A
) ::= condline
(B
).
{
513 condline
(A
) ::= context LCURLY metalines RCURLY.
{
519 configparser_pop
(ctx
);
521 force_assert
(cur
&& ctx
->current
);
527 cond_else
(A
) ::= context_else LCURLY metalines RCURLY.
{
533 configparser_pop
(ctx
);
535 force_assert
(cur
&& ctx
->current
);
541 context
::= DOLLAR SRVVARNAME
(B
) LBRACKET stringop
(C
) RBRACKET cond
(E
) expression
(D
).
{
543 buffer
*b
= NULL
, *rvalue
, *op
= NULL
;
545 if
(ctx
->ok
&& D
->type
!= TYPE_STRING
) {
546 fprintf
(stderr
, "rvalue must be string");
553 op
= buffer_init_string
("!=");
556 op
= buffer_init_string
("==");
558 case CONFIG_COND_NOMATCH
:
559 op
= buffer_init_string
("!~");
561 case CONFIG_COND_MATCH
:
562 op
= buffer_init_string
("=~");
566 return
; /* unreachable */
570 buffer_copy_buffer
(b
, ctx
->current
->key
);
571 buffer_append_string_len
(b
, CONST_STR_LEN
("/"));
572 buffer_append_string_buffer
(b
, B
);
573 buffer_append_string_buffer
(b
, C
);
574 buffer_append_string_buffer
(b
, op
);
575 rvalue
= ((data_string
*)D
)->value
;
576 buffer_append_string_buffer
(b
, rvalue
);
578 if
(NULL
!= (dc
= (data_config
*)array_get_element_klen
(ctx
->all_configs
, CONST_BUF_LEN
(b
)))) {
579 configparser_push
(ctx
, dc
, 0);
581 static const struct {
586 { COMP_SERVER_SOCKET
, CONST_STR_LEN
("SERVER[\"socket\"]" ) },
587 { COMP_HTTP_URL
, CONST_STR_LEN
("HTTP[\"url\"]" ) },
588 { COMP_HTTP_HOST
, CONST_STR_LEN
("HTTP[\"host\"]" ) },
589 { COMP_HTTP_REQUEST_HEADER
,CONST_STR_LEN
("HTTP[\"referer\"]" ) },
590 { COMP_HTTP_USER_AGENT
, CONST_STR_LEN
("HTTP[\"useragent\"]" ) },
591 { COMP_HTTP_REQUEST_HEADER
,CONST_STR_LEN
("HTTP[\"user-agent\"]" ) },
592 { COMP_HTTP_LANGUAGE
, CONST_STR_LEN
("HTTP[\"language\"]" ) },
593 { COMP_HTTP_REQUEST_HEADER
,CONST_STR_LEN
("HTTP[\"cookie\"]" ) },
594 { COMP_HTTP_REMOTE_IP
, CONST_STR_LEN
("HTTP[\"remoteip\"]" ) },
595 { COMP_HTTP_REMOTE_IP
, CONST_STR_LEN
("HTTP[\"remote-ip\"]" ) },
596 { COMP_HTTP_QUERY_STRING
, CONST_STR_LEN
("HTTP[\"querystring\"]") },
597 { COMP_HTTP_QUERY_STRING
, CONST_STR_LEN
("HTTP[\"query-string\"]") },
598 { COMP_HTTP_REQUEST_METHOD
, CONST_STR_LEN
("HTTP[\"request-method\"]") },
599 { COMP_HTTP_SCHEME
, CONST_STR_LEN
("HTTP[\"scheme\"]" ) },
600 { COMP_UNSET
, NULL
, 0 },
604 dc
= data_config_init
();
606 buffer_copy_buffer
(dc
->key
, b
);
607 buffer_copy_buffer
(dc
->op
, op
);
608 buffer_copy_buffer
(dc
->comp_tag
, C
);
609 buffer_copy_buffer
(dc
->comp_key
, B
);
610 buffer_append_string_len
(dc
->comp_key
, CONST_STR_LEN
("[\""));
611 buffer_append_string_buffer
(dc
->comp_key
, C
);
612 buffer_append_string_len
(dc
->comp_key
, CONST_STR_LEN
("\"]"));
615 for
(i
= 0; comps
[i
].comp_key
; i
++) {
616 if
(buffer_is_equal_string
(
617 dc
->comp_key
, comps
[i
].comp_key
, comps
[i
].len
)) {
618 dc
->comp
= comps
[i
].comp
;
622 if
(COMP_UNSET
== dc
->comp
) {
623 if
(buffer_is_equal_string
(B
, CONST_STR_LEN
("REQUEST_HEADER"))) {
624 dc
->comp
= COMP_HTTP_REQUEST_HEADER
;
627 fprintf
(stderr
, "error comp_key %s", dc
->comp_key
->ptr
);
631 else if
(COMP_HTTP_LANGUAGE
== dc
->comp
) {
632 dc
->comp
= COMP_HTTP_REQUEST_HEADER
;
633 buffer_copy_string_len
(dc
->comp_tag
, CONST_STR_LEN
("Accept-Language"));
635 else if
(COMP_HTTP_USER_AGENT
== dc
->comp
) {
636 dc
->comp
= COMP_HTTP_REQUEST_HEADER
;
637 buffer_copy_string_len
(dc
->comp_tag
, CONST_STR_LEN
("User-Agent"));
639 else if
(COMP_HTTP_REMOTE_IP
== dc
->comp
640 && (dc
->cond
== CONFIG_COND_EQ || dc
->cond
== CONFIG_COND_NE
)) {
641 char * const slash
= strchr
(rvalue
->ptr
, '/'); /* CIDR mask */
642 char * const colon
= strchr
(rvalue
->ptr
, ':'); /* IPv6 */
643 if
(NULL
!= slash
&& slash
== rvalue
->ptr
){/*(skip AF_UNIX /path/file)*/
645 else if
(NULL
!= slash
) {
647 const unsigned long nm_bits
= strtoul
(slash
+ 1, &nptr
, 10);
648 if
(*nptr ||
0 == nm_bits || nm_bits
> (NULL
!= colon ?
128 : 32)) {
649 /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/
650 fprintf
(stderr
, "invalid or missing netmask: %s\n", rvalue
->ptr
);
655 buffer_string_set_length
(rvalue
, (size_t)(slash
- rvalue
->ptr
)); /*(truncate)*/
657 ? http_request_host_normalize
(rvalue
, 0)
658 : configparser_remoteip_normalize_compat
(rvalue
);
659 buffer_append_string_len
(rvalue
, CONST_STR_LEN
("/"));
660 buffer_append_int
(rvalue
, (int)nm_bits
);
662 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
668 int rc
= (NULL
== colon
)
669 ? http_request_host_normalize
(rvalue
, 0)
670 : configparser_remoteip_normalize_compat
(rvalue
);
672 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
677 else if
(COMP_SERVER_SOCKET
== dc
->comp
) {
678 /*(redundant with parsing in network.c; not actually required here)*/
679 if
(rvalue
->ptr
[0] != ':' /*(network.c special-cases ":" and "[]")*/
680 && !(rvalue
->ptr
[0] == '[' && rvalue
->ptr
[1] == ']')) {
681 if
(http_request_host_normalize
(rvalue
, 0)) {
682 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
687 else if
(COMP_HTTP_HOST
== dc
->comp
) {
688 if
(dc
->cond
== CONFIG_COND_EQ || dc
->cond
== CONFIG_COND_NE
) {
689 if
(http_request_host_normalize
(rvalue
, 0)) {
690 fprintf
(stderr
, "invalid IP addr: %s\n", rvalue
->ptr
);
696 dc
->string = buffer_init_buffer
(rvalue
);
698 if
(ctx
->ok
) switch
(E
) {
702 case CONFIG_COND_NOMATCH
:
703 case CONFIG_COND_MATCH
: {
704 if
(!data_config_pcre_compile
(dc
)) {
711 fprintf
(stderr
, "unknown condition for $%s[%s]\n",
718 configparser_push
(ctx
, dc
, 1);
720 dc
->fn
->free
((data_unset
*) dc
);
731 if
(D
) D
->fn
->free
(D
);
737 data_config
*dc
= data_config_init
();
738 buffer_copy_buffer
(dc
->key
, ctx
->current
->key
);
739 buffer_append_string_len
(dc
->key
, CONST_STR_LEN
("/"));
740 buffer_append_string_len
(dc
->key
, CONST_STR_LEN
("else_tmp_token"));
741 dc
->cond
= CONFIG_COND_ELSE
;
742 configparser_push
(ctx
, dc
, 1);
750 A
= CONFIG_COND_MATCH
;
755 cond
(A
) ::= NOMATCH.
{
756 A
= CONFIG_COND_NOMATCH
;
759 stringop
(A
) ::= expression
(B
).
{
762 if
(B
->type
== TYPE_STRING
) {
763 A
= ((data_string
*)B
)->value
;
764 ((data_string
*)B
)->value
= NULL
;
765 } else if
(B
->type
== TYPE_INTEGER
) {
767 buffer_copy_int
(A
, ((data_integer
*)B
)->value
);
769 fprintf
(stderr
, "operand must be string");
773 if
(B
) B
->fn
->free
(B
);
777 include
::= INCLUDE stringop
(A
).
{
779 if
(0 != config_parse_file
(ctx
->srv
, ctx
, A
->ptr
)) {
787 include_shell
::= INCLUDE_SHELL stringop
(A
).
{
789 if
(0 != config_parse_cmd
(ctx
->srv
, ctx
, A
->ptr
)) {