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