Updating dir_html.c
[MonkeyD.git] / src / method.c
bloba052aadee1e3fed00d37b7224e0ef182a2eb4afd
1 /* Monkey HTTP Daemon
2 * ------------------
3 * Copyright (C) 2001-2003, 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 <sys/types.h>
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/wait.h>
25 #include <netinet/in.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <time.h>
31 #include "monkey.h"
33 /* Get & Head Method */
34 int M_METHOD_Get_and_Head(struct client_request *cr, struct request *sr,
35 int socket)
37 /*
38 cr = client request
39 sr = struct request
42 int method_value=0;
43 char *location=0, *real_location=0; /* ruta para redireccion */
44 char **mime_info;
46 struct stat checkpath, statfile;
48 /* Peticion normal, no es a un Virtualhost */
49 if((strcmp(sr->uri_processed,"/"))==0)
50 sr->real_path = m_build_buffer("%s", sr->temp_path);
52 if(sr->user_home==VAR_OFF){
53 sr->real_path = m_build_buffer("%s%s", sr->temp_path, sr->uri_processed);
56 if(sr->method!=HEAD_METHOD)
57 method_value=1;
59 if(stat(sr->real_path, &checkpath)==-1){
60 Request_Error(M_CLIENT_NOT_FOUND, cr, sr, method_value, sr->log);
61 return -1;
64 // it's an symbolic link
65 if(Check_symlink(sr->real_path)==0){
66 if(config->symlink==VAR_OFF){
67 sr->log->final_response=M_CLIENT_FORBIDDEN;
68 Request_Error(M_CLIENT_FORBIDDEN, cr, sr, method_value, sr->log);
69 return -1;
71 else{
72 char linked_file[MAX_PATH];
73 readlink(sr->real_path, linked_file, MAX_PATH);
74 if(Deny_Check(linked_file)==-1) {
75 sr->log->final_response=M_CLIENT_FORBIDDEN;
76 Request_Error(M_CLIENT_FORBIDDEN, cr, sr, method_value, sr->log);
77 return -1;
81 /* Checkeando si la ruta es un Directorio */
82 if(checkpath.st_mode & S_IFDIR) {
83 /* This pointer never must be freed */
84 char *index_file = 0;
86 /*
87 We have to check if exist an slash to the end of
88 this string, if doesn't exist we send a redirection header
90 if(sr->uri_processed[strlen(sr->uri_processed) - 1] != '/') {
91 location=m_build_buffer("%s/", sr->uri_processed);
92 if(config->serverport == config->standard_port)
93 real_location=m_build_buffer("http://%s%s", sr->host, location);
94 else
95 real_location=m_build_buffer("http://%s:%i%s", sr->host, config->serverport, location);
97 sr->headers->status = M_REDIR_MOVED;
98 sr->headers->content_length = 0;
99 sr->headers->content_type = NULL;
100 sr->headers->location = real_location;
101 sr->headers->cgi = SH_NOCGI;
102 sr->headers->pconnections_left = config->max_keep_alive_request - cr->counter_connections;
104 M_METHOD_send_headers(socket, sr->headers, sr->log);
106 M_free(location);
107 M_free(real_location);
108 sr->headers->location=NULL;
109 return 0;
112 /* Buscar un indexfile según configuración */
113 index_file = (char *) FindIndex(sr->real_path);
114 if(!index_file) {
115 /* No existe un indexfile, mostrar el directorio */
116 if(sr->getdir==VAR_ON) {
117 int getdir_res = 0;
119 getdir_res = GetDir(cr, sr, config->header_file, config->footer_file);
121 if(getdir_res == -1){
122 Request_Error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
123 return -1;
125 return 0;
127 else {
128 Request_Error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
129 return -1;
132 else{
133 sr->real_path = m_build_buffer_from_buffer(sr->real_path, "%s", index_file);
137 /* ¿Existe archivo? */
138 if(access(sr->real_path,F_OK)!=0){
139 Request_Error(M_CLIENT_NOT_FOUND, cr, sr, 1, sr->log);
140 return -1;
143 /* Permisos de lectura */
144 if(AccessFile(sr->real_path)!=0){
145 Request_Error(M_CLIENT_FORBIDDEN, cr, sr, 1, sr->log);
146 return -1;
149 /* Buscanco MimeType que coincida */
150 mime_info=Mimetype_Find(sr->real_path);
152 if(mime_info[1]){
153 /* ¿ Script ejecutable (e.g PHP) ? */
154 if(access(mime_info[1],X_OK)==0){
155 int cgi_status=0;
156 char *arg_script[3];
158 /* ¿ Es un archivo regular ? */
159 if(CheckFile(mime_info[1])!=0){
160 Request_Error(M_SERVER_INTERNAL_ERROR, cr, sr, 1, sr->log);
161 Mimetype_free(mime_info);
162 return -1;
165 sr->log->final_response=M_HTTP_OK;
166 sr->script_filename=M_strdup(sr->real_path);
168 arg_script[0] = mime_info[1];
169 arg_script[1] = sr->script_filename;
170 arg_script[2] = NULL;
172 if(sr->method==GET_METHOD || sr->method==POST_METHOD)
173 cgi_status=M_CGI_run(sr, mime_info[1], arg_script);
175 switch(cgi_status){
176 case -2: /* Timeout */
177 sr->log->final_response=M_CLIENT_REQUEST_TIMEOUT;
178 break;
179 case -3: /* Internal server Error */
180 sr->log->final_response=M_SERVER_INTERNAL_ERROR;
181 break;
183 case -1:
184 sr->make_log=VAR_OFF;
185 break;
186 case 0: /* Ok */
187 sr->log->final_response=M_HTTP_OK;
188 break;
191 if(cgi_status==M_CGI_TIMEOUT || cgi_status==M_CGI_INTERNAL_SERVER_ERR){
192 Request_Error(sr->log->final_response, cr, sr, 1, sr->log);
195 Mimetype_free(mime_info);
196 return cgi_status;
199 /* Rescatando largo del archivo */
200 if(stat(sr->real_path,&statfile) < 0) {
201 Request_Error(M_CLIENT_NOT_FOUND, cr, sr, 1, sr->log);
202 Mimetype_free(mime_info);
203 return -1;
206 /* Fue enviado if_modified_since por el cliente ? */
207 sr->headers->pconnections_left = config->max_keep_alive_request - cr->counter_connections;
208 if(sr->if_modified_since && sr->method==GET_METHOD){
210 time_t date_client; // Date send by client
211 time_t date_file_server; // Date server file
212 char *gmt_file_unix_time; // gmt time of server file (unix time)
214 date_client = PutDate_unix(sr->if_modified_since);
216 gmt_file_unix_time = PutDate_string((time_t) statfile.st_mtime);
217 date_file_server = PutDate_unix(gmt_file_unix_time);
219 if( (date_file_server <= date_client) && (date_client > 0) ){
220 sr->headers->status = M_NOT_MODIFIED;
221 M_METHOD_send_headers(socket, sr->headers, sr->log);
222 Mimetype_free(mime_info);
223 return 0;
225 // M_free(gmt_file_unix_time);
227 sr->headers->status = M_HTTP_OK;
228 sr->headers->content_length = statfile.st_size;
229 sr->headers->cgi = SH_NOCGI;
230 sr->headers->last_modified = m_build_buffer("%s", PutDate_string( statfile.st_mtime ));
231 sr->headers->location = NULL;
233 sr->log->size=(statfile.st_size);
234 if(sr->method==GET_METHOD || sr->method==POST_METHOD){
235 sr->headers->content_type = mime_info[0];
236 /* Range */
237 if(sr->range!=NULL && config->resume==VAR_ON){
238 M_METHOD_get_range(sr->range, sr->headers->range_values);
239 if(sr->headers->range_values[0]>=0 || sr->headers->range_values[1]>=0)
240 sr->headers->status = M_HTTP_PARTIAL;
243 else{ /* Sin content-type */
244 sr->headers->content_type = NULL;
246 M_METHOD_send_headers(socket, sr->headers, sr->log);
248 if(sr->headers->content_length==0){
249 Mimetype_free(mime_info);
250 return -1;
252 /* Enviando archivo */
253 if((sr->method==GET_METHOD || sr->method==POST_METHOD) && statfile.st_size>0)
254 SendFile(socket, sr->real_path, sr->headers->range_values);
256 Mimetype_free(mime_info);
258 sr->headers->content_type=NULL;
260 return 0;
263 /* POST METHOD */
264 int M_METHOD_Post(struct client_request *cr,
265 struct request *s_request, char *request_body)
267 char *tmp;
268 char *post_buffer;
269 char buffer[MAX_REQUEST_BODY];
270 int i=0, content_length_post=0;
272 if(!(tmp=Request_Find_Variable(request_body, RH_CONTENT_LENGTH))){
273 Request_Error(M_CLIENT_LENGHT_REQUIRED, cr, s_request,0,s_request->log);
274 return -1;
277 content_length_post = (int) atoi(tmp);
278 M_free(tmp);
280 if(content_length_post<=0 || content_length_post >=MAX_REQUEST_BODY){
281 Request_Error(M_CLIENT_BAD_REQUEST, cr, s_request, 0, s_request->log);
282 return -1;
285 if(!(tmp = Request_Find_Variable(request_body, RH_CONTENT_TYPE))){
286 Request_Error(M_CLIENT_BAD_REQUEST, cr, s_request, 0, s_request->log);
287 return -1;
290 s_request->content_type = tmp;
292 post_buffer = (char *) strstr(request_body,"\r\n\r\n");
294 if(post_buffer==NULL || strlen(post_buffer)<=4) {
295 s_request->post_variables=NULL;
296 return -1;
299 memset(buffer,'\0',sizeof(buffer));
300 for(i=4;i<strlen(post_buffer);i++){
301 buffer[i-4]=post_buffer[i];
304 if(strlen(buffer) < content_length_post){
305 content_length_post=strlen(buffer);
308 s_request->post_variables = M_malloc(sizeof(buffer) + 1);
309 memset(s_request->post_variables, '\0', sizeof(buffer) + 1);
310 strncpy(s_request->post_variables, buffer, content_length_post);
311 s_request->post_variables[content_length_post ]='\0';
312 s_request->content_length=content_length_post;
313 return 0;
316 /* Send_Header , envia las cabeceras principales */
317 int M_METHOD_send_headers(int fd, struct header_values *sh, struct log_info *s_log)
319 int fd_status=0;
320 char *buffer=0;
322 /* Status Code */
323 switch(sh->status){
324 case M_HTTP_OK:
325 buffer = m_build_buffer_from_buffer(buffer,"HTTP/1.1 200 OK\r\n");
326 break;
328 case M_HTTP_PARTIAL:
329 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 206 Partial Content\r\n");
330 break;
332 case M_REDIR_MOVED:
333 s_log->status=S_LOG_OFF;
334 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 301 Moved Permanently\r\n");
335 break;
337 case M_REDIR_MOVED_T:
338 s_log->status=S_LOG_ON;
339 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 302 Found\r\n");
340 break;
342 case M_NOT_MODIFIED:
343 s_log->status=S_LOG_OFF;
344 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 304 Not Modified\r\n");
345 break;
347 case M_CLIENT_BAD_REQUEST:
348 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 400 Bad Request\r\n");
349 break;
351 case M_CLIENT_FORBIDDEN:
352 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 403 Forbidden\r\n");
353 break;
355 case M_CLIENT_NOT_FOUND:
356 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 404 Not Found\r\n");
357 break;
359 case M_CLIENT_METHOD_NOT_ALLOWED:
360 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 405 Method Not Allowed\r\n");
361 break;
363 case M_CLIENT_REQUEST_TIMEOUT:
364 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 408 Request Timeout\r\n");
365 s_log->status=S_LOG_OFF;
366 break;
368 case M_CLIENT_LENGHT_REQUIRED:
369 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 411 Length Required\r\n");
370 break;
372 case M_SERVER_INTERNAL_ERROR:
373 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 500 Internal Server Error\r\n");
374 break;
376 case M_SERVER_HTTP_VERSION_UNSUP:
377 buffer = m_build_buffer_from_buffer(buffer, "HTTP/1.1 505 HTTP Version Not Supported\r\n");
378 break;
381 if(sh->status!=0){
382 s_log->final_response = sh->status;
385 if(fd_status<0){
386 return -1;
389 /* Informacion del server */
390 buffer = m_build_buffer_from_buffer(buffer,"Server: %s\r\n", config->server_software);
392 /* Fecha */
393 buffer = m_build_buffer_from_buffer(buffer,"Date: %s\r\n", PutDate_string(0));
395 /* Location */
396 if(sh->location!=NULL)
397 buffer = m_build_buffer_from_buffer(buffer, "Location: %s\r\n", sh->location);
399 /* Last-Modified */
400 if(sh->last_modified!=NULL){
401 buffer = m_build_buffer_from_buffer(buffer,"%s %s\r\n", RH_LAST_MODIFIED, sh->last_modified);
404 /* Conexion */
405 if(sh->pconnections_left!=0 && config->keep_alive==VAR_ON){
406 buffer = m_build_buffer_from_buffer(buffer, "Keep-Alive: timeout=%i, max=%i\r\n", config->keep_alive_timeout, sh->pconnections_left);
407 buffer = m_build_buffer_from_buffer(buffer, "Connection: Keep-Alive\r\n");
409 else{
410 buffer = m_build_buffer_from_buffer(buffer, "Connection: close\r\n");
413 /* Tipo de contenido de la informacion */
414 if(sh->content_type!=NULL){
415 buffer = m_build_buffer_from_buffer(buffer, "Content-Type: %s\r\n", sh->content_type);
418 /* Ranges */
419 buffer = m_build_buffer_from_buffer(buffer, "Accept-Ranges: bytes\r\n", sh->content_type);
421 /* Tamaño total de la informacion a enviar */
422 if((sh->content_length!=0 && (sh->range_values[0]>=0 || sh->range_values[1]>=0)) && config->resume==VAR_ON){
423 long int length;
425 /* yyy- */
426 if(sh->range_values[0]>=0 && sh->range_values[1]==-1){
427 length = (unsigned long int) ( sh->content_length - sh->range_values[0] );
428 buffer = m_build_buffer_from_buffer(buffer, "%s %i\r\n", RH_CONTENT_LENGTH, length);
429 buffer = m_build_buffer_from_buffer(buffer, "%s bytes %d-%d/%d\r\n", RH_CONTENT_RANGE, sh->range_values[0],
430 (sh->content_length - 1), sh->content_length);
433 /* yyy-xxx */
434 if(sh->range_values[0]>=0 && sh->range_values[1]>=0){
435 length = (unsigned long int) (sh->range_values[1] - sh->range_values[0] + 1);
436 buffer = m_build_buffer_from_buffer(buffer, "%s %d\r\n", RH_CONTENT_LENGTH, length);
437 buffer = m_build_buffer_from_buffer(buffer, "%s bytes %d-%d/%d\r\n", RH_CONTENT_RANGE, sh->range_values[0],
438 sh->range_values[1], sh->content_length);
441 /* -xxx */
442 if(sh->range_values[0]==-1 && sh->range_values[1]>=0){
443 buffer = m_build_buffer_from_buffer(buffer, "%s %d\r\n", RH_CONTENT_LENGTH, sh->range_values[1]);
444 buffer = m_build_buffer_from_buffer(buffer, "%s bytes %d-%d/%d\r\n", RH_CONTENT_RANGE, (sh->content_length - sh->range_values[1]),
445 (sh->content_length - 1) , sh->content_length);
448 else if(sh->content_length!=0)
449 buffer = m_build_buffer_from_buffer(buffer, "%s %d\r\n", RH_CONTENT_LENGTH, sh->content_length);
450 else if(sh->status==M_REDIR_MOVED)
451 buffer = m_build_buffer_from_buffer(buffer, "%s %d\r\n", RH_CONTENT_LENGTH, sh->content_length);
453 if(sh->cgi==SH_NOCGI)
454 buffer = m_build_buffer_from_buffer(buffer, "\r\n");
456 fdprintf(fd, NO_CHUNKED, "%s", buffer);
457 M_free(buffer);
458 return 0;
461 int M_METHOD_get_number(char *method)
463 if(strcmp(method, GET_METHOD_STR)==0)
464 return GET_METHOD;
466 if(strcmp(method, POST_METHOD_STR)==0)
467 return POST_METHOD;
469 if(strcmp(method, HEAD_METHOD_STR)==0)
470 return HEAD_METHOD;
472 return METHOD_NOT_ALLOWED;
475 char *M_METHOD_get_name(int method)
477 switch(method){
478 case GET_METHOD:
479 return (char *) GET_METHOD_STR;
481 case POST_METHOD:
482 return (char *) POST_METHOD_STR;
484 case HEAD_METHOD:
485 return (char *) HEAD_METHOD_STR;
487 return (char *) "";
490 int M_METHOD_get_range(char *header, int range_from_to[2])
492 int eq_pos, sep_pos;
494 range_from_to[0] = -1;
495 range_from_to[1] = -1;
497 if(!header)
498 return -1;
500 if((eq_pos = str_search(header, "=", 1))<0)
501 return -1;
503 if(strncasecmp(header, "Bytes", eq_pos)!=0)
504 return -1;
506 if((sep_pos = str_search(header, "-", 1))<0)
507 return -1;
509 /* =-xxx */
510 if(eq_pos+1 == sep_pos){
511 range_from_to[0] = -1;
512 range_from_to[1] = (unsigned long) atol(header + sep_pos + 1);
513 return 0;
516 /* =yyy-xxx */
517 if( (eq_pos+1 != sep_pos) && (strlen(header) > sep_pos + 1) ){
518 char *buffer_start=0, *buffer=0, *last=0;
520 buffer_start = buffer = M_strdup(header+eq_pos+1);
521 buffer = strtok_r(buffer, "-", &last);
522 range_from_to[0] = (unsigned long) atol(m_build_buffer("%d", atol(buffer)));
523 buffer = strtok_r(NULL, "\n", &last);
524 range_from_to[1] = (unsigned long) atol(m_build_buffer("%d", atol(buffer)));
525 M_free(buffer_start);
526 return 0;
528 /* =yyy- */
529 if( (eq_pos+1 != sep_pos) && (strlen(header) == sep_pos + 1 ) ){
530 char *buffer_start=0, *buffer=0, *last=0;
532 buffer = M_strdup(header+eq_pos+1);
533 buffer = strtok_r(buffer, "-", &last);
535 range_from_to[0] = (unsigned long) atol(m_build_buffer("%d", atol(buffer)));
536 range_from_to[1] = -1;
537 M_free(buffer_start);
538 return 0;
541 return -1;