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 2003 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. *
****************************************************************************/
/*
* General notes: (ToDo stuff)
* strtok() is used a LOT in here... notice that there is a strtok_r() for reentrant...
* this may imply that strtok() is NOT thread-safe... if in fact it isn't this HAS
* to be fixed before any real production-level quality is achieved with this web server
* however, strtok_r() may not be a standard function.
*
* RE: not sending the headers if an nph scrpit is detected. (The headers buffer could
* just be free()ed and NULLed)
*
* Currently, all SSJS requests for a session are ran in the same context without clearing the context in
* any way. This behaviour should not be relied on as it may disappear in the future... this will require
* some thought as it COULD be handy in some circumstances and COULD cause weird bugs in others.
*
* Dynamic content is always resent on an If-Modified-Since request... this may not be optimal behaviour
* for GET requests...
*
* Should support RFC2617 Digest auth.
*
* Fix up all the logging stuff.
*
* SSJS stuff could work using three different methods:
* 1) Temporary file as happens currently
* Advantages:
* Allows to keep current connection (keep-alive works)
* write() doesn't need to be "special"
* Disadvantages:
* Depends on the temp dir being writable and capable of holding
* the full reply
* Everything goes throug the disk, so probobly some performance
* penalty is involved
* No way of sending directly to the remote system
* 2) nph- style
* Advantages:
* No file I/O involved
* Can do magic tricks (ala my perl web wrapper)
* Disadvantages:
* Pretty much everything needs to be handled by the script.
* 3) Return body in http_reply object
* All the advantages of 1)
* Could use a special write() to make everything just great.
* Still doesn't allow page to be sent until fully composed (ie: long
* delays)
* 4) Type three with a callback that sends the header and current body, then
* converts write() to send directly to remote.
*
* Add in support to pass connections through to a different webserver...
* probobly in access.ars... with like a simplified mod_rewrite.
* This would allow people to run apache and Synchronet as the same site.
#if defined(__unix__)
#include <sys/wait.h> /* waitpid() */
#include <sys/types.h>
#include <signal.h> /* kill() */
#define JAVASCRIPT
#include "link_list.h"
#include "sbbs.h"
#include "sockwrap.h" /* sendfilesocket() */
#include "semwrap.h"
#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;
static const char* error_404="404 Not Found";
static const char* error_500="500 Internal Server Error";
extern const uchar* nular;
#define MAX_MIME_TYPES 128
#define MAX_REQUEST_LINE 1024
#define MAX_HEADERS_SIZE 16384 /* Maximum total size of all headers */
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 terminate_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 time_t uptime=0;
static DWORD served=0;
static web_startup_t* startup=NULL;
static js_server_props_t js_server_props;
/* Logging stuff */
sem_t log_sem;
pthread_mutex_t log_mutex;
link_list_t *log_list;
struct log_data {
char *hostname;
char *ident;
char *user;
char *request;
char *referrer;
char *agent;
int status;
unsigned int size;
struct tm completed;
};
void *next;
} linked_list;
typedef struct {
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 */
char extra_path_info[MAX_REQUEST_LINE];
linked_list* cgi_env;
linked_list* dynamic_heads;
char status[MAX_REQUEST_LINE+1];
char * post_data;
size_t post_len;
int dynamic;
struct log_data *ld;
/* Dynamically (sever-side JS) generated HTML parameters */
FILE* fp;
} http_request_t;
typedef struct {
SOCKET socket;
SOCKADDR_IN addr;
http_request_t req;
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];
enum {
HTTP_0_9
,HTTP_1_0
,HTTP_1_1
};
static char* http_vers[] = {
""
,"HTTP/1.0"
,"HTTP/1.1"
};
enum {
HTTP_HEAD
,HTTP_GET
};
enum {
IS_STATIC
,IS_CGI
,IS_JS
,IS_SSJS
};
HEAD_DATE
,HEAD_HOST
,HEAD_IFMODIFIED
,HEAD_LENGTH
,HEAD_TYPE
,HEAD_AUTH
,HEAD_CONNECTION
,HEAD_WWWAUTH
,HEAD_STATUS
,HEAD_ALLOW
,HEAD_EXPIRES
,HEAD_LASTMODIFIED
,HEAD_LOCATION
,HEAD_PRAGMA
,HEAD_SERVER
,HEAD_REFERER
,HEAD_AGENT
};
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" },
{ HEAD_REFERER, "Referer" },
{ HEAD_AGENT, "User-Agent" },
/* Everything MOVED_TEMP and everything after is a magical internal redirect */
enum {
NO_LOCATION
,MOVED_PERM
,MOVED_STAT
/* Max. times to follow internal redirects for a single request */
#define MAX_REDIR_LOOPS 20
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 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 char *find_last_slash(char *str);
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(int level, 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(startup->cbdata,level,sbuf));
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
return (TRUE);
}
lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#define winsock_startup() (TRUE)
#define SOCKLIB_DESC NULL
#endif
static void status(char* str)
{
if(startup!=NULL && startup->status!=NULL)
startup->status(startup->cbdata,str);
}
static void update_clients(void)
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(startup->cbdata,active_clients);
#if 0 /* These will be used later ToDo */
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);
}
#endif
static void thread_up(BOOL setuid)
{
thread_count++;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,TRUE, setuid);
}
static void thread_down(void)
{
if(thread_count>0)
thread_count--;
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,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(LOG_CRIT,"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(LOG_CRIT,"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(LOG_WARNING,"%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,session->host_ip))
add_env(session,"REMOTE_HOST",session->host_name);
add_env(session,"REMOTE_ADDR",session->host_ip);
}
/*
* Sets string str to socket sock... returns number of bytes written, or 0 on an error
* (Should it be -1 on an error?)
* Can not close the socket since it can not set it to INVALID_SOCKET
* ToDo - Decide error behaviour, should a SOCKET * be passed around rather than a socket?
*/
static int sockprint(SOCKET sock, const char *str)
{
int len;
int result;
int written=0;
if(sock==INVALID_SOCKET)
return(0);
if(startup->options&WEB_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d TX: %s", sock, str);
while(socket_check(sock,NULL,&wr,60000) && wr && written<len) {
result=sendsocket(sock,str+written,len-written);
if(result==SOCKET_ERROR) {
if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",sock);
else if(ERROR_VALUE==ECONNABORTED)
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",sock);
lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
return(0);
}
written+=result;
}
if(written != len) {
lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
return(0);
}
return(len);
}
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;
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
if(token==NULL)
return(0);
/* 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," ");
if(token==NULL)
return(0);
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;
}
t=time_gm(&ti);
}
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(startup->cbdata,TRUE);
if(sock!=INVALID_SOCKET) {
if(set_socket_options(&scfg, sock,error))
lprintf(LOG_ERR,"%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(startup->cbdata,FALSE);
}
sockets--;
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_WARNING,"%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);
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)
{
uint i;
if(ext==NULL)
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);
/* This function appends append plus a newline IF the final dst string would have a length less than maxlen */
static void safecat(char *dst, const char *append, size_t maxlen) {
size_t dstlen,appendlen;
dstlen=strlen(dst);
appendlen=strlen(append);
if(dstlen+appendlen+2 < maxlen) {
strcat(dst,append);
strcat(dst,newline);
}
}
static BOOL send_headers(http_session_t *session, const char *status)
int ret;
const char *status_line;
struct stat stats;
struct tm tm;
char *headers;
char header[MAX_REQUEST_LINE];
status_line=status;
ret=stat(session->req.physical_path,&stats);
/*
* ToDo this always resends dynamic content... although this makes complete sense to me,
* I can't help but feel that this may not be required for GET requests.
* Look into this and revisit this section - ToDo
*/
if(!ret && (stats.st_mtime < session->req.if_modified_since) && !session->req.dynamic) {
status_line="304 Not Modified";
if(session->req.send_location==MOVED_PERM) {
status_line="301 Moved Permanently";
ret=-1;
send_file=FALSE;
}
if(session->req.send_location==MOVED_TEMP) {
status_line="302 Moved Temporarily";
ret=-1;
send_file=FALSE;
}
headers=malloc(MAX_HEADERS_SIZE);
if(headers==NULL) {
lprintf(LOG_CRIT,"Could not allocate memory for response headers.");
return(FALSE);
}
*headers=0;
snprintf(header,MAX_REQUEST_LINE,"%s %s",http_vers[session->http_ver],status_line);
safecat(headers,header,MAX_HEADERS_SIZE);
/* General Headers */
ti=time(NULL);
if(gmtime_r(&ti,&tm)==NULL)
memset(&tm,0,sizeof(tm));
snprintf(header,MAX_REQUEST_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);
safecat(headers,header,MAX_HEADERS_SIZE);
if(session->req.keep_alive) {
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_CONNECTION),"Keep-Alive");
safecat(headers,header,MAX_HEADERS_SIZE);
}
else {
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_CONNECTION),"Close");
safecat(headers,header,MAX_HEADERS_SIZE);
}
/* Response Headers */
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_SERVER),VERSION_NOTICE);
safecat(headers,header,MAX_HEADERS_SIZE);
/* Entity Headers */
if(session->req.dynamic) {
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD, POST");
safecat(headers,header,MAX_HEADERS_SIZE);
}
else {
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD");
safecat(headers,header,MAX_HEADERS_SIZE);
}
if(session->req.send_location) {
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_LOCATION),(session->req.virtual_path));
safecat(headers,header,MAX_HEADERS_SIZE);
if(session->req.keep_alive) {
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_LENGTH),"0");
safecat(headers,header,MAX_HEADERS_SIZE);
snprintf(header,MAX_REQUEST_LINE,"%s: %d",get_header(HEAD_LENGTH),(int)stats.st_size);
safecat(headers,header,MAX_HEADERS_SIZE);
if(!ret && !session->req.dynamic) {
snprintf(header,MAX_REQUEST_LINE,"%s: %s",get_header(HEAD_TYPE),session->req.mime_type);
safecat(headers,header,MAX_HEADERS_SIZE);
gmtime_r(&stats.st_mtime,&tm);
snprintf(header,MAX_REQUEST_LINE,"%s: %s, %02d %s %04d %02d:%02d:%02d GMT"
,days[tm.tm_wday],tm.tm_mday,months[tm.tm_mon]
,tm.tm_year+1900,tm.tm_hour,tm.tm_min,tm.tm_sec);
safecat(headers,header,MAX_HEADERS_SIZE);
}
if(session->req.dynamic) {
/* Dynamic headers */
/* Set up environment */
p=session->req.dynamic_heads;
safecat(headers,p->val,MAX_HEADERS_SIZE);
safecat(headers,"",MAX_HEADERS_SIZE);
send_file = (sockprint(session->socket,headers) && send_file);
free(headers);
static int sock_sendfile(SOCKET socket,char *path)
long offset=0;
int ret=0;
if(startup->options&WEB_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d Sending %s",socket,path);
if((file=open(path,O_RDONLY|O_BINARY))==-1)
lprintf(LOG_WARNING,"%04d !ERROR %d opening %s",socket,errno,path);
if((ret=sendfilesocket(socket, file, &offset, 0)) < 1) {
lprintf(LOG_DEBUG,"%04d !ERROR %d sending %s"
ret=0;
}
close(file);
}
return(ret);
static void send_error(http_session_t * session, const char* message)
{
char error_code[4];
char sbuf[1024];
time_t now;
lprintf(LOG_INFO,"%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);
session->req.ld->status=atoi(message);
if(session->http_ver > HTTP_0_9) {
session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
send_headers(session,message);
if(!stat(session->req.physical_path,&sb)) {
session->req.ld->size=sock_sendfile(session->socket,session->req.physical_path);
if(session->req.ld->size<0)
session->req.ld->size=0;
}
lprintf(LOG_NOTICE,"%04d Error message file %s doesn't exist."
,session->socket,session->req.physical_path);
snprintf(sbuf,1024
,"<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\">"
"%s</a></BODY></HTML>"
,error_code,error_code,error_code,scfg.sys_inetaddr,scfg.sys_op);
sockprint(session->socket,sbuf);
session->req.ld->size=strlen(sbuf);
}
now=time(NULL);
localtime_r(&now,&session->req.ld->completed);
pthread_mutex_lock(&log_mutex);
listPushNode(log_list,session->req.ld);
pthread_mutex_unlock(&log_mutex);
sem_post(&log_sem);
close_request(session);
}
static BOOL check_ars(http_session_t * session)
{
char *username;
char *password;
uchar *ar;
BOOL authorized;
char auth_req[MAX_REQUEST_LINE];
if(session->req.auth[0]==0) {
if(startup->options&WEB_OPT_DEBUG_RX)
lprintf(LOG_NOTICE,"%04d !No authentication information",session->socket);
return(FALSE);
}
SAFECOPY(auth_req,session->req.auth);
username=strtok(auth_req,":");
if(username==NULL)
username="";
password=strtok(NULL,":");
/* Require a password */
if(password==NULL)
password="";
session->user.number=matchuser(&scfg, username, FALSE);
if(session->user.number==0) {
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s, Password: %s"
,session->socket,username,password);
else
lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s"
,session->socket,username);
return(FALSE);
}
getuserdat(&scfg, &session->user);
if(session->user.pass[0] && stricmp(session->user.pass,password)) {
/* Should go to the hack log? */
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !PASSWORD FAILURE for user %s: '%s' expected '%s'"
,session->socket,username,password,session->user.pass);
else
lprintf(LOG_WARNING,"%04d !PASSWORD FAILURE for user %s"
,session->socket,username);
session->user.number=0;
return(FALSE);
ar = arstr(NULL,session->req.ars,&scfg);
authorized=chk_ar(&scfg,ar,&session->user);
if(ar!=NULL && ar!=nular)
free(ar);
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(LOG_ERR,"%04d !JavaScript ERROR creating user objects",session->socket);
if(session->req.dynamic==IS_CGI || session->req.dynamic==IS_STATIC) {
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);
}
session->req.ld->user=strdup(username);
return(TRUE);
/* Should go to the hack log? */
lprintf(LOG_WARNING,"%04d !AUTHORIZATION FAILURE for user %s, ARS: %s"
,session->socket,username,session->req.ars);
return(FALSE);
}
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);