From 6a4e7bbda73aecb92ff59aa3c6152490f5a53ec1 Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Tue, 30 Oct 2012 06:48:46 +0000
Subject: [PATCH] Implement gating support for Frank/netsurge: 1. Scan message
 areas based on those listed in the areas.bbs, not those listed in the
 msgs.cnf file (e.g. configured with SCFG->Message Areas). This allows the
 same message base to be scanned more than one for a different set of uplinks
 (i.e. gating). 2. Store the message base export pointer (in the
 data/subs/code.ini file) with the echotag as part of the key-name. This
 allows the different upstream networks to have different export pointers
 (though after a complete scan the export pointers should be the same for each
 msg base).

Increment the version number to 2.20.
---
 src/sbbs3/sbbsecho.c | 540 ++++++++++++++++++++++---------------------
 src/sbbs3/sbbsecho.h |   2 +-
 2 files changed, 282 insertions(+), 260 deletions(-)

diff --git a/src/sbbs3/sbbsecho.c b/src/sbbs3/sbbsecho.c
index 8bf4609382..7fe96964ba 100644
--- a/src/sbbs3/sbbsecho.c
+++ b/src/sbbs3/sbbsecho.c
@@ -3557,9 +3557,10 @@ int import_netmail(char *path,fmsghdr_t hdr, FILE *fidomsg)
 	return(0);
 }
 
-static uint32_t read_export_ptr(int subnum)
+static uint32_t read_export_ptr(int subnum, const char* tag)
 {
 	char		path[MAX_PATH+1];
+	char		key[INI_MAX_VALUE_LEN];
 	int			file;
 	FILE*		fp;
 	uint32_t	ptr=0;
@@ -3567,7 +3568,9 @@ static uint32_t read_export_ptr(int subnum)
 	/* New way (July-21-2012): */
 	safe_snprintf(path,sizeof(path),"%s%s.ini",scfg.sub[subnum]->data_dir,scfg.sub[subnum]->code);
 	if((fp=iniOpenFile(path, /* create: */FALSE)) != NULL) {
-		ptr=iniReadLongInt(fp, "SBBSecho", "export_ptr", 0);
+		safe_snprintf(key, sizeof(key), "%s.export_ptr", tag);
+		if((ptr=iniReadLongInt(fp, "SBBSecho", key, 0)) == 0)
+			ptr=iniReadLongInt(fp, "SBBSecho", "export_ptr", 0);	/* the previous .ini method (did not support gating) */
 		iniCloseFile(fp);
 	}
 	if(ptr)	return ptr;
@@ -3580,16 +3583,19 @@ static uint32_t read_export_ptr(int subnum)
 	return ptr;
 }
 
