diff --git a/ctrl/modopts.ini b/ctrl/modopts.ini
index 17bd452ad79bac47aefe922e3bb5c30eefdf28ef..b59357a6afdcf5eda3c5ec5388bc5256beb40ffa 100644
--- a/ctrl/modopts.ini
+++ b/ctrl/modopts.ini
@@ -57,6 +57,11 @@
         backup_level = 10
 
 [xtrn_sec]
+; Set to true to disable execution of prextrn.js and postxtrn.js for external programs
+; when they are running as a logon event
+  disable_pre_on_logon_event = false
+  disable_post_on_logon_event = false
+  
 ; Enable multi-column display (when more than 10 external programs in a section)
 	multicolumn = true
 ; Sort the list of external programs alphabetically by name
diff --git a/exec/load/cnfdefs.js b/exec/load/cnfdefs.js
index 543bb75ceb7e1ed64b6466e243dd4195f7a66c01..b76931383655ce625fccbe6521d22cc8425c3935 100644
--- a/exec/load/cnfdefs.js
+++ b/exec/load/cnfdefs.js
@@ -295,7 +295,10 @@ struct.main={
 	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},
+	prextrn_mod:        {bytes:LEN_MODNAME+1,       type:"str"},
+	postxtrn_mod:       {bytes:LEN_MODNAME+1,       type:"str"},
+	
+	__PADDING3__:		{bytes:234},
 	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},
diff --git a/exec/postxtrn.js b/exec/postxtrn.js
new file mode 100644
index 0000000000000000000000000000000000000000..1634164392cf9e9c989ec4b37b6c62912bc5ff1f
--- /dev/null
+++ b/exec/postxtrn.js
@@ -0,0 +1,41 @@
+// postxtrn.js
+
+// External Program Post Module
+// These actions execute after an external program is launched via bbs.exec_xtrn()
+
+"use strict";
+
+load("sbbsdefs.js");
+
+/* text.dat entries */
+load("text.js");
+
+var options, program;
+
+if((options=load({}, "modopts.js","xtrn_sec")) == null)
+	options = {};	// default values
+
+if(options.clear_screen === undefined)
+	options.clear_screen = true;
+
+function exec_xtrn_post(program)
+{
+	if ((options.disable_post_on_logon_event) && (bbs.node_action == NODE_LOGN)) {
+		exit(1);
+	}
+
+	console.attributes = 0;
+	console.attributes = LIGHTGRAY;
+
+	load('fonts.js', 'default');
+
+	if (options.eval_after_exec) {
+		eval(options.eval_after_exec);
+	}
+}
+
+
+/* main: */
+{
+	exec_xtrn_post(xtrn_area.prog[argv[0].toLowerCase()] );
+}
diff --git a/exec/prextrn.js b/exec/prextrn.js
new file mode 100644
index 0000000000000000000000000000000000000000..07d16322bd427279e90cf79bb563e1b278c1e0c1
--- /dev/null
+++ b/exec/prextrn.js
@@ -0,0 +1,57 @@
+// prextrn.js
+
+// External Program Pre Module
+// These actions execute before an external program is launched via bbs.exec_xtrn()
+
+"use strict";
+
+load("sbbsdefs.js");
+
+/* text.dat entries */
+load("text.js");
+
+var options, program;
+
+if((options=load({}, "modopts.js","xtrn_sec")) == null)
+	options = {};	// default values
+
+
+function exec_xtrn_pre(program)
+{
+	if ((options.disable_pre_on_logon_event) && (bbs.node_action == NODE_LOGN)) {
+		exit(1);
+	}
+	
+	if (options.restricted_user_msg === undefined) {
+		options.restricted_user_msg = bbs.text(R_ExternalPrograms);
+	}
+
+	if (user.security.restrictions&UFLAG_X) {
+		write(options.restricted_user_msg);
+		exit(1);
+	}
+
+	if (bbs.menu_exists("xtrn/" + program.code)) {
+		bbs.menu("xtrn/" + program.code);
+		console.pause();
+		console.line_counter=0;
+	}
+
+	console.attributes = LIGHTGRAY;
+
+	if (options.clear_screen_on_exec) {
+		console.clear();
+	}
+
+	if (options.eval_before_exec) {
+		eval(options.eval_before_exec);
+	}
+
+	load('fonts.js', 'xtrn:' + program.code);
+}
+
+
+/* main: */
+{
+	exec_xtrn_pre(xtrn_area.prog[argv[0].toLowerCase()]);
+}
diff --git a/exec/xtrn_sec.js b/exec/xtrn_sec.js
index f312d5e904e21e13c78e8f041bb96e021feff914..b55d3f10cd7262667522f027a47986bd439ebd15 100644
--- a/exec/xtrn_sec.js
+++ b/exec/xtrn_sec.js
@@ -80,22 +80,6 @@ function sort_by_name(a, b)
 	return 0;
 }
 
