xref: /lighttpd1.4/src/configfile.c (revision 49e9f0ac)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "burl.h"
5 #include "fdevent.h"
6 #include "keyvalue.h"
7 #include "log.h"
8 #include "stream.h"
9 
10 #include "configparser.h"
11 #include "configfile.h"
12 #include "stat_cache.h"
13 #include "sys-crypto.h"
14 
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <limits.h>
25 #include <glob.h>
26 
27 #ifndef PATH_MAX
28 #define PATH_MAX 4096
29 #endif
30 
31 #if defined(HAVE_MYSQL) || (defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER))
32 static void config_warn_authn_module (server *srv, const char *module, size_t len) {
33 	for (size_t i = 0; i < srv->config_context->used; ++i) {
34 		const data_config *config = (data_config const*)srv->config_context->data[i];
35 		const data_unset *du = array_get_element(config->value, "auth.backend");
36 		if (NULL != du && du->type == TYPE_STRING) {
37 			data_string *ds = (data_string *)du;
38 			if (buffer_is_equal_string(ds->value, module, len)) {
39 				buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("mod_authn_"));
40 				buffer_append_string_len(srv->tmp_buf, module, len);
41 				array_insert_value(srv->srvconf.modules, CONST_BUF_LEN(srv->tmp_buf));
42 				log_error_write(srv, __FILE__, __LINE__, "SSSsSSS", "Warning: please add \"mod_authn_", module, "\" to server.modules list in lighttpd.conf.  A future release of lighttpd 1.4.x will not automatically load mod_authn_", module, "and lighttpd will fail to start up since your lighttpd.conf uses auth.backend = \"", module, "\".");
43 				return;
44 			}
45 		}
46 	}
47 }
48 #endif
49 
50 #ifdef USE_OPENSSL_CRYPTO
51 static void config_warn_openssl_module (server *srv) {
52 	for (size_t i = 0; i < srv->config_context->used; ++i) {
53 		const data_config *config = (data_config const*)srv->config_context->data[i];
54 		for (size_t j = 0; j < config->value->used; ++j) {
55 			data_unset *du = config->value->data[j];
56 			if (0 == strncmp(du->key->ptr, "ssl.", sizeof("ssl.")-1)) {
57 				/* mod_openssl should be loaded after mod_extforward */
58 				array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_openssl"));
59 				log_error_write(srv, __FILE__, __LINE__, "S", "Warning: please add \"mod_openssl\" to server.modules list in lighttpd.conf.  A future release of lighttpd 1.4.x *will not* automatically load mod_openssl and lighttpd *will not* use SSL/TLS where your lighttpd.conf contains ssl.* directives");
60 				return;
61 			}
62 		}
63 	}
64 }
65 #endif
66 
67 static int config_http_parseopts (server *srv, array *a) {
68     unsigned short int opts = srv->srvconf.http_url_normalize;
69     unsigned short int decode_2f = 1;
70     int rc = 1;
71     if (!array_is_kvstring(a)) {
72         log_error_write(srv, __FILE__, __LINE__, "s",
73                         "unexpected value for server.http-parseopts; "
74                         "expected list of \"key\" => \"[enable|disable]\"");
75         return 0;
76     }
77     for (size_t i = 0; i < a->used; ++i) {
78         const data_string * const ds = (data_string *)a->data[i];
79         unsigned short int opt;
80         int val = 0;
81         if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable")))
82             val = 1;
83         else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable")))
84             val = 0;
85         else {
86             log_error_write(srv, __FILE__, __LINE__, "sbsbs",
87                             "unrecognized value for server.http-parseopts:",
88                             ds->key, "=>", ds->value,
89                             "(expect \"[enable|disable]\")");
90             rc = 0;
91         }
92         if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-normalize")))
93             opt = HTTP_PARSEOPT_URL_NORMALIZE;
94         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-normalize-unreserved")))
95             opt = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED;
96         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-normalize-required")))
97             opt = HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED;
98         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-ctrls-reject")))
99             opt = HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT;
100         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-backslash-trans")))
101             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS;
102         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-2f-decode")))
103             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE;
104         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-2f-reject")))
105             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT;
106         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-dotseg-remove")))
107             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE;
108         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-dotseg-reject")))
109             opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT;
110         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-query-20-plus")))
111             opt = HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS;
112         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("header-strict"))) {
113             srv->srvconf.http_header_strict = val;
114             continue;
115         }
116         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("host-strict"))) {
117             srv->srvconf.http_host_strict = val;
118             continue;
119         }
120         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("host-normalize"))) {
121             srv->srvconf.http_host_normalize = val;
122             continue;
123         }
124         else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("method-get-body"))) {
125             srv->srvconf.http_method_get_body = val;
126             continue;
127         }
128         else {
129             log_error_write(srv, __FILE__, __LINE__, "sb",
130                             "unrecognized key for server.http-parseopts:",
131                             ds->key);
132             rc = 0;
133             continue;
134         }
135         if (val)
136             opts |= opt;
137         else {
138             opts &= ~opt;
139             if (opt == HTTP_PARSEOPT_URL_NORMALIZE) {
140                 opts = 0;
141                 break;
142             }
143             if (opt == HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE) {
144                 decode_2f = 0;
145             }
146         }
147     }
148     if (opts != 0) {
149         opts |= HTTP_PARSEOPT_URL_NORMALIZE;
150         if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
151                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT))
152                  == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE
153                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) {
154             log_error_write(srv, __FILE__, __LINE__, "s",
155                             "conflicting options in server.http-parseopts:"
156                             "url-path-2f-decode, url-path-2f-reject");
157             rc = 0;
158         }
159         if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
160                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT))
161                  == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE
162                     |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) {
163             log_error_write(srv, __FILE__, __LINE__, "s",
164                             "conflicting options in server.http-parseopts:"
165                             "url-path-dotseg-remove, url-path-dotseg-reject");
166             rc = 0;
167         }
168         if (!(opts & (HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED
169                      |HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED))) {
170             opts |= HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED;
171             if (decode_2f
172                 && !(opts & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT))
173                 opts |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE;
174         }
175     }
176     srv->srvconf.http_url_normalize = opts;
177     return rc;
178 }
179 
180 static int config_insert(server *srv) {
181 	size_t i;
182 	int ret = 0;
183 	buffer *stat_cache_string;
184 	array *http_parseopts;
185 	unsigned int chunk_sz = 0;
186 
187 	config_values_t cv[] = {
188 		{ "server.bind",                       NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 0 */
189 		{ "server.errorlog",                   NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 1 */
190 		{ "server.errorfile-prefix",           NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 2 */
191 		{ "server.chroot",                     NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 3 */
192 		{ "server.username",                   NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 4 */
193 		{ "server.groupname",                  NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 5 */
194 		{ "server.port",                       NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_SERVER     }, /* 6 */
195 		{ "server.tag",                        NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 7 */
196 		{ "server.use-ipv6",                   NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
197 		{ "server.modules",                    NULL, T_CONFIG_ARRAY,   T_CONFIG_SCOPE_SERVER     }, /* 9 */
198 
199 		{ "server.event-handler",              NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 10 */
200 		{ "server.pid-file",                   NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 11 */
201 		{ "server.max-request-size",           NULL, T_CONFIG_INT,     T_CONFIG_SCOPE_CONNECTION }, /* 12 */
202 		{ "server.max-worker",                 NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_SERVER     }, /* 13 */
203 		{ "server.document-root",              NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 14 */
204 		{ "server.force-lowercase-filenames",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
205 		{ "debug.log-condition-handling",      NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
206 		{ "server.max-keep-alive-requests",    NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 17 */
207 		{ "server.name",                       NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 18 */
208 		{ "server.max-keep-alive-idle",        NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 19 */
209 
210 		{ "server.max-read-idle",              NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 20 */
211 		{ "server.max-write-idle",             NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 21 */
212 		{ "server.error-handler",              NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 22 */
213 		{ "server.max-fds",                    NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_SERVER     }, /* 23 */
214 #ifdef HAVE_LSTAT
215 		{ "server.follow-symlink",             NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 24 */
216 #else
217 		{ "server.follow-symlink",
218 			"Your system lacks lstat(). We can not differ symlinks from files."
219 			"Please remove server.follow-symlinks from your config.",
220 			T_CONFIG_UNSUPPORTED, T_CONFIG_SCOPE_UNSET },
221 #endif
222 		{ "server.kbytes-per-second",          NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 25 */
223 		{ "connection.kbytes-per-second",      NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 26 */
224 		{ "mimetype.use-xattr",                NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 27 */
225 		{ "mimetype.assign",                   NULL, T_CONFIG_ARRAY,   T_CONFIG_SCOPE_CONNECTION }, /* 28 */
226 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 29 */
227 
228 		{ "ssl.engine",                        NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 30 */
229 		{ "debug.log-file-not-found",          NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 31 */
230 		{ "debug.log-request-handling",        NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 32 */
231 		{ "debug.log-response-header",         NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 33 */
232 		{ "debug.log-request-header",          NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 34 */
233 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 35 */
234 		{ "server.protocol-http11",            NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 36 */
235 		{ "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 37 */
236 		{ "debug.log-state-handling",          NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 38 */
237 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 39 */
238 
239 		{ "server.errorlog-use-syslog",        NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 40 */
240 		{ "server.range-requests",             NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 41 */
241 		{ "server.stat-cache-engine",          NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 42 */
242 		{ "server.max-connections",            NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_SERVER     }, /* 43 */
243 		{ "server.network-backend",            NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 44 */
244 		{ "server.upload-dirs",                NULL, T_CONFIG_ARRAY,   T_CONFIG_SCOPE_SERVER     }, /* 45 */
245 		{ "server.core-files",                 NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 46 */
246 		{ "server.compat-module-load",         NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 47 */
247 		{ "server.chunkqueue-chunk-sz",        NULL, T_CONFIG_INT,     T_CONFIG_SCOPE_SERVER     }, /* 48 */
248 		{ "etag.use-inode",                    NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 49 */
249 
250 		{ "etag.use-mtime",                    NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 50 */
251 		{ "etag.use-size",                     NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 51 */
252 		{ "server.reject-expect-100-with-417", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 52 */
253 		{ "debug.log-timeouts",                NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 53 */
254 		{ "server.defer-accept",               NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 54 */
255 		{ "server.breakagelog",                NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 55 */
256 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 56 */
257 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 57 */
258 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 58 */
259 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 59 */
260 
261 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 60 */
262 		{ "server.set-v6only",                 NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 61 */
263 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 62 */
264 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 63 */
265 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 64 */
266 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 65 */
267 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 66 */
268 		{ "unused-slot-moved-to-mod-openssl",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 67 */
269 		{ "server.upload-temp-file-size",      NULL, T_CONFIG_INT,     T_CONFIG_SCOPE_SERVER     }, /* 68 */
270 		{ "mimetype.xattr-name",               NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 69 */
271 		{ "server.listen-backlog",             NULL, T_CONFIG_INT,     T_CONFIG_SCOPE_CONNECTION }, /* 70 */
272 		{ "server.error-handler-404",          NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 71 */
273 		{ "server.http-parseopt-header-strict",NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 72 */
274 		{ "server.http-parseopt-host-strict",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 73 */
275 		{ "server.http-parseopt-host-normalize",NULL,T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 74 */
276 		{ "server.bsd-accept-filter",          NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 75 */
277 		{ "server.stream-request-body",        NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 76 */
278 		{ "server.stream-response-body",       NULL, T_CONFIG_SHORT,   T_CONFIG_SCOPE_CONNECTION }, /* 77 */
279 		{ "server.max-request-field-size",     NULL, T_CONFIG_INT,     T_CONFIG_SCOPE_SERVER     }, /* 78 */
280 		{ "server.error-intercept",            NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 79 */
281 		{ "server.syslog-facility",            NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_SERVER     }, /* 80 */
282 		{ "server.socket-perms",               NULL, T_CONFIG_STRING,  T_CONFIG_SCOPE_CONNECTION }, /* 81 */
283 		{ "server.http-parseopts",             NULL, T_CONFIG_ARRAY,   T_CONFIG_SCOPE_SERVER     }, /* 82 */
284 		{ "server.systemd-socket-activation",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER     }, /* 83 */
285 
286 		{ NULL,                                NULL, T_CONFIG_UNSET,   T_CONFIG_SCOPE_UNSET      }
287 	};
288 
289 	/* all T_CONFIG_SCOPE_SERVER options */
290 	cv[0].destination = srv->srvconf.bindhost;
291 	cv[1].destination = srv->srvconf.errorlog_file;
292 	cv[3].destination = srv->srvconf.changeroot;
293 	cv[4].destination = srv->srvconf.username;
294 	cv[5].destination = srv->srvconf.groupname;
295 	cv[6].destination = &(srv->srvconf.port);
296 	cv[9].destination = srv->srvconf.modules;
297 
298 	cv[10].destination = srv->srvconf.event_handler;
299 	cv[11].destination = srv->srvconf.pid_file;
300 	cv[13].destination = &(srv->srvconf.max_worker);
301 
302 	cv[23].destination = &(srv->srvconf.max_fds);
303 
304 	cv[37].destination = &(srv->srvconf.log_request_header_on_error);
305 	cv[38].destination = &(srv->srvconf.log_state_handling);
306 
307 	cv[40].destination = &(srv->srvconf.errorlog_use_syslog);
308 	stat_cache_string = buffer_init();
309 	cv[42].destination = stat_cache_string;
310 	cv[43].destination = &(srv->srvconf.max_conns);
311 	cv[44].destination = srv->srvconf.network_backend;
312 	cv[45].destination = srv->srvconf.upload_tempdirs;
313 	cv[46].destination = &(srv->srvconf.enable_cores);
314 	cv[47].destination = &(srv->srvconf.compat_module_load);
315 	cv[48].destination = &chunk_sz;
316 
317 	cv[52].destination = &(srv->srvconf.reject_expect_100_with_417);
318 	cv[55].destination = srv->srvconf.breakagelog_file;
319 
320 	cv[68].destination = &(srv->srvconf.upload_temp_file_size);
321 	cv[69].destination = srv->srvconf.xattr_name;
322 	cv[72].destination = &(srv->srvconf.http_header_strict);
323 	cv[73].destination = &(srv->srvconf.http_host_strict);
324 	cv[74].destination = &(srv->srvconf.http_host_normalize);
325 	cv[78].destination = &(srv->srvconf.max_request_field_size);
326 	cv[80].destination = srv->srvconf.syslog_facility;
327 	http_parseopts = array_init();
328 	cv[82].destination = http_parseopts;
329 	cv[83].destination = &(srv->srvconf.systemd_socket_activation);
330 
331 	srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
332 
333 	force_assert(srv->config_storage);
334 	force_assert(srv->config_context->used); /* static analysis hint for ccc
335 -analyzer */
336 
337 	for (i = 0; i < srv->config_context->used; i++) {
338 		data_config * const config = (data_config *)srv->config_context->data[i];
339 		specific_config *s;
340 
341 		s = calloc(1, sizeof(specific_config));
342 		force_assert(s);
343 		s->document_root = buffer_init();
344 		s->mimetypes     = array_init();
345 		s->server_name   = buffer_init();
346 		s->error_handler = buffer_init();
347 		s->error_handler_404 = buffer_init();
348 		s->server_tag    = buffer_init();
349 		s->errorfile_prefix = buffer_init();
350 	      #if defined(__FreeBSD__) || defined(__NetBSD__) \
351 	       || defined(__OpenBSD__) || defined(__DragonFly__)
352 		s->bsd_accept_filter = (i == 0)
353 		  ? buffer_init()
354 		  : buffer_init_buffer(srv->config_storage[0]->bsd_accept_filter);
355 	      #endif
356 		s->socket_perms = (i == 0 || buffer_string_is_empty(srv->config_storage[0]->socket_perms))
357 		  ? buffer_init()
358 		  : buffer_init_buffer(srv->config_storage[0]->socket_perms);
359 		s->max_keep_alive_requests = 100;
360 		s->max_keep_alive_idle = 5;
361 		s->max_read_idle = 60;
362 		s->max_write_idle = 360;
363 		s->max_request_size = 0;
364 		s->use_xattr     = 0;
365 		s->ssl_enabled   = 0;
366 		s->use_ipv6      = (i == 0) ? 0 : srv->config_storage[0]->use_ipv6;
367 		s->set_v6only    = (i == 0) ? 1 : srv->config_storage[0]->set_v6only;
368 		s->defer_accept  = (i == 0) ? 0 : srv->config_storage[0]->defer_accept;
369 		s->follow_symlink = 1;
370 		s->kbytes_per_second = 0;
371 		s->allow_http11  = 1;
372 		s->etag_use_inode = 1;
373 		s->etag_use_mtime = 1;
374 		s->etag_use_size  = 1;
375 		s->range_requests = 1;
376 		s->force_lowercase_filenames = (i == 0) ? 2 : 0; /* we wan't to detect later if user changed this for global section */
377 		s->global_kbytes_per_second = 0;
378 		s->global_bytes_per_second_cnt = 0;
379 		s->global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt;
380 		s->listen_backlog = (0 == i ? 1024 : srv->config_storage[0]->listen_backlog);
381 		s->stream_request_body = 0;
382 		s->stream_response_body = 0;
383 		s->error_intercept = 0;
384 
385 		/* all T_CONFIG_SCOPE_CONNECTION options */
386 		cv[2].destination = s->errorfile_prefix;
387 		cv[7].destination = s->server_tag;
388 		cv[8].destination = &(s->use_ipv6);
389 
390 		cv[12].destination = &(s->max_request_size);
391 		cv[14].destination = s->document_root;
392 		cv[15].destination = &(s->force_lowercase_filenames);
393 		cv[16].destination = &(s->log_condition_handling);
394 		cv[17].destination = &(s->max_keep_alive_requests);
395 		cv[18].destination = s->server_name;
396 		cv[19].destination = &(s->max_keep_alive_idle);
397 
398 		cv[20].destination = &(s->max_read_idle);
399 		cv[21].destination = &(s->max_write_idle);
400 		cv[22].destination = s->error_handler;
401 		cv[24].destination = &(s->follow_symlink);
402 		cv[25].destination = &(s->global_kbytes_per_second);
403 		cv[26].destination = &(s->kbytes_per_second);
404 		cv[27].destination = &(s->use_xattr);
405 		cv[28].destination = s->mimetypes;
406 		/*cv[29].destination = s->unused;*/
407 
408 		cv[30].destination = &(s->ssl_enabled);
409 		cv[31].destination = &(s->log_file_not_found);
410 		cv[32].destination = &(s->log_request_handling);
411 		cv[33].destination = &(s->log_response_header);
412 		cv[34].destination = &(s->log_request_header);
413 		/*cv[35].destination = &(s->unused);*/
414 		cv[36].destination = &(s->allow_http11);
415 		/*cv[39].destination = s->unused;*/
416 
417 		cv[41].destination = &(s->range_requests);
418 		/*cv[47].destination = s->unused;*/
419 		/*cv[48].destination = &(s->unused);*/
420 		cv[49].destination = &(s->etag_use_inode);
421 
422 		cv[50].destination = &(s->etag_use_mtime);
423 		cv[51].destination = &(s->etag_use_size);
424 		cv[53].destination = &(s->log_timeouts);
425 		cv[54].destination = &(s->defer_accept);
426 		/*cv[56].destination = &(s->unused);*/
427 		/*cv[57].destination = &(s->unused);*/
428 		/*cv[58].destination = &(s->unused);*/
429 		/*cv[59].destination = s->unused;*/
430 
431 		/*cv[60].destination = &(s->unused);*/
432 		cv[61].destination = &(s->set_v6only);
433 		/*cv[62].destination = &(s->unused);*/
434 		/*cv[63].destination = s->unused;*/
435 		/*cv[64].destination = s->unused;*/
436 		/*cv[65].destination = &(s->unused);*/
437 		/*cv[66].destination = &(s->unused);*/
438 		/*cv[67].destination = &(s->unused);*/
439 		cv[70].destination = &(s->listen_backlog);
440 		cv[71].destination = s->error_handler_404;
441 	      #if defined(__FreeBSD__) || defined(__NetBSD__) \
442 	       || defined(__OpenBSD__) || defined(__DragonFly__)
443 		cv[75].destination = s->bsd_accept_filter;
444 	      #endif
445 		cv[76].destination = &(s->stream_request_body);
446 		cv[77].destination = &(s->stream_response_body);
447 		cv[79].destination = &(s->error_intercept);
448 		cv[81].destination = s->socket_perms;
449 
450 		srv->config_storage[i] = s;
451 
452 		if (0 != (ret = config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION))) {
453 			break;
454 		}
455 
456 		if (s->stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) {
457 			s->stream_request_body |= FDEVENT_STREAM_REQUEST;
458 		}
459 		if (s->stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) {
460 			s->stream_response_body |= FDEVENT_STREAM_RESPONSE;
461 		}
462 
463 		if (!array_is_kvstring(s->mimetypes)) {
464 			log_error_write(srv, __FILE__, __LINE__, "s",
465 					"unexpected value for mimetype.assign; expected list of \"ext\" => \"mimetype\"");
466 		}
467 
468 		if (!buffer_string_is_empty(s->server_tag)) {
469 			for (char *t = strchr(s->server_tag->ptr,'\n'); NULL != t; t = strchr(t+2,'\n')) {
470 				/* not expecting admin to define multi-line server.tag,
471 				 * but ensure server_tag has proper header continuation,
472 				 * if needed */
473 				off_t off = t - s->server_tag->ptr;
474 				size_t len;
475 				if (t[1] == ' ' || t[1] == '\t') continue;
476 				len = buffer_string_length(s->server_tag);
477 				buffer_string_prepare_append(s->server_tag, 1);
478 				t = s->server_tag->ptr+off;
479 				memmove(t+2, t+1, len - off - 1);
480 				t[1] = ' ';
481 				buffer_commit(s->server_tag, 1);
482 			}
483 		}
484 
485 		if (0 == i) {
486                     if (!config_http_parseopts(srv, http_parseopts)) {
487 			ret = HANDLER_ERROR;
488 			break;
489                     }
490                 }
491 
492 		if (srv->srvconf.http_url_normalize
493 		    && COMP_HTTP_QUERY_STRING == config->comp) {
494 			switch(config->cond) {
495 			case CONFIG_COND_NE:
496 			case CONFIG_COND_EQ:
497 				/* (can use this routine as long as it does not perform
498 				 *  any regex-specific normalization of first arg) */
499 				pcre_keyvalue_burl_normalize_key(config->string, srv->tmp_buf);
500 				break;
501 			case CONFIG_COND_NOMATCH:
502 			case CONFIG_COND_MATCH:
503 				pcre_keyvalue_burl_normalize_key(config->string, srv->tmp_buf);
504 				if (!data_config_pcre_compile(config)) {
505 					ret = HANDLER_ERROR;
506 				}
507 				break;
508 			default:
509 				break;
510 			}
511 			if (HANDLER_ERROR == ret) break;
512 		}
513 
514 	      #ifndef USE_OPENSSL_CRYPTO
515 		if (s->ssl_enabled) {
516 			log_error_write(srv, __FILE__, __LINE__, "s",
517 					"ssl support is missing, recompile with e.g. --with-openssl");
518 			ret = HANDLER_ERROR;
519 			break;
520 		}
521 	      #endif
522 	}
523 	array_free(http_parseopts);
524 
525 	{
526 		specific_config *s = srv->config_storage[0];
527 		s->http_parseopts= /*(global, but stored in con->conf.http_parseopts)*/
528 		   (srv->srvconf.http_header_strict  ?(HTTP_PARSEOPT_HEADER_STRICT) :0)
529 		  |(srv->srvconf.http_host_strict    ?(HTTP_PARSEOPT_HOST_STRICT
530 		                                      |HTTP_PARSEOPT_HOST_NORMALIZE):0)
531 		  |(srv->srvconf.http_host_normalize ?(HTTP_PARSEOPT_HOST_NORMALIZE):0)
532 		  |(srv->srvconf.http_method_get_body?(HTTP_PARSEOPT_METHOD_GET_BODY):0);
533 		s->http_parseopts |= srv->srvconf.http_url_normalize;
534 
535 		if (s->log_request_handling || s->log_request_header)
536 			srv->srvconf.log_request_header_on_error = 1;
537 	}
538 
539 	if (0 != chunk_sz) {
540 		chunkqueue_set_chunk_size(chunk_sz);
541 	}
542 
543 	if (0 != stat_cache_choose_engine(srv, stat_cache_string)) {
544 		ret = HANDLER_ERROR;
545 	}
546 	buffer_free(stat_cache_string);
547 
548 	if (!array_is_vlist(srv->srvconf.upload_tempdirs)) {
549 		log_error_write(srv, __FILE__, __LINE__, "s",
550 				"unexpected value for server.upload-dirs; expected list of \"path\" strings");
551 		ret = HANDLER_ERROR;
552 	}
553 
554 	if (!array_is_vlist(srv->srvconf.modules)) {
555 		log_error_write(srv, __FILE__, __LINE__, "s",
556 				"unexpected value for server.modules; expected list of \"mod_xxxxxx\" strings");
557 		ret = HANDLER_ERROR;
558 	} else if (srv->srvconf.compat_module_load) {
559 		data_string *ds;
560 		int prepend_mod_indexfile = 1;
561 		int append_mod_dirlisting = 1;
562 		int append_mod_staticfile = 1;
563 		int append_mod_authn_file = 1;
564 		int append_mod_authn_ldap = 1;
565 		int append_mod_authn_mysql = 1;
566 		int append_mod_openssl = 1;
567 		int contains_mod_auth = 0;
568 
569 		/* prepend default modules */
570 		for (i = 0; i < srv->srvconf.modules->used; i++) {
571 			ds = (data_string *)srv->srvconf.modules->data[i];
572 
573 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_indexfile"))) {
574 				prepend_mod_indexfile = 0;
575 			}
576 
577 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_staticfile"))) {
578 				append_mod_staticfile = 0;
579 			}
580 
581 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_dirlisting"))) {
582 				append_mod_dirlisting = 0;
583 			}
584 
585 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_openssl"))) {
586 				append_mod_openssl = 0;
587 			}
588 
589 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_file"))) {
590 				append_mod_authn_file = 0;
591 			}
592 
593 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_ldap"))) {
594 				append_mod_authn_ldap = 0;
595 			}
596 
597 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_mysql"))) {
598 				append_mod_authn_mysql = 0;
599 			}
600 
601 			if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_auth"))) {
602 				contains_mod_auth = 1;
603 			}
604 
605 			if (0 == prepend_mod_indexfile &&
606 			    0 == append_mod_dirlisting &&
607 			    0 == append_mod_staticfile &&
608 			    0 == append_mod_openssl &&
609 			    0 == append_mod_authn_file &&
610 			    0 == append_mod_authn_ldap &&
611 			    0 == append_mod_authn_mysql &&
612 			    1 == contains_mod_auth) {
613 				break;
614 			}
615 		}
616 
617 		if (prepend_mod_indexfile) {
618 			/* mod_indexfile has to be loaded before mod_fastcgi and friends */
619 			array *modules = array_init();
620 			array_insert_value(modules, CONST_STR_LEN("mod_indexfile"));
621 
622 			for (i = 0; i < srv->srvconf.modules->used; i++) {
623 				ds = (data_string *)srv->srvconf.modules->data[i];
624 				array_insert_value(modules, CONST_BUF_LEN(ds->value));
625 			}
626 
627 			array_free(srv->srvconf.modules);
628 			srv->srvconf.modules = modules;
629 		}
630 
631 		/* append default modules */
632 		if (append_mod_dirlisting) {
633 			array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting"));
634 		}
635 
636 		if (append_mod_staticfile) {
637 			array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile"));
638 		}
639 
640 		if (append_mod_openssl) {
641 		      #ifdef USE_OPENSSL_CRYPTO
642 			config_warn_openssl_module(srv);
643 		      #endif
644 		}
645 
646 		/* mod_auth.c,http_auth.c auth backends were split into separate modules
647 		 * Automatically load auth backend modules for compatibility with
648 		 * existing lighttpd 1.4.x configs */
649 		if (contains_mod_auth) {
650 			if (append_mod_authn_file) {
651 				array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_authn_file"));
652 			}
653 			if (append_mod_authn_ldap) {
654 			      #if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)
655 				config_warn_authn_module(srv, CONST_STR_LEN("ldap"));
656 			      #endif
657 			}
658 			if (append_mod_authn_mysql) {
659 			      #if defined(HAVE_MYSQL)
660 				config_warn_authn_module(srv, CONST_STR_LEN("mysql"));
661 			      #endif
662 			}
663 		}
664 	}
665 
666 	return ret;
667 
668 }
669 
670 
671 #define PATCH(x) con->conf.x = s->x
672 int config_setup_connection(server *srv, connection *con) {
673 	specific_config *s = srv->config_storage[0];
674 
675 	PATCH(http_parseopts);
676 
677 	PATCH(allow_http11);
678 	PATCH(mimetypes);
679 	PATCH(document_root);
680 	PATCH(high_precision_timestamps);
681 	PATCH(max_keep_alive_requests);
682 	PATCH(max_keep_alive_idle);
683 	PATCH(max_read_idle);
684 	PATCH(max_write_idle);
685 	PATCH(max_request_size);
686 	PATCH(use_xattr);
687 	PATCH(error_handler);
688 	PATCH(error_handler_404);
689 	PATCH(error_intercept);
690 	PATCH(errorfile_prefix);
691 	PATCH(follow_symlink);
692 	PATCH(server_tag);
693 	PATCH(kbytes_per_second);
694 	PATCH(global_kbytes_per_second);
695 	PATCH(global_bytes_per_second_cnt);
696 
697 	con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt;
698 	buffer_copy_buffer(con->server_name, s->server_name);
699 
700 	PATCH(log_request_header);
701 	PATCH(log_response_header);
702 	PATCH(log_request_handling);
703 	PATCH(log_condition_handling);
704 	PATCH(log_file_not_found);
705 	PATCH(log_timeouts);
706 
707 	PATCH(range_requests);
708 	PATCH(force_lowercase_filenames);
709 	/*PATCH(listen_backlog);*//*(not necessary; used only at startup)*/
710 	PATCH(stream_request_body);
711 	PATCH(stream_response_body);
712 	PATCH(socket_perms);
713 
714 	PATCH(etag_use_inode);
715 	PATCH(etag_use_mtime);
716 	PATCH(etag_use_size);
717 
718 	return 0;
719 }
720 
721 int config_patch_connection(server *srv, connection *con) {
722 	size_t i, j;
723 
724 	/* skip the first, the global context */
725 	for (i = 1; i < srv->config_context->used; i++) {
726 		data_config *dc = (data_config *)srv->config_context->data[i];
727 		specific_config *s = srv->config_storage[i];
728 
729 		/* condition didn't match */
730 		if (!config_check_cond(srv, con, dc)) continue;
731 
732 		/* merge config */
733 		for (j = 0; j < dc->value->used; j++) {
734 			data_unset *du = dc->value->data[j];
735 
736 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.document-root"))) {
737 				PATCH(document_root);
738 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.range-requests"))) {
739 				PATCH(range_requests);
740 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler"))) {
741 				PATCH(error_handler);
742 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler-404"))) {
743 				PATCH(error_handler_404);
744 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-intercept"))) {
745 				PATCH(error_intercept);
746 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.errorfile-prefix"))) {
747 				PATCH(errorfile_prefix);
748 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.assign"))) {
749 				PATCH(mimetypes);
750 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-keep-alive-requests"))) {
751 				PATCH(max_keep_alive_requests);
752 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-keep-alive-idle"))) {
753 				PATCH(max_keep_alive_idle);
754 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-write-idle"))) {
755 				PATCH(max_write_idle);
756 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-read-idle"))) {
757 				PATCH(max_read_idle);
758 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-request-size"))) {
759 				PATCH(max_request_size);
760 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.use-xattr"))) {
761 				PATCH(use_xattr);
762 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-inode"))) {
763 				PATCH(etag_use_inode);
764 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-mtime"))) {
765 				PATCH(etag_use_mtime);
766 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-size"))) {
767 				PATCH(etag_use_size);
768 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.follow-symlink"))) {
769 				PATCH(follow_symlink);
770 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.name"))) {
771 				buffer_copy_buffer(con->server_name, s->server_name);
772 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.tag"))) {
773 				PATCH(server_tag);
774 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.stream-request-body"))) {
775 				PATCH(stream_request_body);
776 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.stream-response-body"))) {
777 				PATCH(stream_response_body);
778 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("connection.kbytes-per-second"))) {
779 				PATCH(kbytes_per_second);
780 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-request-handling"))) {
781 				PATCH(log_request_handling);
782 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-request-header"))) {
783 				PATCH(log_request_header);
784 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-response-header"))) {
785 				PATCH(log_response_header);
786 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-condition-handling"))) {
787 				PATCH(log_condition_handling);
788 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-file-not-found"))) {
789 				PATCH(log_file_not_found);
790 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-timeouts"))) {
791 				PATCH(log_timeouts);
792 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.protocol-http11"))) {
793 				PATCH(allow_http11);
794 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.force-lowercase-filenames"))) {
795 				PATCH(force_lowercase_filenames);
796 		      #if 0 /*(not necessary; used only at startup)*/
797 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.listen-backlog"))) {
798 				PATCH(listen_backlog);
799 		      #endif
800 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.kbytes-per-second"))) {
801 				PATCH(global_kbytes_per_second);
802 				PATCH(global_bytes_per_second_cnt);
803 				con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt;
804 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.socket-perms"))) {
805 				PATCH(socket_perms);
806 			}
807 		}
808 	}
809 
810 		con->etag_flags = (con->conf.etag_use_mtime ? ETAG_USE_MTIME : 0) |
811 				  (con->conf.etag_use_inode ? ETAG_USE_INODE : 0) |
812 				  (con->conf.etag_use_size  ? ETAG_USE_SIZE  : 0);
813 
814 	return 0;
815 }
816 #undef PATCH
817 
818 typedef struct {
819 	int foo;
820 	int bar;
821 
822 	const buffer *source;
823 	const char *input;
824 	size_t offset;
825 	size_t size;
826 
827 	int line_pos;
828 	int line;
829 
830 	int in_key;
831 	int in_brace;
832 	int in_cond;
833 	int simulate_eol;
834 } tokenizer_t;
835 
836 #if 0
837 static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const char *fn) {
838 	if (buffer_string_is_empty(basedir) ||
839 			(fn[0] == '/' || fn[0] == '\\') ||
840 			(fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) {
841 		t->file = buffer_init_string(fn);
842 	} else {
843 		t->file = buffer_init_buffer(basedir);
844 		buffer_append_string(t->file, fn);
845 	}
846 
847 	if (0 != stream_open(&(t->s), t->file)) {
848 		log_error_write(srv, __FILE__, __LINE__, "sbss",
849 				"opening configfile ", t->file, "failed:", strerror(errno));
850 		buffer_free(t->file);
851 		return -1;
852 	}
853 
854 	t->input = t->s.start;
855 	t->offset = 0;
856 	t->size = t->s.size;
857 	t->line = 1;
858 	t->line_pos = 1;
859 
860 	t->in_key = 1;
861 	t->in_brace = 0;
862 	t->in_cond = 0;
863 	return 0;
864 }
865 
866 static int tokenizer_close(server *srv, tokenizer_t *t) {
867 	UNUSED(srv);
868 
869 	buffer_free(t->file);
870 	return stream_close(&(t->s));
871 }
872 #endif
873 static int config_skip_newline(tokenizer_t *t) {
874 	int skipped = 1;
875 	force_assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n');
876 	if (t->input[t->offset] == '\r' && t->input[t->offset + 1] == '\n') {
877 		skipped ++;
878 		t->offset ++;
879 	}
880 	t->offset ++;
881 	return skipped;
882 }
883 
884 static int config_skip_comment(tokenizer_t *t) {
885 	int i;
886 	force_assert(t->input[t->offset] == '#');
887 	for (i = 1; t->input[t->offset + i] &&
888 	     (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r');
889 	     i++);
890 	t->offset += i;
891 	return i;
892 }
893 
894 static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) {
895 	int tid = 0;
896 	size_t i;
897 
898 	if (t->simulate_eol) {
899 		t->simulate_eol = 0;
900 		t->in_key = 1;
901 		tid = TK_EOL;
902 		buffer_copy_string_len(token, CONST_STR_LEN("(EOL)"));
903 	}
904 
905 	while (tid == 0 && t->offset < t->size && t->input[t->offset]) {
906 		char c = t->input[t->offset];
907 		const char *start = NULL;
908 
909 		switch (c) {
910 		case '=':
911 			if (t->in_brace) {
912 				if (t->input[t->offset + 1] == '>') {
913 					t->offset += 2;
914 
915 					buffer_copy_string_len(token, CONST_STR_LEN("=>"));
916 
917 					tid = TK_ARRAY_ASSIGN;
918 				} else {
919 					log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
920 							"source:", t->source,
921 							"line:", t->line, "pos:", t->line_pos,
922 							"use => for assignments in arrays");
923 					return -1;
924 				}
925 			} else if (t->in_cond) {
926 				if (t->input[t->offset + 1] == '=') {
927 					t->offset += 2;
928 
929 					buffer_copy_string_len(token, CONST_STR_LEN("=="));
930 
931 					tid = TK_EQ;
932 				} else if (t->input[t->offset + 1] == '~') {
933 					t->offset += 2;
934 
935 					buffer_copy_string_len(token, CONST_STR_LEN("=~"));
936 
937 					tid = TK_MATCH;
938 				} else {
939 					log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
940 							"source:", t->source,
941 							"line:", t->line, "pos:", t->line_pos,
942 							"only =~ and == are allowed in the condition");
943 					return -1;
944 				}
945 				t->in_key = 1;
946 				t->in_cond = 0;
947 			} else if (t->in_key) {
948 				tid = TK_ASSIGN;
949 
950 				buffer_copy_string_len(token, t->input + t->offset, 1);
951 
952 				t->offset++;
953 				t->line_pos++;
954 			} else {
955 				log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
956 						"source:", t->source,
957 						"line:", t->line, "pos:", t->line_pos,
958 						"unexpected equal-sign: =");
959 				return -1;
960 			}
961 
962 			break;
963 		case '!':
964 			if (t->in_cond) {
965 				if (t->input[t->offset + 1] == '=') {
966 					t->offset += 2;
967 
968 					buffer_copy_string_len(token, CONST_STR_LEN("!="));
969 
970 					tid = TK_NE;
971 				} else if (t->input[t->offset + 1] == '~') {
972 					t->offset += 2;
973 
974 					buffer_copy_string_len(token, CONST_STR_LEN("!~"));
975 
976 					tid = TK_NOMATCH;
977 				} else {
978 					log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
979 							"source:", t->source,
980 							"line:", t->line, "pos:", t->line_pos,
981 							"only !~ and != are allowed in the condition");
982 					return -1;
983 				}
984 				t->in_key = 1;
985 				t->in_cond = 0;
986 			} else {
987 				log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
988 						"source:", t->source,
989 						"line:", t->line, "pos:", t->line_pos,
990 						"unexpected exclamation-marks: !");
991 				return -1;
992 			}
993 
994 			break;
995 		case '\t':
996 		case ' ':
997 			t->offset++;
998 			t->line_pos++;
999 			break;
1000 		case '\n':
1001 		case '\r':
1002 			if (t->in_brace == 0) {
1003 				int done = 0;
1004 				while (!done && t->offset < t->size) {
1005 					switch (t->input[t->offset]) {
1006 					case '\r':
1007 					case '\n':
1008 						config_skip_newline(t);
1009 						t->line_pos = 1;
1010 						t->line++;
1011 						break;
1012 
1013 					case '#':
1014 						t->line_pos += config_skip_comment(t);
1015 						break;
1016 
1017 					case '\t':
1018 					case ' ':
1019 						t->offset++;
1020 						t->line_pos++;
1021 						break;
1022 
1023 					default:
1024 						done = 1;
1025 					}
1026 				}
1027 				t->in_key = 1;
1028 				tid = TK_EOL;
1029 				buffer_copy_string_len(token, CONST_STR_LEN("(EOL)"));
1030 			} else {
1031 				config_skip_newline(t);
1032 				t->line_pos = 1;
1033 				t->line++;
1034 			}
1035 			break;
1036 		case ',':
1037 			if (t->in_brace > 0) {
1038 				tid = TK_COMMA;
1039 
1040 				buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)"));
1041 			}
1042 
1043 			t->offset++;
1044 			t->line_pos++;
1045 			break;
1046 		case '"':
1047 			/* search for the terminating " */
1048 			start = t->input + t->offset + 1;
1049 			buffer_copy_string_len(token, CONST_STR_LEN(""));
1050 
1051 			for (i = 1; t->input[t->offset + i]; i++) {
1052 				if (t->input[t->offset + i] == '\\' &&
1053 				    t->input[t->offset + i + 1] == '"') {
1054 
1055 					buffer_append_string_len(token, start, t->input + t->offset + i - start);
1056 
1057 					start = t->input + t->offset + i + 1;
1058 
1059 					/* skip the " */
1060 					i++;
1061 					continue;
1062 				}
1063 
1064 
1065 				if (t->input[t->offset + i] == '"') {
1066 					tid = TK_STRING;
1067 
1068 					buffer_append_string_len(token, start, t->input + t->offset + i - start);
1069 
1070 					break;
1071 				}
1072 			}
1073 
1074 			if (t->input[t->offset + i] == '\0') {
1075 				/* ERROR */
1076 
1077 				log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
1078 						"source:", t->source,
1079 						"line:", t->line, "pos:", t->line_pos,
1080 						"missing closing quote");
1081 
1082 				return -1;
1083 			}
1084 
1085 			t->offset += i + 1;
1086 			t->line_pos += i + 1;
1087 
1088 			break;
1089 		case '(':
1090 			t->offset++;
1091 			t->in_brace++;
1092 
1093 			tid = TK_LPARAN;
1094 
1095 			buffer_copy_string_len(token, CONST_STR_LEN("("));
1096 			break;
1097 		case ')':
1098 			t->offset++;
1099 			t->in_brace--;
1100 
1101 			tid = TK_RPARAN;
1102 
1103 			buffer_copy_string_len(token, CONST_STR_LEN(")"));
1104 			break;
1105 		case '$':
1106 			t->offset++;
1107 
1108 			tid = TK_DOLLAR;
1109 			t->in_cond = 1;
1110 			t->in_key = 0;
1111 
1112 			buffer_copy_string_len(token, CONST_STR_LEN("$"));
1113 
1114 			break;
1115 
1116 		case '+':
1117 			if (t->input[t->offset + 1] == '=') {
1118 				t->offset += 2;
1119 				buffer_copy_string_len(token, CONST_STR_LEN("+="));
1120 				tid = TK_APPEND;
1121 			} else {
1122 				t->offset++;
1123 				tid = TK_PLUS;
1124 				buffer_copy_string_len(token, CONST_STR_LEN("+"));
1125 			}
1126 			break;
1127 
1128 		case ':':
1129 			if (t->input[t->offset+1] == '=') {
1130 				t->offset += 2;
1131 				tid = TK_FORCE_ASSIGN;
1132 				buffer_copy_string_len(token, CONST_STR_LEN(":="));
1133 			}
1134 			break;
1135 
1136 		case '{':
1137 			t->offset++;
1138 
1139 			tid = TK_LCURLY;
1140 
1141 			buffer_copy_string_len(token, CONST_STR_LEN("{"));
1142 
1143 			break;
1144 
1145 		case '}':
1146 			t->offset++;
1147 
1148 			tid = TK_RCURLY;
1149 
1150 			buffer_copy_string_len(token, CONST_STR_LEN("}"));
1151 
1152 			for (; t->offset < t->size; ++t->offset,++t->line_pos) {
1153 				c = t->input[t->offset];
1154 				if (c == '\r' || c == '\n') {
1155 					break;
1156 				}
1157 				else if (c == '#') {
1158 					t->line_pos += config_skip_comment(t);
1159 					break;
1160 				}
1161 				else if (c != ' ' && c != '\t') {
1162 					t->simulate_eol = 1;
1163 					break;
1164 				} /* else (c == ' ' || c == '\t') */
1165 			}
1166 
1167 			break;
1168 
1169 		case '[':
1170 			t->offset++;
1171 
1172 			tid = TK_LBRACKET;
1173 
1174 			buffer_copy_string_len(token, CONST_STR_LEN("["));
1175 
1176 			break;
1177 
1178 		case ']':
1179 			t->offset++;
1180 
1181 			tid = TK_RBRACKET;
1182 
1183 			buffer_copy_string_len(token, CONST_STR_LEN("]"));
1184 
1185 			break;
1186 		case '#':
1187 			t->line_pos += config_skip_comment(t);
1188 
1189 			break;
1190 		default:
1191 			if (t->in_cond) {
1192 				for (i = 0; t->input[t->offset + i] &&
1193 				     (isalpha((unsigned char)t->input[t->offset + i])
1194 				      || t->input[t->offset + i] == '_'); ++i);
1195 
1196 				if (i && t->input[t->offset + i]) {
1197 					tid = TK_SRVVARNAME;
1198 					buffer_copy_string_len(token, t->input + t->offset, i);
1199 
1200 					t->offset += i;
1201 					t->line_pos += i;
1202 				} else {
1203 					/* ERROR */
1204 					log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
1205 							"source:", t->source,
1206 							"line:", t->line, "pos:", t->line_pos,
1207 							"invalid character in condition");
1208 					return -1;
1209 				}
1210 			} else if (isdigit((unsigned char)c)) {
1211 				/* take all digits */
1212 				for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]);  i++);
1213 
1214 				/* was there it least a digit ? */
1215 				if (i) {
1216 					tid = TK_INTEGER;
1217 
1218 					buffer_copy_string_len(token, t->input + t->offset, i);
1219 
1220 					t->offset += i;
1221 					t->line_pos += i;
1222 				}
1223 			} else {
1224 				/* the key might consist of [-.0-9a-z] */
1225 				for (i = 0; t->input[t->offset + i] &&
1226 				     (isalnum((unsigned char)t->input[t->offset + i]) ||
1227 				      t->input[t->offset + i] == '.' ||
1228 				      t->input[t->offset + i] == '_' || /* for env.* */
1229 				      t->input[t->offset + i] == '-'
1230 				      ); i++);
1231 
1232 				if (i && t->input[t->offset + i]) {
1233 					buffer_copy_string_len(token, t->input + t->offset, i);
1234 
1235 					if (strcmp(token->ptr, "include") == 0) {
1236 						tid = TK_INCLUDE;
1237 					} else if (strcmp(token->ptr, "include_shell") == 0) {
1238 						tid = TK_INCLUDE_SHELL;
1239 					} else if (strcmp(token->ptr, "global") == 0) {
1240 						tid = TK_GLOBAL;
1241 					} else if (strcmp(token->ptr, "else") == 0) {
1242 						tid = TK_ELSE;
1243 					} else {
1244 						tid = TK_LKEY;
1245 					}
1246 
1247 					t->offset += i;
1248 					t->line_pos += i;
1249 				} else {
1250 					/* ERROR */
1251 					log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
1252 							"source:", t->source,
1253 							"line:", t->line, "pos:", t->line_pos,
1254 							"invalid character in variable name");
1255 					return -1;
1256 				}
1257 			}
1258 			break;
1259 		}
1260 	}
1261 
1262 	if (tid) {
1263 		*token_id = tid;
1264 #if 0
1265 		log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd",
1266 				"source:", t->source,
1267 				"line:", t->line, "pos:", t->line_pos,
1268 				token, token->used - 1, tid);
1269 #endif
1270 
1271 		return 1;
1272 	} else if (t->offset < t->size) {
1273 		log_error_write(srv, __FILE__, __LINE__, "Dsb", tid, ",", token);
1274 	}
1275 	return 0;
1276 }
1277 
1278 static int config_parse(server *srv, config_t *context, tokenizer_t *t) {
1279 	void *pParser;
1280 	int token_id;
1281 	buffer *token, *lasttoken;
1282 	int ret;
1283 
1284 	pParser = configparserAlloc( malloc );
1285 	force_assert(pParser);
1286 	lasttoken = buffer_init();
1287 	token = buffer_init();
1288 	while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) {
1289 		buffer_copy_buffer(lasttoken, token);
1290 		configparser(pParser, token_id, token, context);
1291 
1292 		token = buffer_init();
1293 	}
1294 	buffer_free(token);
1295 
1296 	if (ret != -1 && context->ok) {
1297 		/* add an EOL at EOF, better than say sorry */
1298 		configparser(pParser, TK_EOL, buffer_init_string("(EOL)"), context);
1299 		if (context->ok) {
1300 			configparser(pParser, 0, NULL, context);
1301 		}
1302 	}
1303 	configparserFree(pParser, free);
1304 
1305 	if (ret == -1) {
1306 		log_error_write(srv, __FILE__, __LINE__, "sb",
1307 				"configfile parser failed at:", lasttoken);
1308 	} else if (context->ok == 0) {
1309 		log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb",
1310 				"source:", t->source,
1311 				"line:", t->line, "pos:", t->line_pos,
1312 				"parser failed somehow near here:", lasttoken);
1313 		ret = -1;
1314 	}
1315 	buffer_free(lasttoken);
1316 
1317 	return ret == -1 ? -1 : 0;
1318 }
1319 
1320 static int tokenizer_init(tokenizer_t *t, const buffer *source, const char *input, size_t size) {
1321 
1322 	t->source = source;
1323 	t->input = input;
1324 	t->size = size;
1325 	t->offset = 0;
1326 	t->line = 1;
1327 	t->line_pos = 1;
1328 
1329 	t->in_key = 1;
1330 	t->in_brace = 0;
1331 	t->in_cond = 0;
1332 	t->simulate_eol = 0;
1333 	return 0;
1334 }
1335 
1336 static int config_parse_file_stream(server *srv, config_t *context, const buffer *filename) {
1337 	tokenizer_t t;
1338 	stream s;
1339 	int ret;
1340 
1341 	if (0 != stream_open(&s, filename)) {
1342 		log_error_write(srv, __FILE__, __LINE__, "sbss",
1343 				"opening configfile ", filename, "failed:", strerror(errno));
1344 		return -1;
1345 	} else {
1346 		tokenizer_init(&t, filename, s.start, s.size);
1347 		ret = config_parse(srv, context, &t);
1348 	}
1349 
1350 	stream_close(&s);
1351 	return ret;
1352 }
1353 
1354 int config_parse_file(server *srv, config_t *context, const char *fn) {
1355 	buffer *filename;
1356 	size_t i;
1357 	int ret = -1;
1358       #ifdef GLOB_BRACE
1359 	int flags = GLOB_BRACE;
1360       #else
1361 	int flags = 0;
1362       #endif
1363 	glob_t gl;
1364 
1365 	if ((fn[0] == '/' || fn[0] == '\\') ||
1366 	    (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\')) ||
1367 	    (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\\'))) {
1368 		filename = buffer_init_string(fn);
1369 	} else {
1370 		filename = buffer_init_buffer(context->basedir);
1371 		buffer_append_string(filename, fn);
1372 	}
1373 
1374 	switch (glob(filename->ptr, flags, NULL, &gl)) {
1375 	case 0:
1376 		for (i = 0; i < gl.gl_pathc; ++i) {
1377 			buffer_copy_string(filename, gl.gl_pathv[i]);
1378 			ret = config_parse_file_stream(srv, context, filename);
1379 			if (0 != ret) break;
1380 		}
1381 		globfree(&gl);
1382 		break;
1383 	case GLOB_NOMATCH:
1384 		if (filename->ptr[strcspn(filename->ptr, "*?[]{}")] != '\0') { /*(contains glob metachars)*/
1385 			ret = 0; /* not an error if no files match glob pattern */
1386 		}
1387 		else {
1388 			log_error_write(srv, __FILE__, __LINE__, "sb", "include file not found: ", filename);
1389 		}
1390 		break;
1391 	case GLOB_ABORTED:
1392 	case GLOB_NOSPACE:
1393 		log_error_write(srv, __FILE__, __LINE__, "sbss", "glob()", filename, "failed:", strerror(errno));
1394 		break;
1395 	}
1396 
1397 	buffer_free(filename);
1398 	return ret;
1399 }
1400 
1401 #ifdef __CYGWIN__
1402 
1403 static char* getCWD(char *buf, size_t sz) {
1404     if (NULL == getcwd(buf, sz)) {
1405         return NULL;
1406     }
1407     for (size_t i = 0; buf[i]; ++i) {
1408         if (buf[i] == '\\') buf[i] = '/';
1409     }
1410     return buf;
1411 }
1412 
1413 #define getcwd(buf, sz) getCWD((buf),(sz))
1414 
1415 #endif /* __CYGWIN__ */
1416 
1417 int config_parse_cmd(server *srv, config_t *context, const char *cmd) {
1418 	int ret = 0;
1419 	int fds[2];
1420 	char oldpwd[PATH_MAX];
1421 
1422 	if (NULL == getcwd(oldpwd, sizeof(oldpwd))) {
1423 		log_error_write(srv, __FILE__, __LINE__, "s",
1424 			"cannot get cwd", strerror(errno));
1425 		return -1;
1426 	}
1427 
1428 	if (!buffer_string_is_empty(context->basedir)) {
1429 		if (0 != chdir(context->basedir->ptr)) {
1430 			log_error_write(srv, __FILE__, __LINE__, "sbs",
1431 				"cannot change directory to", context->basedir, strerror(errno));
1432 			return -1;
1433 		}
1434 	}
1435 
1436 	if (pipe(fds)) {
1437 		log_error_write(srv, __FILE__, __LINE__, "ss",
1438 				"pipe failed: ", strerror(errno));
1439 		ret = -1;
1440 	}
1441 	else {
1442 		char *shell = getenv("SHELL");
1443 		char *args[4];
1444 		pid_t pid;
1445 		*(const char **)&args[0] = shell ? shell : "/bin/sh";
1446 		*(const char **)&args[1] = "-c";
1447 		*(const char **)&args[2] = cmd;
1448 		args[3] = NULL;
1449 
1450 		fdevent_setfd_cloexec(fds[0]);
1451 		pid = fdevent_fork_execve(args[0], args, NULL, -1, fds[1], -1, -1);
1452 		if (-1 == pid) {
1453 			log_error_write(srv, __FILE__, __LINE__, "SSss",
1454 					"fork/exec(", cmd, "):", strerror(errno));
1455 			ret = -1;
1456 		}
1457 		else {
1458 			ssize_t rd;
1459 			pid_t wpid;
1460 			int wstatus;
1461 			buffer *out = buffer_init();
1462 			close(fds[1]);
1463 			fds[1] = -1;
1464 			do {
1465 				rd = read(fds[0], buffer_string_prepare_append(out, 1023), 1023);
1466 				if (rd >= 0) buffer_commit(out, (size_t)rd);
1467 			} while (rd > 0 || (-1 == rd && errno == EINTR));
1468 			if (0 != rd) {
1469 				log_error_write(srv, __FILE__, __LINE__, "SSss",
1470 						"read \"", cmd, "\" failed:", strerror(errno));
1471 				ret = -1;
1472 			}
1473 			close(fds[0]);
1474 			fds[0] = -1;
1475 			while (-1 == (wpid = waitpid(pid, &wstatus, 0)) && errno == EINTR) ;
1476 			if (wpid != pid) {
1477 				log_error_write(srv, __FILE__, __LINE__, "SSss",
1478 						"waitpid \"", cmd, "\" failed:", strerror(errno));
1479 				ret = -1;
1480 			}
1481 			if (0 != wstatus) {
1482 				log_error_write(srv, __FILE__, __LINE__, "SSsd",
1483 						"command \"", cmd, "\" exited non-zero:", WEXITSTATUS(wstatus));
1484 				ret = -1;
1485 			}
1486 
1487 			if (-1 != ret) {
1488 				buffer *source = buffer_init_string(cmd);
1489 				tokenizer_t t;
1490 				tokenizer_init(&t, source, CONST_BUF_LEN(out));
1491 				ret = config_parse(srv, context, &t);
1492 				buffer_free(source);
1493 			}
1494 			buffer_free(out);
1495 		}
1496 		if (-1 != fds[0]) close(fds[0]);
1497 		if (-1 != fds[1]) close(fds[1]);
1498 	}
1499 
1500 	if (0 != chdir(oldpwd)) {
1501 		log_error_write(srv, __FILE__, __LINE__, "sss",
1502 			"cannot change directory to", oldpwd, strerror(errno));
1503 		ret = -1;
1504 	}
1505 	return ret;
1506 }
1507 
1508 static void context_init(server *srv, config_t *context) {
1509 	context->srv = srv;
1510 	context->ok = 1;
1511 	vector_config_weak_init(&context->configs_stack);
1512 	context->basedir = buffer_init();
1513 }
1514 
1515 static void context_free(config_t *context) {
1516 	vector_config_weak_clear(&context->configs_stack);
1517 	buffer_free(context->basedir);
1518 }
1519 
1520 int config_read(server *srv, const char *fn) {
1521 	config_t context;
1522 	data_config *dc;
1523 	buffer *dcwd;
1524 	int ret;
1525 	char *pos;
1526 	buffer *filename;
1527 
1528 	context_init(srv, &context);
1529 	context.all_configs = srv->config_context;
1530 
1531 #ifdef __WIN32
1532 	pos = strrchr(fn, '\\');
1533 #else
1534 	pos = strrchr(fn, '/');
1535 #endif
1536 	if (pos) {
1537 		buffer_copy_string_len(context.basedir, fn, pos - fn + 1);
1538 	}
1539 
1540 	dc = data_config_init();
1541 	buffer_copy_string_len(dc->key, CONST_STR_LEN("global"));
1542 
1543 	force_assert(context.all_configs->used == 0);
1544 	dc->context_ndx = context.all_configs->used;
1545 	array_insert_unique(context.all_configs, (data_unset *)dc);
1546 	context.current = dc;
1547 
1548 	/* default context */
1549 	*array_get_int_ptr(dc->value, CONST_STR_LEN("var.PID")) = getpid();
1550 
1551 	dcwd = srv->tmp_buf;
1552 	buffer_string_prepare_copy(dcwd, PATH_MAX-1);
1553 	if (NULL != getcwd(dcwd->ptr, buffer_string_space(dcwd)+1)) {
1554 		buffer_commit(dcwd, strlen(dcwd->ptr));
1555 		array_set_key_value(dc->value, CONST_STR_LEN("var.CWD"), CONST_BUF_LEN(dcwd));
1556 	}
1557 
1558 	filename = buffer_init_string(fn);
1559 	ret = config_parse_file_stream(srv, &context, filename);
1560 	buffer_free(filename);
1561 
1562 	/* remains nothing if parser is ok */
1563 	force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used));
1564 	context_free(&context);
1565 
1566 	if (0 != ret) {
1567 		return ret;
1568 	}
1569 
1570 	if (0 != config_insert(srv)) {
1571 		return -1;
1572 	}
1573 
1574 	return 0;
1575 }
1576 
1577 int config_set_defaults(server *srv) {
1578 	size_t i;
1579 	specific_config *s = srv->config_storage[0];
1580 	struct stat st1, st2;
1581 
1582 	force_assert(sizeof(((connection *)0)->conditional_is_valid) >= COMP_LAST_ELEMENT);
1583 
1584 	if (0 != fdevent_config(srv)) return -1;
1585 
1586 	if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
1587 		if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) {
1588 			log_error_write(srv, __FILE__, __LINE__, "sb",
1589 					"server.chroot doesn't exist:", srv->srvconf.changeroot);
1590 			return -1;
1591 		}
1592 		if (!S_ISDIR(st1.st_mode)) {
1593 			log_error_write(srv, __FILE__, __LINE__, "sb",
1594 					"server.chroot isn't a directory:", srv->srvconf.changeroot);
1595 			return -1;
1596 		}
1597 	}
1598 
1599 	if (!srv->srvconf.upload_tempdirs->used) {
1600 		const char *tmpdir = getenv("TMPDIR");
1601 		if (NULL == tmpdir) tmpdir = "/var/tmp";
1602 		array_insert_value(srv->srvconf.upload_tempdirs, tmpdir, strlen(tmpdir));
1603 	}
1604 
1605 	if (srv->srvconf.upload_tempdirs->used) {
1606 		buffer * const b = srv->tmp_buf;
1607 		size_t len;
1608 		buffer_clear(b);
1609 		if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
1610 			buffer_copy_buffer(b, srv->srvconf.changeroot);
1611 			buffer_append_slash(b);
1612 		}
1613 		len = buffer_string_length(b);
1614 
1615 		for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) {
1616 			const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i];
1617 			buffer_string_set_length(b, len); /*(truncate)*/
1618 			buffer_append_string_buffer(b, ds->value);
1619 			if (-1 == stat(b->ptr, &st1)) {
1620 				log_error_write(srv, __FILE__, __LINE__, "sb",
1621 					"server.upload-dirs doesn't exist:", b);
1622 			} else if (!S_ISDIR(st1.st_mode)) {
1623 				log_error_write(srv, __FILE__, __LINE__, "sb",
1624 					"server.upload-dirs isn't a directory:", b);
1625 			}
1626 		}
1627 	}
1628 
1629 	chunkqueue_set_tempdirs_default(
1630 		srv->srvconf.upload_tempdirs,
1631 		srv->srvconf.upload_temp_file_size);
1632 
1633 	if (buffer_string_is_empty(s->document_root)) {
1634 		log_error_write(srv, __FILE__, __LINE__, "s",
1635 				"a default document-root has to be set");
1636 
1637 		return -1;
1638 	}
1639 
1640 	buffer_copy_buffer(srv->tmp_buf, s->document_root);
1641 
1642 	buffer_to_lower(srv->tmp_buf);
1643 
1644 	if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */
1645 		s->force_lowercase_filenames = 0; /* default to 0 */
1646 
1647 		if (0 == stat(srv->tmp_buf->ptr, &st1)) {
1648 			int is_lower = 0;
1649 
1650 			is_lower = buffer_is_equal(srv->tmp_buf, s->document_root);
1651 
1652 			/* lower-case existed, check upper-case */
1653 			buffer_copy_buffer(srv->tmp_buf, s->document_root);
1654 
1655 			buffer_to_upper(srv->tmp_buf);
1656 
1657 			/* we have to handle the special case that upper and lower-casing results in the same filename
1658 			 * as in server.document-root = "/" or "/12345/" */
1659 
1660 			if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) {
1661 				/* lower-casing and upper-casing didn't result in
1662 				 * an other filename, no need to stat(),
1663 				 * just assume it is case-sensitive. */
1664 
1665 				s->force_lowercase_filenames = 0;
1666 			} else if (0 == stat(srv->tmp_buf->ptr, &st2)) {
1667 
1668 				/* upper case exists too, doesn't the FS handle this ? */
1669 
1670 				/* upper and lower have the same inode -> case-insensitve FS */
1671 
1672 				if (st1.st_ino == st2.st_ino) {
1673 					/* upper and lower have the same inode -> case-insensitve FS */
1674 
1675 					s->force_lowercase_filenames = 1;
1676 				}
1677 			}
1678 		}
1679 	}
1680 
1681 	if (srv->srvconf.port == 0) {
1682 		srv->srvconf.port = s->ssl_enabled ? 443 : 80;
1683 	}
1684 
1685 	return 0;
1686 }
1687