Skip to content
Snippets Groups Projects
websrvr.c 67 KiB
Newer Older
/* websrvr.c */

/* Synchronet Web Server */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright 2000 Rob Swindell - http://www.synchro.net/copyright.html		*
 *																			*
 * 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.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#if defined(__unix__)
	#include <sys/wait.h>		/* waitpid() */
	#include <sys/types.h>
	#include <signal.h>			/* kill() */
#ifndef JAVASCRIPT
#include "sbbs.h"
#include "sockwrap.h"		/* sendfilesocket() */
#include "websrvr.h"

static const char*	server_name="Synchronet Web Server";
static const char*	newline="\r\n";
static const char*	http_scheme="http://";
static const size_t	http_scheme_len=7;
rswindell's avatar
rswindell committed

#define TIMEOUT_THREAD_WAIT		60		/* Seconds */
#define MAX_MIME_TYPES			128
#define MAX_REQUEST_LINE		1024

static scfg_t	scfg;
static BOOL		scfg_reloaded=TRUE;
static uint 	http_threads_running=0;
static ulong	active_clients=0;
static ulong	sockets=0;
static BOOL		recycle_server=FALSE;
static uint		thread_count=0;
static SOCKET	server_socket=INVALID_SOCKET;
static ulong	mime_count=0;
static char		revision[16];
static char		root_dir[MAX_PATH+1];
static char		error_dir[MAX_PATH+1];
static web_startup_t* startup=NULL;

typedef struct  {
	void	*next;
} linked_list;

typedef struct  {
	BOOL		method;
	char		virtual_path[MAX_PATH+1];
	char		physical_path[MAX_PATH+1];
	BOOL		parsed_headers;
	BOOL    	expect_go_ahead;
	time_t		if_modified_since;
	BOOL		keep_alive;
	char		ars[256];
	char    	auth[128];				/* UserID:Password */
	char		host[128];				/* The requested host. (virtual hosts) */
	int			send_location;
	const char*	mime_type;

	/* CGI parameters */
deuce's avatar
deuce committed
	char		query_str[MAX_REQUEST_LINE];

	linked_list*	dynamic_heads;
	char		status[MAX_REQUEST_LINE+1];

	/* Dynamically (sever-side JS) generated HTML parameters */
	FILE*	fp;

} http_request_t;

typedef struct  {
	char			host_ip[64];
	char			host_name[128];	/* Resolved remote host */
	int				http_ver;       /* HTTP version.  0 = HTTP/0.9, 1=HTTP/1.0, 2=HTTP/1.1 */
	BOOL			finished;		/* Do not accept any more imput from client */
	user_t			user;

	/* JavaScript parameters */
	JSRuntime*		js_runtime;
	JSContext*		js_cx;
	JSObject*		js_glob;
	JSObject*		js_query;
	JSObject*		js_header;
	JSObject*		js_request;
} http_session_t;

typedef struct {
	char	ext[16];
	char	type[128];
} mime_types_t;

static mime_types_t		mime_types[MAX_MIME_TYPES];
};
static char* http_vers[] = {
	 ""
	,"HTTP/1.0"
rswindell's avatar
rswindell committed
	,NULL	/* terminator */
rswindell's avatar
rswindell committed
static char* methods[] = {
	 "HEAD"
	,"GET"
rswindell's avatar
rswindell committed
	,NULL	/* terminator */
};
	 HEAD_DATE
	,HEAD_HOST
	,HEAD_IFMODIFIED
	,HEAD_AUTH
	,HEAD_CONNECTION
	,HEAD_WWWAUTH
	,HEAD_STATUS
	,HEAD_ALLOW
	,HEAD_EXPIRES
	,HEAD_LASTMODIFIED
	,HEAD_LOCATION
	,HEAD_PRAGMA
	,HEAD_SERVER
};

