Skip to content
Snippets Groups Projects
websrvr.c 163 KiB
Newer Older
	if(cgi_handlers==NULL || (ext=getfext(fname))==NULL)
		return(NULL);
	for(i=0;cgi_handlers[i]!=NULL;i++) {
		if(stricmp(cgi_handlers[i]->name, ext+1)==0)
			return(cgi_handlers[i]->value);
}

static BOOL get_xjs_handler(char* ext, http_session_t* session)
{
	size_t	i;

deuce's avatar
deuce committed
	if(ext==NULL || xjs_handlers==NULL || ext[0]==0)
		return(FALSE);

	for(i=0;xjs_handlers[i]!=NULL;i++) {
		if(stricmp(xjs_handlers[i]->name, ext+1)==0) {
			if(getfname(xjs_handlers[i]->value)==xjs_handlers[i]->value)	/* no path specified */
				SAFEPRINTF2(session->req.xjs_handler,"%s%s",scfg.exec_dir,xjs_handlers[i]->value);
			else
				SAFECOPY(session->req.xjs_handler,xjs_handlers[i]->value);
			return(TRUE);
		}
	}
	return(FALSE);
}

/* 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) {
	dstlen=strlen(dst);
	appendlen=strlen(append);
	if(dstlen+appendlen+2 < maxlen) {
		strcat(dst,append);
		strcat(dst,newline);
	}
}

deuce's avatar
deuce committed
/*************************************************/
/* Sends headers for the reply.					 */
/* HTTP/0.9 doesn't use headers, so just returns */
/*************************************************/
static BOOL send_headers(http_session_t *session, const char *status, int chunked)
	BOOL	send_file=TRUE;
	char	header[MAX_REQUEST_LINE+1];
	BOOL	send_entity=TRUE;
deuce's avatar
deuce committed
	if(session->socket==INVALID_SOCKET) {
		session->req.sent_headers=TRUE;
	lprintf(LOG_DEBUG,"%04d Request resolved to: %s"
		,session->socket,session->req.physical_path);
	if(session->http_ver <= HTTP_0_9) {
deuce's avatar
deuce committed
		session->req.sent_headers=TRUE;
deuce's avatar
deuce committed
		if(session->req.ld != NULL)
			session->req.ld->status=atoi(status);
deuce's avatar
deuce committed
		return(TRUE);
	headers=alloca(MAX_HEADERS_SIZE);
deuce's avatar
deuce committed
	if(headers==NULL)  {
		lprintf(LOG_CRIT,"Could not allocate memory for response headers.");
		return(FALSE);
	}
	*headers=0;
	if(!session->req.sent_headers) {
deuce's avatar
deuce committed
		session->req.sent_headers=TRUE;
		status_line=status;
		ret=stat(session->req.physical_path,&stats);
		if(session->req.method==HTTP_OPTIONS)
			ret=-1;
		if(!ret && session->req.if_modified_since && (stats.st_mtime <= session->req.if_modified_since) && !session->req.dynamic) {
			status_line="304 Not Modified";
			ret=-1;
			send_file=FALSE;
			send_entity=FALSE;
deuce's avatar
deuce committed
		if(!ret && session->req.if_range && (stats.st_mtime > session->req.if_range || session->req.dynamic)) {
			status_line="200 OK";
			session->req.range_start=0;
			session->req.range_end=0;
		}
		if(session->req.send_location==MOVED_PERM)  {
			status_line=error_301;
			ret=-1;
			send_file=FALSE;
		}
		if(session->req.send_location==MOVED_TEMP)  {
			status_line=error_302;
			ret=-1;
			send_file=FALSE;
		}
			session->req.ld->status=stat_code;
		if(stat_code==304 || stat_code==204 || (stat_code >= 100 && stat_code<=199)) {
		/* Status-Line */
		safe_snprintf(header,sizeof(header),"%s %s",http_vers[session->http_ver],status_line);

		lprintf(LOG_DEBUG,"%04d Result: %s",session->socket,header);
		safecat(headers,header,MAX_HEADERS_SIZE);
		/* General Headers */
		ti=time(NULL);
		if(gmtime_r(&ti,&tm)==NULL)
			memset(&tm,0,sizeof(tm));
		safe_snprintf(header,sizeof(header),"%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) {
			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_CONNECTION),"Keep-Alive");
			safecat(headers,header,MAX_HEADERS_SIZE);
		}
		else {
			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_CONNECTION),"Close");
			safecat(headers,header,MAX_HEADERS_SIZE);
		}
		/* Response Headers */
		safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_SERVER),VERSION_NOTICE);
		safecat(headers,header,MAX_HEADERS_SIZE);

		/* Entity Headers */
		if(session->req.dynamic) {
			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD, POST, OPTIONS");
			safecat(headers,header,MAX_HEADERS_SIZE);
			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ACCEPT_RANGES),"none");
			safecat(headers,header,MAX_HEADERS_SIZE);
