2005-05-26 05:06:32 +04:00
/*
Unix SMB / CIFS implementation .
http handling code
Copyright ( C ) Andrew Tridgell 2005
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2005-05-27 04:29:58 +04:00
# include "smbd/service_task.h"
2005-05-26 05:06:32 +04:00
# include "web_server/web_server.h"
# include "smbd/service_stream.h"
# include "lib/events/events.h"
# include "system/time.h"
2005-07-13 04:06:38 +04:00
# include "lib/appweb/esp/esp.h"
2005-08-23 09:21:04 +04:00
# include "lib/appweb/ejs/ejsInternal.h"
2005-05-27 04:29:58 +04:00
# include "dlinklist.h"
2005-06-19 08:21:45 +04:00
# include "lib/tls/tls.h"
2005-05-26 05:06:32 +04:00
2005-05-30 16:18:23 +04:00
# define SWAT_SESSION_KEY "SwatSessionId"
2005-05-30 10:08:54 +04:00
# define HTTP_PREAUTH_URI " / scripting / preauth.esp"
2005-05-27 04:29:58 +04:00
/* state of the esp subsystem for a specific request */
2005-05-26 05:06:32 +04:00
struct esp_state {
struct websrv_context * web ;
struct EspRequest * req ;
2005-05-27 04:29:58 +04:00
struct MprVar variables [ ESP_OBJ_MAX ] ;
struct session_data * session ;
2005-05-26 05:06:32 +04:00
} ;
/*
output the http headers
*/
static void http_output_headers ( struct websrv_context * web )
{
int i ;
char * s ;
DATA_BLOB b ;
2005-05-27 16:05:36 +04:00
uint32_t content_length = 0 ;
2005-05-26 05:06:32 +04:00
const char * response_string = " Unknown Code " ;
const struct {
unsigned code ;
const char * response_string ;
} codes [ ] = {
{ 200 , " OK " } ,
{ 301 , " Moved " } ,
{ 302 , " Found " } ,
{ 303 , " Method " } ,
{ 304 , " Not Modified " } ,
{ 400 , " Bad request " } ,
{ 401 , " Unauthorized " } ,
{ 403 , " Forbidden " } ,
{ 404 , " Not Found " } ,
{ 500 , " Internal Server Error " } ,
{ 501 , " Not implemented " }
} ;
for ( i = 0 ; i < ARRAY_SIZE ( codes ) ; i + + ) {
if ( codes [ i ] . code = = web - > output . response_code ) {
response_string = codes [ i ] . response_string ;
}
}
if ( web - > output . headers = = NULL ) return ;
s = talloc_asprintf ( web , " HTTP/1.0 %u %s \r \n " ,
web - > output . response_code , response_string ) ;
if ( s = = NULL ) return ;
for ( i = 0 ; web - > output . headers [ i ] ; i + + ) {
s = talloc_asprintf_append ( s , " %s \r \n " , web - > output . headers [ i ] ) ;
}
2005-05-27 16:05:36 +04:00
/* work out the content length */
content_length = web - > output . content . length ;
if ( web - > output . fd ! = - 1 ) {
struct stat st ;
fstat ( web - > output . fd , & st ) ;
content_length + = st . st_size ;
}
s = talloc_asprintf_append ( s , " Content-Length: %u \r \n \r \n " , content_length ) ;
2005-05-26 05:06:32 +04:00
if ( s = = NULL ) return ;
b = web - > output . content ;
2005-06-02 03:18:17 +04:00
web - > output . content = data_blob_string_const ( s ) ;
2005-05-26 05:06:32 +04:00
data_blob_append ( web , & web - > output . content , b . data , b . length ) ;
data_blob_free ( & b ) ;
}
2005-05-26 07:05:37 +04:00
/*
return the local path for a URL
*/
static const char * http_local_path ( struct websrv_context * web , const char * url )
{
int i ;
char * path ;
/* check that the url is OK */
if ( url [ 0 ] ! = ' / ' ) return NULL ;
for ( i = 0 ; url [ i ] ; i + + ) {
2005-07-12 12:17:34 +04:00
if ( ( ! isalnum ( ( unsigned char ) url [ i ] ) & & ! strchr ( " ./_ " , url [ i ] ) ) | |
2005-05-26 07:05:37 +04:00
( url [ i ] = = ' . ' & & strchr ( " /. " , url [ i + 1 ] ) ) ) {
return NULL ;
}
}
path = talloc_asprintf ( web , " %s/%s " , lp_swat_directory ( ) , url + 1 ) ;
if ( path = = NULL ) return NULL ;
if ( directory_exist ( path ) ) {
2005-05-27 04:29:58 +04:00
path = talloc_asprintf_append ( path , " /index.esp " ) ;
2005-05-26 07:05:37 +04:00
}
return path ;
}
2005-05-26 06:52:05 +04:00
/*
called when esp wants to read a file to support include ( ) calls
*/
2005-05-26 07:05:37 +04:00
static int http_readFile ( EspHandle handle , char * * buf , int * len , const char * path )
2005-05-26 06:52:05 +04:00
{
2005-05-26 07:05:37 +04:00
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
2005-05-26 06:52:05 +04:00
int fd = - 1 ;
struct stat st ;
* buf = NULL ;
2005-05-26 07:05:37 +04:00
path = http_local_path ( web , path ) ;
if ( path = = NULL ) goto failed ;
2005-05-26 06:52:05 +04:00
fd = open ( path , O_RDONLY ) ;
if ( fd = = - 1 | | fstat ( fd , & st ) ! = 0 | | ! S_ISREG ( st . st_mode ) ) goto failed ;
* buf = talloc_size ( handle , st . st_size + 1 ) ;
if ( * buf = = NULL ) goto failed ;
if ( read ( fd , * buf , st . st_size ) ! = st . st_size ) goto failed ;
( * buf ) [ st . st_size ] = 0 ;
close ( fd ) ;
* len = st . st_size ;
return 0 ;
failed :
2005-05-30 10:08:54 +04:00
DEBUG ( 0 , ( " Failed to read file %s - %s \n " , path , strerror ( errno ) ) ) ;
2005-05-26 06:52:05 +04:00
if ( fd ! = - 1 ) close ( fd ) ;
talloc_free ( * buf ) ;
* buf = NULL ;
return - 1 ;
}
2005-05-26 07:22:38 +04:00
/*
called when esp wants to find the real path of a file
*/
static int http_mapToStorage ( EspHandle handle , char * path , int len , const char * uri , int flags )
{
if ( uri = = NULL | | strlen ( uri ) > = len ) return - 1 ;
strncpy ( path , uri , len ) ;
return 0 ;
}
2005-05-26 05:06:32 +04:00
/*
called when esp wants to output something
*/
2005-07-20 09:40:51 +04:00
static int http_writeBlock ( EspHandle handle , const char * buf , int size )
2005-05-26 05:06:32 +04:00
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
NTSTATUS status ;
status = data_blob_append ( web , & web - > output . content , buf , size ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) return - 1 ;
return size ;
}
/*
set a http header
*/
static void http_setHeader ( EspHandle handle , const char * value , bool allowMultiple )
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
char * p = strchr ( value , ' : ' ) ;
if ( p & & ! allowMultiple & & web - > output . headers ) {
int i ;
for ( i = 0 ; web - > output . headers [ i ] ; i + + ) {
if ( strncmp ( web - > output . headers [ i ] , value , ( p + 1 ) - value ) = = 0 ) {
web - > output . headers [ i ] = talloc_strdup ( web , value ) ;
return ;
}
}
}
web - > output . headers = str_list_add ( web - > output . headers , value ) ;
2005-05-27 04:29:58 +04:00
talloc_steal ( web , web - > output . headers ) ;
2005-05-26 05:06:32 +04:00
}
/*
set a http response code
*/
static void http_setResponseCode ( EspHandle handle , int code )
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
web - > output . response_code = code ;
}
/*
redirect to another web page
*/
static void http_redirect ( EspHandle handle , int code , char * url )
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
const char * host = web - > input . host ;
/* form the full url, unless it already looks like a url */
if ( strchr ( url , ' : ' ) = = NULL ) {
if ( host = = NULL ) {
2006-01-10 01:12:53 +03:00
struct socket_address * socket_address = socket_get_my_addr ( web - > conn - > socket , web ) ;
if ( socket_address = = NULL ) goto internal_error ;
2005-05-26 05:06:32 +04:00
host = talloc_asprintf ( web , " %s:%u " ,
2006-01-10 01:12:53 +03:00
socket_address - > addr , socket_address - > port ) ;
2005-05-26 05:06:32 +04:00
}
if ( host = = NULL ) goto internal_error ;
if ( url [ 0 ] ! = ' / ' ) {
char * p = strrchr ( web - > input . url , ' / ' ) ;
if ( p = = web - > input . url ) {
2005-05-27 15:57:14 +04:00
url = talloc_asprintf ( web , " http%s://%s/%s " ,
2005-06-19 08:21:45 +04:00
tls_enabled ( web - > tls ) ? " s " : " " ,
2005-05-27 15:57:14 +04:00
host , url ) ;
2005-05-26 05:06:32 +04:00
} else {
int dirlen = p - web - > input . url ;
2005-05-27 15:57:14 +04:00
url = talloc_asprintf ( web , " http%s://%s%*.*s/%s " ,
2005-06-19 08:21:45 +04:00
tls_enabled ( web - > tls ) ? " s " : " " ,
2005-05-26 05:06:32 +04:00
host ,
dirlen , dirlen , web - > input . url ,
url ) ;
}
if ( url = = NULL ) goto internal_error ;
}
}
http_setHeader ( handle , talloc_asprintf ( web , " Location: %s " , url ) , 0 ) ;
/* make sure we give a valid redirect code */
if ( code > = 300 & & code < 400 ) {
http_setResponseCode ( handle , code ) ;
} else {
http_setResponseCode ( handle , 302 ) ;
}
return ;
internal_error :
http_error ( web , 500 , " Internal server error " ) ;
}
2005-05-27 04:29:58 +04:00
/*
setup a cookie
*/
static void http_setCookie ( EspHandle handle , const char * name , const char * value ,
int lifetime , const char * path , bool secure )
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
char * buf ;
if ( lifetime > 0 ) {
buf = talloc_asprintf ( web , " Set-Cookie: %s=%s; path=%s; Expires=%s; %s " ,
name , value , path ? path : " / " ,
http_timestring ( web , time ( NULL ) + lifetime ) ,
secure ? " secure " : " " ) ;
} else {
buf = talloc_asprintf ( web , " Set-Cookie: %s=%s; path=%s; %s " ,
name , value , path ? path : " / " ,
secure ? " secure " : " " ) ;
}
http_setHeader ( handle , " Cache-control: no-cache= \" set-cookie \" " , 0 ) ;
http_setHeader ( handle , buf , 0 ) ;
talloc_free ( buf ) ;
}
/*
return the session id
*/
static const char * http_getSessionId ( EspHandle handle )
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
return web - > session - > id ;
}
/*
setup a session
*/
static void http_createSession ( EspHandle handle , int timeout )
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
if ( web - > session ) {
web - > session - > lifetime = timeout ;
http_setCookie ( web , SWAT_SESSION_KEY , web - > session - > id ,
web - > session - > lifetime , " / " , 0 ) ;
}
}
/*
destroy a session
*/
static void http_destroySession ( EspHandle handle )
{
struct websrv_context * web = talloc_get_type ( handle , struct websrv_context ) ;
talloc_free ( web - > session ) ;
web - > session = NULL ;
}
2005-05-26 05:06:32 +04:00
/*
setup for a raw http level error
*/
void http_error ( struct websrv_context * web , int code , const char * info )
{
char * s ;
2005-08-23 09:21:04 +04:00
s = talloc_asprintf ( web , " <HTML><HEAD><TITLE>Error %u</TITLE></HEAD><BODY><H1>Error %u</H1><pre>%s</pre><p></BODY></HTML> \r \n \r \n " ,
2005-05-26 05:06:32 +04:00
code , code , info ) ;
if ( s = = NULL ) {
stream_terminate_connection ( web - > conn , " http_error: out of memory " ) ;
return ;
}
http_writeBlock ( web , s , strlen ( s ) ) ;
http_setResponseCode ( web , code ) ;
http_output_headers ( web ) ;
EVENT_FD_NOT_READABLE ( web - > conn - > event . fde ) ;
EVENT_FD_WRITEABLE ( web - > conn - > event . fde ) ;
2005-05-27 15:57:14 +04:00
web - > output . output_pending = True ;
2005-05-26 05:06:32 +04:00
}
/*
map a unix error code to a http error
*/
void http_error_unix ( struct websrv_context * web , const char * info )
{
int code = 500 ;
switch ( errno ) {
case ENOENT :
case EISDIR :
code = 404 ;
break ;
case EACCES :
code = 403 ;
break ;
}
2005-05-27 04:29:58 +04:00
info = talloc_asprintf ( web , " %s<p>%s<p> \n " , info , strerror ( errno ) ) ;
2005-05-26 05:06:32 +04:00
http_error ( web , code , info ) ;
}
/*
a simple file request
*/
static void http_simple_request ( struct websrv_context * web )
{
const char * url = web - > input . url ;
const char * path ;
struct stat st ;
path = http_local_path ( web , url ) ;
if ( path = = NULL ) goto invalid ;
/* looks ok */
web - > output . fd = open ( path , O_RDONLY ) ;
if ( web - > output . fd = = - 1 ) {
2005-05-30 10:08:54 +04:00
DEBUG ( 0 , ( " Failed to read file %s - %s \n " , path , strerror ( errno ) ) ) ;
2005-05-26 06:06:33 +04:00
http_error_unix ( web , path ) ;
2005-05-26 05:06:32 +04:00
return ;
}
if ( fstat ( web - > output . fd , & st ) ! = 0 | | ! S_ISREG ( st . st_mode ) ) {
close ( web - > output . fd ) ;
goto invalid ;
}
return ;
invalid :
http_error ( web , 400 , " Malformed URL " ) ;
}
/*
setup the standard ESP arrays
*/
static void http_setup_arrays ( struct esp_state * esp )
{
struct websrv_context * web = esp - > web ;
2005-05-27 16:28:59 +04:00
struct esp_data * edata = talloc_get_type ( web - > task - > private , struct esp_data ) ;
2005-05-26 05:06:32 +04:00
struct EspRequest * req = esp - > req ;
2006-01-10 01:12:53 +03:00
struct socket_address * socket_address = socket_get_my_addr ( web - > conn - > socket , esp ) ;
2006-01-10 12:17:58 +03:00
struct socket_address * peer_address = socket_get_peer_addr ( web - > conn - > socket , esp ) ;
2005-05-26 05:06:32 +04:00
char * p ;
2005-05-27 04:29:58 +04:00
# define SETVAR(type, name, value) do { \
const char * v = value ; \
if ( v ) espSetStringVar ( req , type , name , v ) ; \
} while ( 0 )
2005-05-26 05:06:32 +04:00
2005-05-27 04:29:58 +04:00
SETVAR ( ESP_REQUEST_OBJ , " CONTENT_LENGTH " ,
talloc_asprintf ( esp , " %u " , web - > input . content_length ) ) ;
SETVAR ( ESP_REQUEST_OBJ , " QUERY_STRING " , web - > input . query_string ) ;
SETVAR ( ESP_REQUEST_OBJ , " REQUEST_METHOD " , web - > input . post_request ? " POST " : " GET " ) ;
SETVAR ( ESP_REQUEST_OBJ , " REQUEST_URI " , web - > input . url ) ;
p = strrchr ( web - > input . url , ' / ' ) ;
SETVAR ( ESP_REQUEST_OBJ , " SCRIPT_NAME " , p + 1 ) ;
2005-05-27 16:28:59 +04:00
SETVAR ( ESP_REQUEST_OBJ , " SCRIPT_FILENAME " , web - > input . url ) ;
2006-01-10 01:12:53 +03:00
if ( peer_address ) {
2006-01-10 12:17:58 +03:00
struct MprVar mpv = mprObject ( " socket_address " ) ;
mprSetPtrChild ( & mpv , " socket_address " , peer_address ) ;
espSetVar ( req , ESP_REQUEST_OBJ , " REMOTE_SOCKET_ADDRESS " , mpv ) ;
2006-01-10 01:12:53 +03:00
SETVAR ( ESP_REQUEST_OBJ , " REMOTE_ADDR " , peer_address - > addr ) ;
}
2005-05-27 04:29:58 +04:00
p = socket_get_peer_name ( web - > conn - > socket , esp ) ;
SETVAR ( ESP_REQUEST_OBJ , " REMOTE_HOST " , p ) ;
SETVAR ( ESP_REQUEST_OBJ , " REMOTE_USER " , " " ) ;
SETVAR ( ESP_REQUEST_OBJ , " CONTENT_TYPE " , web - > input . content_type ) ;
if ( web - > session ) {
SETVAR ( ESP_REQUEST_OBJ , " SESSION_ID " , web - > session - > id ) ;
2005-05-26 05:06:32 +04:00
}
2005-05-30 16:18:23 +04:00
SETVAR ( ESP_REQUEST_OBJ , " COOKIE_SUPPORT " , web - > input . cookie ? " True " : " False " ) ;
2005-05-26 05:06:32 +04:00
2005-05-27 04:29:58 +04:00
SETVAR ( ESP_HEADERS_OBJ , " HTT_REFERER " , web - > input . referer ) ;
SETVAR ( ESP_HEADERS_OBJ , " HOST " , web - > input . host ) ;
SETVAR ( ESP_HEADERS_OBJ , " ACCEPT_ENCODING " , web - > input . accept_encoding ) ;
SETVAR ( ESP_HEADERS_OBJ , " ACCEPT_LANGUAGE " , web - > input . accept_language ) ;
SETVAR ( ESP_HEADERS_OBJ , " ACCEPT_CHARSET " , web - > input . accept_charset ) ;
SETVAR ( ESP_HEADERS_OBJ , " COOKIE " , web - > input . cookie ) ;
SETVAR ( ESP_HEADERS_OBJ , " USER_AGENT " , web - > input . user_agent ) ;
2006-01-10 01:12:53 +03:00
if ( socket_address ) {
SETVAR ( ESP_SERVER_OBJ , " SERVER_ADDR " , socket_address - > addr ) ;
SETVAR ( ESP_SERVER_OBJ , " SERVER_NAME " , socket_address - > addr ) ;
SETVAR ( ESP_SERVER_OBJ , " SERVER_HOST " , socket_address - > addr ) ;
SETVAR ( ESP_SERVER_OBJ , " SERVER_PORT " ,
talloc_asprintf ( esp , " %u " , socket_address - > port ) ) ;
}
2005-05-27 04:29:58 +04:00
SETVAR ( ESP_SERVER_OBJ , " DOCUMENT_ROOT " , lp_swat_directory ( ) ) ;
2005-06-19 08:21:45 +04:00
SETVAR ( ESP_SERVER_OBJ , " SERVER_PROTOCOL " , tls_enabled ( web - > tls ) ? " https " : " http " ) ;
2005-05-27 04:29:58 +04:00
SETVAR ( ESP_SERVER_OBJ , " SERVER_SOFTWARE " , " SWAT " ) ;
SETVAR ( ESP_SERVER_OBJ , " GATEWAY_INTERFACE " , " CGI/1.1 " ) ;
2005-06-19 08:21:45 +04:00
SETVAR ( ESP_SERVER_OBJ , " TLS_SUPPORT " , tls_support ( edata - > tls_params ) ? " True " : " False " ) ;
2005-05-26 05:06:32 +04:00
}
2005-05-27 08:37:07 +04:00
# if HAVE_SETJMP_H
/* the esp scripting lirary generates exceptions when
it hits a major error . We need to catch these and
report a internal server error via http
*/
# include <setjmp.h>
2005-05-29 08:10:22 +04:00
static jmp_buf ejs_exception_buf ;
2005-05-27 08:37:07 +04:00
static const char * exception_reason ;
2005-05-29 08:10:22 +04:00
void ejs_exception ( const char * reason )
2005-05-27 08:37:07 +04:00
{
2005-08-23 09:21:04 +04:00
Ejs * ep = ejsPtr ( 0 ) ;
if ( ep ) {
ejsSetErrorMsg ( 0 , " %s " , reason ) ;
exception_reason = ep - > error ;
} else {
exception_reason = reason ;
}
DEBUG ( 0 , ( " %s " , exception_reason ) ) ;
2005-05-29 08:10:22 +04:00
longjmp ( ejs_exception_buf , - 1 ) ;
2005-05-27 08:37:07 +04:00
}
# else
2005-05-29 08:10:22 +04:00
void ejs_exception ( const char * reason )
2005-05-27 08:37:07 +04:00
{
DEBUG ( 0 , ( " %s " , reason ) ) ;
smb_panic ( reason ) ;
}
# endif
2005-05-26 05:06:32 +04:00
/*
process a esp request
*/
2005-05-30 10:08:54 +04:00
static void esp_request ( struct esp_state * esp , const char * url )
2005-05-26 05:06:32 +04:00
{
struct websrv_context * web = esp - > web ;
2005-07-17 13:20:52 +04:00
int size ;
2005-05-26 06:52:05 +04:00
int res ;
char * emsg = NULL , * buf ;
2005-05-26 05:06:32 +04:00
2005-05-26 07:05:37 +04:00
if ( http_readFile ( web , & buf , & size , url ) ! = 0 ) {
http_error_unix ( web , url ) ;
2005-05-26 05:06:32 +04:00
return ;
}
2005-05-27 08:37:07 +04:00
# if HAVE_SETJMP_H
2005-05-29 08:10:22 +04:00
if ( setjmp ( ejs_exception_buf ) ! = 0 ) {
2005-05-27 08:37:07 +04:00
http_error ( web , 500 , exception_reason ) ;
return ;
}
# endif
2005-08-20 08:38:35 +04:00
2005-05-26 07:05:37 +04:00
res = espProcessRequest ( esp - > req , url , buf , & emsg ) ;
2005-05-26 05:06:32 +04:00
if ( res ! = 0 & & emsg ) {
2005-07-20 09:40:51 +04:00
http_writeBlock ( web , " <pre> " , 5 ) ;
2005-05-26 07:05:37 +04:00
http_writeBlock ( web , emsg , strlen ( emsg ) ) ;
2005-07-20 09:40:51 +04:00
http_writeBlock ( web , " </pre> " , 6 ) ;
2005-05-26 05:06:32 +04:00
}
2005-05-26 06:52:05 +04:00
talloc_free ( buf ) ;
2005-05-30 10:08:54 +04:00
}
/*
perform pre - authentication on every page is / scripting / preauth . esp
exists . If this script generates any non - whitepace output at all ,
then we don ' t run the requested URL .
note that the preauth is run even for static pages such as images .
*/
static BOOL http_preauth ( struct esp_state * esp )
{
const char * path = http_local_path ( esp - > web , HTTP_PREAUTH_URI ) ;
int i ;
if ( path = = NULL ) {
http_error ( esp - > web , 500 , " Internal server error " ) ;
return False ;
}
if ( ! file_exist ( path ) ) {
/* if the preath script is not installed then allow access */
return True ;
}
esp_request ( esp , HTTP_PREAUTH_URI ) ;
for ( i = 0 ; i < esp - > web - > output . content . length ; i + + ) {
if ( ! isspace ( esp - > web - > output . content . data [ i ] ) ) {
/* if the preauth has generated content, then force it to be
html , so that we can show the login page for failed
access to images */
http_setHeader ( esp - > web , " Content-Type: text/html " , 0 ) ;
return False ;
}
}
data_blob_free ( & esp - > web - > output . content ) ;
return True ;
2005-05-26 05:06:32 +04:00
}
/*
handling of + and % escapes in http variables
*/
static const char * http_unescape ( TALLOC_CTX * mem_ctx , const char * p )
{
char * s0 = talloc_strdup ( mem_ctx , p ) ;
char * s = s0 ;
if ( s = = NULL ) return NULL ;
while ( * s ) {
unsigned v ;
if ( * s = = ' + ' ) * s = ' ' ;
if ( * s = = ' % ' & & sscanf ( s + 1 , " %02x " , & v ) = = 1 ) {
* s = ( char ) v ;
memmove ( s + 1 , s + 3 , strlen ( s + 3 ) + 1 ) ;
}
s + + ;
}
return s0 ;
}
/*
set a form or GET variable
*/
static void esp_putvar ( struct esp_state * esp , const char * var , const char * value )
{
2005-05-30 16:18:23 +04:00
if ( strcasecmp ( var , SWAT_SESSION_KEY ) = = 0 ) {
/* special case support for browsers without cookie
support */
esp - > web - > input . session_key = talloc_strdup ( esp , value ) ;
} else {
mprSetPropertyValue ( & esp - > variables [ ESP_FORM_OBJ ] ,
http_unescape ( esp , var ) ,
mprCreateStringVar ( http_unescape ( esp , value ) , 0 ) ) ;
}
2005-05-26 05:06:32 +04:00
}
/*
parse the variables in a POST style request
*/
static NTSTATUS http_parse_post ( struct esp_state * esp )
{
DATA_BLOB b = esp - > web - > input . partial ;
while ( b . length ) {
char * p , * line ;
size_t len ;
p = memchr ( b . data , ' & ' , b . length ) ;
if ( p = = NULL ) {
len = b . length ;
} else {
len = p - ( char * ) b . data ;
}
2005-06-02 03:18:17 +04:00
line = talloc_strndup ( esp , ( char * ) b . data , len ) ;
2005-05-26 05:06:32 +04:00
NT_STATUS_HAVE_NO_MEMORY ( line ) ;
p = strchr ( line , ' = ' ) ;
if ( p ) {
* p = 0 ;
esp_putvar ( esp , line , p + 1 ) ;
}
talloc_free ( line ) ;
b . length - = len ;
b . data + = len ;
if ( b . length > 0 ) {
b . length - - ;
b . data + + ;
}
}
return NT_STATUS_OK ;
}
/*
parse the variables in a GET style request
*/
static NTSTATUS http_parse_get ( struct esp_state * esp )
{
struct websrv_context * web = esp - > web ;
char * p , * s , * tok ;
char * pp ;
p = strchr ( web - > input . url , ' ? ' ) ;
web - > input . query_string = p + 1 ;
* p = 0 ;
s = talloc_strdup ( esp , esp - > web - > input . query_string ) ;
NT_STATUS_HAVE_NO_MEMORY ( s ) ;
for ( tok = strtok_r ( s , " &; " , & pp ) ; tok ; tok = strtok_r ( NULL , " &; " , & pp ) ) {
p = strchr ( tok , ' = ' ) ;
if ( p ) {
* p = 0 ;
esp_putvar ( esp , tok , p + 1 ) ;
}
}
return NT_STATUS_OK ;
}
/*
2005-05-27 04:29:58 +04:00
called when a session times out
2005-05-26 05:06:32 +04:00
*/
2005-05-27 04:29:58 +04:00
static void session_timeout ( struct event_context * ev , struct timed_event * te ,
struct timeval t , void * private )
2005-05-26 05:06:32 +04:00
{
2005-05-27 04:29:58 +04:00
struct session_data * s = talloc_get_type ( private , struct session_data ) ;
talloc_free ( s ) ;
}
2005-05-26 05:06:32 +04:00
2005-05-27 04:29:58 +04:00
/*
destroy a session
*/
static int session_destructor ( void * ptr )
{
struct session_data * s = talloc_get_type ( ptr , struct session_data ) ;
DLIST_REMOVE ( s - > edata - > sessions , s ) ;
return 0 ;
}
/*
setup the session for this request
*/
static void http_setup_session ( struct esp_state * esp )
{
const char * session_key = SWAT_SESSION_KEY ;
char * p ;
const char * cookie = esp - > web - > input . cookie ;
const char * key = NULL ;
struct esp_data * edata = talloc_get_type ( esp - > web - > task - > private , struct esp_data ) ;
struct session_data * s ;
2005-05-30 16:57:31 +04:00
BOOL generated_key = False ;
2005-05-27 04:29:58 +04:00
/* look for our session key */
if ( cookie & & ( p = strstr ( cookie , session_key ) ) & &
p [ strlen ( session_key ) ] = = ' = ' ) {
p + = strlen ( session_key ) + 1 ;
key = talloc_strndup ( esp , p , strcspn ( p , " ; " ) ) ;
2005-05-26 05:06:32 +04:00
}
2005-05-27 04:29:58 +04:00
2005-05-30 16:18:23 +04:00
if ( key = = NULL & & esp - > web - > input . session_key ) {
key = esp - > web - > input . session_key ;
} else if ( key = = NULL ) {
key = generate_random_str_list ( esp , 16 , " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ " ) ;
2005-05-30 16:57:31 +04:00
generated_key = True ;
2005-05-27 04:29:58 +04:00
}
/* try to find this session in the existing session list */
for ( s = edata - > sessions ; s ; s = s - > next ) {
2005-05-30 16:18:23 +04:00
if ( strcmp ( key , s - > id ) = = 0 ) {
break ;
}
2005-05-27 04:29:58 +04:00
}
if ( s = = NULL ) {
/* create a new session */
s = talloc_zero ( edata , struct session_data ) ;
s - > id = talloc_steal ( s , key ) ;
s - > data = NULL ;
s - > te = NULL ;
s - > edata = edata ;
2005-07-23 15:11:37 +04:00
s - > lifetime = lp_parm_int ( - 1 , " web " , " sessiontimeout " , 900 ) ;
2005-05-27 04:29:58 +04:00
DLIST_ADD ( edata - > sessions , s ) ;
talloc_set_destructor ( s , session_destructor ) ;
2005-05-30 16:57:31 +04:00
if ( ! generated_key ) {
mprSetPropertyValue ( & esp - > variables [ ESP_REQUEST_OBJ ] ,
" SESSION_EXPIRED " , mprCreateStringVar ( " True " , 0 ) ) ;
}
2005-05-27 04:29:58 +04:00
}
http_setCookie ( esp - > web , session_key , key , s - > lifetime , " / " , 0 ) ;
if ( s - > data ) {
mprCopyVar ( & esp - > variables [ ESP_SESSION_OBJ ] , s - > data , MPR_DEEP_COPY ) ;
}
esp - > web - > session = s ;
2005-05-26 05:06:32 +04:00
}
2005-05-27 04:29:58 +04:00
/* callbacks for esp processing */
static const struct Esp esp_control = {
. maxScriptSize = 60000 ,
. writeBlock = http_writeBlock ,
. setHeader = http_setHeader ,
. redirect = http_redirect ,
. setResponseCode = http_setResponseCode ,
. readFile = http_readFile ,
. mapToStorage = http_mapToStorage ,
. setCookie = http_setCookie ,
. createSession = http_createSession ,
. destroySession = http_destroySession ,
. getSessionId = http_getSessionId
} ;
2005-05-26 05:06:32 +04:00
/*
process a complete http request
*/
void http_process_input ( struct websrv_context * web )
{
NTSTATUS status ;
struct esp_state * esp ;
2005-05-27 04:29:58 +04:00
struct esp_data * edata = talloc_get_type ( web - > task - > private , struct esp_data ) ;
2005-05-26 05:06:32 +04:00
char * p ;
2005-08-07 19:20:25 +04:00
void * save_mpr_ctx = mprMemCtx ( ) ;
2005-08-07 19:45:59 +04:00
void * ejs_save = ejs_save_state ( ) ;
2005-05-26 05:06:32 +04:00
int i ;
const char * file_type = NULL ;
2005-05-27 04:29:58 +04:00
BOOL esp_enable = False ;
2005-05-26 05:06:32 +04:00
const struct {
const char * extension ;
const char * mime_type ;
2005-05-27 04:29:58 +04:00
BOOL esp_enable ;
2005-05-26 05:06:32 +04:00
} mime_types [ ] = {
{ " gif " , " image/gif " } ,
{ " png " , " image/png " } ,
{ " jpg " , " image/jpeg " } ,
2005-05-27 04:29:58 +04:00
{ " txt " , " text/plain " } ,
{ " ico " , " image/x-icon " } ,
2005-05-30 10:08:54 +04:00
{ " css " , " text/css " } ,
2005-05-27 04:29:58 +04:00
{ " esp " , " text/html " , True }
2005-05-26 05:06:32 +04:00
} ;
esp = talloc_zero ( web , struct esp_state ) ;
if ( esp = = NULL ) goto internal_error ;
esp - > web = web ;
mprSetCtx ( esp ) ;
if ( espOpen ( & esp_control ) ! = 0 ) goto internal_error ;
2005-05-27 04:29:58 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( esp - > variables ) ; i + + ) {
esp - > variables [ i ] = mprCreateUndefinedVar ( ) ;
}
esp - > variables [ ESP_HEADERS_OBJ ] = mprCreateObjVar ( " headers " , ESP_HASH_SIZE ) ;
esp - > variables [ ESP_FORM_OBJ ] = mprCreateObjVar ( " form " , ESP_HASH_SIZE ) ;
esp - > variables [ ESP_APPLICATION_OBJ ] = mprCreateObjVar ( " application " , ESP_HASH_SIZE ) ;
esp - > variables [ ESP_COOKIES_OBJ ] = mprCreateObjVar ( " cookies " , ESP_HASH_SIZE ) ;
esp - > variables [ ESP_FILES_OBJ ] = mprCreateObjVar ( " files " , ESP_HASH_SIZE ) ;
esp - > variables [ ESP_REQUEST_OBJ ] = mprCreateObjVar ( " request " , ESP_HASH_SIZE ) ;
esp - > variables [ ESP_SERVER_OBJ ] = mprCreateObjVar ( " server " , ESP_HASH_SIZE ) ;
esp - > variables [ ESP_SESSION_OBJ ] = mprCreateObjVar ( " session " , ESP_HASH_SIZE ) ;
if ( edata - > application_data ) {
mprCopyVar ( & esp - > variables [ ESP_APPLICATION_OBJ ] ,
edata - > application_data , MPR_DEEP_COPY ) ;
}
2005-05-29 15:35:56 +04:00
smb_setup_ejs_functions ( ) ;
2005-05-27 07:58:12 +04:00
2005-05-26 05:06:32 +04:00
if ( web - > input . url = = NULL ) {
http_error ( web , 400 , " You must specify a GET or POST request " ) ;
2005-08-07 19:20:25 +04:00
mprSetCtx ( save_mpr_ctx ) ;
2005-08-07 19:45:59 +04:00
ejs_restore_state ( ejs_save ) ;
2005-05-26 05:06:32 +04:00
return ;
}
2005-05-27 04:29:58 +04:00
/* parse any form or get variables */
2005-05-26 05:06:32 +04:00
if ( web - > input . post_request ) {
status = http_parse_post ( esp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
http_error ( web , 400 , " Malformed POST data " ) ;
2005-08-07 19:20:25 +04:00
mprSetCtx ( save_mpr_ctx ) ;
2005-08-07 19:45:59 +04:00
ejs_restore_state ( ejs_save ) ;
2005-05-26 05:06:32 +04:00
return ;
}
2005-05-30 16:18:23 +04:00
}
if ( strchr ( web - > input . url , ' ? ' ) ) {
2005-05-26 05:06:32 +04:00
status = http_parse_get ( esp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
http_error ( web , 400 , " Malformed GET data " ) ;
2005-08-07 19:20:25 +04:00
mprSetCtx ( save_mpr_ctx ) ;
2005-08-07 19:45:59 +04:00
ejs_restore_state ( ejs_save ) ;
2005-05-26 05:06:32 +04:00
return ;
}
}
2005-05-30 16:18:23 +04:00
http_setup_session ( esp ) ;
esp - > req = espCreateRequest ( web , web - > input . url , esp - > variables ) ;
if ( esp - > req = = NULL ) goto internal_error ;
2005-05-27 04:29:58 +04:00
/* work out the mime type */
2005-05-26 05:06:32 +04:00
p = strrchr ( web - > input . url , ' . ' ) ;
2005-05-27 15:57:14 +04:00
if ( p = = NULL ) {
esp_enable = True ;
}
2005-05-26 05:06:32 +04:00
for ( i = 0 ; p & & i < ARRAY_SIZE ( mime_types ) ; i + + ) {
if ( strcmp ( mime_types [ i ] . extension , p + 1 ) = = 0 ) {
file_type = mime_types [ i ] . mime_type ;
2005-05-27 04:29:58 +04:00
esp_enable = mime_types [ i ] . esp_enable ;
2005-05-26 05:06:32 +04:00
}
}
if ( file_type = = NULL ) {
file_type = " text/html " ;
}
/* setup basic headers */
http_setResponseCode ( web , 200 ) ;
http_setHeader ( web , talloc_asprintf ( esp , " Date: %s " ,
http_timestring ( esp , time ( NULL ) ) ) , 0 ) ;
http_setHeader ( web , " Server: Samba " , 0 ) ;
http_setHeader ( web , " Connection: close " , 0 ) ;
http_setHeader ( web , talloc_asprintf ( esp , " Content-Type: %s " , file_type ) , 0 ) ;
2005-05-30 10:08:54 +04:00
http_setup_arrays ( esp ) ;
/* possibly do pre-authentication */
if ( http_preauth ( esp ) ) {
if ( esp_enable ) {
esp_request ( esp , web - > input . url ) ;
} else {
http_simple_request ( web ) ;
}
}
2005-08-20 08:38:35 +04:00
if ( web - > conn = = NULL ) {
/* the connection has been terminated above us, probably
via a timeout */
goto internal_error ;
}
2005-05-30 10:08:54 +04:00
if ( ! web - > output . output_pending ) {
http_output_headers ( web ) ;
EVENT_FD_WRITEABLE ( web - > conn - > event . fde ) ;
web - > output . output_pending = True ;
2005-05-26 05:06:32 +04:00
}
2005-05-27 04:29:58 +04:00
/* copy any application data to long term storage in edata */
talloc_free ( edata - > application_data ) ;
edata - > application_data = talloc_zero ( edata , struct MprVar ) ;
mprSetCtx ( edata - > application_data ) ;
2005-05-30 16:18:23 +04:00
mprCopyVar ( edata - > application_data , & esp - > variables [ ESP_APPLICATION_OBJ ] ,
MPR_DEEP_COPY ) ;
2005-07-21 11:52:31 +04:00
mprSetCtx ( esp ) ;
2005-05-27 04:29:58 +04:00
/* copy any session data */
if ( web - > session ) {
talloc_free ( web - > session - > data ) ;
web - > session - > data = talloc_zero ( web - > session , struct MprVar ) ;
2005-05-27 04:43:24 +04:00
if ( esp - > variables [ ESP_SESSION_OBJ ] . properties = = NULL | |
esp - > variables [ ESP_SESSION_OBJ ] . properties [ 0 ] . numItems = = 0 ) {
talloc_free ( web - > session ) ;
web - > session = NULL ;
} else {
2005-07-21 11:52:31 +04:00
mprSetCtx ( web - > session - > data ) ;
2005-05-27 04:43:24 +04:00
mprCopyVar ( web - > session - > data , & esp - > variables [ ESP_SESSION_OBJ ] ,
MPR_DEEP_COPY ) ;
/* setup the timeout for the session data */
2005-07-21 11:52:31 +04:00
mprSetCtx ( esp ) ;
2005-05-27 04:43:24 +04:00
talloc_free ( web - > session - > te ) ;
web - > session - > te = event_add_timed ( web - > conn - > event . ctx , web - > session ,
timeval_current_ofs ( web - > session - > lifetime , 0 ) ,
session_timeout , web - > session ) ;
}
2005-05-27 04:29:58 +04:00
}
2005-05-26 05:06:32 +04:00
talloc_free ( esp ) ;
2005-08-07 19:20:25 +04:00
mprSetCtx ( save_mpr_ctx ) ;
2005-08-07 19:45:59 +04:00
ejs_restore_state ( ejs_save ) ;
2005-05-26 05:06:32 +04:00
return ;
internal_error :
2005-07-21 11:52:31 +04:00
mprSetCtx ( esp ) ;
2005-05-26 05:06:32 +04:00
talloc_free ( esp ) ;
2005-08-20 08:38:35 +04:00
if ( web - > conn ! = NULL ) {
http_error ( web , 500 , " Internal server error " ) ;
}
2005-08-07 19:20:25 +04:00
mprSetCtx ( save_mpr_ctx ) ;
2005-08-07 19:45:59 +04:00
ejs_restore_state ( ejs_save ) ;
2005-05-26 05:06:32 +04:00
}
/*
parse one line of header input
*/
NTSTATUS http_parse_header ( struct websrv_context * web , const char * line )
{
if ( line [ 0 ] = = 0 ) {
web - > input . end_of_headers = True ;
} else if ( strncasecmp ( line , " GET " , 4 ) = = 0 ) {
web - > input . url = talloc_strndup ( web , & line [ 4 ] , strcspn ( & line [ 4 ] , " \t " ) ) ;
} else if ( strncasecmp ( line , " POST " , 5 ) = = 0 ) {
web - > input . post_request = True ;
web - > input . url = talloc_strndup ( web , & line [ 5 ] , strcspn ( & line [ 5 ] , " \t " ) ) ;
} else if ( strchr ( line , ' : ' ) = = NULL ) {
http_error ( web , 400 , " This server only accepts GET and POST requests " ) ;
return NT_STATUS_INVALID_PARAMETER ;
} else if ( strncasecmp ( line , " Content-Length: " , 16 ) = = 0 ) {
web - > input . content_length = strtoul ( & line [ 16 ] , NULL , 10 ) ;
} else {
# define PULL_HEADER(v, s) do { \
if ( strncmp ( line , s , strlen ( s ) ) = = 0 ) { \
web - > input . v = talloc_strdup ( web , & line [ strlen ( s ) ] ) ; \
return NT_STATUS_OK ; \
} \
} while ( 0 )
PULL_HEADER ( content_type , " Content-Type: " ) ;
PULL_HEADER ( user_agent , " User-Agent: " ) ;
PULL_HEADER ( referer , " Referer: " ) ;
PULL_HEADER ( host , " Host: " ) ;
PULL_HEADER ( accept_encoding , " Accept-Encoding: " ) ;
2005-05-27 04:29:58 +04:00
PULL_HEADER ( accept_language , " Accept-Language: " ) ;
PULL_HEADER ( accept_charset , " Accept-Charset: " ) ;
PULL_HEADER ( cookie , " Cookie: " ) ;
2005-05-26 05:06:32 +04:00
}
/* ignore all other headers for now */
return NT_STATUS_OK ;
}
2005-05-27 04:29:58 +04:00
/*
setup the esp processor - called at task initialisation
*/
NTSTATUS http_setup_esp ( struct task_server * task )
{
struct esp_data * edata ;
2005-05-27 15:57:14 +04:00
edata = talloc_zero ( task , struct esp_data ) ;
2005-05-27 04:29:58 +04:00
NT_STATUS_HAVE_NO_MEMORY ( edata ) ;
task - > private = edata ;
2005-06-19 08:21:45 +04:00
edata - > tls_params = tls_initialise ( edata ) ;
NT_STATUS_HAVE_NO_MEMORY ( edata - > tls_params ) ;
2005-05-27 04:29:58 +04:00
return NT_STATUS_OK ;
}