static struct {
	int		id;
	char*	text;
} headers[] = {
	{ HEAD_DATE,			"Date"					},
	{ HEAD_HOST,			"Host"					},
	{ HEAD_IFMODIFIED,		"If-Modified-Since"		},
	{ HEAD_LENGTH,			"Content-Length"		},
	{ HEAD_TYPE,			"Content-Type"			},
	{ HEAD_AUTH,			"Authorization"			},
	{ HEAD_CONNECTION,		"Connection"			},
	{ HEAD_WWWAUTH,			"WWW-Authenticate"		},
	{ HEAD_STATUS,			"Status"				},
	{ HEAD_ALLOW,			"Allow"					},
	{ HEAD_EXPIRES,			"Expires"				},
	{ HEAD_LASTMODIFIED,	"Last-Modified"			},
	{ HEAD_LOCATION,		"Location"				},
	{ HEAD_PRAGMA,			"Pragma"				},
	{ HEAD_SERVER,			"Server"				},
	{ -1,					NULL /* terminator */	},
enum  {
	NO_LOCATION
	,MOVED_TEMP
	,MOVED_PERM
static char	*days[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
static char	*months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

static const char * base64alphabet = 
 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

static DWORD monthdays[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};

static void respond(http_session_t * session);
static BOOL js_setup(http_session_t* session);
static time_t time_gm( struct tm* ti )  {
	time_t t;

	t=(ti->tm_year-70)*365;
	t+=(ti->tm_year-69)/4;
	t+=monthdays[ti->tm_mon];
	if(ti->tm_mon >= 2 && ti->tm_year+1900%400 ? (ti->tm_year+1900%100 ? (ti->tm_year+1900%4 ? 0:1):0):1)
		++t;
	t += ti->tm_mday - 1;
	t = t * 24 + ti->tm_hour;
	t = t * 60 + ti->tm_min;
	t = t * 60 + ti->tm_sec;

	return t;
}

static int lprintf(char *fmt, ...)
{
	va_list argptr;
	char sbuf[1024];

    if(startup==NULL || startup->lputs==NULL)
        return(0);

	va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
    return(startup->lputs(sbuf));
}

#ifdef _WINSOCKAPI_

static WSADATA WSAData;
static BOOL WSAInitialized=FALSE;

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
		lprintf("%s %s",WSAData.szDescription, WSAData.szSystemStatus);
		WSAInitialized=TRUE;
		return (TRUE);
	}

    lprintf("!WinSock startup ERROR %d", status);
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)

#endif

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
	    startup->status(str);
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
		startup->clients(active_clients);
}

static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
		startup->client_on(TRUE,sock,client,update);
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
		startup->client_on(FALSE,sock,NULL,FALSE);
}

static void thread_up(BOOL setuid)
{
	thread_count++;
	if(startup!=NULL && startup->thread_up!=NULL)
		startup->thread_up(TRUE, setuid);
}

static void thread_down(void)
{
	if(thread_count>0)
		thread_count--;
	if(startup!=NULL && startup->thread_up!=NULL)
		startup->thread_up(FALSE, FALSE);
}