-static void write_export_ptr(int subnum, uint32_t ptr)
+static void write_export_ptr(int subnum, uint32_t ptr, const char* tag)
 {
 	char		path[MAX_PATH+1];
+	char		key[INI_MAX_VALUE_LEN];
 	FILE*		fp;
 	str_list_t	ini_file;
 
 	/* New way (July-21-2012): */
 	safe_snprintf(path,sizeof(path),"%s%s.ini",scfg.sub[subnum]->data_dir,scfg.sub[subnum]->code);
 	if((fp=iniOpenFile(path, /* create: */TRUE)) != NULL) {
+		safe_snprintf(key, sizeof(key), "%s.export_ptr", tag);
 		ini_file = iniReadFile(fp);
+		iniSetLongInt(&ini_file, "SBBSecho", key, ptr, /* style (default): */NULL);
 		iniSetLongInt(&ini_file, "SBBSecho", "export_ptr", ptr, /* style (default): */NULL);
 		iniWriteFile(fp, ini_file);
 		iniCloseFile(fp);
@@ -3612,7 +3618,8 @@ void export_echomail(char *sub_code,faddr_t addr)
 	char*	fmsgbuf=NULL;
 	ulong	fmsgbuflen;
 	int		tzone;
-	int		g,i,j,k=0;
+	int		area;
+	int		i,j,k=0;
 	ulong	f,l,m,exp,exported=0;
 	uint32_t ptr,msgs,lastmsg,posts;
 	float	export_time;
@@ -3636,265 +3643,279 @@ void export_echomail(char *sub_code,faddr_t addr)
 
 	lprintf(LOG_DEBUG,"\nScanning for Outbound EchoMail...");
 
-	for(g=0;g<scfg.total_grps;g++) {
-		for(i=0;i<scfg.total_subs;i++) {
-			if((scfg.sub[i]->misc&SUB_FIDO) && scfg.sub[i]->grp==g) {
-				for(j=0;j<cfg.areas;j++)	/* Skip areas with no uplinks */
-					if(cfg.area[j].sub==i)
-						break;
-				if(j==cfg.areas || (j<cfg.areas && !cfg.area[j].uplinks))
-					continue;
-				if(addr.zone) { 		/* Skip areas not meant for this address */
-					if(j<cfg.areas)
-						for(k=0;k<cfg.area[j].uplinks;k++)
-							if(!memcmp(&cfg.area[j].uplink[k],&addr,sizeof(faddr_t)))
-								break;
-					if(k==cfg.area[j].uplinks)
-						continue; }
-				if(sub_code[0] && stricmp(sub_code,scfg.sub[i]->code))
-					continue;
-				lprintf(LOG_DEBUG,"\nScanning %-15.15s %s"
-					,scfg.grp[scfg.sub[i]->grp]->sname,scfg.sub[i]->lname);
-				ptr=0;
-				if(!addr.zone && !(misc&IGNORE_MSGPTRS))
-					ptr=read_export_ptr(i);
-
-				msgs=getlastmsg(i,&lastmsg,0);
-				if(!msgs || (!addr.zone && !(misc&IGNORE_MSGPTRS) && ptr>=lastmsg)) {
-					lprintf(LOG_DEBUG,"No new messages.");
-					if(ptr>lastmsg && !addr.zone && !(misc&LEAVE_MSGPTRS)) {
-						lprintf(LOG_DEBUG,"Fixing new-scan pointer.");
-						write_export_ptr(i, lastmsg);
-					}
-					continue; 
-				}
+	for(area=0; area<cfg.areas; area++) {
+		const char* tag=cfg.area[area].name;
+		if(area==cfg.badecho)		/* Don't scan the bad-echo area */
+			continue;
+		if(!cfg.area[area].uplinks)
+			continue;
+		i=cfg.area[area].sub;
+		if(i<0 || i>=scfg.total_subs)	/* Don't scan pass-through areas */
+			continue;
+		if(addr.zone) { 		/* Skip areas not meant for this address */
+			for(k=0;k<cfg.area[area].uplinks;k++)
+				if(!memcmp(&cfg.area[area].uplink[k],&addr,sizeof(faddr_t)))
+					break;
+			if(k==cfg.area[area].uplinks)
+				continue; 
+		}
+		if(sub_code[0] && stricmp(sub_code,scfg.sub[i]->code))
+			continue;
+		lprintf(LOG_DEBUG,"\nScanning %-*.*s -> %s"
+			,LEN_EXTCODE,LEN_EXTCODE,scfg.sub[i]->code
+			,tag);
+		ptr=0;
+		if(!addr.zone && !(misc&IGNORE_MSGPTRS))
+			ptr=read_export_ptr(i, tag);
+
+		msgs=getlastmsg(i,&lastmsg,0);
+		if(!msgs || (!addr.zone && !(misc&IGNORE_MSGPTRS) && ptr>=lastmsg)) {
+			lprintf(LOG_DEBUG,"No new messages.");
+			if(ptr>lastmsg && !addr.zone && !(misc&LEAVE_MSGPTRS)) {
+				lprintf(LOG_DEBUG,"Fixing new-scan pointer.");
+				write_export_ptr(i, lastmsg, tag);
+			}
+			continue; 
+		}
 
-				sprintf(smb[cur_smb].file,"%s%s"
-					,scfg.sub[i]->data_dir,scfg.sub[i]->code);
-				smb[cur_smb].retry_time=scfg.smb_retry_time;
-				if((j=smb_open(&smb[cur_smb]))!=SMB_SUCCESS) {
-					lprintf(LOG_ERR,"ERROR %d line %d opening %s",j,__LINE__
-						,smb[cur_smb].file);
-					continue; }
+		sprintf(smb[cur_smb].file,"%s%s"
+			,scfg.sub[i]->data_dir,scfg.sub[i]->code);
+		smb[cur_smb].retry_time=scfg.smb_retry_time;
+		if((j=smb_open(&smb[cur_smb]))!=SMB_SUCCESS) {
+			lprintf(LOG_ERR,"ERROR %d line %d opening %s",j,__LINE__
+				,smb[cur_smb].file);
+			continue; 
+		}
+
+		post=NULL;
+		posts=loadmsgs(&post,ptr);
 
-				post=NULL;
-				posts=loadmsgs(&post,ptr);
+		if(!posts)	{ /* no new messages */
+			smb_close(&smb[cur_smb]);
+			FREE_AND_NULL(post);
+			continue; 
+		}
 
-				if(!posts)	{ /* no new messages */
-					smb_close(&smb[cur_smb]);
-					FREE_AND_NULL(post);
+		if(start_tick)
+			export_ticks+=msclock()-start_tick;
+		start_tick=msclock();
+
+		for(m=exp=0;m<posts;m++) {
+			printf("\r%8s %5lu of %-5"PRIu32"  "
+				,scfg.sub[i]->code,m+1,posts);
+			memset(&msg,0,sizeof(msg));
+			msg.idx=post[m];
+			if((k=smb_lockmsghdr(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
+				lprintf(LOG_ERR,"ERROR %d line %d locking %s msghdr"
+					,k,__LINE__,smb[cur_smb].file);
+				continue; 
+			}
+			k=smb_getmsghdr(&smb[cur_smb],&msg);
+			if(k || msg.hdr.number!=post[m].number) {
+				smb_unlockmsghdr(&smb[cur_smb],&msg);
+				smb_freemsgmem(&msg);
+
+				msg.hdr.number=post[m].number;
+				if((k=smb_getmsgidx(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
+					lprintf(LOG_ERR,"ERROR %d line %d reading %s index",k,__LINE__
+						,smb[cur_smb].file);
+					continue; 
+				}
+				if((k=smb_lockmsghdr(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
+					lprintf(LOG_ERR,"ERROR %d line %d locking %s msghdr",k,__LINE__
+						,smb[cur_smb].file);
 					continue; 
 				}
+				if((k=smb_getmsghdr(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
+					smb_unlockmsghdr(&smb[cur_smb],&msg);
+					lprintf(LOG_ERR,"ERROR %d line %d reading %s msghdr",k,__LINE__
+						,smb[cur_smb].file);
+					continue; 
+				} 
+			}
 
-				if(start_tick)
-					export_ticks+=msclock()-start_tick;
-				start_tick=msclock();
+			if((!addr.zone && !(misc&EXPORT_ALL)
+				&& (msg.from_net.type==NET_FIDO || msg.from_net.type==NET_FIDO_ASCII))
+				|| !strnicmp(msg.subj,"NE:",3)) {   /* no echo */
+				smb_unlockmsghdr(&smb[cur_smb],&msg);
+				smb_freemsgmem(&msg);
+				continue;   /* From a Fido node, ignore it */
+			}
 
-				for(m=exp=0;m<posts;m++) {
-					printf("\r%8s %5lu of %-5"PRIu32"  "
-						,scfg.sub[i]->code,m+1,posts);
-					memset(&msg,0,sizeof(msg));
-					msg.idx=post[m];
-					if((k=smb_lockmsghdr(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
-						lprintf(LOG_ERR,"ERROR %d line %d locking %s msghdr"
-							,k,__LINE__,smb[cur_smb].file);
-						continue; 
-					}
-					k=smb_getmsghdr(&smb[cur_smb],&msg);
-					if(k || msg.hdr.number!=post[m].number) {
-						smb_unlockmsghdr(&smb[cur_smb],&msg);
-						smb_freemsgmem(&msg);
-
-						msg.hdr.number=post[m].number;
-						if((k=smb_getmsgidx(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
-							lprintf(LOG_ERR,"ERROR %d line %d reading %s index",k,__LINE__
-								,smb[cur_smb].file);
-							continue; }
-						if((k=smb_lockmsghdr(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
-							lprintf(LOG_ERR,"ERROR %d line %d locking %s msghdr",k,__LINE__
-								,smb[cur_smb].file);
-							continue; }
-						if((k=smb_getmsghdr(&smb[cur_smb],&msg))!=SMB_SUCCESS) {
-							smb_unlockmsghdr(&smb[cur_smb],&msg);
-							lprintf(LOG_ERR,"ERROR %d line %d reading %s msghdr",k,__LINE__
-								,smb[cur_smb].file);
-							continue; } }
-
-					if((!addr.zone && !(misc&EXPORT_ALL)
-						&& (msg.from_net.type==NET_FIDO || msg.from_net.type==NET_FIDO_ASCII))
-						|| !strnicmp(msg.subj,"NE:",3)) {   /* no echo */
-						smb_unlockmsghdr(&smb[cur_smb],&msg);
-						smb_freemsgmem(&msg);
-						continue; }  /* From a Fido node, ignore it */
-
-					if(msg.from_net.type!=NET_NONE 
-						&& msg.from_net.type!=NET_FIDO
-						&& msg.from_net.type!=NET_FIDO_ASCII
-						&& !(scfg.sub[i]->misc&SUB_GATE)) {
-						smb_unlockmsghdr(&smb[cur_smb],&msg);
-						smb_freemsgmem(&msg);
-						continue; }
-
-					memset(&hdr,0,sizeof(fmsghdr_t));	 /* Zero the header */
-					hdr.origzone=scfg.sub[i]->faddr.zone;
-					hdr.orignet=scfg.sub[i]->faddr.net;
-					hdr.orignode=scfg.sub[i]->faddr.node;
-					hdr.origpoint=scfg.sub[i]->faddr.point;
-
-					hdr.attr=FIDO_LOCAL;
-					if(msg.hdr.attr&MSG_PRIVATE)
-						hdr.attr|=FIDO_PRIVATE;
-
-					SAFECOPY(hdr.from,msg.from);
-
-					tt=msg.hdr.when_written.time;
-					if((tm=localtime(&tt)) != NULL)
-						sprintf(hdr.time,"%02u %3.3s %02u  %02u:%02u:%02u"
-							,tm->tm_mday,mon[tm->tm_mon],TM_YEAR(tm->tm_year)
-							,tm->tm_hour,tm->tm_min,tm->tm_sec);
-
-					SAFECOPY(hdr.to,msg.to);
-
-					SAFECOPY(hdr.subj,msg.subj);
-
-					buf=smb_getmsgtxt(&smb[cur_smb],&msg,GETMSGTXT_ALL);
-					if(!buf) {
-						smb_unlockmsghdr(&smb[cur_smb],&msg);
-						smb_freemsgmem(&msg);
-						continue; 
-					}
-					fmsgbuflen=strlen((char *)buf)+4096; /* over alloc for kludge lines */
-					fmsgbuf=malloc(fmsgbuflen);
-					if(!fmsgbuf) {
-						lprintf(LOG_ERR,"ERROR line %d allocating %lu bytes for fmsgbuf"
-							,__LINE__,fmsgbuflen);
-						smb_unlockmsghdr(&smb[cur_smb],&msg);
-						smb_freemsgmem(&msg);
-						continue; 
-					}
-					fmsgbuflen-=1024;	/* give us a bit of a guard band here */
-
-					tear=0;
-					f=0;
-
-					tzone=smb_tzutc(msg.hdr.when_written.zone);
-					if(tzone<0) {
-						minus="-";
-						tzone=-tzone;
-					} else
-						minus="";
-					f+=sprintf(fmsgbuf+f,"\1TZUTC: %s%02d%02u\r"		/* TZUTC (FSP-1001) */
-						,minus,tzone/60,tzone%60);
-
-					if(msg.ftn_flags!=NULL)
-						f+=sprintf(fmsgbuf+f,"\1FLAGS %.256s\r", msg.ftn_flags);
-
-					f+=sprintf(fmsgbuf+f,"\1MSGID: %.256s\r"
-						,ftn_msgid(scfg.sub[i],&msg,msgid,sizeof(msgid)));
-
-					if(msg.ftn_reply!=NULL)			/* use original REPLYID */
-						f+=sprintf(fmsgbuf+f,"\1REPLY: %.256s\r", msg.ftn_reply);
-					else if(msg.hdr.thread_back) {	/* generate REPLYID (from original message's MSG-ID, if it had one) */
-						memset(&orig_msg,0,sizeof(orig_msg));
-						orig_msg.hdr.number=msg.hdr.thread_back;
-						if(smb_getmsgidx(&smb[cur_smb], &orig_msg))
-							f+=sprintf(fmsgbuf+f,"\1REPLY: <%s>\r",smb[cur_smb].last_error);
-						else {
-							smb_lockmsghdr(&smb[cur_smb],&orig_msg);
-							smb_getmsghdr(&smb[cur_smb],&orig_msg);
-							smb_unlockmsghdr(&smb[cur_smb],&orig_msg);
-							if(orig_msg.ftn_msgid != NULL && orig_msg.ftn_msgid[0])
-								f+=sprintf(fmsgbuf+f,"\1REPLY: %.256s\r",orig_msg.ftn_msgid);	
-						}
-					}
-					if(msg.ftn_pid!=NULL)	/* use original PID */
-						f+=sprintf(fmsgbuf+f,"\1PID: %.256s\r", msg.ftn_pid);
-					if(msg.ftn_tid!=NULL)	/* use original TID */
-						f+=sprintf(fmsgbuf+f,"\1TID: %.256s\r", msg.ftn_tid);
-					else					/* generate TID */
-						f+=sprintf(fmsgbuf+f,"\1TID: SBBSecho %s-%s r%s %s %s\r"
-							,SBBSECHO_VER,PLATFORM_DESC,revision,__DATE__,compiler);
-
-					/* Unknown kludge lines are added here */
-					for(l=0;l<msg.total_hfields && f<fmsgbuflen;l++)
-						if(msg.hfield[l].type == FIDOCTRL)
-							f+=sprintf(fmsgbuf+f,"\1%.512s\r",(char*)msg.hfield_dat[l]);
-
-					for(l=0,cr=1;buf[l] && f<fmsgbuflen;l++) {
-						if(buf[l]==CTRL_A) { /* Ctrl-A, so skip it and the next char */
-							char ch;
-							l++;
-							if(buf[l]==0 || toupper(buf[l])=='Z')	/* EOF */
-								break;
-							if((ch=ctrl_a_to_ascii_char(buf[l])) != 0)
-								fmsgbuf[f++]=ch;
-							continue; 
-						}
-					
-						/* Need to support converting sole-LFs to Hard-CR and soft-CR (0x8D) as well */
-						if((misc&STRIP_LF) && buf[l]=='\n')	/* Ignore line feeds */
-							continue;
-
-						if(cr) {
-							if(buf[l]=='-' && buf[l+1]=='-'
-								&& buf[l+2]=='-'
-								&& (buf[l+3]==' ' || buf[l+3]=='\r')) {
-								if(misc&CONVERT_TEAR)	/* Convert to === */
-									buf[l]=buf[l+1]=buf[l+2]='=';
-								else
-									tear=1; }
-							else if(!strncmp((char *)buf+l," * Origin: ",11))
-								buf[l+1]='#'; } /* Convert * Origin into # Origin */
+			if(msg.from_net.type!=NET_NONE 
+				&& msg.from_net.type!=NET_FIDO
+				&& msg.from_net.type!=NET_FIDO_ASCII
+				&& !(scfg.sub[i]->misc&SUB_GATE)) {
+				smb_unlockmsghdr(&smb[cur_smb],&msg);
+				smb_freemsgmem(&msg);
+				continue; 
+			}
 
-						if(buf[l]=='\r')
-							cr=1;
-						else
-							cr=0;
-						if((scfg.sub[i]->misc&SUB_ASCII) || (misc&ASCII_ONLY)) {
-							if(buf[l]<' ' && buf[l]>=0 && buf[l]!='\r'
-								&& buf[l]!='\n')			/* Ctrl ascii */
-								buf[l]='.';             /* converted to '.' */
-							if((uchar)buf[l]&0x80)		/* extended ASCII */
-								buf[l]=exascii_to_ascii_char(buf[l]);
-						}
+			memset(&hdr,0,sizeof(fmsghdr_t));	 /* Zero the header */
+			hdr.origzone=scfg.sub[i]->faddr.zone;
+			hdr.orignet=scfg.sub[i]->faddr.net;
+			hdr.orignode=scfg.sub[i]->faddr.node;
+			hdr.origpoint=scfg.sub[i]->faddr.point;
 
-						fmsgbuf[f++]=buf[l]; }
+			hdr.attr=FIDO_LOCAL;
+			if(msg.hdr.attr&MSG_PRIVATE)
+				hdr.attr|=FIDO_PRIVATE;
 
-					FREE_AND_NULL(buf);
-					fmsgbuf[f]=0;
+			SAFECOPY(hdr.from,msg.from);
 
-					if(!(scfg.sub[i]->misc&SUB_NOTAG)) {
-						if(!tear) {  /* No previous tear line */
-							sprintf(str,"--- SBBSecho %s-%s\r"
-								,SBBSECHO_VER,PLATFORM_DESC);
-							strcat((char *)fmsgbuf,str); }
+			tt=msg.hdr.when_written.time;
+			if((tm=localtime(&tt)) != NULL)
+				sprintf(hdr.time,"%02u %3.3s %02u  %02u:%02u:%02u"
+					,tm->tm_mday,mon[tm->tm_mon],TM_YEAR(tm->tm_year)
+					,tm->tm_hour,tm->tm_min,tm->tm_sec);
 
-						sprintf(str," * Origin: %s (%s)\r"
-							,scfg.sub[i]->origline[0] ? scfg.sub[i]->origline : scfg.origline
-							,smb_faddrtoa(&scfg.sub[i]->faddr,NULL));
-						strcat((char *)fmsgbuf,str); }
+			SAFECOPY(hdr.to,msg.to);
 
-					for(k=0;k<cfg.areas;k++)
-						if(cfg.area[k].sub==i) {
-							cfg.area[k].exported++;
-							pkt_to_pkt(fmsgbuf,cfg.area[k]
-								,(addr.zone) ? addr:pkt_faddr,hdr,msg_seen
-								,msg_path,(addr.zone) ? 2:0);
-							break; }
-					FREE_AND_NULL(fmsgbuf);
-					exported++;
-					exp++;
-					printf("Exp: %lu ",exp);
-					smb_unlockmsghdr(&smb[cur_smb],&msg);
-					smb_freemsgmem(&msg); }
+			SAFECOPY(hdr.subj,msg.subj);
+
+			buf=smb_getmsgtxt(&smb[cur_smb],&msg,GETMSGTXT_ALL);
+			if(!buf) {
+				smb_unlockmsghdr(&smb[cur_smb],&msg);
+				smb_freemsgmem(&msg);
+				continue; 
+			}
+			fmsgbuflen=strlen((char *)buf)+4096; /* over alloc for kludge lines */
+			fmsgbuf=malloc(fmsgbuflen);
+			if(!fmsgbuf) {
+				lprintf(LOG_ERR,"ERROR line %d allocating %lu bytes for fmsgbuf"
+					,__LINE__,fmsgbuflen);
+				smb_unlockmsghdr(&smb[cur_smb],&msg);
+				smb_freemsgmem(&msg);
+				continue; 
+			}
+			fmsgbuflen-=1024;	/* give us a bit of a guard band here */
+
+			tear=0;
+			f=0;
+
+			tzone=smb_tzutc(msg.hdr.when_written.zone);
+			if(tzone<0) {
+				minus="-";
+				tzone=-tzone;
+			} else
+				minus="";
+			f+=sprintf(fmsgbuf+f,"\1TZUTC: %s%02d%02u\r"		/* TZUTC (FSP-1001) */
+				,minus,tzone/60,tzone%60);
+
+			if(msg.ftn_flags!=NULL)
+				f+=sprintf(fmsgbuf+f,"\1FLAGS %.256s\r", msg.ftn_flags);
+
+			f+=sprintf(fmsgbuf+f,"\1MSGID: %.256s\r"
+				,ftn_msgid(scfg.sub[i],&msg,msgid,sizeof(msgid)));
+
+			if(msg.ftn_reply!=NULL)			/* use original REPLYID */
+				f+=sprintf(fmsgbuf+f,"\1REPLY: %.256s\r", msg.ftn_reply);
+			else if(msg.hdr.thread_back) {	/* generate REPLYID (from original message's MSG-ID, if it had one) */
+				memset(&orig_msg,0,sizeof(orig_msg));
+				orig_msg.hdr.number=msg.hdr.thread_back;
+				if(smb_getmsgidx(&smb[cur_smb], &orig_msg))
+					f+=sprintf(fmsgbuf+f,"\1REPLY: <%s>\r",smb[cur_smb].last_error);
+				else {
+					smb_lockmsghdr(&smb[cur_smb],&orig_msg);
+					smb_getmsghdr(&smb[cur_smb],&orig_msg);
+					smb_unlockmsghdr(&smb[cur_smb],&orig_msg);
+					if(orig_msg.ftn_msgid != NULL && orig_msg.ftn_msgid[0])
+						f+=sprintf(fmsgbuf+f,"\1REPLY: %.256s\r",orig_msg.ftn_msgid);	
+				}
+			}
+			if(msg.ftn_pid!=NULL)	/* use original PID */
+				f+=sprintf(fmsgbuf+f,"\1PID: %.256s\r", msg.ftn_pid);
+			if(msg.ftn_tid!=NULL)	/* use original TID */
+				f+=sprintf(fmsgbuf+f,"\1TID: %.256s\r", msg.ftn_tid);
+			else					/* generate TID */
+				f+=sprintf(fmsgbuf+f,"\1TID: SBBSecho %s-%s r%s %s %s\r"
+					,SBBSECHO_VER,PLATFORM_DESC,revision,__DATE__,compiler);
+
+			/* Unknown kludge lines are added here */
+			for(l=0;l<msg.total_hfields && f<fmsgbuflen;l++)
+				if(msg.hfield[l].type == FIDOCTRL)
+					f+=sprintf(fmsgbuf+f,"\1%.512s\r",(char*)msg.hfield_dat[l]);
+
+			for(l=0,cr=1;buf[l] && f<fmsgbuflen;l++) {
+				if(buf[l]==CTRL_A) { /* Ctrl-A, so skip it and the next char */
+					char ch;
+					l++;
+					if(buf[l]==0 || toupper(buf[l])=='Z')	/* EOF */
+						break;
+					if((ch=ctrl_a_to_ascii_char(buf[l])) != 0)
+						fmsgbuf[f++]=ch;
+					continue; 
+				}
+					
+				/* Need to support converting sole-LFs to Hard-CR and soft-CR (0x8D) as well */
+				if((misc&STRIP_LF) && buf[l]=='\n')	/* Ignore line feeds */
+					continue;
+
+				if(cr) {
+					if(buf[l]=='-' && buf[l+1]=='-'
+						&& buf[l+2]=='-'
+						&& (buf[l+3]==' ' || buf[l+3]=='\r')) {
+						if(misc&CONVERT_TEAR)	/* Convert to === */
+							buf[l]=buf[l+1]=buf[l+2]='=';
+						else
+							tear=1; 
+					}
+					else if(!strncmp((char *)buf+l," * Origin: ",11))
+						buf[l+1]='#'; 
+				} /* Convert * Origin into # Origin */
+
+				if(buf[l]=='\r')
+					cr=1;
+				else
+					cr=0;
+				if((scfg.sub[i]->misc&SUB_ASCII) || (misc&ASCII_ONLY)) {
+					if(buf[l]<' ' && buf[l]>=0 && buf[l]!='\r'
+						&& buf[l]!='\n')			/* Ctrl ascii */
+						buf[l]='.';             /* converted to '.' */
+					if((uchar)buf[l]&0x80)		/* extended ASCII */
+						buf[l]=exascii_to_ascii_char(buf[l]);
+				}
+
+				fmsgbuf[f++]=buf[l]; 
+			}
+
+			FREE_AND_NULL(buf);
+			fmsgbuf[f]=0;
 
-				smb_close(&smb[cur_smb]);
-				FREE_AND_NULL(post);
+			if(!(scfg.sub[i]->misc&SUB_NOTAG)) {
+				if(!tear) {  /* No previous tear line */
+					sprintf(str,"--- SBBSecho %s-%s\r"
+						,SBBSECHO_VER,PLATFORM_DESC);
+					strcat((char *)fmsgbuf,str); 
+				}
 
-				if(!addr.zone && !(misc&LEAVE_MSGPTRS) && lastmsg>ptr)
-					write_export_ptr(i, lastmsg);
+				sprintf(str," * Origin: %s (%s)\r"
+					,scfg.sub[i]->origline[0] ? scfg.sub[i]->origline : scfg.origline
+					,smb_faddrtoa(&scfg.sub[i]->faddr,NULL));
+				strcat((char *)fmsgbuf,str); 
 			}
+
+			for(k=0;k<cfg.areas;k++)
+				if(cfg.area[k].sub==i) {
+					cfg.area[k].exported++;
+					pkt_to_pkt(fmsgbuf,cfg.area[k]
+						,(addr.zone) ? addr:pkt_faddr,hdr,msg_seen
+						,msg_path,(addr.zone) ? 2:0);
+					break; 
+				}
+			FREE_AND_NULL(fmsgbuf);
+			exported++;
+			exp++;
+			printf("Exp: %lu ",exp);
+			smb_unlockmsghdr(&smb[cur_smb],&msg);
+			smb_freemsgmem(&msg); 
 		}
+
+		smb_close(&smb[cur_smb]);
+		FREE_AND_NULL(post);
+
+		if(!addr.zone && !(misc&LEAVE_MSGPTRS) && lastmsg>ptr)
+			write_export_ptr(i, lastmsg, tag);
 	}
 
 	printf("\n");
@@ -3906,8 +3927,8 @@ void export_echomail(char *sub_code,faddr_t addr)
 	if(!addr.zone && cfg.log&LOG_AREA_TOTALS && exported)
 		for(i=0;i<cfg.areas;i++)
 			if(cfg.area[i].exported)
-				lprintf(LOG_INFO,"Exported: %5u msgs %8s -> %s"
-					,cfg.area[i].exported,scfg.sub[cfg.area[i].sub]->code
+				lprintf(LOG_INFO,"Exported: %5u msgs %*s -> %s"
+					,cfg.area[i].exported,LEN_EXTCODE,scfg.sub[cfg.area[i].sub]->code
 					,cfg.area[i].name);
 
 	export_time=((float)export_ticks)/(float)MSCLOCKS_PER_SEC;
@@ -3954,7 +3975,7 @@ int main(int argc, char **argv)
 			,password[16];
 	char	*fmsgbuf=NULL;
 	ushort	attr;
-	int 	i,j,k,file,fmsg,grp,node;
+	int 	i,j,k,file,fmsg,node;
 	BOOL	grunged;
 	uint	subnum[MAX_OPEN_SMBS]={INVALID_SUB};
 	ulong	echomail=0,m/* f, */,areatag;
@@ -5057,16 +5078,17 @@ int main(int argc, char **argv)
 
 		lprintf(LOG_DEBUG,"\nUpdating Message Pointers to Last Posted Message...");
 
-		for(grp=0;grp<scfg.total_grps;grp++) {
-			for(i=0;i<scfg.total_subs;i++) {
-				if((scfg.sub[i]->misc&SUB_FIDO) && scfg.sub[i]->grp==grp) {
-					uint32_t lastmsg;
-					lprintf(LOG_DEBUG,"\n%-15.15s %s"
-						,scfg.grp[scfg.sub[i]->grp]->sname,scfg.sub[i]->lname);
-					getlastmsg(i,&lastmsg,0);
-					write_export_ptr(i, lastmsg);
-				}
-			}
+		for(j=0; j<cfg.areas; j++) {
+			uint32_t lastmsg;
+			if(j==cfg.badecho)	/* Don't scan the bad-echo area */
+				continue;
+			i=cfg.area[j].sub;
+			if(i<0 || i>=scfg.total_subs)	/* Don't scan pass-through areas */
+				continue;
+			lprintf(LOG_DEBUG,"\n%-*.*s -> %s"
+				,LEN_EXTCODE, LEN_EXTCODE, scfg.sub[i]->code, cfg.area[j].name);
+			getlastmsg(i,&lastmsg,0);
+			write_export_ptr(i, lastmsg, cfg.area[j].name);
 		}
 	}
 
diff --git a/src/sbbs3/sbbsecho.h b/src/sbbs3/sbbsecho.h
index 69f123f75e..3131772891 100644
--- a/src/sbbs3/sbbsecho.h
+++ b/src/sbbs3/sbbsecho.h
@@ -37,7 +37,7 @@
 
 /* Portions written by Allen Christiansen 1994-1996 						*/
 
-#define SBBSECHO_VER "2.14"
+#define SBBSECHO_VER "2.20"
 
 #define IMPORT_NETMAIL  (1L<<0)
 #define IMPORT_PACKETS	(1L<<1)
-- 
GitLab