diff --git a/exec/gnats_cvs.js b/exec/gnats_cvs.js
deleted file mode 100755
index dcf35021dca6b4f4053d7de7e8b8d40fb98cfa87..0000000000000000000000000000000000000000
--- a/exec/gnats_cvs.js
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/sbbs/exec/jsexec -x -c /sbbs/ctrl
-load("gnatslib.js");
-
-var logmsg='----- CVS Commit Message -----\n';
-var name=argv[0];
-
-var gnats_user="guest";
-var password=undefined;
-if(argc>1)
-        gnats_user=argv[1];
-if(argc>2)
-        password=argv[2];
-var gnats = new GNATS("bugs.synchro.net",gnats_user,password);
-
-if(!gnats.connect())
-        handle_error();
-
-while((line=readln()) != undefined)
-	logmsg += line+"\n";
-logmsg+='--- End CVS Commit Message ---\n\n';
-
-auditprs=new Object;
-stateprs=new Object;
-myRe=/PR:\s?([0-9]+)/g;
-while((pr=myRe.exec(logmsg))!=undefined) {
-	auditprs[pr[1]]=pr[1];
-}
-
-myRe=/PR:\s?([0-9]+)-\>([a-z]*)/g;
-while((pr=myRe.exec(logmsg))!=undefined) {
-	stateprs[pr[1]]=pr[2];
-}
-
-for(pr in stateprs) {
-	var oldstate = gnats.get_field(pr,"State");
-	oldstate = oldstate.replace(/[\r\n]/g,'');
-	if(stateprs[pr] != oldstate) {
-		log("Changeing state of PR: "+pr+" to "+stateprs[pr]);
-		if(!gnats.replace(pr, "State", stateprs[pr], logmsg))
-			handle_error();
-		else
-			delete auditprs[pr];
-	}
-}
-
-for(pr in auditprs) {
-	var oldname = gnats.get_field(pr,"Responsible");
-	oldname = oldname.replace(/[\r\n]/g,'');
-	if(name != oldname) {
-		log("Changeing responsible to "+name+" for PR: "+pr);
-		if(!gnats.replace(pr, "Responsible", name, logmsg))
-			handle_error();
-	}
-	else {
-		log("Sending followup to PR: "+pr);
-		if(!gnats.send_followup(pr, name, name, logmsg))
-			handle_error();
-	}
-}
-
-function handle_error()
-{
-        log(gnats.error);
-        gnats.close();
-        exit(1);
-}
diff --git a/exec/load/cnfdefs.js b/exec/load/cnfdefs.js
index 9ee249e23f6a2837b7bf5063d09a08fbd854ee6d..543bb75ceb7e1ed64b6466e243dd4195f7a66c01 100644
--- a/exec/load/cnfdefs.js
+++ b/exec/load/cnfdefs.js
@@ -47,13 +47,13 @@ struct.xedit_t={
 	settings:	{bytes:UINT32_T,		type:"int"},	// was misc
 	ars:		{bytes:LEN_ARSTR+1,		type:"str"},	// was arstr
 	type:		{bytes:UCHAR,			type:"int"},
-	__PADDING__:15
+	__PADDING__:{bytes:15}
 };
 struct.xtrnsec_t={
 	name:		{bytes:41,				type:"str"},
 	code:		{bytes:LEN_CODE+1,		type:"str"},
 	ars:		{bytes:LEN_ARSTR+1,		type:"str"},	// was arstr
-	__PADDING__:16
+	__PADDING__:{bytes:16}
 };
 struct.xtrn_t={
 	sec:			{bytes:UINT16_T,		type:"int"},
@@ -70,7 +70,7 @@ struct.xtrn_t={
 	startup_dir:	{bytes:LEN_DIR+1,		type:"str"},	// was path
 	textra:			{bytes:UCHAR,			type:"int"},
 	max_time:		{bytes:UCHAR,			type:"int"},	// was maxtime
-	__PADDING__:14
+	__PADDING__:{bytes:14}
 };
 struct.event_t={
 	code:			{bytes:LEN_CODE+1,		type:"str"},
@@ -83,7 +83,7 @@ struct.event_t={
 	freq:			{bytes:UINT16_T,		type:"int"},
 	mdays:			{bytes:UINT32_T,		type:"int"},
 	months:			{bytes:UINT16_T,		type:"int"},
-	__PADDING__:8
+	__PADDING__:{bytes:8}
 };
 struct.natvpgm_t={
 	name:		{bytes:13,				type:"str"},
@@ -92,7 +92,7 @@ struct.natvpgm_t={
 struct.hotkey_t={
 	key:		{bytes:UCHAR,			type:"int"},
 	cmd:		{bytes:LEN_CMD+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:{bytes:16}
 };
 
 /* message group/sub record structures */
@@ -130,7 +130,7 @@ struct.sub_t={
 	moderated_ars:	{bytes:LEN_ARSTR+1,		type:"str"},	// was mod_arstr
 	// *mod_ar;
 	qwk_conf:		{bytes:UINT16_T,		type:"int"},	// was qwkconf
-	__PADDING__:53
+	__PADDING__:{bytes:53}
 };
 struct.grp_t={
 	description:	{bytes:LEN_GLNAME+1,	type:"str"},	// was lname
@@ -138,7 +138,7 @@ struct.grp_t={
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},	// was arstr
 	code_prefix:	{bytes:LEN_CODE+1,		type:"str"},
 	// uchar		*ar;
-	__PADDING__:87
+	__PADDING__:{bytes:87}
 };
 struct.qhubsub_t={
 	conf:		{bytes:UINT16_T,		type:"int"},
@@ -159,7 +159,7 @@ struct.qhub_t={
 	// 			*conf;					/* Conference number of ea. */
 	// ulong	*sub;					/* Number of local sub-board for ea. */
 	// time32_t	last;					/* Last network attempt */
-	__PADDING__:64
+	__PADDING__:{bytes:64}
 };
 struct.phub_t={
 	days:		{bytes:1,				type:"str"},
@@ -169,7 +169,7 @@ struct.phub_t={
 	node:		{bytes:UINT16_T,		type:"str"},
 	freq:		{bytes:UINT16_T,		type:"str"},
 // time32_t	last;						/* Last network attempt */
-	__PADDING__:64
+	__PADDING__:{bytes:64}
 };
 
 struct.node_dir_t={
@@ -186,7 +186,7 @@ struct.validation_set_t={
 	cdt:		{bytes:UINT32_T,			type:"int"},
 	exempt:		{bytes:UINT32_T,			type:"int"},
 	rest:		{bytes:UINT32_T,			type:"int"},
-	__PADDING__:16
+	__PADDING__:{bytes:16}
 };
 
 struct.sec_level_t={
@@ -199,7 +199,7 @@ struct.sec_level_t={
 	emailperday:{bytes:UINT16_T,			type:"int"},
 	misc:		{bytes:UINT32_T,			type:"int"},
 	expireto:	{bytes:UCHAR,				type:"int"},
-	__PADDING__:11
+	__PADDING__:{bytes:11}
 };
 
 struct.cmd_shell_t={
@@ -207,7 +207,7 @@ struct.cmd_shell_t={
 	code:		{bytes:LEN_CODE+1,			type:"str"},
 	ars:		{bytes:LEN_ARSTR+1,			type:"str"},
 	misc:		{bytes:UINT32_T,			type:"int"},
-	__PADDING__:16
+	__PADDING__:{bytes:16}
 };
 
 /* NOTE: top-level data structures only below this point 
@@ -238,7 +238,7 @@ struct.main={
 	sys_deldays:		{bytes:UINT16_T,			type:"int"},
 	sys_exp_warn:		{bytes:UCHAR,				type:"int"},
 	sys_autodel:		{bytes:UINT16_T,			type:"int"},
-	__PADDING__:1,
+	__PADDING__:		{bytes:1},
 	sys_chat_ars:		{bytes:LEN_ARSTR+1,			type:"str"},	// sys_chat_ar
 	cdt_min_value:		{bytes:UINT16_T,			type:"int"},
 	max_minutes:		{bytes:UINT32_T,			type:"int"},
@@ -264,7 +264,8 @@ struct.main={
 	new_install:				{bytes:UCHAR,		type:"int"},
 	newuser_msgscan_init:		{bytes:UINT16_T,	type:"int"},	// new_msgcan_init
 	guest_msgscan_init:			{bytes:UINT16_T,	type:"int"},
-	__PADDING1__:10,
+	min_pwlen:					{bytes:UCHAR,		type:"int"},
+	__PADDING1__:				{bytes:9},
 	expired_level:				{bytes:UCHAR,		type:"int"},
 	expired_flags1:				{bytes:UINT32_T,	type:"int"},
 	expired_flags2:				{bytes:UINT32_T,	type:"int"},
@@ -286,7 +287,15 @@ struct.main={
 	scanposts_mod:		{bytes:LEN_CMD+1,			type:"str"},
 	scansubs_mod:		{bytes:LEN_CMD+1,			type:"str"},
 	listmsgs_mod:		{bytes:LEN_CMD+1,			type:"str"},
-	__PADDING2__:569,
+	textsec_mod:		{bytes:LEN_MODNAME+1,		type:"str"},
+	automsg_mod:		{bytes:LEN_MODNAME+1,		type:"str"},
+	xtrnsec_mod:		{bytes:LEN_MODNAME+1,		type:"str"},
+	__PADDING2__:		{bytes:34},
+	nodelist_mod:		{bytes:LEN_CMD+1,			type:"str"},
+	whosonline_mod:		{bytes:LEN_CMD+1,			type:"str"},
+	privatemsg_mod:		{bytes:LEN_CMD+1,			type:"str"},
+	logonlist_mod:		{bytes:LEN_CMD+1,			type:"str"},
+	__PADDING3__:		{bytes:252},
 	user_backup_level:	{bytes:UINT16_T,			type:"int"},
 	mail_backup_level:	{bytes:UINT16_T,			type:"int"},
 	validation_set:		{bytes:struct.validation_set_t, type:"lst", length: 10},
@@ -314,9 +323,9 @@ struct.msg={
 	preqwk_ars:		{bytes:LEN_ARSTR+1, 	type:"str"},	// preqwk_ar
 	smb_retry_time:	{bytes:UCHAR, 			type:"int"},
 	max_qwkmsgage:	{bytes:UINT16_T, 		type:"int"},
-	__PADDING1__:466,
+	__PADDING1__:	{bytes:466},
 	settings:		{bytes:UINT32_T, 		type:"int"},	// was msg_misc
-	__PADDING2__:510,
+	__PADDING2__:	{bytes:510},
 	grp:			{bytes:struct.grp_t, 	type:"lst"},
 	sub:			{bytes:struct.sub_t, 	type:"lst"},
 	fido_addr_list:	{bytes:struct.faddr_t, 	type:"lst"},
@@ -329,15 +338,15 @@ struct.msg={
 	fido_netmail_misc:	{bytes:UINT16_T, 	type:"int"},
 	fido_netmail_cost:	{bytes:UINT32_T, 	type:"int"},
 	fido_default_addr:	{bytes:struct.faddr_t, 	type:"obj"},
-	__PADDING3__:56,
+	__PADDING3__:	{bytes:56},
 	qwknet_default_tagline:{bytes:128, 		type:"str"},
 	qwknet_hub:		{bytes:struct.qhub_t, 	type:"lst"},
-	__PADDING4__:64,
-	__PADDING5__:11,
+	__PADDING4__:	{bytes:64},
+	__PADDING5__:	{bytes:11},
 	postlink_num:	{bytes:UINT32_T, 		type:"int"},
 	postlink_hub:	{bytes:struct.phub_t, 	type:"lst"},
 	postlink_name:	{bytes:13, 				type:"str"},
-	__PADDING6__:64,
+	__PADDING6__:	{bytes:64},
 	sys_inetaddr:	{bytes:128, 			type:"str"},
 	inetmail_sem:	{bytes:LEN_DIR+1, 		type:"str"},
 	inetmail_misc:	{bytes:UINT32_T, 		type:"str"},
@@ -369,7 +378,7 @@ struct.dir_t={
 	max_age:		{bytes:UINT16_T,		type:"int"},
 	upload_pct:		{bytes:UINT16_T,		type:"int"},
 	download_pct:	{bytes:UINT16_T,		type:"int"},
-	__PADDING__:49
+	__PADDING__:	{bytes:49}
 };
 struct.lib_t={
 	description:	{bytes:LEN_GLNAME+1,	type:"str"},
@@ -379,7 +388,7 @@ struct.lib_t={
 	code_prefix:	{bytes:LEN_CODE+1,		type:"str"},
 	sort:			{bytes:1,				type:"int"},
 	settings:		{bytes:UINT32_T,		type:"int"},
-	__PADDING__:34
+	__PADDING__:	{bytes:34}
 };
 
 /* Extractable File Types */
@@ -387,7 +396,7 @@ struct.fextr_t={
 	extension:		{bytes:4,				type:"str"},
 	cmd:			{bytes:LEN_CMD+1,		type:"str"},
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 /* Compressable File Types */
@@ -395,7 +404,7 @@ struct.fcomp_t={
 	extension:		{bytes:4,				type:"str"},
 	cmd:			{bytes:LEN_CMD+1,		type:"str"},
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 /* Viewable File Types */
@@ -403,7 +412,7 @@ struct.fview_t={
 	extension:		{bytes:4,				type:"str"},
 	cmd:			{bytes:LEN_CMD+1,		type:"str"},
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 /* Testable File Types */
@@ -412,7 +421,7 @@ struct.ftest_t={
 	cmd:			{bytes:LEN_CMD+1,		type:"str"},
 	working:		{bytes:41,				type:"str"},
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 /* Download Events */
@@ -421,7 +430,7 @@ struct.dlevent_t={
 	cmd:			{bytes:LEN_CMD+1,		type:"str"},
 	working:		{bytes:41,				type:"str"},
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 /* File Transfer Protocol (drivers) */
@@ -436,19 +445,19 @@ struct.prot_t={
 	bicmd:			{bytes:LEN_CMD+1,		type:"str"},
 	settings:		{bytes:UINT32_T,		type:"int"},	// misc
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 struct.altpath_t={
 	path:			{bytes:LEN_DIR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 struct.txtsec_t={
 	name:			{bytes:41,				type:"str"},
 	code:			{bytes:LEN_CODE+1,		type:"str"},
 	ars:			{bytes:LEN_ARSTR+1,		type:"str"},
-	__PADDING__:16
+	__PADDING__:	{bytes:16}
 };
 
 /* file transfer configuration (file.cnf) */
@@ -457,14 +466,14 @@ struct.file={
 	max_batup:		{bytes:UINT16_T,		type:"int"},
 	max_batdn:		{bytes:UINT16_T,		type:"int"},
 	max_userxfer:	{bytes:UINT16_T,		type:"int"},
-	__PADDING1__:4,
+	__PADDING1__:	{bytes:4},
 	cdt_up_pct:		{bytes:UINT16_T,		type:"int"},
 	cdt_dn_pct:		{bytes:UINT16_T,		type:"int"},
-	__PADDING2__:68,
+	__PADDING2__:	{bytes:68},
 	leech_pct:		{bytes:UINT16_T,		type:"int"},
 	leech_sec:		{bytes:UINT16_T,		type:"int"},
 	settings:		{bytes:UINT32_T,		type:"int"},
-	__PADDING3__:60,
+	__PADDING3__:	{bytes:60},
 	fextr:			{bytes:struct.fextr_t,	type:"lst"},
 	fcomp:			{bytes:struct.fcomp_t,	type:"lst"},
 	fview:			{bytes:struct.fview_t,	type:"lst"},
diff --git a/exec/load/cnflib.js b/exec/load/cnflib.js
index 7078eaaf4b8e80f9c79cce799c770eb3fcf1a84e..ff797c7b66632a9060c5f7f8047a542a13c914c7 100644
--- a/exec/load/cnflib.js
+++ b/exec/load/cnflib.js
@@ -42,13 +42,12 @@ var CNF = new (function() {
 
 	/* get record padding */
 	function getPadding(file,bytes) {
-		file.position += bytes;
-		return false;
+		return file.read(bytes);
 	}
 
 	/* set record padding */
-	function setPadding(file,bytes) {
-		setStr(file,bytes,"");
+	function setPadding(file,struct) {
+		file.write(struct.data,struct.bytes);
 	}
 
 	/* read a set of records from *.cnf */
@@ -70,7 +69,7 @@ var CNF = new (function() {
 			if(file.eof)
 				break;
 			if(p.match(/__PADDING\d*__/))
-				getPadding(file,struct[p]);
+				struct[p].data = getPadding(file,struct[p].bytes);
 			else {
 				switch(struct[p].type) {
 				case "int":
diff --git a/exec/nntpservice.js b/exec/nntpservice.js
index 048f8dd412047418d91a41630f1e2a5ac9566706..e682d2f4f01b5f285f4fe12f882f1dfda2ace6d5 100644
--- a/exec/nntpservice.js
+++ b/exec/nntpservice.js
@@ -53,6 +53,8 @@ var impose_limit = true;
 var sysop_login = false;
 var add_tag = true;
 var ex_ascii = true;
+var force_newsgroups = false;
+var filter_newsgroups = false;
 
 // Parse arguments
 for(i=0;i<argc;i++) {
@@ -70,6 +72,10 @@ for(i=0;i<argc;i++) {
 		add_tag = false;
 	else if(argv[i].toLowerCase()=="-ascii")
 		ex_ascii = false;
+	else if(argv[i].toLowerCase()=="-force")
+		force_newsgroups = true;
+	else if(argv[i].toLowerCase()=="-filter")
+		filter_newsgroups = true;
 	else if(argv[i].toLowerCase()=="-auto") {
 		no_anonymous = true;
 		auto_login = true;
@@ -142,6 +148,21 @@ function count_msgs(msgbase)
 	return { total: count, first: first, last: last };
 }
 
+function get_newsgroup_list()
+{
+	// list of newsgroup names the logged-in user has access to
+	var newsgroup_list = [];
+	if(include_mail) {
+		newsgroup_list.push("mail");
+	}
+	for(var g in msg_area.grp_list) {
+		for(var s in msg_area.grp_list[g].sub_list) {
+			newsgroup_list.push(msg_area.grp_list[g].sub_list[s].newsgroup);
+		}
+	}
+	return newsgroup_list;
+}
+
 function bogus_cmd(cmdline)
 {
 	log(LOG_DEBUG, "Received bogus command: '" + cmdline + "'");
@@ -758,18 +779,20 @@ while(client.socket.is_connected && !quit) {
 				if(hdr.path==undefined)
 					hdr.path="not-for-mail";
 
-				if(hdr.newsgroups==undefined)
+				if(hdr.newsgroups==undefined || force_newsgroups)
 					hdr.newsgroups = selected.newsgroup;
-				else {	/* Tracker1's mod for adding the correct newsgroup name		*/
-					var ng_found = false;					/* Requires sbbs v3.13	*/
-					var ng_list = hdr.newsgroups.split(',');
-					for(n in ng_list)
-						if(ng_list[n].toLowerCase() == selected.newsgroup.toLowerCase()) {
-							ng_found = true;
-							break;
-						}
-					if(!ng_found)
-						hdr.newsgroups = selected.newsgroup + ',' + hdr.newsgroups;
+				else {
+					var nghdr_list = hdr.newsgroups.split(',');
+					var newsgroup_list = get_newsgroup_list();
+					var filtered_list = [];
+					for(var n in nghdr_list) {
+						if(filter_newsgroups && newsgroup_list.indexOf(nghdr_list[n]) < 0)
+							continue;
+						filtered_list.push(nghdr_list[n]);
+					}
+					if(filtered_list.indexOf(selected.newsgroup) < 0)
+						filtered_list.push(selected.newsgroup);
+					hdr.newsgroups = filtered_list.join(',');
 				}
 
 				if(hdr.from_org==undefined && !hdr.from_net_type)
diff --git a/src/sbbs3/putmsg.cpp b/src/sbbs3/putmsg.cpp
index a18df8a2eb4fa57737fee43f29c75543d553b4fa..887dc36a51547b4414fa8a473bdc48c7792cda64 100644
--- a/src/sbbs3/putmsg.cpp
+++ b/src/sbbs3/putmsg.cpp
@@ -160,13 +160,19 @@ char sbbs_t::putmsgfrag(const char* buf, long* mode, long org_cols, JSObject* ob
 				break;
 		}
 		if((*mode) & P_MARKUP) {
-			if(((mark == 0) && (str[l] == '*' || str[l] == '/' || str[l] == '_' || str[l] == '#')) || str[l] == mark) {
-				char* next= NULL;
-				if(mark == 0)
+			const char* marks = "*/_#";
+			if(((mark == 0) && strchr(marks, str[l]) != NULL) || str[l] == mark) {
+				char* next = NULL;
+				char following = '\0';
+				if(mark == 0) {
 					next = strchr(str + l + 1, str[l]);
+					if(next != NULL)
+						following = *(next + 1);
+				}
 				char *blank = strstr(str + l + 1, "\n\r");
-				if(((l < 1 || IS_WHITESPACE(str[l - 1]) || IS_PUNCTUATION(str[l - 1]))
-						&& IS_ALPHANUMERIC(str[l + 1]) && mark == 0 && next != NULL	&& (*(next + 1) == '\0' || IS_WHITESPACE(*(next + 1)) || IS_PUNCTUATION(*(next+1)))
+				if(((l < 1 || ((IS_WHITESPACE(str[l - 1]) || IS_PUNCTUATION(str[l - 1])) && strchr(marks, str[l - 1]) == NULL))
+						&& IS_ALPHANUMERIC(str[l + 1]) && mark == 0 && next != NULL
+						&& (following == '\0' || IS_WHITESPACE(following) || (IS_PUNCTUATION(following) && strchr(marks, following) == NULL))
 						&& (blank == NULL || next < blank))
 					|| str[l] == mark) {
 					if(mark == 0)