diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index 62b62189802c31652ed9c9364cc7ccfc8f58559d..9ab394b11a9339a916b2e1f276d5bea2d7502ba7 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -2243,9 +2243,9 @@ static BOOL badlogin(SOCKET sock, ulong* login_attempts, char* user, char* passw
 
 	if(addr!=NULL) {
 		count=loginFailure(startup->login_attempt_list, addr, "FTP", user, passwd);
-		if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold)
+		if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
 			ftp_hacklog("FTP LOGIN", user, passwd, host, addr);
-		if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) {
+		if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold) {
 			inet_addrtop(addr, host_ip, sizeof(host_ip));
 			filter_ip(&scfg, "FTP", "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
 				,host, host_ip, user, /* fname: */NULL);
@@ -2255,7 +2255,7 @@ static BOOL badlogin(SOCKET sock, ulong* login_attempts, char* user, char* passw
 	} else
 		(*login_attempts)++;
 
-	mswait(startup->login_attempt_delay);	/* As recommended by RFC2577 */
+	mswait(startup->login_attempt.delay);	/* As recommended by RFC2577 */
 
 	if((*login_attempts)>=3) {
 		sockprintf(sock,"421 Too many failed login attempts.");
@@ -2421,8 +2421,9 @@ static void ctrl_thread(void* arg)
 	if(!(startup->options&FTP_OPT_NO_HOST_LOOKUP))
 		lprintf(LOG_INFO,"%04d Hostname: %s", sock, host_name);
 
-	if(trashcan(&scfg,host_ip,"ip")) {
-		lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", sock, host_ip);
+	ulong banned = loginBanned(&scfg, startup->login_attempt_list, &ftp.client_addr,  startup->login_attempt);
+	if((banned && lprintf(LOG_NOTICE, "%04d %s is TEMPORARILY BANNED (%lu more seconds)", socket, host_ip, banned))
+		|| (trashcan(&scfg,host_ip,"ip") && lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", sock, host_ip))) {
 		sockprintf(sock,"550 Access denied.");
 		ftp_close_socket(&sock,__LINE__);
 		thread_down();
@@ -2460,11 +2461,11 @@ static void ctrl_thread(void* arg)
 	client.user="<unknown>";
 	client_on(sock,&client,FALSE /* update */);
 
-	if(startup->login_attempt_throttle
+	if(startup->login_attempt.throttle
 		&& (login_attempts=loginAttempts(startup->login_attempt_list, &ftp.client_addr)) > 1) {
 		lprintf(LOG_DEBUG,"%04d Throttling suspicious connection from: %s (%u login attempts)"
 			,sock, host_ip, login_attempts);
-		mswait(login_attempts*startup->login_attempt_throttle);
+		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
 	sockprintf(sock,"220-%s (%s)",scfg.sys_name, startup->host_name);
diff --git a/src/sbbs3/ftpsrvr.h b/src/sbbs3/ftpsrvr.h
index ff0ad17aacf1df53912d2af4039135d27eaee200..540bbff1d53d2fbc85eb2253c492a7027b50fc70 100644
--- a/src/sbbs3/ftpsrvr.h
+++ b/src/sbbs3/ftpsrvr.h
@@ -100,10 +100,7 @@ typedef struct {
 	js_startup_t js;
 
 	/* Login Attempt parameters */
-	ulong	login_attempt_delay;
-	ulong	login_attempt_throttle;
-	ulong	login_attempt_hack_threshold;
-	ulong	login_attempt_filter_threshold;
+	struct login_attempt_settings login_attempt;
 	link_list_t* login_attempt_list;
 
 } ftp_startup_t;
diff --git a/src/sbbs3/login.cpp b/src/sbbs3/login.cpp
index 62526b6d94fdeedba7e7dfd9164252519e703a18..6de4ba711e2d278b2b289de0b281b02b76203006 100644
--- a/src/sbbs3/login.cpp
+++ b/src/sbbs3/login.cpp
@@ -148,11 +148,11 @@ void sbbs_t::badlogin(char* user, char* passwd)
 
 	SAFEPRINTF(reason,"%s LOGIN", connection);
 	count=loginFailure(startup->login_attempt_list, &client_addr, connection, user, passwd);
-	if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold)
+	if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
 		::hacklog(&cfg, reason, user, passwd, client_name, &client_addr);
-	if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold)
+	if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold)
 		filter_ip(&cfg, connection, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
 			,client_name, client_ipaddr, user, /* fname: */NULL);
 
-	mswait(startup->login_attempt_delay);
+	mswait(startup->login_attempt.delay);
 }
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index d6aa5d47b32356260ed35a3ef2860cac6501df9d..ace13203031c66f051c2b742f01e2a9e44f40d92 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -522,7 +522,7 @@ static ulong sockmimetext(SOCKET socket, smbmsg_t* msg, char* msgtxt, ulong maxl
 		s=sockprintf(socket,"From: %s",p);	/* use original RFC822 header field */
 	else {
 		char fromname[256];
-		SAFECOPY(fromname, msg->from);
+		SAFEPRINTF(fromname, "\"%s\"", msg->from);
 		if(msg->from_net.type==NET_QWK && msg->from_net.addr!=NULL)
 			SAFEPRINTF2(fromaddr,"%s!%s"
 				,(char*)msg->from_net.addr
@@ -530,7 +530,7 @@ static ulong sockmimetext(SOCKET socket, smbmsg_t* msg, char* msgtxt, ulong maxl
 		else if(msg->from_net.type==NET_FIDO && msg->from_net.addr!=NULL) {
 			faddr_t* faddr = (faddr_t *)msg->from_net.addr;
 			char faddrstr[128];
-			SAFEPRINTF2(fromname,"%s (%s)", msg->from, smb_faddrtoa(faddr, NULL));
+			SAFEPRINTF2(fromname,"\"%s\" (%s)", msg->from, smb_faddrtoa(faddr, NULL));
 			if(faddr->point)
 				SAFEPRINTF4(faddrstr,"p%hu.f%hu.n%hu.z%hu"FIDO_TLD
 					,faddr->point, faddr->node, faddr->net, faddr->zone);
@@ -543,9 +543,9 @@ static ulong sockmimetext(SOCKET socket, smbmsg_t* msg, char* msgtxt, ulong maxl
 		else 
 			usermailaddr(&scfg,fromaddr,msg->from);
 		if(fromaddr[0]=='<')
-			s=sockprintf(socket,"From: \"%s\" %s",fromname,fromaddr);
+			s=sockprintf(socket,"From: %s %s",fromname,fromaddr);
 		else
-			s=sockprintf(socket,"From: \"%s\" <%s>",fromname,fromaddr);
+			s=sockprintf(socket,"From: %s <%s>",fromname,fromaddr);
 	}
 	if(!s)
 		return(0);
@@ -569,7 +569,7 @@ static ulong sockmimetext(SOCKET socket, smbmsg_t* msg, char* msgtxt, ulong maxl
 			else
 				s=sockprintf(socket,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
 		} else if(msg->to_net.type==NET_FIDO) {
-			s=sockprintf(socket,"To: \"%s (%s)\"",msg->to, smb_faddrtoa((fidoaddr_t*)msg->to_net.addr, NULL));
+			s=sockprintf(socket,"To: \"%s\" (%s)",msg->to, smb_faddrtoa((fidoaddr_t*)msg->to_net.addr, NULL));
 		} else {
 			usermailaddr(&scfg,toaddr,msg->to);
 			s=sockprintf(socket,"To: \"%s\" <%s>",msg->to,toaddr);
@@ -782,15 +782,15 @@ static void badlogin(SOCKET sock, const char* prot, const char* resp, char* user
 	if(addr!=NULL) {
 		SAFEPRINTF(reason,"%s LOGIN", prot);
 		count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
-		if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold)
+		if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
 			hacklog(&scfg, reason, user, passwd, host, addr);
 		inet_addrtop(addr, ip, sizeof(ip));
-		if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold)
+		if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold)
 			filter_ip(&scfg, (char*)prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
 				,host, ip, user, /* fname: */NULL);
 	}
 
-	mswait(startup->login_attempt_delay);
+	mswait(startup->login_attempt.delay);
 	sockprintf(sock,(char*)resp);
 }
 
@@ -853,9 +853,9 @@ static void pop3_thread(void* arg)
 	if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP) && (startup->options&MAIL_OPT_DEBUG_POP3))
 		lprintf(LOG_INFO,"%04d POP3 Hostname: %s", socket, host_name);
 
-	if(trashcan(&scfg,host_ip,"ip")) {
-		lprintf(LOG_NOTICE,"%04d !POP3 CLIENT IP ADDRESS BLOCKED: %s"
-			,socket, host_ip);
+	ulong banned = loginBanned(&scfg, startup->login_attempt_list, &pop3.client_addr,  startup->login_attempt);
+	if((banned && lprintf(LOG_NOTICE, "%04d %s is TEMPORARILY BANNED (%lu more seconds)", socket, host_ip, banned))
+		|| (trashcan(&scfg,host_ip,"ip") && lprintf(LOG_NOTICE,"%04d !POP3 CLIENT IP ADDRESS BLOCKED: %s",socket, host_ip))) {
 		sockprintf(socket,"-ERR Access denied.");
 		mail_close_socket(socket);
 		thread_down();
@@ -887,11 +887,11 @@ static void pop3_thread(void* arg)
 	SAFEPRINTF(str,"POP3: %s", host_ip);
 	status(str);
 
-	if(startup->login_attempt_throttle
+	if(startup->login_attempt.throttle
 		&& (login_attempts=loginAttempts(startup->login_attempt_list, &pop3.client_addr)) > 1) {
 		lprintf(LOG_DEBUG,"%04d POP3 Throttling suspicious connection from: %s (%u login attempts)"
 			,socket, host_ip, login_attempts);
-		mswait(login_attempts*startup->login_attempt_throttle);
+		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
 	mail=NULL;
@@ -2217,6 +2217,24 @@ static int chk_received_hdr(SOCKET socket,const char *buf,IN_ADDR *dnsbl_result,
 	return(dnsbl_result->s_addr);
 }
 
+static void strip_char(char* str, char ch)
+{
+	char* src;
+	char* p;
+	char* tmp = strdup(str);
+
+	if(tmp == NULL)
+		return;
+	p=tmp;
+	for(src = str; *src; src++) {
+		if(*src != ch)
+			*(p++) = *src;
+	}
+	*p=0;
+	strcpy(str, tmp);
+	free(tmp);
+}
+
 static void parse_mail_address(char* p
 							   ,char* name, size_t name_len
 							   ,char* addr, size_t addr_len)
@@ -2238,20 +2256,21 @@ static void parse_mail_address(char* p
 	SAFECOPY(tmp,p);
 	p=tmp;
 	/* Get the "name" (if possible) */
-	if((tp=strchr(p,'('))!=NULL) {			/* name in parenthesis? */
+	if((tp=strchr(p,'"'))!=NULL) {	/* name in quotes? */
 		p=tp+1;
-		tp=strchr(p,')');
-	} else if((tp=strchr(p,'"'))!=NULL) {	/* name in quotes? */
+		tp=strrchr(p,'"');
+	} else if((tp=strchr(p,'('))!=NULL) {	/* name in parenthesis? */
 		p=tp+1;
-		tp=strchr(p,'"');
+		tp=strrchr(p,')');
 	} else if(*p=='<') {					/* address in brackets? */
 		p++;
-		tp=strchr(p,'>');
+		tp=strrchr(p,'>');
 	} else									/* name, then address in brackets */
 		tp=strchr(p,'<');
 	if(tp) *tp=0;
 	sprintf(name,"%.*s",(int)name_len,p);
 	truncsp(name);
+	strip_char(name, '\\');
 }
 
 /* Decode quoted-printable content-transfer-encoded text */
@@ -2377,7 +2396,8 @@ static void smtp_thread(void* arg)
 	char		spam_bait[MAX_PATH+1];
 	BOOL		spam_bait_result=FALSE;
 	char		spam_block[MAX_PATH+1];
-	char		spam_block_exempt[MAX_PATH+1];
+	char		spam_block_exemptions[MAX_PATH+1];
+	BOOL		spam_block_exempt=FALSE;
 	char		host_name[128];
 	char		host_ip[INET6_ADDRSTRLEN];
 	char		server_ip[INET6_ADDRSTRLEN];
@@ -2528,7 +2548,7 @@ static void smtp_thread(void* arg)
 
 	SAFEPRINTF(spam_bait,"%sspambait.cfg",scfg.ctrl_dir);
 	SAFEPRINTF(spam_block,"%sspamblock.cfg",scfg.ctrl_dir);
-	SAFEPRINTF(spam_block_exempt,"%sspamblock_exempt.cfg",scfg.ctrl_dir);
+	SAFEPRINTF(spam_block_exemptions,"%sspamblock_exempt.cfg",scfg.ctrl_dir);
 
 	inet_addrtop(&server_addr,server_ip,sizeof(server_ip));
 
@@ -2536,8 +2556,20 @@ static void smtp_thread(void* arg)
 		/* local connection */
 		dnsbl_result.s_addr=0;
 	} else {
+		ulong banned = loginBanned(&scfg, startup->login_attempt_list, &smtp.client_addr,  startup->login_attempt);
+		if(banned) {
+			lprintf(LOG_NOTICE, "%04d %s is TEMPORARILY BANNED (%lu more seconds)", socket, host_ip, banned);
+			mail_close_socket(socket);
+			thread_down();
+			protected_uint32_adjust(&active_clients, -1);
+			update_clients();
+			free(mailproc_to_match);
+			return;
+		}
+
+		spam_block_exempt = findstr(host_ip,spam_block_exemptions) || findstr(host_name,spam_block_exemptions);
 		if(trashcan(&scfg,host_ip,"ip") 
-			|| (findstr(host_ip,spam_block) && !findstr(host_ip,spam_block_exempt))) {
+			|| (findstr(host_ip,spam_block) && !spam_block_exempt)) {
 			lprintf(LOG_NOTICE,"%04d !SMTP CLIENT IP ADDRESS BLOCKED: %s (%u total)"
 				,socket, host_ip, ++stats.sessions_refused);
 			sockprintf(socket,"550 CLIENT IP ADDRESS BLOCKED: %s", host_ip);
@@ -2550,7 +2582,7 @@ static void smtp_thread(void* arg)
 		}
 
 		if(trashcan(&scfg,host_name,"host") 
-			|| (findstr(host_name,spam_block) && !findstr(host_name,spam_block_exempt))) {
+			|| (findstr(host_name,spam_block) && !spam_block_exempt)) {
 			lprintf(LOG_NOTICE,"%04d !SMTP CLIENT HOSTNAME BLOCKED: %s (%u total)"
 				,socket, host_name, ++stats.sessions_refused);
 			sockprintf(socket,"550 CLIENT HOSTNAME BLOCKED: %s", host_name);
@@ -2641,11 +2673,11 @@ static void smtp_thread(void* arg)
 	SAFEPRINTF(str,"SMTP: %s",host_ip);
 	status(str);
 
-	if(startup->login_attempt_throttle
+	if(startup->login_attempt.throttle
 		&& (login_attempts=loginAttempts(startup->login_attempt_list, &smtp.client_addr)) > 1) {
 		lprintf(LOG_DEBUG,"%04d SMTP Throttling suspicious connection from: %s (%u login attempts)"
 			,socket, host_ip, login_attempts);
-		mswait(login_attempts*startup->login_attempt_throttle);
+		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
 	/* SMTP session active: */
@@ -3849,8 +3881,7 @@ static void smtp_thread(void* arg)
 				if(relay_user.number==0) {
 					strcpy(tmp,"IGNORED");
 					if(dnsbl_result.s_addr==0						/* Don't double-filter */
-						&& !findstr(host_name,spam_block_exempt)
-						&& !findstr(host_ip,spam_block_exempt))	{ 
+						&& !spam_block_exempt)	{ 
 						lprintf(LOG_NOTICE,"%04d !BLOCKING IP ADDRESS: %s in %s", socket, host_ip, spam_block);
 						filter_ip(&scfg, "SMTP", reason, host_name, host_ip, reverse_path, spam_block);
 						strcat(tmp," and BLOCKED");
diff --git a/src/sbbs3/mailsrvr.h b/src/sbbs3/mailsrvr.h
index e8aae6b8fb854be7d845eb5da2ca11f518e3ac45..7af590539ca80ba82fa12cc63151097f820ec379 100644
--- a/src/sbbs3/mailsrvr.h
+++ b/src/sbbs3/mailsrvr.h
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2014 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 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				*
@@ -120,10 +120,7 @@ typedef struct {
 	js_startup_t js;
 
 	/* Login Attempt parameters */
-	ulong	login_attempt_delay;
-	ulong	login_attempt_throttle;
-	ulong	login_attempt_hack_threshold;
-	ulong	login_attempt_filter_threshold;
+	struct login_attempt_settings login_attempt;
 	link_list_t* login_attempt_list;
 
 } mail_startup_t;
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index ff8e95b9f3e11a18d4acfeddaffc431d126a7036..226518607f1af1f51d962ebb8b30861c11f98134 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -4146,11 +4146,11 @@ void node_thread(void* arg)
 	}
 #endif
 
-	if(startup->login_attempt_throttle
+	if(startup->login_attempt.throttle
 		&& (login_attempts=loginAttempts(startup->login_attempt_list, &sbbs->client_addr)) > 1) {
 		lprintf(LOG_DEBUG,"Node %d Throttling suspicious connection from: %s (%u login attempts)"
 			,sbbs->cfg.node_num, sbbs->client_ipaddr, login_attempts);
-		mswait(login_attempts*startup->login_attempt_throttle);
+		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
 	if(sbbs->answer()) {
@@ -5121,6 +5121,16 @@ NO_SSH:
 #endif
 			, host_ip, inet_addrport(&client_addr));
 
+		ulong banned = loginBanned(&scfg, startup->login_attempt_list, &client_addr,  startup->login_attempt);
+		if((banned && lprintf(LOG_NOTICE, "%04d %s is TEMPORARILY BANNED (%lu more seconds)", client_socket, host_ip, banned))
+			|| (sbbs->trashcan(host_ip,"ip") && lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", client_socket, host_ip))) {
+			SSH_END();
+			close_socket(client_socket);
+			SAFEPRINTF(logstr, "Blocked IP: %s",host_ip);
+			sbbs->syslog("@!",logstr);
+			continue;
+		}
+
 #ifdef _WIN32
 		if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
 			PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
@@ -5202,16 +5212,6 @@ NO_SSH:
    		sbbs->client_socket=client_socket;	// required for output to the user
         sbbs->online=ON_REMOTE;
 
-		if(sbbs->trashcan(host_ip,"ip")) {
-			SSH_END();
-			close_socket(client_socket);
-			lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s"
-				,client_socket, host_ip);
-			SAFEPRINTF(logstr, "Blocked IP: %s",host_ip);
-			sbbs->syslog("@!",logstr);
-			continue;
-		}
-
 		if(rlogin)
 			sbbs->outcom(0); /* acknowledge RLogin per RFC 1282 */
 
diff --git a/src/sbbs3/sbbs_ini.c b/src/sbbs3/sbbs_ini.c
index 1651ef109e1b235743f32d968c9cfe3e5cf76411..1162e3fd6cdaa0f73295a54972e48a88ac5526af 100644
--- a/src/sbbs3/sbbs_ini.c
+++ b/src/sbbs3/sbbs_ini.c
@@ -64,6 +64,8 @@ static const char*	strHackAttemptSound="HackAttemptSound";
 static const char*	strLoginAttemptDelay="LoginAttemptDelay";
 static const char*	strLoginAttemptThrottle="LoginAttemptThrottle";
 static const char*	strLoginAttemptHackThreshold="LoginAttemptHackThreshold";
+static const char*	strLoginAttemptTempBanThreshold="LoginAttemptTempBanThreshold";
+static const char*	strLoginAttemptTempBanDuration="LoginAttemptTempBanDuration";
 static const char*	strLoginAttemptFilterThreshold="LoginAttemptFilterThreshold";
 static const char*	strJavaScriptMaxBytes		="JavaScriptMaxBytes";
 static const char*	strJavaScriptContextStack	="JavaScriptContextStack";
@@ -76,10 +78,6 @@ static const char*	strSemFileCheckFrequency	="SemFileCheckFrequency";
 #define DEFAULT_LOG_LEVEL				LOG_DEBUG
 #define DEFAULT_BIND_RETRY_COUNT		2
 #define DEFAULT_BIND_RETRY_DELAY		15
-#define DEFAULT_LOGIN_ATTEMPT_DELAY		5000	/* milliseconds */
-#define DEFAULT_LOGIN_ATTEMPT_THROTTLE	1000	/* milliseconds */
-#define DEFAULT_LOGIN_ATTEMPT_HACKLOG	10		/* write to hack.log after this many consecutive unique attempts */
-#define DEFAULT_LOGIN_ATTEMPT_FILTER	0		/* filter client IP address after this many consecutive unique attempts */
 
 void sbbs_get_ini_fname(char* ini_file, char* ctrl_dir, char* pHostName)
 {
@@ -190,6 +188,29 @@ BOOL sbbs_set_js_settings(
 	return(!failure);
 }
 
+static struct login_attempt_settings get_login_attempt_settings(str_list_t list, const char* section, global_startup_t* global)
+{
+	struct login_attempt_settings settings;
+
+	settings.delay				=iniGetInteger(list,section,strLoginAttemptDelay			,global == NULL ? 5000 : global->login_attempt.delay);
+	settings.throttle			=iniGetInteger(list,section,strLoginAttemptThrottle			,global == NULL ? 1000 : global->login_attempt.throttle);
+	settings.hack_threshold		=iniGetInteger(list,section,strLoginAttemptHackThreshold	,global == NULL ? 10 : global->login_attempt.hack_threshold);
+	settings.tempban_threshold	=iniGetInteger(list,section,strLoginAttemptTempBanThreshold	,global == NULL ? 20 : global->login_attempt.tempban_threshold);
+	settings.tempban_duration	=iniGetInteger(list,section,strLoginAttemptTempBanDuration	,global == NULL ? (10*60) : global->login_attempt.tempban_duration);
+	settings.filter_threshold	=iniGetInteger(list,section,strLoginAttemptFilterThreshold	,global == NULL ? 0 : global->login_attempt.filter_threshold);
+	return settings;
+}
+
+static void set_login_attempt_settings(str_list_t* lp, const char* section, struct login_attempt_settings settings, ini_style_t style)
+{
+	iniSetInteger(lp,section,strLoginAttemptDelay,settings.delay,&style);
+	iniSetInteger(lp,section,strLoginAttemptThrottle,settings.throttle,&style);
+	iniSetInteger(lp,section,strLoginAttemptHackThreshold,settings.hack_threshold,&style);
+	iniSetInteger(lp,section,strLoginAttemptTempBanThreshold,settings.tempban_threshold,&style);
+	iniSetInteger(lp,section,strLoginAttemptTempBanDuration,settings.tempban_duration,&style);
+	iniSetInteger(lp,section,strLoginAttemptFilterThreshold,settings.filter_threshold,&style);
+}
+
 static void get_ini_globals(str_list_t list, global_startup_t* global)
 {
 	const char* section = "Global";
@@ -221,10 +242,7 @@ static void get_ini_globals(str_list_t list, global_startup_t* global)
 	global->log_level=iniGetLogLevel(list,section,strLogLevel,DEFAULT_LOG_LEVEL);
 	global->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,DEFAULT_BIND_RETRY_COUNT);
 	global->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,DEFAULT_BIND_RETRY_DELAY);
-	global->login_attempt_delay=iniGetInteger(list,section,strLoginAttemptDelay,DEFAULT_LOGIN_ATTEMPT_DELAY);
-	global->login_attempt_throttle=iniGetInteger(list,section,strLoginAttemptThrottle,DEFAULT_LOGIN_ATTEMPT_THROTTLE);
-	global->login_attempt_hack_threshold=iniGetInteger(list,section,strLoginAttemptHackThreshold,DEFAULT_LOGIN_ATTEMPT_HACKLOG);
-	global->login_attempt_filter_threshold=iniGetInteger(list,section,strLoginAttemptFilterThreshold,DEFAULT_LOGIN_ATTEMPT_FILTER);
+	global->login_attempt = get_login_attempt_settings(list, section, NULL);
 
 	/* Setup default values here */
 	global->js.max_bytes		= JAVASCRIPT_MAX_BYTES;
@@ -375,10 +393,8 @@ void sbbs_read_ini(
 
 		bbs->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,global->bind_retry_count);
 		bbs->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,global->bind_retry_delay);
-		bbs->login_attempt_delay=iniGetInteger(list,section,strLoginAttemptDelay,global->login_attempt_delay);
-		bbs->login_attempt_throttle=iniGetInteger(list,section,strLoginAttemptThrottle,global->login_attempt_throttle);
-		bbs->login_attempt_hack_threshold=iniGetInteger(list,section,strLoginAttemptHackThreshold,global->login_attempt_hack_threshold);
-		bbs->login_attempt_filter_threshold=iniGetInteger(list,section,strLoginAttemptFilterThreshold,global->login_attempt_filter_threshold);
+
+		bbs->login_attempt = get_login_attempt_settings(list, section, global);
 	}
 
 	/***********************************************************************/
@@ -453,10 +469,7 @@ void sbbs_read_ini(
 
 		ftp->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,global->bind_retry_count);
 		ftp->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,global->bind_retry_delay);
-		ftp->login_attempt_delay=iniGetInteger(list,section,strLoginAttemptDelay,global->login_attempt_delay);
-		ftp->login_attempt_throttle=iniGetInteger(list,section,strLoginAttemptThrottle,global->login_attempt_throttle);
-		ftp->login_attempt_hack_threshold=iniGetInteger(list,section,strLoginAttemptHackThreshold,global->login_attempt_hack_threshold);
-		ftp->login_attempt_filter_threshold=iniGetInteger(list,section,strLoginAttemptFilterThreshold,global->login_attempt_filter_threshold);
+		ftp->login_attempt = get_login_attempt_settings(list, section, global);
 	}
 
 	/***********************************************************************/
@@ -551,10 +564,7 @@ void sbbs_read_ini(
 
 		mail->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,global->bind_retry_count);
 		mail->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,global->bind_retry_delay);
-		mail->login_attempt_delay=iniGetInteger(list,section,strLoginAttemptDelay,global->login_attempt_delay);
-		mail->login_attempt_throttle=iniGetInteger(list,section,strLoginAttemptThrottle,global->login_attempt_throttle);
-		mail->login_attempt_hack_threshold=iniGetInteger(list,section,strLoginAttemptHackThreshold,global->login_attempt_hack_threshold);
-		mail->login_attempt_filter_threshold=iniGetInteger(list,section,strLoginAttemptFilterThreshold,global->login_attempt_filter_threshold);
+		mail->login_attempt = get_login_attempt_settings(list, section, global);
 	}
 
 	/***********************************************************************/
@@ -598,10 +608,7 @@ void sbbs_read_ini(
 
 		services->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,global->bind_retry_count);
 		services->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,global->bind_retry_delay);
-		services->login_attempt_delay=iniGetInteger(list,section,strLoginAttemptDelay,global->login_attempt_delay);
-		services->login_attempt_throttle=iniGetInteger(list,section,strLoginAttemptThrottle,global->login_attempt_throttle);
-		services->login_attempt_hack_threshold=iniGetInteger(list,section,strLoginAttemptHackThreshold,global->login_attempt_hack_threshold);
-		services->login_attempt_filter_threshold=iniGetInteger(list,section,strLoginAttemptFilterThreshold,global->login_attempt_filter_threshold);
+		services->login_attempt = get_login_attempt_settings(list, section, global);
 	}
 
 	/***********************************************************************/
@@ -695,10 +702,7 @@ void sbbs_read_ini(
 
 		web->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,global->bind_retry_count);
 		web->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,global->bind_retry_delay);
-		web->login_attempt_delay=iniGetInteger(list,section,strLoginAttemptDelay,global->login_attempt_delay);
-		web->login_attempt_throttle=iniGetInteger(list,section,strLoginAttemptThrottle,global->login_attempt_throttle);
-		web->login_attempt_hack_threshold=iniGetInteger(list,section,strLoginAttemptHackThreshold,global->login_attempt_hack_threshold);
-		web->login_attempt_filter_threshold=iniGetInteger(list,section,strLoginAttemptFilterThreshold,global->login_attempt_filter_threshold);
+		web->login_attempt = get_login_attempt_settings(list, section, global);
 	}
 
 	free(global_interfaces);
@@ -758,10 +762,7 @@ BOOL sbbs_write_ini(
 		iniSetLogLevel(lp,section,strLogLevel,global->log_level,&style);
 		iniSetInteger(lp,section,strBindRetryCount,global->bind_retry_count,&style);
 		iniSetInteger(lp,section,strBindRetryDelay,global->bind_retry_delay,&style);
-		iniSetInteger(lp,section,strLoginAttemptDelay,global->login_attempt_delay,&style);
-		iniSetInteger(lp,section,strLoginAttemptThrottle,global->login_attempt_throttle,&style);
-		iniSetInteger(lp,section,strLoginAttemptHackThreshold,global->login_attempt_hack_threshold,&style);
-		iniSetInteger(lp,section,strLoginAttemptFilterThreshold,global->login_attempt_filter_threshold,&style);
+		set_login_attempt_settings(lp, section, global->login_attempt, style);
 
 		/* JavaScript operating parameters */
 		if(!sbbs_set_js_settings(lp,section,&global->js,NULL,&style))
diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index 0477f66683a43889daaddd38be6730dd1810b3fc..5cb5e10e70de879999a8627c801443daa3ba1e27 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -331,15 +331,15 @@ static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* ho
 
 	SAFEPRINTF(reason,"%s LOGIN", prot);
 	count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
-	if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold)
+	if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
 		hacklog(&scfg, reason, user, passwd, host, addr);
-	if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) {
+	if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold) {
 		inet_addrtop(addr, addr_ip, sizeof(addr_ip));
 		filter_ip(&scfg, prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
 			,host, addr_ip, user, /* fname: */NULL);
 	}
 
-	mswait(startup->login_attempt_delay);
+	mswait(startup->login_attempt.delay);
 }
 
 static JSBool
@@ -1085,11 +1085,11 @@ static void js_service_thread(void* arg)
 
 	update_clients();
 
-	if(startup->login_attempt_throttle
+	if(startup->login_attempt.throttle
 		&& (login_attempts=loginAttempts(startup->login_attempt_list, &service_client.addr)) > 1) {
 		lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%u login attempts)"
 			,socket, service->protocol, client.addr, login_attempts);
-		mswait(login_attempts*startup->login_attempt_throttle);
+		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
 	/* RUN SCRIPT */
@@ -1427,11 +1427,11 @@ static void native_service_thread(void* arg)
 	/* Initialize client display */
 	client_on(socket,&client,FALSE /* update */);
 
-	if(startup->login_attempt_throttle
+	if(startup->login_attempt.throttle
 		&& (login_attempts=loginAttempts(startup->login_attempt_list, &service_client.addr)) > 1) {
 		lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%u login attempts)"
 			,socket, service->protocol, client.addr, login_attempts);
-		mswait(login_attempts*startup->login_attempt_throttle);
+		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
 	/* RUN SCRIPT */
@@ -2104,21 +2104,22 @@ void DLLCALL services_thread(void* arg)
 						continue;
 					}
 
-	#ifdef _WIN32
-					if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)
-						&& !(service[i].options&BBS_OPT_MUTE))
-						PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
-	#endif
-
-					if(trashcan(&scfg,host_ip,"ip")) {
+					ulong banned = loginBanned(&scfg, startup->login_attempt_list, &client_addr,  startup->login_attempt);
+					if((banned && lprintf(LOG_NOTICE, "%04d %s is TEMPORARILY BANNED (%lu more seconds)", socket, host_ip, banned))
+						|| (trashcan(&scfg,host_ip,"ip") && lprintf(LOG_NOTICE,"%04d !%s CLIENT BLOCKED in ip.can: %s"
+							,client_socket, service[i].protocol, host_ip))) {
 						FREE_AND_NULL(udp_buf);
-						lprintf(LOG_NOTICE,"%04d !%s CLIENT BLOCKED in ip.can: %s"
-							,client_socket, service[i].protocol, host_ip);
 						mswait(3000);
 						close_socket(client_socket);
 						continue;
 					}
 
+	#ifdef _WIN32
+					if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)
+						&& !(service[i].options&BBS_OPT_MUTE))
+						PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
+	#endif
+
 					if((client=malloc(sizeof(service_client_t)))==NULL) {
 						FREE_AND_NULL(udp_buf);
 						lprintf(LOG_CRIT,"%04d !%s ERROR allocating %u bytes of memory for service_client"
diff --git a/src/sbbs3/services.h b/src/sbbs3/services.h
index 642073f62c311fd8c1fea72d69150bc3d30ee73d..662d9e92a226d29ca62447c84e4152951658b21f 100644
--- a/src/sbbs3/services.h
+++ b/src/sbbs3/services.h
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 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				*
@@ -83,10 +83,7 @@ typedef struct {
 	js_startup_t js;
 
 	/* Login Attempt parameters */
-	ulong	login_attempt_delay;
-	ulong	login_attempt_throttle;
-	ulong	login_attempt_hack_threshold;
-	ulong	login_attempt_filter_threshold;
+	struct login_attempt_settings login_attempt;
 	link_list_t* login_attempt_list;
 
 } services_startup_t;
diff --git a/src/sbbs3/startup.h b/src/sbbs3/startup.h
index 5be96476f241f07f17a397baa3d4fdabab59205c..f6b839225e4fe9fbccd77bc47f2478afa4207722 100644
--- a/src/sbbs3/startup.h
+++ b/src/sbbs3/startup.h
@@ -58,6 +58,16 @@ typedef struct {
 	char	load_path[INI_MAX_VALUE_LEN];	/* additional (comma-separated) directories to search for load()ed scripts */
 } js_startup_t;
 
+/* Login Attempt parameters */
+struct login_attempt_settings {
+	ulong	delay;				/* in milliseconds */
+	ulong	throttle;			/* in milliseconds */
+	ulong	hack_threshold;
+	ulong	tempban_threshold;
+	ulong	tempban_duration;	/* in seconds */
+	ulong	filter_threshold;
+};
+
 typedef struct {
 
 	char	ctrl_dir[INI_MAX_VALUE_LEN];
@@ -71,10 +81,7 @@ typedef struct {
 	js_startup_t js;
 	uint	bind_retry_count;		/* Number of times to retry bind() calls */
 	uint	bind_retry_delay;		/* Time to wait between each bind() retry */
-	ulong	login_attempt_delay;
-	ulong	login_attempt_throttle;
-	ulong	login_attempt_hack_threshold;
-	ulong	login_attempt_filter_threshold;
+	struct login_attempt_settings login_attempt;
 
 } global_startup_t;
 
@@ -137,11 +144,7 @@ typedef struct {
 	/* JavaScript operating parameters */
 	js_startup_t js;
 
-	/* Login Attempt parameters */
-	ulong	login_attempt_delay;
-	ulong	login_attempt_throttle;
-	ulong	login_attempt_hack_threshold;
-	ulong	login_attempt_filter_threshold;
+	struct login_attempt_settings login_attempt;
 	link_list_t* login_attempt_list;
 
 } bbs_startup_t;
diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c
index 04ad99055abbc77da71938a7fab899f8af0b1a82..eee753febcc91bc5d453072bd67f0e80f973978b 100644
--- a/src/sbbs3/userdat.c
+++ b/src/sbbs3/userdat.c
@@ -2888,7 +2888,7 @@ ulong DLLCALL loginFailure(link_list_t* list, const union xp_sockaddr* addr, con
 	SAFECOPY(attempt->user, user);
 	SAFECOPY(attempt->pass, pass);
 	attempt->count++;
-	count = attempt->count-attempt->dupes;
+	count = attempt->count - attempt->dupes;
 	if(node==NULL)
 		listPushNodeData(list, attempt, sizeof(login_attempt_t));
 	listUnlock(list);
@@ -2896,6 +2896,26 @@ ulong DLLCALL loginFailure(link_list_t* list, const union xp_sockaddr* addr, con
 	return count;
 }
 
+ulong DLLCALL loginBanned(scfg_t* cfg, link_list_t* list, const union xp_sockaddr* addr, struct login_attempt_settings settings)
+{
+	list_node_t*		node;
+	login_attempt_t*	attempt;
+	BOOL				result = FALSE;
+	long				diff;
+	time32_t			now = time32(NULL);
+
+	listLock(list);
+	node = login_attempted(list, addr);
+	listUnlock(list);
+	if(node == NULL)
+		return 0;
+	attempt = node->data;
+	if(((settings.tempban_threshold && (attempt->count - attempt->dupes) >= settings.tempban_threshold)
+		|| trashcan(cfg, attempt->user, "name")) && now < (attempt->time + settings.tempban_duration))
+		return settings.tempban_duration - (now - attempt->time);
+	return 0;
+}
+
 /****************************************************************************/
 /* Message-new-scan pointer/configuration functions							*/
 /****************************************************************************/
diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h
index 0f8496aad49697565bec8e949b176cc07cd792a3..5eecdc225274b122f49691f814dd9324ce9cdd72 100644
--- a/src/sbbs3/userdat.h
+++ b/src/sbbs3/userdat.h
@@ -145,9 +145,9 @@ DLLEXPORT BOOL	DLLCALL check_name(scfg_t*, const char* name);
 
 /* Login attempt/hack tracking */
 typedef struct {
-	union xp_sockaddr addr;	/* host with consecutive failed login attmepts */
+	union xp_sockaddr addr;	/* host with consecutive failed login attempts */
 	ulong		count;	/* number of consecutive failed login attempts */
-	ulong		dupes;	/* number of consecutive dupliate login attempts (same name and password) */
+	ulong		dupes;	/* number of consecutive duplicate login attempts (same name and password) */
 	time32_t	time;	/* time of last attempt */
 	char		prot[32];	/* protocol used in last attempt */
 	char		user[128];
@@ -160,6 +160,7 @@ DLLEXPORT long				DLLCALL	loginAttemptListClear(link_list_t*);
 DLLEXPORT long				DLLCALL loginAttempts(link_list_t*, const union xp_sockaddr*);
 DLLEXPORT void				DLLCALL	loginSuccess(link_list_t*, const union xp_sockaddr*);
 DLLEXPORT ulong				DLLCALL loginFailure(link_list_t*, const union xp_sockaddr*, const char* prot, const char* user, const char* pass);
+DLLEXPORT ulong				DLLCALL loginBanned(scfg_t*, link_list_t*, const union xp_sockaddr*, struct login_attempt_settings);
 
 #ifdef __cplusplus
 }
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index 4017ed89798d82564969ba26c81258c087523047..a24970fdc9563d16397e890a8414fbb0fb33a7a7 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -1780,18 +1780,18 @@ static void badlogin(SOCKET sock, const char* prot, const char* user, const char
 
 	SAFEPRINTF(reason,"%s LOGIN", prot);
 	count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
-	if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold) {
+	if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold) {
 		hacklog(&scfg, reason, user, passwd, host, addr);
 #ifdef _WIN32
 		if(startup->hack_sound[0] && !(startup->options&BBS_OPT_MUTE)) 
 			PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
 #endif
 	}
-	if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold)
+	if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold)
 		filter_ip(&scfg, prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
 			,host, inet_addrtop(addr, addrstr, sizeof(addrstr)), user, /* fname: */NULL);
 	if(count>1)
-		mswait(startup->login_attempt_delay);
+		mswait(startup->login_attempt.delay);
 }
 
 static BOOL check_ars(http_session_t * session)
@@ -6246,9 +6246,11 @@ void http_session_thread(void* arg)
 		}
 	}
 