-function exec_xtrn(prog)
-{
-	console.attributes = LIGHTGRAY;
-	if(options.clear_screen_on_exec)
-		console.clear();
-	if(options.eval_before_exec)
-		eval(options.eval_before_exec);
-	load('fonts.js', 'xtrn:' + prog.code);
-	bbs.exec_xtrn(prog.code);
-	console.attributes = 0;
-	console.attributes = LIGHTGRAY;
-	load('fonts.js', 'default');
-	if(options.eval_after_exec)
-		eval(options.eval_after_exec);
-}
-
 function external_program_menu(xsec)
 {
     var i,j;
@@ -118,7 +102,7 @@ function external_program_menu(xsec)
 
 		// If there's only one program available to the user in the section, just run it (or try to)
 		if(options.autoexec && prog_list.length == 1) {
-			exec_xtrn(prog_list[0]);
+			bbs.exec_xtrn(prog_list[0].code);
 			break;
 		}
 		
@@ -190,11 +174,8 @@ function external_program_menu(xsec)
 		if((i=console.getnum(prog_list.length))<1)
 			break;
 		i--;
-		if(bbs.menu_exists("xtrn/" + prog_list[i].code)) {
-			bbs.menu("xtrn/" + prog_list[i].code);
-			console.line_counter=0;
-		}
-		exec_xtrn(prog_list[i]);
+
+		bbs.exec_xtrn(prog_list[i].code);
 	}
 }
 
diff --git a/src/sbbs3/scfg/scfgsys.c b/src/sbbs3/scfg/scfgsys.c
index d6b0e86fc103da75db9adf434a565487250694e8..7268b9e9ef9656f4e301de9cdd1445d43bb696f7 100644
--- a/src/sbbs3/scfg/scfgsys.c
+++ b/src/sbbs3/scfg/scfgsys.c
@@ -1669,6 +1669,8 @@ void sys_cfg(void)
 					sprintf(opt[i++],"%-16.16s%s","Auto Message",cfg.automsg_mod);
 					sprintf(opt[i++],"%-16.16s%s","Text Section",cfg.textsec_mod);
 					sprintf(opt[i++],"%-16.16s%s","Xtrn Section",cfg.xtrnsec_mod);
+					sprintf(opt[i++],"%-16.16s%s","Pre Xtrn Prog",cfg.prextrn_mod);
+					sprintf(opt[i++],"%-16.16s%s","Post Xtrn Prog",cfg.postxtrn_mod);
 					sprintf(opt[i++],"%-16.16s%s","Read Mail",cfg.readmail_mod);
 					sprintf(opt[i++],"%-16.16s%s","Scan Msgs",cfg.scanposts_mod);
 					sprintf(opt[i++],"%-16.16s%s","Scan Subs",cfg.scansubs_mod);
@@ -1696,6 +1698,8 @@ void sys_cfg(void)
 						"`Auto Message` Executed when a user chooses to edit the auto-message\n"
 						"`Text Section` Executed to handle general text file (viewing) section\n"
 						"`Xtrn Section` Executed to handle external programs (doors) section\n"
+						"`Xtrn Prog Pre` Executed before external programs (doors) run\n"
+						"`Xtrn Prog Post` Executed after external programs (doors) run\n"
 						"\n"
 						"Full module command-lines may be used for the operations listed below:\n"
 						"\n"
@@ -1759,34 +1763,42 @@ void sys_cfg(void)
 								,cfg.xtrnsec_mod,sizeof(cfg.xtrnsec_mod)-1,K_EDIT);
 							break;
 						case 10:
+							uifc.input(WIN_MID|WIN_SAV,0,0,"Pre External Program Module"
+								,cfg.prextrn_mod,sizeof(cfg.prextrn_mod)-1,K_EDIT);
+							break;
+						case 11:
+							uifc.input(WIN_MID|WIN_SAV,0,0,"Post External Program Module"
+								,cfg.postxtrn_mod,sizeof(cfg.postxtrn_mod)-1,K_EDIT);
+							break;														
+						case 12:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"Read Mail Command"
 								,cfg.readmail_mod,sizeof(cfg.readmail_mod)-1,K_EDIT);
 							break;
-						case 11:
+						case 13:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"Scan Msgs Command"
 								,cfg.scanposts_mod,sizeof(cfg.scanposts_mod)-1,K_EDIT);
 							break;
-						case 12:
+						case 14:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"Scan Subs Command"
 								,cfg.scansubs_mod,sizeof(cfg.scansubs_mod)-1,K_EDIT);
 							break;
-						case 13:
+						case 15:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"List Msgs Command"
 								,cfg.listmsgs_mod,sizeof(cfg.listmsgs_mod)-1,K_EDIT);
 							break;
-						case 14:
+						case 16:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"List Logons Command"
 								,cfg.logonlist_mod,sizeof(cfg.logonlist_mod)-1,K_EDIT);
 							break;
