From b3e94c4e7ce62a34a52cc51f74e1635b9d6c18fe Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Sat, 17 Aug 2019 02:21:01 +0000
Subject: [PATCH] New QWK setting: Include UTF-8 chars. When off/false (the
 default), UTF-8 characters in message headers and body text will be converted
 to CP437. Also include a new field in HEADERS.DAT: utf8 = true/false to
 indicate that the message headers and body text contain UTF-8 encoding (not
 CP437).

---
 src/sbbs3/getmsg.cpp      |  2 +-
 src/sbbs3/msgtoqwk.cpp    | 48 +++++++++++++++++++++++++++------------
 src/sbbs3/pack_qwk.cpp    |  3 +--
 src/sbbs3/pack_rep.cpp    |  4 ++--
 src/sbbs3/postmsg.cpp     |  8 +++----
 src/sbbs3/qwk.cpp         |  7 +++++-
 src/sbbs3/qwk.h           |  1 +
 src/sbbs3/sbbsdefs.h      |  2 ++
 src/sbbs3/text.h          |  1 +
 src/sbbs3/text_defaults.c |  8 ++++---
 10 files changed, 56 insertions(+), 28 deletions(-)

diff --git a/src/sbbs3/getmsg.cpp b/src/sbbs3/getmsg.cpp
index d486132735..9113a13cfc 100644
--- a/src/sbbs3/getmsg.cpp
+++ b/src/sbbs3/getmsg.cpp
@@ -139,7 +139,7 @@ const char* sbbs_t::msghdr_field(const smbmsg_t* msg, const char* str, char* buf
 	if(msg == NULL || !(msg->hdr.auxattr & MSG_HFIELDS_UTF8))
 		return str;
 
-	if(can_utf8 && term_supports(UTF8))
+	if(can_utf8)
 		return str;
 
 	if(buf == NULL)
diff --git a/src/sbbs3/msgtoqwk.cpp b/src/sbbs3/msgtoqwk.cpp
index 563b93ac48..67c8049da7 100644
--- a/src/sbbs3/msgtoqwk.cpp
+++ b/src/sbbs3/msgtoqwk.cpp
@@ -35,6 +35,7 @@
 
 #include "sbbs.h"
 #include "qwk.h"
+#include "utf8.h"
 
 #define MAX_MSGNUM	0x7FFFFFUL	// only 7 (decimal) digits allowed for msg num 
 
@@ -46,7 +47,10 @@
 ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 	, int conf, FILE* hdrs, FILE* voting)
 {
-	char	str[512],from[512],to[512],ch=0,tear=0,tearwatch=0,*buf,*p;
+	char	str[512],ch=0,tear=0,tearwatch=0,*buf,*p;
+	char	to[512] = "";
+	char	from[512] = "";
+	char	subj[512] = "";
 	char	msgid[256];
 	char	reply_id[256];
 	char 	tmp[512];
@@ -61,6 +65,13 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 	get_msgid(&cfg, subnum, msg, msgid, sizeof(msgid));
 	offset=(long)ftell(qwk_fp);
 
+	if(msg->to != NULL)
+		SAFECOPY(to, msghdr_field(msg, msg->to, NULL, mode&QM_UTF8));
+	if(msg->from != NULL)
+		SAFECOPY(from, msghdr_field(msg, msg->from, NULL, mode&QM_UTF8));
+	if(msg->subj != NULL)
+	  	SAFECOPY(subj, msghdr_field(msg, msg->subj, NULL, mode&QM_UTF8));
+
 	if(msg->hdr.type != SMB_MSG_TYPE_NORMAL) {
 		if(voting == NULL)
 			return 0;
@@ -78,6 +89,8 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 			unsigned comments = 0;
 			unsigned answers = 0;
 			fprintf(voting, "[poll:%s]\n", msgid);
+			fprintf(voting , "Utf8 = %s\n"
+				,((msg->hdr.auxattr & MSG_HFIELDS_UTF8) && (mode&QM_UTF8)) ? "true" : "false");
 			if(msg->hdr.votes)
 				fprintf(voting, "MaxVotes = %hd\n", msg->hdr.votes);
 			if(msg->hdr.auxattr&POLL_RESULTS_MASK)
@@ -94,8 +107,8 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 			fprintf(voting, "[close:%s]\n", msgid);
 			break;
 		}
-		if(msg->subj && *msg->subj)
-			fprintf(voting, "%s: %s\n",smb_hfieldtype(SUBJECT), msg->subj);
+		if(subj[0])
+			fprintf(voting, "%s: %s\n",smb_hfieldtype(SUBJECT), subj);
 		if((p = get_replyid(&cfg, smb, msg, reply_id, sizeof(reply_id))) != NULL)
 			fprintf(voting, "%s: %s\n", smb_hfieldtype(RFC822REPLYID), p);
 		/* Time/Date/Zone info */
@@ -108,7 +121,7 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 			);
 
 		/* SENDER */
