Skip to content
Snippets Groups Projects
websrvr.c 161 KiB
Newer Older
	if(session->js_cx==NULL || session->js_request==NULL)
		return;
	if(key==NULL || value==NULL)
		return;
	if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL)
		return;
	JS_DefineProperty(session->js_cx, session->js_request, key, STRING_TO_JSVAL(js_str)
		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
}

static void js_add_header(http_session_t * session, char *key, char *value)  
{
	JSString*	js_str;
	if((lckey=(char *)alloca(strlen(key)+1))==NULL)
	strcpy(lckey,key);
	strlwr(lckey);
	if((js_str=JS_NewStringCopyZ(session->js_cx, value))==NULL) {
		return;
	}
	JS_DefineProperty(session->js_cx, session->js_header, lckey, STRING_TO_JSVAL(js_str)
		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
}

#if 0
static void js_parse_multipart(http_session_t * session, char *p)  {
	size_t		key_len;
	size_t		value_len;
	char		*lp;
	char		*key;
	char		*value;

	if(p == NULL)
		return;

	lp=p;

	while((key_len=strcspn(lp,"="))!=0)  {
		key=lp;
		lp+=key_len;
		if(*lp) {
			*lp=0;
			lp++;
		}
		value_len=strcspn(lp,"&");
		value=lp;
		lp+=value_len;
		if(*lp) {
			*lp=0;
			lp++;
		}
		unescape(value);
		unescape(key);
		js_add_queryval(session, key, value);
	}
}
#endif

static void js_parse_query(http_session_t * session, char *p)  {
	size_t		key_len;
	size_t		value_len;
	char		*lp;
	while((key_len=strcspn(lp,"="))!=0)  {
		if(*lp) {
			*lp=0;
			lp++;
		}
		value_len=strcspn(lp,"&");
		value=lp;
deuce's avatar
deuce committed
		lp+=value_len;
		if(*lp) {
			*lp=0;
			lp++;
		}
		js_add_queryval(session, key, value);
static char *get_token_value(char **p)
{
	char	*pos=*p;
	char	*start;
	char	*out;
	BOOL	escaped=FALSE;

	start=pos;
	out=start;
	if(*pos=='"') {
		for(pos++; *pos; pos++) {
			if(escaped && *pos)
				*(out++)=*pos;
			else if(*pos=='"') {
				pos++;
				break;
			}
			else if(*pos=='\\')
				escaped=TRUE;
			else
				*(out++)=*pos;
		}
	}
	else {
		for(; *pos; pos++) {
			if(iscntrl(*pos))
			switch(*pos) {
				case 0:
				case '(':
				case ')':
				case '<':
				case '>':
				case '@':
				case ',':
				case ';':
				case ':':
				case '\\':
				case '"':
				case '/':
				case '[':
				case ']':
				case '?':
				case '=':
				case '{':
				case '}':
				case ' ':
				case '\t':
					goto end_of_text;
			}
			*(out++)=*pos;
		}
	}
end_of_text:
	while(*pos==',' || isspace(*pos))
		pos++;
	*out=0;
static int hexval(unsigned char ch)
{
	ch-='0';
	if(ch<10)
		return(ch);
	ch-=7;
	if(ch<16 && ch>9)
		return(ch);
	if(ch>41) {
		ch-=32;
		if(ch<16 && ch>9)
			return(ch);
	}
	return(0);
}

static BOOL parse_headers(http_session_t * session)
{
	char	*tvalue;
	char	env_name[128];
	for(idx=0;session->req.headers[idx]!=NULL;idx++) {
		/* TODO: strdup() is possibly too slow here... */
		head_line=strdup(session->req.headers[idx]);
		if((strtok_r(head_line,":",&last))!=NULL && (value=strtok_r(NULL,"",&last))!=NULL) {
			i=get_header_type(head_line);
			while(*value && *value<=' ') value++;
			switch(i) {
				case HEAD_AUTH:
					if((p=strtok_r(value," ",&last))!=NULL) {
						if(stricmp(p, "Basic")==0) {
							p=strtok_r(NULL," ",&last);
							if(p==NULL)
								break;
							while(*p && *p<' ') p++;
							b64_decode(p,strlen(p),p,strlen(p));
							p=strtok_r(p,":",&last);
							if(p) {
								if(strlen(p) >= sizeof(session->req.auth.username))
									break;
								SAFECOPY(session->req.auth.username, p);
								p=strtok_r(NULL,":",&last);
								if(p) {
									if(strlen(p) >= sizeof(session->req.auth.password))
										break;
									SAFECOPY(session->req.auth.password, p);
									session->req.auth.type=AUTHENTICATION_BASIC;
								}
							}
						}
						else if(stricmp(p, "Digest")==0) {
							p=strtok_r(NULL, "", &last);
							/* Defaults */
							session->req.auth.algorithm=ALGORITHM_MD5;
							session->req.auth.type=AUTHENTICATION_DIGEST;
							/* Parse out values one at a time and store */
							while(*p) {
								while(isspace(*p))
									p++;
								if(strnicmp(p,"username=",9)==0) {
									p+=9;
									tvalue=get_token_value(&p);
									if(strlen(tvalue) >= sizeof(session->req.auth.username))
										break;
									SAFECOPY(session->req.auth.username, tvalue);
								}
								else if(strnicmp(p,"realm=",6)==0) {
									p+=6;
									session->req.auth.realm=strdup(get_token_value(&p));
								}
								else if(strnicmp(p,"nonce=",6)==0) {
									p+=6;
									session->req.auth.nonce=strdup(get_token_value(&p));
								}
								else if(strnicmp(p,"uri=",4)==0) {
									p+=4;
									session->req.auth.digest_uri=strdup(get_token_value(&p));
								}
								else if(strnicmp(p,"response=",9)==0) {
									p+=9;
									tvalue=get_token_value(&p);
									if(strlen(tvalue)==32) {
										for(i=0; i<16; i++) {
											session->req.auth.digest[i]=hexval(tvalue[i*2])<<4 | hexval(tvalue[i*2+1]);
										}
									}
								}
								else if(strnicmp(p,"algorithm=",10)==0) {
									p+=10;
									tvalue=get_token_value(&p);
									if(stricmp(tvalue,"MD5")==0) {
										session->req.auth.algorithm=ALGORITHM_MD5;
									}
									else {
										session->req.auth.algorithm=ALGORITHM_UNKNOWN;
									}
								}
								else if(strnicmp(p,"cnonce=",7)==0) {
									p+=7;
									session->req.auth.cnonce=strdup(get_token_value(&p));
								}
								else if(strnicmp(p,"qop=",4)==0) {
									p+=4;
									tvalue=get_token_value(&p);
									if(stricmp(tvalue,"auth")==0) {
										session->req.auth.qop_value=QOP_AUTH;
									}
									else if (stricmp(tvalue,"auth-int")==0) {
										session->req.auth.qop_value=QOP_AUTH_INT;
									}
									else {
										session->req.auth.qop_value=QOP_UNKNOWN;
									}
								}
								else if(strnicmp(p,"nc=",3)==0) {
									p+=3;
									session->req.auth.nonce_count=strdup(get_token_value(&p));
								}
									while(*p && *p != '=')
									if(*p == '=')
										get_token_value(&p);
							if(session->req.auth.digest_uri==NULL)
								session->req.auth.digest_uri=strdup(session->req.request_line);
							/* Validate that we have the required values... */
							switch(session->req.auth.qop_value) {
								case QOP_NONE:
									if(session->req.auth.realm==NULL
											|| session->req.auth.nonce==NULL
											|| session->req.auth.digest_uri==NULL)
										send_error(session,"400 Bad Request");
									break;
								case QOP_AUTH:
								case QOP_AUTH_INT:
									if(session->req.auth.realm==NULL
											|| session->req.auth.nonce==NULL
											|| session->req.auth.nonce_count==NULL
											|| session->req.auth.cnonce==NULL
											|| session->req.auth.digest_uri==NULL)
										send_error(session,"400 Bad Request");
									break;
								default:
									send_error(session,"400 Bad Request");
									break;
							}
					add_env(session,"CONTENT_LENGTH",value);
deuce's avatar
deuce committed
					content_len=strtol(value,NULL,10);
					break;
				case HEAD_IFMODIFIED:
					session->req.if_modified_since=decode_date(value);
					break;
				case HEAD_CONNECTION:
					if(!stricmp(value,"Keep-Alive")) {
						session->req.keep_alive=TRUE;
					}
					if(!stricmp(value,"Close")) {
						session->req.keep_alive=FALSE;
					}
					if(session->req.ld!=NULL) {
						FREE_AND_NULL(session->req.ld->referrer);
						/* FREE()d in http_logging_thread() */
						session->req.ld->referrer=strdup(value);
					if(session->req.ld!=NULL) {
						FREE_AND_NULL(session->req.ld->agent);
						/* FREE()d in http_logging_thread() */
						session->req.ld->agent=strdup(value);
				case HEAD_TRANSFER_ENCODING:
					if(!stricmp(value,"chunked"))
						session->req.read_chunked=TRUE;
					else
						send_error(session,"501 Not Implemented");
					break;
				case HEAD_RANGE:
					if(!stricmp(value,"bytes=")) {
						send_error(session,error_416);
						break;
					}
					value+=6;
					if(strchr(value,',')!=NULL) {	/* We don't do multiple ranges yet - TODO */
						send_error(session,error_416);
						break;
					}
					/* Check for offset from end. */
					if(*value=='-') {
						session->req.range_start=strtol(value,NULL,10);
						session->req.range_end=-1;
						break;
					}
					if((p=strtok_r(value,"-",&last))!=NULL) {
						session->req.range_start=strtol(p,NULL,10);
						if((p=strtok_r(NULL,"-",&last))!=NULL)
							session->req.range_end=strtol(p,NULL,10);
						else
							session->req.range_end=-1;
					}
					else {
						send_error(session,error_416);
deuce's avatar
deuce committed
				case HEAD_IFRANGE:
					session->req.if_range=decode_date(value);
					break;
				case HEAD_TYPE:
					add_env(session,"CONTENT_TYPE",value);
					break;
				default:
					break;
			}
			sprintf(env_name,"HTTP_%s",head_line);
			add_env(session,env_name,value);
		}
		free(head_line);
	}
	if(content_len)
		session->req.post_len = content_len;
	add_env(session,"SERVER_NAME",session->req.host[0] ? session->req.host : startup->host_name );
	return TRUE;
}

static BOOL parse_js_headers(http_session_t * session)
{
	char	*head_line;
	char	*value;
	char	*last;
	char	*p;
	int		i;
	size_t	idx;

	for(idx=0;session->req.headers[idx]!=NULL;idx++) {
		head_line=session->req.headers[idx];
		if((strtok_r(head_line,":",&last))!=NULL && (value=strtok_r(NULL,"",&last))!=NULL) {
			i=get_header_type(head_line);
			while(*value && *value<=' ') value++;
			js_add_header(session,head_line,value);
			switch(i) {
				case HEAD_TYPE:
					if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
						/*
						 * We need to parse out the files based on RFC1867
						 *
						 * And example reponse looks like this:
						 * Content-type: multipart/form-data, boundary=AaB03x
						 * 
						 * --AaB03x
						 * content-disposition: form-data; name="field1"
						 * 
						 * Joe Blow
						 * --AaB03x
						 * content-disposition: form-data; name="pics"
						 * Content-type: multipart/mixed, boundary=BbC04y
						 * 
						 * --BbC04y
						 * Content-disposition: attachment; filename="file1.txt"
						 * 
						 * Content-Type: text/plain
						 * 
						 * ... contents of file1.txt ...
						 * --BbC04y
						 * Content-disposition: attachment; filename="file2.gif"
						 * Content-type: image/gif
						 * Content-Transfer-Encoding: binary
						 * 
						 * ...contents of file2.gif...
						 * --BbC04y--
						 * --AaB03x--						 
						 */
					}
					break;
					if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
						while((key=strtok_r(p,"=",&last))!=NULL) {
							while(isspace(*key))
								key++;
							if((val=strtok_r(p,";\t\n\v\f\r ",&last))!=NULL) {	/* Whitespace */
				default:
					break;
			}
		}
	}
	return TRUE;
}

static int get_version(char *p)
{
	int		i;
	if(p==NULL)
		return(0);
	while(*p && *p<' ') p++;
	if(*p==0)
		return(0);
	for(i=1;http_vers[i]!=NULL;i++) {
		if(!stricmp(p,http_vers[i])) {
			return(i);
		}
	}
	return(i-1);
}

static int is_dynamic_req(http_session_t* session)
{
	char	fname[MAX_PATH+1];
	char	ext[MAX_PATH+1];
	check_extra_path(session);
	_splitpath(session->req.physical_path, drive, dir, fname, ext);
	if(stricmp(ext,startup->ssjs_ext)==0)
		i=IS_SSJS;
	else if(get_xjs_handler(ext,session))
		i=IS_SSJS;
	else if(stricmp(ext,startup->js_ext)==0)
		i=IS_JS;
	if(!(startup->options&BBS_OPT_NO_JAVASCRIPT) && i)  {
rswindell's avatar
rswindell committed
		lprintf(LOG_DEBUG,"%04d Setting up JavaScript support", session->socket);
			lprintf(LOG_ERR,"%04d !ERROR setting up JavaScript support", session->socket);
			send_error(session,error_500);
	if(!(startup->options&WEB_OPT_NO_CGI)) {
		for(i=0; startup->cgi_ext!=NULL && startup->cgi_ext[i]!=NULL; i++)  {
			if(stricmp(ext,startup->cgi_ext[i])==0)  {
				init_enviro(session);
		_splitpath(session->req.cgi_dir?session->req.cgi_dir:cgi_dir, cgidrive, cgidir, fname, ext);
		if(stricmp(dir,cgidir)==0 && stricmp(drive,cgidrive)==0)  {
			init_enviro(session);
			return(IS_CGI);
static char *get_request(http_session_t * session, char *req_line)
	char*	query;
	SAFECOPY(session->req.virtual_path,req_line);
	if(strtok_r(session->req.virtual_path," \t",&last))
		retval=strtok_r(NULL," \t",&last);
	else
		retval=NULL;
	SAFECOPY(session->req.request_line,session->req.virtual_path);
	if(strtok_r(session->req.virtual_path,"?",&last))
		query=strtok_r(NULL,"",&last);
	else
		query=NULL;

	/* Must initialize physical_path before calling is_dynamic_req() */
	SAFECOPY(session->req.physical_path,session->req.virtual_path);
	unescape(session->req.physical_path);
	if(!strnicmp(session->req.physical_path,http_scheme,http_scheme_len)) {
deuce's avatar
deuce committed
		/* Remove http:// from start of physical_path */
		memmove(session->req.physical_path, session->req.physical_path+http_scheme_len, strlen(session->req.physical_path+http_scheme_len)+1);

		/* Set HOST value... ignore HOST header */
deuce's avatar
deuce committed
		SAFECOPY(session->req.host,session->req.physical_path);

		/* Remove path if present (everything after the first /) */
		strtok_r(session->req.host,"/",&last);

deuce's avatar
deuce committed
		/* Set vhost value to host value */
		SAFECOPY(session->req.vhost,session->req.host);
deuce's avatar
deuce committed

		/* Remove port specification from vhost (if present) */
		strtok_r(session->req.vhost,":",&last);
		/* Sets p to point to the first character after the first slash */
		p=strchr(session->req.physical_path, '/');
		 * If we have a slash, make it the first char in the string.
		 * otherwise, set path to "/"
			strcpy(session->req.physical_path, "/");
deuce's avatar
deuce committed
		}
		else {
			offset=p-session->req.physical_path;
			memmove(session->req.physical_path
				,session->req.physical_path+offset
				,strlen(session->req.physical_path+offset)+1	/* move '\0' terminator too */
				);
		SAFECOPY(session->req.query_str,query);
static char *get_method(http_session_t * session, char *req_line)
{
	int i;

	for(i=0;methods[i]!=NULL;i++) {
		if(!strnicmp(req_line,methods[i],strlen(methods[i]))) {
			session->req.method=i;
			if(strlen(req_line)<strlen(methods[i])+2) {
				send_error(session,"400 Bad Request");
				return(NULL);
			}
			return(req_line+strlen(methods[i])+1);
		}
	}
rswindell's avatar
rswindell committed
	if(req_line!=NULL && *req_line>=' ')
		send_error(session,"501 Not Implemented");
static BOOL get_request_headers(http_session_t * session)
{
	char	head_line[MAX_REQUEST_LINE+1];
	char	next_char;
	char	*value;
	int		i;

	while(sockreadline(session,head_line,sizeof(head_line)-1)>0) {
		/* Multi-line headers */
rswindell's avatar
rswindell committed
		while((i=recv(session->socket,&next_char,1,MSG_PEEK))>0
			&& (next_char=='\t' || next_char==' ')) {
				close_socket(&session->socket);
			i=strlen(head_line);
			if(i>sizeof(head_line)-1) {
				lprintf(LOG_ERR,"%04d !ERROR long multi-line header. The web server is broken!", session->socket);
				i=sizeof(head_line)/2;
				break;
			}
			sockreadline(session,head_line+i,sizeof(head_line)-i-1);
		}
		strListPush(&session->req.headers,head_line);
		if((strtok_r(head_line,":",&last))!=NULL && (value=strtok_r(NULL,"",&last))!=NULL) {
			i=get_header_type(head_line);
			while(*value && *value<=' ') value++;
			switch(i) {
				case HEAD_HOST:
					if(session->req.host[0]==0) {
						SAFECOPY(session->req.host,value);
						SAFECOPY(session->req.vhost,value);
						/* Remove port part of host (Win32 doesn't allow : in dir names) */
						/* Either an existing : will be replaced with a null, or nothing */
						/* Will happen... the return value is not relevent here */
						strtok_r(session->req.vhost,":",&last);
		SAFECOPY(session->req.vhost, startup->host_name);
	if(!(session->req.host[0]))
		SAFECOPY(session->req.host, startup->host_name);
	return TRUE;
}

static BOOL get_fullpath(http_session_t * session)
{
	char	str[MAX_PATH+1];

	if(session->req.vhost[0] && startup->options&WEB_OPT_VIRTUAL_HOSTS) {
		safe_snprintf(str,sizeof(str),"%s/%s",root_dir,session->req.vhost);
			safe_snprintf(str,sizeof(str),"%s/%s%s",root_dir,session->req.vhost,session->req.physical_path);
		else
			safe_snprintf(str,sizeof(str),"%s%s",root_dir,session->req.physical_path);
	} else
		safe_snprintf(str,sizeof(str),"%s%s",root_dir,session->req.physical_path);
	if(FULLPATH(session->req.physical_path,str,sizeof(session->req.physical_path))==NULL)
	return(isabspath(session->req.physical_path));
static BOOL get_req(http_session_t * session, char *request_line)
	char	req_line[MAX_REQUEST_LINE+1];

	req_line[0]=0;
	if(request_line == NULL) {
		/* Eat leaing blank lines... as apache does...
		 * "This is a legacy issue. The CERN webserver required POST data to have an extra
		 * CRLF following it. Thus many clients send an extra CRLF that is not included in the
		 * Content-Length of the request. Apache works around this problem by eating any empty
		 * lines which appear before a request."
		 * http://httpd.apache.org/docs/misc/known_client_problems.html
		 */
		while((len=sockreadline(session,req_line,sizeof(req_line)-1))==0);
		if(len<0)
		if(req_line[0])
			lprintf(LOG_INFO,"%04d Request: %s",session->socket,req_line);
		if(session->req.ld!=NULL && session->req.ld->request==NULL)
			/* FREE()d in http_logging_thread() */
			session->req.ld->request=strdup(req_line);
		lprintf(LOG_DEBUG,"%04d Handling Internal Redirect to: %s",session->socket,request_line);
		SAFECOPY(req_line,request_line);
		p=NULL;
		p=get_method(session,req_line);
		if(p!=NULL) {
			p=get_request(session,p);
			session->http_ver=get_version(p);
			if(session->http_ver>=HTTP_1_1)
				session->req.keep_alive=TRUE;
			if(!is_redir)
				get_request_headers(session);
			if(!get_fullpath(session)) {
				send_error(session,error_500);
			if(session->req.ld!=NULL && session->req.ld->vhost==NULL)
				/* FREE()d in http_logging_thread() */
				session->req.ld->vhost=strdup(session->req.vhost);
			session->req.dynamic=is_dynamic_req(session);
				add_env(session,"QUERY_STRING",session->req.query_str);
			add_env(session,"REQUEST_METHOD",methods[session->req.method]);
			add_env(session,"SERVER_PROTOCOL",session->http_ver ? 
				http_vers[session->http_ver] : "HTTP/0.9");
			return(TRUE);
	session->req.keep_alive=FALSE;
	send_error(session,"400 Bad Request");
/* This may exist somewhere else - ToDo */
static char *find_last_slash(char *str)
{
#ifdef _WIN32
	char * LastFSlash;
	char * LastBSlash;
	LastFSlash=strrchr(str,'/');
	LastBSlash=strrchr(str,'\\');
	if(LastFSlash==NULL)
		return(LastBSlash);
	if(LastBSlash==NULL)
		return(LastFSlash);
	if(LastBSlash < LastFSlash)
		return(LastFSlash);
	return(LastBSlash);
#else
	return(strrchr(str,'/'));
#endif
}

/* This may exist somewhere else - ToDo */
static char *find_first_slash(char *str)
{
#ifdef _WIN32
	char * FirstFSlash;
	char * FirstBSlash;
	FirstFSlash=strchr(str,'/');
	FirstBSlash=strchr(str,'\\');
	if(FirstFSlash==NULL)
		return(FirstBSlash);
	if(FirstBSlash==NULL)
		return(FirstFSlash);
	if(FirstBSlash > FirstFSlash)
		return(FirstFSlash);
	return(FirstBSlash);
#else
	return(strchr(str,'/'));
#endif
}

static BOOL check_extra_path(http_session_t * session)
	char	rpath[MAX_PATH+1];
	char	vpath[MAX_PATH+1];
	char	epath[MAX_PATH+1];
	char	str[MAX_PATH+1];
	struct	stat sb;
	if(IS_PATH_DELIM(*lastchar(session->req.physical_path)) || stat(session->req.physical_path,&sb)==-1 /* && errno==ENOTDIR */)
	{
		SAFECOPY(vpath,session->req.virtual_path);
		SAFECOPY(rpath,session->req.physical_path);
		while((vp_slash=find_last_slash(vpath))!=NULL)
			*vp_slash=0;
			if((rp_slash=find_last_slash(rpath))==NULL)
				return(FALSE);
			SAFECOPY(str,epath);
			if(*rp_slash)
				sprintf(epath,"%s%s",rp_slash,str);
			*(rp_slash+1)=0;

			/* Check if this contains an index */
			end=strchr(rpath,0);
			if(session->req.path_info_index) {
				if(isdir(rpath) && !isdir(session->req.physical_path)) {
					for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++)  {
						*end=0;
						strcat(rpath,startup->index_file_name[i]);
						if(!stat(rpath,&sb)) {
							SAFECOPY(session->req.extra_path_info,epath);
							SAFECOPY(session->req.virtual_path,vpath);
							strcat(session->req.virtual_path,"/");
							SAFECOPY(session->req.physical_path,rpath);
							return(TRUE);
						}
					/* rpath was an existing path and DID NOT contain an index. */
					/* We do not allow scripts to mask existing dirs/files */
					return(FALSE);
			if(vp_slash==vpath)
				return(FALSE);

			/* Check if this is a script */
			*rp_slash=0;
			if(vp_slash!=vpath) {
				if(stat(rpath,&sb)!=-1 && (!(sb.st_mode&S_IFDIR)))
				{
					SAFECOPY(session->req.extra_path_info,epath);
					SAFECOPY(session->req.virtual_path,vpath);
					SAFECOPY(session->req.physical_path,rpath);
					return(TRUE);
				}
static BOOL check_request(http_session_t * session)
{
	char	path[MAX_PATH+1];
deuce's avatar
deuce committed
	char*	p;
	struct stat sb;
	char	filename[MAX_PATH+1];
	char	*spec;
	str_list_t	specs;
	BOOL	recheck_dynamic=FALSE;
	SAFECOPY(path,session->req.physical_path);
deuce's avatar
deuce committed
	if(startup->options&WEB_OPT_DEBUG_TX)
		lprintf(LOG_DEBUG,"%04d Path is: %s",session->socket,path);
		if(!IS_PATH_DELIM(last_ch))  {
deuce's avatar
deuce committed
			session->req.send_location=MOVED_PERM;
			strcat(path,"/");
			strcat(session->req.physical_path,"/");
		last_ch=*lastchar(session->req.virtual_path);
		if(!IS_PATH_DELIM(last_ch))  {
deuce's avatar
deuce committed
			session->req.send_location=MOVED_PERM;
			strcat(session->req.virtual_path,"/");
		}
		last_slash=find_last_slash(path);
deuce's avatar
deuce committed
		if(last_slash==NULL) {
			send_error(session,error_500);
deuce's avatar
deuce committed
			return(FALSE);
		}
		for(i=0; startup->index_file_name!=NULL && startup->index_file_name[i]!=NULL ;i++)  {
			*last_slash=0;
			strcat(path,startup->index_file_name[i]);
deuce's avatar
deuce committed
			if(startup->options&WEB_OPT_DEBUG_TX)
				lprintf(LOG_DEBUG,"%04d Checking for %s",session->socket,path);
			if(!stat(path,&sb))
deuce's avatar
deuce committed
				break;
			SAFECOPY(path,session->req.physical_path);

		/* Don't send 404 unless authourized... prevent info leak */
		if(startup->index_file_name==NULL || startup->index_file_name[i] == NULL)
			send404=1;
		else {
			strcat(session->req.virtual_path,startup->index_file_name[i]);
			if(session->req.send_location != MOVED_PERM)
				session->req.send_location=MOVED_STAT;
		filename[0]=0;
	}
	else {
		last_slash=find_last_slash(path);
		if(last_slash==NULL)
			last_slash=path;
		else
			last_slash++;
		strcpy(filename,last_slash);
	if(strnicmp(path,root_dir,strlen(root_dir))) {
		send_error(session,"400 Bad Request");
		lprintf(LOG_NOTICE,"%04d !ERROR Request for %s is outside of web root %s"
			,session->socket,path,root_dir);

	/* Set default ARS to a 0-length string */
	session->req.ars[0]=0;
	/* Walk up from root_dir checking for access.ars and webctrl.ini */
	SAFECOPY(curdir,path);
	last_slash=curdir+strlen(root_dir)-1;
	/* Loop while there's more /s in path*/
deuce's avatar
deuce committed
	p=last_slash;
	while((last_slash=find_first_slash(p+1))!=NULL) {
deuce's avatar
deuce committed
		p=last_slash;
		/* Terminate the path after the slash */
		sprintf(str,"%saccess.ars",curdir);
		if(!stat(str,&sb)) {
			/* NEVER serve up an access.ars file */
			lprintf(LOG_WARNING,"%04d !WARNING! access.ars support is depreciated and will be REMOVED very soon.",session->socket);
			lprintf(LOG_WARNING,"%04d !WARNING! access.ars found at %s.",session->socket,str);
			if(!strcmp(path,str)) {
				send_error(session,"403 Forbidden");
				return(FALSE);
			}
			/* Read access.ars file */
			if((file=fopen(str,"r"))!=NULL) {
rswindell's avatar
rswindell committed
				fgets(session->req.ars,sizeof(session->req.ars),file);
				fclose(file);
			}
			else  {
				/* If cannot open access.ars, only allow sysop access */
				SAFECOPY(session->req.ars,"LEVEL 90");
				break;
			}
			/* Truncate at \r or \n - can use last_slash since I'm done with it.*/
			truncsp(session->req.ars);
		}
		sprintf(str,"%swebctrl.ini",curdir);
			/* NEVER serve up a webctrl.ini file */
			if(!strcmp(path,str)) {
				send_error(session,"403 Forbidden");
				return(FALSE);
			}
rswindell's avatar
rswindell committed
			/* Read webctrl.ini file */
			if((file=fopen(str,"r"))!=NULL) {
deuce's avatar
deuce committed
				/* FREE()d in this block */
				specs=iniReadSectionList(file,NULL);
				/* Read in globals */
				if(iniReadString(file, NULL, "AccessRequirements", session->req.ars,str)==str)
					SAFECOPY(session->req.ars,str);
deuce's avatar
deuce committed
				if(iniReadString(file, NULL, "Realm", scfg.sys_name,str)==str) {
					FREE_AND_NULL(session->req.realm);
					/* FREE()d in close_request() */
					session->req.realm=strdup(str);
				if(iniReadString(file, NULL, "DigestRealm", scfg.sys_name,str)==str) {
					FREE_AND_NULL(session->req.digest_realm);
					/* FREE()d in close_request() */
					session->req.digest_realm=strdup(str);
				}
				if(iniReadString(file, NULL, "ErrorDirectory", error_dir,str)==str) {
					prep_dir(root_dir, str, sizeof(str));
deuce's avatar
deuce committed
					FREE_AND_NULL(session->req.error_dir);
					/* FREE()d in close_request() */
					session->req.error_dir=strdup(str);
				}