+	ulong banned = loginBanned(&scfg, startup->login_attempt_list, &session.addr,  startup->login_attempt);
+
 	/* host_ip wasn't defined in http_session_thread */
-	if(trashcan(&scfg,session.host_ip,"ip")) {
-		lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip);
+	if((banned && lprintf(LOG_NOTICE, "%04d %s is TEMPORARILY BANNED (%lu more seconds)", socket, session.host_ip, banned))
+		|| (trashcan(&scfg,session.host_ip,"ip") && lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip))) {
 		close_session_socket(&session);
 		sem_wait(&session.output_thread_terminated);
 		sem_destroy(&session.output_thread_terminated);
@@ -6270,11 +6272,11 @@ void http_session_thread(void* arg)
 	session.client.size=sizeof(session.client);
 	client_on(session.socket, &session.client, /* update existing client record? */FALSE);
 
-	if(startup->login_attempt_throttle
+	if(startup->login_attempt.throttle
 		&& (login_attempts=loginAttempts(startup->login_attempt_list, &session.addr)) > 1) {
 		lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%u login attempts)"
 			,socket, session.client.protocol, session.host_ip, login_attempts);
-		mswait(login_attempts*startup->login_attempt_throttle);
+		mswait(login_attempts*startup->login_attempt.throttle);
 	}
 
 	session.last_user_num=-1;
diff --git a/src/sbbs3/websrvr.h b/src/sbbs3/websrvr.h
index 8a4bb3ee7ff6af5f874ee4fcb1a987ad15cbc8bf..14b65ed6a822438731deefb4c0ebd42716397beb 100644
--- a/src/sbbs3/websrvr.h
+++ b/src/sbbs3/websrvr.h
@@ -106,10 +106,7 @@ typedef struct {
 	js_startup_t js;
 
 	/* Login Attempt parameters */
-	uint32_t	login_attempt_delay;
-	uint32_t	login_attempt_throttle;
-	uint32_t	login_attempt_hack_threshold;
-	uint32_t	login_attempt_filter_threshold;
+	struct login_attempt_settings login_attempt;
 	link_list_t* login_attempt_list;
 
 } web_startup_t;