1997-09-11 06:17:16 +04:00
/*
some simple CGI helper routines
1998-01-22 16:27:43 +03:00
Copyright ( C ) Andrew Tridgell 1997 - 1998
1997-09-11 06:17:16 +04:00
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 .
*/
1998-03-10 07:56:58 +03:00
# include "includes.h"
# include "smb.h"
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
# define MAX_VARIABLES 10000
1998-03-10 07:56:58 +03:00
/* set the expiry on fixed pages */
# define EXPIRY_TIME (60*60*24*7)
1998-03-21 06:03:59 +03:00
# define CGI_LOGGING 0
1998-03-10 07:56:58 +03:00
1997-11-22 10:51:23 +03:00
# ifdef DEBUG_COMMENTS
extern void print_title ( char * fmt , . . . ) ;
# endif
1997-09-11 06:17:16 +04:00
struct var {
char * name ;
char * value ;
} ;
static struct var variables [ MAX_VARIABLES ] ;
static int num_variables ;
1997-11-22 10:51:23 +03:00
static int content_length ;
static int request_post ;
static char * query_string ;
1998-03-08 17:14:49 +03:00
static char * baseurl ;
1998-03-14 07:13:24 +03:00
static char * pathinfo ;
1997-11-22 10:51:23 +03:00
static void unescape ( char * buf )
{
char * p = buf ;
while ( ( p = strchr ( p , ' + ' ) ) )
* p = ' ' ;
p = buf ;
while ( p & & * p & & ( p = strchr ( p , ' % ' ) ) ) {
int c1 = p [ 1 ] ;
int c2 = p [ 2 ] ;
if ( c1 > = ' 0 ' & & c1 < = ' 9 ' )
c1 = c1 - ' 0 ' ;
else if ( c1 > = ' A ' & & c1 < = ' F ' )
c1 = 10 + c1 - ' A ' ;
else if ( c1 > = ' a ' & & c1 < = ' f ' )
c1 = 10 + c1 - ' a ' ;
else { p + + ; continue ; }
if ( c2 > = ' 0 ' & & c2 < = ' 9 ' )
c2 = c2 - ' 0 ' ;
else if ( c2 > = ' A ' & & c2 < = ' F ' )
c2 = 10 + c2 - ' A ' ;
else if ( c2 > = ' a ' & & c2 < = ' f ' )
c2 = 10 + c2 - ' a ' ;
else { p + + ; continue ; }
* p = ( c1 < < 4 ) | c2 ;
memcpy ( p + 1 , p + 3 , strlen ( p + 3 ) + 1 ) ;
p + + ;
}
}
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
static char * grab_line ( FILE * f , int * cl )
1997-09-11 06:17:16 +04:00
{
1997-11-22 10:51:23 +03:00
char * ret ;
1997-09-11 06:17:16 +04:00
int i = 0 ;
1997-11-22 10:51:23 +03:00
int len = 1024 ;
ret = ( char * ) malloc ( len ) ;
if ( ! ret ) return NULL ;
1997-09-11 06:17:16 +04:00
while ( ( * cl ) ) {
1997-11-22 10:51:23 +03:00
int c = fgetc ( f ) ;
1997-09-11 06:17:16 +04:00
( * cl ) - - ;
if ( c = = EOF ) {
( * cl ) = 0 ;
break ;
}
if ( c = = ' \r ' ) continue ;
if ( strchr ( " \n & " , c ) ) break ;
1997-11-22 10:51:23 +03:00
ret [ i + + ] = c ;
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
if ( i = = len - 1 ) {
char * ret2 ;
ret2 = ( char * ) realloc ( ret , len * 2 ) ;
if ( ! ret2 ) return ret ;
len * = 2 ;
ret = ret2 ;
}
1997-09-11 06:17:16 +04:00
}
1997-11-22 10:51:23 +03:00
ret [ i ] = 0 ;
return ret ;
1997-09-11 06:17:16 +04:00
}
/***************************************************************************
1997-11-22 10:51:23 +03:00
load all the variables passed to the CGI program . May have multiple variables
with the same name and the same or different values . Takes a file parameter
for simulating CGI invocation eg loading saved preferences .
1997-09-11 06:17:16 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1997-11-22 10:51:23 +03:00
void cgi_load_variables ( FILE * f1 )
1997-09-11 06:17:16 +04:00
{
1997-11-22 10:51:23 +03:00
FILE * f = f1 ;
static char * line ;
char * p , * s , * tok ;
1997-09-11 06:17:16 +04:00
int len ;
1997-11-22 10:51:23 +03:00
# ifdef DEBUG_COMMENTS
char dummy [ 100 ] = " " ;
print_title ( dummy ) ;
printf ( " <!== Start dump in cgi_load_variables() %s ==> \n " , __FILE__ ) ;
# endif
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
if ( ! f1 ) {
f = stdin ;
if ( ! content_length ) {
p = getenv ( " CONTENT_LENGTH " ) ;
len = p ? atoi ( p ) : 0 ;
} else {
len = content_length ;
}
} else {
fseek ( f , 0 , SEEK_END ) ;
len = ftell ( f ) ;
fseek ( f , 0 , SEEK_SET ) ;
}
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
if ( len > 0 & &
( f1 | | request_post | |
( ( s = getenv ( " REQUEST_METHOD " ) ) & &
strcasecmp ( s , " POST " ) = = 0 ) ) ) {
while ( len & & ( line = grab_line ( f , & len ) ) ) {
p = strchr ( line , ' = ' ) ;
if ( ! p ) continue ;
* p = 0 ;
variables [ num_variables ] . name = strdup ( line ) ;
variables [ num_variables ] . value = strdup ( p + 1 ) ;
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
free ( line ) ;
if ( ! variables [ num_variables ] . name | |
! variables [ num_variables ] . value )
continue ;
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
unescape ( variables [ num_variables ] . value ) ;
unescape ( variables [ num_variables ] . name ) ;
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
# ifdef DEBUG_COMMENTS
printf ( " <!== POST var %s has value \" %s \" ==> \n " ,
variables [ num_variables ] . name ,
variables [ num_variables ] . value ) ;
1997-09-11 06:17:16 +04:00
# endif
1997-11-22 10:51:23 +03:00
num_variables + + ;
if ( num_variables = = MAX_VARIABLES ) break ;
}
}
1997-09-11 06:17:16 +04:00
1997-11-22 10:51:23 +03:00
if ( f1 ) {
# ifdef DEBUG_COMMENTS
printf ( " <!== End dump in cgi_load_variables() ==> \n " ) ;
# endif
return ;
1997-09-11 06:17:16 +04:00
}
fclose ( stdin ) ;
1997-11-22 10:51:23 +03:00
if ( ( s = query_string ) | | ( s = getenv ( " QUERY_STRING " ) ) ) {
for ( tok = strtok ( s , " &; " ) ; tok ; tok = strtok ( NULL , " &; " ) ) {
p = strchr ( tok , ' = ' ) ;
if ( ! p ) continue ;
* p = 0 ;
variables [ num_variables ] . name = strdup ( tok ) ;
variables [ num_variables ] . value = strdup ( p + 1 ) ;
if ( ! variables [ num_variables ] . name | |
! variables [ num_variables ] . value )
continue ;
unescape ( variables [ num_variables ] . value ) ;
unescape ( variables [ num_variables ] . name ) ;
# ifdef DEBUG_COMMENTS
printf ( " <!== Commandline var %s has value \" %s \" ==> \n " ,
variables [ num_variables ] . name ,
variables [ num_variables ] . value ) ;
# endif
num_variables + + ;
if ( num_variables = = MAX_VARIABLES ) break ;
}
}
# ifdef DEBUG_COMMENTS
printf ( " <!== End dump in cgi_load_variables() ==> \n " ) ;
# endif
1997-09-11 06:17:16 +04:00
}
/***************************************************************************
find a variable passed via CGI
1997-11-22 10:51:23 +03:00
Doesn ' t quite do what you think in the case of POST text variables , because
if they exist they might have a value of " " or even " " , depending on the
browser . Also doesn ' t allow for variables [ ] containing multiple variables
with the same name and the same or different values .
1997-09-11 06:17:16 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char * cgi_variable ( char * name )
{
int i ;
for ( i = 0 ; i < num_variables ; i + + )
if ( strcmp ( variables [ i ] . name , name ) = = 0 )
return variables [ i ] . value ;
return NULL ;
}
1997-11-22 10:51:23 +03:00
/***************************************************************************
tell a browser about a fatal error in the http processing
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void cgi_setup_error ( char * err , char * header , char * info )
{
1998-10-26 13:55:29 +03:00
printf ( " HTTP/1.0 %s \r \n %sConnection: close \r \n Content-Type: text/html \r \n \r \n <HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML> \r \n " , err , header , err , err , info ) ;
1997-11-22 10:51:23 +03:00
exit ( 0 ) ;
}
/***************************************************************************
decode a base64 string in - place - simple and slow algorithm
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void base64_decode ( char * s )
{
char * b64 = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ " ;
1998-09-01 10:01:19 +04:00
int bit_offset , byte_offset , idx , i , n ;
1997-11-22 10:51:23 +03:00
unsigned char * d = ( unsigned char * ) s ;
char * p ;
1998-09-01 10:01:19 +04:00
n = i = 0 ;
1997-11-22 10:51:23 +03:00
while ( * s & & ( p = strchr ( b64 , * s ) ) ) {
idx = ( int ) ( p - b64 ) ;
byte_offset = ( i * 6 ) / 8 ;
bit_offset = ( i * 6 ) % 8 ;
d [ byte_offset ] & = ~ ( ( 1 < < ( 8 - bit_offset ) ) - 1 ) ;
if ( bit_offset < 3 ) {
d [ byte_offset ] | = ( idx < < ( 2 - bit_offset ) ) ;
1998-09-01 10:01:19 +04:00
n = byte_offset + 1 ;
1997-11-22 10:51:23 +03:00
} else {
d [ byte_offset ] | = ( idx > > ( bit_offset - 2 ) ) ;
d [ byte_offset + 1 ] = 0 ;
d [ byte_offset + 1 ] | = ( idx < < ( 8 - ( bit_offset - 2 ) ) ) & 0xFF ;
1998-09-01 10:01:19 +04:00
n = byte_offset + 2 ;
1997-11-22 10:51:23 +03:00
}
s + + ; i + + ;
}
1998-09-01 10:01:19 +04:00
/* null terminate */
d [ n ] = 0 ;
1997-11-22 10:51:23 +03:00
}
/***************************************************************************
handle a http authentication line
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int cgi_handle_authorization ( char * line )
{
char * p , * user , * pass ;
if ( strncasecmp ( line , " Basic " , 6 ) ) {
cgi_setup_error ( " 401 Bad Authorization " , " " ,
" Only basic authorization is understood " ) ;
}
line + = 6 ;
while ( line [ 0 ] = = ' ' ) line + + ;
base64_decode ( line ) ;
if ( ! ( p = strchr ( line , ' : ' ) ) ) {
cgi_setup_error ( " 401 Bad Authorization " , " " ,
" username/password must be supplied " ) ;
}
* p = 0 ;
user = line ;
pass = p + 1 ;
/* currently only allow connections as root */
1998-03-12 05:42:39 +03:00
if ( strcmp ( user , " root " ) ) {
1997-11-22 10:51:23 +03:00
cgi_setup_error ( " 401 Bad Authorization " , " " ,
" incorrect username/password " ) ;
}
1998-08-10 11:04:53 +04:00
return pass_check ( user , pass , strlen ( pass ) , NULL , NULL ) ;
1997-11-22 10:51:23 +03:00
}
/***************************************************************************
handle a file download
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void cgi_download ( char * file )
{
1998-09-02 00:11:54 +04:00
SMB_STRUCT_STAT st ;
1997-11-22 10:51:23 +03:00
char buf [ 1024 ] ;
1997-11-23 05:42:22 +03:00
int fd , l , i ;
1997-11-22 10:51:23 +03:00
char * p ;
1997-11-23 05:42:22 +03:00
/* sanitise the filename */
for ( i = 0 ; file [ i ] ; i + + ) {
1998-08-15 05:19:26 +04:00
if ( ! isalnum ( ( int ) file [ i ] ) & & ! strchr ( " /.-_ " , file [ i ] ) ) {
1997-11-23 05:42:22 +03:00
cgi_setup_error ( " 404 File Not Found " , " " ,
" Illegal character in filename " ) ;
}
}
1997-11-22 10:51:23 +03:00
if ( ! file_exist ( file , & st ) ) {
cgi_setup_error ( " 404 File Not Found " , " " ,
" The requested file was not found " ) ;
}
fd = open ( file , O_RDONLY ) ;
if ( fd = = - 1 ) {
cgi_setup_error ( " 404 File Not Found " , " " ,
" The requested file was not found " ) ;
}
1998-10-26 13:55:29 +03:00
printf ( " HTTP/1.0 200 OK \r \n " ) ;
1997-11-22 10:51:23 +03:00
if ( ( p = strrchr ( file , ' . ' ) ) ) {
1998-03-10 07:56:58 +03:00
if ( strcmp ( p , " .gif " ) = = 0 ) {
1997-11-22 10:51:23 +03:00
printf ( " Content-Type: image/gif \r \n " ) ;
1998-03-10 07:56:58 +03:00
} else if ( strcmp ( p , " .jpg " ) = = 0 ) {
printf ( " Content-Type: image/jpeg \r \n " ) ;
1997-11-22 10:51:23 +03:00
} else {
printf ( " Content-Type: text/html \r \n " ) ;
}
}
1998-03-10 07:56:58 +03:00
printf ( " Expires: %s \r \n " , http_timestring ( time ( NULL ) + EXPIRY_TIME ) ) ;
1997-11-22 10:51:23 +03:00
printf ( " Content-Length: %d \r \n \r \n " , ( int ) st . st_size ) ;
while ( ( l = read ( fd , buf , sizeof ( buf ) ) ) > 0 ) {
fwrite ( buf , 1 , l , stdout ) ;
}
close ( fd ) ;
exit ( 0 ) ;
}
/***************************************************************************
setup the cgi framework , handling the possability that this program is either
run as a true cgi program by a web browser or is itself a mini web server
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-03-08 17:31:50 +03:00
void cgi_setup ( char * rootdir , int auth_required )
1997-11-22 10:51:23 +03:00
{
int authenticated = 0 ;
char line [ 1024 ] ;
char * url = NULL ;
char * p ;
1998-03-10 07:56:58 +03:00
# if CGI_LOGGING
1998-03-14 07:13:24 +03:00
FILE * f ;
1998-03-10 07:56:58 +03:00
# endif
1997-11-22 10:51:23 +03:00
if ( chdir ( rootdir ) ) {
cgi_setup_error ( " 400 Server Error " , " " ,
" chdir failed - the server is not configured correctly " ) ;
}
if ( getenv ( " CONTENT_LENGTH " ) | | getenv ( " REQUEST_METHOD " ) ) {
/* assume we are running under a real web server */
return ;
}
1998-03-14 07:13:24 +03:00
# if CGI_LOGGING
f = fopen ( " /tmp/cgi.log " , " a " ) ;
1998-03-17 15:37:06 +03:00
if ( f ) fprintf ( f , " \n [Date: %s %s (%s)] \n " ,
http_timestring ( time ( NULL ) ) ,
client_name ( 1 ) , client_addr ( 1 ) ) ;
1998-03-14 07:13:24 +03:00
# endif
1997-11-22 10:51:23 +03:00
/* we are a mini-web server. We need to read the request from stdin
and handle authentication etc */
while ( fgets ( line , sizeof ( line ) - 1 , stdin ) ) {
1998-03-10 07:56:58 +03:00
# if CGI_LOGGING
1998-03-14 07:13:24 +03:00
if ( f ) fputs ( line , f ) ;
1998-03-10 07:56:58 +03:00
# endif
1997-11-22 10:51:23 +03:00
if ( line [ 0 ] = = ' \r ' | | line [ 0 ] = = ' \n ' ) break ;
if ( strncasecmp ( line , " GET " , 4 ) = = 0 ) {
url = strdup ( & line [ 4 ] ) ;
} else if ( strncasecmp ( line , " POST " , 5 ) = = 0 ) {
request_post = 1 ;
url = strdup ( & line [ 5 ] ) ;
} else if ( strncasecmp ( line , " PUT " , 4 ) = = 0 ) {
cgi_setup_error ( " 400 Bad Request " , " " ,
" This server does not accept PUT requests " ) ;
} else if ( strncasecmp ( line , " Authorization: " , 15 ) = = 0 ) {
authenticated = cgi_handle_authorization ( & line [ 15 ] ) ;
} else if ( strncasecmp ( line , " Content-Length: " , 16 ) = = 0 ) {
content_length = atoi ( & line [ 16 ] ) ;
}
/* ignore all other requests! */
}
1998-03-10 07:56:58 +03:00
# if CGI_LOGGING
1998-03-14 07:13:24 +03:00
if ( f ) fclose ( f ) ;
1998-03-10 07:56:58 +03:00
# endif
1997-11-22 10:51:23 +03:00
1998-03-08 17:31:50 +03:00
if ( auth_required & & ! authenticated ) {
1997-11-22 10:51:23 +03:00
cgi_setup_error ( " 401 Authorization Required " ,
1997-11-23 05:42:22 +03:00
" WWW-Authenticate: Basic realm= \" root \" \r \n " ,
1997-11-22 10:51:23 +03:00
" You must be authenticated to use this service " ) ;
}
if ( ! url ) {
cgi_setup_error ( " 400 Bad Request " , " " ,
" You must specify a GET or POST request " ) ;
}
/* trim the URL */
if ( ( p = strchr ( url , ' ' ) ) | | ( p = strchr ( url , ' \t ' ) ) ) {
* p = 0 ;
}
while ( * url & & strchr ( " \r \n " , url [ strlen ( url ) - 1 ] ) ) {
url [ strlen ( url ) - 1 ] = 0 ;
}
/* anything following a ? in the URL is part of the query string */
if ( ( p = strchr ( url , ' ? ' ) ) ) {
query_string = p + 1 ;
* p = 0 ;
}
1998-09-02 06:02:30 +04:00
string_sub ( url , " /swat/ " , " " ) ;
if ( strstr ( url , " .. " ) = = 0 & & file_exist ( url , NULL ) ) {
cgi_download ( url ) ;
1997-11-22 10:51:23 +03:00
}
1998-10-26 13:55:29 +03:00
printf ( " HTTP/1.0 200 OK \r \n Connection: close \r \n " ) ;
1998-03-10 07:56:58 +03:00
printf ( " Date: %s \r \n " , http_timestring ( time ( NULL ) ) ) ;
1998-03-14 07:13:24 +03:00
baseurl = " " ;
pathinfo = url + 1 ;
1997-11-22 10:51:23 +03:00
}
1997-11-23 05:42:22 +03:00
1998-03-08 17:14:49 +03:00
/***************************************************************************
return the current pages URL
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char * cgi_baseurl ( void )
{
1998-03-14 07:13:24 +03:00
if ( baseurl ) {
return baseurl ;
}
return getenv ( " SCRIPT_NAME " ) ;
1998-03-08 17:14:49 +03:00
}
1998-03-14 07:13:24 +03:00
/***************************************************************************
return the current pages path info
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char * cgi_pathinfo ( void )
{
char * r ;
if ( pathinfo ) {
return pathinfo ;
}
r = getenv ( " PATH_INFO " ) ;
if ( ! r ) return " " ;
if ( * r = = ' / ' ) r + + ;
return r ;
}
1998-03-17 15:31:43 +03:00
/***************************************************************************
return the hostname of the client
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char * cgi_remote_host ( void )
{
if ( baseurl ) {
return client_name ( 1 ) ;
}
return getenv ( " REMOTE_HOST " ) ;
}
/***************************************************************************
return the hostname of the client
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char * cgi_remote_addr ( void )
{
if ( baseurl ) {
return client_addr ( 1 ) ;
}
return getenv ( " REMOTE_ADDR " ) ;
}
1998-03-18 10:33:11 +03:00
/***************************************************************************
return True if the request was a POST
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL cgi_waspost ( void )
{
if ( baseurl ) {
return request_post ;
}
return strequal ( getenv ( " REQUEST_METHOD " ) , " POST " ) ;
}