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