-		fprintf(voting, "%s: %s\n", smb_hfieldtype(SENDER), msg->from);
+		fprintf(voting, "%s: %s\n", smb_hfieldtype(SENDER), from);
 		if(msg->from_net.type)
 			fprintf(voting, "%s: %s\n", smb_hfieldtype(SENDERNETADDR), smb_netaddrstr(&msg->from_net, tmp));
 		fprintf(voting, "Conference: %u\n", conf);
@@ -117,6 +130,10 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 	else if(hdrs!=NULL) {
 		fprintf(hdrs,"[%lx]\n",offset);
 
+		fprintf(voting , "Utf8 = %s\n"
+			,((smb_msg_is_utf8(msg) || (msg->hdr.auxattr & MSG_HFIELDS_UTF8)) && (mode&QM_UTF8))
+				? "true" : "false");
+
 		/* Message-IDs */
 		fprintf(hdrs,"%s: %s\n", smb_hfieldtype(RFC822MSGID), msgid);
 		if((p = get_replyid(&cfg, smb, msg, reply_id, sizeof(reply_id))) != NULL)
@@ -151,7 +168,7 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 			);
 
 		/* SENDER */
-		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(SENDER),msg->from);
+		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(SENDER), from);
 		if(msg->from_net.type)
 			fprintf(hdrs,"%s: %s\n",smb_hfieldtype(SENDERNETADDR),smb_netaddrstr(&msg->from_net,tmp));
 		if((p=(char*)smb_get_hfield(msg,hfield_type=SENDERIPADDR,NULL))!=NULL)
@@ -176,10 +193,10 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 			fprintf(hdrs,"Reply-To: %s\n",p);	/* use original RFC822 header field */
 
 		/* SUBJECT */
-		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(SUBJECT),msg->subj);
+		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(SUBJECT), subj);
 
 		/* RECIPIENT */
-		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(RECIPIENT),msg->to);
+		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(RECIPIENT), to);
 		if(msg->to_net.type!=NET_NONE && subnum==INVALID_SUB)
 			fprintf(hdrs,"%s: %s\n",smb_hfieldtype(RECIPIENTNETADDR),smb_netaddrstr(&msg->to_net,tmp));
 
@@ -242,14 +259,15 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 		return(0);
 
 	char qwk_newline = QWK_NEWLINE;
-	if(smb_msg_is_utf8(msg))
-		qwk_newline = '\n';
+	if(smb_msg_is_utf8(msg)) {
+		if(mode&QM_UTF8)
+			qwk_newline = '\n';
+		else
+			utf8_normalize_str(buf);
+	}
 
 	fprintf(qwk_fp,"%*s",QWK_BLOCK_LEN,"");		/* Init header to space */
 
