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