- next is 1.4.56
[lighttpd.git] / src / configfile.c
bloba7ac53bcf38b28eafb274d5005a428d28f4872c0
1 #include "first.h"
3 #include "base.h"
4 #include "burl.h"
5 #include "fdevent.h"
6 #include "keyvalue.h"
7 #include "log.h"
8 #include "stream.h"
10 #include "configparser.h"
11 #include "configfile.h"
12 #include "stat_cache.h"
13 #include "sys-crypto.h"
15 #include <sys/stat.h>
16 #include <sys/wait.h>
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>
27 #ifndef PATH_MAX
28 #define PATH_MAX 4096
29 #endif
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;
48 #endif
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;
65 #endif
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;
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;
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;
116 else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("host-strict"))) {
117 srv->srvconf.http_host_strict = val;
118 continue;
120 else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("host-normalize"))) {
121 srv->srvconf.http_host_normalize = val;
122 continue;
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;
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;
135 if (val)
136 opts |= opt;
137 else {
138 opts &= ~opt;
139 if (opt == HTTP_PARSEOPT_URL_NORMALIZE) {
140 opts = 0;
141 break;
143 if (opt == HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE) {
144 decode_2f = 0;
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;
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;
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;
176 srv->srvconf.http_url_normalize = opts;
177 return rc;
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;
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 */
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 */
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 */
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 */
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 */
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 */
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 */
286 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
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;
298 cv[10].destination = srv->srvconf.event_handler;
299 cv[11].destination = srv->srvconf.pid_file;
300 cv[13].destination = &(srv->srvconf.max_worker);
302 cv[23].destination = &(srv->srvconf.max_fds);
304 cv[37].destination = &(srv->srvconf.log_request_header_on_error);
305 cv[38].destination = &(srv->srvconf.log_state_handling);
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;
317 cv[52].destination = &(srv->srvconf.reject_expect_100_with_417);
318 cv[55].destination = srv->srvconf.breakagelog_file;
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);
331 srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
333 force_assert(srv->config_storage);
334 force_assert(srv->config_context->used); /* static analysis hint for ccc
335 -analyzer */
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;
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;
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);
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);
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;*/
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;*/
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);
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;*/
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;
450 srv->config_storage[i] = s;
452 if (0 != (ret = config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION))) {
453 break;
456 if (s->stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) {
457 s->stream_request_body |= FDEVENT_STREAM_REQUEST;
459 if (s->stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) {
460 s->stream_response_body |= FDEVENT_STREAM_RESPONSE;
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\"");
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);
485 if (0 == i) {
486 if (!config_http_parseopts(srv, http_parseopts)) {
487 ret = HANDLER_ERROR;
488 break;
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;
507 break;
508 default:
509 break;
511 if (HANDLER_ERROR == ret) break;
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;
521 #endif
523 array_free(http_parseopts);
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;
535 if (s->log_request_handling || s->log_request_header)
536 srv->srvconf.log_request_header_on_error = 1;
539 if (0 != chunk_sz) {
540 chunkqueue_set_chunk_size(chunk_sz);
543 if (0 != stat_cache_choose_engine(srv, stat_cache_string)) {
544 ret = HANDLER_ERROR;
546 buffer_free(stat_cache_string);
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;
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;
569 /* prepend default modules */
570 for (i = 0; i < srv->srvconf.modules->used; i++) {
571 ds = (data_string *)srv->srvconf.modules->data[i];
573 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_indexfile"))) {
574 prepend_mod_indexfile = 0;
577 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_staticfile"))) {
578 append_mod_staticfile = 0;
581 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_dirlisting"))) {
582 append_mod_dirlisting = 0;
585 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_openssl"))) {
586 append_mod_openssl = 0;
589 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_file"))) {
590 append_mod_authn_file = 0;
593 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_ldap"))) {
594 append_mod_authn_ldap = 0;
597 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_mysql"))) {
598 append_mod_authn_mysql = 0;
601 if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_auth"))) {
602 contains_mod_auth = 1;
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;
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"));
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));
627 array_free(srv->srvconf.modules);
628 srv->srvconf.modules = modules;
631 /* append default modules */
632 if (append_mod_dirlisting) {
633 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting"));
636 if (append_mod_staticfile) {
637 array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile"));
640 if (append_mod_openssl) {
641 #ifdef USE_OPENSSL_CRYPTO
642 config_warn_openssl_module(srv);
643 #endif
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"));
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
658 if (append_mod_authn_mysql) {
659 #if defined(HAVE_MYSQL)
660 config_warn_authn_module(srv, CONST_STR_LEN("mysql"));
661 #endif
666 return ret;
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];
675 PATCH(http_parseopts);
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);
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);
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);
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);
714 PATCH(etag_use_inode);
715 PATCH(etag_use_mtime);
716 PATCH(etag_use_size);
718 return 0;
721 int config_patch_connection(server *srv, connection *con) {
722 size_t i, j;
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];
729 /* condition didn't match */
730 if (!config_check_cond(srv, con, dc)) continue;
732 /* merge config */
733 for (j = 0; j < dc->value->used; j++) {
734 data_unset *du = dc->value->data[j];
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);
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);
814 return 0;
816 #undef PATCH
818 typedef struct {
819 int foo;
820 int bar;
822 const buffer *source;
823 const char *input;
824 size_t offset;
825 size_t size;
827 int line_pos;
828 int line;
830 int in_key;
831 int in_brace;
832 int in_cond;
833 int simulate_eol;
834 } tokenizer_t;
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);
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;
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;
860 t->in_key = 1;
861 t->in_brace = 0;
862 t->in_cond = 0;
863 return 0;
866 static int tokenizer_close(server *srv, tokenizer_t *t) {
867 UNUSED(srv);
869 buffer_free(t->file);
870 return stream_close(&(t->s));
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 ++;
880 t->offset ++;
881 return skipped;
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;
894 static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) {
895 int tid = 0;
896 size_t i;
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)"));
905 while (tid == 0 && t->offset < t->size && t->input[t->offset]) {
906 char c = t->input[t->offset];
907 const char *start = NULL;
909 switch (c) {
910 case '=':
911 if (t->in_brace) {
912 if (t->input[t->offset + 1] == '>') {
913 t->offset += 2;
915 buffer_copy_string_len(token, CONST_STR_LEN("=>"));
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;
925 } else if (t->in_cond) {
926 if (t->input[t->offset + 1] == '=') {
927 t->offset += 2;
929 buffer_copy_string_len(token, CONST_STR_LEN("=="));
931 tid = TK_EQ;
932 } else if (t->input[t->offset + 1] == '~') {
933 t->offset += 2;
935 buffer_copy_string_len(token, CONST_STR_LEN("=~"));
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;
945 t->in_key = 1;
946 t->in_cond = 0;
947 } else if (t->in_key) {
948 tid = TK_ASSIGN;
950 buffer_copy_string_len(token, t->input + t->offset, 1);
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;
962 break;
963 case '!':
964 if (t->in_cond) {
965 if (t->input[t->offset + 1] == '=') {
966 t->offset += 2;
968 buffer_copy_string_len(token, CONST_STR_LEN("!="));
970 tid = TK_NE;
971 } else if (t->input[t->offset + 1] == '~') {
972 t->offset += 2;
974 buffer_copy_string_len(token, CONST_STR_LEN("!~"));
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;
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;
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;
1013 case '#':
1014 t->line_pos += config_skip_comment(t);
1015 break;
1017 case '\t':
1018 case ' ':
1019 t->offset++;
1020 t->line_pos++;
1021 break;
1023 default:
1024 done = 1;
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++;
1035 break;
1036 case ',':
1037 if (t->in_brace > 0) {
1038 tid = TK_COMMA;
1040 buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)"));
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(""));
1051 for (i = 1; t->input[t->offset + i]; i++) {
1052 if (t->input[t->offset + i] == '\\' &&
1053 t->input[t->offset + i + 1] == '"') {
1055 buffer_append_string_len(token, start, t->input + t->offset + i - start);
1057 start = t->input + t->offset + i + 1;
1059 /* skip the " */
1060 i++;
1061 continue;
1065 if (t->input[t->offset + i] == '"') {
1066 tid = TK_STRING;
1068 buffer_append_string_len(token, start, t->input + t->offset + i - start);
1070 break;
1074 if (t->input[t->offset + i] == '\0') {
1075 /* ERROR */
1077 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
1078 "source:", t->source,
1079 "line:", t->line, "pos:", t->line_pos,
1080 "missing closing quote");
1082 return -1;
1085 t->offset += i + 1;
1086 t->line_pos += i + 1;
1088 break;
1089 case '(':
1090 t->offset++;
1091 t->in_brace++;
1093 tid = TK_LPARAN;
1095 buffer_copy_string_len(token, CONST_STR_LEN("("));
1096 break;
1097 case ')':
1098 t->offset++;
1099 t->in_brace--;
1101 tid = TK_RPARAN;
1103 buffer_copy_string_len(token, CONST_STR_LEN(")"));
1104 break;
1105 case '$':
1106 t->offset++;
1108 tid = TK_DOLLAR;
1109 t->in_cond = 1;
1110 t->in_key = 0;
1112 buffer_copy_string_len(token, CONST_STR_LEN("$"));
1114 break;
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("+"));
1126 break;
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 } else {
1134 /* ERROR */
1135 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
1136 "source:", t->source,
1137 "line:", t->line, "pos:", t->line_pos,
1138 "unexpected character ':'");
1139 return -1;
1141 break;
1143 case '{':
1144 t->offset++;
1146 tid = TK_LCURLY;
1148 buffer_copy_string_len(token, CONST_STR_LEN("{"));
1150 break;
1152 case '}':
1153 t->offset++;
1155 tid = TK_RCURLY;
1157 buffer_copy_string_len(token, CONST_STR_LEN("}"));
1159 for (; t->offset < t->size; ++t->offset,++t->line_pos) {
1160 c = t->input[t->offset];
1161 if (c == '\r' || c == '\n') {
1162 break;
1164 else if (c == '#') {
1165 t->line_pos += config_skip_comment(t);
1166 break;
1168 else if (c != ' ' && c != '\t') {
1169 t->simulate_eol = 1;
1170 break;
1171 } /* else (c == ' ' || c == '\t') */
1174 break;
1176 case '[':
1177 t->offset++;
1179 tid = TK_LBRACKET;
1181 buffer_copy_string_len(token, CONST_STR_LEN("["));
1183 break;
1185 case ']':
1186 t->offset++;
1188 tid = TK_RBRACKET;
1190 buffer_copy_string_len(token, CONST_STR_LEN("]"));
1192 break;
1193 case '#':
1194 t->line_pos += config_skip_comment(t);
1196 break;
1197 default:
1198 if (t->in_cond) {
1199 for (i = 0; t->input[t->offset + i] &&
1200 (isalpha((unsigned char)t->input[t->offset + i])
1201 || t->input[t->offset + i] == '_'); ++i);
1203 if (i && t->input[t->offset + i]) {
1204 tid = TK_SRVVARNAME;
1205 buffer_copy_string_len(token, t->input + t->offset, i);
1207 t->offset += i;
1208 t->line_pos += i;
1209 } else {
1210 /* ERROR */
1211 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
1212 "source:", t->source,
1213 "line:", t->line, "pos:", t->line_pos,
1214 "invalid character in condition");
1215 return -1;
1217 } else if (isdigit((unsigned char)c)) {
1218 /* take all digits */
1219 for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++);
1221 /* was there it least a digit ? */
1222 if (i) {
1223 tid = TK_INTEGER;
1225 buffer_copy_string_len(token, t->input + t->offset, i);
1227 t->offset += i;
1228 t->line_pos += i;
1230 } else {
1231 /* the key might consist of [-.0-9a-z] */
1232 for (i = 0; t->input[t->offset + i] &&
1233 (isalnum((unsigned char)t->input[t->offset + i]) ||
1234 t->input[t->offset + i] == '.' ||
1235 t->input[t->offset + i] == '_' || /* for env.* */
1236 t->input[t->offset + i] == '-'
1237 ); i++);
1239 if (i && t->input[t->offset + i]) {
1240 buffer_copy_string_len(token, t->input + t->offset, i);
1242 if (strcmp(token->ptr, "include") == 0) {
1243 tid = TK_INCLUDE;
1244 } else if (strcmp(token->ptr, "include_shell") == 0) {
1245 tid = TK_INCLUDE_SHELL;
1246 } else if (strcmp(token->ptr, "global") == 0) {
1247 tid = TK_GLOBAL;
1248 } else if (strcmp(token->ptr, "else") == 0) {
1249 tid = TK_ELSE;
1250 } else {
1251 tid = TK_LKEY;
1254 t->offset += i;
1255 t->line_pos += i;
1256 } else {
1257 /* ERROR */
1258 log_error_write(srv, __FILE__, __LINE__, "sbsdsds",
1259 "source:", t->source,
1260 "line:", t->line, "pos:", t->line_pos,
1261 "invalid character in variable name");
1262 return -1;
1265 break;
1269 if (tid) {
1270 *token_id = tid;
1271 #if 0
1272 log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd",
1273 "source:", t->source,
1274 "line:", t->line, "pos:", t->line_pos,
1275 token, token->used - 1, tid);
1276 #endif
1278 return 1;
1279 } else if (t->offset < t->size) {
1280 log_error_write(srv, __FILE__, __LINE__, "Dsb", tid, ",", token);
1282 return 0;
1285 static int config_parse(server *srv, config_t *context, tokenizer_t *t) {
1286 void *pParser;
1287 int token_id;
1288 buffer *token, *lasttoken;
1289 int ret;
1291 pParser = configparserAlloc( malloc );
1292 force_assert(pParser);
1293 lasttoken = buffer_init();
1294 token = buffer_init();
1295 while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) {
1296 buffer_copy_buffer(lasttoken, token);
1297 configparser(pParser, token_id, token, context);
1299 token = buffer_init();
1301 buffer_free(token);
1303 if (ret != -1 && context->ok) {
1304 /* add an EOL at EOF, better than say sorry */
1305 configparser(pParser, TK_EOL, buffer_init_string("(EOL)"), context);
1306 if (context->ok) {
1307 configparser(pParser, 0, NULL, context);
1310 configparserFree(pParser, free);
1312 if (ret == -1) {
1313 log_error_write(srv, __FILE__, __LINE__, "sb",
1314 "configfile parser failed at:", lasttoken);
1315 } else if (context->ok == 0) {
1316 log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb",
1317 "source:", t->source,
1318 "line:", t->line, "pos:", t->line_pos,
1319 "parser failed somehow near here:", lasttoken);
1320 ret = -1;
1322 buffer_free(lasttoken);
1324 return ret == -1 ? -1 : 0;
1327 static int tokenizer_init(tokenizer_t *t, const buffer *source, const char *input, size_t size) {
1329 t->source = source;
1330 t->input = input;
1331 t->size = size;
1332 t->offset = 0;
1333 t->line = 1;
1334 t->line_pos = 1;
1336 t->in_key = 1;
1337 t->in_brace = 0;
1338 t->in_cond = 0;
1339 t->simulate_eol = 0;
1340 return 0;
1343 static int config_parse_file_stream(server *srv, config_t *context, const buffer *filename) {
1344 tokenizer_t t;
1345 stream s;
1346 int ret;
1348 if (0 != stream_open(&s, filename)) {
1349 log_error_write(srv, __FILE__, __LINE__, "sbss",
1350 "opening configfile ", filename, "failed:", strerror(errno));
1351 return -1;
1352 } else {
1353 tokenizer_init(&t, filename, s.start, s.size);
1354 ret = config_parse(srv, context, &t);
1357 stream_close(&s);
1358 return ret;
1361 int config_parse_file(server *srv, config_t *context, const char *fn) {
1362 buffer *filename;
1363 size_t i;
1364 int ret = -1;
1365 #ifdef GLOB_BRACE
1366 int flags = GLOB_BRACE;
1367 #else
1368 int flags = 0;
1369 #endif
1370 glob_t gl;
1372 if ((fn[0] == '/' || fn[0] == '\\') ||
1373 (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\')) ||
1374 (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\\'))) {
1375 filename = buffer_init_string(fn);
1376 } else {
1377 filename = buffer_init_buffer(context->basedir);
1378 buffer_append_string(filename, fn);
1381 switch (glob(filename->ptr, flags, NULL, &gl)) {
1382 case 0:
1383 for (i = 0; i < gl.gl_pathc; ++i) {
1384 buffer_copy_string(filename, gl.gl_pathv[i]);
1385 ret = config_parse_file_stream(srv, context, filename);
1386 if (0 != ret) break;
1388 globfree(&gl);
1389 break;
1390 case GLOB_NOMATCH:
1391 if (filename->ptr[strcspn(filename->ptr, "*?[]{}")] != '\0') { /*(contains glob metachars)*/
1392 ret = 0; /* not an error if no files match glob pattern */
1394 else {
1395 log_error_write(srv, __FILE__, __LINE__, "sb", "include file not found: ", filename);
1397 break;
1398 case GLOB_ABORTED:
1399 case GLOB_NOSPACE:
1400 log_error_write(srv, __FILE__, __LINE__, "sbss", "glob()", filename, "failed:", strerror(errno));
1401 break;
1404 buffer_free(filename);
1405 return ret;
1408 #ifdef __CYGWIN__
1410 static char* getCWD(char *buf, size_t sz) {
1411 if (NULL == getcwd(buf, sz)) {
1412 return NULL;
1414 for (size_t i = 0; buf[i]; ++i) {
1415 if (buf[i] == '\\') buf[i] = '/';
1417 return buf;
1420 #define getcwd(buf, sz) getCWD((buf),(sz))
1422 #endif /* __CYGWIN__ */
1424 int config_parse_cmd(server *srv, config_t *context, const char *cmd) {
1425 int ret = 0;
1426 int fds[2];
1427 char oldpwd[PATH_MAX];
1429 if (NULL == getcwd(oldpwd, sizeof(oldpwd))) {
1430 log_error_write(srv, __FILE__, __LINE__, "s",
1431 "cannot get cwd", strerror(errno));
1432 return -1;
1435 if (!buffer_string_is_empty(context->basedir)) {
1436 if (0 != chdir(context->basedir->ptr)) {
1437 log_error_write(srv, __FILE__, __LINE__, "sbs",
1438 "cannot change directory to", context->basedir, strerror(errno));
1439 return -1;
1443 if (pipe(fds)) {
1444 log_error_write(srv, __FILE__, __LINE__, "ss",
1445 "pipe failed: ", strerror(errno));
1446 ret = -1;
1448 else {
1449 char *shell = getenv("SHELL");
1450 char *args[4];
1451 pid_t pid;
1452 *(const char **)&args[0] = shell ? shell : "/bin/sh";
1453 *(const char **)&args[1] = "-c";
1454 *(const char **)&args[2] = cmd;
1455 args[3] = NULL;
1457 fdevent_setfd_cloexec(fds[0]);
1458 pid = fdevent_fork_execve(args[0], args, NULL, -1, fds[1], -1, -1);
1459 if (-1 == pid) {
1460 log_error_write(srv, __FILE__, __LINE__, "SSss",
1461 "fork/exec(", cmd, "):", strerror(errno));
1462 ret = -1;
1464 else {
1465 ssize_t rd;
1466 pid_t wpid;
1467 int wstatus;
1468 buffer *out = buffer_init();
1469 close(fds[1]);
1470 fds[1] = -1;
1471 do {
1472 rd = read(fds[0], buffer_string_prepare_append(out, 1023), 1023);
1473 if (rd >= 0) buffer_commit(out, (size_t)rd);
1474 } while (rd > 0 || (-1 == rd && errno == EINTR));
1475 if (0 != rd) {
1476 log_error_write(srv, __FILE__, __LINE__, "SSss",
1477 "read \"", cmd, "\" failed:", strerror(errno));
1478 ret = -1;
1480 close(fds[0]);
1481 fds[0] = -1;
1482 while (-1 == (wpid = waitpid(pid, &wstatus, 0)) && errno == EINTR) ;
1483 if (wpid != pid) {
1484 log_error_write(srv, __FILE__, __LINE__, "SSss",
1485 "waitpid \"", cmd, "\" failed:", strerror(errno));
1486 ret = -1;
1488 if (0 != wstatus) {
1489 log_error_write(srv, __FILE__, __LINE__, "SSsd",
1490 "command \"", cmd, "\" exited non-zero:", WEXITSTATUS(wstatus));
1491 ret = -1;
1494 if (-1 != ret) {
1495 buffer *source = buffer_init_string(cmd);
1496 tokenizer_t t;
1497 tokenizer_init(&t, source, CONST_BUF_LEN(out));
1498 ret = config_parse(srv, context, &t);
1499 buffer_free(source);
1501 buffer_free(out);
1503 if (-1 != fds[0]) close(fds[0]);
1504 if (-1 != fds[1]) close(fds[1]);
1507 if (0 != chdir(oldpwd)) {
1508 log_error_write(srv, __FILE__, __LINE__, "sss",
1509 "cannot change directory to", oldpwd, strerror(errno));
1510 ret = -1;
1512 return ret;
1515 static void context_init(server *srv, config_t *context) {
1516 context->srv = srv;
1517 context->ok = 1;
1518 vector_config_weak_init(&context->configs_stack);
1519 context->basedir = buffer_init();
1522 static void context_free(config_t *context) {
1523 vector_config_weak_clear(&context->configs_stack);
1524 buffer_free(context->basedir);
1527 int config_read(server *srv, const char *fn) {
1528 config_t context;
1529 data_config *dc;
1530 buffer *dcwd;
1531 int ret;
1532 char *pos;
1533 buffer *filename;
1535 context_init(srv, &context);
1536 context.all_configs = srv->config_context;
1538 #ifdef __WIN32
1539 pos = strrchr(fn, '\\');
1540 #else
1541 pos = strrchr(fn, '/');
1542 #endif
1543 if (pos) {
1544 buffer_copy_string_len(context.basedir, fn, pos - fn + 1);
1547 dc = data_config_init();
1548 buffer_copy_string_len(dc->key, CONST_STR_LEN("global"));
1550 force_assert(context.all_configs->used == 0);
1551 dc->context_ndx = context.all_configs->used;
1552 array_insert_unique(context.all_configs, (data_unset *)dc);
1553 context.current = dc;
1555 /* default context */
1556 *array_get_int_ptr(dc->value, CONST_STR_LEN("var.PID")) = getpid();
1558 dcwd = srv->tmp_buf;
1559 buffer_string_prepare_copy(dcwd, PATH_MAX-1);
1560 if (NULL != getcwd(dcwd->ptr, buffer_string_space(dcwd)+1)) {
1561 buffer_commit(dcwd, strlen(dcwd->ptr));
1562 array_set_key_value(dc->value, CONST_STR_LEN("var.CWD"), CONST_BUF_LEN(dcwd));
1565 filename = buffer_init_string(fn);
1566 ret = config_parse_file_stream(srv, &context, filename);
1567 buffer_free(filename);
1569 /* remains nothing if parser is ok */
1570 force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used));
1571 context_free(&context);
1573 if (0 != ret) {
1574 return ret;
1577 if (0 != config_insert(srv)) {
1578 return -1;
1581 return 0;
1584 int config_set_defaults(server *srv) {
1585 size_t i;
1586 specific_config *s = srv->config_storage[0];
1587 struct stat st1, st2;
1589 force_assert(sizeof(((connection *)0)->conditional_is_valid) >= COMP_LAST_ELEMENT);
1591 if (0 != fdevent_config(srv)) return -1;
1593 if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
1594 if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) {
1595 log_error_write(srv, __FILE__, __LINE__, "sb",
1596 "server.chroot doesn't exist:", srv->srvconf.changeroot);
1597 return -1;
1599 if (!S_ISDIR(st1.st_mode)) {
1600 log_error_write(srv, __FILE__, __LINE__, "sb",
1601 "server.chroot isn't a directory:", srv->srvconf.changeroot);
1602 return -1;
1606 if (!srv->srvconf.upload_tempdirs->used) {
1607 const char *tmpdir = getenv("TMPDIR");
1608 if (NULL == tmpdir) tmpdir = "/var/tmp";
1609 array_insert_value(srv->srvconf.upload_tempdirs, tmpdir, strlen(tmpdir));
1612 if (srv->srvconf.upload_tempdirs->used) {
1613 buffer * const b = srv->tmp_buf;
1614 size_t len;
1615 buffer_clear(b);
1616 if (!buffer_string_is_empty(srv->srvconf.changeroot)) {
1617 buffer_copy_buffer(b, srv->srvconf.changeroot);
1618 buffer_append_slash(b);
1620 len = buffer_string_length(b);
1622 for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) {
1623 const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i];
1624 buffer_string_set_length(b, len); /*(truncate)*/
1625 buffer_append_string_buffer(b, ds->value);
1626 if (-1 == stat(b->ptr, &st1)) {
1627 log_error_write(srv, __FILE__, __LINE__, "sb",
1628 "server.upload-dirs doesn't exist:", b);
1629 } else if (!S_ISDIR(st1.st_mode)) {
1630 log_error_write(srv, __FILE__, __LINE__, "sb",
1631 "server.upload-dirs isn't a directory:", b);
1636 chunkqueue_set_tempdirs_default(
1637 srv->srvconf.upload_tempdirs,
1638 srv->srvconf.upload_temp_file_size);
1640 if (buffer_string_is_empty(s->document_root)) {
1641 log_error_write(srv, __FILE__, __LINE__, "s",
1642 "a default document-root has to be set");
1644 return -1;
1647 buffer_copy_buffer(srv->tmp_buf, s->document_root);
1649 buffer_to_lower(srv->tmp_buf);
1651 if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */
1652 s->force_lowercase_filenames = 0; /* default to 0 */
1654 if (0 == stat(srv->tmp_buf->ptr, &st1)) {
1655 int is_lower = 0;
1657 is_lower = buffer_is_equal(srv->tmp_buf, s->document_root);
1659 /* lower-case existed, check upper-case */
1660 buffer_copy_buffer(srv->tmp_buf, s->document_root);
1662 buffer_to_upper(srv->tmp_buf);
1664 /* we have to handle the special case that upper and lower-casing results in the same filename
1665 * as in server.document-root = "/" or "/12345/" */
1667 if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) {
1668 /* lower-casing and upper-casing didn't result in
1669 * an other filename, no need to stat(),
1670 * just assume it is case-sensitive. */
1672 s->force_lowercase_filenames = 0;
1673 } else if (0 == stat(srv->tmp_buf->ptr, &st2)) {
1675 /* upper case exists too, doesn't the FS handle this ? */
1677 /* upper and lower have the same inode -> case-insensitve FS */
1679 if (st1.st_ino == st2.st_ino) {
1680 /* upper and lower have the same inode -> case-insensitve FS */
1682 s->force_lowercase_filenames = 1;
1688 if (srv->srvconf.port == 0) {
1689 srv->srvconf.port = s->ssl_enabled ? 443 : 80;
1692 return 0;