-	SAFECOPY(from,msg->from);
-	SAFECOPY(to,msg->to);
-
 	if(msg->hdr.type == SMB_MSG_TYPE_NORMAL) {
 		/* QWKE compatible kludges */
 		if(msg->from_net.addr && (uint)subnum==INVALID_SUB && !(mode&QM_TO_QNET)) {
@@ -297,8 +315,8 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 			else
 				SAFECOPY(to,msg->to); 
 		}
-		if((mode&QM_EXT) && strlen(msg->subj) > QWK_HFIELD_LEN)
-			size+=fprintf(qwk_fp,"Subject: %.128s%c", msg->subj, qwk_newline);
+		if((mode&QM_EXT) && strlen(subj) > QWK_HFIELD_LEN)
+			size+=fprintf(qwk_fp,"Subject: %.128s%c", subj, qwk_newline);
 
 		if(msg->from_net.type==NET_QWK && mode&QM_VIA && !msg->forwarded)
 			size+=fprintf(qwk_fp,"@VIA: %s%c"
@@ -535,7 +553,7 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, smb_t* smb
 		,tmp					/* date and time */
 		,to 					/* To: */
 		,from					/* From: */
-		,msg->subj              /* Subject */
+		,subj					/* Subject */
 		,nulstr                 /* Password */
 		,msg->hdr.thread_back&MAX_MSGNUM   /* Message Re: Number */
 		,(size/QWK_BLOCK_LEN)+1	/* Number of blocks */
diff --git a/src/sbbs3/pack_qwk.cpp b/src/sbbs3/pack_qwk.cpp
index 24c6dd513d..c9f3551fc2 100644
--- a/src/sbbs3/pack_qwk.cpp
+++ b/src/sbbs3/pack_qwk.cpp
@@ -105,8 +105,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		mode|=QM_VIA;
 	if(useron.qwk&QWK_MSGID)
 		mode|=QM_MSGID;
-	if(useron.qwk&QWK_EXT)
-		mode|=QM_EXT;
+	mode |= useron.qwk&(QWK_EXT | QWK_UTF8);
 
 	(*msgcnt)=0L;
 	if(/* !prepack && */ !(useron.qwk&QWK_NOCTRL)) {
diff --git a/src/sbbs3/pack_rep.cpp b/src/sbbs3/pack_rep.cpp
index 10b8f5b73d..de6a7e7efb 100644
--- a/src/sbbs3/pack_rep.cpp
+++ b/src/sbbs3/pack_rep.cpp
@@ -149,7 +149,7 @@ bool sbbs_t::pack_rep(uint hubnum)
 			}
 
 			mode = QM_TO_QNET|QM_REP;
-			mode |= (cfg.qhub[hubnum]->misc&(QHUB_EXT|QHUB_CTRL_A));
+			mode |= (cfg.qhub[hubnum]->misc&(QHUB_EXT | QHUB_CTRL_A | QHUB_UTF8));
 			/* For an unclear reason, kludge lines (including @VIA and @TZ) were not included in NetMail previously */
 			if(!(cfg.qhub[hubnum]->misc&QHUB_NOHEADERS)) mode|=(QM_VIA|QM_TZ|QM_MSGID|QM_REPLYTO);
 			msgtoqwk(&msg, rep, mode, &smb, /* confnum: */0, hdrs);
@@ -223,7 +223,7 @@ bool sbbs_t::pack_rep(uint hubnum)
 			}
 
 			mode = cfg.qhub[hubnum]->mode[i]|QM_TO_QNET|QM_REP;
-			mode |= (cfg.qhub[hubnum]->misc&(QHUB_EXT|QHUB_CTRL_A));
+			mode |= (cfg.qhub[hubnum]->misc&(QHUB_EXT | QHUB_CTRL_A | QHUB_UTF8));
 			if(!(cfg.qhub[hubnum]->misc&QHUB_NOHEADERS)) mode|=(QM_VIA|QM_TZ|QM_MSGID|QM_REPLYTO);
 			if(msg.from_net.type!=NET_QWK)
 				mode|=QM_TAGLINE;
diff --git a/src/sbbs3/postmsg.cpp b/src/sbbs3/postmsg.cpp
index 5326f08ce0..41ec13ec82 100644
--- a/src/sbbs3/postmsg.cpp
+++ b/src/sbbs3/postmsg.cpp
@@ -91,23 +91,23 @@ bool sbbs_t::postmsg(uint subnum, long wm_mode, smb_t* resmb, smbmsg_t* remsg)
 	uint	reason;
 
 	if(remsg) {
-		SAFECOPY(title, msghdr_field(remsg, remsg->subj, NULL, /* UTF8: */true));
+		SAFECOPY(title, msghdr_field(remsg, remsg->subj, NULL, term_supports(UTF8)));
 		if(remsg->hdr.attr&MSG_ANONYMOUS)
 			SAFECOPY(from,text[Anonymous]);
 		else
-			SAFECOPY(from, msghdr_field(remsg, remsg->from, NULL, /* UTF8: */true));
+			SAFECOPY(from, msghdr_field(remsg, remsg->from, NULL, term_supports(UTF8)));
 		// If user posted this message, reply to the original recipient again
 		if(remsg->to != NULL
 			&& ((remsg->from_ext != NULL && atoi(remsg->from_ext)==useron.number)
 				|| stricmp(useron.alias,remsg->from) == 0 || stricmp(useron.name,remsg->from) == 0))
-			SAFECOPY(touser, msghdr_field(remsg, remsg->to, NULL, /* UTF8: */true));
+			SAFECOPY(touser, msghdr_field(remsg, remsg->to, NULL, term_supports(UTF8)));
 		else
 			SAFECOPY(touser,from);
 		msgattr=(ushort)(remsg->hdr.attr&MSG_PRIVATE);
 		sprintf(top,text[RegardingByToOn]
 			,title
 			,from
-			,msghdr_field(remsg, remsg->to, NULL, /* UTF8: */true)
+			,msghdr_field(remsg, remsg->to, NULL, term_supports(UTF8))
 			,timestr(remsg->hdr.when_written.time)
 			,smb_zonestr(remsg->hdr.when_written.zone,NULL));
 		if(remsg->tags != NULL)
diff --git a/src/sbbs3/qwk.cpp b/src/sbbs3/qwk.cpp
index 7222702e45..9dc9bbd42d 100644
--- a/src/sbbs3/qwk.cpp
+++ b/src/sbbs3/qwk.cpp
@@ -483,10 +483,12 @@ void sbbs_t::qwk_sec()
 					,useron.qwk&QWK_VIA ? text[Yes]:text[No]);
 				bprintf(text[QWKSettingsMsgID]
 					,useron.qwk&QWK_MSGID ? text[Yes]:text[No]);
+				bprintf(text[QWKSettingsUtf8]
+					,useron.qwk&QWK_UTF8 ? text[Yes]:text[No]);
 				bprintf(text[QWKSettingsExtended]
 					,useron.qwk&QWK_EXT ? text[Yes]:text[No]);
 				bputs(text[QWKSettingsWhich]);
-				ch=(char)getkeys("AEDFHIOPQTYMNCXZV",0);
+				ch=(char)getkeys("AEDFHIOPQTUYMNCXZV",0);
 				if(sys_status&SS_ABORT || !ch || ch=='Q' || !online)
 					break;
 				switch(ch) {
@@ -555,6 +557,9 @@ void sbbs_t::qwk_sec()
 					case 'X':	/* QWKE */
 						useron.qwk^=QWK_EXT;
 						break;
+					case 'U':
+						useron.qwk^=QWK_UTF8;
+						break;
 				}
 				putuserrec(&cfg,useron.number,U_QWK,8,ultoa(useron.qwk,str,16));
 			}
