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