static linked_list *add_list(linked_list *list,const char *value)  {
	linked_list*	entry;
	entry=malloc(sizeof(linked_list));
	if(entry==NULL)  {
		lprintf("Could not allocate memory for \"%s\" in linked list.",&value);
		return(list);
	entry->val=malloc(strlen(value)+1);
	if(entry->val==NULL)  {
		FREE_AND_NULL(entry);
		lprintf("Could not allocate memory for \"%s\" in linked list.",&value);
		return(list);
	}
	strcpy(entry->val,value);
	entry->next=list;
	return(entry);
}

static void add_env(http_session_t *session, const char *name,const char *value)  {
	char	newname[129];
	char	fullname[387];
	char	*p;
	
	if(name==NULL || value==NULL)  {
		lprintf("%04d Attempt to set NULL env variable", session->socket);
		return;
	}
	SAFECOPY(newname,name);

	for(p=newname;*p;p++)  {
		*p=toupper(*p);
		if(*p=='-')
			*p='_';
	}

	sprintf(fullname,"%s=%s",newname,value);
	session->req.cgi_env=add_list(session->req.cgi_env,fullname);
}

static void init_enviro(http_session_t *session)  {
	char	str[128];

	add_env(session,"SERVER_SOFTWARE",VERSION_NOTICE);
	sprintf(str,"%d",startup->port);
	add_env(session,"SERVER_PORT",str);
	add_env(session,"GATEWAY_INTERFACE","CGI/1.1");
	if(!strcmp(session->host_name,"<no name>"))
		add_env(session,"REMOTE_HOST",session->host_name);
	add_env(session,"REMOTE_ADDR",session->host_ip);
}

static int sockprint(SOCKET sock, const char *str)  {
	int len;
	int	result;
	int written=0;

deuce's avatar
deuce committed
	if(startup->options&WEB_OPT_DEBUG_TX)
		lprintf("%04d TX: %s", sock, str);
	if(sock==INVALID_SOCKET)
		return(0);
	len=strlen(str);
	while((result=sendsocket(sock,str+written,len-written))>0)  {
		written+=result;
	}
	if(written != len) {
		lprintf("%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
		return(0);
	}
	return(len);
}

static int sockprinttwo(SOCKET sock, const char *head, const char *value, BOOL colon)  {
	int total=0;

	if(sock==INVALID_SOCKET)
		return(0);
	total+=sockprint(sock,head);
	if(colon)  {
		total+=sockprint(sock,": ");
	}
	else  {
		total+=sockprint(sock," ");
	}
	
	total+=sockprint(sock,value);
	total+=sockprint(sock,newline);

	if(sock==INVALID_SOCKET)
		return(0);
	return(total);
}

static int sockprintf(SOCKET sock, char *fmt, ...)
{
	int		len;
	int		result;
	va_list argptr;
	char	sbuf[1024];
	fd_set	socket_set;
	struct timeval tv;

    va_start(argptr,fmt);
    len=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
	if(startup->options&WEB_OPT_DEBUG_TX)
		lprintf("%04d TX: %s", sock, sbuf);
	len+=2;
    va_end(argptr);

	if(sock==INVALID_SOCKET) {
		lprintf("!INVALID SOCKET in call to sockprintf");
		return(0);
	}

	/* Check socket for writability (using select) */
	tv.tv_sec=60;
	tv.tv_usec=0;

	FD_ZERO(&socket_set);
	FD_SET(sock,&socket_set);

	if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
		if(result==0)
			lprintf("%04d !TIMEOUT selecting socket for send"
				,sock);
		else
			lprintf("%04d !ERROR %d selecting socket for send"
				,sock, ERROR_VALUE);
		return(0);
	}

	while((result=sendsocket(sock,sbuf,len))!=len) {
		if(result==SOCKET_ERROR) {
			if(ERROR_VALUE==EWOULDBLOCK) {
				mswait(1);
				continue;
			}
			if(ERROR_VALUE==ECONNRESET) 
				lprintf("%04d Connection reset by peer on send",sock);
			else if(ERROR_VALUE==ECONNABORTED) 
				lprintf("%04d Connection aborted by peer on send",sock);
			else
				lprintf("%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
			return(0);
		}
		lprintf("%04d !ERROR: short send on socket: %d instead of %d",sock,result,len);
	}
	return(len);
}

deuce's avatar
deuce committed
static char *cleanpath(char *target, char *path, size_t size)  {
	char	*out;
	char	*p;
	char	*p2;
	
	out=target;
	*out=0;

	if(*path != '/' && *path != '\\')  {
		p=getcwd(target,size);
		if(p==NULL || strlen(p)+strlen(path)>=size)
			return(NULL);
		out=strrchr(target,'\0');
		*(out++)='/';
		*out=0;
		out--;
	}
	strncat(target,path,size-1);
	
	for(;*out;out++)  {
		while(*out=='/' || *out=='\\')  {
			if(*(out+1)=='/' || *(out+1)=='\\')
				memmove(out,out+1,strlen(out));
			else if(*(out+1)=='.' && (*(out+2)=='/' || *(out+2)=='\\'))
				memmove(out,out+2,strlen(out)-1);
			else if(*(out+1)=='.' && *(out+2)=='.' && (*(out+3)=='/' || *(out+3)=='\\'))  {
deuce's avatar
deuce committed
				*out=0;
				p=strrchr(target,'/');
				p2=strrchr(target,'\\');
				if(p2>p)
					p=p2;
				memmove(p,out+3,strlen(out+3)+1);
deuce's avatar
deuce committed
			}
			else  {
				out++;
			}
		}
	}
	return(target);
}

static int getmonth(char *mon)
{
	int	i;
	for(i=0;i<12;i++)
		if(!stricmp(mon,months[i]))
			return(i);

	return 0;
}

static time_t decode_date(char *date)
{
	struct	tm	ti;
	char	*token;
	time_t	t;

	ti.tm_sec=0;		/* seconds (0 - 60) */
	ti.tm_min=0;		/* minutes (0 - 59) */
	ti.tm_hour=0;		/* hours (0 - 23) */
	ti.tm_mday=1;		/* day of month (1 - 31) */
	ti.tm_mon=0;		/* month of year (0 - 11) */
	ti.tm_year=0;		/* year - 1900 */
	ti.tm_isdst=0;		/* is summer time in effect? */

#if 0	/* non-standard */
	ti.tm_zone="UTC";	/* abbreviation of timezone name */
	ti.tm_gmtoff=0;		/* offset from UTC in seconds */
#endif

	token=strtok(date,",");
	/* This probobly only needs to be 9, but the extra one is for luck. */
	if(strlen(date)>15) {
		/* Toss away week day */
		token=strtok(date," ");
		token=strtok(NULL," ");
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
		token=strtok(NULL," ");
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
		token=strtok(NULL,":");
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
		token=strtok(NULL,":");
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
		token=strtok(NULL," ");
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
		token=strtok(NULL,"");
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token)-1900;
	}
	else  {
		/* RFC 1123 or RFC 850 */
		token=strtok(NULL," -");
		if(token==NULL)
			return(0);
		ti.tm_mday=atoi(token);
		token=strtok(NULL," -");
		if(token==NULL)
			return(0);
		ti.tm_mon=getmonth(token);
		token=strtok(NULL," ");
		if(token==NULL)
			return(0);
		ti.tm_year=atoi(token);
		token=strtok(NULL,":");
		if(token==NULL)
			return(0);
		ti.tm_hour=atoi(token);
		token=strtok(NULL,":");
		if(token==NULL)
			return(0);
		ti.tm_min=atoi(token);
		token=strtok(NULL," ");
		if(token==NULL)
			return(0);
		ti.tm_sec=atoi(token);
		if(ti.tm_year>1900)
			ti.tm_year -= 1900;
	}
}

static SOCKET open_socket(int type)
{
	char	error[256];
	SOCKET	sock;

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
		startup->socket_open(TRUE);
	if(sock!=INVALID_SOCKET) {
		if(set_socket_options(&scfg, sock,error))
			lprintf("%04d !ERROR %s",sock,error);

		sockets++;
	}
	return(sock);
}


static int close_socket(SOCKET sock)
{
	int		result;

	if(sock==INVALID_SOCKET)
		return(-1);

	shutdown(sock,SHUT_RDWR);	/* required on Unix */
	result=closesocket(sock);
	if(startup!=NULL && startup->socket_open!=NULL) {
		startup->socket_open(FALSE);
	}
	sockets--;
	if(result!=0) {
		if(ERROR_VALUE!=ENOTSOCK)
			lprintf("%04d !ERROR %d closing socket",sock, ERROR_VALUE);
	}

	return(result);
}

static void close_request(http_session_t * session)
{
	linked_list	*p;
	while(session->req.dynamic_heads != NULL)  {
		FREE_AND_NULL(session->req.dynamic_heads->val);
		p=session->req.dynamic_heads->next;
		FREE_AND_NULL(session->req.dynamic_heads);
		session->req.dynamic_heads=p;
	while(session->req.cgi_env != NULL)  {
		FREE_AND_NULL(session->req.cgi_env->val);
		p=session->req.cgi_env->next;
		FREE_AND_NULL(session->req.cgi_env);
		session->req.cgi_env=p;
	}
	FREE_AND_NULL(session->req.post_data);
deuce's avatar
deuce committed
	if(!session->req.keep_alive || session->socket==INVALID_SOCKET) {
		close_socket(session->socket);
		session->socket=INVALID_SOCKET;
		session->finished=TRUE;
	}
}

static int get_header_type(char *header)
{
	for(i=0; headers[i].text!=NULL; i++) {
		if(!stricmp(header,headers[i].text)) {
			return(headers[i].id);
		}
	}
	return(-1);
}

static char *get_header(int id) 
{
	if(headers[id].id==id)
		return(headers[id].text);

	for(i=0;headers[i].text!=NULL;i++) {
		if(headers[i].id==id) {
			return(headers[i].text);
		}
	}
	return(NULL);
}

static const char* unknown_mime_type="application/octet-stream";

static const char* get_mime_type(char *ext)
		return(unknown_mime_type);

	for(i=0;i<mime_count;i++)
		if(!stricmp(ext+1,mime_types[i].ext))
			return(mime_types[i].type);

	return(unknown_mime_type);
static BOOL send_headers(http_session_t *session, const char *status)
	BOOL	send_file=TRUE;
	time_t	ti;
	char	status_line[MAX_REQUEST_LINE];
	struct stat	stats;
	linked_list	*p;

	SAFECOPY(status_line,status);
	ret=stat(session->req.physical_path,&stats);
	if(!ret && (stats.st_mtime < session->req.if_modified_since) && !session->req.dynamic) {
		SAFECOPY(status_line,"304 Not Modified");
		ret=-1;
		send_file=FALSE;
	if(session->req.send_location==MOVED_PERM)  {
		SAFECOPY(status_line,"301 Moved Permanently");
		ret=-1;
		send_file=FALSE;
	}
	if(session->req.send_location==MOVED_TEMP)  {
		SAFECOPY(status_line,"302 Moved Temporarily");
		ret=-1;
		send_file=FALSE;
	}
	sockprinttwo(session->socket,http_vers[session->http_ver],status_line,FALSE);

	/* General Headers */
	ti=time(NULL);
	if(gmtime_r(&ti,&tm)==NULL)
		memset(&tm,0,sizeof(tm));
	sprintf(status_line,"%s: %s, %02d %s %04d %02d:%02d:%02d GMT",get_header(HEAD_DATE),days[tm.tm_wday],tm.tm_mday,months[tm.tm_mon],tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
	sockprint(session->socket,status_line);
deuce's avatar
deuce committed
	sockprint(session->socket,newline);
	if(session->req.keep_alive)
		sockprinttwo(session->socket,get_header(HEAD_CONNECTION),"Keep-Alive",TRUE);
		sockprinttwo(session->socket,get_header(HEAD_CONNECTION),"Close",TRUE);
	sockprinttwo(session->socket,get_header(HEAD_SERVER),VERSION_NOTICE,TRUE);
		sockprinttwo(session->socket,get_header(HEAD_ALLOW),"GET, HEAD, POST",TRUE);
		sockprinttwo(session->socket,get_header(HEAD_ALLOW),"GET, HEAD",TRUE);
	if(session->req.send_location) {
		sockprinttwo(session->socket,get_header(HEAD_LOCATION),(session->req.virtual_path),TRUE);
	if(session->req.keep_alive) {
deuce's avatar
deuce committed
		if(ret)  {
			sockprinttwo(session->socket,get_header(HEAD_LENGTH),"0",TRUE);
deuce's avatar
deuce committed
		}
		else  {
			sprintf(status_line,"%s: %d",get_header(HEAD_LENGTH),(int)stats.st_size);
			sockprint(session->socket,status_line);
deuce's avatar
deuce committed
			sockprint(session->socket,newline);
	if(!ret && !session->req.dynamic)  {
		send_file=sockprinttwo(session->socket,get_header(HEAD_TYPE)
			,session->req.mime_type,TRUE);
		sprintf(status_line,"%s: %s, %02d %s %04d %02d:%02d:%02d GMT"
			,get_header(HEAD_LASTMODIFIED)
			,days[tm.tm_wday],tm.tm_mday,months[tm.tm_mon]
			,tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
		sockprint(session->socket,status_line);
deuce's avatar
deuce committed
		sockprint(session->socket,newline);
	if(session->req.dynamic)  {
		/* Dynamic headers */
		p=session->req.dynamic_heads;
		while(p != NULL)  {
			sockprint(session->socket,p->val);
			sockprint(session->socket,newline);
			p=p->next;
deuce's avatar
deuce committed
	sockprint(session->socket,newline);
	return(send_file);
}

static void sock_sendfile(SOCKET socket,char *path)
{
	int		file;
	if(startup->options&WEB_OPT_DEBUG_TX)
		lprintf("%04d Sending %s",socket,path);
	if((file=open(path,O_RDONLY|O_BINARY))==-1)
		lprintf("%04d !ERROR %d opening %s",socket,errno,path);
	else {
		if(sendfilesocket(socket, file, &offset, 0) < 1)
deuce's avatar
deuce committed
			lprintf("%04d !ERROR %d sending %s"
				, socket, errno, path);
static void send_error(http_session_t * session, char *message)
	lprintf("%04d !ERROR %s",session->socket,message);
	session->req.keep_alive=FALSE;
	session->req.send_location=NO_LOCATION;
	SAFECOPY(error_code,message);
	sprintf(session->req.physical_path,"%s/%s.html",error_dir,error_code);
	if(session->http_ver > HTTP_0_9)  {
		session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
		send_headers(session,message);
	}
	if(fexist(session->req.physical_path))
		sock_sendfile(session->socket,session->req.physical_path);
		lprintf("%04d Error message file %s doesn't exist.",session->socket,session->req.physical_path);
		sockprintf(session->socket,"<HTML><HEAD><TITLE>%s Error</TITLE></HEAD><BODY><H1>%s Error</H1><BR><H3>In addition, \
			I can't seem to find the %s error file</H3><br>please notify <a href=\"mailto:SysOp@%s\">\
			The SysOp</a></BODY></HTML>"
			,error_code,error_code,error_code,scfg.sys_inetaddr);
static BOOL check_ars(http_session_t * session)
{
	char	*username;
	char	*password;
	uchar	*ar;
		if(startup->options&WEB_OPT_DEBUG_RX)
			lprintf("%04d !No authentication information",session->socket);

	username=strtok(session->req.auth,":");
	password=strtok(NULL,":");
	/* Require a password */
	if(password==NULL)
	session->user.number=matchuser(&scfg, username, FALSE);
	if(session->user.number==0) {
		if(scfg.sys_misc&SM_ECHO_PW)
			lprintf("%04d !UNKNOWN USER: %s, Password: %s"
				,session->socket,username,password);
		else
			lprintf("%04d !UNKNOWN USER: %s"
				,session->socket,username);
		return(FALSE);
	}
	getuserdat(&scfg, &session->user);
	if(session->user.pass[0] && stricmp(session->user.pass,password)) {
		if(scfg.sys_misc&SM_ECHO_PW)
			lprintf("%04d !PASSWORD FAILURE for user %s: '%s' expected '%s'"
				,session->socket,username,password,session->user.pass);
		else
			lprintf("%04d !PASSWORD FAILURE for user %s"
				,session->socket,username);
	ar = arstr(NULL,session->req.ars,&scfg);
	authorized=chk_ar(&scfg,ar,&session->user);
	if(authorized)  {
		if(session->req.dynamic==IS_CGI)  {
			add_env(session,"AUTH_TYPE","Basic");
			/* Should use real name if set to do so somewhere ToDo */
			add_env(session,"REMOTE_USER",session->user.alias);
		}
		if(session->req.dynamic==IS_SSJS)  {
			if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user
				,NULL /* ftp index file */, NULL /* subscan */)) 
				lprintf("%04d !JavaScript ERROR creating user objects",session->socket);
		}

	lprintf("%04d !AUTHORIZATION FAILURE for user %s, ARS: %s"
		,session->socket,username,session->req.ars);
{
	uchar	*read;
	uchar	*write;
	int		bits=0;
	int		working=0;
	char *	i;

	write=p;
	read=write;
	for(;*read;read++) {
		working<<=6;
		i=strchr(base64alphabet,(char)*read);
		if(i==NULL) {
			break;
		}
rswindell's avatar
rswindell committed
		if(*i=='=') i=(char*)base64alphabet; /* pad char */
		working |= (i-base64alphabet);
		bits+=6;
		if(bits>8) {
			*(write++)=(uchar)((working&(0xFF<<(bits-8)))>>(bits-8));
			bits-=8;
		}
	}
	*write=0;
	return;
}

static BOOL read_mime_types(char* fname)
{
	char	str[1024];
	char *	ext;
	char *	type;
	FILE*	mime_config;

	if((mime_config=fopen(fname,"r"))==NULL)
	while (!feof(mime_config)&&mime_count<MAX_MIME_TYPES) {
		if(fgets(str,sizeof(str),mime_config)!=NULL) {
			truncsp(str);
			ext=strtok(str," \t");
			if(ext!=NULL) {
				while(*ext && *ext<=' ') ext++;
				if(*ext!=';') {
					type=strtok(NULL," \t");
					if(type!=NULL) {
						while(*type && *type<=' ') type++;
						if(strlen(ext)>0 && strlen(type)>0) {
							SAFECOPY((mime_types[mime_count]).ext,ext);
							SAFECOPY((mime_types[mime_count++]).type,type);
						}
					}
				}
			}
		}
	}
	fclose(mime_config);
	lprintf("Loaded %d mime types",mime_count);
static int sockreadline(http_session_t * session, char *buf, size_t length)
	BOOL	rd;
	time_t	start;

	start=time(NULL);
	for(i=0;TRUE;) {
		if(!socket_check(session->socket,&rd)) {
			session->req.keep_alive=FALSE;
			close_request(session);
			session->socket=INVALID_SOCKET;
			if(time(NULL)-start>startup->max_inactivity) {
				session->req.keep_alive=FALSE;
				close_request(session);