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