From d57c383063e173d902bac303bbcf32bd5597b068 Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Mon, 27 Apr 2015 10:46:45 +0000
Subject: [PATCH] Enhancement at the request of Nightfox: Optionally, external
 modules may be executed in place of "built-in" operations for: - reading mail
 (email or netmail) - scanning posts (searching/scanning/reading a single
 sub-board) - scanning sub-boards (all, one, or a group)

Each of these may be configured (in SCFG->System->Loadable Modules)
with command-line options for a total length of up to 63 characters.

Note to module programmers: each module here will be passed additional
command-line arguments to indicate how or why the user operation was invoked.

For the Reading Mail module, the 'which' value (in decimal) and the user-number
is passed (in decimal). Usually the user-number will be the current user logged
in, but not always (e.g. if the user is a sysop and reading other user's mail).
The 'which' values (defined in load/sbbsdefs.js) of MAIL_YOUR, MAIL_SENT,
and MAIL_ALL must be handled (MAIL_ANY won't be used).

For the Scan Posts module, the sub-board number and scan mode (both in decimal)
and the 'find' string (text being searched for) are passed as arguments
(in that order). The scan mode values (defined in sbbsdefs.js) are the same as
those supported by bbs.scan_posts(). All of the values should be supported
and they can be combined (e.g. SCAN_CONST|SCAN_NEW
and SCAN_NEW|SCAN_TOYOU). The 'find' string is only used if the SCAN_FIND mode
bit is set. It is the responsibilty of the module (if configured) to enforce
sub-board read access restrictions.

For the Scan Subs module, the first argument is 1 if "all subs" are being
scanned, 0 otherwise (normally the user is prompted for the breadth of the
scan in this case, but that's up to the module author), and the scan 'mode' is
passed in decimal, in that order. If the SCAN_FIND mode flag is passed in, this
module should prompt the user for the text string to search for. If this module
is not configured, the Scan Posts module will be executed for each sub-board
scanned.

Recursion protection was added, so it is actually possible for the Read Mail
module, for example, to call bbs.read_mail() and it'll work, but why would you?

If any of these user operations are initiated during logon, at the request of
Baja or JavaScript module, or whatever, and if the module is actually
configured (and not already executing to handle the operation), the module
will be executed in-place-of the built-in functionality. Feedback welcome,
---
 src/sbbs3/main.cpp       |  5 ++++-
 src/sbbs3/readmail.cpp   | 12 +++++++++++-
 src/sbbs3/readmsgs.cpp   | 11 ++++++++++-
 src/sbbs3/sbbs.h         |  5 ++++-
 src/sbbs3/scansubs.cpp   | 22 +++++++++++++++++++++-
 src/sbbs3/scfg/scfgsys.c | 26 +++++++++++++++++++++++---
 src/sbbs3/scfgdefs.h     |  5 ++++-
 src/sbbs3/scfglib1.c     |  7 +++++--
 src/sbbs3/scfgsave.c     |  7 +++++--
 9 files changed, 87 insertions(+), 13 deletions(-)

diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index 5224db01e6..095de263cf 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -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 2015 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				*
@@ -2983,6 +2983,9 @@ sbbs_t::sbbs_t(ushort node_num, SOCKADDR_IN addr, const char* name, SOCKET sd,
 	nodesync_inside = false;
 	errormsg_inside = false;
 	gettimeleft_inside = false;
+	readmail_inside = false;
+	scanposts_inside = false;
+	scansubs_inside = false;
 	timeleft = 60*10;	/* just incase this is being used for calling gettimeleft() */
 	uselect_total = 0;
 	lbuflen = 0;
diff --git a/src/sbbs3/readmail.cpp b/src/sbbs3/readmail.cpp
index f84db9106a..8f92753cc0 100644
--- a/src/sbbs3/readmail.cpp
+++ b/src/sbbs3/readmail.cpp
@@ -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 2013 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2015 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				*
@@ -57,6 +57,16 @@ void sbbs_t::readmail(uint usernumber, int which)
 	mail_t	*mail;
 	smbmsg_t msg;
 
+	if(cfg.readmail_mod[0] && !readmail_inside) {
+		char cmdline[256];
+
+		readmail_inside = true;
+		safe_snprintf(cmdline, sizeof(cmdline), "%s %d %u", cfg.readmail_mod, which, usernumber);
+		exec_bin(cmdline, &main_csi);
+		readmail_inside = false;
+		return;
+	}
+
 	if(which==MAIL_SENT && useron.rest&FLAG('K')) {
 		bputs(text[R_ReadSentMail]);
 		return;
diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp
index 3d99ad9901..d6910864aa 100644
--- a/src/sbbs3/readmsgs.cpp
+++ b/src/sbbs3/readmsgs.cpp
@@ -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 2013 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2015 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				*
@@ -372,6 +372,15 @@ int sbbs_t::scanposts(uint subnum, long mode, const char *find)
 	post_t	*post;
 	smbmsg_t	msg;
 
+	if(cfg.scanposts_mod[0] && !scanposts_inside) {
+		char cmdline[256];
+
+		scanposts_inside = true;
+		safe_snprintf(cmdline, sizeof(cmdline), "%s %u %u %s", cfg.scanposts_mod, subnum, mode, find);
+		i=exec_bin(cmdline, &main_csi);
+		scanposts_inside = false;
+		return i;
+	}
 	find_buf[0]=0;
 	cursubnum=subnum;	/* for ARS */
 	if(!chk_ar(cfg.sub[subnum]->read_ar,&useron,&client)) {
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index c2bbb974cf..557953a6a8 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.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 2015 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				*
@@ -650,6 +650,7 @@ public:
 
 	/* readmail.cpp */
 	void	readmail(uint usernumber, int sent);
+	bool	readmail_inside;
 
 	/* bulkmail.cpp */
 	bool	bulkmail(uchar *ar);
@@ -746,6 +747,7 @@ public:
 
 	/* readmsgs.cpp */
 	int		scanposts(uint subnum, long mode, const char* find);	/* Scan sub-board */
+	bool	scanposts_inside;
 	long	listsub(uint subnum, long mode, long start, const char* search);
 	long	listmsgs(uint subnum, long mode, post_t* post, long start, long posts);
 	long	searchposts(uint subnum, post_t* post, long start, long msgs, const char* find);
@@ -927,6 +929,7 @@ public:
 
 	/* scansubs.cpp */
 	void	scansubs(long mode);
+	bool	scansubs_inside;
 	void	scanallsubs(long mode);
 	void	new_scan_cfg(ulong misc);
 	void	new_scan_ptr_cfg(void);
diff --git a/src/sbbs3/scansubs.cpp b/src/sbbs3/scansubs.cpp
index 5aaa97ff98..f5748b59f9 100644
--- a/src/sbbs3/scansubs.cpp
+++ b/src/sbbs3/scansubs.cpp
@@ -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 2015 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				*
@@ -47,6 +47,16 @@ void sbbs_t::scansubs(long mode)
 	uint	i=0,found=0;
 	ulong	subs_scanned=0;
 
+	if(cfg.scansubs_mod[0] && !scansubs_inside) {
+		char cmdline[256];
+
+		scansubs_inside = true;
+		safe_snprintf(cmdline, sizeof(cmdline), "%s 0 %ld", cfg.scansubs_mod, mode);
+		exec_bin(cmdline, &main_csi);
+		scansubs_inside = false;
+		return;
+	}
+
 	mnemonics(text[SubGroupOrAll]);
 	ch=(char)getkeys("SGA\r",0);
 	if(sys_status&SS_ABORT || ch==CR)
@@ -132,6 +142,16 @@ void sbbs_t::scanallsubs(long mode)
 	uint	i,j,found=0;
 	ulong	subs_scanned=0;
 
+	if(cfg.scansubs_mod[0] && !scansubs_inside) {
+		char cmdline[256];
+
+		scansubs_inside = true;
+		safe_snprintf(cmdline, sizeof(cmdline), "%s 1 %ld", cfg.scansubs_mod, mode);
+		exec_bin(cmdline, &main_csi);
+		scansubs_inside = false;
+		return;
+	}
+
 	if(/* action==NODE_MAIN && */ mode&(SCAN_FIND|SCAN_TOYOU)) {
 		if(text[DisplaySubjectsOnlyQ][0])
 			i=yesno(text[DisplaySubjectsOnlyQ]);
diff --git a/src/sbbs3/scfg/scfgsys.c b/src/sbbs3/scfg/scfgsys.c
index b796c8c747..8904750f68 100644
--- a/src/sbbs3/scfg/scfgsys.c
+++ b/src/sbbs3/scfg/scfgsys.c
@@ -6,7 +6,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 2015 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				*
@@ -1383,6 +1383,9 @@ while(1) {
 				sprintf(opt[i++],"%-16.16s%s","Logout",cfg.logout_mod);
 				sprintf(opt[i++],"%-16.16s%s","New User",cfg.newuser_mod);
 				sprintf(opt[i++],"%-16.16s%s","Expired User",cfg.expire_mod);
+				sprintf(opt[i++],"%-16.16s%s","Read Mail",cfg.readmail_mod);
+				sprintf(opt[i++],"%-16.16s%s","Scan Posts",cfg.scanposts_mod);
+				sprintf(opt[i++],"%-16.16s%s","Scan Subs",cfg.scansubs_mod);
 				opt[i][0]=0;
 				uifc.helpbuf=
 					"`Loadable Modules:`\n"
@@ -1390,7 +1393,7 @@ while(1) {
 					"Baja modules (`.bin` files) or JavaScript modules (`.js` files) can be\n"
 					"automatically loaded and executed during certain Terminal Server\n"
 					"operations. The name (root filename) of the module can be specified for\n"
-					"each of the available operations listed here:\n"
+					"each of the available operations listed below:\n"
 					"\n"
 					"`Login`        Required module for interactive terminal logins (answer)\n"
 					"`Logon`        Executed during terminal logon procedure\n"
@@ -1400,6 +1403,12 @@ while(1) {
 					"`New User`     Executed at end of new terminal user creation process\n"
 					"`Expired User` Executed during daily event when user expires (offline)\n"
 					"\n"
+					"Full module command-lines may be used for the operations listed below:\n"
+					"\n"
+					"`Read Mail`    Executed when a user reads email/netmail\n"
+					"`Scan Posts`   Executed when a user reads or scans a message sub-board\n"
+					"`Scan Subs`    Executed when a user scans one or more sub-boards for msgs\n"
+					"\n"
 					"`Note:` JavaScript modules take precedence over Baja modules if both exist\n"
 					"in your `exec` or `mods` directories.\n"
 				;
@@ -1438,7 +1447,18 @@ while(1) {
 						uifc.input(WIN_MID|WIN_SAV,0,0,"Expired User Module"
 							,cfg.expire_mod,sizeof(cfg.expire_mod)-1,K_EDIT);
                         break;
-
+					case 7:
+						uifc.input(WIN_MID|WIN_SAV,0,0,"Read Mail Command"
+							,cfg.readmail_mod,sizeof(cfg.readmail_mod)-1,K_EDIT);
+                        break;
+					case 8:
+						uifc.input(WIN_MID|WIN_SAV,0,0,"Scan Posts Command"
+							,cfg.scanposts_mod,sizeof(cfg.scanposts_mod)-1,K_EDIT);
+                        break;
+					case 9:
+						uifc.input(WIN_MID|WIN_SAV,0,0,"Scan Subs Command"
+							,cfg.scansubs_mod,sizeof(cfg.scansubs_mod)-1,K_EDIT);
+                        break;
 				} 
 			}
 			break;
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index 4067fe9d65..9192b1b072 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.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 2009 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2015 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				*
@@ -576,6 +576,9 @@ typedef struct
 	char			logout_mod[LEN_MODNAME+1];			/* Logout module */
 	char			sync_mod[LEN_MODNAME+1];			/* Synchronization module */
 	char			expire_mod[LEN_MODNAME+1];			/* User expiration module */
+	char			readmail_mod[LEN_CMD+1];	/* Reading mail module */
+	char			scanposts_mod[LEN_CMD+1];	/* Scanning posts (in a single sub) module */
+	char			scansubs_mod[LEN_CMD+1];	/* Scanning sub-boards module */
 	char			scfg_cmd[LEN_CMD+1];	/* SCFG command line */
 	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 a22882814f..b38d48f3f3 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -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 2015 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				*
@@ -303,9 +303,12 @@ BOOL read_main_cfg(scfg_t* cfg, char* error)
 	get_str(cfg->mods_dir,instream);
 	get_str(cfg->logs_dir,instream);
 	if(!cfg->logs_dir[0]) SAFECOPY(cfg->logs_dir,cfg->data_dir);
+	get_str(cfg->readmail_mod, instream);
+	get_str(cfg->scanposts_mod, instream);
+	get_str(cfg->scansubs_mod, instream);
 
 	get_int(c,instream);
-	for(i=0;i<158;i++)					/* unused - initialized to NULL */
+	for(i=0;i<62;i++)					/* unused - initialized to NULL */
 		get_int(n,instream);
 	for(i=0;i<254;i++)					/* unused - initialized to 0xff */
 		get_int(n,instream);
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index ff9e9ddaf3..c9f3836a1a 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -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 2012 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2015 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				*
@@ -373,10 +373,13 @@ BOOL DLLCALL write_main_cfg(scfg_t* cfg, int backup_level)
 	put_int(cfg->ctrlkey_passthru,stream);
 	put_str(cfg->mods_dir,stream);
 	put_str(cfg->logs_dir,stream);
+	put_str(cfg->readmail_mod, stream);
+	put_str(cfg->scanposts_mod, stream);
+	put_str(cfg->scansubs_mod, stream);
 
 	put_int(c,stream);
 	n=0;
-	for(i=0;i<158;i++)
+	for(i=0;i<62;i++)
 		put_int(n,stream);
 	n=0xffff;
 	for(i=0;i<254;i++)
-- 
GitLab