diff --git a/src/sbbs3/qwk.h b/src/sbbs3/qwk.h
index 8a289f7a02..a8939207d6 100644
--- a/src/sbbs3/qwk.h
+++ b/src/sbbs3/qwk.h
@@ -50,6 +50,7 @@
 #define QM_MSGID	(1<<10)	/* Include @MSGID and @REPLY kludges */
 #define QM_REPLYTO	(1<<11)	/* Include @REPLYTO kludge */
 #define QM_EXT		(1<<13)	/* QWK Extended (QWKE) mode (same as QWK_EXT and QHUB_EXT) */
+#define QM_UTF8		(1<<18)	/* Include UTF-8 characters */
 
 float	ltomsbin(long val);
 bool	route_circ(char *via, char *id);
diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index 7b13063903..1395a77788 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -436,6 +436,7 @@ typedef enum {						/* Values for xtrn_t.event				*/
 #define QWK_MSGID	(1L<<14)		/* Include "@MSGID" in msgs				*/
 #define QWK_HEADERS	(1L<<16)		/* Include HEADERS.DAT file				*/
 #define QWK_VOTING	(1L<<17)		/* Include VOTING.DAT					*/
+#define QWK_UTF8	(1L<<18)		/* Include UTF-8 characters				*/
 
 #define QWK_DEFAULT	(QWK_FILES|QWK_ATTACH|QWK_EMAIL|QWK_DELMAIL)
 
@@ -447,6 +448,7 @@ typedef enum {						/* Values for xtrn_t.event				*/
 #define QHUB_NOKLUDGES	(1<<14)		/* Don't include @-kludges */
 #define QHUB_NOHEADERS	(1<<16)		/* Don't include HEADERS.DAT */
 #define QHUB_NOVOTING	(1<<17)		/* Don't include VOTING.DAT */
+#define QHUB_UTF8		(1<<18)		/* Include UTF-8 characters */
 
 							/* Bits in user.chat							*/
 #define CHAT_ECHO	(1<<0)	/* Multinode chat echo							*/
diff --git a/src/sbbs3/text.h b/src/sbbs3/text.h
index 325f6add70..74906184c3 100644
--- a/src/sbbs3/text.h
+++ b/src/sbbs3/text.h
@@ -837,6 +837,7 @@ enum {
 	,Utf8TerminalQ
 	,MsgCarbonCopyList
 	,LoggingOn
+	,QWKSettingsUtf8
 
 	,TOTAL_TEXT
 };
diff --git a/src/sbbs3/text_defaults.c b/src/sbbs3/text_defaults.c
index 8f720cffd4..d8310ca798 100644
--- a/src/sbbs3/text_defaults.c
+++ b/src/sbbs3/text_defaults.c
@@ -116,9 +116,9 @@ const char * const text_defaults[TOTAL_TEXT]={
 	,"\x01\x6e\x0d\x0a\x4e\x6f\x20\x6d\x61\x69\x6c\x20\x6f\x6e\x20\x73\x79\x73\x74\x65\x6d\x2e\x0d\x0a" // 063 NoMailOnSystem
 	,"\x01\x6e\x0d\x0a\x01\x63\xfe\x20\x01\x68\x01\x62\x52\x65\x61\x64\x69\x6e\x67\x20\x41\x6c\x6c\x20\x45\x2d\x6d\x61\x69\x6c\x20\x01"
 		"\x6e\x01\x63\xfe\x20\x01\x68\x01\x62\x28\x01\x77\x25\x75\x20\x01\x62\x6f\x66\x20\x01\x77\x25\x75\x01\x62\x29\x3a\x20\x01\x6e" // 064 ReadingAllMail
-	,"\x01\x5f\x0d\x0a\x01\x71\x01\x67\x01\x68\x20\x20\x20\x20\x20\x20\x46\x72\x6f\x6d\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20"
-		"\x20\x20\x20\x20\x20\x20\x20\x54\x6f\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x41\x20"
-		"\x53\x75\x62\x6a\x65\x63\x74\x0d\x0a\x01\x6e" // 065 MailOnSystemLstHdr
+	,"\x01\x5f\x0d\x0a\x01\x67\x01\x68\x20\x20\x20\x20\x20\x20\x46\x72\x6f\x6d\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20"
+		"\x20\x20\x20\x20\x20\x54\x6f\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x41\x20\x53\x75"
+		"\x62\x6a\x65\x63\x74\x0d\x0a\x01\x6e" // 065 MailOnSystemLstHdr
 	,"\x01\x67\x01\x68\x25\x35\x64\x01\x6e\x01\x67\x20\x25\x2d\x32\x32\x2e\x32\x32\x73\x20\x25\x2d\x32\x32\x2e\x32\x32\x73\x20\x01\x68"
 		"\x25\x63\x01\x6e\x01\x67\x20\x25\x73\x0d\x0a" // 066 MailOnSystemLstFmt
 	,"\x01\x2d\x0d\x0a\x01\x63\x25\x2d\x31\x35\x2e\x31\x35\x73\x20\x01\x79\x01\x68\x25\x2d\x34\x34\x2e\x34\x34\x73\x20\x01\x6e\x01\x63"
@@ -1362,4 +1362,6 @@ const char * const text_defaults[TOTAL_TEXT]={
 	,"\x0d\x0a\xb3\x20\x01\x62\x43\x43\x20\x20\x01\x6e\x01\x62\x3a\x20\x01\x68\x01\x63\x25\x2e\x37\x30\x73" // 825 MsgCarbonCopyList
 	,"\x01\x6e\x01\x68\x4c\x6f\x67\x67\x69\x6e\x67\x20\x6f\x6e\x20\x74\x6f\x20\x40\x42\x42\x53\x40\x20\x61\x73\x20\x40\x41\x4c\x49\x41"
 		"\x53\x40\x20\x40\x45\x4c\x4c\x49\x50\x53\x49\x53\x40\x01\x6e\x0d\x0a\x40\x52\x45\x53\x45\x54\x50\x41\x55\x53\x45\x40" // 826 LoggingOn
+	,"\x01\x6e\x01\x62\x5b\x01\x68\x01\x77\x55\x01\x6e\x01\x62\x5d\x20\x01\x68\x49\x6e\x63\x6c\x75\x64\x65\x20\x55\x54\x46\x2d\x38\x20"
+		"\x43\x68\x61\x72\x61\x63\x74\x65\x72\x73\x20\x20\x20\x20\x20\x01\x6e\x01\x62\x3a\x20\x01\x63\x25\x73\x0d\x0a" // 827 QWKSettingsUtf8
 };
-- 
GitLab