Merge struct file_info into struct request
[MonkeyD.git] / src / http.c
blob7d174f8429abb0342cdc518c98fe7cf798b3feb2
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
3 /* Monkey HTTP Daemon
4 * ------------------
5 * Copyright (C) 2001-2008, Eduardo Silva P.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
30 #include "monkey.h"
31 #include "memory.h"
32 #include "http.h"
33 #include "http_status.h"
34 #include "file.h"
35 #include "utils.h"
36 #include "config.h"
37 #include "cgi.h"
38 #include "str.h"
39 #include "method.h"
40 #include "socket.h"
41 #include "mimetype.h"
42 #include "logfile.h"
43 #include "header.h"
44 #include "plugin.h"
46 int mk_http_method_check(mk_pointer method)
48 if(strncasecmp(method.data,
49 HTTP_METHOD_GET_STR,
50 method.len)==0)
52 return HTTP_METHOD_GET;
55 if(strncasecmp(method.data,
56 HTTP_METHOD_POST_STR,
57 method.len)==0)
59 return HTTP_METHOD_POST;
62 if(strncasecmp(method.data,
63 HTTP_METHOD_HEAD_STR,
64 method.len)==0)
66 return HTTP_METHOD_HEAD;
69 return METHOD_NOT_FOUND;
72 mk_pointer mk_http_method_check_str(int method)
74 switch(method){
75 case HTTP_METHOD_GET:
76 return mk_http_method_get_p;
78 case HTTP_METHOD_POST:
79 return mk_http_method_post_p;
81 case HTTP_METHOD_HEAD:
82 return mk_http_method_head_p;
84 return mk_http_method_null_p;
87 int mk_http_method_get(char *body)
89 int int_method, pos = 0;
90 int max_len_method = 5;
91 mk_pointer method;
93 // mk_pointer_reset(&method);
95 pos = mk_string_search(body, " ");
96 if(pos<=2 || pos>=max_len_method){
97 return METHOD_NOT_FOUND;
100 method.data = body;
101 method.len = (unsigned long) pos;
103 int_method = mk_http_method_check(method);
105 return int_method;
108 int mk_http_protocol_check(char *protocol)
110 if(strcasecmp(protocol, HTTP_PROTOCOL_11_STR)==0)
112 return HTTP_PROTOCOL_11;
114 if(strcasecmp(protocol, HTTP_PROTOCOL_10_STR)==0)
116 return HTTP_PROTOCOL_10;
118 if(strcasecmp(protocol, HTTP_PROTOCOL_09_STR)==0)
120 return HTTP_PROTOCOL_09;
123 return HTTP_PROTOCOL_UNKNOWN;
126 mk_pointer mk_http_protocol_check_str(int protocol)
128 if(protocol==HTTP_PROTOCOL_11)
130 return mk_http_protocol_11_p;
132 if(protocol==HTTP_PROTOCOL_10)
134 return mk_http_protocol_10_p;
136 if(protocol==HTTP_PROTOCOL_09)
138 return mk_http_protocol_09_p;
141 return mk_http_protocol_null_p;
144 int mk_http_init(struct client_request *cr, struct request *sr)
146 int debug_error=0, bytes=0;
147 char *location=0, *real_location=0; /* ruta para redireccion */
148 struct mimetype *mime;
149 mk_pointer gmt_file_unix_time; // gmt time of server file (unix time)
150 unsigned long len;
151 char *palm;
153 /* Normal request default site */
154 if((strcmp(sr->uri_processed,"/"))==0)
156 sr->real_path.data = strdup(sr->host_conf->documentroot.data);
157 sr->real_path.len = sr->host_conf->documentroot.len;
160 if(sr->user_home==VAR_OFF)
162 mk_buffer_cat(&sr->real_path, sr->host_conf->documentroot.data,
163 sr->uri_processed);
166 if(sr->method!=HTTP_METHOD_HEAD){
167 debug_error=1;
170 sr->file_info = mk_file_get_info(sr->real_path.data);
172 if(!sr->file_info){
173 mk_request_error(M_CLIENT_NOT_FOUND, cr, sr,
174 debug_error, sr->log);
175 return -1;
178 /* Check symbolic link file */
179 if(sr->file_info->is_link == MK_FILE_TRUE){
180 if(config->symlink==VAR_OFF){
181 sr->log->final_response=M_CLIENT_FORBIDDEN;
182 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr,
183 debug_error, sr->log);
184 return -1;
186 else{
187 int n;
188 char linked_file[MAX_PATH];
189 n = readlink(sr->real_path.data, linked_file, MAX_PATH);
191 if(Deny_Check(linked_file)==-1) {
192 sr->log->final_response=M_CLIENT_FORBIDDEN;
193 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, debug_error, sr->log);
194 return -1;
201 /* Plugin Stage 40 */
202 mk_plugin_stage_run(MK_PLUGIN_STAGE_40, cr, sr);
204 /* is it a valid directory ? */
205 if(sr->file_info->is_directory == MK_FILE_TRUE) {
206 /* This pointer never must be freed */
207 mk_pointer index_file;
210 We have to check if exist an slash to the end of
211 this string, if doesn't exist we send a redirection header
213 if(sr->uri_processed[strlen(sr->uri_processed) - 1] != '/') {
214 char *host;
215 host = mk_pointer_to_buf(sr->host);
217 m_build_buffer(&location, &len, "%s/", sr->uri_processed);
218 if(config->serverport == config->standard_port)
220 m_build_buffer(&real_location, &len, "http://%s%s",
221 host, location);
223 else
225 m_build_buffer(&real_location, &len, "http://%s:%i%s",
226 host, config->serverport,
227 location);
229 mk_mem_free(host);
231 sr->headers->status = M_REDIR_MOVED;
232 sr->headers->content_length = -1;
233 mk_pointer_reset(&sr->headers->content_type);
234 sr->headers->location = real_location;
235 sr->headers->cgi = SH_NOCGI;
236 sr->headers->pconnections_left =
237 (config->max_keep_alive_request -
238 cr->counter_connections);
240 mk_header_send(cr->socket, cr, sr, sr->log);
241 mk_socket_set_cork_flag(cr->socket, TCP_CORK_OFF);
244 * we do not free() real_location
245 * as it's freed by iov
247 mk_mem_free(location);
248 sr->headers->location=NULL;
249 return 0;
252 /* looking for a index file */
253 index_file = mk_request_index(sr->real_path.data);
255 if(!index_file.data) {
256 /* No index file found, show the content directory */
257 if(sr->host_conf->getdir==VAR_ON) {
258 int getdir_res = 0;
260 //getdir_res = mk_dirhtml_init(cr, sr);
262 if(getdir_res == -1){
263 mk_request_error(M_CLIENT_FORBIDDEN, cr,
264 sr, 1, sr->log);
265 return -1;
267 return getdir_res;
269 else {
270 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr,
271 1, sr->log);
272 return -1;
275 else{
276 mk_mem_free(sr->file_info);
277 mk_pointer_free(&sr->real_path);
279 sr->real_path = index_file;
280 sr->file_info = mk_file_get_info(sr->real_path.data);
284 /* read permission */
285 if(sr->file_info->read_access == MK_FILE_FALSE){
286 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
287 return -1;
290 /* Matching MimeType */
291 mime = mk_mimetype_find(&sr->real_path);
292 if(!mime)
294 mime = mimetype_default;
298 palm = mk_palm_check_request(cr, sr);
299 if(palm)
301 mk_palm_send_response(cr, sr, palm);
302 return -1;
306 /* get file size */
307 if(sr->file_info->size < 0) {
308 mk_request_error(M_CLIENT_NOT_FOUND, cr, sr, 1, sr->log);
309 return -1;
312 /* counter connections */
313 sr->headers->pconnections_left = (int)
314 (config->max_keep_alive_request - cr->counter_connections);
317 gmt_file_unix_time =
318 PutDate_string((time_t) sr->file_info->last_modification);
320 if(sr->if_modified_since.data && sr->method==HTTP_METHOD_GET){
321 time_t date_client; // Date send by client
322 time_t date_file_server; // Date server file
324 date_client = PutDate_unix(sr->if_modified_since.data);
325 date_file_server = sr->file_info->last_modification;
327 if( (date_file_server <= date_client) && (date_client > 0) )
329 sr->headers->status = M_NOT_MODIFIED;
330 mk_header_send(cr->socket, cr, sr, sr->log);
331 mk_pointer_free(&gmt_file_unix_time);
332 return 0;
335 sr->headers->status = M_HTTP_OK;
336 sr->headers->cgi = SH_NOCGI;
337 sr->headers->last_modified = gmt_file_unix_time;
338 sr->headers->location = NULL;
340 /* Object size for log and response headers */
341 sr->log->size = sr->headers->content_length = sr->file_info->size;
342 sr->log->size_p = sr->headers->content_length_p =
343 mk_utils_int2mkp(sr->file_info->size);
345 if(sr->method==HTTP_METHOD_GET || sr->method==HTTP_METHOD_POST)
347 sr->headers->content_type = mime->type;
348 /* Range */
349 if(sr->range.data!=NULL && config->resume==VAR_ON){
350 if(mk_http_range_parse(sr)<0)
352 mk_request_error(M_CLIENT_BAD_REQUEST, cr,
353 sr, 1, sr->log);
354 mk_pointer_free(&gmt_file_unix_time);
355 return -1;
357 if(sr->headers->ranges[0]>=0 || sr->headers->ranges[1]>=0)
358 sr->headers->status = M_HTTP_PARTIAL;
361 else{ /* without content-type */
362 mk_pointer_reset(&sr->headers->content_type);
365 mk_header_send(cr->socket, cr, sr, sr->log);
367 if(sr->headers->content_length==0){
368 return 0;
371 /* Sending file */
372 if((sr->method==HTTP_METHOD_GET || sr->method==HTTP_METHOD_POST)
373 && sr->file_info->size>0)
375 sr->fd_file = open(sr->real_path.data, config->open_flags);
377 if(sr->fd_file == -1){
378 perror("open");
379 return -1;
382 /* Calc bytes to send & offset */
383 if(mk_http_range_set(sr, sr->file_info->size)!=0)
385 mk_request_error(M_CLIENT_BAD_REQUEST, cr,
386 sr, 1, sr->log);
387 return -1;
390 bytes = SendFile(cr->socket, sr);
393 return bytes;
397 * Check if a connection can continue open using as criteria
398 * the keepalive headers vars and Monkey configuration
400 int mk_http_keepalive_check(int socket, struct client_request *cr)
402 if(!cr->request)
404 return -1;
407 if(config->keep_alive==VAR_OFF || cr->request->keep_alive==VAR_OFF)
409 return -1;
412 if(cr->counter_connections>=config->max_keep_alive_request)
414 return -1;
417 return 0;
420 int mk_http_range_set(struct request *sr, long file_size)
422 struct header_values *sh = sr->headers;
424 sr->bytes_to_send = file_size;
425 sr->bytes_offset = 0;
427 if(config->resume==VAR_ON && sr->range.data){
428 /* yyy- */
429 if(sh->ranges[0]>=0 && sh->ranges[1]==-1){
430 sr->bytes_offset = sh->ranges[0];
431 sr->bytes_to_send = file_size - sr->bytes_offset;
434 /* yyy-xxx */
435 if(sh->ranges[0]>=0 && sh->ranges[1]>=0){
436 sr->bytes_offset = sh->ranges[0];
437 sr->bytes_to_send = labs(sh->ranges[1]-sh->ranges[0])+1;
440 /* -xxx */
441 if(sh->ranges[0]==-1 && sh->ranges[1]>0){
442 sr->bytes_to_send = sh->ranges[1];
443 sr->bytes_offset = file_size - sh->ranges[1];
446 if(sr->bytes_offset>file_size || sr->bytes_to_send>file_size)
448 return -1;
451 lseek(sr->fd_file, sr->bytes_offset, SEEK_SET);
453 return 0;
458 int mk_http_range_parse(struct request *sr)
460 int eq_pos, sep_pos, len;
461 char *buffer=0;
463 if(!sr->range.data)
464 return -1;
466 if((eq_pos = mk_string_search_n(sr->range.data, "=",
467 sr->range.len))<0)
468 return -1;
470 if(strncasecmp(sr->range.data, "Bytes", eq_pos)!=0)
471 return -1;
473 if((sep_pos = mk_string_search_n(sr->range.data, "-",
474 sr->range.len))<0)
475 return -1;
477 len = sr->range.len;
479 /* =-xxx */
480 if(eq_pos+1 == sep_pos){
481 sr->headers->ranges[0] = -1;
482 sr->headers->ranges[1] = (unsigned long) atol(sr->range.data + sep_pos + 1);
484 if(sr->headers->ranges[1]<=0)
486 return -1;
488 return 0;
491 /* =yyy-xxx */
492 if( (eq_pos+1 != sep_pos) && (len > sep_pos + 1))
494 buffer = mk_string_copy_substr(sr->range.data, eq_pos+1, sep_pos);
495 sr->headers->ranges[0] = (unsigned long) atol(buffer);
496 mk_mem_free(buffer);
498 buffer = mk_string_copy_substr(sr->range.data, sep_pos+1, len);
499 sr->headers->ranges[1] = (unsigned long) atol(buffer);
500 mk_mem_free(buffer);
502 if(sr->headers->ranges[1]<=0 ||
503 sr->headers->ranges[0]>sr->headers->ranges[1])
505 return -1;
508 return 0;
510 /* =yyy- */
511 if( (eq_pos+1 != sep_pos) && (len == sep_pos + 1))
513 buffer = mk_string_copy_substr(sr->range.data, eq_pos+1, len);
514 sr->headers->ranges[0] = (unsigned long) atol(buffer);
515 mk_mem_free(buffer);
516 return 0;
519 return -1;
523 * Check if client request still has pending data
525 * Return 0 when all expected data has arrived or -1 when
526 * the connection is on a pending status due to HTTP spec
528 * This function is called from request.c :: mk_handler_read(..)
530 int mk_http_pending_request(struct client_request *cr)
532 int n;
533 char *str;
535 n = mk_string_search(cr->body, mk_endblock.data);
537 if(n<=0)
539 return -1;
542 if(cr->first_block_end<0)
544 cr->first_block_end = n;
547 str = cr->body + n + mk_endblock.len;
549 if(cr->first_method == HTTP_METHOD_UNKNOWN){
550 cr->first_method = mk_http_method_get(cr->body);
553 if(cr->first_method == HTTP_METHOD_POST)
555 if(cr->first_block_end > 0){
556 /* if first block has ended, we need to verify if exists
557 * a previous block end, that will means that the POST
558 * method has sent the whole information.
559 * just for ref: pipelining is not allowed with POST
561 if(cr->first_block_end == cr->body_length-mk_endblock.len){
562 /* Content-length is required, if is it not found,
563 * we pass as successfull in order to raise the error
564 * later
566 if(mk_method_post_content_length(cr->body) < 0){
567 cr->status = MK_REQUEST_STATUS_COMPLETED;
568 return 0;
571 else{
572 cr->status = MK_REQUEST_STATUS_COMPLETED;
573 return 0;
576 else{
577 return -1;
581 cr->status = MK_REQUEST_STATUS_COMPLETED;
582 return 0;
585 mk_pointer *mk_http_status_get(short int code)
587 mk_list_sint_t *l;
589 l = mk_http_status_list;
590 while(l)
592 if(l->index == code)
594 return &l->value;
596 else {
597 l = l->next;
601 return NULL;
604 void mk_http_status_add(short int val[2])
606 short i, len=6;
607 char *str_val;
608 mk_list_sint_t *list, *new;
610 for(i=val[0];i<=val[1]; i++)
613 new = mk_mem_malloc(sizeof(mk_list_sint_t));
614 new->index = i;
615 new->next = NULL;
617 str_val = mk_mem_malloc(6);
618 snprintf(str_val, len-1, "%i", i);
620 new->value.data = str_val;
621 new->value.len = 3;
623 if(!mk_http_status_list)
625 mk_http_status_list = new;
627 else{
628 list = mk_http_status_list;
629 while(list->next)
630 list = list->next;
632 list->next = new;
633 list = new;
638 void mk_http_status_list_init()
640 /* Status type */
641 short int success[2] = {200, 206};
642 short int redirections[2] = {300, 305};
643 short int client_errors[2] = {400, 415};
644 short int server_errors[2] = {500, 505};
646 mk_http_status_add(success);
647 mk_http_status_add(redirections);
648 mk_http_status_add(client_errors);
649 mk_http_status_add(server_errors);