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.
21 #include <sys/types.h>
23 #include <sys/socket.h>
25 #include <netinet/in.h>
33 /* Get & Head Method */
34 int M_METHOD_Get_and_Head(struct client_request
*cr
, struct request
*sr
,
43 char *location
=0, *real_location
=0; /* ruta para redireccion */
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
)
59 if(stat(sr
->real_path
, &checkpath
)==-1){
60 Request_Error(M_CLIENT_NOT_FOUND
, cr
, sr
, method_value
, sr
->log
);
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
);
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
);
81 /* Checkeando si la ruta es un Directorio */
82 if(checkpath
.st_mode
& S_IFDIR
) {
83 /* This pointer never must be freed */
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
);
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
);
107 M_free(real_location
);
108 sr
->headers
->location
=NULL
;
112 /* Buscar un indexfile según configuración */
113 index_file
= (char *) FindIndex(sr
->real_path
);
115 /* No existe un indexfile, mostrar el directorio */
116 if(sr
->getdir
==VAR_ON
) {
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
);
128 Request_Error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
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
);
143 /* Permisos de lectura */
144 if(AccessFile(sr
->real_path
)!=0){
145 Request_Error(M_CLIENT_FORBIDDEN
, cr
, sr
, 1, sr
->log
);
149 /* Buscanco MimeType que coincida */
150 mime_info
=Mimetype_Find(sr
->real_path
);
153 /* ¿ Script ejecutable (e.g PHP) ? */
154 if(access(mime_info
[1],X_OK
)==0){
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
);
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
);
176 case -2: /* Timeout */
177 sr
->log
->final_response
=M_CLIENT_REQUEST_TIMEOUT
;
179 case -3: /* Internal server Error */
180 sr
->log
->final_response
=M_SERVER_INTERNAL_ERROR
;
184 sr
->make_log
=VAR_OFF
;
187 sr
->log
->final_response
=M_HTTP_OK
;
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
);
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
);
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
);
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];
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
);
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
;
264 int M_METHOD_Post(struct client_request
*cr
,
265 struct request
*s_request
, char *request_body
)
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
);
277 content_length_post
= (int) atoi(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
);
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
);
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
;
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
;
316 /* Send_Header , envia las cabeceras principales */
317 int M_METHOD_send_headers(int fd
, struct header_values
*sh
, struct log_info
*s_log
)
325 buffer
= m_build_buffer_from_buffer(buffer
,"HTTP/1.1 200 OK\r\n");
329 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 206 Partial Content\r\n");
333 s_log
->status
=S_LOG_OFF
;
334 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 301 Moved Permanently\r\n");
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");
343 s_log
->status
=S_LOG_OFF
;
344 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 304 Not Modified\r\n");
347 case M_CLIENT_BAD_REQUEST
:
348 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 400 Bad Request\r\n");
351 case M_CLIENT_FORBIDDEN
:
352 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 403 Forbidden\r\n");
355 case M_CLIENT_NOT_FOUND
:
356 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 404 Not Found\r\n");
359 case M_CLIENT_METHOD_NOT_ALLOWED
:
360 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 405 Method Not Allowed\r\n");
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
;
368 case M_CLIENT_LENGHT_REQUIRED
:
369 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 411 Length Required\r\n");
372 case M_SERVER_INTERNAL_ERROR
:
373 buffer
= m_build_buffer_from_buffer(buffer
, "HTTP/1.1 500 Internal Server Error\r\n");
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");
382 s_log
->final_response
= sh
->status
;
389 /* Informacion del server */
390 buffer
= m_build_buffer_from_buffer(buffer
,"Server: %s\r\n", config
->server_software
);
393 buffer
= m_build_buffer_from_buffer(buffer
,"Date: %s\r\n", PutDate_string(0));
396 if(sh
->location
!=NULL
)
397 buffer
= m_build_buffer_from_buffer(buffer
, "Location: %s\r\n", sh
->location
);
400 if(sh
->last_modified
!=NULL
){
401 buffer
= m_build_buffer_from_buffer(buffer
,"%s %s\r\n", RH_LAST_MODIFIED
, sh
->last_modified
);
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");
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
);
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
){
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
);
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
);
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
);
461 int M_METHOD_get_number(char *method
)
463 if(strcmp(method
, GET_METHOD_STR
)==0)
466 if(strcmp(method
, POST_METHOD_STR
)==0)
469 if(strcmp(method
, HEAD_METHOD_STR
)==0)
472 return METHOD_NOT_ALLOWED
;
475 char *M_METHOD_get_name(int method
)
479 return (char *) GET_METHOD_STR
;
482 return (char *) POST_METHOD_STR
;
485 return (char *) HEAD_METHOD_STR
;
490 int M_METHOD_get_range(char *header
, int range_from_to
[2])
494 range_from_to
[0] = -1;
495 range_from_to
[1] = -1;
500 if((eq_pos
= str_search(header
, "=", 1))<0)
503 if(strncasecmp(header
, "Bytes", eq_pos
)!=0)
506 if((sep_pos
= str_search(header
, "-", 1))<0)
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);
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
);
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
);