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