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