diff --git a/src/sbbs3/msgtoqwk.cpp b/src/sbbs3/msgtoqwk.cpp
index 7c7dcaddbe4b6931db895187cfb9bb6db388b1d5..81c58c9154789f79939e6c5d43c924e1614fe753 100644
--- a/src/sbbs3/msgtoqwk.cpp
+++ b/src/sbbs3/msgtoqwk.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 2004 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2008 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				*
@@ -45,17 +45,105 @@
 /* mode determines how to handle Ctrl-A codes								*/
 /****************************************************************************/
 ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
-	, int conf)
+	, int conf, FILE* hdrs)
 {
 	char	str[512],from[512],to[512],ch=0,tear=0,tearwatch=0,*buf,*p;
 	char 	tmp[512];
 	long	l,size=0,offset;
 	int 	i;
+	ushort	hfield_type;
 	struct	tm	tm;
 	smbmsg_t	remsg;
 	time_t	tt;
 
 	offset=ftell(qwk_fp);
+	if(hdrs!=NULL) {
+		fprintf(hdrs,"[%x]\n",offset);
+
+		/* Message-IDs */
+		fprintf(hdrs,"Message-ID:  %s\n",get_msgid(&cfg,subnum,msg));
+		if(msg->reply_id!=NULL)
+			fprintf(hdrs,"In-Reply-To: %s\n",msg->reply_id);
+
+		/* Time/Date/Zone info */
+		fprintf(hdrs,"WhenWritten:  %-20s %04hx\n"
+			,xpDateTime_to_isoDateTimeStr(
+				time_to_xpDateTime(msg->hdr.when_written.time,smb_tzutc(msg->hdr.when_written.zone))
+				,/* separators: */"","","", /* precision: */0
+				,str,sizeof(str))
+			,msg->hdr.when_written.zone
+			);
+		fprintf(hdrs,"WhenImported: %-20s %04hx\n"
+			,xpDateTime_to_isoDateTimeStr(
+				time_to_xpDateTime(msg->hdr.when_imported.time,smb_tzutc(msg->hdr.when_imported.zone))
+				,/* separators: */"","","", /* precision: */0
+				,str,sizeof(str))
+			,msg->hdr.when_imported.zone
+			);
+
+		/* SENDER */
+		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(SENDER),msg->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)
+			fprintf(hdrs,"%s: %s\n",smb_hfieldtype(hfield_type),p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=SENDERHOSTNAME,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n",smb_hfieldtype(hfield_type),p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=SENDERPROTOCOL,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n",smb_hfieldtype(hfield_type),p);
+		if(msg->from_org!=NULL)
+			fprintf(hdrs,"Organization: %s\n",msg->from_org);
+
+		/* Reply-To */
+		if((p=(char*)smb_get_hfield(msg,RFC822REPLYTO,NULL))==NULL) {
+			if(msg->replyto_net.type==NET_INTERNET)
+				p=(char*)msg->replyto_net.addr;
+			else if(msg->replyto!=NULL)
+				p=msg->replyto;
+		}
+		if(p!=NULL)
+			fprintf(hdrs,"Reply-To: %s\n",p);	/* use original RFC822 header field */
+
+		/* SUBJECT */
+		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(SUBJECT),msg->subj);
+
+		/* RECIPIENT */
+		fprintf(hdrs,"%s: %s\n",smb_hfieldtype(RECIPIENT),msg->to);
+		if(msg->to_net.type)
+			fprintf(hdrs,"%s: %s\n",smb_hfieldtype(RECIPIENTNETADDR),smb_netaddrstr(&msg->to_net,tmp));
+
+		/* FidoNet */
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOAREA,NULL))!=NULL)	
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOSEENBY,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOPATH,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOMSGID,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOREPLYID,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOPID,NULL))!=NULL)	
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOFLAGS,NULL))!=NULL)	
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=FIDOTID,NULL))!=NULL)	
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+
+		/* USENET */
+		if((p=(char*)smb_get_hfield(msg,hfield_type=USENETPATH,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+		if((p=(char*)smb_get_hfield(msg,hfield_type=USENETNEWSGROUPS,NULL))!=NULL)
+			fprintf(hdrs,"%s: %s\n", smb_hfieldtype(hfield_type), p);
+
+		/* RFC822 header fields: */
+		for(i=0;i<msg->total_hfields;i++)
+			if(msg->hfield[i].type==RFC822HEADER)
+				fprintf(hdrs,"%s\n",msg->hfield_dat[i]);
+
+		/* Blank line: */
+		fprintf(hdrs,"\n");
+	}
 	memset(str,' ',QWK_BLOCK_LEN);
 	fwrite(str,QWK_BLOCK_LEN,1,qwk_fp);		/* Init header to space */
 
@@ -70,14 +158,15 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
 		else
 			sprintf(from,"%.128s@%.128s",msg->from,(char*)msg->from_net.addr);
 		if(strlen(from)>25) {
-			sprintf(str,"From: %.128s%c%c",from,QWK_NEWLINE,QWK_NEWLINE);
-			fwrite(str,strlen(str),1,qwk_fp);
-			size+=strlen(str);
-			sprintf(from,"%.128s",msg->from); } }
+			size+=fprintf(qwk_fp,"From: %.128s%c%c",from,QWK_NEWLINE,QWK_NEWLINE);
+			sprintf(from,"%.128s",msg->from); 
+		} 
+	}
 	else {
 		sprintf(from,"%.128s",msg->from);
 		if(msg->hdr.attr&MSG_ANONYMOUS && !SYSOP)	   /* from user */
-			strcpy(from,text[Anonymous]); }
+			SAFECOPY(from,text[Anonymous]); 
+	}
 
 	if(msg->to_net.addr && (uint)subnum==INVALID_SUB) {
 		if(msg->to_net.type==NET_FIDO)
@@ -89,10 +178,9 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
 				p=strchr((char *)msg->to_net.addr,'/');
 				if(p) { 	/* Another hop */
 					p++;
-					strcpy(to,"NETMAIL");
-					sprintf(str,"%.128s@%.128s%c",msg->to,p,QWK_NEWLINE);
-					fwrite(str,strlen(str),1,qwk_fp);
-					size+=strlen(str); }
+					SAFECOPY(to,"NETMAIL");
+					size+=fprintf(qwk_fp,"%.128s@%.128s%c",msg->to,p,QWK_NEWLINE);
+				}
 				else
 					sprintf(to,"%.128s",msg->to); }
 			else
@@ -100,73 +188,56 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
 		else
 			sprintf(to,"%.128s@%.128s",msg->to,(char*)msg->to_net.addr);
 		if(strlen(to)>25) {
-			sprintf(str,"To: %.128s%c%c",to,QWK_NEWLINE,QWK_NEWLINE);
-			fwrite(str,strlen(str),1,qwk_fp);
-			size+=strlen(str);
+			size+=fprintf(qwk_fp,"To: %.128s%c%c",to,QWK_NEWLINE,QWK_NEWLINE);
 			if(msg->to_net.type==NET_QWK)
-				strcpy(to,"NETMAIL");
+				SAFECOPY(to,"NETMAIL");
 			else
-				sprintf(to,"%.128s",msg->to); } }
+				sprintf(to,"%.128s",msg->to); 
+		} 
+	}
 	else
 		sprintf(to,"%.128s",msg->to);
 
