1 #include "response.h"
2 #include "keyvalue.h"
3 #include "log.h"
4 #include "stat_cache.h"
5 #include "chunk.h"
6
7 #include "configfile.h"
8 #include "connections.h"
9
10 #include "plugin.h"
11
12 #include <sys/types.h>
13 #include <sys/stat.h>
14
15 #include <limits.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <time.h>
21 #include <unistd.h>
22 #include <ctype.h>
23 #include <assert.h>
24
25 #include <stdio.h>
26
27 #include "sys-socket.h"
28 #include "version.h"
29
http_response_write_header(server * srv,connection * con)30 int http_response_write_header(server *srv, connection *con) {
31 buffer *b;
32 size_t i;
33 int have_date = 0;
34 int have_server = 0;
35
36 b = chunkqueue_get_prepend_buffer(con->write_queue);
37
38 if (con->request.http_version == HTTP_VERSION_1_1) {
39 buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 "));
40 } else {
41 buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 "));
42 }
43 buffer_append_long(b, con->http_status);
44 buffer_append_string_len(b, CONST_STR_LEN(" "));
45 buffer_append_string(b, get_http_status_name(con->http_status));
46
47 /* disable keep-alive if requested */
48 if ((con->conf.infinite_keep_alive_requests == 0 &&
49 con->request_count > con->conf.max_keep_alive_requests) ||
50 0 == con->conf.max_keep_alive_idle) {
51 con->keep_alive = 0;
52 } else {
53 con->keep_alive_idle = con->conf.max_keep_alive_idle;
54 }
55
56 if (con->request.http_version != HTTP_VERSION_1_1 || con->keep_alive == 0) {
57 if (con->keep_alive) {
58 response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive"));
59 } else {
60 response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("close"));
61 }
62 }
63
64 if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) {
65 response_header_overwrite(srv, con, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked"));
66 }
67
68
69 /* add all headers */
70 for (i = 0; i < con->response.headers->used; i++) {
71 data_string *ds;
72
73 ds = (data_string *)con->response.headers->data[i];
74
75 if (ds->value->used && ds->key->used &&
76 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) &&
77 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) {
78 if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1;
79 if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1;
80 if (0 == strcasecmp(ds->key->ptr, "Content-Encoding") && 304 == con->http_status) continue;
81
82 buffer_append_string_len(b, CONST_STR_LEN("\r\n"));
83 buffer_append_string_buffer(b, ds->key);
84 buffer_append_string_len(b, CONST_STR_LEN(": "));
85 #if 0
86 /**
87 * the value might contain newlines, encode them with at least one white-space
88 */
89 buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_HTTP_HEADER);
90 #else
91 buffer_append_string_buffer(b, ds->value);
92 #endif
93 }
94 }
95
96 if (!have_date) {
97 /* HTTP/1.1 requires a Date: header */
98 buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: "));
99
100 /* cache the generated timestamp */
101 if (srv->cur_ts != srv->last_generated_date_ts) {
102 buffer_prepare_copy(srv->ts_date_str, 255);
103
104 strftime(srv->ts_date_str->ptr, srv->ts_date_str->size - 1,
105 "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts)));
106
107 srv->ts_date_str->used = strlen(srv->ts_date_str->ptr) + 1;
108
109 srv->last_generated_date_ts = srv->cur_ts;
110 }
111
112 buffer_append_string_buffer(b, srv->ts_date_str);
113 }
114
115 if (!have_server) {
116 if (buffer_is_empty(con->conf.server_tag)) {
117 buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_DESC));
118 } else if (con->conf.server_tag->used > 1) {
119 buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: "));
120 buffer_append_string_encoded(b, CONST_BUF_LEN(con->conf.server_tag), ENCODING_HTTP_HEADER);
121 }
122 }
123
124 buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
125
126
127 con->bytes_header = b->used - 1;
128
129 if (con->conf.log_response_header) {
130 log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b);
131 }
132
133 return 0;
134 }
135
136 #ifdef USE_OPENSSL
https_add_ssl_entries(connection * con)137 static void https_add_ssl_entries(connection *con) {
138 X509 *xs;
139 X509_NAME *xn;
140 X509_NAME_ENTRY *xe;
141 int i, nentries;
142
143 if (
144 SSL_get_verify_result(con->ssl) != X509_V_OK
145 || !(xs = SSL_get_peer_certificate(con->ssl))
146 ) {
147 return;
148 }
149
150 xn = X509_get_subject_name(xs);
151 for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) {
152 int xobjnid;
153 const char * xobjsn;
154 data_string *envds;
155
156 if (!(xe = X509_NAME_get_entry(xn, i))) {
157 continue;
158 }
159 xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe));
160 xobjsn = OBJ_nid2sn(xobjnid);
161 if (!xobjsn) {
162 continue;
163 }
164
165 if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
166 envds = data_string_init();
167 }
168 buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_S_DN_"));
169 buffer_append_string(envds->key, xobjsn);
170 buffer_copy_string_len(
171 envds->value,
172 (const char *)xe->value->data, xe->value->length
173 );
174 /* pick one of the exported values as "authed user", for example
175 * ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID" or "SSL_CLIENT_S_DN_emailAddress"
176 */
177 if (buffer_is_equal(con->conf.ssl_verifyclient_username, envds->key)) {
178 buffer_copy_string_buffer(con->authed_user, envds->value);
179 }
180 array_insert_unique(con->environment, (data_unset *)envds);
181 }
182 if (con->conf.ssl_verifyclient_export_cert) {
183 BIO *bio;
184 if (NULL != (bio = BIO_new(BIO_s_mem()))) {
185 data_string *envds;
186 int n;
187
188 PEM_write_bio_X509(bio, xs);
189 n = BIO_pending(bio);
190
191 if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
192 envds = data_string_init();
193 }
194
195 buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_CERT"));
196 buffer_prepare_copy(envds->value, n+1);
197 BIO_read(bio, envds->value->ptr, n);
198 BIO_free(bio);
199 envds->value->ptr[n] = '\0';
200 envds->value->used = n+1;
201 array_insert_unique(con->environment, (data_unset *)envds);
202 }
203 }
204 X509_free(xs);
205 }
206 #endif
207
208
http_response_prepare(server * srv,connection * con)209 handler_t http_response_prepare(server *srv, connection *con) {
210 handler_t r;
211
212 /* looks like someone has already done a decision */
213 if (con->mode == DIRECT &&
214 (con->http_status != 0 && con->http_status != 200)) {
215 /* remove a packets in the queue */
216 if (con->file_finished == 0) {
217 chunkqueue_reset(con->write_queue);
218 }
219
220 return HANDLER_FINISHED;
221 }
222
223 /* no decision yet, build conf->filename */
224 if (con->mode == DIRECT && con->physical.path->used == 0) {
225 char *qstr;
226
227 /* we only come here when we have the parse the full request again
228 *
229 * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a
230 * problem here as mod_setenv might get called multiple times
231 *
232 * fastcgi-auth might lead to a COMEBACK too
233 * fastcgi again dead server too
234 *
235 * mod_compress might add headers twice too
236 *
237 * */
238
239 config_cond_cache_reset(srv, con);
240 config_setup_connection(srv, con); /* Perhaps this could be removed at other places. */
241
242 if (con->conf.log_condition_handling) {
243 log_error_write(srv, __FILE__, __LINE__, "s", "run condition");
244 }
245 config_patch_connection(srv, con, COMP_SERVER_SOCKET); /* SERVERsocket */
246
247 /**
248 * prepare strings
249 *
250 * - uri.path_raw
251 * - uri.path (secure)
252 * - uri.query
253 *
254 */
255
256 /**
257 * Name according to RFC 2396
258 *
259 * - scheme
260 * - authority
261 * - path
262 * - query
263 *
264 * (scheme)://(authority)(path)?(query)#fragment
265 *
266 *
267 */
268
269 if (con->conf.is_ssl) {
270 buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https"));
271 } else {
272 buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http"));
273 }
274 buffer_copy_string_buffer(con->uri.authority, con->request.http_host);
275 buffer_to_lower(con->uri.authority);
276
277 config_patch_connection(srv, con, COMP_HTTP_SCHEME); /* Scheme: */
278 config_patch_connection(srv, con, COMP_HTTP_HOST); /* Host: */
279 config_patch_connection(srv, con, COMP_HTTP_REMOTE_IP); /* Client-IP */
280 config_patch_connection(srv, con, COMP_HTTP_REFERER); /* Referer: */
281 config_patch_connection(srv, con, COMP_HTTP_USER_AGENT);/* User-Agent: */
282 config_patch_connection(srv, con, COMP_HTTP_LANGUAGE); /* Accept-Language: */
283 config_patch_connection(srv, con, COMP_HTTP_COOKIE); /* Cookie: */
284 config_patch_connection(srv, con, COMP_HTTP_REQUEST_METHOD); /* REQUEST_METHOD */
285
286 /** their might be a fragment which has to be cut away */
287 if (NULL != (qstr = strchr(con->request.uri->ptr, '#'))) {
288 con->request.uri->used = qstr - con->request.uri->ptr;
289 con->request.uri->ptr[con->request.uri->used++] = '\0';
290 }
291
292 /** extract query string from request.uri */
293 if (NULL != (qstr = strchr(con->request.uri->ptr, '?'))) {
294 buffer_copy_string (con->uri.query, qstr + 1);
295 buffer_copy_string_len(con->uri.path_raw, con->request.uri->ptr, qstr - con->request.uri->ptr);
296 } else {
297 buffer_reset (con->uri.query);
298 buffer_copy_string_buffer(con->uri.path_raw, con->request.uri);
299 }
300
301 if (con->conf.log_request_handling) {
302 log_error_write(srv, __FILE__, __LINE__, "s", "-- splitting Request-URI");
303 log_error_write(srv, __FILE__, __LINE__, "sb", "Request-URI : ", con->request.uri);
304 log_error_write(srv, __FILE__, __LINE__, "sb", "URI-scheme : ", con->uri.scheme);
305 log_error_write(srv, __FILE__, __LINE__, "sb", "URI-authority: ", con->uri.authority);
306 log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path : ", con->uri.path_raw);
307 log_error_write(srv, __FILE__, __LINE__, "sb", "URI-query : ", con->uri.query);
308 }
309
310
311 /**
312 *
313 * call plugins
314 *
315 * - based on the raw URL
316 *
317 */
318
319 switch(r = plugins_call_handle_uri_raw(srv, con)) {
320 case HANDLER_GO_ON:
321 break;
322 case HANDLER_FINISHED:
323 case HANDLER_COMEBACK:
324 case HANDLER_WAIT_FOR_EVENT:
325 case HANDLER_ERROR:
326 return r;
327 default:
328 log_error_write(srv, __FILE__, __LINE__, "sd", "handle_uri_raw: unknown return value", r);
329 break;
330 }
331
332 /* build filename
333 *
334 * - decode url-encodings (e.g. %20 -> ' ')
335 * - remove path-modifiers (e.g. /../)
336 */
337
338
339
340 if (con->request.http_method == HTTP_METHOD_OPTIONS &&
341 con->uri.path_raw->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') {
342 /* OPTIONS * ... */
343 buffer_copy_string_buffer(con->uri.path, con->uri.path_raw);
344 } else {
345 buffer_copy_string_buffer(srv->tmp_buf, con->uri.path_raw);
346 buffer_urldecode_path(srv->tmp_buf);
347 buffer_path_simplify(con->uri.path, srv->tmp_buf);
348 }
349
350 if (con->conf.log_request_handling) {
351 log_error_write(srv, __FILE__, __LINE__, "s", "-- sanatising URI");
352 log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path : ", con->uri.path);
353 }
354
355 #ifdef USE_OPENSSL
356 if (con->conf.is_ssl && con->conf.ssl_verifyclient) {
357 https_add_ssl_entries(con);
358 }
359 #endif
360
361 /**
362 *
363 * call plugins
364 *
365 * - based on the clean URL
366 *
367 */
368
369 config_patch_connection(srv, con, COMP_HTTP_URL); /* HTTPurl */
370 config_patch_connection(srv, con, COMP_HTTP_QUERY_STRING); /* HTTPqs */
371
372 /* do we have to downgrade to 1.0 ? */
373 if (!con->conf.allow_http11) {
374 con->request.http_version = HTTP_VERSION_1_0;
375 }
376
377 switch(r = plugins_call_handle_uri_clean(srv, con)) {
378 case HANDLER_GO_ON:
379 break;
380 case HANDLER_FINISHED:
381 case HANDLER_COMEBACK:
382 case HANDLER_WAIT_FOR_EVENT:
383 case HANDLER_ERROR:
384 return r;
385 default:
386 log_error_write(srv, __FILE__, __LINE__, "");
387 break;
388 }
389
390 if (con->request.http_method == HTTP_METHOD_OPTIONS &&
391 con->uri.path->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') {
392 /* option requests are handled directly without checking of the path */
393
394 response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST"));
395
396 con->http_status = 200;
397 con->file_finished = 1;
398
399 return HANDLER_FINISHED;
400 }
401
402 /***
403 *
404 * border
405 *
406 * logical filename (URI) becomes a physical filename here
407 *
408 *
409 *
410 */
411
412
413
414
415 /* 1. stat()
416 * ... ISREG() -> ok, go on
417 * ... ISDIR() -> index-file -> redirect
418 *
419 * 2. pathinfo()
420 * ... ISREG()
421 *
422 * 3. -> 404
423 *
424 */
425
426 /*
427 * SEARCH DOCUMENT ROOT
428 */
429
430 /* set a default */
431
432 buffer_copy_string_buffer(con->physical.doc_root, con->conf.document_root);
433 buffer_copy_string_buffer(con->physical.rel_path, con->uri.path);
434
435 #if defined(__WIN32) || defined(__CYGWIN__)
436 /* strip dots from the end and spaces
437 *
438 * windows/dos handle those filenames as the same file
439 *
440 * foo == foo. == foo..... == "foo... " == "foo.. ./"
441 *
442 * This will affect in some cases PATHINFO
443 *
444 * on native windows we could prepend the filename with \\?\ to circumvent
445 * this behaviour. I have no idea how to push this through cygwin
446 *
447 * */
448
449 if (con->physical.rel_path->used > 1) {
450 buffer *b = con->physical.rel_path;
451 size_t i;
452
453 if (b->used > 2 &&
454 b->ptr[b->used-2] == '/' &&
455 (b->ptr[b->used-3] == ' ' ||
456 b->ptr[b->used-3] == '.')) {
457 b->ptr[b->used--] = '\0';
458 }
459
460 for (i = b->used - 2; b->used > 1; i--) {
461 if (b->ptr[i] == ' ' ||
462 b->ptr[i] == '.') {
463 b->ptr[b->used--] = '\0';
464 } else {
465 break;
466 }
467 }
468 }
469 #endif
470
471 if (con->conf.log_request_handling) {
472 log_error_write(srv, __FILE__, __LINE__, "s", "-- before doc_root");
473 log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root);
474 log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path);
475 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
476 }
477 /* the docroot plugin should set the doc_root and might also set the physical.path
478 * for us (all vhost-plugins are supposed to set the doc_root)
479 * */
480 switch(r = plugins_call_handle_docroot(srv, con)) {
481 case HANDLER_GO_ON:
482 break;
483 case HANDLER_FINISHED:
484 case HANDLER_COMEBACK:
485 case HANDLER_WAIT_FOR_EVENT:
486 case HANDLER_ERROR:
487 return r;
488 default:
489 log_error_write(srv, __FILE__, __LINE__, "");
490 break;
491 }
492
493 /* MacOS X and Windows can't distiguish between upper and lower-case
494 *
495 * convert to lower-case
496 */
497 if (con->conf.force_lowercase_filenames) {
498 buffer_to_lower(con->physical.rel_path);
499 }
500
501 /* the docroot plugins might set the servername, if they don't we take http-host */
502 if (buffer_is_empty(con->server_name)) {
503 buffer_copy_string_buffer(con->server_name, con->uri.authority);
504 }
505
506 /**
507 * create physical filename
508 * -> physical.path = docroot + rel_path
509 *
510 */
511
512 buffer_copy_string_buffer(con->physical.path, con->physical.doc_root);
513 BUFFER_APPEND_SLASH(con->physical.path);
514 buffer_copy_string_buffer(con->physical.basedir, con->physical.path);
515 if (con->physical.rel_path->used &&
516 con->physical.rel_path->ptr[0] == '/') {
517 buffer_append_string_len(con->physical.path, con->physical.rel_path->ptr + 1, con->physical.rel_path->used - 2);
518 } else {
519 buffer_append_string_buffer(con->physical.path, con->physical.rel_path);
520 }
521
522 if (con->conf.log_request_handling) {
523 log_error_write(srv, __FILE__, __LINE__, "s", "-- after doc_root");
524 log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root);
525 log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path);
526 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
527 }
528
529 switch(r = plugins_call_handle_physical(srv, con)) {
530 case HANDLER_GO_ON:
531 break;
532 case HANDLER_FINISHED:
533 case HANDLER_COMEBACK:
534 case HANDLER_WAIT_FOR_EVENT:
535 case HANDLER_ERROR:
536 return r;
537 default:
538 log_error_write(srv, __FILE__, __LINE__, "");
539 break;
540 }
541
542 if (con->conf.log_request_handling) {
543 log_error_write(srv, __FILE__, __LINE__, "s", "-- logical -> physical");
544 log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root);
545 log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path);
546 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
547 }
548 }
549
550 /*
551 * Noone catched away the file from normal path of execution yet (like mod_access)
552 *
553 * Go on and check of the file exists at all
554 */
555
556 if (con->mode == DIRECT) {
557 char *slash = NULL;
558 char *pathinfo = NULL;
559 int found = 0;
560 stat_cache_entry *sce = NULL;
561
562 if (con->conf.log_request_handling) {
563 log_error_write(srv, __FILE__, __LINE__, "s", "-- handling physical path");
564 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
565 }
566
567 if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
568 /* file exists */
569
570 if (con->conf.log_request_handling) {
571 log_error_write(srv, __FILE__, __LINE__, "s", "-- file found");
572 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
573 }
574 #ifdef HAVE_LSTAT
575 if ((sce->is_symlink != 0) && !con->conf.follow_symlink) {
576 con->http_status = 403;
577
578 if (con->conf.log_request_handling) {
579 log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction");
580 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
581 }
582
583 buffer_reset(con->physical.path);
584 return HANDLER_FINISHED;
585 };
586 #endif
587 if (S_ISDIR(sce->st.st_mode)) {
588 if (con->uri.path->ptr[con->uri.path->used - 2] != '/') {
589 /* redirect to .../ */
590
591 http_response_redirect_to_directory(srv, con);
592
593 return HANDLER_FINISHED;
594 }
595 #ifdef HAVE_LSTAT
596 } else if (!S_ISREG(sce->st.st_mode) && !sce->is_symlink) {
597 #else
598 } else if (!S_ISREG(sce->st.st_mode)) {
599 #endif
600 /* any special handling of non-reg files ?*/
601
602
603 }
604 } else {
605 switch (errno) {
606 case EACCES:
607 con->http_status = 403;
608
609 if (con->conf.log_request_handling) {
610 log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied");
611 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
612 }
613
614 buffer_reset(con->physical.path);
615 return HANDLER_FINISHED;
616 case ENAMETOOLONG:
617 /* file name to be read was too long. return 404 */
618 case ENOENT:
619 con->http_status = 404;
620
621 if (con->conf.log_request_handling) {
622 log_error_write(srv, __FILE__, __LINE__, "s", "-- file not found");
623 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
624 }
625
626 buffer_reset(con->physical.path);
627 return HANDLER_FINISHED;
628 case ENOTDIR:
629 /* PATH_INFO ! :) */
630 break;
631 default:
632 /* we have no idea what happend. let's tell the user so. */
633 con->http_status = 500;
634 buffer_reset(con->physical.path);
635
636 log_error_write(srv, __FILE__, __LINE__, "ssbsb",
637 "file not found ... or so: ", strerror(errno),
638 con->uri.path,
639 "->", con->physical.path);
640
641 return HANDLER_FINISHED;
642 }
643
644 /* not found, perhaps PATHINFO */
645
646 buffer_copy_string_buffer(srv->tmp_buf, con->physical.path);
647
648 do {
649 if (slash) {
650 buffer_copy_string_len(con->physical.path, srv->tmp_buf->ptr, slash - srv->tmp_buf->ptr);
651 } else {
652 buffer_copy_string_buffer(con->physical.path, srv->tmp_buf);
653 }
654
655 if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
656 found = S_ISREG(sce->st.st_mode);
657 break;
658 }
659
660 if (pathinfo != NULL) {
661 *pathinfo = '\0';
662 }
663 slash = strrchr(srv->tmp_buf->ptr, '/');
664
665 if (pathinfo != NULL) {
666 /* restore '/' */
667 *pathinfo = '/';
668 }
669
670 if (slash) pathinfo = slash;
671 } while ((found == 0) && (slash != NULL) && ((size_t)(slash - srv->tmp_buf->ptr) > (con->physical.basedir->used - 2)));
672
673 if (found == 0) {
674 /* no it really doesn't exists */
675 con->http_status = 404;
676
677 if (con->conf.log_file_not_found) {
678 log_error_write(srv, __FILE__, __LINE__, "sbsb",
679 "file not found:", con->uri.path,
680 "->", con->physical.path);
681 }
682
683 buffer_reset(con->physical.path);
684
685 return HANDLER_FINISHED;
686 }
687
688 #ifdef HAVE_LSTAT
689 if ((sce->is_symlink != 0) && !con->conf.follow_symlink) {
690 con->http_status = 403;
691
692 if (con->conf.log_request_handling) {
693 log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction");
694 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
695 }
696
697 buffer_reset(con->physical.path);
698 return HANDLER_FINISHED;
699 };
700 #endif
701
702 /* we have a PATHINFO */
703 if (pathinfo) {
704 buffer_copy_string(con->request.pathinfo, pathinfo);
705
706 /*
707 * shorten uri.path
708 */
709
710 con->uri.path->used -= strlen(pathinfo);
711 con->uri.path->ptr[con->uri.path->used - 1] = '\0';
712 }
713
714 if (con->conf.log_request_handling) {
715 log_error_write(srv, __FILE__, __LINE__, "s", "-- after pathinfo check");
716 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
717 log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path);
718 log_error_write(srv, __FILE__, __LINE__, "sb", "Pathinfo :", con->request.pathinfo);
719 }
720 }
721
722 if (con->conf.log_request_handling) {
723 log_error_write(srv, __FILE__, __LINE__, "s", "-- handling subrequest");
724 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
725 }
726
727 /* call the handlers */
728 switch(r = plugins_call_handle_subrequest_start(srv, con)) {
729 case HANDLER_GO_ON:
730 /* request was not handled */
731 break;
732 case HANDLER_FINISHED:
733 default:
734 if (con->conf.log_request_handling) {
735 log_error_write(srv, __FILE__, __LINE__, "s", "-- subrequest finished");
736 }
737
738 /* something strange happend */
739 return r;
740 }
741
742 /* if we are still here, no one wanted the file, status 403 is ok I think */
743
744 if (con->mode == DIRECT && con->http_status == 0) {
745 switch (con->request.http_method) {
746 case HTTP_METHOD_OPTIONS:
747 con->http_status = 200;
748 break;
749 default:
750 con->http_status = 403;
751 }
752
753 return HANDLER_FINISHED;
754 }
755
756 }
757
758 switch(r = plugins_call_handle_subrequest(srv, con)) {
759 case HANDLER_GO_ON:
760 /* request was not handled, looks like we are done */
761 return HANDLER_FINISHED;
762 case HANDLER_FINISHED:
763 /* request is finished */
764 default:
765 /* something strange happend */
766 return r;
767 }
768
769 /* can't happen */
770 return HANDLER_COMEBACK;
771 }
772
773
774
775