deuce's avatar
deuce committed
		}
		else {
			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ALLOW),"GET, HEAD, OPTIONS");
			safecat(headers,header,MAX_HEADERS_SIZE);
			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_ACCEPT_RANGES),"bytes");
			safecat(headers,header,MAX_HEADERS_SIZE);
		if(session->req.send_location) {
			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_LOCATION),(session->req.virtual_path));
			safecat(headers,header,MAX_HEADERS_SIZE);
		}

			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_TRANSFER_ENCODING),"Chunked");
			safecat(headers,header,MAX_HEADERS_SIZE);
		}

		/* DO NOT send a content-length for chunked */
		if(send_entity) {
			if(session->req.keep_alive && session->req.dynamic!=IS_CGI && (!chunked)) {
				if(ret)  {
					safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_LENGTH),"0");
					safecat(headers,header,MAX_HEADERS_SIZE);
				}
				else  {
					if((session->req.range_start || session->req.range_end) && atoi(status_line)==206) {
						safe_snprintf(header,sizeof(header),"%s: %d",get_header(HEAD_LENGTH),session->req.range_end-session->req.range_start+1);
						safecat(headers,header,MAX_HEADERS_SIZE);
					}
					else {
						safe_snprintf(header,sizeof(header),"%s: %d",get_header(HEAD_LENGTH),(int)stats.st_size);
						safecat(headers,header,MAX_HEADERS_SIZE);
					}
			if(!ret && !session->req.dynamic)  {
				safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_TYPE),session->req.mime_type);
				safecat(headers,header,MAX_HEADERS_SIZE);
				gmtime_r(&stats.st_mtime,&tm);
				safe_snprintf(header,sizeof(header),"%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);
				safecat(headers,header,MAX_HEADERS_SIZE);
			}
			if(session->req.range_start || session->req.range_end) {
				switch(atoi(status_line)) {
					case 206:	/* Partial reply */
						safe_snprintf(header,sizeof(header),"%s: bytes %d-%d/%d",get_header(HEAD_CONTENT_RANGE),session->req.range_start,session->req.range_end,stats.st_size);
						safecat(headers,header,MAX_HEADERS_SIZE);
						break;
					default:
						safe_snprintf(header,sizeof(header),"%s: *",get_header(HEAD_CONTENT_RANGE));
						safecat(headers,header,MAX_HEADERS_SIZE);
						break;
				}
	if(session->req.dynamic)  {
		/* Dynamic headers */
		for(idx=0;session->req.dynamic_heads[idx]!=NULL;idx++)
			safecat(headers,session->req.dynamic_heads[idx],MAX_HEADERS_SIZE);
		/* free() the headers so they don't get sent again if more are sent at the end of the request (chunked) */
		strListFreeStrings(session->req.dynamic_heads);
	safecat(headers,"",MAX_HEADERS_SIZE);
	send_file = (bufprint(session,headers) && send_file);
	session->req.write_chunked=chunked;
	return(send_file);
static int sock_sendfile(http_session_t *session,char *path,unsigned long start, unsigned long end)
	int		i;
	char	buf[2048];		/* Input buffer */
	unsigned long		remain;
	if(startup->options&WEB_OPT_DEBUG_TX)
		lprintf(LOG_DEBUG,"%04d Sending %s",session->socket,path);
	if((file=open(path,O_RDONLY|O_BINARY))==-1)
		lprintf(LOG_WARNING,"%04d !ERROR %d opening %s",session->socket,errno,path);
		if(start || end) {
			if(lseek(file, start, SEEK_SET)==-1) {
				lprintf(LOG_WARNING,"%04d !ERROR %d seeking to position %lu in %s",session->socket,ERROR_VALUE,start,path);
deuce's avatar
deuce committed
			remain=end-start+1;
		}
		else {
			remain=-1L;
		}
		while((i=read(file, buf, remain>sizeof(buf)?sizeof(buf):remain))>0) {
			if(writebuf(session,buf,i)!=i) {
				lprintf(LOG_WARNING,"%04d !ERROR sending %s",session->socket,path);
				return(0);
			}
			ret+=i;
deuce's avatar
deuce committed
/********************************************************/
/* Sends a specified error message, closes the request, */
/* and marks the session to be closed 					*/
/********************************************************/
static void send_error(http_session_t * session, const char* message)
	struct stat	sb;
	char	sbuf[MAX_PATH+1];
	char	sbuf2[MAX_PATH+1];
	if(session->socket==INVALID_SOCKET)
		return;
	session->req.if_modified_since=0;
	lprintf(LOG_INFO,"%04d !ERROR: %s",session->socket,message);
	session->req.send_location=NO_LOCATION;
	SAFECOPY(error_code,message);
	SAFECOPY(session->req.status,message);
	if(atoi(error_code)<500) {
		/*
		 * Attempt to run SSJS error pages
		 * If this fails, do the standard error page instead,
		 * ie: Don't "upgrade" to a 500 error
		 */

			/* We have a custom error directory from webctrl.ini look there first */
			sprintf(sbuf,"%s%s%s",session->req.error_dir,error_code,startup->ssjs_ext);
			if(stat(sbuf,&sb)) {
				/* No custom .ssjs error message... check for custom .html */
				sprintf(sbuf2,"%s%s.html",session->req.error_dir,error_code);
				if(stat(sbuf2,&sb)) {
					/* Nope, no custom .html error either, check for global ssjs one */
					sprintf(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
				}
			}
		}
		else
			sprintf(sbuf,"%s%s%s",error_dir,error_code,startup->ssjs_ext);
		if(!stat(sbuf,&sb)) {
			lprintf(LOG_INFO,"%04d Using SSJS error page",session->socket);
			session->req.dynamic=IS_SSJS;
			if(js_setup(session)) {
				sent_ssjs=exec_ssjs(session,sbuf);
					int	snt=0;

					lprintf(LOG_INFO,"%04d Sending generated error page",session->socket);
					snt=sock_sendfile(session,session->req.physical_path,0,0);
					if(snt<0)
						snt=0;
					if(session->req.ld!=NULL)
						session->req.ld->size=snt;
				}
				else
					 session->req.dynamic=IS_STATIC;
			else
				session->req.dynamic=IS_STATIC;
		if(session->req.error_dir) {
			sprintf(session->req.physical_path,"%s%s.html",session->req.error_dir,error_code);
			if(stat(session->req.physical_path,&sb))
				sprintf(session->req.physical_path,"%s%s.html",error_dir,error_code);
		}
		else
deuce's avatar
deuce committed
			sprintf(session->req.physical_path,"%s%s.html",error_dir,error_code);
		session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
		send_headers(session,message,FALSE);
		if(!stat(session->req.physical_path,&sb)) {
			int	snt=0;
			snt=sock_sendfile(session,session->req.physical_path,0,0);
			if(snt<0)
				snt=0;
			if(session->req.ld!=NULL)
				session->req.ld->size=snt;
		}
		else {
			lprintf(LOG_NOTICE,"%04d Error message file %s doesn't exist"
				,session->socket,session->req.physical_path);
			safe_snprintf(sbuf,sizeof(sbuf)
				,"<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);
			if(session->req.ld!=NULL)
				session->req.ld->size=strlen(sbuf);
		}
	session->req.finished=TRUE;
void http_logon(http_session_t * session, user_t *usr)
{
	if(usr==NULL)
		getuserdat(&scfg, &session->user);

	if(session->user.number==session->last_user_num)
		return;
	lprintf(LOG_DEBUG,"%04d HTTP Logon (user #%d)",session->socket,session->user.number);
	if(session->subscan!=NULL)
		getmsgptrs(&scfg,session->user.number,session->subscan);

	session->logon_time=time(NULL);
		SAFECOPY(session->username,unknown);
		SAFECOPY(session->username,session->user.alias);
		/* Adjust Connect and host */
		putuserrec(&scfg,session->user.number,U_MODEM,LEN_MODEM,"HTTP");
		putuserrec(&scfg,session->user.number,U_COMP,LEN_COMP,session->host_name);
		putuserrec(&scfg,session->user.number,U_NOTE,LEN_NOTE,session->host_ip);
		putuserrec(&scfg,session->user.number,U_LOGONTIME,0,ultoa((ulong)session->logon_time,str,16));
	session->client.user=session->username;
	client_on(session->socket, &session->client, /* update existing client record? */TRUE);

	session->last_user_num=session->user.number;
}

void http_logoff(http_session_t* session, SOCKET socket, int line)
	lprintf(LOG_DEBUG,"%04d HTTP Logoff (user #%d) from line %d"
		,socket,session->user.number, line);
	SAFECOPY(session->username,unknown);
	if(!logoutuserdat(&scfg, &session->user, time(NULL), session->logon_time))
		lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat", socket);
	memset(&session->user,0,sizeof(session->user));
	session->last_user_num=session->user.number;
}

BOOL http_checkuser(http_session_t * session)
{
	if(session->req.dynamic==IS_SSJS || session->req.dynamic==IS_JS) {
		if(session->last_js_user_num==session->user.number)
			return(TRUE);
		lprintf(LOG_DEBUG,"%04d JavaScript: Initializing User Objects",session->socket);
		JS_BEGINREQUEST(session->js_cx);
rswindell's avatar
rswindell committed
			if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, &session->user, &session->client
				,NULL /* ftp index file */, session->subscan /* subscan */)) {
				JS_ENDREQUEST(session->js_cx);
				lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user objects",session->socket);
				send_error(session,"500 Error initializing JavaScript User Objects");
				return(FALSE);
			}
		}
		else {
rswindell's avatar
rswindell committed
			if(!js_CreateUserObjects(session->js_cx, session->js_glob, &scfg, /* user: */NULL, &session->client
				,NULL /* ftp index file */, session->subscan /* subscan */)) {
				JS_ENDREQUEST(session->js_cx);
				lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript User Objects",session->socket);
				send_error(session,"500 Error initializing JavaScript User Objects");
				return(FALSE);
			}
		}
		JS_ENDREQUEST(session->js_cx);
		session->last_js_user_num=session->user.number;
static void calculate_digest(http_session_t * session, char *ha1, char *ha2, unsigned char digest[MD5_DIGEST_SIZE])
{
	MD5		ctx;

	MD5_open(&ctx);
	MD5_digest(&ctx, ha1, strlen(ha1));
	MD5_digest(&ctx, ":", 1);
	/* exception on next line (session->req.auth.nonce==NULL) */
	MD5_digest(&ctx, session->req.auth.nonce, strlen(session->req.auth.nonce));
	MD5_digest(&ctx, ":", 1);

	if(session->req.auth.qop_value != QOP_NONE) {
		MD5_digest(&ctx, session->req.auth.nonce_count, strlen(session->req.auth.nonce_count));
		MD5_digest(&ctx, ":", 1);
		MD5_digest(&ctx, session->req.auth.cnonce, strlen(session->req.auth.cnonce));
		MD5_digest(&ctx, ":", 1);
		switch(session->req.auth.qop_value) {
			case QOP_AUTH:
				MD5_digest(&ctx, "auth", 4);
				break;
			case QOP_AUTH_INT:
				MD5_digest(&ctx, "auth-int", 7);
				break;
		}
		MD5_digest(&ctx, ":", 1);
	}
	MD5_digest(&ctx, ha2, strlen(ha2));
	MD5_close(&ctx, digest);
}

static BOOL check_ars(http_session_t * session)
	int		auth_allowed=0;
	unsigned *auth_list;
	unsigned auth_list_len;

	auth_list=parseEnumList(session->req.auth_list?session->req.auth_list:default_auth_list, ",", auth_type_names, &auth_list_len);
deuce's avatar
deuce committed
	for(i=0; ((unsigned)i)<auth_list_len; i++)
		auth_allowed |= 1<<auth_list[i];
	if(auth_list)
		free(auth_list);
	/* No authentication provided */
	if(session->req.auth.type==AUTHENTICATION_UNKNOWN) {
		/* No authentication information... */
		if(session->last_user_num!=0) {
			if(session->last_user_num>0)
				http_logoff(session,session->socket,__LINE__);
			session->user.number=0;
			http_logon(session,NULL);
		}
		if(!http_checkuser(session))
			return(FALSE);
		if(session->req.ars[0]) {
			/* There *IS* an ARS string  ie: Auth is required */
			if(startup->options&WEB_OPT_DEBUG_RX)
				lprintf(LOG_NOTICE,"%04d !No authentication information",session->socket);
			return(FALSE);
		}
		/* No auth required, allow */
		return(TRUE);
	i=matchuser(&scfg, session->req.auth.username, FALSE);
	if(i==0) {
		if(session->last_user_num!=0) {
			if(session->last_user_num>0)
				http_logoff(session,session->socket,__LINE__);
			session->user.number=0;
			http_logon(session,NULL);
		}
		if(!http_checkuser(session))
			return(FALSE);
		if(scfg.sys_misc&SM_ECHO_PW && session->req.auth.type==AUTHENTICATION_BASIC)
			lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s, Password: %s"
				,session->socket,session->req.auth.username,session->req.auth.password);
			lprintf(LOG_NOTICE,"%04d !UNKNOWN USER: %s"
				,session->socket,session->req.auth.username);
	switch(session->req.auth.type) {
		case AUTHENTICATION_BASIC:
			if((auth_allowed & (1<<AUTHENTICATION_BASIC))==0)
				return(FALSE);
			if(thisuser.pass[0] && stricmp(thisuser.pass,session->req.auth.password)) {
				if(session->last_user_num!=0) {
					if(session->last_user_num>0)
						http_logoff(session,session->socket,__LINE__);
					session->user.number=0;
					http_logon(session,NULL);
				}
				if(!http_checkuser(session))
					return(FALSE);
				/* 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,session->req.auth.username,session->req.auth.password,thisuser.pass);
				else
					lprintf(LOG_WARNING,"%04d !PASSWORD FAILURE for user %s"
						,session->socket,session->req.auth.username);
		#ifdef _WIN32
				if(startup->hack_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
					PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
		#endif
				return(FALSE);
			}
			break;
		case AUTHENTICATION_DIGEST:
			{
				unsigned char	digest[MD5_DIGEST_SIZE];
				char			ha1[MD5_DIGEST_SIZE*2+1];
				char			ha1l[MD5_DIGEST_SIZE*2+1];
				char			ha1u[MD5_DIGEST_SIZE*2+1];
				char			ha2[MD5_DIGEST_SIZE*2+1];
				char			*p;
				time32_t		nonce_time;
				time32_t		now;
				MD5				ctx;
				if((auth_allowed & (1<<AUTHENTICATION_DIGEST))==0)
					return(FALSE);
				if(session->req.auth.qop_value==QOP_UNKNOWN)
					return(FALSE);
				if(session->req.auth.algorithm==ALGORITHM_UNKNOWN)
					return(FALSE);
				/* Validate rules from RFC-2617 */
				if(session->req.auth.qop_value==QOP_AUTH
						|| session->req.auth.qop_value==QOP_AUTH_INT) {
					if(session->req.auth.cnonce==NULL)
						return(FALSE);
					if(session->req.auth.nonce_count==NULL)
						return(FALSE);
				}
				else {
					if(session->req.auth.cnonce!=NULL)
						return(FALSE);
					if(session->req.auth.nonce_count!=NULL)
						return(FALSE);
				}

				/* H(A1) */
				MD5_open(&ctx);
				MD5_digest(&ctx, session->req.auth.username, strlen(session->req.auth.username));
				MD5_digest(&ctx, ":", 1);
				MD5_digest(&ctx, session->req.digest_realm?session->req.digest_realm:(session->req.realm?session->req.realm:scfg.sys_name), strlen(session->req.digest_realm?session->req.digest_realm:(session->req.realm?session->req.realm:scfg.sys_name)));
				MD5_digest(&ctx, ":", 1);
				MD5_digest(&ctx, thisuser.pass, strlen(thisuser.pass));
				MD5_close(&ctx, digest);
				MD5_hex(ha1, digest);

				/* H(A1)l */
				pass=strdup(thisuser.pass);
				strlwr(pass);
				MD5_open(&ctx);
				MD5_digest(&ctx, session->req.auth.username, strlen(session->req.auth.username));
				MD5_digest(&ctx, ":", 1);
				MD5_digest(&ctx, session->req.digest_realm?session->req.digest_realm:(session->req.realm?session->req.realm:scfg.sys_name), strlen(session->req.digest_realm?session->req.digest_realm:(session->req.realm?session->req.realm:scfg.sys_name)));
				MD5_digest(&ctx, ":", 1);
				MD5_digest(&ctx, pass, strlen(pass));
				MD5_close(&ctx, digest);
				MD5_hex(ha1l, digest);

				/* H(A1)u */
				strupr(pass);
				MD5_open(&ctx);
				MD5_digest(&ctx, session->req.auth.username, strlen(session->req.auth.username));
				MD5_digest(&ctx, ":", 1);
				MD5_digest(&ctx, session->req.digest_realm?session->req.digest_realm:(session->req.realm?session->req.realm:scfg.sys_name), strlen(session->req.digest_realm?session->req.digest_realm:(session->req.realm?session->req.realm:scfg.sys_name)));
				MD5_digest(&ctx, ":", 1);
				MD5_digest(&ctx, thisuser.pass, strlen(thisuser.pass));
				MD5_close(&ctx, digest);
				MD5_hex(ha1u, digest);
				free(pass);

				/* H(A2) */
				MD5_open(&ctx);
				MD5_digest(&ctx, methods[session->req.method], strlen(methods[session->req.method]));
				MD5_digest(&ctx, ":", 1);
				/* exception here, session->req.auth.digest_uri==NULL */
				MD5_digest(&ctx, session->req.auth.digest_uri, strlen(session->req.auth.digest_uri));
				/* TODO QOP==AUTH_INT */
				if(session->req.auth.qop_value == QOP_AUTH_INT)
					return(FALSE);
				MD5_close(&ctx, digest);
				MD5_hex(ha2, digest);

				/* Check password as in user.dat */
				calculate_digest(session, ha1, ha2, digest);
				if(thisuser.pass[0]) {	// Zero-length password is "special" (any password will work)
					if(memcmp(digest, session->req.auth.digest, sizeof(digest))) {
						/* Check against lower-case password */
						calculate_digest(session, ha1l, ha2, digest);
						if(memcmp(digest, session->req.auth.digest, sizeof(digest))) {
							/* Check against upper-case password */
							calculate_digest(session, ha1u, ha2, digest);
							if(memcmp(digest, session->req.auth.digest, sizeof(digest)))
								return(FALSE);
						}
				p=strchr(session->req.auth.nonce, '@');
				if(p==NULL) {
					session->req.auth.stale=TRUE;
					return(FALSE);
				}
				*p=0;
				if(strcmp(session->req.auth.nonce, session->client.addr)) {
					session->req.auth.stale=TRUE;
					return(FALSE);
				}
				nonce_time=strtoul(p, &p, 10);
				if(*p) {
					session->req.auth.stale=TRUE;
					return(FALSE);
				}
				now=(time32_t)time(NULL);
				if(nonce_time > now) {
					session->req.auth.stale=TRUE;
					return(FALSE);
				}
				if(nonce_time < now-1800) {
					session->req.auth.stale=TRUE;
					return(FALSE);
				}
		http_logoff(session,session->socket,__LINE__);
		session->user.number=i;
		http_logon(session,&thisuser);
	}
	if(!http_checkuser(session))
		return(FALSE);

	if(session->req.ld!=NULL) {
		FREE_AND_NULL(session->req.ld->user);
		/* FREE()d in http_logging_thread */
		session->req.ld->user=strdup(session->req.auth.username);
	ar = arstr(NULL,session->req.ars,&scfg);
rswindell's avatar
rswindell committed
	authorized=chk_ar(&scfg,ar,&session->user,&session->client);
deuce's avatar
deuce committed
		FREE_AND_NULL(ar);
	if(authorized)  {
		switch(session->req.auth.type) {
			case AUTHENTICATION_BASIC:
				add_env(session,"AUTH_TYPE","Basic");
				break;
			case AUTHENTICATION_DIGEST:
				add_env(session,"AUTH_TYPE","Digest");
				break;
		}
		/* Should use real name if set to do so somewhere ToDo */
		add_env(session,"REMOTE_USER",session->user.alias);
	lprintf(LOG_WARNING,"%04d !AUTHORIZATION FAILURE for user %s, ARS: %s"
		,session->socket,session->req.auth.username,session->req.ars);
#ifdef _WIN32
	if(startup->hack_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
		PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
#endif

static named_string_t** read_ini_list(char* path, char* section, char* desc
	if((fp=iniOpenFile(path, /* create? */FALSE))!=NULL) {
		list=iniReadNamedStringList(fp,section);
		iniCloseFile(fp);
		COUNT_LIST_ITEMS(list,i);
			lprintf(LOG_DEBUG,"Read %u %s from %s section of %s"
				,i,desc,section==NULL ? "root":section,path);
static int sockreadline(http_session_t * session, char *buf, size_t length)
	fd_set	rd_set;
	struct	timeval tv;
	for(i=0;TRUE;) {
		if(session->socket==INVALID_SOCKET)
			return(-1);
		FD_ZERO(&rd_set);
		FD_SET(session->socket,&rd_set);
		/* Convert timeout from ms to sec/usec */
		tv.tv_sec=startup->max_inactivity;
		tv.tv_usec=0;
		sel=select(session->socket+1,&rd_set,NULL,NULL,&tv);
		switch(sel) {
deuce's avatar
deuce committed
			case -1:
				close_socket(&session->socket);
				lprintf(LOG_DEBUG,"%04d !ERROR %d selecting socket for read",session->socket,ERROR_VALUE);
				return(-1);
				/* Timeout */
				lprintf(LOG_NOTICE,"%04d Session timeout due to inactivity (%d seconds)",session->socket,startup->max_inactivity);
				return(-1);
		switch(recv(session->socket, &ch, 1, 0)) {
			case -1:
					if(startup->options&WEB_OPT_DEBUG_RX)
						lprintf(LOG_DEBUG,"%04d !ERROR %d receiving on socket",session->socket,ERROR_VALUE);
					close_socket(&session->socket);
				/* Socket has been closed */
				close_socket(&session->socket);
		if(ch=='\n')
			break;

		if(i<length)
			buf[i++]=ch;

	/* Terminate at length if longer */
	if(i>length)
		i=length;
	while(i>0 && buf[i-1]=='\r')
		i--;

	buf[i]=0;
	if(startup->options&WEB_OPT_DEBUG_RX) {
		lprintf(LOG_DEBUG,"%04d RX: %s",session->socket,buf);
			lprintf(LOG_DEBUG,"%04d Long header, chucked %d bytes",session->socket,chucked);
	return(i);
deuce's avatar
deuce committed
static int pipereadline(HANDLE pipe, char *buf, size_t length, char *fullbuf, size_t fullbuf_len)
deuce's avatar
deuce committed
static int pipereadline(int pipe, char *buf, size_t length, char *fullbuf, size_t fullbuf_len)
#ifndef _WIN32
	struct timeval tv={0,0};
	fd_set  read_set;
#endif
deuce's avatar
deuce committed
	/* Terminate buffers */
	if(buf != NULL)
		buf[0]=0;
	if(fullbuf != NULL)
		fullbuf[0]=0;
		ReadFile(pipe, &ch, 1, (DWORD*)&ret, NULL);
		tv.tv_sec=startup->max_cgi_inactivity;
		tv.tv_usec=0;
		FD_ZERO(&read_set);
		FD_SET(pipe, &read_set);
		if(select(pipe+1, &read_set, NULL, NULL, &tv)<1)
			return(-1);
		ret=read(pipe, &ch, 1);
deuce's avatar
deuce committed
			if(fullbuf != NULL && i < (fullbuf_len-1)) {
				fullbuf[i]=ch;
				fullbuf[i+1]=0;
			}

deuce's avatar
deuce committed
			if(buf != NULL && i<length)
				buf[i]=ch;

			i++;
	}

	/* Terminate at length if longer */
	if(i>length)
		i=length;
deuce's avatar
deuce committed
	if(i>0 && buf != NULL && buf[i-1]=='\r')
		buf[--i]=0;
deuce's avatar
deuce committed
	else {
		if(buf != NULL)
			buf[i]=0;
	}
	return(i);
int recvbufsocket(SOCKET *sock, char *buf, long count)
	int		i;
	time_t	start;
	while(rd<count && socket_check(*sock,NULL,NULL,startup->max_inactivity*1000))  {
		i=recv(*sock,buf+rd,count-rd,0);
		switch(i) {
			case -1:
		}

		rd+=i;
		start=time(NULL);
	return(0);
static void unescape(char *p)
{
	char *	dst;
	char	code[3];
	
	dst=p;
	for(;*p;p++) {
		if(*p=='%' && isxdigit(*(p+1)) && isxdigit(*(p+2))) {
			sprintf(code,"%.2s",p+1);
			*(dst++)=(char)strtol(code,NULL,16);
			p+=2;
		}
		else  {
			if(*p=='+')  {
				*(dst++)=' ';
			}
			else  {
				*(dst++)=*p;
			}
		}
	}
	*(dst)=0;
}

static void js_add_queryval(http_session_t * session, char *key, char *value)
	jsuint		len;
	int			alen;

	/* Return existing object if it's already been created */
	if(JS_GetProperty(session->js_cx,session->js_query,key,&val) && val!=JSVAL_VOID)  {
		keyarray = JSVAL_TO_OBJECT(val);
		alen=-1;
	}
	else {
		keyarray = JS_NewArrayObject(session->js_cx, 0, NULL);
		if(!JS_DefineProperty(session->js_cx, session->js_query, key, OBJECT_TO_JSVAL(keyarray)
			, NULL, NULL, JSPROP_ENUMERATE))
			return;
		alen=0;
	}

	if(alen==-1) {
		if(JS_GetArrayLength(session->js_cx, keyarray, &len)==JS_FALSE)
			return;
deuce's avatar
deuce committed
		alen=len;
deuce's avatar
deuce committed
	lprintf(LOG_DEBUG,"%04d Adding query value %s=%s at pos %d",session->socket,key,value,alen);
	val=STRING_TO_JSVAL(JS_NewStringCopyZ(session->js_cx,value));
	JS_SetElement(session->js_cx, keyarray, alen, &val);
}

static void js_add_cookieval(http_session_t * session, char *key, char *value)
{
	JSObject*	keyarray;
	jsval		val;
	jsuint		len;
	int			alen;

	/* Return existing object if it's already been created */
	if(JS_GetProperty(session->js_cx,session->js_cookie,key,&val) && val!=JSVAL_VOID)  {
		keyarray = JSVAL_TO_OBJECT(val);
		alen=-1;
	}
	else {
		keyarray = JS_NewArrayObject(session->js_cx, 0, NULL);
		if(!JS_DefineProperty(session->js_cx, session->js_cookie, key, OBJECT_TO_JSVAL(keyarray)
			, NULL, NULL, JSPROP_ENUMERATE))
			return;
		alen=0;
	}

	if(alen==-1) {
		if(JS_GetArrayLength(session->js_cx, keyarray, &len)==JS_FALSE)
			return;
		alen=len;
	}