[core] set REDIRECT_STATUS to error_handler_saved_status (fixes #1828)
[lighttpd.git] / src / configfile.c
blob1a0f3dbe3c46ce705b61e45c906657caba7905d9
1 #include "first.h"
3 #include "server.h"
4 #include "log.h"
5 #include "stream.h"
6 #include "plugin.h"
8 #include "configparser.h"
9 #include "configfile.h"
10 #include "proc_open.h"
12 #include <sys/stat.h>
14 #include <stdlib.h>
15 #include <fcntl.h>
16 #include <unistd.h>
17 #include <errno.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <ctype.h>
21 #include <limits.h>
22 #include <assert.h>
25 static int config_insert(server *srv) {
26 size_t i;
27 int ret = 0;
28 buffer *stat_cache_string;
30 config_values_t cv[] = {
31 { "server.bind", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 0 */
32 { "server.errorlog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 1 */
33 { "server.errorfile-prefix", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
34 { "server.chroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 3 */
35 { "server.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 4 */
36 { "server.groupname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 5 */
37 { "server.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 6 */
38 { "server.tag", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
39 { "server.use-ipv6", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
40 { "server.modules", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 9 */
42 { "server.event-handler", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 10 */
43 { "server.pid-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 11 */
44 { "server.max-request-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 12 */
45 { "server.max-worker", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 13 */
46 { "server.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */
47 { "server.force-lowercase-filenames", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */
48 { "debug.log-condition-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 16 */
49 { "server.max-keep-alive-requests", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */
50 { "server.name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 18 */
51 { "server.max-keep-alive-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */
53 { "server.max-read-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 20 */
54 { "server.max-write-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 21 */
55 { "server.error-handler-404", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */
56 { "server.max-fds", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 23 */
57 #ifdef HAVE_LSTAT
58 { "server.follow-symlink", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 24 */
59 #else
60 { "server.follow-symlink",
61 "Your system lacks lstat(). We can not differ symlinks from files."
62 "Please remove server.follow-symlinks from your config.",
63 T_CONFIG_UNSUPPORTED, T_CONFIG_SCOPE_UNSET },
64 #endif
65 { "server.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 25 */
66 { "connection.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 26 */
67 { "mimetype.use-xattr", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 27 */
68 { "mimetype.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 28 */
69 { "ssl.pemfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 29 */
71 { "ssl.engine", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 30 */
72 { "debug.log-file-not-found", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 31 */
73 { "debug.log-request-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 32 */
74 { "debug.log-response-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 33 */
75 { "debug.log-request-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 34 */
76 { "debug.log-ssl-noise", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 35 */
77 { "server.protocol-http11", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 36 */
78 { "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 37 */
79 { "debug.log-state-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 38 */
80 { "ssl.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 39 */
82 { "server.errorlog-use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 40 */
83 { "server.range-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 41 */
84 { "server.stat-cache-engine", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 42 */
85 { "server.max-connections", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 43 */
86 { "server.network-backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 44 */
87 { "server.upload-dirs", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 45 */
88 { "server.core-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 46 */
89 { "ssl.cipher-list", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 47 */
90 { "ssl.use-sslv2", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 48 */
91 { "etag.use-inode", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 49 */
93 { "etag.use-mtime", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 50 */
94 { "etag.use-size", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 51 */
95 { "server.reject-expect-100-with-417", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 52 */
96 { "debug.log-timeouts", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 53 */
97 { "server.defer-accept", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 54 */
98 { "server.breakagelog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 55 */
99 { "ssl.verifyclient.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 56 */
100 { "ssl.verifyclient.enforce", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 57 */
101 { "ssl.verifyclient.depth", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 58 */
102 { "ssl.verifyclient.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 59 */
104 { "ssl.verifyclient.exportcert", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 60 */
105 { "server.set-v6only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 61 */
106 { "ssl.use-sslv3", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 62 */
107 { "ssl.dh-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 63 */
108 { "ssl.ec-curve", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 64 */
109 { "ssl.disable-client-renegotiation", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 65 */
110 { "ssl.honor-cipher-order", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 66 */
111 { "ssl.empty-fragments", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 67 */
112 { "server.upload-temp-file-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 68 */
113 { "mimetype.xattr-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 69 */
114 { "server.listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 70 */
116 { "server.host",
117 "use server.bind instead",
118 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
119 { "server.docroot",
120 "use server.document-root instead",
121 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
122 { "server.virtual-root",
123 "load mod_simple_vhost and use simple-vhost.server-root instead",
124 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
125 { "server.virtual-default-host",
126 "load mod_simple_vhost and use simple-vhost.default-host instead",
127 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
128 { "server.virtual-docroot",
129 "load mod_simple_vhost and use simple-vhost.document-root instead",
130 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
131 { "server.userid",
132 "use server.username instead",
133 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
134 { "server.groupid",
135 "use server.groupname instead",
136 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
137 { "server.use-keep-alive",
138 "use server.max-keep-alive-requests = 0 instead",
139 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
140 { "server.force-lower-case-files",
141 "use server.force-lowercase-filenames instead",
142 T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET },
144 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
147 /* all T_CONFIG_SCOPE_SERVER options */
148 cv[0].destination = srv->srvconf.bindhost;
149 cv[1].destination = srv->srvconf.errorlog_file;
150 cv[3].destination = srv->srvconf.changeroot;
151 cv[4].destination = srv->srvconf.username;
152 cv[5].destination = srv->srvconf.groupname;
153 cv[6].destination = &(srv->srvconf.port);
154 cv[9].destination = srv->srvconf.modules;
156 cv[10].destination = srv->srvconf.event_handler;
157 cv[11].destination = srv->srvconf.pid_file;
158 cv[12].destination = &(srv->srvconf.max_request_size);
159 cv[13].destination = &(srv->srvconf.max_worker);
161 cv[23].destination = &(srv->srvconf.max_fds);
163 cv[37].destination = &(srv->srvconf.log_request_header_on_error);
164 cv[38].destination = &(srv->srvconf.log_state_handling);
166 cv[40].destination = &(srv->srvconf.errorlog_use_syslog);
167 stat_cache_string = buffer_init();
168 cv[42].destination = stat_cache_string;
169 cv[43].destination = &(srv->srvconf.max_conns);
170 cv[44].destination = srv->srvconf.network_backend;
171 cv[45].destination = srv->srvconf.upload_tempdirs;
172 cv[46].destination = &(srv->srvconf.enable_cores);
174 cv[52].destination = &(srv->srvconf.reject_expect_100_with_417);
175 cv[55].destination = srv->srvconf.breakagelog_file;
177 cv[68].destination = &(srv->srvconf.upload_temp_file_size);
178 cv[69].destination = srv->srvconf.xattr_name;
180 srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
182 force_assert(srv->config_storage);
184 for (i = 0; i < srv->config_context->used; i++) {
185 data_config const* config = (data_config const*)srv->config_context->data[i];
186 specific_config *s;
188 s = calloc(1, sizeof(specific_config));
189 force_assert(s);
190 s->document_root = buffer_init();
191 s->mimetypes = array_init();
192 s->server_name = buffer_init();
193 s->ssl_pemfile = buffer_init();
194 s->ssl_ca_file = buffer_init();
195 s->error_handler = buffer_init();
196 s->server_tag = buffer_init();
197 s->ssl_cipher_list = buffer_init();
198 s->ssl_dh_file = buffer_init();
199 s->ssl_ec_curve = buffer_init();
200 s->errorfile_prefix = buffer_init();
201 s->max_keep_alive_requests = 16;
202 s->max_keep_alive_idle = 5;
203 s->max_read_idle = 60;
204 s->max_write_idle = 360;
205 s->use_xattr = 0;
206 s->ssl_enabled = 0;
207 s->ssl_honor_cipher_order = 1;
208 s->ssl_empty_fragments = 0;
209 s->ssl_use_sslv2 = 0;
210 s->ssl_use_sslv3 = 0;
211 s->use_ipv6 = 0;
212 s->set_v6only = 1;
213 s->defer_accept = 0;
214 #ifdef HAVE_LSTAT
215 s->follow_symlink = 1;
216 #endif
217 s->kbytes_per_second = 0;
218 s->allow_http11 = 1;
219 s->etag_use_inode = 1;
220 s->etag_use_mtime = 1;
221 s->etag_use_size = 1;
222 s->range_requests = 1;
223 s->force_lowercase_filenames = (i == 0) ? 2 : 0; /* we wan't to detect later if user changed this for global section */
224 s->global_kbytes_per_second = 0;
225 s->global_bytes_per_second_cnt = 0;
226 s->global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt;
227 s->ssl_verifyclient = 0;
228 s->ssl_verifyclient_enforce = 1;
229 s->ssl_verifyclient_username = buffer_init();
230 s->ssl_verifyclient_depth = 9;
231 s->ssl_verifyclient_export_cert = 0;
232 s->ssl_disable_client_renegotiation = 1;
233 s->listen_backlog = (0 == i ? 1024 : srv->config_storage[0]->listen_backlog);
235 /* all T_CONFIG_SCOPE_CONNECTION options */
236 cv[2].destination = s->errorfile_prefix;
237 cv[7].destination = s->server_tag;
238 cv[8].destination = &(s->use_ipv6);
240 cv[14].destination = s->document_root;
241 cv[15].destination = &(s->force_lowercase_filenames);
242 cv[16].destination = &(s->log_condition_handling);
243 cv[17].destination = &(s->max_keep_alive_requests);
244 cv[18].destination = s->server_name;
245 cv[19].destination = &(s->max_keep_alive_idle);
247 cv[20].destination = &(s->max_read_idle);
248 cv[21].destination = &(s->max_write_idle);
249 cv[22].destination = s->error_handler;
250 #ifdef HAVE_LSTAT
251 cv[24].destination = &(s->follow_symlink);
252 #endif
253 cv[25].destination = &(s->global_kbytes_per_second);
254 cv[26].destination = &(s->kbytes_per_second);
255 cv[27].destination = &(s->use_xattr);
256 cv[28].destination = s->mimetypes;
257 cv[29].destination = s->ssl_pemfile;
259 cv[30].destination = &(s->ssl_enabled);
260 cv[31].destination = &(s->log_file_not_found);
261 cv[32].destination = &(s->log_request_handling);
262 cv[33].destination = &(s->log_response_header);
263 cv[34].destination = &(s->log_request_header);
264 cv[35].destination = &(s->log_ssl_noise);
265 cv[36].destination = &(s->allow_http11);
266 cv[39].destination = s->ssl_ca_file;
268 cv[41].destination = &(s->range_requests);
269 cv[47].destination = s->ssl_cipher_list;
270 cv[48].destination = &(s->ssl_use_sslv2);
271 cv[49].destination = &(s->etag_use_inode);
273 cv[50].destination = &(s->etag_use_mtime);
274 cv[51].destination = &(s->etag_use_size);
275 cv[53].destination = &(s->log_timeouts);
276 cv[54].destination = &(s->defer_accept);
277 cv[56].destination = &(s->ssl_verifyclient);
278 cv[57].destination = &(s->ssl_verifyclient_enforce);
279 cv[58].destination = &(s->ssl_verifyclient_depth);
280 cv[59].destination = s->ssl_verifyclient_username;
282 cv[60].destination = &(s->ssl_verifyclient_export_cert);
283 cv[61].destination = &(s->set_v6only);
284 cv[62].destination = &(s->ssl_use_sslv3);
285 cv[63].destination = s->ssl_dh_file;
286 cv[64].destination = s->ssl_ec_curve;
287 cv[65].destination = &(s->ssl_disable_client_renegotiation);
288 cv[66].destination = &(s->ssl_honor_cipher_order);
289 cv[67].destination = &(s->ssl_empty_fragments);
290 cv[70].destination = &(s->listen_backlog);
292 srv->config_storage[i] = s;
294 if (0 != (ret = config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION))) {
295 break;
299 if (buffer_string_is_empty(stat_cache_string)) {
300 srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE;
301 } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("simple"))) {
302 srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE;
303 #ifdef HAVE_FAM_H
304 } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("fam"))) {
305 srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_FAM;
306 #endif
307 } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("disable"))) {
308 srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_NONE;
309 } else {
310 log_error_write(srv, __FILE__, __LINE__, "sb",
311 "server.stat-cache-engine can be one of \"disable\", \"simple\","
312 #ifdef HAVE_FAM_H
313 " \"fam\","
314 #endif
315 " but not:", stat_cache_string);
316 ret = HANDLER_ERROR;
319 buffer_free(stat_cache_string);
321 return ret;
326 #define PATCH(x) con->conf.x = s->x
327 int config_setup_connection(server *srv, connection *con) {
328 specific_config *s = srv->config_storage[0];
330 PATCH(allow_http11);
331 PATCH(mimetypes);
332 PATCH(document_root);
333 PATCH(max_keep_alive_requests);
334 PATCH(max_keep_alive_idle);
335 PATCH(max_read_idle);
336 PATCH(max_write_idle);
337 PATCH(use_xattr);
338 PATCH(error_handler);
339 PATCH(errorfile_prefix);
340 #ifdef HAVE_LSTAT
341 PATCH(follow_symlink);
342 #endif
343 PATCH(server_tag);
344 PATCH(kbytes_per_second);
345 PATCH(global_kbytes_per_second);
346 PATCH(global_bytes_per_second_cnt);
348 con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt;
349 buffer_copy_buffer(con->server_name, s->server_name);
351 PATCH(log_request_header);
352 PATCH(log_response_header);
353 PATCH(log_request_handling);
354 PATCH(log_condition_handling);
355 PATCH(log_file_not_found);
356 PATCH(log_ssl_noise);
357 PATCH(log_timeouts);
359 PATCH(range_requests);
360 PATCH(force_lowercase_filenames);
361 /*PATCH(listen_backlog);*//*(not necessary; used only at startup)*/
362 PATCH(ssl_enabled);
364 PATCH(ssl_pemfile);
365 #ifdef USE_OPENSSL
366 PATCH(ssl_pemfile_x509);
367 PATCH(ssl_pemfile_pkey);
368 #endif
369 PATCH(ssl_ca_file);
370 #ifdef USE_OPENSSL
371 PATCH(ssl_ca_file_cert_names);
372 #endif
373 PATCH(ssl_cipher_list);
374 PATCH(ssl_dh_file);
375 PATCH(ssl_ec_curve);
376 PATCH(ssl_honor_cipher_order);
377 PATCH(ssl_empty_fragments);
378 PATCH(ssl_use_sslv2);
379 PATCH(ssl_use_sslv3);
380 PATCH(etag_use_inode);
381 PATCH(etag_use_mtime);
382 PATCH(etag_use_size);
384 PATCH(ssl_verifyclient);
385 PATCH(ssl_verifyclient_enforce);
386 PATCH(ssl_verifyclient_depth);
387 PATCH(ssl_verifyclient_username);
388 PATCH(ssl_verifyclient_export_cert);
389 PATCH(ssl_disable_client_renegotiation);
391 return 0;
394 int config_patch_connection(server *srv, connection *con) {
395 size_t i, j;
397 /* skip the first, the global context */
398 for (i = 1; i < srv->config_context->used; i++) {
399 data_config *dc = (data_config *)srv->config_context->data[i];
400 specific_config *s = srv->config_storage[i];
402 /* condition didn't match */
403 if (!config_check_cond(srv, con, dc)) continue;
405 /* merge config */
406 for (j = 0; j < dc->value->used; j++) {
407 data_unset *du = dc->value->data[j];
409 if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.document-root"))) {
410 PATCH(document_root);
411 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.range-requests"))) {
412 PATCH(range_requests);
413 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler-404"))) {
414 PATCH(error_handler);
415 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.errorfile-prefix"))) {
416 PATCH(errorfile_prefix);
417 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.assign"))) {
418 PATCH(mimetypes);
419 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-keep-alive-requests"))) {
420 PATCH(max_keep_alive_requests);
421 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-keep-alive-idle"))) {
422 PATCH(max_keep_alive_idle);
423 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-write-idle"))) {
424 PATCH(max_write_idle);
425 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-read-idle"))) {
426 PATCH(max_read_idle);
427 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.use-xattr"))) {
428 PATCH(use_xattr);
429 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-inode"))) {
430 PATCH(etag_use_inode);
431 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-mtime"))) {
432 PATCH(etag_use_mtime);
433 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-size"))) {
434 PATCH(etag_use_size);
435 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.pemfile"))) {
436 PATCH(ssl_pemfile);
437 #ifdef USE_OPENSSL
438 PATCH(ssl_pemfile_x509);
439 PATCH(ssl_pemfile_pkey);
440 #endif
441 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-file"))) {
442 PATCH(ssl_ca_file);
443 #ifdef USE_OPENSSL
444 PATCH(ssl_ca_file_cert_names);
445 #endif
446 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.honor-cipher-order"))) {
447 PATCH(ssl_honor_cipher_order);
448 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.empty-fragments"))) {
449 PATCH(ssl_empty_fragments);
450 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv2"))) {
451 PATCH(ssl_use_sslv2);
452 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv3"))) {
453 PATCH(ssl_use_sslv3);
454 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.cipher-list"))) {
455 PATCH(ssl_cipher_list);
456 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.engine"))) {
457 PATCH(ssl_enabled);
458 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.dh-file"))) {
459 PATCH(ssl_dh_file);
460 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ec-curve"))) {
461 PATCH(ssl_ec_curve);
462 #ifdef HAVE_LSTAT
463 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.follow-symlink"))) {
464 PATCH(follow_symlink);
465 #endif
466 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.name"))) {
467 buffer_copy_buffer(con->server_name, s->server_name);
468 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.tag"))) {
469 PATCH(server_tag);
470 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("connection.kbytes-per-second"))) {
471 PATCH(kbytes_per_second);
472 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-request-handling"))) {
473 PATCH(log_request_handling);
474 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-request-header"))) {
475 PATCH(log_request_header);
476 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-response-header"))) {
477 PATCH(log_response_header);
478 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-condition-handling"))) {
479 PATCH(log_condition_handling);
480 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-file-not-found"))) {
481 PATCH(log_file_not_found);
482 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-ssl-noise"))) {
483 PATCH(log_ssl_noise);
484 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-timeouts"))) {
485 PATCH(log_timeouts);
486 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.protocol-http11"))) {
487 PATCH(allow_http11);
488 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.force-lowercase-filenames"))) {
489 PATCH(force_lowercase_filenames);
490 #if 0 /*(not necessary; used only at startup)*/
491 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.listen-backlog"))) {
492 PATCH(listen_backlog);
493 #endif
494 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.kbytes-per-second"))) {
495 PATCH(global_kbytes_per_second);
496 PATCH(global_bytes_per_second_cnt);
497 con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt;
498 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.activate"))) {
499 PATCH(ssl_verifyclient);
500 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.enforce"))) {
501 PATCH(ssl_verifyclient_enforce);
502 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.depth"))) {
503 PATCH(ssl_verifyclient_depth);
504 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.username"))) {
505 PATCH(ssl_verifyclient_username);
506 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.exportcert"))) {
507 PATCH(ssl_verifyclient_export_cert);
508 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.disable-client-renegotiation"))) {
509 PATCH(ssl_disable_client_renegotiation);
514 con->etag_flags = (con->conf.etag_use_mtime ? ETAG_USE_MTIME : 0) |
515 (con->conf.etag_use_inode ? ETAG_USE_INODE : 0) |
516 (con->conf.etag_use_size ? ETAG_USE_SIZE : 0);
518 return 0;
520 #undef PATCH
522 typedef struct {
523 int foo;
524 int bar;
526 const buffer *source;
527 const char *input;
528 size_t offset;
529 size_t size;
531 int line_pos;
532 int line;
534 int in_key;
535 int in_brace;
536 int in_cond;
537 } tokenizer_t;
539 #if 0
540 static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const char *fn) {
541 if (buffer_string_is_empty(basedir) ||
542 (fn[0] == '/' || fn[0] == '\\') ||
543 (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) {
544 t->file = buffer_init_string(fn);
545 } else {
546 t->file = buffer_init_buffer(basedir);
547 buffer_append_string(t->file, fn);
550 if (0 != stream_open(&(t->s), t->file)) {
551 log_error_write(srv, __FILE__, __LINE__, "sbss",
552 "opening configfile ", t->file, "failed:", strerror(errno));
553 buffer_free(t->file);
554 return -1;
557 t->input = t->s.start;
558 t->offset = 0;
559 t->size = t->s.size;
560 t->line = 1;
561 t->line_pos = 1;
563 t->in_key = 1;
564 t->in_brace = 0;
565 t->in_cond = 0;
566 return 0;
569 static int tokenizer_close(server *srv, tokenizer_t *t) {
570 UNUSED(srv);
572 buffer_free(t->file);
573 return stream_close(&(t->s));
575 #endif
576 static int config_skip_newline(tokenizer_t *t) {
577 int skipped = 1;
578 force_assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n');
579 if (t->input[t->offset] == '\r' && t->input[t->offset + 1] == '\n') {
580 skipped ++;
581 t->offset ++;
583 t->offset ++;
584 return skipped;
587 static int config_skip_comment(tokenizer_t *t) {
588 int i;
589 force_assert(t->input[t->offset] == '#');
590 for (i = 1; t->input[t->offset + i] &&
591 (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r');
592 i++);
593 t->offset += i;
594 return i;
597 static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) {
598 int tid = 0;
599 size_t i;
601 for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) {
602 char c = t->input[t->offset];
603 const char *start = NULL;
605 switch (c) {
606 case '=':
607 if (t->in_brace) {
608 if (t->input[t->offset + 1] == '>') {
609 t->offset += 2;
611 buffer_copy_string_len(token, CONST_STR_LEN("=>"));
613 tid = TK_ARRAY_ASSIGN;
614 } else {
615 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
616 "source:", t->source,
617 "line:", t->line, "pos:", t->line_pos,
618 "use => for assignments in arrays");
619 return -1;
621 } else if (t->in_cond) {
622 if (t->input[t->offset + 1] == '=') {
623 t->offset += 2;
625 buffer_copy_string_len(token, CONST_STR_LEN("=="));
627 tid = TK_EQ;
628 } else if (t->input[t->offset + 1] == '~') {
629 t->offset += 2;
631 buffer_copy_string_len(token, CONST_STR_LEN("=~"));
633 tid = TK_MATCH;
634 } else {
635 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
636 "source:", t->source,
637 "line:", t->line, "pos:", t->line_pos,
638 "only =~ and == are allowed in the condition");
639 return -1;
641 t->in_key = 1;
642 t->in_cond = 0;
643 } else if (t->in_key) {
644 tid = TK_ASSIGN;
646 buffer_copy_string_len(token, t->input + t->offset, 1);
648 t->offset++;
649 t->line_pos++;
650 } else {
651 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
652 "source:", t->source,
653 "line:", t->line, "pos:", t->line_pos,
654 "unexpected equal-sign: =");
655 return -1;
658 break;
659 case '!':
660 if (t->in_cond) {
661 if (t->input[t->offset + 1] == '=') {
662 t->offset += 2;
664 buffer_copy_string_len(token, CONST_STR_LEN("!="));
666 tid = TK_NE;
667 } else if (t->input[t->offset + 1] == '~') {
668 t->offset += 2;
670 buffer_copy_string_len(token, CONST_STR_LEN("!~"));
672 tid = TK_NOMATCH;
673 } else {
674 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
675 "source:", t->source,
676 "line:", t->line, "pos:", t->line_pos,
677 "only !~ and != are allowed in the condition");
678 return -1;
680 t->in_key = 1;
681 t->in_cond = 0;
682 } else {
683 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
684 "source:", t->source,
685 "line:", t->line, "pos:", t->line_pos,
686 "unexpected exclamation-marks: !");
687 return -1;
690 break;
691 case '\t':
692 case ' ':
693 t->offset++;
694 t->line_pos++;
695 break;
696 case '\n':
697 case '\r':
698 if (t->in_brace == 0) {
699 int done = 0;
700 while (!done && t->offset < t->size) {
701 switch (t->input[t->offset]) {
702 case '\r':
703 case '\n':
704 config_skip_newline(t);
705 t->line_pos = 1;
706 t->line++;
707 break;
709 case '#':
710 t->line_pos += config_skip_comment(t);
711 break;
713 case '\t':
714 case ' ':
715 t->offset++;
716 t->line_pos++;
717 break;
719 default:
720 done = 1;
723 t->in_key = 1;
724 tid = TK_EOL;
725 buffer_copy_string_len(token, CONST_STR_LEN("(EOL)"));
726 } else {
727 config_skip_newline(t);
728 t->line_pos = 1;
729 t->line++;
731 break;
732 case ',':
733 if (t->in_brace > 0) {
734 tid = TK_COMMA;
736 buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)"));
739 t->offset++;
740 t->line_pos++;
741 break;
742 case '"':
743 /* search for the terminating " */
744 start = t->input + t->offset + 1;
745 buffer_copy_string_len(token, CONST_STR_LEN(""));
747 for (i = 1; t->input[t->offset + i]; i++) {
748 if (t->input[t->offset + i] == '\\' &&
749 t->input[t->offset + i + 1] == '"') {
751 buffer_append_string_len(token, start, t->input + t->offset + i - start);
753 start = t->input + t->offset + i + 1;
755 /* skip the " */
756 i++;
757 continue;
761 if (t->input[t->offset + i] == '"') {
762 tid = TK_STRING;
764 buffer_append_string_len(token, start, t->input + t->offset + i - start);
766 break;
770 if (t->input[t->offset + i] == '\0') {
771 /* ERROR */
773 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
774 "source:", t->source,
775 "line:", t->line, "pos:", t->line_pos,
776 "missing closing quote");
778 return -1;
781 t->offset += i + 1;
782 t->line_pos += i + 1;
784 break;
785 case '(':
786 t->offset++;
787 t->in_brace++;
789 tid = TK_LPARAN;
791 buffer_copy_string_len(token, CONST_STR_LEN("("));
792 break;
793 case ')':
794 t->offset++;
795 t->in_brace--;
797 tid = TK_RPARAN;
799 buffer_copy_string_len(token, CONST_STR_LEN(")"));
800 break;
801 case '$':
802 t->offset++;
804 tid = TK_DOLLAR;
805 t->in_cond = 1;
806 t->in_key = 0;
808 buffer_copy_string_len(token, CONST_STR_LEN("$"));
810 break;
812 case '+':
813 if (t->input[t->offset + 1] == '=') {
814 t->offset += 2;
815 buffer_copy_string_len(token, CONST_STR_LEN("+="));
816 tid = TK_APPEND;
817 } else {
818 t->offset++;
819 tid = TK_PLUS;
820 buffer_copy_string_len(token, CONST_STR_LEN("+"));
822 break;
824 case '{':
825 t->offset++;
827 tid = TK_LCURLY;
829 buffer_copy_string_len(token, CONST_STR_LEN("{"));
831 break;
833 case '}':
834 t->offset++;
836 tid = TK_RCURLY;
838 buffer_copy_string_len(token, CONST_STR_LEN("}"));
840 break;
842 case '[':
843 t->offset++;
845 tid = TK_LBRACKET;
847 buffer_copy_string_len(token, CONST_STR_LEN("["));
849 break;
851 case ']':
852 t->offset++;
854 tid = TK_RBRACKET;
856 buffer_copy_string_len(token, CONST_STR_LEN("]"));
858 break;
859 case '#':
860 t->line_pos += config_skip_comment(t);
862 break;
863 default:
864 if (t->in_cond) {
865 for (i = 0; t->input[t->offset + i] &&
866 (isalpha((unsigned char)t->input[t->offset + i])
867 ); i++);
869 if (i && t->input[t->offset + i]) {
870 tid = TK_SRVVARNAME;
871 buffer_copy_string_len(token, t->input + t->offset, i);
873 t->offset += i;
874 t->line_pos += i;
875 } else {
876 /* ERROR */
877 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
878 "source:", t->source,
879 "line:", t->line, "pos:", t->line_pos,
880 "invalid character in condition");
881 return -1;
883 } else if (isdigit((unsigned char)c)) {
884 /* take all digits */
885 for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++);
887 /* was there it least a digit ? */
888 if (i) {
889 tid = TK_INTEGER;
891 buffer_copy_string_len(token, t->input + t->offset, i);
893 t->offset += i;
894 t->line_pos += i;
896 } else {
897 /* the key might consist of [-.0-9a-z] */
898 for (i = 0; t->input[t->offset + i] &&
899 (isalnum((unsigned char)t->input[t->offset + i]) ||
900 t->input[t->offset + i] == '.' ||
901 t->input[t->offset + i] == '_' || /* for env.* */
902 t->input[t->offset + i] == '-'
903 ); i++);
905 if (i && t->input[t->offset + i]) {
906 buffer_copy_string_len(token, t->input + t->offset, i);
908 if (strcmp(token->ptr, "include") == 0) {
909 tid = TK_INCLUDE;
910 } else if (strcmp(token->ptr, "include_shell") == 0) {
911 tid = TK_INCLUDE_SHELL;
912 } else if (strcmp(token->ptr, "global") == 0) {
913 tid = TK_GLOBAL;
914 } else if (strcmp(token->ptr, "else") == 0) {
915 tid = TK_ELSE;
916 } else {
917 tid = TK_LKEY;
920 t->offset += i;
921 t->line_pos += i;
922 } else {
923 /* ERROR */
924 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
925 "source:", t->source,
926 "line:", t->line, "pos:", t->line_pos,
927 "invalid character in variable name");
928 return -1;
931 break;
935 if (tid) {
936 *token_id = tid;
937 #if 0
938 log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd",
939 "source:", t->source,
940 "line:", t->line, "pos:", t->line_pos,
941 token, token->used - 1, tid);
942 #endif
944 return 1;
945 } else if (t->offset < t->size) {
946 fprintf(stderr, "%s.%d: %d, %s\n",
947 __FILE__, __LINE__,
948 tid, token->ptr);
950 return 0;
953 static int config_parse(server *srv, config_t *context, tokenizer_t *t) {
954 void *pParser;
955 int token_id;
956 buffer *token, *lasttoken;
957 int ret;
959 pParser = configparserAlloc( malloc );
960 force_assert(pParser);
961 lasttoken = buffer_init();
962 token = buffer_init();
963 while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) {
964 buffer_copy_buffer(lasttoken, token);
965 configparser(pParser, token_id, token, context);
967 token = buffer_init();
969 buffer_free(token);
971 if (ret != -1 && context->ok) {
972 /* add an EOL at EOF, better than say sorry */
973 configparser(pParser, TK_EOL, buffer_init_string("(EOL)"), context);
974 if (context->ok) {
975 configparser(pParser, 0, NULL, context);
978 configparserFree(pParser, free);
980 if (ret == -1) {
981 log_error_write(srv, __FILE__, __LINE__, "sb",
982 "configfile parser failed at:", lasttoken);
983 } else if (context->ok == 0) {
984 log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb",
985 "source:", t->source,
986 "line:", t->line, "pos:", t->line_pos,
987 "parser failed somehow near here:", lasttoken);
988 ret = -1;
990 buffer_free(lasttoken);
992 return ret == -1 ? -1 : 0;
995 static int tokenizer_init(tokenizer_t *t, const buffer *source, const char *input, size_t size) {
997 t->source = source;
998 t->input = input;
999 t->size = size;
1000 t->offset = 0;
1001 t->line = 1;
1002 t->line_pos = 1;
1004 t->in_key = 1;
1005 t->in_brace = 0;
1006 t->in_cond = 0;
1007 return 0;
1010 int config_parse_file(server *srv, config_t *context, const char *fn) {
1011 tokenizer_t t;
1012 stream s;
1013 int ret;
1014 buffer *filename;
1016 if (buffer_string_is_empty(context->basedir) ||
1017 (fn[0] == '/' || fn[0] == '\\') ||
1018 (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) {
1019 filename = buffer_init_string(fn);
1020 } else {
1021 filename = buffer_init_buffer(context->basedir);
1022 buffer_append_string(filename, fn);
1025 if (0 != stream_open(&s, filename)) {
1026 log_error_write(srv, __FILE__, __LINE__, "sbss",
1027 "opening configfile ", filename, "failed:", strerror(errno));
1028 ret = -1;
1029 } else {
1030 tokenizer_init(&t, filename, s.start, s.size);
1031 ret = config_parse(srv, context, &t);
1034 stream_close(&s);
1035 buffer_free(filename);
1036 return ret;
1039 static char* getCWD(void) {
1040 char *s, *s1;
1041 size_t len;
1042 #ifdef PATH_MAX
1043 len = PATH_MAX;
1044 #else
1045 len = 4096;
1046 #endif
1048 s = malloc(len);
1049 if (!s) return NULL;
1050 while (NULL == getcwd(s, len)) {
1051 if (errno != ERANGE || SSIZE_MAX - len < len) {
1052 free(s);
1053 return NULL;
1055 len *= 2;
1056 s1 = realloc(s, len);
1057 if (!s1) {
1058 free(s);
1059 return NULL;
1061 s = s1;
1063 return s;
1066 int config_parse_cmd(server *srv, config_t *context, const char *cmd) {
1067 tokenizer_t t;
1068 int ret;
1069 buffer *source;
1070 buffer *out;
1071 char *oldpwd;
1073 if (NULL == (oldpwd = getCWD())) {
1074 log_error_write(srv, __FILE__, __LINE__, "s",
1075 "cannot get cwd", strerror(errno));
1076 return -1;
1079 if (!buffer_string_is_empty(context->basedir)) {
1080 if (0 != chdir(context->basedir->ptr)) {
1081 log_error_write(srv, __FILE__, __LINE__, "sbs",
1082 "cannot change directory to", context->basedir, strerror(errno));
1083 free(oldpwd);
1084 return -1;
1088 source = buffer_init_string(cmd);
1089 out = buffer_init();
1091 if (0 != proc_open_buffer(cmd, NULL, out, NULL)) {
1092 log_error_write(srv, __FILE__, __LINE__, "sbss",
1093 "opening", source, "failed:", strerror(errno));
1094 ret = -1;
1095 } else {
1096 tokenizer_init(&t, source, CONST_BUF_LEN(out));
1097 ret = config_parse(srv, context, &t);
1100 buffer_free(source);
1101 buffer_free(out);
1102 if (0 != chdir(oldpwd)) {
1103 log_error_write(srv, __FILE__, __LINE__, "sss",
1104 "cannot change directory to", oldpwd, strerror(errno));
1105 free(oldpwd);
1106 return -1;
1108 free(oldpwd);
1109 return ret;
1112 static void context_init(server *srv, config_t *context) {
1113 context->srv = srv;
1114 context->ok = 1;
1115 vector_config_weak_init(&context->configs_stack);
1116 context->basedir = buffer_init();
1119 static void context_free(config_t *context) {
1120 vector_config_weak_clear(&context->configs_stack);
1121 buffer_free(context->basedir);
1124 int config_read(server *srv, const char *fn) {
1125 config_t context;
1126 data_config *dc;
1127 data_integer *dpid;
1128 data_string *dcwd;
1129 int ret;
1130 char *pos;
1131 data_array *modules;
1133 context_init(srv, &context);
1134 context.all_configs = srv->config_context;
1136 #ifdef __WIN32
1137 pos = strrchr(fn, '\\');
1138 #else
1139 pos = strrchr(fn, '/');
1140 #endif
1141 if (pos) {
1142 buffer_copy_string_len(context.basedir, fn, pos - fn + 1);
1143 fn = pos + 1;
1146 dc = data_config_init();
1147 buffer_copy_string_len(dc->key, CONST_STR_LEN("global"));
1149 force_assert(context.all_configs->used == 0);
1150 dc->context_ndx = context.all_configs->used;
1151 array_insert_unique(context.all_configs, (data_unset *)dc);
1152 context.current = dc;
1154 /* default context */
1155 srv->config = dc->value;
1156 dpid = data_integer_init();
1157 dpid->value = getpid();
1158 buffer_copy_string_len(dpid->key, CONST_STR_LEN("var.PID"));
1159 array_insert_unique(srv->config, (data_unset *)dpid);
1161 dcwd = data_string_init();
1162 buffer_string_prepare_copy(dcwd->value, 1023);
1163 if (NULL != getcwd(dcwd->value->ptr, dcwd->value->size - 1)) {
1164 buffer_commit(dcwd->value, strlen(dcwd->value->ptr));
1165 buffer_copy_string_len(dcwd->key, CONST_STR_LEN("var.CWD"));
1166 array_insert_unique(srv->config, (data_unset *)dcwd);
1167 } else {
1168 dcwd->free((data_unset*) dcwd);
1171 ret = config_parse_file(srv, &context, fn);
1173 /* remains nothing if parser is ok */
1174 force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used));
1175 context_free(&context);
1177 if (0 != ret) {
1178 return ret;
1181 if (NULL != (dc = (data_config *)array_get_element(srv->config_context, "global"))) {
1182 srv->config = dc->value;
1183 } else {
1184 return -1;
1187 if (NULL != (modules = (data_array *)array_get_element(srv->config, "server.modules"))) {
1188 data_string *ds;
1189 data_array *prepends;
1190 int prepend_mod_indexfile = 1;
1191 int append_mod_dirlisting = 1;
1192 int append_mod_staticfile = 1;
1193 size_t i;
1195 if (modules->type != TYPE_ARRAY) {
1196 fprintf(stderr, "server.modules must be an array");
1197 return -1;
1200 prepends = data_array_init();
1202 /* prepend default modules */
1203 for (i = 0; i < modules->value->used; i++) {
1204 ds = (data_string *)modules->value->data[i];
1206 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_indexfile"))) {
1207 prepend_mod_indexfile = 0;
1210 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_staticfile"))) {
1211 append_mod_staticfile = 0;
1214 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_dirlisting"))) {
1215 append_mod_dirlisting = 0;
1218 if (0 == prepend_mod_indexfile &&
1219 0 == append_mod_dirlisting &&
1220 0 == append_mod_staticfile) {
1221 break;
1225 if (prepend_mod_indexfile) {
1226 ds = data_string_init();
1227 buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile"));
1228 array_insert_unique(prepends->value, (data_unset *)ds);
1231 prepends = (data_array *)configparser_merge_data((data_unset *)prepends, (data_unset *)modules);
1232 force_assert(NULL != prepends);
1233 buffer_copy_buffer(prepends->key, modules->key);
1234 array_replace(srv->config, (data_unset *)prepends);
1235 modules = prepends;
1237 /* append default modules */
1238 if (append_mod_dirlisting) {
1239 ds = data_string_init();
1240 buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting"));
1241 array_insert_unique(modules->value, (data_unset *)ds);
1244 if (append_mod_staticfile) {
1245 ds = data_string_init();
1246 buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile"));
1247 array_insert_unique(modules->value, (data_unset *)ds);
1249 } else {
1250 data_string *ds;
1252 modules = data_array_init();
1254 /* server.modules is not set */
1255 ds = data_string_init();
1256 buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile"));
1257 array_insert_unique(modules->value, (data_unset *)ds);
1259 ds = data_string_init();
1260 buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting"));
1261 array_insert_unique(modules->value, (data_unset *)ds);
1263 ds = data_string_init();
1264 buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile"));
1265 array_insert_unique(modules->value, (data_unset *)ds);
1267 buffer_copy_string_len(modules->key, CONST_STR_LEN("server.modules"));
1268 array_insert_unique(srv->config, (data_unset *)modules);
1272 if (0 != config_insert(srv)) {
1273 return -1;
1276 return 0;
1279 int config_set_defaults(server *srv) {
1280 size_t i;
1281 specific_config *s = srv->config_storage[0];
1282 struct stat st1, st2;
1284 struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] =
1286 /* - epoll is most reliable
1287 * - select works everywhere
1289 #ifdef USE_LINUX_EPOLL
1290 { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" },
1291 #endif
1292 #ifdef USE_POLL
1293 { FDEVENT_HANDLER_POLL, "poll" },
1294 #endif
1295 #ifdef USE_SELECT
1296 { FDEVENT_HANDLER_SELECT, "select" },
1297 #endif
1298 #ifdef USE_LIBEV
1299 { FDEVENT_HANDLER_LIBEV, "libev" },
1300 #endif
1301 #ifdef USE_SOLARIS_DEVPOLL
1302 { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" },
1303 #endif
1304 #ifdef USE_SOLARIS_PORT
1305 { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" },
1306 #endif
1307 #ifdef USE_FREEBSD_KQUEUE
1308 { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" },
1309 { FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" },
1310 #endif
1311 { FDEVENT_HANDLER_UNSET, NULL }
1314 if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
1315 if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) {
1316 log_error_write(srv, __FILE__, __LINE__, "sb",
1317 "server.chroot doesn't exist:", srv->srvconf.changeroot);
1318 return -1;
1320 if (!S_ISDIR(st1.st_mode)) {
1321 log_error_write(srv, __FILE__, __LINE__, "sb",
1322 "server.chroot isn't a directory:", srv->srvconf.changeroot);
1323 return -1;
1327 if (srv->srvconf.upload_tempdirs->used) {
1328 buffer * const b = srv->tmp_buf;
1329 size_t len;
1330 if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
1331 buffer_copy_buffer(b, srv->srvconf.changeroot);
1332 buffer_append_slash(b);
1333 } else {
1334 buffer_reset(b);
1336 len = buffer_string_length(b);
1338 for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) {
1339 const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i];
1340 buffer_string_set_length(b, len); /*(truncate)*/
1341 buffer_append_string_buffer(b, ds->value);
1342 if (-1 == stat(b->ptr, &st1)) {
1343 log_error_write(srv, __FILE__, __LINE__, "sb",
1344 "server.upload-dirs doesn't exist:", b);
1345 } else if (!S_ISDIR(st1.st_mode)) {
1346 log_error_write(srv, __FILE__, __LINE__, "sb",
1347 "server.upload-dirs isn't a directory:", b);
1352 if (buffer_string_is_empty(s->document_root)) {
1353 log_error_write(srv, __FILE__, __LINE__, "s",
1354 "a default document-root has to be set");
1356 return -1;
1359 buffer_copy_buffer(srv->tmp_buf, s->document_root);
1361 buffer_to_lower(srv->tmp_buf);
1363 if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */
1364 s->force_lowercase_filenames = 0; /* default to 0 */
1366 if (0 == stat(srv->tmp_buf->ptr, &st1)) {
1367 int is_lower = 0;
1369 is_lower = buffer_is_equal(srv->tmp_buf, s->document_root);
1371 /* lower-case existed, check upper-case */
1372 buffer_copy_buffer(srv->tmp_buf, s->document_root);
1374 buffer_to_upper(srv->tmp_buf);
1376 /* we have to handle the special case that upper and lower-casing results in the same filename
1377 * as in server.document-root = "/" or "/12345/" */
1379 if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) {
1380 /* lower-casing and upper-casing didn't result in
1381 * an other filename, no need to stat(),
1382 * just assume it is case-sensitive. */
1384 s->force_lowercase_filenames = 0;
1385 } else if (0 == stat(srv->tmp_buf->ptr, &st2)) {
1387 /* upper case exists too, doesn't the FS handle this ? */
1389 /* upper and lower have the same inode -> case-insensitve FS */
1391 if (st1.st_ino == st2.st_ino) {
1392 /* upper and lower have the same inode -> case-insensitve FS */
1394 s->force_lowercase_filenames = 1;
1400 if (srv->srvconf.port == 0) {
1401 srv->srvconf.port = s->ssl_enabled ? 443 : 80;
1404 if (buffer_string_is_empty(srv->srvconf.event_handler)) {
1405 /* choose a good default
1407 * the event_handler list is sorted by 'goodness'
1408 * taking the first available should be the best solution
1410 srv->event_handler = event_handlers[0].et;
1412 if (FDEVENT_HANDLER_UNSET == srv->event_handler) {
1413 log_error_write(srv, __FILE__, __LINE__, "s",
1414 "sorry, there is no event handler for this system");
1416 return -1;
1418 } else {
1420 * User override
1423 for (i = 0; event_handlers[i].name; i++) {
1424 if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler->ptr)) {
1425 srv->event_handler = event_handlers[i].et;
1426 break;
1430 if (FDEVENT_HANDLER_UNSET == srv->event_handler) {
1431 log_error_write(srv, __FILE__, __LINE__, "sb",
1432 "the selected event-handler in unknown or not supported:",
1433 srv->srvconf.event_handler );
1435 return -1;
1439 if (s->ssl_enabled) {
1440 if (buffer_string_is_empty(s->ssl_pemfile)) {
1441 /* PEM file is require */
1443 log_error_write(srv, __FILE__, __LINE__, "s",
1444 "ssl.pemfile has to be set");
1445 return -1;
1448 #ifndef USE_OPENSSL
1449 log_error_write(srv, __FILE__, __LINE__, "s",
1450 "ssl support is missing, recompile with --with-openssl");
1452 return -1;
1453 #endif
1456 return 0;