-	if(msg->from_net.type==NET_QWK && mode&QM_VIA && !msg->forwarded) {
-		sprintf(str,"@VIA: %.*s%c"
-			,(int)(sizeof(str)-12),(char*)msg->from_net.addr,QWK_NEWLINE);
-		fwrite(str,strlen(str),1,qwk_fp);
-		size+=strlen(str); }
+	if(msg->from_net.type==NET_QWK && mode&QM_VIA && !msg->forwarded)
+		size+=fprintf(qwk_fp,"@VIA: %s%c"
+			,(char*)msg->from_net.addr,QWK_NEWLINE);
 	
 	if(mode&QM_MSGID && (uint)subnum!=INVALID_SUB) {
-		sprintf(str,"@MSGID: %.*s%c"
-			,(int)(sizeof(str)-12),get_msgid(&cfg,subnum,msg),QWK_NEWLINE);
-		fwrite(str,strlen(str),1,qwk_fp);
-		size+=strlen(str); 
+		size+=fprintf(qwk_fp,"@MSGID: %s%c"
+			,get_msgid(&cfg,subnum,msg),QWK_NEWLINE);
 
-		str[0]=0;
 		if(msg->reply_id) {
 			SAFECOPY(tmp,msg->reply_id);
 			truncstr(tmp," ");
-			sprintf(str,"@REPLY: %.*s%c"
-				,(int)(sizeof(str)-12),tmp,QWK_NEWLINE);
+			size+=fprintf(qwk_fp,"@REPLY: %s%c"
+				,tmp,QWK_NEWLINE);
 		} else if(msg->hdr.thread_back) {
 			memset(&remsg,0,sizeof(remsg));
 			remsg.hdr.number=msg->hdr.thread_back;
 			if(smb_getmsgidx(&smb, &remsg))
-				sprintf(str,"@REPLY: <%s>%c",smb.last_error,QWK_NEWLINE);
+				size+=fprintf(qwk_fp,"@REPLY: <%s>%c",smb.last_error,QWK_NEWLINE);
 			else
-				sprintf(str,"@REPLY: %s%c"
+				size+=fprintf(qwk_fp,"@REPLY: %s%c"
 					,get_msgid(&cfg,subnum,&remsg)
 					,QWK_NEWLINE);
 		}
-		if(str[0]) {
-			fwrite(str,strlen(str),1,qwk_fp);
-			size+=strlen(str); 
-		}
 	}
 
-	if(msg->hdr.when_written.zone && mode&QM_TZ) {
-		sprintf(str,"@TZ: %04x%c",msg->hdr.when_written.zone,QWK_NEWLINE);
-		fwrite(str,strlen(str),1,qwk_fp);
-		size+=strlen(str); 
-	}
+	if(msg->hdr.when_written.zone && mode&QM_TZ)
+		size+=fprintf(qwk_fp,"@TZ: %04x%c",msg->hdr.when_written.zone,QWK_NEWLINE);
 
-	if(msg->replyto!=NULL && mode&QM_REPLYTO) {
-		sprintf(str,"@REPLYTO: %.*s%c"
-			,(int)(sizeof(str)-12),msg->replyto,QWK_NEWLINE);
-		fwrite(str,strlen(str),1,qwk_fp);
-		size+=strlen(str); 
-	}
+	if(msg->replyto!=NULL && mode&QM_REPLYTO)
+		size+=fprintf(qwk_fp,"@REPLYTO: %s%c"
+			,msg->replyto,QWK_NEWLINE);
 
 	p=0;
 	for(i=0;i<msg->total_hfields;i++) {
 		if(msg->hfield[i].type==SENDER)
 			p=(char *)msg->hfield_dat[i];
 		if(msg->hfield[i].type==FORWARDED && p) {
-			sprintf(str,"Forwarded from %s on %s%c",p
+			size+=fprintf(qwk_fp,"Forwarded from %s on %s%c",p
 				,timestr(*(time32_t *)msg->hfield_dat[i])
 				,QWK_NEWLINE);
-			fwrite(str,strlen(str),1,qwk_fp);
-			size+=strlen(str); 
 		} 
 	}
 
@@ -228,70 +299,68 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
 				str[0]=0;
 				switch(toupper(ch)) {		/* non-color codes */
 					case 'L':
-						strcpy(str,"\x1b[2J\x1b[H");
+						SAFECOPY(str,"\x1b[2J\x1b[H");
 						break;
 					case 'W':
-						strcpy(str,ansi(LIGHTGRAY));
+						SAFECOPY(str,ansi(LIGHTGRAY));
 						break;
 					case 'K':
-						strcpy(str,ansi(BLACK));
+						SAFECOPY(str,ansi(BLACK));
 						break;
 					case 'H':
-						strcpy(str,ansi(HIGH));
+						SAFECOPY(str,ansi(HIGH));
 						break;
 					case 'I':
-						strcpy(str,ansi(BLINK));
+						SAFECOPY(str,ansi(BLINK));
 						break;
 					case 'N':   /* Normal */
-						strcpy(str,ansi(ANSI_NORMAL));
+						SAFECOPY(str,ansi(ANSI_NORMAL));
 						break;
 					case 'R':                               /* Color codes */
-						strcpy(str,ansi(RED));
+						SAFECOPY(str,ansi(RED));
 						break;
 					case 'G':
-						strcpy(str,ansi(GREEN));
+						SAFECOPY(str,ansi(GREEN));
 						break;
 					case 'B':
-						strcpy(str,ansi(BLUE));
+						SAFECOPY(str,ansi(BLUE));
 						break;
 					case 'C':
-						strcpy(str,ansi(CYAN));
+						SAFECOPY(str,ansi(CYAN));
 						break;
 					case 'M':
-						strcpy(str,ansi(MAGENTA));
+						SAFECOPY(str,ansi(MAGENTA));
 						break;
 					case 'Y':   /* Yellow */
-						strcpy(str,ansi(BROWN));
+						SAFECOPY(str,ansi(BROWN));
 						break;
 					case '0':
-						strcpy(str,ansi(BG_BLACK));
+						SAFECOPY(str,ansi(BG_BLACK));
 						break;
 					case '1':
-						strcpy(str,ansi(BG_RED));
+						SAFECOPY(str,ansi(BG_RED));
 						break;
 					case '2':
-						strcpy(str,ansi(BG_GREEN));
+						SAFECOPY(str,ansi(BG_GREEN));
 						break;
 					case '3':
-						strcpy(str,ansi(BG_BROWN));
+						SAFECOPY(str,ansi(BG_BROWN));
 						break;
 					case '4':
-						strcpy(str,ansi(BG_BLUE));
+						SAFECOPY(str,ansi(BG_BLUE));
 						break;
 					case '5':
-						strcpy(str,ansi(BG_MAGENTA));
+						SAFECOPY(str,ansi(BG_MAGENTA));
 						break;
 					case '6':
-						strcpy(str,ansi(BG_CYAN));
+						SAFECOPY(str,ansi(BG_CYAN));
 						break; 
 					case '7':
-						strcpy(str,ansi(BG_LIGHTGRAY));
+						SAFECOPY(str,ansi(BG_LIGHTGRAY));
 						break;
 				}
-				if(str[0]) {
-					fwrite(str,strlen(str),1,qwk_fp);
-					size+=strlen(str); 
-				}
+				if(str[0])
+					size+=fwrite(str,sizeof(char),strlen(str),qwk_fp);
 				continue; 
 			} 						/* End Expand */
 
@@ -314,18 +383,18 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
 
 	if(mode&QM_TAGLINE && !(cfg.sub[subnum]->misc&SUB_NOTAG)) {
 		if(!tear)										/* no tear line */
-			sprintf(str,"\1n---%c",QWK_NEWLINE);        /* so add one */
+			SAFEPRINTF(str,"\1n---%c",QWK_NEWLINE);        /* so add one */
 		else
-			strcpy(str,"\1n");
+			SAFECOPY(str,"\1n");
 		if(cfg.sub[subnum]->misc&SUB_ASCII) ch='*';
 		else ch='�';
-		sprintf(tmp," %c \1g%.10s\1n %c %.127s%c"
+		safe_snprintf(tmp,sizeof(tmp)," %c \1g%.10s\1n %c %.127s%c"
 			,ch,VERSION_NOTICE,ch,cfg.sub[subnum]->tagline,QWK_NEWLINE);
 		strcat(str,tmp);
 		if(!(mode&A_LEAVE))
 			remove_ctrl_a(str,NULL);
-		fwrite(str,strlen(str),1,qwk_fp);
-		size+=strlen(str); }
+		size+=fwrite(str,sizeof(char),strlen(str),qwk_fp);
+	}
 
 	while(size%QWK_BLOCK_LEN) {				 /* Pad with spaces */
 		size++;
@@ -335,7 +404,7 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
 	if(localtime_r(&tt,&tm)==NULL)
 		memset(&tm,0,sizeof(tm));
 
-	sprintf(tmp,"%02u-%02u-%02u%02u:%02u"
+	safe_snprintf(tmp,sizeof(tmp),"%02u-%02u-%02u%02u:%02u"
 		,tm.tm_mon+1,tm.tm_mday,TM_YEAR(tm.tm_year)
 		,tm.tm_hour,tm.tm_min);
 
@@ -351,7 +420,7 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum
 			ch=' '; /* public, unread */ }
 
 
-	sprintf(str,"%c%-7lu%-13.13s%-25.25s"
+	safe_snprintf(str,sizeof(str),"%c%-7lu%-13.13s%-25.25s"
 		"%-25.25s%-25.25s%12s%-8lu%-6lu\xe1%c%c%c%c%c"
 		,ch                     /* message status flag */
 		,mode&QM_REP ? (ulong)conf /* conference or */
diff --git a/src/sbbs3/pack_qwk.cpp b/src/sbbs3/pack_qwk.cpp
index f3105b5b35c41455e104689afcb03af80747ca58..92269a09c27906612f053524a6e5180d6077d7fe 100644
--- a/src/sbbs3/pack_qwk.cpp
+++ b/src/sbbs3/pack_qwk.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 2007 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2008 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				*
@@ -63,6 +63,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 	post_t	*post;
 	glob_t	g;
 	FILE	*stream,*qwk,*personal,*ndx;
+	FILE*	hdrs=NULL;
 	DIR*	dir;
 	DIRENT*	dirent;
 	struct	tm tm;
@@ -73,7 +74,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		ex|=EX_OFFLINE;
 
 	delfiles(cfg.temp_dir,ALLFILES);
-	sprintf(str,"%sfile/%04u.qwk",cfg.data_dir,useron.number);
+	SAFEPRINTF2(str,"%sfile/%04u.qwk",cfg.data_dir,useron.number);
 	if(fexistcase(str)) {
 		for(k=0;k<cfg.total_fextrs;k++)
 			if(!stricmp(cfg.fextr[k]->ext,useron.tmpext)
@@ -108,7 +109,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		/***************************/
 		/* Create CONTROL.DAT file */
 		/***************************/
-		sprintf(str,"%sCONTROL.DAT",cfg.temp_dir);
+		SAFEPRINTF(str,"%sCONTROL.DAT",cfg.temp_dir);
 		if((stream=fopen(str,"wb"))==NULL) {
 			errormsg(WHERE,ERR_OPEN,str,0);
 			return(false); 
@@ -137,11 +138,11 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		for(i=0;i<usrgrps;i++) 
 			for(j=0;j<usrsubs[i];j++) {
 				if(useron.qwk&QWK_EXT)	/* 255 char max */
-					sprintf(confname,"%s %s"
+					SAFEPRINTF2(confname,"%s %s"
 						,cfg.grp[cfg.sub[usrsub[i][j]]->grp]->sname
 						,cfg.sub[usrsub[i][j]]->lname);
 				else					/* 10 char max */
-					strcpy(confname,cfg.sub[usrsub[i][j]]->qwkname);
+					SAFECOPY(confname,cfg.sub[usrsub[i][j]]->qwkname);
 				fprintf(stream,"%u\r\n%s\r\n"
 					,cfg.sub[usrsub[i][j]]->qwkconf ? cfg.sub[usrsub[i][j]]->qwkconf
 					: ((i+1)*1000)+j+1,confname);
@@ -151,7 +152,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		/***********************/
 		/* Create DOOR.ID File */
 		/***********************/
-		sprintf(str,"%sDOOR.ID",cfg.temp_dir);
+		SAFEPRINTF(str,"%sDOOR.ID",cfg.temp_dir);
 		if((stream=fopen(str,"wb"))==NULL) {
 			errormsg(WHERE,ERR_OPEN,str,0);
 			return(false); 
@@ -190,7 +191,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 			/***********************/
 			/* Create NETFLAGS.DAT */
 			/***********************/
-			sprintf(str,"%sNETFLAGS.DAT",cfg.temp_dir);
+			SAFEPRINTF(str,"%sNETFLAGS.DAT",cfg.temp_dir);
 			if((stream=fopen(str,"wb"))==NULL) {
 				errormsg(WHERE,ERR_CREATE,str,0);
 				return(false); 
@@ -206,7 +207,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 	/****************************************************/
 	/* Create MESSAGES.DAT, write header and leave open */
 	/****************************************************/
-	sprintf(str,"%sMESSAGES.DAT",cfg.temp_dir);
+	SAFEPRINTF(str,"%sMESSAGES.DAT",cfg.temp_dir);
 	if(fexistcase(str))
 		fmode="r+b";
 	else
@@ -215,6 +216,14 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		errormsg(WHERE,ERR_OPEN,str,0);
 		return(false); 
 	}
+	if(useron.qwk&QWK_HEADERS) {
+		SAFEPRINTF(str,"%sHEADERS.DAT",cfg.temp_dir);
+		if((hdrs=fopen(str,"a"))==NULL) {
+			fclose(qwk);
+			errormsg(WHERE,ERR_OPEN,str,0);
+			return(false); 
+		}
+	}
 	l=filelength(fileno(qwk));
 	if(l<1) {
 		fprintf(qwk,"%-128.128s","Produced by " VERSION_NOTICE "  " COPYRIGHT_NOTICE);
@@ -223,7 +232,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		msgndx=l/QWK_BLOCK_LEN;
 		fseek(qwk,0,SEEK_END);
 	}
-	sprintf(str,"%sNEWFILES.DAT",cfg.temp_dir);
+	SAFEPRINTF(str,"%sNEWFILES.DAT",cfg.temp_dir);
 	remove(str);
 	if(!(useron.rest&FLAG('T')) && useron.qwk&QWK_FILES)
 		files=create_filelist("NEWFILES.DAT",FL_ULTIME);
@@ -236,9 +245,11 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		useron.qwk|=(QWK_EMAIL|QWK_ALLMAIL|QWK_DELMAIL);
 
 	if(!(useron.qwk&QWK_NOINDEX)) {
-		sprintf(str,"%sPERSONAL.NDX",cfg.temp_dir);
+		SAFEPRINTF(str,"%sPERSONAL.NDX",cfg.temp_dir);
 		if((personal=fopen(str,"ab"))==NULL) {
 			fclose(qwk);
+			if(hdrs!=NULL)
+				fclose(hdrs);
 			errormsg(WHERE,ERR_OPEN,str,0);
 			return(false); 
 		}
@@ -247,11 +258,13 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		personal=NULL;
 
 	if(useron.qwk&(QWK_EMAIL|QWK_ALLMAIL) /* && !prepack */) {
-		sprintf(smb.file,"%smail",cfg.data_dir);
+		SAFEPRINTF(smb.file,"%smail",cfg.data_dir);
 		smb.retry_time=cfg.smb_retry_time;
 		smb.subnum=INVALID_SUB;
 		if((i=smb_open(&smb))!=0) {
 			fclose(qwk);
+			if(hdrs!=NULL)
+				fclose(hdrs);
 			if(personal)
 				fclose(personal);
 			errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
@@ -267,9 +280,11 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		if(mailmsgs && !(sys_status&SS_ABORT)) {
 			bputs(text[QWKPackingEmail]);
 			if(!(useron.qwk&QWK_NOINDEX)) {
-				sprintf(str,"%s000.NDX",cfg.temp_dir);
+				SAFEPRINTF(str,"%s000.NDX",cfg.temp_dir);
 				if((ndx=fopen(str,"ab"))==NULL) {
 					fclose(qwk);
+					if(hdrs!=NULL)
+						fclose(hdrs);
 					if(personal)
 						fclose(personal);
 					smb_close(&smb);
@@ -298,14 +313,14 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 					continue;
 
 				if(msg.hdr.auxattr&MSG_FILEATTACH && useron.qwk&QWK_ATTACH) {
-					sprintf(str,"%sfile/%04u.in/%s"
+					SAFEPRINTF3(str,"%sfile/%04u.in/%s"
 						,cfg.data_dir,useron.number,msg.subj);
-					sprintf(tmp,"%s%s",cfg.temp_dir,msg.subj);
+					SAFEPRINTF2(tmp,"%s%s",cfg.temp_dir,msg.subj);
 					if(fexistcase(str) && !fexistcase(tmp))
 						mv(str,tmp,1); 
 				}
 
-				size=msgtoqwk(&msg,qwk,mode,INVALID_SUB,0);
+				size=msgtoqwk(&msg,qwk,mode,INVALID_SUB,0,hdrs);
 				smb_unlockmsghdr(&smb,&msg);
 				smb_freemsgmem(&msg);
 				if(ndx) {
@@ -359,7 +374,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 					continue; 
 				}
 
-				sprintf(smb.file,"%s%s"
+				SAFEPRINTF2(smb.file,"%s%s"
 					,cfg.sub[usrsub[i][j]]->data_dir,cfg.sub[usrsub[i][j]]->code);
 				smb.retry_time=cfg.smb_retry_time;
 				smb.subnum=usrsub[i][j];
@@ -389,9 +404,11 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 					conf=((i+1)*1000)+j+1;
 
 				if(!(useron.qwk&QWK_NOINDEX)) {
-					sprintf(str,"%s%u.NDX",cfg.temp_dir,conf);
+					SAFEPRINTF2(str,"%s%u.NDX",cfg.temp_dir,conf);
 					if((ndx=fopen(str,"ab"))==NULL) {
 						fclose(qwk);
+						if(hdrs!=NULL)
+							fclose(hdrs);
 						if(personal)
 							fclose(personal);
 						smb_close(&smb);
@@ -435,7 +452,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 					else
 						mode&=~(QM_TAGLINE|QM_TO_QNET);
 
-					size=msgtoqwk(&msg,qwk,mode,usrsub[i][j],conf);
+					size=msgtoqwk(&msg,qwk,mode,usrsub[i][j],conf,hdrs);
 					smb_unlockmsghdr(&smb,&msg);
 
 					if(ndx) {
@@ -468,7 +485,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 					bprintf(text[QWKPackedSubboard],submsgs,(*msgcnt));
 				if(ndx) {
 					fclose(ndx);
-					sprintf(str,"%s%u.NDX",cfg.temp_dir,conf);
+					SAFEPRINTF2(str,"%s%u.NDX",cfg.temp_dir,conf);
 					if(!flength(str))
 						remove(str); 
 				}
@@ -497,7 +514,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 			,ftell(qwk)
 			,time(NULL)-start
 			,((*msgcnt)+mailmsgs)/(time(NULL)-start));
-		sprintf(str,"Packed %lu messages (%lu bytes) in %lu seconds (%lu msgs/sec)"
+		SAFEPRINTF4(str,"Packed %lu messages (%lu bytes) in %lu seconds (%lu msgs/sec)"
 			,(*msgcnt)+mailmsgs
 			,ftell(qwk)
 			,(ulong)(time(NULL)-start)
@@ -509,9 +526,11 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 	}
 
 	fclose(qwk);			/* close MESSAGE.DAT */
+	if(hdrs!=NULL)
+		fclose(hdrs);		/* close HEADERS.DAT */
 	if(personal) {
 		fclose(personal);		 /* close PERSONAL.NDX */
-		sprintf(str,"%sPERSONAL.NDX",cfg.temp_dir);
+		SAFEPRINTF(str,"%sPERSONAL.NDX",cfg.temp_dir);
 		if(!flength(str))
 			remove(str); 
 	}
@@ -521,13 +540,13 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		return(false);
 
 	if(/*!prepack && */ useron.rest&FLAG('Q')) { /* If QWK Net node, check for files */
-		sprintf(str,"%sqnet/%s.out/",cfg.data_dir,useron.alias);
+		SAFEPRINTF2(str,"%sqnet/%s.out/",cfg.data_dir,useron.alias);
 		dir=opendir(str);
 		while(dir!=NULL && (dirent=readdir(dir))!=NULL) {    /* Move files into temp dir */
-			sprintf(str,"%sqnet/%s.out/%s",cfg.data_dir,useron.alias,dirent->d_name);
+			SAFEPRINTF3(str,"%sqnet/%s.out/%s",cfg.data_dir,useron.alias,dirent->d_name);
 			if(isdir(str))
 				continue;
-			sprintf(tmp2,"%s%s",cfg.temp_dir,dirent->d_name);
+			SAFEPRINTF2(tmp2,"%s%s",cfg.temp_dir,dirent->d_name);
 			lncntr=0;	/* Default pause */
 			if(online==ON_LOCAL)
 				eprintf(LOG_INFO,"Including %s in packet",str);
@@ -562,11 +581,11 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 				for(i=0;i<batdn_total;i++) {
 					lncntr=0;
 					unpadfname(batdn_name[i],tmp);
-					sprintf(tmp2,"%s%s",cfg.temp_dir,tmp);
+					SAFEPRINTF2(tmp2,"%s%s",cfg.temp_dir,tmp);
 					if(!fexistcase(tmp2)) {
 						seqwait(cfg.dir[batdn_dir[i]]->seqdev);
 						bprintf(text[RetrievingFile],tmp);
-						sprintf(str,"%s%s"
+						SAFEPRINTF2(str,"%s%s"
 							,batdn_alt[i]>0 && batdn_alt[i]<=cfg.altpaths
 							? cfg.altpath[batdn_alt[i]-1]
 							: cfg.dir[batdn_dir[i]]->path
@@ -592,29 +611,29 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		/***********************/					/* packets */
 		/* Copy QWK Text files */
 		/***********************/
-		sprintf(str,"%sQWK/HELLO",cfg.text_dir);
+		SAFEPRINTF(str,"%sQWK/HELLO",cfg.text_dir);
 		if(fexistcase(str)) {
-			sprintf(tmp2,"%sHELLO",cfg.temp_dir);
+			SAFEPRINTF(tmp2,"%sHELLO",cfg.temp_dir);
 			mv(str,tmp2,1); 
 		}
-		sprintf(str,"%sQWK/BBSNEWS",cfg.text_dir);
+		SAFEPRINTF(str,"%sQWK/BBSNEWS",cfg.text_dir);
 		if(fexistcase(str)) {
-			sprintf(tmp2,"%sBBSNEWS",cfg.temp_dir);
+			SAFEPRINTF(tmp2,"%sBBSNEWS",cfg.temp_dir);
 			mv(str,tmp2,1); 
 		}
-		sprintf(str,"%sQWK/GOODBYE",cfg.text_dir);
+		SAFEPRINTF(str,"%sQWK/GOODBYE",cfg.text_dir);
 		if(fexistcase(str)) {
-			sprintf(tmp2,"%sGOODBYE",cfg.temp_dir);
+			SAFEPRINTF(tmp2,"%sGOODBYE",cfg.temp_dir);
 			mv(str,tmp2,1); 
 		}
-		sprintf(str,"%sQWK/BLT-*",cfg.text_dir);
+		SAFEPRINTF(str,"%sQWK/BLT-*",cfg.text_dir);
 		glob(str,0,NULL,&g);
 		for(i=0;i<(uint)g.gl_pathc;i++) { 			/* Copy BLT-*.* files */
 			fname=getfname(g.gl_pathv[i]);
 			padfname(fname,str);
 			if(isdigit(str[4]) && isdigit(str[9])) {
-				sprintf(str,"%sQWK/%s",cfg.text_dir,fname);
-				sprintf(tmp2,"%s%s",cfg.temp_dir,fname);
+				SAFEPRINTF2(str,"%sQWK/%s",cfg.text_dir,fname);
+				SAFEPRINTF2(tmp2,"%s%s",cfg.temp_dir,fname);
 				mv(str,tmp2,1); 
 			}
 		}
@@ -635,7 +654,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 	/*******************/
 	/* Compress Packet */
 	/*******************/
-	sprintf(tmp2,"%s%s",cfg.temp_dir,ALLFILES);
+	SAFEPRINTF2(tmp2,"%s%s",cfg.temp_dir,ALLFILES);
 	i=external(cmdstr(temp_cmd(),packet,tmp2,NULL)
 		,ex|EX_WILDCARD);
 	if(!fexist(packet)) {
@@ -651,8 +670,7 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 		return(true);
 
 	l=flength(packet);
-	sprintf(str,"%s.qwk",cfg.sys_id);
-	bprintf(text[FiFilename],str);
+	bprintf(text[FiFilename],getfname(packet));
 	bprintf(text[FiFileSize],ultoac(l,tmp));
 	if(l>0L && cur_cps)
 		i=l/(ulong)cur_cps;
@@ -666,12 +684,11 @@ bool sbbs_t::pack_qwk(char *packet, ulong *msgcnt, bool prepack)
 	}
 
 	if(useron.rest&FLAG('Q')) {
-		sprintf(str,"%s.qwk",cfg.sys_id);
 		dir=opendir(cfg.temp_dir);
 		while(dir!=NULL && (dirent=readdir(dir))!=NULL) {
-			if(!stricmp(str,dirent->d_name))	/* QWK packet */
+			if(!stricmp(getfname(packet),dirent->d_name))	/* QWK packet */
 				continue;
-			sprintf(tmp,"%s%s",cfg.temp_dir,dirent->d_name);
+			SAFEPRINTF2(tmp,"%s%s",cfg.temp_dir,dirent->d_name);
 			if(!isdir(tmp))
 				remove(tmp); 
 		}
diff --git a/src/sbbs3/pack_rep.cpp b/src/sbbs3/pack_rep.cpp
index 5fa16ccfcd462e12d54e7048b055f0652d9ff490..2f8dd0aed3224298a2f3378d2e1021c9a5206525 100644
--- a/src/sbbs3/pack_rep.cpp
+++ b/src/sbbs3/pack_rep.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 2005 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2008 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				*
@@ -44,26 +44,35 @@
 /****************************************************************************/
 bool sbbs_t::pack_rep(uint hubnum)
 {
-	char	str[MAX_PATH+1];
-	char 	tmp[MAX_PATH+1],tmp2[MAX_PATH+1];
-	int 	file,mode;
-	uint	i,j,k;
-	long	l,msgcnt,submsgs,packedmail,netfiles=0,deleted;
-	int32_t	posts;
-	int32_t	mailmsgs;
-	ulong	msgs;
+	char		str[MAX_PATH+1];
+	char 		tmp[MAX_PATH+1],tmp2[MAX_PATH+1];
+	char		hubid_upper[LEN_QWKID+1];
+	char		hubid_lower[LEN_QWKID+1];
+	int 		file,mode;
+	uint		i,j,k;
+	long		l,msgcnt,submsgs,packedmail,netfiles=0,deleted;
+	int32_t		posts;
+	int32_t		mailmsgs;
+	ulong		msgs;
 	uint32_t	last;
-	post_t	*post;
-	mail_t	*mail;
-	FILE*	rep;
-	DIR*	dir;
-	DIRENT*	dirent;
-	smbmsg_t msg;
+	post_t*		post;
+	mail_t*		mail;
+	FILE*		rep;
+	FILE*		hdrs=NULL;
+	DIR*		dir;
+	DIRENT*		dirent;
+	smbmsg_t	msg;
 
 	msgcnt=0L;
 	delfiles(cfg.temp_dir,ALLFILES);
-	sprintf(str,"%s%s.rep",cfg.data_dir,cfg.qhub[hubnum]->id);
-	if(fexist(str)) {
+
+	SAFECOPY(hubid_upper,cfg.qhub[hubnum]->id);
+	strupr(hubid_upper);
+	SAFECOPY(hubid_lower,cfg.qhub[hubnum]->id);
+	strlwr(hubid_lower);
+
+	SAFEPRINTF2(str,"%s%s.REP",cfg.data_dir,hubid_upper);
+	if(fexistcase(str)) {
 		eprintf(LOG_INFO,"Updating %s", str);
 		external(cmdstr(cfg.qhub[hubnum]->unpack,str,ALLFILES,NULL),EX_OFFLINE);
 	} else
@@ -71,26 +80,39 @@ bool sbbs_t::pack_rep(uint hubnum)
 	/*************************************************/
 	/* Create SYSID.MSG, write header and leave open */
 	/*************************************************/
-	sprintf(str,"%s%s.msg",cfg.temp_dir,cfg.qhub[hubnum]->id);
+	SAFEPRINTF2(str,"%s%s.MSG",cfg.temp_dir,hubid_upper);
+	fexistcase(str);
 	if((rep=fnopen(&file,str,O_CREAT|O_WRONLY|O_TRUNC))==NULL) {
 		errormsg(WHERE,ERR_CREATE,str,O_CREAT|O_WRONLY|O_TRUNC);
-		return(false); }
+		return(false);
+	}
 	if(filelength(file)<1) { 							/* New REP packet */
-		sprintf(str,"%-*s"
-			,QWK_BLOCK_LEN,cfg.qhub[hubnum]->id);		/* So write header */
+		SAFEPRINTF2(str,"%-*s"
+			,QWK_BLOCK_LEN,hubid_upper);		/* So write header */
 		fwrite(str,QWK_BLOCK_LEN,1,rep); 
 	}
 	fseek(rep,0L,SEEK_END);
+
+	/* Always includes HEADERS.DAT in .REP packets which are only for QWKnet hubs */
+	/* And *usually* a Synchronet system */
+	SAFEPRINTF(str,"%sHEADERS.DAT",cfg.temp_dir);
+	fexistcase(str);
+	if((hdrs=fopen(str,"a"))==NULL)
+		errormsg(WHERE,ERR_CREATE,str,0);
+
 	/*********************/
 	/* Pack new messages */
 	/*********************/
-	sprintf(smb.file,"%smail",cfg.data_dir);
+	SAFEPRINTF(smb.file,"%smail",cfg.data_dir);
 	smb.retry_time=cfg.smb_retry_time;
 	smb.subnum=INVALID_SUB;
 	if((i=smb_open(&smb))!=0) {
 		fclose(rep);
+		if(hdrs!=NULL)
+			fclose(hdrs);
 		errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
-		return(false); }
+		return(false); 
+	}
 
 	/***********************/
 	/* Pack E-mail, if any */
@@ -110,15 +132,16 @@ bool sbbs_t::pack_rep(uint hubnum)
 			if(!loadmsg(&msg,mail[l].number))
 				continue;
 
-			sprintf(str,"%s/",cfg.qhub[hubnum]->id);
+			SAFEPRINTF(str,"%s/",cfg.qhub[hubnum]->id);
 			if(msg.to_net.type!=NET_QWK
 				|| (strcmp((char *)msg.to_net.addr,cfg.qhub[hubnum]->id)
 				&& strncmp((char *)msg.to_net.addr,str,strlen(str)))) {
 				smb_unlockmsghdr(&smb,&msg);
 				smb_freemsgmem(&msg);
-				continue; }
+				continue; 
+			}
 
-			msgtoqwk(&msg,rep,QM_TO_QNET|QM_REP|A_LEAVE,INVALID_SUB,0);
+			msgtoqwk(&msg,rep,QM_TO_QNET|QM_REP|A_LEAVE,INVALID_SUB,0,hdrs);
 			packedmail++;
 			smb_unlockmsghdr(&smb,&msg);
 			smb_freemsgmem(&msg); 
@@ -137,19 +160,22 @@ bool sbbs_t::pack_rep(uint hubnum)
 		if(!msgs || last<=subscan[j].ptr) {
 			if(subscan[j].ptr>last) {
 				subscan[j].ptr=last;
-				subscan[j].last=last; }
+				subscan[j].last=last; 
+			}
 			eprintf(LOG_INFO,remove_ctrl_a(text[NScanStatusFmt],tmp)
 				,cfg.grp[cfg.sub[j]->grp]->sname
 				,cfg.sub[j]->lname,0L,msgs);
-			continue; }
+			continue; 
+		}
 
-		sprintf(smb.file,"%s%s"
+		SAFEPRINTF2(smb.file,"%s%s"
 			,cfg.sub[j]->data_dir,cfg.sub[j]->code);
 		smb.retry_time=cfg.smb_retry_time;
 		smb.subnum=j;
 		if((k=smb_open(&smb))!=0) {
 			errormsg(WHERE,ERR_OPEN,smb.file,k,smb.last_error);
-			continue; }
+			continue; 
+		}
 
 		post=loadposts(&posts,j,subscan[j].ptr,LP_BYSELF|LP_OTHERS|LP_PRIVATE|LP_REP);
 		eprintf(LOG_INFO,remove_ctrl_a(text[NScanStatusFmt],tmp)
@@ -157,7 +183,8 @@ bool sbbs_t::pack_rep(uint hubnum)
 			,cfg.sub[j]->lname,posts,msgs);
 		if(!posts)	{ /* no new messages */
 			smb_close(&smb);
-			continue; }
+			continue; 
+		}
 
 		subscan[j].ptr=last;                   /* set pointer */
 		eprintf(LOG_INFO,"%s",remove_ctrl_a(text[QWKPackingSubboard],tmp));	/* ptr to last msg	*/
@@ -174,20 +201,22 @@ bool sbbs_t::pack_rep(uint hubnum)
 				!(cfg.sub[j]->misc&SUB_GATE)) {
 				smb_freemsgmem(&msg);
 				smb_unlockmsghdr(&smb,&msg);
-				continue; }
+				continue; 
+			}
 
 			if(!strnicmp(msg.subj,"NE:",3) || (msg.from_net.type==NET_QWK &&
 				route_circ((char *)msg.from_net.addr,cfg.qhub[hubnum]->id))) {
 				smb_freemsgmem(&msg);
 				smb_unlockmsghdr(&smb,&msg);
-				continue; }
+				continue; 
+			}
 
 			mode=cfg.qhub[hubnum]->mode[i]|QM_TO_QNET|QM_REP;
 			if(mode&A_LEAVE) mode|=(QM_VIA|QM_TZ|QM_MSGID);
 			if(msg.from_net.type!=NET_QWK)
 				mode|=QM_TAGLINE;
 
-			msgtoqwk(&msg,rep,mode,j,cfg.qhub[hubnum]->conf[i]);
+			msgtoqwk(&msg,rep,mode,j,cfg.qhub[hubnum]->conf[i],hdrs);
 
 			smb_freemsgmem(&msg);
 			smb_unlockmsghdr(&smb,&msg);
@@ -202,18 +231,18 @@ bool sbbs_t::pack_rep(uint hubnum)
 		YIELD();	/* yield */
 	}
 
+	if(hdrs!=NULL)
+		fclose(hdrs);
 	fclose(rep);			/* close HUB_ID.MSG */
 	CRLF;
 							/* Look for extra files to send out */
-	sprintf(str,"%sqnet/%s.out",cfg.data_dir,cfg.qhub[hubnum]->id);
-	strlwr(str);
+	SAFEPRINTF2(str,"%sqnet/%s.out",cfg.data_dir,hubid_lower);
 	dir=opendir(str);
 	while(dir!=NULL && (dirent=readdir(dir))!=NULL) {
-		sprintf(str,"%sqnet/%s.out/%s",cfg.data_dir,cfg.qhub[hubnum]->id,dirent->d_name);
-		strlwr(str);
+		SAFEPRINTF3(str,"%sqnet/%s.out/%s",cfg.data_dir,hubid_lower,dirent->d_name);
 		if(isdir(str))
 			continue;
-		sprintf(tmp2,"%s%s",cfg.temp_dir,dirent->d_name);
+		SAFEPRINTF2(tmp2,"%s%s",cfg.temp_dir,dirent->d_name);
 		eprintf(LOG_INFO,remove_ctrl_a(text[RetrievingFile],tmp),str);
 		if(!mv(str,tmp2,1))
 			netfiles++;
@@ -231,28 +260,29 @@ bool sbbs_t::pack_rep(uint hubnum)
 	/*******************/
 	/* Compress Packet */
 	/*******************/
-	sprintf(str,"%s%s.rep",cfg.data_dir,cfg.qhub[hubnum]->id);
-	sprintf(tmp2,"%s%s",cfg.temp_dir,ALLFILES);
+	SAFEPRINTF2(str,"%s%s.REP",cfg.data_dir,hubid_upper);
+	SAFEPRINTF2(tmp2,"%s%s",cfg.temp_dir,ALLFILES);
 	i=external(cmdstr(cfg.qhub[hubnum]->pack,str,tmp2,NULL)
 		,EX_OFFLINE|EX_WILDCARD);
-	if(!fexist(str)) {
+	if(!fexistcase(str)) {
 		eprintf(LOG_WARNING,"%s",remove_ctrl_a(text[QWKCompressionFailed],tmp));
 		if(i)
 			errormsg(WHERE,ERR_EXEC,cmdstr(cfg.qhub[hubnum]->pack,str,tmp2,NULL),i);
 		else
 			errorlog("Couldn't compress REP packet");
-		return(false); }
-	sprintf(str,"%sqnet/%s.out/",cfg.data_dir,cfg.qhub[hubnum]->id);
-	strlwr(str);
+		return(false); 
+	}
+	SAFEPRINTF2(str,"%sqnet/%s.out/",cfg.data_dir,hubid_lower);
 	delfiles(str,ALLFILES);
 
 	if(packedmail) {						/* Delete NetMail */
-		sprintf(smb.file,"%smail",cfg.data_dir);
+		SAFEPRINTF(smb.file,"%smail",cfg.data_dir);
 		smb.retry_time=cfg.smb_retry_time;
 		smb.subnum=INVALID_SUB;
 		if((i=smb_open(&smb))!=0) {
 			errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error);
-			return(true); }
+			return(true); 
+		}
 
 		mail=loadmail(&smb,&mailmsgs,0,MAIL_YOUR,0);
 
@@ -261,14 +291,16 @@ bool sbbs_t::pack_rep(uint hubnum)
 				free(mail);
 			smb_close(&smb);
 			errormsg(WHERE,ERR_LOCK,smb.file,i,smb.last_error);	/* messes with the index */
-			return(true); }
+			return(true); 
+		}
 
 		if((i=smb_getstatus(&smb))!=0) {
 			if(mailmsgs)
 				free(mail);
 			smb_close(&smb);
 			errormsg(WHERE,ERR_READ,smb.file,i,smb.last_error);
-			return(true); }
+			return(true); 
+		}
 
 		deleted=0;
 		/* Mark as READ and DELETE */
@@ -280,13 +312,14 @@ bool sbbs_t::pack_rep(uint hubnum)
 			if(!loadmsg(&msg,mail[l].number))
 				continue;
 
-			sprintf(str,"%s/",cfg.qhub[hubnum]->id);
+			SAFEPRINTF(str,"%s/",cfg.qhub[hubnum]->id);
 			if(msg.to_net.type!=NET_QWK
 				|| (strcmp((char *)msg.to_net.addr,cfg.qhub[hubnum]->id)
 				&& strncmp((char *)msg.to_net.addr,str,strlen(str)))) {
 				smb_unlockmsghdr(&smb,&msg);
 				smb_freemsgmem(&msg);
-				continue; }
+				continue; 
+			}
 
 			msg.hdr.attr|=MSG_DELETE;
 			msg.idx.attr=msg.hdr.attr;
diff --git a/src/sbbs3/qwk.cpp b/src/sbbs3/qwk.cpp
index 341b8a4ba056a085843340dead4926d8b52bb6ad..d29de6d447f0e752bfd6c5d12686d30842d806be 100644
--- a/src/sbbs3/qwk.cpp
+++ b/src/sbbs3/qwk.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 2005 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2008 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				*
@@ -475,6 +475,9 @@ void sbbs_t::qwk_sec()
 				bprintf("\1hC\1n) %-30s: \1h%s\r\n"
 					,"Include Control Files"
 					,useron.qwk&QWK_NOCTRL ? text[No]:text[Yes]);
+				bprintf("\1hH\1n) %-30s: \1h%s\r\n"
+					,"Include HEADERS.DAT"
+					,useron.qwk&QWK_HEADERS ? text[Yes]:text[No]);
 				bprintf("\1hY\1n) %-30s: \1h%s\r\n"
 					,"Include Messages from You"
 					,useron.qwk&QWK_BYSELF ? text[Yes]:text[No]);
@@ -491,7 +494,7 @@ void sbbs_t::qwk_sec()
 					,"Extended (QWKE) Packet Format"
 					,useron.qwk&QWK_EXT ? text[Yes]:text[No]);
 				bputs(text[UserDefaultsWhich]);
-				ch=(char)getkeys("AEDFIOQTYMNCXZV",0);
+				ch=(char)getkeys("AEDFHIOQTYMNCXZV",0);
 				if(sys_status&SS_ABORT || !ch || ch=='Q' || !online)
 					break;
 				switch(ch) {
@@ -521,6 +524,9 @@ void sbbs_t::qwk_sec()
 						else
 							useron.qwk&=~(QWK_EMAIL|QWK_ALLMAIL);
 						break;
+					case 'H':
+						useron.qwk^=QWK_HEADERS;
+						break;
 					case 'I':
 						useron.qwk^=QWK_ATTACH;
 						break;
diff --git a/src/sbbs3/readmsgs.cpp b/src/sbbs3/readmsgs.cpp
index 5a61d1ac84e31fc3cc5b9c769b3baa1134ec4b51..e07b30badda2bd23b3943342f62ca4107e16f886 100644
--- a/src/sbbs3/readmsgs.cpp
+++ b/src/sbbs3/readmsgs.cpp
@@ -99,6 +99,10 @@ char *binstr(uchar *buf, ushort length, char* str)
 	for(i=0;i<length;i++) {
 		sprintf(tmp,"%02X ",buf[i]);
 		strcat(str,tmp); 
+		if(i >= 100) {
+			strcat(str,"...");
+			break;
+		}
 	}
 	return(str);
 }
@@ -118,9 +122,11 @@ void sbbs_t::msghdr(smbmsg_t* msg)
 			,binstr((uchar *)msg->hfield_dat[i],msg->hfield[i].length,str));
 
 	/* fixed fields */
-	bprintf("%-16.16s %s %s\r\n","when_written"	
+	bprintf("%-16.16s %08lX %04hX %s %s\r\n","when_written"	
+		,msg->hdr.when_written.time, msg->hdr.when_written.zone
 		,timestr(msg->hdr.when_written.time), smb_zonestr(msg->hdr.when_written.zone,NULL));
-	bprintf("%-16.16s %s %s\r\n","when_imported"	
+	bprintf("%-16.16s %08lX %04hX %s %s\r\n","when_imported"	
+		,msg->hdr.when_imported.time, msg->hdr.when_imported.zone
 		,timestr(msg->hdr.when_imported.time), smb_zonestr(msg->hdr.when_imported.zone,NULL));
 	bprintf("%-16.16s %04Xh\r\n","type"				,msg->hdr.type);
 	bprintf("%-16.16s %04Xh\r\n","version"			,msg->hdr.version);
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index f7442078f5682bb0a3efc8ae3ccfb0e18cd56bd2..f5359ea3cc45de8d1bf13eb9e748e0d4f861bebf 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 2007 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2008 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				*
@@ -132,9 +132,11 @@ extern int	thread_suid_broken;			/* NPTL is no longer broken */
 #include "semfile.h"
 #include "dirwrap.h"
 #include "filewrap.h"
+#include "datewrap.h"
 #include "sockwrap.h"
 #include "link_list.h"
 #include "msg_queue.h"
+#include "xpdatetime.h"
 
 #include "smblib.h"
 #include "ars_defs.h"
@@ -730,7 +732,7 @@ public:
 	bool	unpack_rep(char* repfile=NULL);
 
 	/* msgtoqwk.cpp */
-	ulong	msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum, int conf);
+	ulong	msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, int subnum, int conf, FILE* hdrs_dat);
 
 	/* qwktomsg.cpp */
 	bool	qwktomsg(FILE *qwk_fp, char *hdrblk, char fromhub, uint subnum
diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index d1c2d5d87f61d102c2b63408394c399db99e272b..44ee0ed041140a19d2bbc092c05e3e498d0018df 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -407,6 +407,7 @@ typedef enum {						/* Values for xtrn_t.event				*/
 #define QWK_NOCTRL	(1L<<12)		/* No extraneous control files			*/
 #define QWK_EXT		(1L<<13)		/* QWK Extended (QWKE) format			*/
 #define QWK_MSGID	(1L<<14)		/* Include "@MSGID" in msgs				*/
+#define QWK_HEADERS	(1L<<16)		/* Include HEADERS.DAT file				*/
 
 #define QWK_DEFAULT	(QWK_FILES|QWK_ATTACH|QWK_EMAIL|QWK_DELMAIL)
 																			
diff --git a/src/sbbs3/un_rep.cpp b/src/sbbs3/un_rep.cpp
index 133e9d72860f648d69684b42dbe88d4663e7754f..160f51b001fa2d329bf771772afaf4e1efc64c0a 100644
--- a/src/sbbs3/un_rep.cpp
+++ b/src/sbbs3/un_rep.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 2007 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2008 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				*
@@ -45,6 +45,9 @@ bool sbbs_t::unpack_rep(char* repfile)
 {
 	char	str[MAX_PATH+1],fname[MAX_PATH+1]
 			,*AttemptedToUploadREPpacket="Attempted to upload REP packet";
+	char	rep_fname[MAX_PATH+1];
+	char	msg_fname[MAX_PATH+1];
+	char	hdrs_fname[MAX_PATH+1];
 	char 	tmp[512];
 	char	from[26];
 	char	to[26];
@@ -57,18 +60,19 @@ bool sbbs_t::unpack_rep(char* repfile)
 	ulong	ex;
 	node_t	node;
 	FILE*	rep;
+	FILE*	hdrs=NULL;
 	DIR*	dir;
 	DIRENT*	dirent;
 	BOOL	twit_list;
 
-	sprintf(fname,"%stwitlist.cfg",cfg.ctrl_dir);
+	SAFEPRINTF(fname,"%stwitlist.cfg",cfg.ctrl_dir);
 	twit_list=fexist(fname);
 
 	if(repfile!=NULL)
-		strcpy(str,repfile);
+		SAFECOPY(rep_fname,repfile);
 	else
-		sprintf(str,"%s%s.rep",cfg.temp_dir,cfg.sys_id);
-	if(!fexistcase(str)) {
+		SAFEPRINTF2(rep_fname,"%s%s.rep",cfg.temp_dir,cfg.sys_id);
+	if(!fexistcase(rep_fname)) {
 		bputs(text[QWKReplyNotReceived]);
 		logline("U!",AttemptedToUploadREPpacket);
 		logline(nulstr,"REP file not received");
@@ -82,27 +86,40 @@ bool sbbs_t::unpack_rep(char* repfile)
 	ex=EX_OUTL|EX_OUTR;
 	if(online!=ON_REMOTE)
 		ex|=EX_OFFLINE;
-	i=external(cmdstr(cfg.fextr[k]->cmd,str,ALLFILES,NULL),ex);
+	i=external(cmdstr(cfg.fextr[k]->cmd,rep_fname,ALLFILES,NULL),ex);
 	if(i) {
 		bputs(text[QWKExtractionFailed]);
 		logline("U!",AttemptedToUploadREPpacket);
 		logline(nulstr,"Extraction failed");
 		return(false); 
 	}
-	sprintf(str,"%s%s.msg",cfg.temp_dir,cfg.sys_id);
-	if(!fexistcase(str)) {
+	SAFEPRINTF(hdrs_fname,"%sHEADERS.DAT",cfg.temp_dir);
+	if(fexistcase(hdrs_fname)) {
+		/* If we receive a headers.dat from a QWKnet node, we know the hubs supports it */
+		/* So turn on headers.dat in subsequent .QWK files */
+		if(!(useron.qwk&QWK_HEADERS)) {
+			useron.qwk|=QWK_HEADERS;
+			putuserrec(&cfg,useron.number,U_QWK,8,ultoa(useron.qwk,str,16));
+		}
+		if((hdrs=fopen(hdrs_fname,"r")) == NULL)
+			errormsg(WHERE,ERR_OPEN,hdrs_fname,0);
+	}
+	SAFEPRINTF2(msg_fname,"%s%s.msg",cfg.temp_dir,cfg.sys_id);
+	if(!fexistcase(msg_fname)) {
 		bputs(text[QWKReplyNotReceived]);
 		logline("U!",AttemptedToUploadREPpacket);
 		logline(nulstr,"MSG file not received");
 		return(false); 
 	}
-	if((rep=fnopen(&file,str,O_RDONLY))==NULL) {
-		errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
+	if((rep=fnopen(&file,msg_fname,O_RDONLY))==NULL) {
+		errormsg(WHERE,ERR_OPEN,msg_fname,O_RDONLY);
 		return(false); 
 	}
 	size=filelength(file);
 	fread(block,QWK_BLOCK_LEN,1,rep);
 	if(strnicmp((char *)block,cfg.sys_id,strlen(cfg.sys_id))) {
+		if(hdrs!=NULL)
+			fclose(hdrs);
 		fclose(rep);
 		bputs(text[QWKReplyNotReceived]);
 		logline("U!",AttemptedToUploadREPpacket);
@@ -123,19 +140,17 @@ bool sbbs_t::unpack_rep(char* repfile)
 
 		lncntr=0;					/* defeat pause */
 		if(fseek(rep,l,SEEK_SET)!=0) {
-			sprintf(str,"%s.msg", cfg.sys_id);
-			errormsg(WHERE,ERR_SEEK,str,l);
+			errormsg(WHERE,ERR_SEEK,msg_fname,l);
 			break;
 		}
 		if(fread(block,1,QWK_BLOCK_LEN,rep)!=QWK_BLOCK_LEN) {
-			sprintf(str,"%s.msg", cfg.sys_id);
-			errormsg(WHERE,ERR_READ,str,ftell(rep));
+			errormsg(WHERE,ERR_READ,msg_fname,ftell(rep));
 			break;
 		}
 		sprintf(tmp,"%.6s",block+116);
 		i=atoi(tmp);  /* i = number of blocks */
 		if(i<2) {
-			sprintf(str,"%s.msg blocks (read '%s' at offset %ld)", cfg.sys_id, tmp, l);
+			SAFEPRINTF3(str,"%s blocks (read '%s' at offset %ld)", msg_fname, tmp, l);
 			errormsg(WHERE,ERR_CHK,str,i);
 			i=1;
 			continue; 
@@ -188,7 +203,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 				continue; 
 			}
 
-			sprintf(smb.file,"%smail",cfg.data_dir);
+			SAFEPRINTF(smb.file,"%smail",cfg.data_dir);
 			smb.retry_time=cfg.smb_retry_time;
 
 			if(lastsub!=INVALID_SUB) {
@@ -250,7 +265,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 			putuserrec(&cfg,useron.number,U_ETODAY,5
 				,ultoa(useron.etoday,tmp,10));
 			bprintf(text[Emailed],username(&cfg,j,tmp),j);
-			sprintf(str,"%s sent e-mail to %s #%d"
+			SAFEPRINTF3(str,"%s sent e-mail to %s #%d"
 				,useron.alias,username(&cfg,j,tmp),j);
 			logline("E+",str);
 			if(useron.rest&FLAG('Q')) {
@@ -258,20 +273,20 @@ bool sbbs_t::unpack_rep(char* repfile)
 				truncsp(tmp); 
 			}
 			else
-				strcpy(tmp,useron.alias);
+				SAFECOPY(tmp,useron.alias);
 			for(k=1;k<=cfg.sys_nodes;k++) { /* Tell user, if online */
 				getnodedat(k,&node,0);
 				if(node.useron==j && !(node.misc&NODE_POFF)
 					&& (node.status==NODE_INUSE
 					|| node.status==NODE_QUIET)) {
-					sprintf(str,text[EmailNodeMsg]
+					SAFEPRINTF2(str,text[EmailNodeMsg]
 						,cfg.node_num,tmp);
 					putnmsg(&cfg,k,str);
 					break; 
 				} 
 			}
 			if(k>cfg.sys_nodes) {
-				sprintf(str,text[UserSentYouMail],tmp);
+				SAFEPRINTF(str,text[UserSentYouMail],tmp);
 				putsmsg(&cfg,j,str); 
 			} 
 		}    /* end of email */
@@ -301,7 +316,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 				k--;	/* k is sub */
 				if(j>=usrgrps || k>=usrsubs[j] || cfg.sub[usrsub[j][k]]->qwkconf) {
 					bprintf(text[QWKInvalidConferenceN],n);
-					sprintf(str,"%s: Invalid conference number %lu",useron.alias,n);
+					SAFEPRINTF2(str,"%s: Invalid conference number %lu",useron.alias,n);
 					logline("P!",str);
 					continue; 
 				} 
@@ -387,14 +402,14 @@ bool sbbs_t::unpack_rep(char* repfile)
 
 			/* TWIT FILTER */
 			if(twit_list) {
-				sprintf(fname,"%stwitlist.cfg",cfg.ctrl_dir);
+				SAFEPRINTF(fname,"%stwitlist.cfg",cfg.ctrl_dir);
 				sprintf(from,"%25.25s",block+46);	/* From user */
 				truncsp(from);
 				sprintf(to,"%25.25s",block+21);		/* To user */
 				truncsp(to);
 
 				if(findstr(from,fname) || findstr(to,fname)) {
-					sprintf(str,"Filtering post from %s to %s on %s %s"
+					SAFEPRINTF4(str,"Filtering post from %s to %s on %s %s"
 						,from
 						,to
 						,cfg.grp[cfg.sub[n]->grp]->sname,cfg.sub[n]->lname);
@@ -407,7 +422,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 				if(lastsub!=INVALID_SUB)
 					smb_close(&smb);
 				lastsub=INVALID_SUB;
-				sprintf(smb.file,"%s%s",cfg.sub[n]->data_dir,cfg.sub[n]->code);
+				SAFEPRINTF2(smb.file,"%s%s",cfg.sub[n]->data_dir,cfg.sub[n]->code);
 				smb.retry_time=cfg.smb_retry_time;
 				smb.subnum=n;
 				if((j=smb_open(&smb))!=0) {
@@ -451,7 +466,7 @@ bool sbbs_t::unpack_rep(char* repfile)
 			user_posted_msg(&cfg, &useron, 1);
 			bprintf(text[Posted],cfg.grp[cfg.sub[n]->grp]->sname
 				,cfg.sub[n]->lname);
-			sprintf(str,"%s posted on %s %s"
+			SAFEPRINTF3(str,"%s posted on %s %s"
 				,useron.alias,cfg.grp[cfg.sub[n]->grp]->sname,cfg.sub[n]->lname);
 			signal_sub_sem(&cfg,n);
 			logline("P+",str); 
@@ -464,39 +479,41 @@ bool sbbs_t::unpack_rep(char* repfile)
 
 	if(lastsub!=INVALID_SUB)
 		smb_close(&smb);
+	if(hdrs!=NULL)
+		fclose(hdrs);
 	fclose(rep);
 
 	if(useron.rest&FLAG('Q')) {             /* QWK Net Node */
-		sprintf(str,"%s%s.msg",cfg.temp_dir,cfg.sys_id);
-		if(fexistcase(str))
-			remove(str);
-		sprintf(str,"%s%s.rep",cfg.temp_dir,cfg.sys_id);
-		if(fexistcase(str))
-			remove(str);
-		sprintf(str,"%sATTXREF.DAT",cfg.temp_dir);
-		if(fexistcase(str))
-			remove(str);
+		if(fexistcase(msg_fname))
+			remove(msg_fname);
+		if(fexistcase(rep_fname))
+			remove(rep_fname);
+		if(fexistcase(hdrs_fname))
+			remove(hdrs_fname);
+		SAFEPRINTF(fname,"%sATTXREF.DAT",cfg.temp_dir);
+		if(fexistcase(fname))
+			remove(fname);
 
 		dir=opendir(cfg.temp_dir);
 		while(dir!=NULL && (dirent=readdir(dir))!=NULL) {				/* Extra files */
 			// Move files
-			sprintf(str,"%s%s",cfg.temp_dir,dirent->d_name);
+			SAFEPRINTF2(str,"%s%s",cfg.temp_dir,dirent->d_name);
 			if(isdir(str))
 				continue;
 
 			// Create directory if necessary
-			sprintf(inbox,"%sqnet/%s.in",cfg.data_dir,useron.alias);
+			SAFEPRINTF2(inbox,"%sqnet/%s.in",cfg.data_dir,useron.alias);
 			MKDIR(inbox); 
 
-			sprintf(fname,"%s/%s",inbox,dirent->d_name);
+			SAFEPRINTF2(fname,"%s/%s",inbox,dirent->d_name);
 			mv(str,fname,1);
-			sprintf(str,text[ReceivedFileViaQWK],dirent->d_name,useron.alias);
+			SAFEPRINTF2(str,text[ReceivedFileViaQWK],dirent->d_name,useron.alias);
 			putsmsg(&cfg,1,str);
 		} 
 		if(dir!=NULL)
 			closedir(dir);
-		sprintf(str,"%sqnet-rep.now",cfg.data_dir);
-		ftouch(str);
+		SAFEPRINTF(fname,"%sqnet-rep.now",cfg.data_dir);
+		ftouch(fname);
 	}
 
 	bputs(text[QWKUnpacked]);