-						case 15:
+						case 17:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"List Nodes Command"
 								,cfg.nodelist_mod,sizeof(cfg.nodelist_mod)-1,K_EDIT);
 							break;
-						case 16:
+						case 18:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"Who's Online Command"
 								,cfg.whosonline_mod,sizeof(cfg.whosonline_mod)-1,K_EDIT);
 							break;
-						case 17:
+						case 19:
 							uifc.input(WIN_MID|WIN_SAV,0,0,"Private Message Command"
 								,cfg.privatemsg_mod,sizeof(cfg.privatemsg_mod)-1,K_EDIT);
 							break;
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index 84d28f8a83df032c7764104ac140d567115905b3..04da305be693d6c78d89953a8825a9c5ebddbf48 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.h
@@ -610,6 +610,8 @@ typedef struct
 	char			whosonline_mod[LEN_CMD+1];
 	char			privatemsg_mod[LEN_CMD+1];
 	char			logonlist_mod[LEN_CMD+1];
+    char			prextrn_mod[LEN_MODNAME+1];			/* External Program pre-execution module */
+    char			postxtrn_mod[LEN_MODNAME+1];		/* External Program post-execution module */
 	char			scfg_cmd[LEN_CMD+1];	/* SCFG command line - unused! */
 	uchar			smb_retry_time; 		/* Seconds to retry on SMBs */
 	uint16_t		sec_warn;				/* Seconds before inactivity warning */
diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index 2b8e2210a647c550ede4a9fc0c8615727378c127..813c27ff3dbe9982f3c4d9281a1c22daf34d2f9b 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -291,7 +291,15 @@ BOOL read_main_cfg(scfg_t* cfg, char* error)
 	get_str(cfg->logonlist_mod,instream);
 	if(cfg->logonlist_mod[0] == '\xff')
 		SAFECOPY(cfg->logonlist_mod, "logonlist");
-	for(i=0;i<126;i++)					/* unused - initialized to 0xff */
+
+	get_str(cfg->prextrn_mod,instream);
+	if(cfg->prextrn_mod[0] == '\xff') 
+	    SAFECOPY(cfg->postxtrn_mod, "prextrn");
+	get_str(cfg->postxtrn_mod,instream);
+	if(cfg->postxtrn_mod[0] == '\xff') 
+	    SAFECOPY(cfg->postxtrn_mod, "postxtrn");		
+		
+	for(i=0;i<117;i++)					/* unused - initialized to 0xff */
 		get_int(n,instream);
 
 	get_int(cfg->user_backup_level,instream);
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index 85877f9dd8d60f4dadfe2032e22cd44cb7b200c6..9f40f4a4ca8324c0bf5f4154dd3d9f00e7d9231c 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -261,8 +261,12 @@ BOOL DLLCALL write_main_cfg(scfg_t* cfg, int backup_level)
 	put_str(cfg->whosonline_mod, stream);
 	put_str(cfg->privatemsg_mod, stream);
 	put_str(cfg->logonlist_mod, stream);
+	
+    put_str(cfg->prextrn_mod,stream);
+    put_str(cfg->postxtrn_mod,stream);
+    
 	n=0xffff;
-	for(i=0;i<126;i++)
+	for(i=0;i<117;i++)
 		put_int(n,stream);
 
 	put_int(cfg->user_backup_level,stream);
diff --git a/src/sbbs3/xtrn_sec.cpp b/src/sbbs3/xtrn_sec.cpp
index d6654bcb95c6e47f5c2135571e4ab4ed279f189e..2e98ca898c2cba96828590835a63ec4d36c447dc 100644
--- a/src/sbbs3/xtrn_sec.cpp
+++ b/src/sbbs3/xtrn_sec.cpp
@@ -1487,6 +1487,13 @@ bool sbbs_t::exec_xtrn(uint xtrnnum)
 		subtract_cdt(&cfg,&useron,cfg.xtrn[xtrnnum]->cost); 
 	}
 
+    if(cfg.prextrn_mod[0] != '\0') {
+        SAFEPRINTF2(str, "%s %s", cfg.prextrn_mod,cfg.xtrn[xtrnnum]->code);
+        if (exec_bin(str, &main_csi) != 0) {
+            return(false);
+        }
+    }
+
 	if(!(cfg.xtrn[xtrnnum]->misc&MULTIUSER)) {
 		for(i=1;i<=cfg.sys_nodes;i++) {
 			getnodedat(i,&node,0);
@@ -1652,6 +1659,11 @@ bool sbbs_t::exec_xtrn(uint xtrnnum)
 	else
 		lncntr = 0;
 
+    if(cfg.postxtrn_mod[0] != '\0') {
+        SAFEPRINTF2(str, "%s %s", cfg.postxtrn_mod,cfg.xtrn[xtrnnum]->code);
+        exec_bin(str, &main_csi);
+    }
+    
 	return(true);
 }