xref: /lighttpd1.4/src/configfile.c (revision f2e4e83a)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "burl.h"
5 #include "etag.h"
6 #include "fdevent.h"
7 #include "keyvalue.h"
8 #include "log.h"
9 #include "stream.h"
10 
11 #include "configparser.h"
12 #include "configfile.h"
13 #include "plugin.h"
14 #include "stat_cache.h"
15 #include "sys-crypto.h"
16 
17 #include <sys/stat.h>
18 #include <sys/wait.h>
19 
20 #include <stdlib.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <limits.h>
27 #include <glob.h>
28 
29 #ifdef HAVE_SYSLOG_H
30 # include <syslog.h>
31 #endif
32 
33 #ifndef PATH_MAX
34 #define PATH_MAX 4096
35 #endif
36 
37 typedef struct {
38     PLUGIN_DATA;
39     request_config defaults;
40 } config_data_base;
41 
42 static void config_free_config(void * const p_d) {
43     plugin_data_base * const p = p_d;
44     if (NULL == p) return;
45     if (NULL == p->cvlist) { free(p); return; }
46     /* (init i to 0 if global context; to 1 to skip empty global context) */
47     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
48         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
49         for (; -1 != cpv->k_id; ++cpv) {
50             switch (cpv->k_id) {
51               case 18:/* server.kbytes-per-second */
52                 if (cpv->vtype == T_CONFIG_LOCAL) free(cpv->v.v);
53                 break;
54               default:
55                 break;
56             }
57         }
58     }
59     free(p->cvlist);
60     free(p);
61 }
62 
63 void config_reset_config_bytes_sec(void * const p_d) {
64     plugin_data_base * const p = p_d;
65     if (NULL == p->cvlist) return;
66     /* (init i to 0 if global context; to 1 to skip empty global context) */
67     for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) {
68         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
69         for (; -1 != cpv->k_id; ++cpv) {
70             switch (cpv->k_id) {
71               case 18:/* server.kbytes-per-second */
72                 if (cpv->vtype == T_CONFIG_LOCAL) ((off_t *)cpv->v.v)[0] = 0;
73                 break;
74               default:
75                 break;
76             }
77         }
78     }
79 }
80 
81 static void config_merge_config_cpv(request_config * const pconf, const config_plugin_value_t * const cpv) {
82     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
83       case 0: /* server.document-root */
84         pconf->document_root = cpv->v.b;
85         break;
86       case 1: /* server.name */
87         pconf->server_name = cpv->v.b;
88         break;
89       case 2: /* server.tag */
90         pconf->server_tag = cpv->v.b;
91         break;
92       case 3: /* server.max-request-size */
93         pconf->max_request_size = cpv->v.u;
94         break;
95       case 4: /* server.max-keep-alive-requests */
96         pconf->max_keep_alive_requests = cpv->v.shrt;
97         break;
98       case 5: /* server.max-keep-alive-idle */
99         pconf->max_keep_alive_idle = cpv->v.shrt;
100         break;
101       case 6: /* server.max-read-idle */
102         pconf->max_read_idle = cpv->v.shrt;
103         break;
104       case 7: /* server.max-write-idle */
105         pconf->max_write_idle = cpv->v.shrt;
106         break;
107       case 8: /* server.errorfile-prefix */
108         pconf->errorfile_prefix = cpv->v.b;
109         break;
110       case 9: /* server.error-handler */
111         pconf->error_handler = cpv->v.b;
112         break;
113       case 10:/* server.error-handler-404 */
114         pconf->error_handler_404 = cpv->v.b;
115         break;
116       case 11:/* server.error-intercept */
117         pconf->error_intercept = (0 != cpv->v.u);
118         break;
119       case 12:/* server.force-lowercase-filenames */
120         pconf->force_lowercase_filenames = (0 != cpv->v.u);
121         break;
122       case 13:/* server.follow-symlink */
123         pconf->follow_symlink = (0 != cpv->v.u);
124         break;
125       case 14:/* server.protocol-http11 */
126         pconf->allow_http11 = (0 != cpv->v.u);
127         break;
128       case 15:/* server.range-requests */
129         pconf->range_requests = (0 != cpv->v.u);
130         break;
131       case 16:/* server.stream-request-body */
132         pconf->stream_request_body = cpv->v.shrt;
133         break;
134       case 17:/* server.stream-response-body */
135         pconf->stream_response_body = cpv->v.shrt;
136         break;
137       case 18:/* server.kbytes-per-second */
138         pconf->global_bytes_per_second = (unsigned int)((off_t *)cpv->v.v)[1];
139         pconf->global_bytes_per_second_cnt_ptr = cpv->v.v;
140         break;
141       case 19:/* connection.kbytes-per-second */
142         pconf->bytes_per_second = (unsigned int)cpv->v.shrt << 10;/* (*=1024) */
143         break;
144       case 20:/* mimetype.assign */
145         pconf->mimetypes = cpv->v.a;
146         break;
147       case 21:/* mimetype.use-xattr */
148         pconf->use_xattr = (0 != cpv->v.u);
149         break;
150       case 22:/* etag.use-inode */
151         cpv->v.u
152           ? (pconf->etag_flags |=  ETAG_USE_INODE)
153           : (pconf->etag_flags &= ~ETAG_USE_INODE);
154         break;
155       case 23:/* etag.use-mtime */
156         cpv->v.u
157           ? (pconf->etag_flags |=  ETAG_USE_MTIME)
158           : (pconf->etag_flags &= ~ETAG_USE_MTIME);
159         break;
160       case 24:/* etag.use-size */
161         cpv->v.u
162           ? (pconf->etag_flags |=  ETAG_USE_SIZE)
163           : (pconf->etag_flags &= ~ETAG_USE_SIZE);
164         break;
165       case 25:/* debug.log-condition-handling */
166         pconf->log_condition_handling = (0 != cpv->v.u);
167         break;
168       case 26:/* debug.log-file-not-found */
169         pconf->log_file_not_found = (0 != cpv->v.u);
170         break;
171       case 27:/* debug.log-request-handling */
172         pconf->log_request_handling = (0 != cpv->v.u);
173         break;
174       case 28:/* debug.log-request-header */
175         pconf->log_request_header = (0 != cpv->v.u);
176         break;
177       case 29:/* debug.log-response-header */
178         pconf->log_response_header = (0 != cpv->v.u);
179         break;
180       case 30:/* debug.log-timeouts */
181         pconf->log_timeouts = (0 != cpv->v.u);
182         break;
183       case 31: /* server.errorlog */
184         if (cpv->vtype == T_CONFIG_LOCAL) pconf->errh = cpv->v.v;
185         break;
186       case 32:/* server.breakagelog */
187         if (cpv->vtype == T_CONFIG_LOCAL) pconf->serrh = cpv->v.v;
188         break;
189       default:/* should not happen */
190         return;
191     }
192 }
193 
194 static void config_merge_config(request_config * const pconf, const config_plugin_value_t *cpv) {
195     do {
196         config_merge_config_cpv(pconf, cpv);
197     } while ((++cpv)->k_id != -1);
198 }
199 
200 void config_patch_config(request_st * const r) {
201     config_data_base * const p = r->con->config_data_base;
202 
203     /* performed by config_reset_config() */
204     /*memcpy(&r->conf, &p->defaults, sizeof(request_config));*/
205 
206     for (int i = 1, used = p->nconfig; i < used; ++i) {
207         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
208             config_merge_config(&r->conf, p->cvlist + p->cvlist[i].v.u2[0]);
209     }
210 }
211 
212 void config_reset_config(request_st * const r) {
213     /* initialize request_config (r->conf) from top-level request_config */
214     config_data_base * const p = r->con->config_data_base;
215     r->server_name = p->defaults.server_name;
216     memcpy(&r->conf, &p->defaults, sizeof(request_config));
217 }
218 
219 static int config_burl_normalize_cond (server *srv) {
220     buffer * const tb = srv->tmp_buf;
221     for (uint32_t i = 0; i < srv->config_context->used; ++i) {
222         data_config * const config =(data_config *)srv->config_context->data[i];
223         if (COMP_HTTP_QUERY_STRING != config->comp) continue;
224         switch(config->cond) {
225         case CONFIG_COND_NE:
226         case CONFIG_COND_EQ:
227             /* (can use this routine as long as it does not perform
228              *  any regex-specific normalization of first arg) */
229             pcre_keyvalue_burl_normalize_key(&config->string, tb);
230             break;
231         case CONFIG_COND_NOMATCH:
232         case CONFIG_COND_MATCH:
233             pcre_keyvalue_burl_normalize_key(&config->string, tb);
234             if (!data_config_pcre_compile(config)) return 0;
235             break;
236         default:
237             break;
238         }
239     }
240 
241     return 1;
242 }
243 
244 #if defined(HAVE_MYSQL) || (defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER))
245 static void config_warn_authn_module (server *srv, const char *module, size_t len) {
246 	for (uint32_t i = 0; i < srv->config_context->used; ++i) {
247 		const data_config *config = (data_config const*)srv->config_context->data[i];
248 		const data_unset *du = array_get_element_klen(config->value, CONST_STR_LEN("auth.backend"));
249 		if (NULL != du && du->type == TYPE_STRING) {
250 			data_string *ds = (data_string *)du;
251 			if (buffer_is_equal_string(&ds->value, module, len)) {
252 				buffer * const tb = srv->tmp_buf;
253 				buffer_copy_string_len(tb, CONST_STR_LEN("mod_authn_"));
254 				buffer_append_string_len(tb, module, len);
255 				array_insert_value(srv->srvconf.modules, CONST_BUF_LEN(tb));
256 				log_error(srv->errh, __FILE__, __LINE__,
257 				  "Warning: please add \"mod_authn_%s\" to server.modules list "
258 				  "in lighttpd.conf.  A future release of lighttpd 1.4.x will "
259 				  "not automatically load mod_authn_%s and lighttpd will fail "
260 				  "to start up since your lighttpd.conf uses "
261 				  "auth.backend = \"%s\".", module, module, module);
262 				return;
263 			}
264 		}
265 	}
266 }
267 #endif
268 
269 #ifdef USE_OPENSSL_CRYPTO
270 static void config_warn_openssl_module (server *srv) {
271 	for (uint32_t i = 0; i < srv->config_context->used; ++i) {
272 		const data_config *config = (data_config const*)srv->config_context->data[i];
273 		for (uint32_t j = 0; j < config->value->used; ++j) {
274 			data_unset *du = config->value->data[j];
275 			if (0 == strncmp(du->key.ptr, "ssl.", sizeof("ssl.")-1)) {
276 				/* mod_openssl should be loaded after mod_extforward */
277 				array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_openssl"));
278 				log_error(srv->errh, __FILE__, __LINE__,
279 				  "Warning: please add \"mod_openssl\" to server.modules list "
280 				  "in lighttpd.conf.  A future release of lighttpd 1.4.x "
281 				  "*will not* automatically load mod_openssl and lighttpd "
282 				  "*will not* use SSL/TLS where your lighttpd.conf contains "
283 				  "ssl.* directives");
284 				return;
285 			}
286 		}
287 	}
288 }
289 #endif
290 
291 static void config_compat_module_load (server *srv) {
292     int prepend_mod_indexfile  = 1;
293     int append_mod_dirlisting  = 1;
294     int append_mod_staticfile  = 1;
295     int append_mod_authn_file  = 1;
296     int append_mod_authn_ldap  = 1;
297     int append_mod_authn_mysql = 1;
298     int append_mod_openssl     = 1;
299     int contains_mod_auth      = 0;
300 
301     for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
302         buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
303 
304         if (buffer_eq_slen(m, CONST_STR_LEN("mod_indexfile")))
305             prepend_mod_indexfile = 0;
306         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_staticfile")))
307             append_mod_staticfile = 0;
308         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_dirlisting")))
309             append_mod_dirlisting = 0;
310         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_gnutls")))
311             append_mod_openssl = 0;
312         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_mbedtls")))
313             append_mod_openssl = 0;
314         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_nss")))
315             append_mod_openssl = 0;
316         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_openssl")))
317             append_mod_openssl = 0;
318         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_file")))
319             append_mod_authn_file = 0;
320         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_ldap")))
321             append_mod_authn_ldap = 0;
322         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_authn_mysql")))
323             append_mod_authn_mysql = 0;
324         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_auth")))
325             contains_mod_auth = 1;
326 
327         if (0 == prepend_mod_indexfile &&
328             0 == append_mod_dirlisting &&
329             0 == append_mod_staticfile &&
330             0 == append_mod_openssl &&
331             0 == append_mod_authn_file &&
332             0 == append_mod_authn_ldap &&
333             0 == append_mod_authn_mysql &&
334             1 == contains_mod_auth) {
335             break;
336         }
337     }
338 
339     /* prepend default modules */
340 
341     if (prepend_mod_indexfile) {
342         /* mod_indexfile has to be loaded before mod_fastcgi and friends */
343         array *modules = array_init(srv->srvconf.modules->used+4);
344         array_insert_value(modules, CONST_STR_LEN("mod_indexfile"));
345 
346         for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
347             data_string *ds = (data_string *)srv->srvconf.modules->data[i];
348             array_insert_value(modules, CONST_BUF_LEN(&ds->value));
349         }
350 
351         array_free(srv->srvconf.modules);
352         srv->srvconf.modules = modules;
353     }
354 
355     /* append default modules */
356 
357     if (append_mod_dirlisting) {
358         array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting"));
359     }
360 
361     if (append_mod_staticfile) {
362         array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile"));
363     }
364 
365     if (append_mod_openssl) {
366       #ifdef USE_OPENSSL_CRYPTO
367         config_warn_openssl_module(srv);
368       #endif
369     }
370 
371     /* mod_auth.c,http_auth.c auth backends were split into separate modules
372      * Automatically load auth backend modules for compatibility with
373      * existing lighttpd 1.4.x configs */
374     if (contains_mod_auth) {
375         if (append_mod_authn_file) {
376             array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_authn_file"));
377         }
378         if (append_mod_authn_ldap) {
379           #if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)
380             config_warn_authn_module(srv, CONST_STR_LEN("ldap"));
381           #endif
382         }
383         if (append_mod_authn_mysql) {
384           #if defined(HAVE_MYSQL)
385             config_warn_authn_module(srv, CONST_STR_LEN("mysql"));
386           #endif
387         }
388     }
389 }
390 
391 static void config_deprecate_module_compress (server *srv) {
392     int mod_compress_idx = -1;
393     int mod_deflate_idx = -1;
394     for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
395         buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
396         if (buffer_eq_slen(m, CONST_STR_LEN("mod_compress")))
397             mod_compress_idx = (int)i;
398         else if (buffer_eq_slen(m, CONST_STR_LEN("mod_deflate")))
399             mod_deflate_idx = (int)i;
400     }
401     if (mod_compress_idx < 0) return;
402 
403     int has_compress_directive = 0;
404     for (uint32_t i = 0; i < srv->config_context->used; ++i) {
405         const data_config *config =
406           (data_config const *)srv->config_context->data[i];
407         for (uint32_t j = 0; j < config->value->used; ++j) {
408             buffer *k = &config->value->data[j]->key;
409             if (0 == strncmp(k->ptr, "compress.", sizeof("compress.")-1)) {
410                 has_compress_directive = 1;
411                 break;
412             }
413         }
414         if (has_compress_directive) {
415             log_error(srv->errh, __FILE__, __LINE__,
416               "Warning: \"mod_compress\" is DEPRECATED and has been replaced "
417               "with \"mod_deflate\".  A future release of lighttpd 1.4.x will "
418               "not contain mod_compress and lighttpd may fail to start up");
419             break;
420         }
421     }
422 
423     if (mod_deflate_idx >= 0 || !has_compress_directive) {
424         /* create new modules value list without mod_compress */
425         array *a = array_init(srv->srvconf.modules->used-1);
426         for (uint32_t i = 0; i < srv->srvconf.modules->used; ++i) {
427             buffer *m = &((data_string *)srv->srvconf.modules->data[i])->value;
428             if (buffer_eq_slen(m, CONST_STR_LEN("mod_compress")))
429                 continue;
430             array_insert_value(a, CONST_BUF_LEN(m));
431         }
432         array_free(srv->srvconf.modules);
433         srv->srvconf.modules = a;
434     }
435     else {
436         /* replace "mod_compress" value with "mod_deflate" value */
437         buffer *m = &((data_string *)srv->srvconf.modules->data[mod_compress_idx])->value;
438         buffer_copy_string_len(m, CONST_STR_LEN("mod_deflate"));
439     }
440 }
441 
442 static int config_http_parseopts (server *srv, const array *a) {
443     unsigned short int opts = srv->srvconf.http_url_normalize;
444     unsigned short int decode_2f = 1;
445     int rc = 1;
446     for (size_t i = 0; i < a->used; ++i) {
447         const data_string * const ds = (const data_string *)a->data[i];
448         const buffer *k = &ds->key;
449         const buffer *v = &ds->value;
450         unsigned short int opt;
451         int val = 0;
452         if (buffer_eq_slen(v, CONST_STR_LEN("enable")))
453             val = 1;
454         else if (buffer_eq_slen(v, CONST_STR_LEN("disable")))
455             val = 0;
456         else {
457             log_error(srv->errh, __FILE__, __LINE__,
458               "unrecognized value for server.http-parseopts: "
459               "%s => %s (expect \"[enable|disable]\")", k->ptr, v->ptr);
460             rc = 0;
461         }
462         if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize")))
463             opt = HTTP_PARSEOPT_URL_NORMALIZE;
464         else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-unreserved")))
465             opt = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED;
466         else if (buffer_eq_slen(k, CONST_STR_LEN("url-normalize-required")))
467             opt = HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED;
468         else if (buffer_eq_slen(k, CONST_STR_LEN("url-ctrls-reject")))
469             opt = HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT;
470         else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-backslash-trans")))
471             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS;
472         else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-decode")))
473             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE;
474         else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-2f-reject")))
475             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT;
476         else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-remove")))
477             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE;
478         else if (buffer_eq_slen(k, CONST_STR_LEN("url-path-dotseg-reject")))
479             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT;
480         else if (buffer_eq_slen(k, CONST_STR_LEN("url-query-20-plus")))
481             opt = HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS;
482         else if (buffer_eq_slen(k, CONST_STR_LEN("header-strict"))) {
483             srv->srvconf.http_header_strict = val;
484             continue;
485         }
486         else if (buffer_eq_slen(k, CONST_STR_LEN("host-strict"))) {
487             srv->srvconf.http_host_strict = val;
488             continue;
489         }
490         else if (buffer_eq_slen(k, CONST_STR_LEN("host-normalize"))) {
491             srv->srvconf.http_host_normalize = val;
492             continue;
493         }
494         else if (buffer_eq_slen(k, CONST_STR_LEN("method-get-body"))) {
495             srv->srvconf.http_method_get_body = val;
496             continue;
497         }
498         else {
499             log_error(srv->errh, __FILE__, __LINE__,
500               "unrecognized key for server.http-parseopts: %s", k->ptr);
501             rc = 0;
502             continue;
503         }
504         if (val)
505             opts |= opt;
506         else {
507             opts &= ~opt;
508             if (opt == HTTP_PARSEOPT_URL_NORMALIZE) {
509                 opts = 0;
510                 break;
511             }
512             if (opt == HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE) {
513                 decode_2f = 0;
514             }
515         }
516     }
517     if (opts != 0) {
518         opts |= HTTP_PARSEOPT_URL_NORMALIZE;
519         if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
520                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT))
521                  == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
522                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) {
523             log_error(srv->errh, __FILE__, __LINE__,
524               "conflicting options in server.http-parseopts:"
525               "url-path-2f-decode, url-path-2f-reject");
526             rc = 0;
527         }
528         if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
529                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT))
530                  == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
531                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) {
532             log_error(srv->errh, __FILE__, __LINE__,
533               "conflicting options in server.http-parseopts:"
534               "url-path-dotseg-remove, url-path-dotseg-reject");
535             rc = 0;
536         }
537         if (!(opts & (HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED
538                      |HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED))) {
539             opts |= HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED;
540             if (decode_2f
541                 && !(opts & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT))
542                 opts |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE;
543         }
544     }
545     srv->srvconf.http_url_normalize = opts;
546     return rc;
547 }
548 
549 static int config_insert_srvconf(server *srv) {
550     static const config_plugin_keys_t cpk[] = {
551       { CONST_STR_LEN("server.modules"),
552         T_CONFIG_ARRAY_VLIST,
553         T_CONFIG_SCOPE_SERVER }
554      ,{ CONST_STR_LEN("server.compat-module-load"),
555         T_CONFIG_BOOL,
556         T_CONFIG_SCOPE_SERVER }
557      ,{ CONST_STR_LEN("server.systemd-socket-activation"),
558         T_CONFIG_BOOL,
559         T_CONFIG_SCOPE_SERVER }
560      ,{ CONST_STR_LEN("server.port"),
561         T_CONFIG_SHORT,
562         T_CONFIG_SCOPE_SERVER }
563      ,{ CONST_STR_LEN("server.bind"),
564         T_CONFIG_STRING,
565         T_CONFIG_SCOPE_SERVER }
566      ,{ CONST_STR_LEN("server.network-backend"),
567         T_CONFIG_STRING,
568         T_CONFIG_SCOPE_SERVER }
569      ,{ CONST_STR_LEN("server.chroot"),
570         T_CONFIG_STRING,
571         T_CONFIG_SCOPE_SERVER }
572      ,{ CONST_STR_LEN("server.username"),
573         T_CONFIG_STRING,
574         T_CONFIG_SCOPE_SERVER }
575      ,{ CONST_STR_LEN("server.groupname"),
576         T_CONFIG_STRING,
577         T_CONFIG_SCOPE_SERVER }
578      ,{ CONST_STR_LEN("server.errorlog-placeholder-moved-to-config-insert"),
579         T_CONFIG_STRING,
580         T_CONFIG_SCOPE_SERVER }
581      ,{ CONST_STR_LEN("server.breakagelog-placeholder-moved-to-config-insert"),
582         T_CONFIG_STRING,
583         T_CONFIG_SCOPE_SERVER }
584      ,{ CONST_STR_LEN("server.errorlog-use-syslog"),
585         T_CONFIG_BOOL,
586         T_CONFIG_SCOPE_SERVER }
587      ,{ CONST_STR_LEN("server.syslog-facility"),
588         T_CONFIG_STRING,
589         T_CONFIG_SCOPE_SERVER }
590      ,{ CONST_STR_LEN("server.core-files"),
591         T_CONFIG_BOOL,
592         T_CONFIG_SCOPE_SERVER }
593      ,{ CONST_STR_LEN("server.event-handler"),
594         T_CONFIG_STRING,
595         T_CONFIG_SCOPE_SERVER }
596      ,{ CONST_STR_LEN("server.pid-file"),
597         T_CONFIG_STRING,
598         T_CONFIG_SCOPE_SERVER }
599      ,{ CONST_STR_LEN("server.max-worker"),
600         T_CONFIG_SHORT,
601         T_CONFIG_SCOPE_SERVER }
602      ,{ CONST_STR_LEN("server.max-fds"),
603         T_CONFIG_SHORT,
604         T_CONFIG_SCOPE_SERVER }
605      ,{ CONST_STR_LEN("server.max-connections"),
606         T_CONFIG_SHORT,
607         T_CONFIG_SCOPE_SERVER }
608      ,{ CONST_STR_LEN("server.max-request-field-size"),
609         T_CONFIG_INT,
610         T_CONFIG_SCOPE_SERVER }
611      ,{ CONST_STR_LEN("server.chunkqueue-chunk-sz"),
612         T_CONFIG_INT,
613         T_CONFIG_SCOPE_SERVER }
614      ,{ CONST_STR_LEN("server.upload-temp-file-size"),
615         T_CONFIG_INT,
616         T_CONFIG_SCOPE_SERVER }
617      ,{ CONST_STR_LEN("server.upload-dirs"),
618         T_CONFIG_ARRAY_VLIST,
619         T_CONFIG_SCOPE_SERVER }
620      ,{ CONST_STR_LEN("server.http-parseopts"),
621         T_CONFIG_ARRAY_KVSTRING,
622         T_CONFIG_SCOPE_SERVER }
623      ,{ CONST_STR_LEN("server.http-parseopt-header-strict"),
624         T_CONFIG_BOOL,
625         T_CONFIG_SCOPE_SERVER }
626      ,{ CONST_STR_LEN("server.http-parseopt-host-strict"),
627         T_CONFIG_BOOL,
628         T_CONFIG_SCOPE_SERVER }
629      ,{ CONST_STR_LEN("server.http-parseopt-host-normalize"),
630         T_CONFIG_BOOL,
631         T_CONFIG_SCOPE_SERVER }
632      ,{ CONST_STR_LEN("server.reject-expect-100-with-417"), /*(ignored)*/
633         T_CONFIG_BOOL,
634         T_CONFIG_SCOPE_SERVER }
635      ,{ CONST_STR_LEN("server.stat-cache-engine"),
636         T_CONFIG_STRING,
637         T_CONFIG_SCOPE_SERVER }
638      ,{ CONST_STR_LEN("mimetype.xattr-name"),
639         T_CONFIG_STRING,
640         T_CONFIG_SCOPE_SERVER }
641      ,{ CONST_STR_LEN("ssl.engine"),
642         T_CONFIG_BOOL,
643         T_CONFIG_SCOPE_CONNECTION }
644      ,{ CONST_STR_LEN("debug.log-request-header-on-error"),
645         T_CONFIG_BOOL,
646         T_CONFIG_SCOPE_SERVER }
647      ,{ CONST_STR_LEN("debug.log-state-handling"),
648         T_CONFIG_BOOL,
649         T_CONFIG_SCOPE_SERVER }
650      ,{ CONST_STR_LEN("server.feature-flags"),
651         T_CONFIG_ARRAY_KVANY,
652         T_CONFIG_SCOPE_SERVER }
653      ,{ NULL, 0,
654         T_CONFIG_UNSET,
655         T_CONFIG_SCOPE_UNSET }
656     };
657 
658     int rc = 0;
659     plugin_data_base srvplug;
660     memset(&srvplug, 0, sizeof(srvplug));
661     plugin_data_base * const p = &srvplug;
662     if (!config_plugin_values_init(srv, p, cpk, "global"))
663         return HANDLER_ERROR;
664 
665     int ssl_enabled = 0; /*(directive checked here only to set default port)*/
666 
667     /* process and validate T_CONFIG_SCOPE_SERVER config directives */
668     if (p->cvlist[0].v.u2[1]) {
669         config_plugin_value_t *cpv = p->cvlist + p->cvlist[0].v.u2[0];
670         for (; -1 != cpv->k_id; ++cpv) {
671             switch (cpv->k_id) {
672               case 0: /* server.modules */
673                 array_copy_array(srv->srvconf.modules, cpv->v.a);
674                 break;
675               case 1: /* server.compat-module-load */
676                 srv->srvconf.compat_module_load = (unsigned short)cpv->v.u;
677                 break;
678               case 2: /* server.systemd-socket-activation */
679                 srv->srvconf.systemd_socket_activation=(unsigned short)cpv->v.u;
680                 break;
681               case 3: /* server.port */
682                 srv->srvconf.port = cpv->v.shrt;
683                 break;
684               case 4: /* server.bind */
685                 srv->srvconf.bindhost = cpv->v.b;
686                 break;
687               case 5: /* server.network-backend */
688                 srv->srvconf.network_backend = cpv->v.b;
689                 break;
690               case 6: /* server.chroot */
691                 srv->srvconf.changeroot = cpv->v.b;
692                 break;
693               case 7: /* server.username */
694                 srv->srvconf.username = cpv->v.b;
695                 break;
696               case 8: /* server.groupname */
697                 srv->srvconf.groupname = cpv->v.b;
698                 break;
699               case 9: /* server.errorlog */    /* moved to config_insert() */
700                 /*srv->srvconf.errorlog_file = cpv->v.b;*/
701                 break;
702               case 10:/* server.breakagelog */ /* moved to config_insert() */
703                 /*srv->srvconf.breakagelog_file = cpv->v.b;*/
704                 break;
705               case 11:/* server.errorlog-use-syslog */
706                 srv->srvconf.errorlog_use_syslog = (unsigned short)cpv->v.u;
707                 break;
708               case 12:/* server.syslog-facility */
709                 srv->srvconf.syslog_facility = cpv->v.b;
710                 break;
711               case 13:/* server.core-files */
712                 srv->srvconf.enable_cores = (unsigned short)cpv->v.u;
713                 break;
714               case 14:/* server.event-handler */
715                 srv->srvconf.event_handler = cpv->v.b->ptr;
716                 break;
717               case 15:/* server.pid-file */
718                 *(const buffer **)&srv->srvconf.pid_file = cpv->v.b;
719                 break;
720               case 16:/* server.max-worker */
721                 srv->srvconf.max_worker = (unsigned short)cpv->v.u;
722                 break;
723               case 17:/* server.max-fds */
724                 srv->srvconf.max_fds = (unsigned short)cpv->v.u;
725                 break;
726               case 18:/* server.max-connections */
727                 srv->srvconf.max_conns = (unsigned short)cpv->v.u;
728                 break;
729               case 19:/* server.max-request-field-size */
730                 srv->srvconf.max_request_field_size = cpv->v.u;
731                 break;
732               case 20:/* server.chunkqueue-chunk-sz */
733                 chunkqueue_set_chunk_size(cpv->v.u);
734                 break;
735               case 21:/* server.upload-temp-file-size */
736                 srv->srvconf.upload_temp_file_size = cpv->v.u;
737                 break;
738               case 22:/* server.upload-dirs */
739                 array_copy_array(srv->srvconf.upload_tempdirs, cpv->v.a);
740                 break;
741               case 23:/* server.http-parseopts */
742                 if (!config_http_parseopts(srv, cpv->v.a))
743                     rc = HANDLER_ERROR;
744                 break;
745               case 24:/* server.http-parseopt-header-strict */
746                 srv->srvconf.http_header_strict = (0 != cpv->v.u);
747                 break;
748               case 25:/* server.http-parseopt-host-strict */
749                 srv->srvconf.http_host_strict = (0 != cpv->v.u);
750                 break;
751               case 26:/* server.http-parseopt-host-normalize */
752                 srv->srvconf.http_host_normalize = (0 != cpv->v.u);
753                 break;
754               case 27:/* server.reject-expect-100-with-417 *//*(ignored)*/
755                 break;
756               case 28:/* server.stat-cache-engine */
757                 if (0 != stat_cache_choose_engine(cpv->v.b, srv->errh))
758                     rc = HANDLER_ERROR;
759                 break;
760               case 29:/* mimetype.xattr-name */
761                 stat_cache_xattrname(cpv->v.b->ptr);
762                 break;
763               case 30:/* ssl.engine */
764                 ssl_enabled = (0 != cpv->v.u);
765                #if !defined(USE_OPENSSL_CRYPTO) \
766                 && !defined(USE_MBEDTLS_CRYPTO) \
767                 && !defined(USE_NSS_CRYPTO) \
768                 && !defined(USE_GNUTLS_CRYPTO) \
769                 && !defined(USE_WOLFSSL_CRYPTO)
770                 if (ssl_enabled) {
771                     log_error(srv->errh, __FILE__, __LINE__,
772                       "ssl support is missing; "
773                       "recompile with e.g. --with-openssl");
774                     rc = HANDLER_ERROR;
775                     break;
776                 }
777                #endif
778                 break;
779               case 31:/* debug.log-request-header-on-error */
780                 srv->srvconf.log_request_header_on_error = (0 != cpv->v.u);
781                 break;
782               case 32:/* debug.log-state-handling */
783                 srv->srvconf.log_state_handling = (0 != cpv->v.u);
784                 break;
785               case 33:/* server.feature-flags */
786                 srv->srvconf.feature_flags = cpv->v.a;
787                 srv->srvconf.h2proto =
788                   config_plugin_value_tobool(
789                     array_get_element_klen(cpv->v.a,
790                                            CONST_STR_LEN("server.h2proto")), 0);
791                 if (srv->srvconf.h2proto)
792                     srv->srvconf.h2proto +=
793                       config_plugin_value_tobool(
794                         array_get_element_klen(cpv->v.a,
795                                                CONST_STR_LEN("server.h2c")), 0);
796                 srv->srvconf.absolute_dir_redirect =
797                   config_plugin_value_tobool(
798                     array_get_element_klen(cpv->v.a,
799                       CONST_STR_LEN("server.absolute-dir-redirect")), 0);
800                 break;
801               default:/* should not happen */
802                 break;
803             }
804         }
805     }
806 
807     if (0 == srv->srvconf.port)
808         srv->srvconf.port = ssl_enabled ? 443 : 80;
809 
810     if (srv->srvconf.compat_module_load)
811         config_compat_module_load(srv);
812 
813     config_deprecate_module_compress(srv);
814 
815     if (srv->srvconf.http_url_normalize && !config_burl_normalize_cond(srv))
816         rc = HANDLER_ERROR;
817 
818     free(srvplug.cvlist);
819     return rc;
820 }
821 
822 static int config_insert(server *srv) {
823     static const config_plugin_keys_t cpk[] = {
824       { CONST_STR_LEN("server.document-root"),
825         T_CONFIG_STRING,
826         T_CONFIG_SCOPE_CONNECTION }
827      ,{ CONST_STR_LEN("server.name"),
828         T_CONFIG_STRING,
829         T_CONFIG_SCOPE_CONNECTION }
830      ,{ CONST_STR_LEN("server.tag"),
831         T_CONFIG_STRING,
832         T_CONFIG_SCOPE_CONNECTION }
833      ,{ CONST_STR_LEN("server.max-request-size"),
834         T_CONFIG_INT,
835         T_CONFIG_SCOPE_CONNECTION }
836      ,{ CONST_STR_LEN("server.max-keep-alive-requests"),
837         T_CONFIG_SHORT,
838         T_CONFIG_SCOPE_CONNECTION }
839      ,{ CONST_STR_LEN("server.max-keep-alive-idle"),
840         T_CONFIG_SHORT,
841         T_CONFIG_SCOPE_CONNECTION }
842      ,{ CONST_STR_LEN("server.max-read-idle"),
843         T_CONFIG_SHORT,
844         T_CONFIG_SCOPE_CONNECTION }
845      ,{ CONST_STR_LEN("server.max-write-idle"),
846         T_CONFIG_SHORT,
847         T_CONFIG_SCOPE_CONNECTION }
848      ,{ CONST_STR_LEN("server.errorfile-prefix"),
849         T_CONFIG_STRING,
850         T_CONFIG_SCOPE_CONNECTION }
851      ,{ CONST_STR_LEN("server.error-handler"),
852         T_CONFIG_STRING,
853         T_CONFIG_SCOPE_CONNECTION }
854      ,{ CONST_STR_LEN("server.error-handler-404"),
855         T_CONFIG_STRING,
856         T_CONFIG_SCOPE_CONNECTION }
857      ,{ CONST_STR_LEN("server.error-intercept"),
858         T_CONFIG_BOOL,
859         T_CONFIG_SCOPE_CONNECTION }
860      ,{ CONST_STR_LEN("server.force-lowercase-filenames"),
861         T_CONFIG_BOOL,
862         T_CONFIG_SCOPE_CONNECTION }
863      ,{ CONST_STR_LEN("server.follow-symlink"),
864         T_CONFIG_BOOL,
865         T_CONFIG_SCOPE_CONNECTION }
866      ,{ CONST_STR_LEN("server.protocol-http11"),
867         T_CONFIG_BOOL,
868         T_CONFIG_SCOPE_CONNECTION }
869      ,{ CONST_STR_LEN("server.range-requests"),
870         T_CONFIG_BOOL,
871         T_CONFIG_SCOPE_CONNECTION }
872      ,{ CONST_STR_LEN("server.stream-request-body"),
873         T_CONFIG_SHORT,
874         T_CONFIG_SCOPE_CONNECTION }
875      ,{ CONST_STR_LEN("server.stream-response-body"),
876         T_CONFIG_SHORT,
877         T_CONFIG_SCOPE_CONNECTION }
878      ,{ CONST_STR_LEN("server.kbytes-per-second"),
879         T_CONFIG_SHORT,
880         T_CONFIG_SCOPE_CONNECTION }
881      ,{ CONST_STR_LEN("connection.kbytes-per-second"),
882         T_CONFIG_SHORT,
883         T_CONFIG_SCOPE_CONNECTION }
884      ,{ CONST_STR_LEN("mimetype.assign"),
885         T_CONFIG_ARRAY_KVSTRING,
886         T_CONFIG_SCOPE_CONNECTION }
887      ,{ CONST_STR_LEN("mimetype.use-xattr"),
888         T_CONFIG_BOOL,
889         T_CONFIG_SCOPE_CONNECTION }
890      ,{ CONST_STR_LEN("etag.use-inode"),
891         T_CONFIG_BOOL,
892         T_CONFIG_SCOPE_CONNECTION }
893      ,{ CONST_STR_LEN("etag.use-mtime"),
894         T_CONFIG_BOOL,
895         T_CONFIG_SCOPE_CONNECTION }
896      ,{ CONST_STR_LEN("etag.use-size"),
897         T_CONFIG_BOOL,
898         T_CONFIG_SCOPE_CONNECTION }
899      ,{ CONST_STR_LEN("debug.log-condition-handling"),
900         T_CONFIG_BOOL,
901         T_CONFIG_SCOPE_CONNECTION }
902      ,{ CONST_STR_LEN("debug.log-file-not-found"),
903         T_CONFIG_BOOL,
904         T_CONFIG_SCOPE_CONNECTION }
905      ,{ CONST_STR_LEN("debug.log-request-handling"),
906         T_CONFIG_BOOL,
907         T_CONFIG_SCOPE_CONNECTION }
908      ,{ CONST_STR_LEN("debug.log-request-header"),
909         T_CONFIG_BOOL,
910         T_CONFIG_SCOPE_CONNECTION }
911      ,{ CONST_STR_LEN("debug.log-response-header"),
912         T_CONFIG_BOOL,
913         T_CONFIG_SCOPE_CONNECTION }
914      ,{ CONST_STR_LEN("debug.log-timeouts"),
915         T_CONFIG_BOOL,
916         T_CONFIG_SCOPE_CONNECTION }
917      ,{ CONST_STR_LEN("server.errorlog"),
918         T_CONFIG_STRING,
919         T_CONFIG_SCOPE_CONNECTION }
920      ,{ CONST_STR_LEN("server.breakagelog"),
921         T_CONFIG_STRING,
922         T_CONFIG_SCOPE_CONNECTION }
923      ,{ NULL, 0,
924         T_CONFIG_UNSET,
925         T_CONFIG_SCOPE_UNSET }
926     };
927 
928     int rc = 0;
929     config_data_base * const p = calloc(1, sizeof(config_data_base));
930     force_assert(p);
931     srv->config_data_base = p;
932 
933     if (!config_plugin_values_init(srv, p, cpk, "base"))
934         return HANDLER_ERROR;
935 
936     /* process and validate T_CONFIG_SCOPE_CONNECTION config directives
937      * (init i to 0 if global context; to 1 to skip empty global context) */
938     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
939         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
940         for (; -1 != cpv->k_id; ++cpv) {
941             switch (cpv->k_id) {
942               case 0: /* server.document-root */
943               case 1: /* server.name */
944                 break;
945               case 2: /* server.tag */
946                 if (!buffer_string_is_empty(cpv->v.b)) {
947                     buffer *b;
948                     *(const buffer **)&b = cpv->v.b;
949                     for (char *t=strchr(b->ptr,'\n'); t; t=strchr(t+2,'\n')) {
950                         /* not expecting admin to define multi-line server.tag,
951                          * but ensure server_tag has proper header continuation,
952                          * if needed */
953                         if (t[1] == ' ' || t[1] == '\t') continue;
954                         off_t off = t - b->ptr;
955                         size_t len = buffer_string_length(b);
956                         buffer_string_prepare_append(b, 1);
957                         t = b->ptr+off;
958                         memmove(t+2, t+1, len - off - 1);
959                         t[1] = ' ';
960                         buffer_commit(b, 1);
961                     }
962                     char *t = b->ptr; /*(make empty if tag is whitespace-only)*/
963                     while (*t==' ' || *t=='\t' || *t=='\r' || *t=='\n') ++t;
964                     if (*t == '\0') buffer_string_set_length(b, 0);
965                 }
966                 break;
967               case 3: /* server.max-request-size */
968               case 4: /* server.max-keep-alive-requests */
969               case 5: /* server.max-keep-alive-idle */
970               case 6: /* server.max-read-idle */
971               case 7: /* server.max-write-idle */
972               case 8: /* server.errorfile-prefix */
973               case 9: /* server.error-handler */
974               case 10:/* server.error-handler-404 */
975               case 11:/* server.error-intercept */
976                 break;
977               case 12:/* server.force-lowercase-filenames */
978                #ifndef HAVE_LSTAT
979                 if (0 == cpv->v.u)
980                     log_error(srv->errh, __FILE__, __LINE__,
981                       "Your system lacks lstat(). "
982                       "We can not differ symlinks from files. "
983                       "Please remove server.follow-symlinks from your config.");
984                #endif
985                 break;
986               case 13:/* server.follow-symlink */
987               case 14:/* server.protocol-http11 */
988               case 15:/* server.range-requests */
989                 break;
990               case 16:/* server.stream-request-body */
991                 if (cpv->v.shrt & FDEVENT_STREAM_REQUEST_BUFMIN)
992                     cpv->v.shrt |=FDEVENT_STREAM_REQUEST;
993                 break;
994               case 17:/* server.stream-response-body */
995                 if (cpv->v.shrt & FDEVENT_STREAM_RESPONSE_BUFMIN)
996                     cpv->v.shrt |=FDEVENT_STREAM_RESPONSE;
997                 break;
998               case 18:{/*server.kbytes-per-second */
999                 off_t * const cnt = malloc(2*sizeof(off_t));
1000                 force_assert(cnt);
1001                 cnt[0] = 0;
1002                 cnt[1] = (off_t)cpv->v.shrt << 10;
1003                 cpv->v.v = cnt;
1004                 cpv->vtype = T_CONFIG_LOCAL;
1005                 break;
1006               }
1007               case 19:/* connection.kbytes-per-second */
1008               case 20:/* mimetype.assign */
1009               case 21:/* mimetype.use-xattr */
1010               case 22:/* etag.use-inode */
1011               case 23:/* etag.use-mtime */
1012               case 24:/* etag.use-size */
1013               case 25:/* debug.log-condition-handling */
1014               case 26:/* debug.log-file-not-found */
1015               case 27:/* debug.log-request-handling */
1016               case 28:/* debug.log-request-header */
1017               case 29:/* debug.log-response-header */
1018               case 30:/* debug.log-timeouts */
1019               case 31:/* server.errorlog */   /*(idx in server.c must match)*/
1020               case 32:/* server.breakagelog *//*(idx in server.c must match)*/
1021                 break;
1022               default:/* should not happen */
1023                 break;
1024             }
1025         }
1026     }
1027 
1028     p->defaults.errh = srv->errh;
1029     p->defaults.max_keep_alive_requests = 100;
1030     p->defaults.max_keep_alive_idle = 5;
1031     p->defaults.max_read_idle = 60;
1032     p->defaults.max_write_idle = 360;
1033     p->defaults.follow_symlink = 1;
1034     p->defaults.allow_http11 = 1;
1035     p->defaults.etag_flags = ETAG_USE_INODE | ETAG_USE_MTIME | ETAG_USE_SIZE;
1036     p->defaults.range_requests = 1;
1037     /* use 2 to detect later if value is set by user config in global section */
1038     p->defaults.force_lowercase_filenames = 2;
1039 
1040     /*(global, but store in r->conf.http_parseopts)*/
1041     p->defaults.http_parseopts =
1042         (srv->srvconf.http_header_strict   ?  HTTP_PARSEOPT_HEADER_STRICT   :0)
1043       | (srv->srvconf.http_host_strict     ? (HTTP_PARSEOPT_HOST_STRICT
1044                                              |HTTP_PARSEOPT_HOST_NORMALIZE) :0)
1045       | (srv->srvconf.http_host_normalize  ?  HTTP_PARSEOPT_HOST_NORMALIZE  :0)
1046       | (srv->srvconf.http_method_get_body ?  HTTP_PARSEOPT_METHOD_GET_BODY :0);
1047     p->defaults.http_parseopts |= srv->srvconf.http_url_normalize;
1048     p->defaults.mimetypes = &srv->srvconf.empty_array; /*(must not be NULL)*/
1049     p->defaults.h2proto = srv->srvconf.h2proto;
1050 
1051     /* initialize p->defaults from global config context */
1052     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
1053         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
1054         if (-1 != cpv->k_id)
1055             config_merge_config(&p->defaults, cpv);
1056     }
1057 
1058     /* (after processing config defaults) */
1059     p->defaults.max_request_field_size = srv->srvconf.max_request_field_size;
1060     p->defaults.log_state_handling = srv->srvconf.log_state_handling;
1061     p->defaults.log_request_header_on_error =
1062       srv->srvconf.log_request_header_on_error;
1063     if (p->defaults.log_request_handling || p->defaults.log_request_header)
1064         p->defaults.log_request_header_on_error = 1;
1065 
1066     return rc;
1067 }
1068 
1069 int config_finalize(server *srv, const buffer *default_server_tag) {
1070     /* (call after plugins_call_set_defaults()) */
1071 
1072     config_data_base * const p = srv->config_data_base;
1073 
1074     /* settings might be enabled during plugins_call_set_defaults() */
1075     p->defaults.high_precision_timestamps =
1076       srv->srvconf.high_precision_timestamps;
1077 
1078     /* configure default server_tag if not set
1079      * (if configured to blank, unset server_tag)*/
1080     if (buffer_is_empty(p->defaults.server_tag))
1081         p->defaults.server_tag = default_server_tag;
1082     else if (buffer_string_is_empty(p->defaults.server_tag))
1083         p->defaults.server_tag = NULL;
1084 
1085     /* dump unused config keys */
1086     for (uint32_t i = 0; i < srv->config_context->used; ++i) {
1087         array *config = ((data_config *)srv->config_context->data[i])->value;
1088         for (uint32_t j = 0; config && j < config->used; ++j) {
1089             const buffer * const k = &config->data[j]->key;
1090 
1091             /* all var.* is known as user defined variable */
1092             if (strncmp(k->ptr, "var.", sizeof("var.") - 1) == 0)
1093                 continue;
1094 
1095             if (!array_get_element_klen(srv->srvconf.config_touched,
1096                                         CONST_BUF_LEN(k)))
1097                 log_error(srv->errh, __FILE__, __LINE__,
1098                   "WARNING: unknown config-key: %s (ignored)", k->ptr);
1099         }
1100     }
1101 
1102     array_free(srv->srvconf.config_touched);
1103     srv->srvconf.config_touched = NULL;
1104 
1105     if (srv->srvconf.config_unsupported || srv->srvconf.config_deprecated) {
1106         if (srv->srvconf.config_unsupported)
1107             log_error(srv->errh, __FILE__, __LINE__,
1108               "Configuration contains unsupported keys. Going down.");
1109         if (srv->srvconf.config_deprecated)
1110             log_error(srv->errh, __FILE__, __LINE__,
1111               "Configuration contains deprecated keys. Going down.");
1112         return 0;
1113     }
1114 
1115     return 1;
1116 }
1117 
1118 void config_print(server *srv) {
1119     data_unset *dc = srv->config_context->data[0];
1120     dc->fn->print(dc, 0);
1121 }
1122 
1123 void config_free(server *srv) {
1124     config_free_config(srv->config_data_base);
1125 
1126     array_free(srv->config_context);
1127     array_free(srv->srvconf.config_touched);
1128     array_free(srv->srvconf.modules);
1129     buffer_free(srv->srvconf.modules_dir);
1130     array_free(srv->srvconf.upload_tempdirs);
1131 }
1132 
1133 void config_init(server *srv) {
1134     srv->config_context = array_init(16);
1135     srv->srvconf.config_touched = array_init(128);
1136 
1137     srv->srvconf.port = 0;
1138     srv->srvconf.dont_daemonize = 0;
1139     srv->srvconf.preflight_check = 0;
1140     srv->srvconf.compat_module_load = 1;
1141     srv->srvconf.systemd_socket_activation = 0;
1142 
1143     srv->srvconf.high_precision_timestamps = 0;
1144     srv->srvconf.max_request_field_size = 8192;
1145 
1146     srv->srvconf.http_header_strict  = 1;
1147     srv->srvconf.http_host_strict    = 1; /*(implies http_host_normalize)*/
1148     srv->srvconf.http_host_normalize = 0;
1149     srv->srvconf.http_url_normalize =
1150         HTTP_PARSEOPT_URL_NORMALIZE
1151       | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED
1152       | HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT
1153       | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
1154       | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE;
1155 
1156     srv->srvconf.modules = array_init(16);
1157     srv->srvconf.modules_dir = buffer_init_string(LIBRARY_DIR);
1158     srv->srvconf.upload_tempdirs = array_init(2);
1159 }
1160 
1161 /**
1162  * open the errorlog
1163  *
1164  * we have 4 possibilities:
1165  * - stderr (default)
1166  * - syslog
1167  * - logfile
1168  * - pipe
1169  *
1170  */
1171 
1172 static void config_log_error_open_syslog(server *srv, log_error_st *errh, const buffer *syslog_facility) {
1173   #ifdef HAVE_SYSLOG_H
1174     errh->errorlog_mode = ERRORLOG_SYSLOG;
1175     /* perhaps someone wants to use syslog() */
1176     int facility = -1;
1177     if (!buffer_string_is_empty(syslog_facility)) {
1178         static const struct facility_name_st {
1179           const char *name;
1180           int val;
1181         } facility_names[] = {
1182             { "auth",     LOG_AUTH }
1183           #ifdef LOG_AUTHPRIV
1184            ,{ "authpriv", LOG_AUTHPRIV }
1185           #endif
1186           #ifdef LOG_CRON
1187            ,{ "cron",     LOG_CRON }
1188           #endif
1189            ,{ "daemon",   LOG_DAEMON }
1190           #ifdef LOG_FTP
1191            ,{ "ftp",      LOG_FTP }
1192           #endif
1193           #ifdef LOG_KERN
1194            ,{ "kern",     LOG_KERN }
1195           #endif
1196           #ifdef LOG_LPR
1197            ,{ "lpr",      LOG_LPR }
1198           #endif
1199           #ifdef LOG_MAIL
1200            ,{ "mail",     LOG_MAIL }
1201           #endif
1202           #ifdef LOG_NEWS
1203            ,{ "news",     LOG_NEWS }
1204           #endif
1205            ,{ "security", LOG_AUTH }           /* DEPRECATED */
1206           #ifdef LOG_SYSLOG
1207            ,{ "syslog",   LOG_SYSLOG }
1208           #endif
1209           #ifdef LOG_USER
1210            ,{ "user",     LOG_USER }
1211           #endif
1212           #ifdef LOG_UUCP
1213            ,{ "uucp",     LOG_UUCP }
1214           #endif
1215            ,{ "local0",   LOG_LOCAL0 }
1216            ,{ "local1",   LOG_LOCAL1 }
1217            ,{ "local2",   LOG_LOCAL2 }
1218            ,{ "local3",   LOG_LOCAL3 }
1219            ,{ "local4",   LOG_LOCAL4 }
1220            ,{ "local5",   LOG_LOCAL5 }
1221            ,{ "local6",   LOG_LOCAL6 }
1222            ,{ "local7",   LOG_LOCAL7 }
1223         };
1224         for (unsigned int i = 0; i < sizeof(facility_names)/sizeof(facility_names[0]); ++i) {
1225             const struct facility_name_st *f = facility_names+i;
1226             if (0 == strcmp(syslog_facility->ptr, f->name)) {
1227                 facility = f->val;
1228                 break;
1229             }
1230         }
1231         if (-1 == facility) {
1232             log_error(srv->errh, __FILE__, __LINE__,
1233               "unrecognized server.syslog-facility: \"%s\"; "
1234               "defaulting to \"daemon\" facility",
1235               syslog_facility->ptr);
1236         }
1237     }
1238     openlog("lighttpd", LOG_CONS|LOG_PID, -1==facility ? LOG_DAEMON : facility);
1239   #endif
1240 }
1241 
1242 static int config_log_error_open_fn(server *srv, log_error_st *errh, const char *fn) {
1243     int fd = fdevent_open_logger(fn);
1244     if (-1 == fd) {
1245         log_perror(srv->errh, __FILE__, __LINE__,
1246           "opening errorlog '%s' failed", fn);
1247         return -1;
1248     }
1249     errh->errorlog_fd = fd;
1250     errh->errorlog_mode = fn[0] == '|' ? ERRORLOG_PIPE : ERRORLOG_FILE;
1251     errh->fn = fn;
1252 
1253     return 0;
1254 }
1255 
1256 int config_log_error_open(server *srv) {
1257     /* logs are opened after preflight check (srv->srvconf.preflight_check)
1258      * and after dropping privileges instead of being opened during config
1259      * processing */
1260   #ifdef __clang_analyzer__
1261     force_assert(srv->errh);
1262   #endif
1263 
1264     /* Note: implementation does not de-dup repeated files or pipe commands */
1265 
1266     config_data_base * const p = srv->config_data_base;
1267     log_error_st *serrh = NULL;
1268 
1269     /* future: might be slightly faster to have allocated array of open files
1270      * rather than walking config, but only might matter with many directives */
1271     /* (init i to 0 if global context; to 1 to skip empty global context) */
1272     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
1273         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1274         for (; -1 != cpv->k_id; ++cpv) {
1275             const char *fn = NULL;
1276             log_error_st *errh = NULL;
1277             switch (cpv->k_id) {
1278               /* NB: these indexes are repeated below switch() block
1279                *     and all must stay in sync with configfile.c */
1280               case 31:/* server.errorlog */
1281                 if (0 == i) {
1282                     if (srv->srvconf.errorlog_use_syslog) continue;
1283                     errh = srv->errh;
1284                 }
1285                 __attribute_fallthrough__
1286               case 32:/* server.breakagelog */
1287                 if (!buffer_string_is_empty(cpv->v.b)) fn = cpv->v.b->ptr;
1288                 break;
1289               default:
1290                 break;
1291             }
1292 
1293             if (NULL == fn) continue;
1294 
1295             if (NULL == errh) errh = log_error_st_init();
1296             cpv->v.v = errh;
1297             cpv->vtype = T_CONFIG_LOCAL;
1298 
1299             if (0 != config_log_error_open_fn(srv, errh, fn)) return -1;
1300 
1301             if (0 == i && errh != srv->errh) /*(top-level server.breakagelog)*/
1302                 serrh = errh;
1303         }
1304     }
1305 
1306     if (srv->srvconf.errorlog_use_syslog) /*(restricted to global scope)*/
1307         config_log_error_open_syslog(srv, srv->errh,
1308                                      srv->srvconf.syslog_facility);
1309     else if (srv->errh->errorlog_mode == ERRORLOG_FD
1310              && !srv->srvconf.dont_daemonize)
1311         srv->errh->errorlog_fd = -1;
1312         /* We can only log to stderr in dont-daemonize mode;
1313          * if we do daemonize and no errorlog file is specified,
1314          * we log into /dev/null
1315          */
1316 
1317     /* Note: serrh should not be stored in p->defaults.serrh
1318      * If left as NULL, scripts (e.g. mod_cgi and mod_ssi exec) will inherit
1319      * the current STDERR_FILENO, which already is the top-level breakagelog. */
1320     /*p->defaults.serrh = serrh;*/
1321 
1322     int errfd;
1323     if (NULL != serrh) {
1324         if (srv->errh->errorlog_mode == ERRORLOG_FD) {
1325             srv->errh->errorlog_fd = dup(STDERR_FILENO);
1326             fdevent_setfd_cloexec(srv->errh->errorlog_fd);
1327         }
1328 
1329         errfd = serrh->errorlog_fd;
1330         if (*serrh->fn == '|') fdevent_breakagelog_logger_pipe(errfd);
1331     }
1332     else if (!srv->srvconf.dont_daemonize) {
1333         /* move STDERR_FILENO to /dev/null */
1334         if (-1 == (errfd = fdevent_open_devnull())) {
1335             log_perror(srv->errh,__FILE__,__LINE__,"opening /dev/null failed");
1336             return -1;
1337         }
1338     }
1339     else {
1340         /*(leave STDERR_FILENO as-is)*/
1341         errfd = -1;
1342     }
1343 
1344     if (0 != fdevent_set_stdin_stdout_stderr(-1, -1, errfd)) {
1345         log_perror(srv->errh, __FILE__, __LINE__, "setting stderr failed");
1346       #ifdef FD_CLOEXEC
1347         if (-1 != errfd && NULL == serrh) close(errfd);
1348       #endif
1349         return -1;
1350     }
1351   #ifdef FD_CLOEXEC
1352     if (-1 != errfd && NULL == serrh) close(errfd);
1353   #endif
1354 
1355     if (NULL != serrh) {
1356         close(errfd); /* serrh->errorlog_fd */
1357         serrh->errorlog_fd = STDERR_FILENO;
1358     }
1359 
1360     return 0;
1361 }
1362 
1363 /**
1364  * cycle the errorlog
1365  *
1366  */
1367 
1368 void config_log_error_cycle(server *srv) {
1369     /* All logs are rotated in parent and children workers so that all have
1370      * valid filehandles.  The parent might reap a child and respawn, so the
1371      * parent needs valid handles for more than top-level srv->errh */
1372 
1373     /*(no need to flush error log before cycling; error logs are not buffered)*/
1374 
1375     config_data_base * const p = srv->config_data_base;
1376 
1377     /* future: might be slightly faster to have allocated array of open files
1378      * rather than walking config, but only might matter with many directives */
1379     /* (init i to 0 if global context; to 1 to skip empty global context) */
1380     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
1381         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1382         for (; -1 != cpv->k_id; ++cpv) {
1383             if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
1384             log_error_st *errh;
1385             switch (cpv->k_id) {
1386               case 31:/* server.errorlog */
1387               case 32:/* server.breakagelog */
1388                 errh = cpv->v.v; /* cycle only if the error log is a file */
1389                 if (errh->errorlog_mode != ERRORLOG_FILE) continue;
1390                 if (-1 == fdevent_cycle_logger(errh->fn, &errh->errorlog_fd)) {
1391                     /* write to top-level error log
1392                      * (the prior log if srv->errh is the one being cycled) */
1393                     log_perror(srv->errh, __FILE__, __LINE__,
1394                       "cycling errorlog '%s' failed", errh->fn);
1395                 }
1396                 else if (0 == i && errh != srv->errh) { /*(server.breakagelog)*/
1397                     int fd = errh->errorlog_fd;
1398                     if (STDERR_FILENO != dup2(fd, STDERR_FILENO)) {
1399                         errh->errorlog_fd = STDERR_FILENO;
1400                         close(fd);
1401                     }
1402                     else {
1403                         log_perror(srv->errh, __FILE__, __LINE__,
1404                           "dup2() %s to STDERR failed", errh->fn);
1405                     }
1406                 }
1407                 break;
1408               default:
1409                 break;
1410             }
1411         }
1412     }
1413 }
1414 
1415 void config_log_error_close(server *srv) {
1416     config_data_base * const p = srv->config_data_base;
1417     if (NULL == p) return;
1418 
1419     /* future: might be slightly faster to have allocated array of open files
1420      * rather than walking config, but only might matter with many directives */
1421     /* (init i to 0 if global context; to 1 to skip empty global context) */
1422     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
1423         config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1424         for (; -1 != cpv->k_id; ++cpv) {
1425             if (cpv->vtype != T_CONFIG_LOCAL || NULL == cpv->v.v) continue;
1426             log_error_st *errh = NULL;
1427             switch (cpv->k_id) {
1428               /* NB: these indexes are repeated below switch() block
1429                *     and all must stay in sync with configfile.c */
1430               case 31:/* server.errorlog */
1431                 if (0 == i) continue; /*(srv->errh is free'd later)*/
1432                 __attribute_fallthrough__
1433               case 32:/* server.breakagelog */
1434                 errh = cpv->v.v; /* cycle only if the error log is a file */
1435                 break;
1436               default:
1437                 break;
1438             }
1439 
1440             if (NULL == errh) continue;
1441 
1442             switch(errh->errorlog_mode) {
1443               case ERRORLOG_PIPE:
1444               case ERRORLOG_FILE:
1445               case ERRORLOG_FD:
1446                 if (-1 != errh->errorlog_fd) {
1447                     /* don't close STDERR */
1448                     /* fdevent_close_logger_pipes() closes ERRORLOG_PIPE */
1449                     if (STDERR_FILENO != errh->errorlog_fd
1450                         && ERRORLOG_PIPE != errh->errorlog_mode) {
1451                         close(errh->errorlog_fd);
1452                     }
1453                     errh->errorlog_fd = -1;
1454                 }
1455                 break;
1456               case ERRORLOG_SYSLOG: /*(restricted to global scope)*/
1457                #ifdef HAVE_SYSLOG_H
1458                 closelog();
1459                #endif
1460                 break;
1461             }
1462 
1463             log_error_st_free(errh);
1464         }
1465     }
1466 
1467     fdevent_close_logger_pipes();
1468 
1469     if (srv->errh->errorlog_mode == ERRORLOG_SYSLOG) {
1470         srv->errh->errorlog_mode = ERRORLOG_FD;
1471         srv->errh->errorlog_fd = STDERR_FILENO;
1472       #ifdef HAVE_SYSLOG_H
1473         closelog();
1474       #endif
1475     }
1476 }
1477 
1478 
1479 
1480 typedef struct {
1481 	const char *source;
1482 	const char *input;
1483 	size_t offset;
1484 	size_t size;
1485 
1486 	int line_pos;
1487 	int line;
1488 
1489 	int in_key;
1490 	int in_brace;
1491 	int in_cond;
1492 	int simulate_eol;
1493 } tokenizer_t;
1494 
1495 static int config_skip_newline(tokenizer_t *t) {
1496 	int skipped = 1;
1497 	force_assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n');
1498 	if (t->input[t->offset] == '\r' && t->input[t->offset + 1] == '\n') {
1499 		skipped ++;
1500 		t->offset ++;
1501 	}
1502 	t->offset ++;
1503 	return skipped;
1504 }
1505 
1506 static int config_skip_comment(tokenizer_t *t) {
1507 	int i;
1508 	force_assert(t->input[t->offset] == '#');
1509 	for (i = 1; t->input[t->offset + i] &&
1510 	     (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r');
1511 	     i++);
1512 	t->offset += i;
1513 	return i;
1514 }
1515 
1516 __attribute_cold__
1517 static int config_tokenizer_err(server *srv, const char *file, unsigned int line, tokenizer_t *t, const char *msg) {
1518     log_error(srv->errh, file, line, "source: %s line: %d pos: %d %s",
1519               t->source, t->line, t->line_pos, msg);
1520     return -1;
1521 }
1522 
1523 static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) {
1524 	int tid = 0;
1525 	size_t i;
1526 
1527 	if (t->simulate_eol) {
1528 		t->simulate_eol = 0;
1529 		t->in_key = 1;
1530 		tid = TK_EOL;
1531 		buffer_copy_string_len(token, CONST_STR_LEN("(EOL)"));
1532 	}
1533 
1534 	while (tid == 0 && t->offset < t->size && t->input[t->offset]) {
1535 		char c = t->input[t->offset];
1536 		const char *start = NULL;
1537 
1538 		switch (c) {
1539 		case '=':
1540 			if (t->in_brace) {
1541 				if (t->input[t->offset + 1] == '>') {
1542 					t->offset += 2;
1543 
1544 					buffer_copy_string_len(token, CONST_STR_LEN("=>"));
1545 
1546 					tid = TK_ARRAY_ASSIGN;
1547 				} else {
1548 					return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1549 							"use => for assignments in arrays");
1550 				}
1551 			} else if (t->in_cond) {
1552 				if (t->input[t->offset + 1] == '=') {
1553 					t->offset += 2;
1554 
1555 					buffer_copy_string_len(token, CONST_STR_LEN("=="));
1556 
1557 					tid = TK_EQ;
1558 				} else if (t->input[t->offset + 1] == '~') {
1559 					t->offset += 2;
1560 
1561 					buffer_copy_string_len(token, CONST_STR_LEN("=~"));
1562 
1563 					tid = TK_MATCH;
1564 				} else {
1565 					return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1566 							"only =~ and == are allowed in the condition");
1567 				}
1568 				t->in_key = 1;
1569 				t->in_cond = 0;
1570 			} else if (t->in_key) {
1571 				tid = TK_ASSIGN;
1572 
1573 				buffer_copy_string_len(token, t->input + t->offset, 1);
1574 
1575 				t->offset++;
1576 				t->line_pos++;
1577 			} else {
1578 				return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1579 						"unexpected equal-sign: =");
1580 			}
1581 
1582 			break;
1583 		case '!':
1584 			if (t->in_cond) {
1585 				if (t->input[t->offset + 1] == '=') {
1586 					t->offset += 2;
1587 
1588 					buffer_copy_string_len(token, CONST_STR_LEN("!="));
1589 
1590 					tid = TK_NE;
1591 				} else if (t->input[t->offset + 1] == '~') {
1592 					t->offset += 2;
1593 
1594 					buffer_copy_string_len(token, CONST_STR_LEN("!~"));
1595 
1596 					tid = TK_NOMATCH;
1597 				} else {
1598 					return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1599 							"only !~ and != are allowed in the condition");
1600 				}
1601 				t->in_key = 1;
1602 				t->in_cond = 0;
1603 			} else {
1604 				return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1605 						"unexpected exclamation-marks: !");
1606 			}
1607 
1608 			break;
1609 		case '\t':
1610 		case ' ':
1611 			t->offset++;
1612 			t->line_pos++;
1613 			break;
1614 		case '\n':
1615 		case '\r':
1616 			if (t->in_brace == 0) {
1617 				int done = 0;
1618 				while (!done && t->offset < t->size) {
1619 					switch (t->input[t->offset]) {
1620 					case '\r':
1621 					case '\n':
1622 						config_skip_newline(t);
1623 						t->line_pos = 1;
1624 						t->line++;
1625 						break;
1626 
1627 					case '#':
1628 						t->line_pos += config_skip_comment(t);
1629 						break;
1630 
1631 					case '\t':
1632 					case ' ':
1633 						t->offset++;
1634 						t->line_pos++;
1635 						break;
1636 
1637 					default:
1638 						done = 1;
1639 					}
1640 				}
1641 				t->in_key = 1;
1642 				tid = TK_EOL;
1643 				buffer_copy_string_len(token, CONST_STR_LEN("(EOL)"));
1644 			} else {
1645 				config_skip_newline(t);
1646 				t->line_pos = 1;
1647 				t->line++;
1648 			}
1649 			break;
1650 		case ',':
1651 			if (t->in_brace > 0) {
1652 				tid = TK_COMMA;
1653 
1654 				buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)"));
1655 			}
1656 
1657 			t->offset++;
1658 			t->line_pos++;
1659 			break;
1660 		case '"':
1661 			/* search for the terminating " */
1662 			start = t->input + t->offset + 1;
1663 			buffer_copy_string_len(token, CONST_STR_LEN(""));
1664 
1665 			for (i = 1; t->input[t->offset + i]; i++) {
1666 				if (t->input[t->offset + i] == '\\' &&
1667 				    t->input[t->offset + i + 1] == '"') {
1668 
1669 					buffer_append_string_len(token, start, t->input + t->offset + i - start);
1670 
1671 					start = t->input + t->offset + i + 1;
1672 
1673 					/* skip the " */
1674 					i++;
1675 					continue;
1676 				}
1677 
1678 
1679 				if (t->input[t->offset + i] == '"') {
1680 					tid = TK_STRING;
1681 
1682 					buffer_append_string_len(token, start, t->input + t->offset + i - start);
1683 
1684 					break;
1685 				}
1686 			}
1687 
1688 			if (t->input[t->offset + i] == '\0') {
1689 				return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1690 						"missing closing quote");
1691 			}
1692 
1693 			t->offset += i + 1;
1694 			t->line_pos += i + 1;
1695 
1696 			break;
1697 		case '(':
1698 			t->offset++;
1699 			t->in_brace++;
1700 
1701 			tid = TK_LPARAN;
1702 
1703 			buffer_copy_string_len(token, CONST_STR_LEN("("));
1704 			break;
1705 		case ')':
1706 			t->offset++;
1707 			t->in_brace--;
1708 
1709 			tid = TK_RPARAN;
1710 
1711 			buffer_copy_string_len(token, CONST_STR_LEN(")"));
1712 			break;
1713 		case '$':
1714 			t->offset++;
1715 
1716 			tid = TK_DOLLAR;
1717 			t->in_cond = 1;
1718 			t->in_key = 0;
1719 
1720 			buffer_copy_string_len(token, CONST_STR_LEN("$"));
1721 
1722 			break;
1723 
1724 		case '+':
1725 			if (t->input[t->offset + 1] == '=') {
1726 				t->offset += 2;
1727 				buffer_copy_string_len(token, CONST_STR_LEN("+="));
1728 				tid = TK_APPEND;
1729 			} else {
1730 				t->offset++;
1731 				tid = TK_PLUS;
1732 				buffer_copy_string_len(token, CONST_STR_LEN("+"));
1733 			}
1734 			break;
1735 
1736 		case ':':
1737 			if (t->input[t->offset+1] == '=') {
1738 				t->offset += 2;
1739 				tid = TK_FORCE_ASSIGN;
1740 				buffer_copy_string_len(token, CONST_STR_LEN(":="));
1741 			} else {
1742 				return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1743 						"unexpected character ':'");
1744 			}
1745 			break;
1746 
1747 		case '{':
1748 			t->offset++;
1749 
1750 			tid = TK_LCURLY;
1751 
1752 			buffer_copy_string_len(token, CONST_STR_LEN("{"));
1753 
1754 			break;
1755 
1756 		case '}':
1757 			t->offset++;
1758 
1759 			tid = TK_RCURLY;
1760 
1761 			buffer_copy_string_len(token, CONST_STR_LEN("}"));
1762 
1763 			for (; t->offset < t->size; ++t->offset,++t->line_pos) {
1764 				c = t->input[t->offset];
1765 				if (c == '\r' || c == '\n') {
1766 					break;
1767 				}
1768 				else if (c == '#') {
1769 					t->line_pos += config_skip_comment(t);
1770 					break;
1771 				}
1772 				else if (c != ' ' && c != '\t') {
1773 					t->simulate_eol = 1;
1774 					break;
1775 				} /* else (c == ' ' || c == '\t') */
1776 			}
1777 
1778 			break;
1779 
1780 		case '[':
1781 			t->offset++;
1782 
1783 			tid = TK_LBRACKET;
1784 
1785 			buffer_copy_string_len(token, CONST_STR_LEN("["));
1786 
1787 			break;
1788 
1789 		case ']':
1790 			t->offset++;
1791 
1792 			tid = TK_RBRACKET;
1793 
1794 			buffer_copy_string_len(token, CONST_STR_LEN("]"));
1795 
1796 			break;
1797 		case '#':
1798 			t->line_pos += config_skip_comment(t);
1799 
1800 			break;
1801 		default:
1802 			if (t->in_cond) {
1803 				for (i = 0; t->input[t->offset + i] &&
1804 				     (isalpha((unsigned char)t->input[t->offset + i])
1805 				      || t->input[t->offset + i] == '_'); ++i);
1806 
1807 				if (i && t->input[t->offset + i]) {
1808 					tid = TK_SRVVARNAME;
1809 					buffer_copy_string_len(token, t->input + t->offset, i);
1810 
1811 					t->offset += i;
1812 					t->line_pos += i;
1813 				} else {
1814 					return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1815 							"invalid character in condition");
1816 				}
1817 			} else if (isdigit((unsigned char)c)) {
1818 				/* take all digits */
1819 				for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]);  i++);
1820 
1821 				/* was there it least a digit ? */
1822 				if (i) {
1823 					tid = TK_INTEGER;
1824 
1825 					buffer_copy_string_len(token, t->input + t->offset, i);
1826 
1827 					t->offset += i;
1828 					t->line_pos += i;
1829 				}
1830 			} else {
1831 				/* the key might consist of [-.0-9a-z] */
1832 				for (i = 0; t->input[t->offset + i] &&
1833 				     (isalnum((unsigned char)t->input[t->offset + i]) ||
1834 				      t->input[t->offset + i] == '.' ||
1835 				      t->input[t->offset + i] == '_' || /* for env.* */
1836 				      t->input[t->offset + i] == '-'
1837 				      ); i++);
1838 
1839 				if (i && t->input[t->offset + i]) {
1840 					buffer_copy_string_len(token, t->input + t->offset, i);
1841 
1842 					if (strcmp(token->ptr, "include") == 0) {
1843 						tid = TK_INCLUDE;
1844 					} else if (strcmp(token->ptr, "include_shell") == 0) {
1845 						tid = TK_INCLUDE_SHELL;
1846 					} else if (strcmp(token->ptr, "global") == 0) {
1847 						tid = TK_GLOBAL;
1848 					} else if (strcmp(token->ptr, "else") == 0) {
1849 						tid = TK_ELSE;
1850 					} else {
1851 						tid = TK_LKEY;
1852 					}
1853 
1854 					t->offset += i;
1855 					t->line_pos += i;
1856 				} else {
1857 					return config_tokenizer_err(srv, __FILE__, __LINE__, t,
1858 							"invalid character in variable name");
1859 				}
1860 			}
1861 			break;
1862 		}
1863 	}
1864 
1865 	if (tid) {
1866 		*token_id = tid;
1867 		return 1;
1868 	} else if (t->offset < t->size) {
1869 		log_error(srv->errh, __FILE__, __LINE__, "%d, %s", tid, token->ptr);
1870 	}
1871 	return 0;
1872 }
1873 
1874 static int config_parse(server *srv, config_t *context, const char *source, const char *input, size_t isize) {
1875 	void *pParser;
1876 	buffer *token, *lasttoken;
1877 	int token_id = 0;
1878 	int ret;
1879 	tokenizer_t t;
1880 
1881 	t.source = source;
1882 	t.input = input;
1883 	t.size = isize;
1884 	t.offset = 0;
1885 	t.line = 1;
1886 	t.line_pos = 1;
1887 
1888 	t.in_key = 1;
1889 	t.in_brace = 0;
1890 	t.in_cond = 0;
1891 	t.simulate_eol = 0;
1892 
1893 	pParser = configparserAlloc( malloc );
1894 	force_assert(pParser);
1895 	lasttoken = buffer_init();
1896 	token = buffer_init();
1897 	while((1 == (ret = config_tokenizer(srv, &t, &token_id, token))) && context->ok) {
1898 		buffer_copy_buffer(lasttoken, token);
1899 		configparser(pParser, token_id, token, context);
1900 
1901 		token = buffer_init();
1902 	}
1903 	buffer_free(token);
1904 
1905 	if (ret != -1 && context->ok) {
1906 		/* add an EOL at EOF, better than say sorry */
1907 		configparser(pParser, TK_EOL, buffer_init_string("(EOL)"), context);
1908 		if (context->ok) {
1909 			configparser(pParser, 0, NULL, context);
1910 		}
1911 	}
1912 	configparserFree(pParser, free);
1913 
1914 	if (ret == -1) {
1915 		log_error(srv->errh, __FILE__, __LINE__,
1916 		          "configfile parser failed at: %s", lasttoken->ptr);
1917 	} else if (context->ok == 0) {
1918 		log_error(srv->errh, __FILE__, __LINE__, "source: %s line: %d pos: %d "
1919 		          "parser failed somehow near here: %s",
1920 		          t.source, t.line, t.line_pos, lasttoken->ptr);
1921 		ret = -1;
1922 	}
1923 	buffer_free(lasttoken);
1924 
1925 	return ret == -1 ? -1 : 0;
1926 }
1927 
1928 static int config_parse_file_stream(server *srv, config_t *context, const char *fn) {
1929 	stream s;
1930 
1931 	if (0 != stream_open(&s, fn)) {
1932 		log_perror(srv->errh, __FILE__, __LINE__,
1933 		  "opening configfile %s failed", fn);
1934 		return -1;
1935 	}
1936 
1937 	int ret = config_parse(srv, context, fn, s.start, (size_t)s.size);
1938 	stream_close(&s);
1939 	return ret;
1940 }
1941 
1942 int config_parse_file(server *srv, config_t *context, const char *fn) {
1943 	buffer *filename;
1944 	int ret = -1;
1945       #ifdef GLOB_BRACE
1946 	int flags = GLOB_BRACE;
1947       #else
1948 	int flags = 0;
1949       #endif
1950 	glob_t gl;
1951 
1952 	if ((fn[0] == '/' || fn[0] == '\\') ||
1953 	    (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\')) ||
1954 	    (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\\'))) {
1955 		filename = buffer_init_string(fn);
1956 	} else {
1957 		filename = buffer_init_buffer(context->basedir);
1958 		buffer_append_string(filename, fn);
1959 	}
1960 
1961 	switch (glob(filename->ptr, flags, NULL, &gl)) {
1962 	case 0:
1963 		for (size_t i = 0; i < gl.gl_pathc; ++i) {
1964 			ret = config_parse_file_stream(srv, context, gl.gl_pathv[i]);
1965 			if (0 != ret) break;
1966 		}
1967 		globfree(&gl);
1968 		break;
1969 	case GLOB_NOMATCH:
1970 		if (filename->ptr[strcspn(filename->ptr, "*?[]{}")] != '\0') { /*(contains glob metachars)*/
1971 			ret = 0; /* not an error if no files match glob pattern */
1972 		}
1973 		else {
1974 			log_error(srv->errh, __FILE__, __LINE__, "include file not found: %s", filename->ptr);
1975 		}
1976 		break;
1977 	case GLOB_ABORTED:
1978 	case GLOB_NOSPACE:
1979 		log_perror(srv->errh, __FILE__, __LINE__, "glob() %s failed", filename->ptr);
1980 		break;
1981 	}
1982 
1983 	buffer_free(filename);
1984 	return ret;
1985 }
1986 
1987 #ifdef __CYGWIN__
1988 
1989 static char* getCWD(char *buf, size_t sz) {
1990     if (NULL == getcwd(buf, sz)) {
1991         return NULL;
1992     }
1993     for (size_t i = 0; buf[i]; ++i) {
1994         if (buf[i] == '\\') buf[i] = '/';
1995     }
1996     return buf;
1997 }
1998 
1999 #define getcwd(buf, sz) getCWD((buf),(sz))
2000 
2001 #endif /* __CYGWIN__ */
2002 
2003 int config_parse_cmd(server *srv, config_t *context, const char *cmd) {
2004 	int ret = 0;
2005 	int fds[2];
2006 	char oldpwd[PATH_MAX];
2007 
2008 	if (NULL == getcwd(oldpwd, sizeof(oldpwd))) {
2009 		log_perror(srv->errh, __FILE__, __LINE__, "getcwd()");
2010 		return -1;
2011 	}
2012 
2013 	if (!buffer_string_is_empty(context->basedir)) {
2014 		if (0 != chdir(context->basedir->ptr)) {
2015 			log_perror(srv->errh, __FILE__, __LINE__,
2016 			  "cannot change directory to %s", context->basedir->ptr);
2017 			return -1;
2018 		}
2019 	}
2020 
2021 	if (pipe(fds)) {
2022 		log_perror(srv->errh, __FILE__, __LINE__, "pipe()");
2023 		ret = -1;
2024 	}
2025 	else {
2026 		char *shell = getenv("SHELL");
2027 		char *args[4];
2028 		pid_t pid;
2029 		*(const char **)&args[0] = shell ? shell : "/bin/sh";
2030 		*(const char **)&args[1] = "-c";
2031 		*(const char **)&args[2] = cmd;
2032 		args[3] = NULL;
2033 
2034 		fdevent_setfd_cloexec(fds[0]);
2035 		pid = fdevent_fork_execve(args[0], args, NULL, -1, fds[1], -1, -1);
2036 		if (-1 == pid) {
2037 			log_perror(srv->errh, __FILE__, __LINE__, "fork/exec(%s)", cmd);
2038 			ret = -1;
2039 		}
2040 		else {
2041 			ssize_t rd;
2042 			pid_t wpid;
2043 			int wstatus;
2044 			buffer *out = buffer_init();
2045 			close(fds[1]);
2046 			fds[1] = -1;
2047 			do {
2048 				rd = read(fds[0], buffer_string_prepare_append(out, 1023), 1023);
2049 				if (rd >= 0) buffer_commit(out, (size_t)rd);
2050 			} while (rd > 0 || (-1 == rd && errno == EINTR));
2051 			if (0 != rd) {
2052 				log_perror(srv->errh, __FILE__, __LINE__, "read \"%s\"", cmd);
2053 				ret = -1;
2054 			}
2055 			close(fds[0]);
2056 			fds[0] = -1;
2057 			while (-1 == (wpid = waitpid(pid, &wstatus, 0)) && errno == EINTR) ;
2058 			if (wpid != pid) {
2059 				log_perror(srv->errh, __FILE__, __LINE__, "waitpid \"%s\"",cmd);
2060 				ret = -1;
2061 			}
2062 			if (0 != wstatus) {
2063 				log_error(srv->errh, __FILE__, __LINE__,
2064 				  "command \"%s\" exited non-zero: %d",
2065 				  cmd, WEXITSTATUS(wstatus));
2066 				ret = -1;
2067 			}
2068 
2069 			if (-1 != ret) {
2070 				ret = config_parse(srv, context, cmd, CONST_BUF_LEN(out));
2071 			}
2072 			buffer_free(out);
2073 		}
2074 		if (-1 != fds[0]) close(fds[0]);
2075 		if (-1 != fds[1]) close(fds[1]);
2076 	}
2077 
2078 	if (0 != chdir(oldpwd)) {
2079 		log_perror(srv->errh, __FILE__, __LINE__,
2080 		  "cannot change directory to %s", oldpwd);
2081 		ret = -1;
2082 	}
2083 	return ret;
2084 }
2085 
2086 static void context_init(server *srv, config_t *context) {
2087 	context->srv = srv;
2088 	context->ok = 1;
2089 	vector_config_weak_init(&context->configs_stack);
2090 	context->basedir = buffer_init();
2091 }
2092 
2093 static void context_free(config_t *context) {
2094 	vector_config_weak_clear(&context->configs_stack);
2095 	buffer_free(context->basedir);
2096 }
2097 
2098 int config_read(server *srv, const char *fn) {
2099 	config_t context;
2100 	data_config *dc;
2101 	buffer *dcwd;
2102 	int ret;
2103 	char *pos;
2104 
2105 	context_init(srv, &context);
2106 	context.all_configs = srv->config_context;
2107 
2108 #ifdef __WIN32
2109 	pos = strrchr(fn, '\\');
2110 #else
2111 	pos = strrchr(fn, '/');
2112 #endif
2113 	if (pos) {
2114 		buffer_copy_string_len(context.basedir, fn, pos - fn + 1);
2115 	}
2116 
2117 	dc = data_config_init();
2118 	buffer_copy_string_len(&dc->key, CONST_STR_LEN("global"));
2119 
2120 	force_assert(context.all_configs->used == 0);
2121 	dc->context_ndx = context.all_configs->used;
2122 	array_insert_unique(context.all_configs, (data_unset *)dc);
2123 	context.current = dc;
2124 
2125 	/* default context */
2126 	*array_get_int_ptr(dc->value, CONST_STR_LEN("var.PID")) = getpid();
2127 
2128 	dcwd = srv->tmp_buf;
2129 	buffer_string_prepare_copy(dcwd, PATH_MAX-1);
2130 	if (NULL != getcwd(dcwd->ptr, buffer_string_space(dcwd)+1)) {
2131 		buffer_commit(dcwd, strlen(dcwd->ptr));
2132 		array_set_key_value(dc->value, CONST_STR_LEN("var.CWD"), CONST_BUF_LEN(dcwd));
2133 	}
2134 
2135 	ret = config_parse_file_stream(srv, &context, fn);
2136 
2137 	/* remains nothing if parser is ok */
2138 	force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used));
2139 	context_free(&context);
2140 
2141 	if (0 != ret) {
2142 		return ret;
2143 	}
2144 
2145 	if (0 != config_insert_srvconf(srv)) {
2146 		return -1;
2147 	}
2148 
2149 	if (0 != config_insert(srv)) {
2150 		return -1;
2151 	}
2152 
2153 	return 0;
2154 }
2155 
2156 int config_set_defaults(server *srv) {
2157 	size_t i;
2158 	request_config *s = &((config_data_base *)srv->config_data_base)->defaults;
2159 	struct stat st1, st2;
2160 
2161 	if (fdevent_config(&srv->srvconf.event_handler, srv->errh) <= 0)
2162 		return -1;
2163 
2164 	if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
2165 		if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) {
2166 			log_error(srv->errh, __FILE__, __LINE__,
2167 			  "server.chroot doesn't exist: %s",
2168 			  srv->srvconf.changeroot->ptr);
2169 			return -1;
2170 		}
2171 		if (!S_ISDIR(st1.st_mode)) {
2172 			log_error(srv->errh, __FILE__, __LINE__,
2173 			  "server.chroot isn't a directory: %s",
2174 			  srv->srvconf.changeroot->ptr);
2175 			return -1;
2176 		}
2177 	}
2178 
2179 	if (!srv->srvconf.upload_tempdirs->used) {
2180 		const char *tmpdir = getenv("TMPDIR");
2181 		if (NULL == tmpdir) tmpdir = "/var/tmp";
2182 		array_insert_value(srv->srvconf.upload_tempdirs, tmpdir, strlen(tmpdir));
2183 	}
2184 
2185 	if (srv->srvconf.upload_tempdirs->used) {
2186 		buffer * const b = srv->tmp_buf;
2187 		size_t len;
2188 		buffer_clear(b);
2189 		if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
2190 			buffer_copy_buffer(b, srv->srvconf.changeroot);
2191 			buffer_append_slash(b);
2192 		}
2193 		len = buffer_string_length(b);
2194 
2195 		for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) {
2196 			const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i];
2197 			buffer_string_set_length(b, len); /*(truncate)*/
2198 			buffer_append_string_buffer(b, &ds->value);
2199 			if (-1 == stat(b->ptr, &st1)) {
2200 				log_error(srv->errh, __FILE__, __LINE__,
2201 				  "server.upload-dirs doesn't exist: %s", b->ptr);
2202 			} else if (!S_ISDIR(st1.st_mode)) {
2203 				log_error(srv->errh, __FILE__, __LINE__,
2204 				  "server.upload-dirs isn't a directory: %s", b->ptr);
2205 			}
2206 		}
2207 	}
2208 
2209 	chunkqueue_set_tempdirs_default(
2210 		srv->srvconf.upload_tempdirs,
2211 		srv->srvconf.upload_temp_file_size);
2212 
2213 	if (buffer_string_is_empty(s->document_root)) {
2214 		log_error(srv->errh, __FILE__, __LINE__, "document-root is not set");
2215 		return -1;
2216 	}
2217 
2218 	if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */
2219 		s->force_lowercase_filenames = 0; /* default to 0 */
2220 
2221 		buffer * const tb = srv->tmp_buf;
2222 		buffer_copy_buffer(tb, s->document_root);
2223 
2224 		buffer_to_lower(tb);
2225 
2226 		if (0 == stat(tb->ptr, &st1)) {
2227 			int is_lower = 0;
2228 
2229 			is_lower = buffer_is_equal(tb, s->document_root);
2230 
2231 			/* lower-case existed, check upper-case */
2232 			buffer_copy_buffer(tb, s->document_root);
2233 
2234 			buffer_to_upper(tb);
2235 
2236 			/* we have to handle the special case that upper and lower-casing results in the same filename
2237 			 * as in server.document-root = "/" or "/12345/" */
2238 
2239 			if (is_lower && buffer_is_equal(tb, s->document_root)) {
2240 				/* lower-casing and upper-casing didn't result in
2241 				 * an other filename, no need to stat(),
2242 				 * just assume it is case-sensitive. */
2243 
2244 				s->force_lowercase_filenames = 0;
2245 			} else if (0 == stat(tb->ptr, &st2)) {
2246 
2247 				/* upper case exists too, doesn't the FS handle this ? */
2248 
2249 				/* upper and lower have the same inode -> case-insensitve FS */
2250 
2251 				if (st1.st_ino == st2.st_ino) {
2252 					/* upper and lower have the same inode -> case-insensitve FS */
2253 
2254 					s->force_lowercase_filenames = 1;
2255 				}
2256 			}
2257 		}
2258 	}
2259 
2260 	return 0;
2261 }
2262