dir_html 2/3
[MonkeyD.git] / src / http.c
blob45cf34ba3b274f2dd18f8e7b1aad7a9228cbc77f
1 /* Monkey HTTP Daemon
2 * ------------------
3 * Copyright (C) 2001-2008, Eduardo Silva P.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Library General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
28 #include "monkey.h"
29 #include "memory.h"
30 #include "http.h"
31 #include "http_status.h"
32 #include "file.h"
33 #include "utils.h"
34 #include "config.h"
35 #include "cgi.h"
36 #include "str.h"
37 #include "method.h"
38 #include "socket.h"
39 #include "mimetype.h"
40 #include "dir_html.h"
41 #include "logfile.h"
42 #include "header.h"
44 #define O_NOATIME 01000000
46 int mk_http_method_check(char *method)
48 if(strcmp(method, HTTP_METHOD_GET_STR)==0)
50 return HTTP_METHOD_GET;
53 if(strcmp(method, HTTP_METHOD_POST_STR)==0)
55 return HTTP_METHOD_POST;
58 if(strcmp(method, HTTP_METHOD_HEAD_STR)==0)
60 return HTTP_METHOD_HEAD;
63 return METHOD_NOT_FOUND;
66 char *mk_http_method_check_str(int method)
68 switch(method){
69 case HTTP_METHOD_GET:
70 return (char *) HTTP_METHOD_GET_STR;
72 case HTTP_METHOD_POST:
73 return (char *) HTTP_METHOD_POST_STR;
75 case HTTP_METHOD_HEAD:
76 return (char *) HTTP_METHOD_HEAD_STR;
79 return "";
82 int mk_http_method_get(mk_pointer body)
84 int int_method, pos = 0, max_length_method = 5;
85 char *str_method;
87 pos = mk_string_search(body.data, " ");
88 if(pos<=2 || pos>=max_length_method){
89 return -1;
92 str_method = mk_mem_malloc(max_length_method);
93 strncpy(str_method, body.data, pos);
94 str_method[pos]='\0';
96 int_method = mk_http_method_check(str_method);
97 mk_mem_free(str_method);
99 return int_method;
102 int mk_http_protocol_check(char *protocol)
104 if(strcmp(protocol, HTTP_PROTOCOL_11_STR)==0)
106 return HTTP_PROTOCOL_11;
108 if(strcmp(protocol, HTTP_PROTOCOL_10_STR)==0)
110 return HTTP_PROTOCOL_10;
112 if(strcmp(protocol, HTTP_PROTOCOL_09_STR)==0)
114 return HTTP_PROTOCOL_09;
117 return HTTP_PROTOCOL_UNKNOWN;
120 char *mk_http_protocol_check_str(int protocol)
122 if(protocol==HTTP_PROTOCOL_11)
124 return (char *) HTTP_PROTOCOL_11_STR;
126 if(protocol==HTTP_PROTOCOL_10)
128 return (char *) HTTP_PROTOCOL_10_STR;
130 if(protocol==HTTP_PROTOCOL_09)
132 return (char *) HTTP_PROTOCOL_09_STR;
135 return "";
138 int mk_http_init(struct client_request *cr, struct request *sr)
140 int debug_error=0, bytes=0;
141 char *location=0, *real_location=0; /* ruta para redireccion */
142 char **mime_info;
143 mk_pointer gmt_file_unix_time; // gmt time of server file (unix time)
144 struct file_info *path_info;
145 unsigned long len;
147 /* Normal request default site */
148 if((strcmp(sr->uri_processed,"/"))==0)
150 m_build_buffer(&sr->real_path, &len, "%s",
151 sr->host_conf->documentroot);
154 if(sr->user_home==VAR_OFF)
156 m_build_buffer(&sr->real_path, &len, "%s%s",
157 sr->host_conf->documentroot,
158 sr->uri_processed);
161 if(sr->method!=HTTP_METHOD_HEAD){
162 debug_error=1;
165 path_info = mk_file_get_info(sr->real_path);
166 if(!path_info){
167 mk_request_error(M_CLIENT_NOT_FOUND, cr, sr,
168 debug_error, sr->log);
169 return -1;
172 /* Check symbolic link file */
173 if(path_info->is_link == MK_FILE_TRUE){
174 if(config->symlink==VAR_OFF){
175 sr->log->final_response=M_CLIENT_FORBIDDEN;
176 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr,
177 debug_error, sr->log);
178 return -1;
180 else{
181 char linked_file[MAX_PATH];
182 readlink(sr->real_path, linked_file, MAX_PATH);
184 if(Deny_Check(linked_file)==-1) {
185 sr->log->final_response=M_CLIENT_FORBIDDEN;
186 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, debug_error, sr->log);
187 return -1;
193 /* is it a valid directory ? */
194 if(path_info->is_directory == MK_FILE_TRUE) {
195 /* This pointer never must be freed */
196 char *index_file = 0;
199 We have to check if exist an slash to the end of
200 this string, if doesn't exist we send a redirection header
202 if(sr->uri_processed[strlen(sr->uri_processed) - 1] != '/') {
203 char *host;
204 host = mk_pointer_to_buf(sr->host);
206 m_build_buffer(&location, &len, "%s/", sr->uri_processed);
207 if(config->serverport == config->standard_port)
209 m_build_buffer(&real_location, &len, "http://%s%s",
210 host, location);
212 else
214 m_build_buffer(&real_location, &len, "http://%s:%i%s",
215 host, config->serverport,
216 location);
218 mk_mem_free(host);
220 sr->headers->status = M_REDIR_MOVED;
221 sr->headers->content_length = 0;
222 sr->headers->content_type = NULL;
223 sr->headers->location = real_location;
224 sr->headers->cgi = SH_NOCGI;
225 sr->headers->pconnections_left =
226 (config->max_keep_alive_request -
227 cr->counter_connections);
229 mk_header_send(cr->socket, cr, sr, sr->log);
231 mk_mem_free(location);
232 mk_mem_free(real_location);
233 sr->headers->location=NULL;
234 return 0;
237 /* looking for a index file */
238 index_file = (char *) mk_request_index(sr->real_path);
240 if(!index_file) {
241 /* No index file found, show the content directory */
242 if(sr->host_conf->getdir==VAR_ON) {
243 int getdir_res = 0;
245 getdir_res = mk_dirhtml_init(cr, sr);
247 if(getdir_res == -1){
248 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
249 return -1;
251 return 0;
253 else {
254 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
255 return -1;
258 else{
259 mk_mem_free(path_info);
260 sr->real_path =
261 m_build_buffer_from_buffer(sr->real_path, "%s", index_file);
263 path_info = mk_file_get_info(sr->real_path);
267 /* read permission */
268 if(path_info->read_access == MK_FILE_FALSE){
269 mk_request_error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
270 return -1;
273 /* Matching MimeType */
274 mime_info=Mimetype_Find(sr->real_path);
276 /* FIXME:
278 * Some returns are not freeing all memory blocks allocated
281 if(mime_info[1]){
282 struct file_info *fcgi;
283 fcgi = mk_file_get_info(mime_info[1]);
285 /* executable script (e.g PHP) ? */
286 if(fcgi){
287 int cgi_status=0;
288 char *arg_script[3];
290 /* is it normal file ? */
291 if(fcgi->is_directory==MK_FILE_TRUE || fcgi->exec_access==MK_FILE_FALSE){
292 mk_request_error(M_SERVER_INTERNAL_ERROR, cr, sr, 1, sr->log);
293 Mimetype_free(mime_info);
294 return -1;
297 * FIXME: CHECK FOR TARGET PERMISSION AS GCI DOES
299 ftarget = mk_file_get_info(sr->script_filename);
300 if(!ftarget)
305 sr->log->final_response=M_HTTP_OK;
306 sr->script_filename=mk_string_dup(sr->real_path);
308 arg_script[0] = mime_info[1];
309 arg_script[1] = sr->script_filename;
310 arg_script[2] = NULL;
312 if(sr->method==HTTP_METHOD_GET || sr->method==HTTP_METHOD_POST)
314 cgi_status=M_CGI_run(cr, sr, mime_info[1], arg_script);
317 switch(cgi_status){
318 case -2: /* Timeout */
319 sr->log->final_response=M_CLIENT_REQUEST_TIMEOUT;
320 break;
321 case -3: /* Internal server Error */
322 sr->log->final_response=M_SERVER_INTERNAL_ERROR;
323 break;
324 case -1:
325 sr->make_log=VAR_OFF;
326 break;
327 case 0: /* Ok */
328 sr->log->final_response=M_HTTP_OK;
329 break;
332 if(cgi_status==M_CGI_TIMEOUT ||
333 cgi_status==M_CGI_INTERNAL_SERVER_ERR)
335 mk_request_error(sr->log->final_response, cr,
336 sr, 1, sr->log);
339 Mimetype_free(mime_info);
340 mk_mem_free(fcgi);
341 return cgi_status;
344 /* get file size */
345 if(path_info->size < 0) {
346 mk_request_error(M_CLIENT_NOT_FOUND, cr, sr, 1, sr->log);
347 Mimetype_free(mime_info);
348 return -1;
351 /* counter connections */
352 sr->headers->pconnections_left = (int)
353 (config->max_keep_alive_request - cr->counter_connections);
355 gmt_file_unix_time =
356 PutDate_string((time_t) path_info->last_modification);
358 if(sr->if_modified_since.data && sr->method==HTTP_METHOD_GET){
359 time_t date_client; // Date send by client
360 time_t date_file_server; // Date server file
363 date_client = PutDate_unix(sr->if_modified_since.data);
364 date_file_server = PutDate_unix(gmt_file_unix_time.data);
366 if( (date_file_server <= date_client) && (date_client > 0) )
368 sr->headers->status = M_NOT_MODIFIED;
369 mk_header_send(cr->socket, cr, sr, sr->log);
370 Mimetype_free(mime_info);
371 mk_mem_free(path_info);
372 mk_pointer_free(gmt_file_unix_time);
373 return 0;
376 sr->headers->status = M_HTTP_OK;
377 sr->headers->content_length = path_info->size;
378 sr->headers->cgi = SH_NOCGI;
379 sr->headers->last_modified = gmt_file_unix_time.data;
380 sr->headers->location = NULL;
382 sr->log->size = path_info->size;
383 if(sr->method==HTTP_METHOD_GET || sr->method==HTTP_METHOD_POST)
385 sr->headers->content_type = mime_info[0];
386 /* Range */
387 if(sr->range.data!=NULL && config->resume==VAR_ON){
388 if(mk_http_range_parse(sr)<0)
390 mk_request_error(M_CLIENT_BAD_REQUEST, cr,
391 sr, 1, sr->log);
392 mk_pointer_free(gmt_file_unix_time);
393 return -1;
395 if(sr->headers->ranges[0]>=0 || sr->headers->ranges[1]>=0)
396 sr->headers->status = M_HTTP_PARTIAL;
399 else{ /* without content-type */
400 sr->headers->content_type = NULL;
403 mk_header_send(cr->socket, cr, sr, sr->log);
405 if(sr->headers->content_length==0){
406 Mimetype_free(mime_info);
407 return 0;
410 /* Sending file */
411 if((sr->method==HTTP_METHOD_GET || sr->method==HTTP_METHOD_POST)
412 && path_info->size>0)
414 /* O_NOATIME will just work if process owner has
415 * root privileges */
416 sr->fd_file = open(sr->real_path,
417 O_RDONLY|O_NOATIME|O_NONBLOCK);
419 /* Calc bytes to send & offset */
420 if(mk_http_range_set(sr, path_info->size)!=0)
422 mk_request_error(M_CLIENT_BAD_REQUEST, cr,
423 sr, 1, sr->log);
424 return -1;
427 mk_socket_set_cork_flag(cr->socket, TCP_CORK_OFF);
428 bytes = SendFile(cr->socket, sr);
431 Mimetype_free(mime_info);
432 mk_mem_free(path_info);
433 sr->headers->content_type=NULL;
435 return bytes;
439 * Check if a connection can continue open using as criteria
440 * the keepalive headers vars and Monkey configuration
442 int mk_http_keepalive_check(int socket, struct client_request *cr)
444 if(!cr->request)
446 return -1;
449 if(config->keep_alive==VAR_OFF || cr->request->keep_alive==VAR_OFF)
451 return -1;
454 if(cr->counter_connections>=config->max_keep_alive_request)
456 return -1;
459 return 0;
462 int mk_http_range_set(struct request *sr, long file_size)
464 struct header_values *sh = sr->headers;
466 sr->bytes_to_send = file_size;
467 sr->bytes_offset = 0;
469 if(config->resume==VAR_ON && sr->range.data){
470 /* yyy- */
471 if(sh->ranges[0]>=0 && sh->ranges[1]==-1){
472 sr->bytes_offset = sh->ranges[0];
473 sr->bytes_to_send = file_size - sr->bytes_offset;
476 /* yyy-xxx */
477 if(sh->ranges[0]>=0 && sh->ranges[1]>=0){
478 sr->bytes_offset = sh->ranges[0];
479 sr->bytes_to_send = labs(sh->ranges[1]-sh->ranges[0])+1;
482 /* -xxx */
483 if(sh->ranges[0]==-1 && sh->ranges[1]>=0){
484 sr->bytes_to_send = file_size - sh->ranges[1];
487 if(sr->bytes_offset>file_size || sr->bytes_to_send>file_size)
489 return -1;
492 lseek(sr->fd_file, sr->bytes_offset, SEEK_SET);
494 return 0;
499 int mk_http_range_parse(struct request *sr)
501 int eq_pos, sep_pos, len;
502 char *buffer=0;
504 if(!sr->range.data)
505 return -1;
507 if((eq_pos = mk_string_search_n(sr->range.data, "=",
508 sr->range.len))<0)
509 return -1;
511 if(strncasecmp(sr->range.data, "Bytes", eq_pos)!=0)
512 return -1;
514 if((sep_pos = mk_string_search_n(sr->range.data, "-",
515 sr->range.len))<0)
516 return -1;
518 len = sr->range.len;
520 /* =-xxx */
521 if(eq_pos+1 == sep_pos){
522 sr->headers->ranges[0] = -1;
523 sr->headers->ranges[1] = (unsigned long) atol(sr->range.data + sep_pos + 1);
525 if(sr->headers->ranges[1]<=0)
527 return -1;
529 return 0;
532 /* =yyy-xxx */
533 if( (eq_pos+1 != sep_pos) && (len > sep_pos + 1))
535 buffer = mk_string_copy_substr(sr->range.data, eq_pos+1, sep_pos);
536 sr->headers->ranges[0] = (unsigned long) atol(buffer);
537 mk_mem_free(buffer);
539 buffer = mk_string_copy_substr(sr->range.data, sep_pos+1, len);
540 sr->headers->ranges[1] = (unsigned long) atol(buffer);
541 mk_mem_free(buffer);
543 if(sr->headers->ranges[1]<=0 ||
544 sr->headers->ranges[0]>sr->headers->ranges[1])
546 return -1;
549 return 0;
551 /* =yyy- */
552 if( (eq_pos+1 != sep_pos) && (len == sep_pos + 1))
554 buffer = mk_string_copy_substr(sr->range.data, eq_pos+1, len);
555 sr->headers->ranges[0] = (unsigned long) atol(buffer);
556 mk_mem_free(buffer);
557 return 0;
560 return -1;