From 9f3482e40cfe2e0ee379aaeb130f931433ca7a3f Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Sat, 11 Sep 2004 09:27:44 +0000
Subject: [PATCH] Split smblib.c into multiple smaller modules to make source
 maintenance easier and allow for selective linking of objects from library
 archives.

---
 src/smblib/smbadd.c      |  302 ++++++++
 src/smblib/smballoc.c    |  476 +++++++++++++
 src/smblib/smbfile.c     |  201 ++++++
 src/smblib/smbhash.c     |  348 +++++++++
 src/smblib/smblib.c      | 1460 ++------------------------------------
 src/smblib/smblib.dsp    |   20 +
 src/smblib/smblib.h      |   46 +-
 src/smblib/smblib_mt.dsp |   20 +
 src/smblib/smbstr.c      |  245 +++++++
 9 files changed, 1681 insertions(+), 1437 deletions(-)
 create mode 100644 src/smblib/smbadd.c
 create mode 100644 src/smblib/smballoc.c
 create mode 100644 src/smblib/smbfile.c
 create mode 100644 src/smblib/smbhash.c
 create mode 100644 src/smblib/smbstr.c

diff --git a/src/smblib/smbadd.c b/src/smblib/smbadd.c
new file mode 100644
index 0000000000..6ed89fc07c
--- /dev/null
+++ b/src/smblib/smbadd.c
@@ -0,0 +1,302 @@
+/* smbadd.c */
+
+/* Synchronet message base (SMB) high-level "add message" function */
+
+/* $Id$ */
+
+/****************************************************************************
+ * @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		*
+ *																			*
+ * This library is free software; you can redistribute it and/or			*
+ * modify it under the terms of the GNU Lesser General Public License		*
+ * as published by the Free Software Foundation; either version 2			*
+ * of the License, or (at your option) any later version.					*
+ * See the GNU Lesser General Public License for more details: lgpl.txt or	*
+ * http://www.fsf.org/copyleft/lesser.html									*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#include <time.h>
+#include "smblib.h"
+#include "genwrap.h"
+#include "crc32.h"
+
+/****************************************************************************/
+/****************************************************************************/
+int SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, BOOL dupechk
+					   ,ushort xlat, const uchar* body, const uchar* tail)
+{
+	uchar*		lzhbuf=NULL;
+	long		lzhlen;
+	int			retval;
+	size_t		n;
+	size_t		l,length;
+	size_t		taillen=0;
+	size_t		bodylen=0;
+	long		offset;
+	ulong		crc=0xffffffff;
+	hash_t		found;
+	hash_t**	hashes=NULL;	/* This is a NULL-terminated list of hashes */
+	smbmsg_t	remsg;
+
+	if(!SMB_IS_OPEN(smb)) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+
+	if(filelength(fileno(smb->shd_fp))<1) {	 /* Create it if it doesn't exist */
+		/* smb->status.max_crcs, max_msgs, max_age, and attr should be pre-initialized */
+		if((retval=smb_create(smb))!=SMB_SUCCESS) 
+			return(retval);
+	}
+
+	if(!smb->locked && smb_locksmbhdr(smb)!=SMB_SUCCESS)
+		return(SMB_ERR_LOCK);
+
+	msg->hdr.total_dfields = 0;
+
+	/* try */
+	do {
+
+		if((retval=smb_getstatus(smb))!=SMB_SUCCESS)
+			break;
+
+		msg->hdr.number=smb->status.last_msg+1;
+
+		hashes=smb_msghashes(msg,body,dupechk);
+
+		if(smb_findhash(smb, hashes, &found, /* update? */FALSE)==SMB_SUCCESS) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"duplicate %s hash found (message #%lu)"
+				,smb_hashsource(found.source), found.number);
+			retval=SMB_DUPE_MSG;
+			break;
+		}
+
+		if(tail!=NULL && (taillen=strlen(tail))>0)
+			taillen+=sizeof(xlat);	/* xlat string terminator */
+
+		if(body!=NULL && (bodylen=strlen(body))>0) {
+
+			/* Remove white-space from end of message text */
+			while(bodylen && body[bodylen-1]<=' ')
+				bodylen--;
+
+			/* Calculate CRC-32 of message text (before encoding, if any) */
+			if(smb->status.max_crcs && dupechk) {
+				for(l=0;l<bodylen;l++)
+					crc=ucrc32(body[l],crc); 
+				crc=~crc;
+
+				/* Add CRC to CRC history (and check for duplicates) */
+				if((retval=smb_addcrc(smb,crc))!=SMB_SUCCESS)
+					break;
+			}
+
+			bodylen+=sizeof(xlat);	/* xlat string terminator */
+
+			/* LZH compress? */
+			if(xlat==XLAT_LZH && bodylen+taillen>=SDT_BLOCK_LEN
+				&& (lzhbuf=(uchar *)malloc(bodylen*2))!=NULL) {
+				lzhlen=lzh_encode((uchar*)body,bodylen-sizeof(xlat),lzhbuf);
+				if(lzhlen>1
+					&& smb_datblocks(lzhlen+(sizeof(xlat)*2)+taillen) 
+						< smb_datblocks(bodylen+taillen)) {
+					bodylen=lzhlen+(sizeof(xlat)*2); 	/* Compressible */
+					body=lzhbuf; 
+				} else
+					xlat=XLAT_NONE;
+			} else
+				xlat=XLAT_NONE;
+		}
+
+		length=bodylen+taillen;
+
+		if(length&0x80000000) {
+			sprintf(smb->last_error,"message length: 0x%lX",length);
+			retval=SMB_ERR_DAT_LEN;
+			break;
+		}
+
+		/* Allocate Data Blocks */
+		if(smb->status.attr&SMB_HYPERALLOC) {
+			offset=smb_hallocdat(smb);
+			storage=SMB_HYPERALLOC; 
+		} else {
+			if((retval=smb_open_da(smb))!=SMB_SUCCESS)
+				break;
+			if(storage==SMB_FASTALLOC)
+				offset=smb_fallocdat(smb,length,1);
+			else { /* SMB_SELFPACK */
+				offset=smb_allocdat(smb,length,1);
+				storage=SMB_SELFPACK; 
+			}
+			smb_close_da(smb); 
+		}
+
+		if(offset<0) {
+			retval=offset;
+			break;
+		}
+		msg->hdr.offset=offset;
+
+		smb_fseek(smb->sdt_fp,offset,SEEK_SET);
+
+		if(bodylen) {
+			if((retval=smb_dfield(msg,TEXT_BODY,bodylen))!=SMB_SUCCESS)
+				break;
+
+			if(xlat!=XLAT_NONE) {	/* e.g. XLAT_LZH */
+				if(smb_fwrite(smb,&xlat,sizeof(xlat),smb->sdt_fp)!=sizeof(xlat)) {
+					safe_snprintf(smb->last_error,sizeof(smb->last_error)
+						,"%d (%s) writing body xlat string"
+						,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
+					retval=SMB_ERR_WRITE;
+					break;
+				}
+				bodylen-=sizeof(xlat);
+			}
+			xlat=XLAT_NONE;	/* xlat string terminator */
+			if(smb_fwrite(smb,&xlat,sizeof(xlat),smb->sdt_fp)!=sizeof(xlat)) {
+				safe_snprintf(smb->last_error,sizeof(smb->last_error)
+					,"%d (%s) writing body xlat terminator"
+					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
+				retval=SMB_ERR_WRITE;
+				break;
+			}
+			bodylen-=sizeof(xlat);
+
+			if(smb_fwrite(smb,body,bodylen,smb->sdt_fp)!=bodylen) {
+				safe_snprintf(smb->last_error,sizeof(smb->last_error)
+					,"%d (%s) writing body (%ld bytes)"
+					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp))
+					,bodylen);
+				retval=SMB_ERR_WRITE;
+				break;
+			}
+		}
+
+		if(taillen) {
+			if((retval=smb_dfield(msg,TEXT_TAIL,taillen))!=SMB_SUCCESS)
+				break;
+
+			xlat=XLAT_NONE;	/* xlat string terminator */
+			if(smb_fwrite(smb,&xlat,sizeof(xlat),smb->sdt_fp)!=sizeof(xlat)) {
+				safe_snprintf(smb->last_error,sizeof(smb->last_error)
+					,"%d (%s) writing tail xlat terminator"
+					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
+				retval=SMB_ERR_WRITE;
+				break;
+			}
+
+			if(smb_fwrite(smb,tail,taillen-sizeof(xlat),smb->sdt_fp)!=taillen-sizeof(xlat)) {
+				safe_snprintf(smb->last_error,sizeof(smb->last_error)
+					,"%d (%s) writing tail (%ld bytes)"
+					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp))
+					,taillen-sizeof(xlat));
+				retval=SMB_ERR_WRITE;
+				break;
+			}
+		}
+
+		for(l=length;l%SDT_BLOCK_LEN;l++) {
+			if(smb_fputc(0,smb->sdt_fp)!=0)
+				break;
+		}
+		if(l%SDT_BLOCK_LEN) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) writing data padding"
+				,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
+			retval=SMB_ERR_WRITE;
+			break;
+		}
+
+		fflush(smb->sdt_fp);
+
+		if(msg->to==NULL)	/* no recipient, don't add header (required for bulkmail) */
+			break;
+
+		msg->hdr.version=smb_ver();
+		if(msg->hdr.when_imported.time==0) {
+			msg->hdr.when_imported.time=time(NULL);
+			msg->hdr.when_imported.zone=0;	/* how do we detect system TZ? */
+		}
+		if(msg->hdr.when_written.time==0)	/* Uninitialized */
+			msg->hdr.when_written = msg->hdr.when_imported;
+		msg->idx.time=msg->hdr.when_imported.time;
+
+		/* Look-up thread_back if Reply-ID was specified */
+		if(msg->hdr.thread_back==0 && msg->reply_id!=NULL) {
+			if(smb_getmsgidx_by_msgid(smb,&remsg,msg->reply_id)==SMB_SUCCESS)
+				msg->hdr.thread_back=remsg.idx.number;	/* needed for threading backward */
+		}
+
+		/* Auto-thread linkage */
+		if(msg->hdr.thread_back) {
+			memset(&remsg,0,sizeof(remsg));
+			remsg.hdr.number=msg->hdr.thread_back;
+			if((retval=smb_getmsgidx(smb, &remsg))!=SMB_SUCCESS)	/* invalid thread origin */
+				break;
+
+			if((retval=smb_lockmsghdr(smb,&remsg))!=SMB_SUCCESS)
+				break;
+
+			if((retval=smb_getmsghdr(smb, &remsg))!=SMB_SUCCESS) {
+				smb_unlockmsghdr(smb, &remsg); 
+				break;
+			}
+
+			/* Add RFC-822 Reply-ID if original message has RFC Message-ID */
+			if(msg->reply_id==NULL && remsg.id!=NULL
+				&& (retval=smb_hfield_str(msg,RFC822REPLYID,remsg.id))!=SMB_SUCCESS)
+				break;
+
+			/* Add FidoNet Reply if original message has FidoNet MSGID */
+			if(msg->ftn_reply==NULL && remsg.ftn_msgid!=NULL
+				&& (retval=smb_hfield_str(msg,FIDOREPLYID,remsg.ftn_msgid))!=SMB_SUCCESS)
+				break;
+
+			retval=smb_updatethread(smb, &remsg, msg->hdr.number);
+			smb_unlockmsghdr(smb, &remsg);
+			smb_freemsgmem(&remsg);
+
+			if(retval!=SMB_SUCCESS)
+				break;
+		}
+
+		if(smb_addhashes(smb,hashes,/* skip_marked? */FALSE)==SMB_SUCCESS)
+			msg->flags|=MSG_FLAG_HASHED;
+
+		retval=smb_addmsghdr(smb,msg,storage); // calls smb_unlocksmbhdr() 
+
+	} while(0);
+	/* finally */
+
+	if(retval!=SMB_SUCCESS)
+		smb_freemsg_dfields(smb,msg,1);
+
+	smb_unlocksmbhdr(smb);
+	FREE_AND_NULL(lzhbuf);
+	FREE_LIST(hashes,n);
+
+	return(retval);
+}
diff --git a/src/smblib/smballoc.c b/src/smblib/smballoc.c
new file mode 100644
index 0000000000..8e14c9c498
--- /dev/null
+++ b/src/smblib/smballoc.c
@@ -0,0 +1,476 @@
+/* smballoc.c */
+
+/* Synchronet message base (SMB) alloc/free routines */
+
+/* $Id$ */
+
+/****************************************************************************
+ * @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		*
+ *																			*
+ * This library is free software; you can redistribute it and/or			*
+ * modify it under the terms of the GNU Lesser General Public License		*
+ * as published by the Free Software Foundation; either version 2			*
+ * of the License, or (at your option) any later version.					*
+ * See the GNU Lesser General Public License for more details: lgpl.txt or	*
+ * http://www.fsf.org/copyleft/lesser.html									*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#include "smblib.h"
+#include "genwrap.h"
+
+/****************************************************************************/
+/* Finds unused space in data file based on block allocation table and		*/
+/* marks space as used in allocation table.                                 */
+/* File must be opened read/write DENY ALL									*/
+/* Returns offset to beginning of data (in bytes, not blocks)				*/
+/* Assumes smb_open_da() has been called									*/
+/* smb_close_da() should be called after									*/
+/* Returns negative on error												*/
+/****************************************************************************/
+long SMBCALL smb_allocdat(smb_t* smb, ulong length, ushort refs)
+{
+    ushort  i;
+	ulong	j,l,blocks,offset=0L;
+
+	if(smb->sda_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	blocks=smb_datblocks(length);
+	j=0;	/* j is consecutive unused block counter */
+	fflush(smb->sda_fp);
+	rewind(smb->sda_fp);
+	while(!feof(smb->sda_fp) && (long)offset>=0) {
+		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i))
+			break;
+		offset+=SDT_BLOCK_LEN;
+		if(!i) j++;
+		else   j=0;
+		if(j==blocks) {
+			offset-=(blocks*SDT_BLOCK_LEN);
+			break; 
+		} 
+	}
+	if((long)offset<0) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"invalid data offset: %lu",offset);
+		return(SMB_ERR_DAT_OFFSET);
+	}
+	clearerr(smb->sda_fp);
+	if(fseek(smb->sda_fp,(offset/SDT_BLOCK_LEN)*sizeof(refs),SEEK_SET)) {
+		return(SMB_ERR_SEEK);
+	}
+	for(l=0;l<blocks;l++)
+		if(!fwrite(&refs,sizeof(refs),1,smb->sda_fp)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) writing allocation bytes at offset %ld"
+				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
+				,((offset/SDT_BLOCK_LEN)+l)*sizeof(refs));
+			return(SMB_ERR_WRITE);
+		}
+	fflush(smb->sda_fp);
+	return(offset);
+}
+
+/****************************************************************************/
+/* Allocates space for data, but doesn't search for unused blocks           */
+/* Returns negative on error												*/
+/****************************************************************************/
+long SMBCALL smb_fallocdat(smb_t* smb, ulong length, ushort refs)
+{
+	ulong	l,blocks,offset;
+
+	if(smb->sda_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	fflush(smb->sda_fp);
+	clearerr(smb->sda_fp);
+	blocks=smb_datblocks(length);
+	if(fseek(smb->sda_fp,0L,SEEK_END)) {
+		return(SMB_ERR_SEEK);
+	}
+	offset=(ftell(smb->sda_fp)/sizeof(refs))*SDT_BLOCK_LEN;
+	if((long)offset<0) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"invalid data offset: %lu",offset);
+		return(SMB_ERR_DAT_OFFSET);
+	}
+	for(l=0;l<blocks;l++)
+		if(!fwrite(&refs,sizeof(refs),1,smb->sda_fp))
+			break;
+	fflush(smb->sda_fp);
+	if(l<blocks) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"%d (%s) writing allocation bytes"
+			,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp)));
+		return(SMB_ERR_WRITE);
+	}
+	return(offset);
+}
+
+/****************************************************************************/
+/* De-allocates space for data												*/
+/* Returns non-zero on error												*/
+/****************************************************************************/
+int SMBCALL smb_freemsgdat(smb_t* smb, ulong offset, ulong length, ushort refs)
+{
+	BOOL	da_opened=FALSE;
+	int		retval=0;
+	ushort	i;
+	ulong	l,blocks;
+	ulong	sda_offset;
+
+	if(smb->status.attr&SMB_HYPERALLOC)	/* do nothing */
+		return(SMB_SUCCESS);
+
+	blocks=smb_datblocks(length);
+
+	if(smb->sda_fp==NULL) {
+		if((i=smb_open_da(smb))!=SMB_SUCCESS)
+			return(i);
+		da_opened=TRUE;
+	}
+
+	clearerr(smb->sda_fp);
+	for(l=0;l<blocks;l++) {
+		sda_offset=((offset/SDT_BLOCK_LEN)+l)*sizeof(i);
+		if(fseek(smb->sda_fp,sda_offset,SEEK_SET)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) seeking to %lu (0x%lX) of allocation file"
+				,errno,STRERROR(errno)
+				,sda_offset,sda_offset);
+			retval=SMB_ERR_SEEK;
+			break;
+		}
+		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) reading allocation bytes at offset %ld"
+				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
+				,sda_offset);
+			retval=SMB_ERR_READ;
+			break;
+		}
+		if(refs==SMB_ALL_REFS || refs>i)
+			i=0;			/* don't want to go negative */
+		else
+			i-=refs;
+		if(fseek(smb->sda_fp,-(int)sizeof(i),SEEK_CUR)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) seeking backwards 2 bytes in allocation file"
+				,errno,STRERROR(errno));
+			retval=SMB_ERR_SEEK;
+			break;
+		}
+		if(!fwrite(&i,sizeof(i),1,smb->sda_fp)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) writing allocation bytes at offset %ld"
+				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
+				,sda_offset);
+			retval=SMB_ERR_WRITE; 
+			break;
+		}
+	}
+	fflush(smb->sda_fp);
+	if(da_opened)
+		smb_close_da(smb);
+	return(retval);
+}
+
+/****************************************************************************/
+/* Adds to data allocation records for blocks starting at 'offset'          */
+/* Returns non-zero on error												*/
+/****************************************************************************/
+int SMBCALL smb_incdat(smb_t* smb, ulong offset, ulong length, ushort refs)
+{
+	ushort	i;
+	ulong	l,blocks;
+
+	if(smb->sda_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	clearerr(smb->sda_fp);
+	blocks=smb_datblocks(length);
+	for(l=0;l<blocks;l++) {
+		if(fseek(smb->sda_fp,((offset/SDT_BLOCK_LEN)+l)*sizeof(i),SEEK_SET)) {
+			return(SMB_ERR_SEEK);
+		}
+		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) reading allocation record at offset %ld"
+				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
+				,((offset/SDT_BLOCK_LEN)+l)*sizeof(i));
+			return(SMB_ERR_READ);
+		}
+		i+=refs;
+		if(fseek(smb->sda_fp,-(int)sizeof(i),SEEK_CUR)) {
+			return(SMB_ERR_SEEK);
+		}
+		if(!fwrite(&i,sizeof(i),1,smb->sda_fp)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) writing allocation record at offset %ld"
+				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
+				,((offset/SDT_BLOCK_LEN)+l)*sizeof(i));
+			return(SMB_ERR_WRITE); 
+		}
+	}
+	fflush(smb->sda_fp);
+	return(SMB_SUCCESS);
+}
+
+/****************************************************************************/
+/* Increments data allocation records (message references) by number of		*/
+/* header references specified (usually 1)									*/
+/* The opposite function of smb_freemsg()									*/
+/****************************************************************************/
+int SMBCALL smb_incmsg_dfields(smb_t* smb, smbmsg_t* msg, ushort refs)
+{
+	int		i=0;
+	BOOL	da_opened=FALSE;
+	ushort	x;
+
+	if(smb->status.attr&SMB_HYPERALLOC)  /* Nothing to do */
+		return(SMB_SUCCESS);
+
+	if(smb->sda_fp==NULL) {
+		if((i=smb_open_da(smb))!=SMB_SUCCESS)
+			return(i);
+		da_opened=TRUE;
+	}
+
+	for(x=0;x<msg->hdr.total_dfields;x++) {
+		if((i=smb_incdat(smb,msg->hdr.offset+msg->dfield[x].offset
+			,msg->dfield[x].length,refs))!=SMB_SUCCESS)
+			break; 
+	}
+
+	if(da_opened)
+		smb_close_da(smb);
+	return(i);
+}
+
+/****************************************************************************/
+/* De-allocates blocks for header record									*/
+/* Returns non-zero on error												*/
+/****************************************************************************/
+int SMBCALL smb_freemsghdr(smb_t* smb, ulong offset, ulong length)
+{
+	uchar	c=0;
+	ulong	l,blocks;
+
+	if(smb->sha_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	clearerr(smb->sha_fp);
+	blocks=smb_hdrblocks(length);
+	if(fseek(smb->sha_fp,offset/SHD_BLOCK_LEN,SEEK_SET))
+		return(SMB_ERR_SEEK);
+	for(l=0;l<blocks;l++)
+		if(!fwrite(&c,1,1,smb->sha_fp)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) writing allocation record"
+				,ferror(smb->sha_fp),STRERROR(ferror(smb->sha_fp)));
+			return(SMB_ERR_WRITE);
+		}
+	fflush(smb->sha_fp);
+	return(SMB_SUCCESS);
+}
+
+/****************************************************************************/
+/****************************************************************************/
+int SMBCALL smb_freemsg_dfields(smb_t* smb, smbmsg_t* msg, ushort refs)
+{
+	int		i;
+	ushort	x;
+
+	for(x=0;x<msg->hdr.total_dfields;x++) {
+		if((i=smb_freemsgdat(smb,msg->hdr.offset+msg->dfield[x].offset
+			,msg->dfield[x].length,refs))!=SMB_SUCCESS)
+			return(i); 
+	}
+	return(SMB_SUCCESS);
+}
+
+/****************************************************************************/
+/* Frees all allocated header and data blocks (1 reference) for 'msg'       */
+/****************************************************************************/
+int SMBCALL smb_freemsg(smb_t* smb, smbmsg_t* msg)
+{
+	int 	i;
+
+	if(smb->status.attr&SMB_HYPERALLOC)  /* Nothing to do */
+		return(SMB_SUCCESS);
+
+	if(!smb_valid_hdr_offset(smb,msg->idx.offset))
+		return(SMB_ERR_HDR_OFFSET);
+
+	if((i=smb_freemsg_dfields(smb,msg,1))!=SMB_SUCCESS)
+		return(i);
+
+	return(smb_freemsghdr(smb,msg->idx.offset-smb->status.header_offset
+		,msg->hdr.length));
+}
+
+/****************************************************************************/
+/* Finds unused space in header file based on block allocation table and	*/
+/* marks space as used in allocation table.                                 */
+/* File must be opened read/write DENY ALL									*/
+/* Returns offset to beginning of header (in bytes, not blocks) 			*/
+/* Assumes smb_open_ha() has been called									*/
+/* smb_close_ha() should be called after									*/
+/* Returns negative value on error 											*/
+/****************************************************************************/
+long SMBCALL smb_allochdr(smb_t* smb, ulong length)
+{
+	uchar	c;
+	ulong	i,l,blocks,offset=0;
+
+	if(smb->sha_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	blocks=smb_hdrblocks(length);
+	i=0;	/* i is consecutive unused block counter */
+	fflush(smb->sha_fp);
+	rewind(smb->sha_fp);
+	while(!feof(smb->sha_fp)) {
+		if(smb_fread(smb,&c,sizeof(c),smb->sha_fp)!=sizeof(c)) 
+			break;
+		offset+=SHD_BLOCK_LEN;
+		if(!c) i++;
+		else   i=0;
+		if(i==blocks) {
+			offset-=(blocks*SHD_BLOCK_LEN);
+			break; 
+		} 
+	}
+	clearerr(smb->sha_fp);
+	if(fseek(smb->sha_fp,offset/SHD_BLOCK_LEN,SEEK_SET))
+		return(SMB_ERR_SEEK);
+
+	for(l=0;l<blocks;l++)
+		if(fputc(1,smb->sha_fp)!=1) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) writing allocation record"
+				,ferror(smb->sha_fp),STRERROR(ferror(smb->sha_fp)));
+			return(SMB_ERR_WRITE);
+		}
+	fflush(smb->sha_fp);
+	return(offset);
+}
+
+/****************************************************************************/
+/* Allocates space for index, but doesn't search for unused blocks          */
+/* Returns negative value on error 											*/
+/****************************************************************************/
+long SMBCALL smb_fallochdr(smb_t* smb, ulong length)
+{
+	uchar	c=1;
+	ulong	l,blocks,offset;
+
+	if(smb->sha_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	blocks=smb_hdrblocks(length);
+	fflush(smb->sha_fp);
+	clearerr(smb->sha_fp);
+	if(fseek(smb->sha_fp,0L,SEEK_END))
+		return(SMB_ERR_SEEK);
+	offset=ftell(smb->sha_fp)*SHD_BLOCK_LEN;
+	for(l=0;l<blocks;l++)
+		if(!fwrite(&c,1,1,smb->sha_fp)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) writing allocation record"
+				,ferror(smb->sha_fp),STRERROR(ferror(smb->sha_fp)));
+			return(SMB_ERR_WRITE);
+		}
+	fflush(smb->sha_fp);
+	return(offset);
+}
+
+/************************************************************************/
+/* Allocate header blocks using Hyper Allocation						*/
+/* this function should be most likely not be called from anywhere but	*/
+/* smb_addmsghdr()														*/
+/************************************************************************/
+long SMBCALL smb_hallochdr(smb_t* smb)
+{
+	ulong offset;
+
+	if(smb->shd_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	fflush(smb->shd_fp);
+	if(fseek(smb->shd_fp,0L,SEEK_END))
+		return(SMB_ERR_SEEK);
+	offset=ftell(smb->shd_fp);
+	if(offset<smb->status.header_offset) 	/* Header file truncated?!? */
+		return(smb->status.header_offset);
+
+	offset-=smb->status.header_offset;		/* SMB headers not included */
+
+	/* Even block boundry */
+	offset+=PAD_LENGTH_FOR_ALIGNMENT(offset,SHD_BLOCK_LEN);
+
+	return(offset);
+}
+
+/************************************************************************/
+/* Allocate data blocks using Hyper Allocation							*/
+/* smb_locksmbhdr() should be called before this function and not		*/
+/* unlocked until all data fields for this message have been written	*/
+/* to the SDT file														*/
+/************************************************************************/
+long SMBCALL smb_hallocdat(smb_t* smb)
+{
+	long offset;
+
+	if(smb->sdt_fp==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"msgbase not open");
+		return(SMB_ERR_NOT_OPEN);
+	}
+	fflush(smb->sdt_fp);
+	offset=filelength(fileno(smb->sdt_fp));
+	if(offset<0) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"invalid file length: %lu",(ulong)offset);
+		return(SMB_ERR_FILE_LEN);
+	}
+	if(fseek(smb->sdt_fp,0L,SEEK_END))
+		return(SMB_ERR_SEEK);
+	offset=ftell(smb->sdt_fp);
+	if(offset<0) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"invalid file offset: %ld",offset);
+		return(SMB_ERR_DAT_OFFSET);
+	}
+
+	/* Make sure even block boundry */
+	offset+=PAD_LENGTH_FOR_ALIGNMENT(offset,SDT_BLOCK_LEN);
+
+	return(offset);
+}
diff --git a/src/smblib/smbfile.c b/src/smblib/smbfile.c
new file mode 100644
index 0000000000..48678230b1
--- /dev/null
+++ b/src/smblib/smbfile.c
@@ -0,0 +1,201 @@
+/* smbfile.c */
+
+/* Synchronet message base (SMB) FILE stream I/O routines */
+
+/* $Id$ */
+
+/****************************************************************************
+ * @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		*
+ *																			*
+ * This library is free software; you can redistribute it and/or			*
+ * modify it under the terms of the GNU Lesser General Public License		*
+ * as published by the Free Software Foundation; either version 2			*
+ * of the License, or (at your option) any later version.					*
+ * See the GNU Lesser General Public License for more details: lgpl.txt or	*
+ * http://www.fsf.org/copyleft/lesser.html									*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#include <time.h>
+#include "smblib.h"
+#include "genwrap.h"
+
+int SMBCALL smb_feof(FILE* fp)
+{
+	return(feof(fp));
+}
+
+int SMBCALL smb_ferror(FILE* fp)
+{
+	return(ferror(fp));
+}
+
+int SMBCALL smb_fflush(FILE* fp)
+{
+	return(fflush(fp));
+}
+
+int SMBCALL smb_fgetc(FILE* fp)
+{
+	return(fgetc(fp));
+}
+
+int SMBCALL smb_fputc(int ch, FILE* fp)
+{
+	return(fputc(ch,fp));
+}
+
+int SMBCALL smb_fseek(FILE* fp, long offset, int whence)
+{
+	return(fseek(fp,offset,whence));
+}
+
+long SMBCALL smb_ftell(FILE* fp)
+{
+	return(ftell(fp));
+}
+
+long SMBCALL smb_fgetlength(FILE* fp)
+{
+	return(filelength(fileno(fp)));
+}
+
+int SMBCALL smb_fsetlength(FILE* fp, long length)
+{
+	return(chsize(fileno(fp),length));
+}
+
+void SMBCALL smb_rewind(FILE* fp)
+{
+	rewind(fp);
+}
+
+void SMBCALL smb_clearerr(FILE* fp)
+{
+	clearerr(fp);
+}
+
+size_t SMBCALL smb_fread(smb_t* smb, void* buf, size_t bytes, FILE* fp)
+{
+	size_t ret;
+	time_t start=0;
+
+	while(1) {
+		if((ret=fread(buf,sizeof(char),bytes,fp))==bytes)
+			return(ret);
+		if(ferror(fp)!=EDEADLOCK)
+			return(ret);
+		if(!start)
+			start=time(NULL);
+		else
+			if(time(NULL)-start>=(time_t)smb->retry_time)
+				break;
+		SLEEP(smb->retry_delay);
+	}
+	return(ret);
+}
+
+#if defined(__BORLANDC__)
+	#pragma argsused
+#endif
+
+size_t SMBCALL smb_fwrite(smb_t* smb, const void* buf, size_t bytes, FILE* fp)
+{
+	return(fwrite(buf,1,bytes,fp));
+}
+
+/****************************************************************************/
+/* Opens a non-shareable file (FILE*) associated with a message base		*/
+/* Retrys for retry_time number of seconds									*/
+/* Return 0 on success, non-zero otherwise									*/
+/****************************************************************************/
+int SMBCALL smb_open_fp(smb_t* smb, FILE** fp, int share)
+{
+	int 	file;
+	char	path[MAX_PATH+1];
+	char*	ext;
+	time_t	start=0;
+
+	if(fp==&smb->shd_fp)
+		ext="shd";
+	else if(fp==&smb->sid_fp)
+		ext="sid";
+	else if(fp==&smb->sdt_fp)
+		ext="sdt";
+	else if(fp==&smb->sda_fp)
+		ext="sda";
+	else if(fp==&smb->sha_fp)
+		ext="sha";
+	else if(fp==&smb->hash_fp)
+		ext="hash";
+	else {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"opening %s: Illegal FILE* pointer argument: %p"
+			,smb->file, fp);
+		return(SMB_ERR_OPEN);
+	}
+	SAFEPRINTF2(path,"%s.%s",smb->file,ext);
+
+	if(*fp!=NULL)	/* Already open! */
+		return(SMB_SUCCESS);
+
+	while(1) {
+		if((file=sopen(path,O_RDWR|O_CREAT|O_BINARY,share,S_IREAD|S_IWRITE))!=-1)
+			break;
+		if(errno!=EACCES && errno!=EAGAIN) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%d (%s) opening %s"
+				,errno,STRERROR(errno),path);
+			return(SMB_ERR_OPEN);
+		}
+		if(!start)
+			start=time(NULL);
+		else
+			if(time(NULL)-start>=(time_t)smb->retry_time) {
+				safe_snprintf(smb->last_error,sizeof(smb->last_error)
+					,"timeout opening %s (retry_time=%ld)"
+					,path,smb->retry_time);
+				return(SMB_ERR_TIMEOUT); 
+			}
+		SLEEP(smb->retry_delay);
+	}
+	if((*fp=fdopen(file,"r+b"))==NULL) {
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"%d (%s) fdopening %s (%d)"
+			,errno,STRERROR(errno),path,file);
+		close(file);
+		return(SMB_ERR_OPEN); 
+	}
+	setvbuf(*fp,NULL,_IOFBF,2*1024);
+	return(SMB_SUCCESS);
+}
+
+/****************************************************************************/
+/****************************************************************************/
+void SMBCALL smb_close_fp(FILE** fp)
+{
+	if(fp!=NULL) {
+		if(*fp!=NULL)
+			fclose(*fp);
+		*fp=NULL;
+	}
+}
diff --git a/src/smblib/smbhash.c b/src/smblib/smbhash.c
new file mode 100644
index 0000000000..980dee66d3
--- /dev/null
+++ b/src/smblib/smbhash.c
@@ -0,0 +1,348 @@
+/* smblib.c */
+
+/* Synchronet message base (SMB) hash-related functions */
+
+/* $Id$ */
+
+/****************************************************************************
+ * @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		*
+ *																			*
+ * This library is free software; you can redistribute it and/or			*
+ * modify it under the terms of the GNU Lesser General Public License		*
+ * as published by the Free Software Foundation; either version 2			*
+ * of the License, or (at your option) any later version.					*
+ * See the GNU Lesser General Public License for more details: lgpl.txt or	*
+ * http://www.fsf.org/copyleft/lesser.html									*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#include <time.h>
+#include "smblib.h"
+#include "md5.h"
+#include "crc16.h"
+#include "crc32.h"
+#include "genwrap.h"
+
+/* If return value is SMB_ERROR_NOT_FOUND, hash file is left open */
+int SMBCALL smb_findhash(smb_t* smb, hash_t** compare, hash_t* found_hash, BOOL mark)
+{
+	int		retval;
+	BOOL	found=FALSE;
+	size_t	c,count;
+	hash_t	hash;
+
+	if(found_hash!=NULL)
+		memset(found_hash,0,sizeof(hash_t));
+
+	if((retval=smb_open_hash(smb))!=SMB_SUCCESS)
+		return(retval);
+
+	COUNT_LIST_ITEMS(compare, count);
+
+	if(count) {
+
+		rewind(smb->hash_fp);
+		while(!feof(smb->hash_fp)) {
+			memset(&hash,0,sizeof(hash));
+			if(smb_fread(smb,&hash,sizeof(hash),smb->hash_fp)!=sizeof(hash))
+				break;
+
+			if(hash.flags==0)
+				continue;		/* invalid hash record (!?) */
+
+			for(c=0;compare[c]!=NULL;c++) {
+
+				if(compare[c]->source!=hash.source)
+					continue;	/* wrong source */
+				if(compare[c]->flags&SMB_HASH_MARKED)
+					continue;	/* already marked */
+				if((compare[c]->flags&SMB_HASH_PROC_MASK)!=(hash.flags&SMB_HASH_PROC_MASK))
+					continue;	/* wrong pre-process flags */
+				if((compare[c]->flags&hash.flags&SMB_HASH_MASK)==0)	
+					continue;	/* no matching hashes */
+				if(compare[c]->flags&hash.flags&SMB_HASH_CRC16 
+					&& compare[c]->crc16!=hash.crc16)
+					continue;	/* wrong crc-16 */
+				if(compare[c]->flags&hash.flags&SMB_HASH_CRC32
+					&& compare[c]->crc32!=hash.crc32)
+					continue;	/* wrong crc-32 */
+				if(compare[c]->flags&hash.flags&SMB_HASH_MD5 
+					&& memcmp(compare[c]->md5,hash.md5,sizeof(hash.md5)))
+					continue;	/* wrong crc-16 */
+				
+				/* successful match! */
+				break;	/* can't match more than one, so stop comparing */
+			}
+
+			if(compare[c]==NULL)
+				continue;	/* no match */
+
+			found=TRUE;
+
+			if(found_hash!=NULL)
+				memcpy(found_hash,&hash,sizeof(hash));
+
+			if(!mark)
+				break;
+
+			compare[c]->flags|=SMB_HASH_MARKED;
+		}
+		if(found) {
+			smb_close_hash(smb);
+			return(SMB_SUCCESS);
+		}
+	}
+
+	/* hash file left open */
+	return(SMB_ERR_NOT_FOUND);
+}
+
+int SMBCALL smb_addhashes(smb_t* smb, hash_t** hashes, BOOL skip_marked)
+{
+	int		retval;
+	size_t	h;
+
+	COUNT_LIST_ITEMS(hashes, h);
+	if(!h)	/* nothing to add */
+		return(SMB_SUCCESS);
+
+	if((retval=smb_open_hash(smb))!=SMB_SUCCESS)
+		return(retval);
+
+	fseek(smb->hash_fp,0,SEEK_END);
+
+	for(h=0;hashes[h]!=NULL;h++) {
+
+		/* skip hashes marked by smb_findhash() */
+		if(skip_marked && hashes[h]->flags&SMB_HASH_MARKED)	
+			continue;	
+	
+		/* can't think of any reason to strip SMB_HASH_MARKED flag right now */
+		if(smb_fwrite(smb,hashes[h],sizeof(hash_t),smb->hash_fp)!=sizeof(hash_t))
+			return(SMB_ERR_WRITE);
+	}
+
+	smb_close_hash(smb);
+
+	return(SMB_SUCCESS);
+}
+
+static char* strip_chars(uchar* str, uchar* set)
+{
+	char*	src;
+	char*	dst;
+	char*	tmp;
+
+	if((tmp=strdup(str))==NULL)
+		return(NULL);
+	for(src=tmp,dst=str;*src;src++) {
+		if(strchr(set,*src)==NULL)
+			*(dst++)=*src;
+	}
+	*dst=0;
+	
+	return(str);
+}
+
+/* Allocates and calculates hashes of data (based on flags)					*/
+/* Returns NULL on failure													*/
+hash_t* SMBCALL smb_hash(ulong msgnum, ulong t, unsigned source, unsigned flags
+						 ,const void* data, size_t length)
+{
+	hash_t*	hash;
+
+	if((hash=(hash_t*)malloc(sizeof(hash_t)))==NULL)
+		return(NULL);
+
+	hash->number=msgnum;
+	hash->time=t;
+	hash->source=source;
+	hash->flags=flags;
+	if(flags&SMB_HASH_CRC16)
+		hash->crc16=crc16((char*)data,length);
+	if(flags&SMB_HASH_CRC32)
+		hash->crc32=crc32((char*)data,length);
+	if(flags&SMB_HASH_MD5)
+		MD5_calc(hash->md5,data,length);
+
+	return(hash);
+}
+
+/* Allocates and calculates hashes of data (based on flags)					*/
+/* Supports string hash "pre-processing" (e.g. lowercase, strip whitespace)	*/
+/* Returns NULL on failure													*/
+hash_t* SMBCALL smb_hashstr(ulong msgnum, ulong t, unsigned source, unsigned flags
+							,const char* str)
+{
+	char*	p=(uchar*)str;
+	hash_t*	hash;
+
+	if(flags&SMB_HASH_PROC_MASK) {	/* string pre-processing */
+		if((p=strdup(str))==NULL)
+			return(NULL);
+		if(flags&SMB_HASH_LOWERCASE)
+			strlwr(p);
+		if(flags&SMB_HASH_STRIP_WSP)
+			strip_chars(p," \t\r\n");
+	}
+	
+	hash=smb_hash(msgnum, t, source, flags, p, strlen(p));
+
+	if(p!=str)	/* duped string */
+		free(p);
+
+	return(hash);
+}
+
+/* Allocatese and calculates all hashes for a single message				*/
+/* Returns NULL on failure													*/
+hash_t** SMBCALL smb_msghashes(smbmsg_t* msg, const uchar* text, BOOL dupechk)
+{
+	size_t		h=0;
+	uchar		flags=SMB_HASH_CRC16|SMB_HASH_CRC32|SMB_HASH_MD5;
+	hash_t**	hashes;	/* This is a NULL-terminated list of hashes */
+	hash_t*		hash;
+	time_t		t=time(NULL);
+
+#define SMB_MAX_HASH_COUNT 4
+
+	if((hashes=(hash_t**)malloc(sizeof(hash_t*)*SMB_MAX_HASH_COUNT))==NULL)
+		return(NULL);
+
+	memset(hashes, 0, sizeof(hash_t*)*SMB_MAX_HASH_COUNT);
+
+	if(msg->id!=NULL
+		&& (hash=smb_hashstr(msg->hdr.number, t, RFC822MSGID, flags, msg->id))!=NULL)
+		hashes[h++]=hash;
+
+	if(msg->ftn_msgid!=NULL
+		&& (hash=smb_hashstr(msg->hdr.number, t, FIDOMSGID, flags, msg->ftn_msgid))!=NULL)
+		hashes[h++]=hash;
+
+	flags|=SMB_HASH_STRIP_WSP;
+	if(!dupechk)
+		flags|=SMB_HASH_MARKED;	/* ignore for dupe checks */
+	if(text!=NULL
+		&& (hash=smb_hashstr(msg->hdr.number, t, TEXT_BODY, flags, text))!=NULL)
+		hashes[h++]=hash;
+
+	return(hashes);
+}
+
+/* Calculates and stores the hashes for a single message					*/
+int SMBCALL smb_hashmsg(smb_t* smb, smbmsg_t* msg, const uchar* text, BOOL update)
+{
+	size_t		n;
+	int			retval=SMB_SUCCESS;
+	hash_t		found;
+	hash_t**	hashes;	/* This is a NULL-terminated list of hashes */
+
+	hashes=smb_msghashes(msg,text,/* dupechk? */TRUE);
+
+	if(smb_findhash(smb, hashes, &found, update)==SMB_SUCCESS && !update) {
+		retval=SMB_DUPE_MSG;
+		safe_snprintf(smb->last_error,sizeof(smb->last_error)
+			,"duplicate %s hash found (message #%lu)"
+			,smb_hashsource(found.source), found.number);
+	} else
+		if((retval=smb_addhashes(smb,hashes,/* skip_marked? */TRUE))==SMB_SUCCESS)
+			msg->flags|=MSG_FLAG_HASHED;
+
+	FREE_LIST(hashes,n);
+
+	return(retval);
+}
+
+/* length=0 specifies ASCIIZ data											*/
+int SMBCALL smb_getmsgidx_by_hash(smb_t* smb, smbmsg_t* msg, unsigned source
+								 ,unsigned flags, const void* data, size_t length)
+{
+	int			retval;
+	size_t		n;
+	hash_t**	hashes;
+	hash_t		found;
+
+	if((hashes=(hash_t**)malloc(sizeof(hash_t*)*2))==NULL)
+		return(SMB_ERR_MEM);
+
+	if(length==0)
+		hashes[0]=smb_hashstr(0,0,source,flags,data);
+	else
+		hashes[0]=smb_hash(0,0,source,flags,data,length);
+	if(hashes[0]==NULL)
+		return(SMB_ERR_MEM);
+
+	hashes[1]=NULL;	/* terminate list */
+
+	memset(&found,0,sizeof(found));
+	if((retval=smb_findhash(smb, hashes, &found, FALSE))==SMB_SUCCESS) {
+		if(found.number==0)
+			retval=SMB_FAILURE;	/* use better error value here? */
+		else {
+			msg->hdr.number=found.number;
+			retval=smb_getmsgidx(smb, msg);
+		}
+	}
+
+	FREE_LIST(hashes,n);
+
+	return(retval);
+}
+
+int SMBCALL smb_getmsghdr_by_hash(smb_t* smb, smbmsg_t* msg, unsigned source
+								 ,unsigned flags, const void* data, size_t length)
+{
+	int retval;
+
+	if((retval=smb_getmsgidx_by_hash(smb,msg,source,flags,data,length))!=SMB_SUCCESS)
+		return(retval);
+
+	if((retval=smb_lockmsghdr(smb,msg))!=SMB_SUCCESS)
+		return(retval);
+
+	retval=smb_getmsghdr(smb,msg);
+
+	smb_unlockmsghdr(smb,msg); 
+
+	return(retval);
+}
+
+ushort SMBCALL smb_subject_crc(const char *subj)
+{
+	char*	str;
+	ushort	crc;
+
+	while(!strnicmp(subj,"RE:",3)) {
+		subj+=3;
+		while(*subj==' ')
+			subj++; 
+	}
+
+	if((str=strdup(subj))==NULL)
+		return(0xffff);
+
+	strlwr(str);
+	crc=crc16(str,0	/* auto-length */);
+	free(str);
+
+	return(crc);
+}
diff --git a/src/smblib/smblib.c b/src/smblib/smblib.c
index 5ac3d4060a..0e3a1c5287 100644
--- a/src/smblib/smblib.c
+++ b/src/smblib/smblib.c
@@ -51,10 +51,6 @@
 #include "smblib.h"
 #include "genwrap.h"
 #include "filewrap.h"
-#include "md5.h"
-#include "crc16.h"
-#include "crc32.h"
-#include "truncsp.c"
 
 /* Use smb_ver() and smb_lib_ver() to obtain these values */
 #define SMBLIB_VERSION		"2.40"      /* SMB library version */
@@ -175,81 +171,6 @@ void SMBCALL smb_close(smb_t* smb)
 	smb_close_fp(&smb->hash_fp);
 }
 
-/****************************************************************************/
-/* Opens a non-shareable file (FILE*) associated with a message base		*/
-/* Retrys for retry_time number of seconds									*/
-/* Return 0 on success, non-zero otherwise									*/
-/****************************************************************************/
-int SMBCALL smb_open_fp(smb_t* smb, FILE** fp, int share)
-{
-	int 	file;
-	char	path[MAX_PATH+1];
-	char*	ext;
-	time_t	start=0;
-
-	if(fp==&smb->shd_fp)
-		ext="shd";
-	else if(fp==&smb->sid_fp)
-		ext="sid";
-	else if(fp==&smb->sdt_fp)
-		ext="sdt";
-	else if(fp==&smb->sda_fp)
-		ext="sda";
-	else if(fp==&smb->sha_fp)
-		ext="sha";
-	else if(fp==&smb->hash_fp)
-		ext="hash";
-	else {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"opening %s: Illegal FILE* pointer argument: %p"
-			,smb->file, fp);
-		return(SMB_ERR_OPEN);
-	}
-	SAFEPRINTF2(path,"%s.%s",smb->file,ext);
-
-	if(*fp!=NULL)	/* Already open! */
-		return(SMB_SUCCESS);
-
-	while(1) {
-		if((file=sopen(path,O_RDWR|O_CREAT|O_BINARY,share,S_IREAD|S_IWRITE))!=-1)
-			break;
-		if(errno!=EACCES && errno!=EAGAIN) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) opening %s"
-				,errno,STRERROR(errno),path);
-			return(SMB_ERR_OPEN);
-		}
-		if(!start)
-			start=time(NULL);
-		else
-			if(time(NULL)-start>=(time_t)smb->retry_time) {
-				safe_snprintf(smb->last_error,sizeof(smb->last_error)
-					,"timeout opening %s (retry_time=%ld)"
-					,path,smb->retry_time);
-				return(SMB_ERR_TIMEOUT); 
-			}
-		SLEEP(smb->retry_delay);
-	}
-	if((*fp=fdopen(file,"r+b"))==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"%d (%s) fdopening %s (%d)"
-			,errno,STRERROR(errno),path,file);
-		close(file);
-		return(SMB_ERR_OPEN); 
-	}
-	setvbuf(*fp,NULL,_IOFBF,2*1024);
-	return(SMB_SUCCESS);
-}
-
-void SMBCALL smb_close_fp(FILE** fp)
-{
-	if(fp!=NULL) {
-		if(*fp!=NULL)
-			fclose(*fp);
-		*fp=NULL;
-	}
-}
-
 /****************************************************************************/
 /* This set of functions is used to exclusively-lock an entire message base	*/
 /* against any other process opening any of the message base files.			*/
@@ -514,7 +435,7 @@ int SMBCALL smb_unlocksmbhdr(smb_t* smb)
 /****************************************************************************/
 /* Is the offset a valid message header offset?								*/
 /****************************************************************************/
-static BOOL smb_valid_hdr_offset(smb_t* smb, ulong offset)
+BOOL SMBCALL smb_valid_hdr_offset(smb_t* smb, ulong offset)
 {
 	if(offset<sizeof(smbhdr_t)+sizeof(smbstatus_t) 
 		|| offset<smb->status.header_offset) {
@@ -1484,266 +1405,6 @@ int SMBCALL smb_addmsghdr(smb_t* smb, smbmsg_t* msg, int storage)
 	return(i);
 }
 
-/****************************************************************************/
-/****************************************************************************/
-int SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, BOOL dupechk
-					   ,ushort xlat, const uchar* body, const uchar* tail)
-{
-	uchar*		lzhbuf=NULL;
-	long		lzhlen;
-	int			retval;
-	size_t		n;
-	size_t		l,length;
-	size_t		taillen=0;
-	size_t		bodylen=0;
-	long		offset;
-	ulong		crc=0xffffffff;
-	hash_t		found;
-	hash_t**	hashes=NULL;	/* This is a NULL-terminated list of hashes */
-	smbmsg_t	remsg;
-
-	if(!SMB_IS_OPEN(smb)) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-
-	if(filelength(fileno(smb->shd_fp))<1) {	 /* Create it if it doesn't exist */
-		/* smb->status.max_crcs, max_msgs, max_age, and attr should be pre-initialized */
-		if((retval=smb_create(smb))!=SMB_SUCCESS) 
-			return(retval);
-	}
-
-	if(!smb->locked && smb_locksmbhdr(smb)!=SMB_SUCCESS)
-		return(SMB_ERR_LOCK);
-
-	msg->hdr.total_dfields = 0;
-
-	/* try */
-	do {
-
-		if((retval=smb_getstatus(smb))!=SMB_SUCCESS)
-			break;
-
-		msg->hdr.number=smb->status.last_msg+1;
-
-		hashes=smb_msghashes(msg,body,dupechk);
-
-		if(smb_findhash(smb, hashes, &found, /* update? */FALSE)==SMB_SUCCESS) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"duplicate %s hash found (message #%lu)"
-				,smb_hashsource(found.source), found.number);
-			retval=SMB_DUPE_MSG;
-			break;
-		}
-
-		if(tail!=NULL && (taillen=strlen(tail))>0)
-			taillen+=sizeof(xlat);	/* xlat string terminator */
-
-		if(body!=NULL && (bodylen=strlen(body))>0) {
-
-			/* Remove white-space from end of message text */
-			while(bodylen && body[bodylen-1]<=' ')
-				bodylen--;
-
-			/* Calculate CRC-32 of message text (before encoding, if any) */
-			if(smb->status.max_crcs && dupechk) {
-				for(l=0;l<bodylen;l++)
-					crc=ucrc32(body[l],crc); 
-				crc=~crc;
-
-				/* Add CRC to CRC history (and check for duplicates) */
-				if((retval=smb_addcrc(smb,crc))!=SMB_SUCCESS)
-					break;
-			}
-
-			bodylen+=sizeof(xlat);	/* xlat string terminator */
-
-			/* LZH compress? */
-			if(xlat==XLAT_LZH && bodylen+taillen>=SDT_BLOCK_LEN
-				&& (lzhbuf=(uchar *)malloc(bodylen*2))!=NULL) {
-				lzhlen=lzh_encode((uchar*)body,bodylen-sizeof(xlat),lzhbuf);
-				if(lzhlen>1
-					&& smb_datblocks(lzhlen+(sizeof(xlat)*2)+taillen) 
-						< smb_datblocks(bodylen+taillen)) {
-					bodylen=lzhlen+(sizeof(xlat)*2); 	/* Compressible */
-					body=lzhbuf; 
-				} else
-					xlat=XLAT_NONE;
-			} else
-				xlat=XLAT_NONE;
-		}
-
-		length=bodylen+taillen;
-
-		if(length&0x80000000) {
-			sprintf(smb->last_error,"message length: 0x%lX",length);
-			retval=SMB_ERR_DAT_LEN;
-			break;
-		}
-
-		/* Allocate Data Blocks */
-		if(smb->status.attr&SMB_HYPERALLOC) {
-			offset=smb_hallocdat(smb);
-			storage=SMB_HYPERALLOC; 
-		} else {
-			if((retval=smb_open_da(smb))!=SMB_SUCCESS)
-				break;
-			if(storage==SMB_FASTALLOC)
-				offset=smb_fallocdat(smb,length,1);
-			else { /* SMB_SELFPACK */
-				offset=smb_allocdat(smb,length,1);
-				storage=SMB_SELFPACK; 
-			}
-			smb_close_da(smb); 
-		}
-
-		if(offset<0) {
-			retval=offset;
-			break;
-		}
-		msg->hdr.offset=offset;
-
-		smb_fseek(smb->sdt_fp,offset,SEEK_SET);
-
-		if(bodylen) {
-			if((retval=smb_dfield(msg,TEXT_BODY,bodylen))!=SMB_SUCCESS)
-				break;
-
-			if(xlat!=XLAT_NONE) {	/* e.g. XLAT_LZH */
-				if(smb_fwrite(smb,&xlat,sizeof(xlat),smb->sdt_fp)!=sizeof(xlat)) {
-					safe_snprintf(smb->last_error,sizeof(smb->last_error)
-						,"%d (%s) writing body xlat string"
-						,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
-					retval=SMB_ERR_WRITE;
-					break;
-				}
-				bodylen-=sizeof(xlat);
-			}
-			xlat=XLAT_NONE;	/* xlat string terminator */
-			if(smb_fwrite(smb,&xlat,sizeof(xlat),smb->sdt_fp)!=sizeof(xlat)) {
-				safe_snprintf(smb->last_error,sizeof(smb->last_error)
-					,"%d (%s) writing body xlat terminator"
-					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
-				retval=SMB_ERR_WRITE;
-				break;
-			}
-			bodylen-=sizeof(xlat);
-
-			if(smb_fwrite(smb,body,bodylen,smb->sdt_fp)!=bodylen) {
-				safe_snprintf(smb->last_error,sizeof(smb->last_error)
-					,"%d (%s) writing body (%ld bytes)"
-					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp))
-					,bodylen);
-				retval=SMB_ERR_WRITE;
-				break;
-			}
-		}
-
-		if(taillen) {
-			if((retval=smb_dfield(msg,TEXT_TAIL,taillen))!=SMB_SUCCESS)
-				break;
-
-			xlat=XLAT_NONE;	/* xlat string terminator */
-			if(smb_fwrite(smb,&xlat,sizeof(xlat),smb->sdt_fp)!=sizeof(xlat)) {
-				safe_snprintf(smb->last_error,sizeof(smb->last_error)
-					,"%d (%s) writing tail xlat terminator"
-					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
-				retval=SMB_ERR_WRITE;
-				break;
-			}
-
-			if(smb_fwrite(smb,tail,taillen-sizeof(xlat),smb->sdt_fp)!=taillen-sizeof(xlat)) {
-				safe_snprintf(smb->last_error,sizeof(smb->last_error)
-					,"%d (%s) writing tail (%ld bytes)"
-					,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp))
-					,taillen-sizeof(xlat));
-				retval=SMB_ERR_WRITE;
-				break;
-			}
-		}
-
-		for(l=length;l%SDT_BLOCK_LEN;l++) {
-			if(smb_fputc(0,smb->sdt_fp)!=0)
-				break;
-		}
-		if(l%SDT_BLOCK_LEN) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) writing data padding"
-				,ferror(smb->sdt_fp),STRERROR(ferror(smb->sdt_fp)));
-			retval=SMB_ERR_WRITE;
-			break;
-		}
-
-		fflush(smb->sdt_fp);
-
-		if(msg->to==NULL)	/* no recipient, don't add header (required for bulkmail) */
-			break;
-
-		msg->hdr.version=smb_ver();
-		if(msg->hdr.when_imported.time==0) {
-			msg->hdr.when_imported.time=time(NULL);
-			msg->hdr.when_imported.zone=0;	/* how do we detect system TZ? */
-		}
-		if(msg->hdr.when_written.time==0)	/* Uninitialized */
-			msg->hdr.when_written = msg->hdr.when_imported;
-		msg->idx.time=msg->hdr.when_imported.time;
-
-		/* Look-up thread_back if Reply-ID was specified */
-		if(msg->hdr.thread_back==0 && msg->reply_id!=NULL) {
-			if(smb_getmsgidx_by_msgid(smb,&remsg,msg->reply_id)==SMB_SUCCESS)
-				msg->hdr.thread_back=remsg.idx.number;	/* needed for threading backward */
-		}
-
-		/* Auto-thread linkage */
-		if(msg->hdr.thread_back) {
-			memset(&remsg,0,sizeof(remsg));
-			remsg.hdr.number=msg->hdr.thread_back;
-			if((retval=smb_getmsgidx(smb, &remsg))!=SMB_SUCCESS)	/* invalid thread origin */
-				break;
-
-			if((retval=smb_lockmsghdr(smb,&remsg))!=SMB_SUCCESS)
-				break;
-
-			if((retval=smb_getmsghdr(smb, &remsg))!=SMB_SUCCESS) {
-				smb_unlockmsghdr(smb, &remsg); 
-				break;
-			}
-
-			/* Add RFC-822 Reply-ID if original message has RFC Message-ID */
-			if(msg->reply_id==NULL && remsg.id!=NULL
-				&& (retval=smb_hfield_str(msg,RFC822REPLYID,remsg.id))!=SMB_SUCCESS)
-				break;
-
-			/* Add FidoNet Reply if original message has FidoNet MSGID */
-			if(msg->ftn_reply==NULL && remsg.ftn_msgid!=NULL
-				&& (retval=smb_hfield_str(msg,FIDOREPLYID,remsg.ftn_msgid))!=SMB_SUCCESS)
-				break;
-
-			retval=smb_updatethread(smb, &remsg, msg->hdr.number);
-			smb_unlockmsghdr(smb, &remsg);
-			smb_freemsgmem(&remsg);
-
-			if(retval!=SMB_SUCCESS)
-				break;
-		}
-
-		if(smb_addhashes(smb,hashes,/* skip_marked? */FALSE)==SMB_SUCCESS)
-			msg->flags|=MSG_FLAG_HASHED;
-
-		retval=smb_addmsghdr(smb,msg,storage); // calls smb_unlocksmbhdr() 
-
-	} while(0);
-	/* finally */
-
-	if(retval!=SMB_SUCCESS)
-		smb_freemsg_dfields(smb,msg,1);
-
-	smb_unlocksmbhdr(smb);
-	FREE_AND_NULL(lzhbuf);
-	FREE_LIST(hashes,n);
-
-	return(retval);
-}
 
 /****************************************************************************/
 /* Writes both header and index information for msg 'msg'                   */
@@ -1965,1101 +1626,66 @@ ulong SMBCALL smb_hdrblocks(ulong length)
 }
 
 /****************************************************************************/
-/* Finds unused space in data file based on block allocation table and		*/
-/* marks space as used in allocation table.                                 */
-/* File must be opened read/write DENY ALL									*/
-/* Returns offset to beginning of data (in bytes, not blocks)				*/
-/* Assumes smb_open_da() has been called									*/
-/* smb_close_da() should be called after									*/
-/* Returns negative on error												*/
+/* Returns difference from specified timezone and UTC/GMT					*/
 /****************************************************************************/
-long SMBCALL smb_allocdat(smb_t* smb, ulong length, ushort refs)
+int SMBCALL smb_tzutc(short zone)
 {
-    ushort  i;
-	ulong	j,l,blocks,offset=0L;
-
-	if(smb->sda_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	blocks=smb_datblocks(length);
-	j=0;	/* j is consecutive unused block counter */
-	fflush(smb->sda_fp);
-	rewind(smb->sda_fp);
-	while(!feof(smb->sda_fp) && (long)offset>=0) {
-		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i))
-			break;
-		offset+=SDT_BLOCK_LEN;
-		if(!i) j++;
-		else   j=0;
-		if(j==blocks) {
-			offset-=(blocks*SDT_BLOCK_LEN);
-			break; 
-		} 
-	}
-	if((long)offset<0) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"invalid data offset: %lu",offset);
-		return(SMB_ERR_DAT_OFFSET);
-	}
-	clearerr(smb->sda_fp);
-	if(fseek(smb->sda_fp,(offset/SDT_BLOCK_LEN)*sizeof(refs),SEEK_SET)) {
-		return(SMB_ERR_SEEK);
-	}
-	for(l=0;l<blocks;l++)
-		if(!fwrite(&refs,sizeof(refs),1,smb->sda_fp)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) writing allocation bytes at offset %ld"
-				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
-				,((offset/SDT_BLOCK_LEN)+l)*sizeof(refs));
-			return(SMB_ERR_WRITE);
-		}
-	fflush(smb->sda_fp);
-	return(offset);
-}
+	int tz;
 
-/****************************************************************************/
-/* Allocates space for data, but doesn't search for unused blocks           */
-/* Returns negative on error												*/
-/****************************************************************************/
-long SMBCALL smb_fallocdat(smb_t* smb, ulong length, ushort refs)
-{
-	ulong	l,blocks,offset;
+	if(OTHER_ZONE(zone))
+		return(zone);
 
-	if(smb->sda_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	fflush(smb->sda_fp);
-	clearerr(smb->sda_fp);
-	blocks=smb_datblocks(length);
-	if(fseek(smb->sda_fp,0L,SEEK_END)) {
-		return(SMB_ERR_SEEK);
-	}
-	offset=(ftell(smb->sda_fp)/sizeof(refs))*SDT_BLOCK_LEN;
-	if((long)offset<0) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"invalid data offset: %lu",offset);
-		return(SMB_ERR_DAT_OFFSET);
-	}
-	for(l=0;l<blocks;l++)
-		if(!fwrite(&refs,sizeof(refs),1,smb->sda_fp))
-			break;
-	fflush(smb->sda_fp);
-	if(l<blocks) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"%d (%s) writing allocation bytes"
-			,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp)));
-		return(SMB_ERR_WRITE);
-	}
-	return(offset);
+	tz=zone&0xfff;
+	if(zone&(WESTERN_ZONE|US_ZONE))	/* West of UTC? */
+		return(-tz);
+	return(tz);
 }
 
 /****************************************************************************/
-/* De-allocates space for data												*/
-/* Returns non-zero on error												*/
 /****************************************************************************/
-int SMBCALL smb_freemsgdat(smb_t* smb, ulong offset, ulong length, ushort refs)
+int SMBCALL smb_updatethread(smb_t* smb, smbmsg_t* remsg, ulong newmsgnum)
 {
-	BOOL	da_opened=FALSE;
-	int		retval=0;
-	ushort	i;
-	ulong	l,blocks;
-	ulong	sda_offset;
-
-	if(smb->status.attr&SMB_HYPERALLOC)	/* do nothing */
-		return(SMB_SUCCESS);
-
-	blocks=smb_datblocks(length);
+	int			retval=SMB_ERR_NOT_FOUND;
+	ulong		nextmsgnum;
+	smbmsg_t	nextmsg;
 
-	if(smb->sda_fp==NULL) {
-		if((i=smb_open_da(smb))!=SMB_SUCCESS)
-			return(i);
-		da_opened=TRUE;
+	if(!remsg->hdr.thread_first) {	/* New msg is first reply */
+		remsg->hdr.thread_first=newmsgnum;
+		if((retval=smb_lockmsghdr(smb,remsg))!=SMB_SUCCESS)
+			return(retval);
+		retval=smb_putmsghdr(smb,remsg);
+		smb_unlockmsghdr(smb,remsg);
+		return(retval);
 	}
-
-	clearerr(smb->sda_fp);
-	for(l=0;l<blocks;l++) {
-		sda_offset=((offset/SDT_BLOCK_LEN)+l)*sizeof(i);
-		if(fseek(smb->sda_fp,sda_offset,SEEK_SET)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) seeking to %lu (0x%lX) of allocation file"
-				,errno,STRERROR(errno)
-				,sda_offset,sda_offset);
-			retval=SMB_ERR_SEEK;
+	
+	/* Search for last reply and extend chain */
+	memset(&nextmsg,0,sizeof(nextmsg));
+	nextmsgnum=remsg->hdr.thread_first;	/* start with first reply */
+	while(1) {
+		nextmsg.idx.offset=0;
+		nextmsg.hdr.number=nextmsgnum;
+		if(smb_getmsgidx(smb, &nextmsg)!=SMB_SUCCESS) /* invalid thread origin */
 			break;
-		}
-		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) reading allocation bytes at offset %ld"
-				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
-				,sda_offset);
-			retval=SMB_ERR_READ;
+		if(smb_lockmsghdr(smb,&nextmsg)!=SMB_SUCCESS)
 			break;
-		}
-		if(refs==SMB_ALL_REFS || refs>i)
-			i=0;			/* don't want to go negative */
-		else
-			i-=refs;
-		if(fseek(smb->sda_fp,-(int)sizeof(i),SEEK_CUR)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) seeking backwards 2 bytes in allocation file"
-				,errno,STRERROR(errno));
-			retval=SMB_ERR_SEEK;
+		if(smb_getmsghdr(smb, &nextmsg)!=SMB_SUCCESS) {
+			smb_unlockmsghdr(smb,&nextmsg); 
 			break;
 		}
-		if(!fwrite(&i,sizeof(i),1,smb->sda_fp)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) writing allocation bytes at offset %ld"
-				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
-				,sda_offset);
-			retval=SMB_ERR_WRITE; 
-			break;
+		if(nextmsg.hdr.thread_next && nextmsg.hdr.thread_next!=nextmsgnum) {
+			nextmsgnum=nextmsg.hdr.thread_next;
+			smb_unlockmsghdr(smb,&nextmsg);
+			smb_freemsgmem(&nextmsg);
+			continue; 
 		}
+		nextmsg.hdr.thread_next=newmsgnum;
+		retval=smb_putmsghdr(smb,&nextmsg);
+		smb_unlockmsghdr(smb,&nextmsg);
+		smb_freemsgmem(&nextmsg);
+		break; 
 	}
-	fflush(smb->sda_fp);
-	if(da_opened)
-		smb_close_da(smb);
-	return(retval);
-}
-
-/****************************************************************************/
-/* Adds to data allocation records for blocks starting at 'offset'          */
-/* Returns non-zero on error												*/
-/****************************************************************************/
-int SMBCALL smb_incdat(smb_t* smb, ulong offset, ulong length, ushort refs)
-{
-	ushort	i;
-	ulong	l,blocks;
 
-	if(smb->sda_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	clearerr(smb->sda_fp);
-	blocks=smb_datblocks(length);
-	for(l=0;l<blocks;l++) {
-		if(fseek(smb->sda_fp,((offset/SDT_BLOCK_LEN)+l)*sizeof(i),SEEK_SET)) {
-			return(SMB_ERR_SEEK);
-		}
-		if(smb_fread(smb,&i,sizeof(i),smb->sda_fp)!=sizeof(i)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) reading allocation record at offset %ld"
-				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
-				,((offset/SDT_BLOCK_LEN)+l)*sizeof(i));
-			return(SMB_ERR_READ);
-		}
-		i+=refs;
-		if(fseek(smb->sda_fp,-(int)sizeof(i),SEEK_CUR)) {
-			return(SMB_ERR_SEEK);
-		}
-		if(!fwrite(&i,sizeof(i),1,smb->sda_fp)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) writing allocation record at offset %ld"
-				,ferror(smb->sda_fp),STRERROR(ferror(smb->sda_fp))
-				,((offset/SDT_BLOCK_LEN)+l)*sizeof(i));
-			return(SMB_ERR_WRITE); 
-		}
-	}
-	fflush(smb->sda_fp);
-	return(SMB_SUCCESS);
+	return(retval);
 }
 
-/****************************************************************************/
-/* Increments data allocation records (message references) by number of		*/
-/* header references specified (usually 1)									*/
-/* The opposite function of smb_freemsg()									*/
-/****************************************************************************/
-int SMBCALL smb_incmsg_dfields(smb_t* smb, smbmsg_t* msg, ushort refs)
-{
-	int		i=0;
-	BOOL	da_opened=FALSE;
-	ushort	x;
-
-	if(smb->status.attr&SMB_HYPERALLOC)  /* Nothing to do */
-		return(SMB_SUCCESS);
-
-	if(smb->sda_fp==NULL) {
-		if((i=smb_open_da(smb))!=SMB_SUCCESS)
-			return(i);
-		da_opened=TRUE;
-	}
-
-	for(x=0;x<msg->hdr.total_dfields;x++) {
-		if((i=smb_incdat(smb,msg->hdr.offset+msg->dfield[x].offset
-			,msg->dfield[x].length,refs))!=SMB_SUCCESS)
-			break; 
-	}
-
-	if(da_opened)
-		smb_close_da(smb);
-	return(i);
-}
-
-/****************************************************************************/
-/* De-allocates blocks for header record									*/
-/* Returns non-zero on error												*/
-/****************************************************************************/
-int SMBCALL smb_freemsghdr(smb_t* smb, ulong offset, ulong length)
-{
-	uchar	c=0;
-	ulong	l,blocks;
-
-	if(smb->sha_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	clearerr(smb->sha_fp);
-	blocks=smb_hdrblocks(length);
-	if(fseek(smb->sha_fp,offset/SHD_BLOCK_LEN,SEEK_SET))
-		return(SMB_ERR_SEEK);
-	for(l=0;l<blocks;l++)
-		if(!fwrite(&c,1,1,smb->sha_fp)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) writing allocation record"
-				,ferror(smb->sha_fp),STRERROR(ferror(smb->sha_fp)));
-			return(SMB_ERR_WRITE);
-		}
-	fflush(smb->sha_fp);
-	return(SMB_SUCCESS);
-}
-
-/****************************************************************************/
-/****************************************************************************/
-int SMBCALL smb_freemsg_dfields(smb_t* smb, smbmsg_t* msg, ushort refs)
-{
-	int		i;
-	ushort	x;
-
-	for(x=0;x<msg->hdr.total_dfields;x++) {
-		if((i=smb_freemsgdat(smb,msg->hdr.offset+msg->dfield[x].offset
-			,msg->dfield[x].length,refs))!=SMB_SUCCESS)
-			return(i); 
-	}
-	return(SMB_SUCCESS);
-}
-
-/****************************************************************************/
-/* Frees all allocated header and data blocks (1 reference) for 'msg'       */
-/****************************************************************************/
-int SMBCALL smb_freemsg(smb_t* smb, smbmsg_t* msg)
-{
-	int 	i;
-
-	if(smb->status.attr&SMB_HYPERALLOC)  /* Nothing to do */
-		return(SMB_SUCCESS);
-
-	if(!smb_valid_hdr_offset(smb,msg->idx.offset))
-		return(SMB_ERR_HDR_OFFSET);
-
-	if((i=smb_freemsg_dfields(smb,msg,1))!=SMB_SUCCESS)
-		return(i);
-
-	return(smb_freemsghdr(smb,msg->idx.offset-smb->status.header_offset
-		,msg->hdr.length));
-}
-
-/****************************************************************************/
-/* Finds unused space in header file based on block allocation table and	*/
-/* marks space as used in allocation table.                                 */
-/* File must be opened read/write DENY ALL									*/
-/* Returns offset to beginning of header (in bytes, not blocks) 			*/
-/* Assumes smb_open_ha() has been called									*/
-/* smb_close_ha() should be called after									*/
-/* Returns negative value on error 											*/
-/****************************************************************************/
-long SMBCALL smb_allochdr(smb_t* smb, ulong length)
-{
-	uchar	c;
-	ulong	i,l,blocks,offset=0;
-
-	if(smb->sha_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	blocks=smb_hdrblocks(length);
-	i=0;	/* i is consecutive unused block counter */
-	fflush(smb->sha_fp);
-	rewind(smb->sha_fp);
-	while(!feof(smb->sha_fp)) {
-		if(smb_fread(smb,&c,sizeof(c),smb->sha_fp)!=sizeof(c)) 
-			break;
-		offset+=SHD_BLOCK_LEN;
-		if(!c) i++;
-		else   i=0;
-		if(i==blocks) {
-			offset-=(blocks*SHD_BLOCK_LEN);
-			break; 
-		} 
-	}
-	clearerr(smb->sha_fp);
-	if(fseek(smb->sha_fp,offset/SHD_BLOCK_LEN,SEEK_SET))
-		return(SMB_ERR_SEEK);
-
-	for(l=0;l<blocks;l++)
-		if(fputc(1,smb->sha_fp)!=1) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) writing allocation record"
-				,ferror(smb->sha_fp),STRERROR(ferror(smb->sha_fp)));
-			return(SMB_ERR_WRITE);
-		}
-	fflush(smb->sha_fp);
-	return(offset);
-}
-
-/****************************************************************************/
-/* Allocates space for index, but doesn't search for unused blocks          */
-/* Returns negative value on error 											*/
-/****************************************************************************/
-long SMBCALL smb_fallochdr(smb_t* smb, ulong length)
-{
-	uchar	c=1;
-	ulong	l,blocks,offset;
-
-	if(smb->sha_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	blocks=smb_hdrblocks(length);
-	fflush(smb->sha_fp);
-	clearerr(smb->sha_fp);
-	if(fseek(smb->sha_fp,0L,SEEK_END))
-		return(SMB_ERR_SEEK);
-	offset=ftell(smb->sha_fp)*SHD_BLOCK_LEN;
-	for(l=0;l<blocks;l++)
-		if(!fwrite(&c,1,1,smb->sha_fp)) {
-			safe_snprintf(smb->last_error,sizeof(smb->last_error)
-				,"%d (%s) writing allocation record"
-				,ferror(smb->sha_fp),STRERROR(ferror(smb->sha_fp)));
-			return(SMB_ERR_WRITE);
-		}
-	fflush(smb->sha_fp);
-	return(offset);
-}
-
-/************************************************************************/
-/* Allocate header blocks using Hyper Allocation						*/
-/* this function should be most likely not be called from anywhere but	*/
-/* smb_addmsghdr()														*/
-/************************************************************************/
-long SMBCALL smb_hallochdr(smb_t* smb)
-{
-	ulong offset;
-
-	if(smb->shd_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error),"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	fflush(smb->shd_fp);
-	if(fseek(smb->shd_fp,0L,SEEK_END))
-		return(SMB_ERR_SEEK);
-	offset=ftell(smb->shd_fp);
-	if(offset<smb->status.header_offset) 	/* Header file truncated?!? */
-		return(smb->status.header_offset);
-
-	offset-=smb->status.header_offset;		/* SMB headers not included */
-
-	/* Even block boundry */
-	offset+=PAD_LENGTH_FOR_ALIGNMENT(offset,SHD_BLOCK_LEN);
-
-	return(offset);
-}
-
-/************************************************************************/
-/* Allocate data blocks using Hyper Allocation							*/
-/* smb_locksmbhdr() should be called before this function and not		*/
-/* unlocked until all data fields for this message have been written	*/
-/* to the SDT file														*/
-/************************************************************************/
-long SMBCALL smb_hallocdat(smb_t* smb)
-{
-	long offset;
-
-	if(smb->sdt_fp==NULL) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"msgbase not open");
-		return(SMB_ERR_NOT_OPEN);
-	}
-	fflush(smb->sdt_fp);
-	offset=filelength(fileno(smb->sdt_fp));
-	if(offset<0) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"invalid file length: %lu",(ulong)offset);
-		return(SMB_ERR_FILE_LEN);
-	}
-	if(fseek(smb->sdt_fp,0L,SEEK_END))
-		return(SMB_ERR_SEEK);
-	offset=ftell(smb->sdt_fp);
-	if(offset<0) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"invalid file offset: %ld",offset);
-		return(SMB_ERR_DAT_OFFSET);
-	}
-
-	/* Make sure even block boundry */
-	offset+=PAD_LENGTH_FOR_ALIGNMENT(offset,SDT_BLOCK_LEN);
-
-	return(offset);
-}
-
-int SMBCALL smb_feof(FILE* fp)
-{
-	return(feof(fp));
-}
-
-int SMBCALL smb_ferror(FILE* fp)
-{
-	return(ferror(fp));
-}
-
-int SMBCALL smb_fflush(FILE* fp)
-{
-	return(fflush(fp));
-}
-
-int SMBCALL smb_fgetc(FILE* fp)
-{
-	return(fgetc(fp));
-}
-
-int SMBCALL smb_fputc(int ch, FILE* fp)
-{
-	return(fputc(ch,fp));
-}
-
-int SMBCALL smb_fseek(FILE* fp, long offset, int whence)
-{
-	return(fseek(fp,offset,whence));
-}
-
-long SMBCALL smb_ftell(FILE* fp)
-{
-	return(ftell(fp));
-}
-
-long SMBCALL smb_fgetlength(FILE* fp)
-{
-	return(filelength(fileno(fp)));
-}
-
-int SMBCALL smb_fsetlength(FILE* fp, long length)
-{
-	return(chsize(fileno(fp),length));
-}
-
-void SMBCALL smb_rewind(FILE* fp)
-{
-	rewind(fp);
-}
-
-void SMBCALL smb_clearerr(FILE* fp)
-{
-	clearerr(fp);
-}
-
-size_t SMBCALL smb_fread(smb_t* smb, void* buf, size_t bytes, FILE* fp)
-{
-	size_t ret;
-	time_t start=0;
-
-	while(1) {
-		if((ret=fread(buf,sizeof(char),bytes,fp))==bytes)
-			return(ret);
-		if(ferror(fp)!=EDEADLOCK)
-			return(ret);
-		if(!start)
-			start=time(NULL);
-		else
-			if(time(NULL)-start>=(time_t)smb->retry_time)
-				break;
-		SLEEP(smb->retry_delay);
-	}
-	return(ret);
-}
-
-#if defined(__BORLANDC__)
-	#pragma argsused
-#endif
-
-size_t SMBCALL smb_fwrite(smb_t* smb, const void* buf, size_t bytes, FILE* fp)
-{
-	return(fwrite(buf,1,bytes,fp));
-}
-
-/************************************************************************/
-/* Returns difference from specified timezone and UTC/GMT				*/
-/************************************************************************/
-int SMBCALL smb_tzutc(short zone)
-{
-	int tz;
-
-	if(OTHER_ZONE(zone))
-		return(zone);
-
-	tz=zone&0xfff;
-	if(zone&(WESTERN_ZONE|US_ZONE))	/* West of UTC? */
-		return(-tz);
-	return(tz);
-}
-
-char* SMBCALL smb_hfieldtype(ushort type)
-{
-	static char str[8];
-
-	switch(type) {
-		case SENDER:			return("Sender");
-		case SENDERAGENT:		return("SenderAgent");
-		case SENDERNETTYPE:		return("SenderNetType");
-		case SENDERNETADDR:		return("SenderNetAddr");
-		case SENDEREXT:			return("SenderExt");
-		case SENDERORG:			return("SenderOrg");
-		case SENDERIPADDR:		return("SenderIpAddr");
-		case SENDERHOSTNAME:	return("SenderHostName");
-		case SENDERPROTOCOL:	return("SenderProtocol");
-		case SENDERPORT:		return("SenderPort");
-
-		case REPLYTO:			return("ReplyTo");
-		case REPLYTOAGENT:		return("ReplyToAgent");
-		case REPLYTONETTYPE:	return("ReplyToNetType");
-		case REPLYTONETADDR:	return("ReplyToNetAddr");
-		case REPLYTOEXT:		return("ReplyToExt");
-								
-		case RECIPIENT:			return("Recipient");
-		case RECIPIENTAGENT:	return("RecipientAgent");
-		case RECIPIENTNETTYPE:	return("RecipientNetType");
-		case RECIPIENTNETADDR:	return("RecipientNetAddr");
-		case RECIPIENTEXT:		return("RecipientExt");
-
-		case SUBJECT:			return("Subject");
-		case SMB_SUMMARY:		return("Summary");
-		case SMB_COMMENT:		return("Comment");
-		case SMB_CARBONCOPY:	return("CarbonCopy");
-		case SMB_GROUP:			return("Group");
-		case SMB_EXPIRATION:	return("Expiration");
-		case SMB_PRIORITY:		return("Priority");
-		case SMB_COST:			return("Cost");
-
-		case FIDOCTRL:			return("FidoCtrl");
-		case FIDOAREA:			return("FidoArea");
-		case FIDOSEENBY:		return("FidoSeenBy");
-		case FIDOPATH:			return("FidoPath");
-		case FIDOMSGID:			return("FidoMsgID");
-		case FIDOREPLYID:		return("FidoReplyID");
-		case FIDOPID:			return("FidoPID");
-		case FIDOFLAGS:			return("FidoFlags");
-		case FIDOTID:			return("FidoTID");
-
-		case RFC822HEADER:		return("RFC822Header");
-		case RFC822MSGID:		return("RFC822MsgID");
-		case RFC822REPLYID:		return("RFC822ReplyID");
-		case RFC822TO:			return("RFC822To");
-		case RFC822FROM:		return("RFC822From");
-		case RFC822REPLYTO:		return("RFC822ReplyTo");
-
-		case USENETPATH:		return("UsenetPath");
-		case USENETNEWSGROUPS:	return("UsenetNewsgroups");
-
-		case SMTPCOMMAND:		return("SMTPCommand");
-		case SMTPREVERSEPATH:	return("SMTPReversePath");
-
-		case SMTPSYSMSG:		return("SMTPSysMsg");
-
-		case UNKNOWN:			return("UNKNOWN");
-		case UNKNOWNASCII:		return("UNKNOWNASCII");
-		case UNUSED:			return("UNUSED");
-	}
-	sprintf(str,"%02Xh",type);
-	return(str);
-}
-
-ushort SMBCALL smb_hfieldtypelookup(const char* str)
-{
-	ushort type;
-
-	if(isdigit(*str))
-		return((ushort)strtol(str,NULL,0));
-
-	for(type=0;type<=UNUSED;type++)
-		if(stricmp(str,smb_hfieldtype(type))==0)
-			return(type);
-
-	return(UNKNOWN);
-}
-
-char* SMBCALL smb_dfieldtype(ushort type)
-{
-	static char str[8];
-
-	switch(type) {
-		case TEXT_BODY: return("TEXT_BODY");
-		case TEXT_TAIL: return("TEXT_TAIL");
-		case UNUSED:	return("UNUSED");
-	}
-	sprintf(str,"%02Xh",type);
-	return(str);
-}
-
-char* SMBCALL smb_hashsource(uchar type)
-{
-	if(type==TEXT_BODY || type==TEXT_TAIL)
-		return(smb_dfieldtype(type));
-	return(smb_hfieldtype(type));
-}
-
-int SMBCALL smb_updatethread(smb_t* smb, smbmsg_t* remsg, ulong newmsgnum)
-{
-	int			retval=SMB_ERR_NOT_FOUND;
-	ulong		nextmsgnum;
-	smbmsg_t	nextmsg;
-
-	if(!remsg->hdr.thread_first) {	/* New msg is first reply */
-		remsg->hdr.thread_first=newmsgnum;
-		if((retval=smb_lockmsghdr(smb,remsg))!=SMB_SUCCESS)
-			return(retval);
-		retval=smb_putmsghdr(smb,remsg);
-		smb_unlockmsghdr(smb,remsg);
-		return(retval);
-	}
-	
-	/* Search for last reply and extend chain */
-	memset(&nextmsg,0,sizeof(nextmsg));
-	nextmsgnum=remsg->hdr.thread_first;	/* start with first reply */
-	while(1) {
-		nextmsg.idx.offset=0;
-		nextmsg.hdr.number=nextmsgnum;
-		if(smb_getmsgidx(smb, &nextmsg)!=SMB_SUCCESS) /* invalid thread origin */
-			break;
-		if(smb_lockmsghdr(smb,&nextmsg)!=SMB_SUCCESS)
-			break;
-		if(smb_getmsghdr(smb, &nextmsg)!=SMB_SUCCESS) {
-			smb_unlockmsghdr(smb,&nextmsg); 
-			break;
-		}
-		if(nextmsg.hdr.thread_next && nextmsg.hdr.thread_next!=nextmsgnum) {
-			nextmsgnum=nextmsg.hdr.thread_next;
-			smb_unlockmsghdr(smb,&nextmsg);
-			smb_freemsgmem(&nextmsg);
-			continue; 
-		}
-		nextmsg.hdr.thread_next=newmsgnum;
-		retval=smb_putmsghdr(smb,&nextmsg);
-		smb_unlockmsghdr(smb,&nextmsg);
-		smb_freemsgmem(&nextmsg);
-		break; 
-	}
-
-	return(retval);
-}
-
-/**************************/
-/* Hash-related functions */
-/**************************/
-
-/* If return value is SMB_ERROR_NOT_FOUND, hash file is left open */
-int SMBCALL smb_findhash(smb_t* smb, hash_t** compare, hash_t* found_hash, BOOL mark)
-{
-	int		retval;
-	BOOL	found=FALSE;
-	size_t	c,count;
-	hash_t	hash;
-
-	if(found_hash!=NULL)
-		memset(found_hash,0,sizeof(hash_t));
-
-	if((retval=smb_open_hash(smb))!=SMB_SUCCESS)
-		return(retval);
-
-	COUNT_LIST_ITEMS(compare, count);
-
-	if(count) {
-
-		rewind(smb->hash_fp);
-		while(!feof(smb->hash_fp)) {
-			memset(&hash,0,sizeof(hash));
-			if(smb_fread(smb,&hash,sizeof(hash),smb->hash_fp)!=sizeof(hash))
-				break;
-
-			if(hash.flags==0)
-				continue;		/* invalid hash record (!?) */
-
-			for(c=0;compare[c]!=NULL;c++) {
-
-				if(compare[c]->source!=hash.source)
-					continue;	/* wrong source */
-				if(compare[c]->flags&SMB_HASH_MARKED)
-					continue;	/* already marked */
-				if((compare[c]->flags&SMB_HASH_PROC_MASK)!=(hash.flags&SMB_HASH_PROC_MASK))
-					continue;	/* wrong pre-process flags */
-				if((compare[c]->flags&hash.flags&SMB_HASH_MASK)==0)	
-					continue;	/* no matching hashes */
-				if(compare[c]->flags&hash.flags&SMB_HASH_CRC16 
-					&& compare[c]->crc16!=hash.crc16)
-					continue;	/* wrong crc-16 */
-				if(compare[c]->flags&hash.flags&SMB_HASH_CRC32
-					&& compare[c]->crc32!=hash.crc32)
-					continue;	/* wrong crc-32 */
-				if(compare[c]->flags&hash.flags&SMB_HASH_MD5 
-					&& memcmp(compare[c]->md5,hash.md5,sizeof(hash.md5)))
-					continue;	/* wrong crc-16 */
-				
-				/* successful match! */
-				break;	/* can't match more than one, so stop comparing */
-			}
-
-			if(compare[c]==NULL)
-				continue;	/* no match */
-
-			found=TRUE;
-
-			if(found_hash!=NULL)
-				memcpy(found_hash,&hash,sizeof(hash));
-
-			if(!mark)
-				break;
-
-			compare[c]->flags|=SMB_HASH_MARKED;
-		}
-		if(found) {
-			smb_close_hash(smb);
-			return(SMB_SUCCESS);
-		}
-	}
-
-	/* hash file left open */
-	return(SMB_ERR_NOT_FOUND);
-}
-
-int SMBCALL smb_addhashes(smb_t* smb, hash_t** hashes, BOOL skip_marked)
-{
-	int		retval;
-	size_t	h;
-
-	COUNT_LIST_ITEMS(hashes, h);
-	if(!h)	/* nothing to add */
-		return(SMB_SUCCESS);
-
-	if((retval=smb_open_hash(smb))!=SMB_SUCCESS)
-		return(retval);
-
-	fseek(smb->hash_fp,0,SEEK_END);
-
-	for(h=0;hashes[h]!=NULL;h++) {
-
-		/* skip hashes marked by smb_findhash() */
-		if(skip_marked && hashes[h]->flags&SMB_HASH_MARKED)	
-			continue;	
-	
-		/* can't think of any reason to strip SMB_HASH_MARKED flag right now */
-		if(smb_fwrite(smb,hashes[h],sizeof(hash_t),smb->hash_fp)!=sizeof(hash_t))
-			return(SMB_ERR_WRITE);
-	}
-
-	smb_close_hash(smb);
-
-	return(SMB_SUCCESS);
-}
-
-static char* strip_chars(uchar* str, uchar* set)
-{
-	char*	src;
-	char*	dst;
-	char*	tmp;
-
-	if((tmp=strdup(str))==NULL)
-		return(NULL);
-	for(src=tmp,dst=str;*src;src++) {
-		if(strchr(set,*src)==NULL)
-			*(dst++)=*src;
-	}
-	*dst=0;
-	
-	return(str);
-}
-
-/* Allocates and calculates hashes of data (based on flags)					*/
-/* Returns NULL on failure													*/
-hash_t* SMBCALL smb_hash(ulong msgnum, ulong t, unsigned source, unsigned flags
-						 ,const void* data, size_t length)
-{
-	hash_t*	hash;
-
-	if((hash=(hash_t*)malloc(sizeof(hash_t)))==NULL)
-		return(NULL);
-
-	hash->number=msgnum;
-	hash->time=t;
-	hash->source=source;
-	hash->flags=flags;
-	if(flags&SMB_HASH_CRC16)
-		hash->crc16=crc16((char*)data,length);
-	if(flags&SMB_HASH_CRC32)
-		hash->crc32=crc32((char*)data,length);
-	if(flags&SMB_HASH_MD5)
-		MD5_calc(hash->md5,data,length);
-
-	return(hash);
-}
-
-/* Allocates and calculates hashes of data (based on flags)					*/
-/* Supports string hash "pre-processing" (e.g. lowercase, strip whitespace)	*/
-/* Returns NULL on failure													*/
-hash_t* SMBCALL smb_hashstr(ulong msgnum, ulong t, unsigned source, unsigned flags
-							,const char* str)
-{
-	char*	p=(uchar*)str;
-	hash_t*	hash;
-
-	if(flags&SMB_HASH_PROC_MASK) {	/* string pre-processing */
-		if((p=strdup(str))==NULL)
-			return(NULL);
-		if(flags&SMB_HASH_LOWERCASE)
-			strlwr(p);
-		if(flags&SMB_HASH_STRIP_WSP)
-			strip_chars(p," \t\r\n");
-	}
-	
-	hash=smb_hash(msgnum, t, source, flags, p, strlen(p));
-
-	if(p!=str)	/* duped string */
-		free(p);
-
-	return(hash);
-}
-
-/* Allocatese and calculates all hashes for a single message				*/
-/* Returns NULL on failure													*/
-hash_t** SMBCALL smb_msghashes(smbmsg_t* msg, const uchar* text, BOOL dupechk)
-{
-	size_t		h=0;
-	uchar		flags=SMB_HASH_CRC16|SMB_HASH_CRC32|SMB_HASH_MD5;
-	hash_t**	hashes;	/* This is a NULL-terminated list of hashes */
-	hash_t*		hash;
-	time_t		t=time(NULL);
-
-#define SMB_MAX_HASH_COUNT 4
-
-	if((hashes=(hash_t**)malloc(sizeof(hash_t*)*SMB_MAX_HASH_COUNT))==NULL)
-		return(NULL);
-
-	memset(hashes, 0, sizeof(hash_t*)*SMB_MAX_HASH_COUNT);
-
-	if(msg->id!=NULL
-		&& (hash=smb_hashstr(msg->hdr.number, t, RFC822MSGID, flags, msg->id))!=NULL)
-		hashes[h++]=hash;
-
-	if(msg->ftn_msgid!=NULL
-		&& (hash=smb_hashstr(msg->hdr.number, t, FIDOMSGID, flags, msg->ftn_msgid))!=NULL)
-		hashes[h++]=hash;
-
-	flags|=SMB_HASH_STRIP_WSP;
-	if(!dupechk)
-		flags|=SMB_HASH_MARKED;	/* ignore for dupe checks */
-	if(text!=NULL
-		&& (hash=smb_hashstr(msg->hdr.number, t, TEXT_BODY, flags, text))!=NULL)
-		hashes[h++]=hash;
-
-	return(hashes);
-}
-
-/* Calculates and stores the hashes for a single message					*/
-int SMBCALL smb_hashmsg(smb_t* smb, smbmsg_t* msg, const uchar* text, BOOL update)
-{
-	size_t		n;
-	int			retval=SMB_SUCCESS;
-	hash_t		found;
-	hash_t**	hashes;	/* This is a NULL-terminated list of hashes */
-
-	hashes=smb_msghashes(msg,text,/* dupechk? */TRUE);
-
-	if(smb_findhash(smb, hashes, &found, update)==SMB_SUCCESS && !update) {
-		retval=SMB_DUPE_MSG;
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"duplicate %s hash found (message #%lu)"
-			,smb_hashsource(found.source), found.number);
-	} else
-		if((retval=smb_addhashes(smb,hashes,/* skip_marked? */TRUE))==SMB_SUCCESS)
-			msg->flags|=MSG_FLAG_HASHED;
-
-	FREE_LIST(hashes,n);
-
-	return(retval);
-}
-
-/* length=0 specifies ASCIIZ data											*/
-int SMBCALL smb_getmsgidx_by_hash(smb_t* smb, smbmsg_t* msg, unsigned source
-								 ,unsigned flags, const void* data, size_t length)
-{
-	int			retval;
-	size_t		n;
-	hash_t**	hashes;
-	hash_t		found;
-
-	if((hashes=(hash_t**)malloc(sizeof(hash_t*)*2))==NULL)
-		return(SMB_ERR_MEM);
-
-	if(length==0)
-		hashes[0]=smb_hashstr(0,0,source,flags,data);
-	else
-		hashes[0]=smb_hash(0,0,source,flags,data,length);
-	if(hashes[0]==NULL)
-		return(SMB_ERR_MEM);
-
-	hashes[1]=NULL;	/* terminate list */
-
-	memset(&found,0,sizeof(found));
-	if((retval=smb_findhash(smb, hashes, &found, FALSE))==SMB_SUCCESS) {
-		if(found.number==0)
-			retval=SMB_FAILURE;	/* use better error value here? */
-		else {
-			msg->hdr.number=found.number;
-			retval=smb_getmsgidx(smb, msg);
-		}
-	}
-
-	FREE_LIST(hashes,n);
-
-	return(retval);
-}
-
-int SMBCALL smb_getmsghdr_by_hash(smb_t* smb, smbmsg_t* msg, unsigned source
-								 ,unsigned flags, const void* data, size_t length)
-{
-	int retval;
-
-	if((retval=smb_getmsgidx_by_hash(smb,msg,source,flags,data,length))!=SMB_SUCCESS)
-		return(retval);
-
-	if((retval=smb_lockmsghdr(smb,msg))!=SMB_SUCCESS)
-		return(retval);
-
-	retval=smb_getmsghdr(smb,msg);
-
-	smb_unlockmsghdr(smb,msg); 
-
-	return(retval);
-}
-
-
-ushort SMBCALL smb_subject_crc(const char *subj)
-{
-	char*	str;
-	ushort	crc;
-
-	while(!strnicmp(subj,"RE:",3)) {
-		subj+=3;
-		while(*subj==' ')
-			subj++; 
-	}
-
-	if((str=strdup(subj))==NULL)
-		return(0xffff);
-
-	strlwr(str);
-	crc=crc16(str,0	/* auto-length */);
-	free(str);
-
-	return(crc);
-}
-
-/****************************************************************************/
-/* Returns an ASCII string for FidoNet address 'addr'                       */
-/****************************************************************************/
-char* SMBCALL smb_faddrtoa(fidoaddr_t* addr, char* outstr)
-{
-	static char str[64];
-    char point[25];
-
-	if(addr==NULL)
-		return("0:0/0");
-	sprintf(str,"%hu:%hu/%hu",addr->zone,addr->net,addr->node);
-	if(addr->point) {
-		sprintf(point,".%hu",addr->point);
-		strcat(str,point); 
-	}
-	if(outstr==NULL)
-		return(str);
-	strcpy(outstr,str);
-	return(outstr);
-}
-
-char* SMBCALL smb_netaddr(net_t* net)
-{
-	if(net->type==NET_FIDO)
-		return(smb_faddrtoa((fidoaddr_t*)net->addr,NULL));
-	return(net->addr);
-}
-
-/****************************************************************************/
-/* Converts when_t.zone into ASCII format                                   */
-/****************************************************************************/
-char* SMBCALL smb_zonestr(short zone, char* outstr)
-{
-	char*		plus;
-    static char str[32];
-
-	switch((ushort)zone) {
-		case 0:     return("UTC");
-		case AST:   return("AST");
-		case EST:   return("EST");
-		case CST:   return("CST");
-		case MST:   return("MST");
-		case PST:   return("PST");
-		case YST:   return("YST");
-		case HST:   return("HST");
-		case BST:   return("BST");
-		case ADT:   return("ADT");
-		case EDT:   return("EDT");
-		case CDT:   return("CDT");
-		case MDT:   return("MDT");
-		case PDT:   return("PDT");
-		case YDT:   return("YDT");
-		case HDT:   return("HDT");
-		case BDT:   return("BDT");
-		case MID:   return("MID");
-		case VAN:   return("VAN");
-		case EDM:   return("EDM");
-		case WIN:   return("WIN");
-		case BOG:   return("BOG");
-		case CAR:   return("CAR");
-		case RIO:   return("RIO");
-		case FER:   return("FER");
-		case AZO:   return("AZO");
-		case LON:   return("LON");
-		case BER:   return("BER");
-		case ATH:   return("ATH");
-		case MOS:   return("MOS");
-		case DUB:   return("DUB");
-		case KAB:   return("KAB");
-		case KAR:   return("KAR");
-		case BOM:   return("BOM");
-		case KAT:   return("KAT");
-		case DHA:   return("DHA");
-		case BAN:   return("BAN");
-		case HON:   return("HON");
-		case TOK:   return("TOK");
-		case SYD:   return("SYD");
-		case NOU:   return("NOU");
-		case WEL:   return("WEL");
-		}
-
-	if(!OTHER_ZONE(zone)) {
-		if(zone&(WESTERN_ZONE|US_ZONE))	/* West of UTC? */
-			zone=-(zone&0xfff);
-		else
-			zone&=0xfff;
-	}
-
-	if(zone>0)
-		plus="+";
-	else
-		plus="";
-	sprintf(str,"UTC%s%d:%02u", plus, zone/60, zone<0 ? (-zone)%60 : zone%60);
-
-	if(outstr==NULL)
-		return(str);
-	strcpy(outstr,str);
-	return(outstr);
-}
-
-
-
 /* End of SMBLIB.C */
diff --git a/src/smblib/smblib.dsp b/src/smblib/smblib.dsp
index c579f7a171..3202515726 100644
--- a/src/smblib/smblib.dsp
+++ b/src/smblib/smblib.dsp
@@ -98,14 +98,34 @@ SOURCE=.\md5.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\smbadd.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\smballoc.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\smbdump.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\smbfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\smbhash.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\smblib.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\smbstr.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\smbtxt.c
 # End Source File
 # End Target
diff --git a/src/smblib/smblib.h b/src/smblib/smblib.h
index 4f550da6bc..685f117240 100644
--- a/src/smblib/smblib.h
+++ b/src/smblib/smblib.h
@@ -106,8 +106,6 @@ SMBEXPORT int 		SMBCALL smb_ver(void);
 SMBEXPORT char*		SMBCALL smb_lib_ver(void);
 SMBEXPORT int 		SMBCALL smb_open(smb_t* smb);
 SMBEXPORT void		SMBCALL smb_close(smb_t* smb);
-SMBEXPORT int 		SMBCALL smb_open_fp(smb_t* smb, FILE**, int share);
-SMBEXPORT void		SMBCALL smb_close_fp(FILE**);
 SMBEXPORT int 		SMBCALL smb_create(smb_t* smb);
 SMBEXPORT int 		SMBCALL smb_stack(smb_t* smb, int op);
 SMBEXPORT int 		SMBCALL smb_trunchdr(smb_t* smb);
@@ -135,12 +133,7 @@ SMBEXPORT int		SMBCALL smb_hfield_append_str(smbmsg_t* msg, ushort type, const c
 SMBEXPORT int		SMBCALL smb_hfield_addlist(smbmsg_t* msg, hfield_t** hfield_list, void** hfield_dat);
 SMBEXPORT int 		SMBCALL smb_dfield(smbmsg_t* msg, ushort type, ulong length);
 SMBEXPORT void*		SMBCALL smb_get_hfield(smbmsg_t* msg, ushort type, hfield_t* hfield);
-SMBEXPORT char*		SMBCALL smb_hfieldtype(ushort type);
-SMBEXPORT ushort	SMBCALL smb_hfieldtypelookup(const char*);
-SMBEXPORT char*		SMBCALL smb_dfieldtype(ushort type);
 SMBEXPORT int 		SMBCALL smb_addmsghdr(smb_t* smb, smbmsg_t* msg, int storage);
-SMBEXPORT int		SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, BOOL dupechk
-						,ushort xlat, const uchar* body, const uchar* tail);
 SMBEXPORT int 		SMBCALL smb_putmsg(smb_t* smb, smbmsg_t* msg);
 SMBEXPORT int 		SMBCALL smb_putmsgidx(smb_t* smb, smbmsg_t* msg);
 SMBEXPORT int 		SMBCALL smb_putmsghdr(smb_t* smb, smbmsg_t* msg);
@@ -148,6 +141,16 @@ SMBEXPORT void		SMBCALL smb_freemsgmem(smbmsg_t* msg);
 SMBEXPORT void		SMBCALL smb_freemsghdrmem(smbmsg_t* msg);
 SMBEXPORT ulong		SMBCALL smb_hdrblocks(ulong length);
 SMBEXPORT ulong		SMBCALL smb_datblocks(ulong length);
+SMBEXPORT int		SMBCALL	smb_copymsgmem(smb_t* smb, smbmsg_t* destmsg, smbmsg_t* srcmsg);
+SMBEXPORT int		SMBCALL smb_tzutc(short timezone);
+SMBEXPORT int		SMBCALL smb_updatethread(smb_t* smb, smbmsg_t* remsg, ulong newmsgnum);
+SMBEXPORT BOOL		SMBCALL smb_valid_hdr_offset(smb_t* smb, ulong offset);
+
+/* smbadd.c */
+SMBEXPORT int		SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, BOOL dupechk
+						,ushort xlat, const uchar* body, const uchar* tail);
+
+/* smballoc.c */
 SMBEXPORT long		SMBCALL smb_allochdr(smb_t* smb, ulong length);
 SMBEXPORT long		SMBCALL smb_fallochdr(smb_t* smb, ulong length);
 SMBEXPORT long		SMBCALL smb_hallochdr(smb_t* smb);
@@ -161,25 +164,18 @@ SMBEXPORT int		SMBCALL smb_freemsg_dfields(smb_t* smb, smbmsg_t* msg, ushort ref
 SMBEXPORT int 		SMBCALL smb_freemsgdat(smb_t* smb, ulong offset, ulong length, ushort refs);
 SMBEXPORT int 		SMBCALL smb_freemsghdr(smb_t* smb, ulong offset, ulong length);
 SMBEXPORT void		SMBCALL smb_freemsgtxt(char* buf);
-SMBEXPORT int		SMBCALL	smb_copymsgmem(smb_t* smb, smbmsg_t* destmsg, smbmsg_t* srcmsg);
-SMBEXPORT int		SMBCALL smb_tzutc(short timezone);
-SMBEXPORT int		SMBCALL smb_updatethread(smb_t* smb, smbmsg_t* remsg, ulong newmsgnum);
-SMBEXPORT ushort	SMBCALL smb_subject_crc(const char *subj);
-
-SMBEXPORT char*		SMBCALL smb_faddrtoa(fidoaddr_t* addr, char* outstr);
-SMBEXPORT char*		SMBCALL smb_netaddr(net_t* net);
-SMBEXPORT char*		SMBCALL smb_zonestr(short zone, char* outstr);
 
-/* hash-related functions */
+/* smbhash.c */
 SMBEXPORT int		SMBCALL smb_findhash(smb_t* smb, hash_t** compare_list, hash_t* found, BOOL mark);
 SMBEXPORT int		SMBCALL smb_hashmsg(smb_t* smb, smbmsg_t* msg, const uchar* text, BOOL update);
 SMBEXPORT hash_t*	SMBCALL	smb_hash(ulong msgnum, ulong time, unsigned source
 								,unsigned flags, const void* data, size_t length);
 SMBEXPORT hash_t*	SMBCALL	smb_hashstr(ulong msgnum, ulong time, unsigned source
 								,unsigned flags, const char* str);
-SMBEXPORT char*		SMBCALL smb_hashsource(uchar type);
+
 SMBEXPORT hash_t**	SMBCALL smb_msghashes(smbmsg_t* msg, const uchar* text, BOOL dupechk);
 SMBEXPORT int		SMBCALL smb_addhashes(smb_t* smb, hash_t** hash_list, BOOL skip_marked);
+SMBEXPORT ushort	SMBCALL smb_subject_crc(const char *subj);
 
 /* Fast look-up functions (using hashes) */
 SMBEXPORT int 		SMBCALL smb_getmsgidx_by_hash(smb_t* smb, smbmsg_t* msg, unsigned source
@@ -203,14 +199,22 @@ SMBEXPORT int 		SMBCALL smb_getmsghdr_by_hash(smb_t* smb, smbmsg_t* msg, unsigne
 #define smb_getmsghdr_by_ftnid(smb, msg, id) \
 		smb_getmsghdr_by_hashstr(smb, msg, FIDOMSGID, SMB_HASH_MASK, id)
 
-/* smbtxt.c */
-SMBEXPORT char*		SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode);
+/* smbstr.c */
+SMBEXPORT char*		SMBCALL smb_hfieldtype(ushort type);
+SMBEXPORT ushort	SMBCALL smb_hfieldtypelookup(const char*);
+SMBEXPORT char*		SMBCALL smb_dfieldtype(ushort type);
+SMBEXPORT char*		SMBCALL smb_faddrtoa(fidoaddr_t* addr, char* outstr);
+SMBEXPORT char*		SMBCALL smb_netaddr(net_t* net);
+SMBEXPORT char*		SMBCALL smb_zonestr(short zone, char* outstr);
+SMBEXPORT char*		SMBCALL smb_hashsource(uchar type);
 
 /* smbdump.c */
 SMBEXPORT void		SMBCALL smb_dump_msghdr(FILE* fp, smbmsg_t* msg);
 
-/* FILE pointer I/O functions */
+/* smbtxt.c */
+SMBEXPORT char*		SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode);
 
+/* smbfile.c */
 SMBEXPORT int 		SMBCALL smb_feof(FILE* fp);
 SMBEXPORT int 		SMBCALL smb_ferror(FILE* fp);
 SMBEXPORT int 		SMBCALL smb_fflush(FILE* fp);
@@ -224,6 +228,8 @@ SMBEXPORT long		SMBCALL smb_fgetlength(FILE* fp);
 SMBEXPORT int 		SMBCALL smb_fsetlength(FILE* fp, long length);
 SMBEXPORT void		SMBCALL smb_rewind(FILE* fp);
 SMBEXPORT void		SMBCALL smb_clearerr(FILE* fp);
+SMBEXPORT int 		SMBCALL smb_open_fp(smb_t* smb, FILE**, int share);
+SMBEXPORT void		SMBCALL smb_close_fp(FILE**);
 					
 #ifdef __cplusplus
 }
diff --git a/src/smblib/smblib_mt.dsp b/src/smblib/smblib_mt.dsp
index 40a32b15b0..6a664b9619 100644
--- a/src/smblib/smblib_mt.dsp
+++ b/src/smblib/smblib_mt.dsp
@@ -98,14 +98,34 @@ SOURCE=.\md5.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\smbadd.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\smballoc.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\smbdump.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\smbfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\smbhash.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\smblib.c
 # End Source File
 # Begin Source File
 
+SOURCE=.\smbstr.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\smbtxt.c
 # End Source File
 # End Target
diff --git a/src/smblib/smbstr.c b/src/smblib/smbstr.c
new file mode 100644
index 0000000000..a74b146cc7
--- /dev/null
+++ b/src/smblib/smbstr.c
@@ -0,0 +1,245 @@
+/* smbstr.c */
+
+/* Synchronet message base (SMB) library routines returning strings */
+
+/* $Id$ */
+
+/****************************************************************************
+ * @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		*
+ *																			*
+ * This library is free software; you can redistribute it and/or			*
+ * modify it under the terms of the GNU Lesser General Public License		*
+ * as published by the Free Software Foundation; either version 2			*
+ * of the License, or (at your option) any later version.					*
+ * See the GNU Lesser General Public License for more details: lgpl.txt or	*
+ * http://www.fsf.org/copyleft/lesser.html									*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#include "smblib.h"
+
+char* SMBCALL smb_hfieldtype(ushort type)
+{
+	static char str[8];
+
+	switch(type) {
+		case SENDER:			return("Sender");
+		case SENDERAGENT:		return("SenderAgent");
+		case SENDERNETTYPE:		return("SenderNetType");
+		case SENDERNETADDR:		return("SenderNetAddr");
+		case SENDEREXT:			return("SenderExt");
+		case SENDERORG:			return("SenderOrg");
+		case SENDERIPADDR:		return("SenderIpAddr");
+		case SENDERHOSTNAME:	return("SenderHostName");
+		case SENDERPROTOCOL:	return("SenderProtocol");
+		case SENDERPORT:		return("SenderPort");
+
+		case REPLYTO:			return("ReplyTo");
+		case REPLYTOAGENT:		return("ReplyToAgent");
+		case REPLYTONETTYPE:	return("ReplyToNetType");
+		case REPLYTONETADDR:	return("ReplyToNetAddr");
+		case REPLYTOEXT:		return("ReplyToExt");
+								
+		case RECIPIENT:			return("Recipient");
+		case RECIPIENTAGENT:	return("RecipientAgent");
+		case RECIPIENTNETTYPE:	return("RecipientNetType");
+		case RECIPIENTNETADDR:	return("RecipientNetAddr");
+		case RECIPIENTEXT:		return("RecipientExt");
+
+		case SUBJECT:			return("Subject");
+		case SMB_SUMMARY:		return("Summary");
+		case SMB_COMMENT:		return("Comment");
+		case SMB_CARBONCOPY:	return("CarbonCopy");
+		case SMB_GROUP:			return("Group");
+		case SMB_EXPIRATION:	return("Expiration");
+		case SMB_PRIORITY:		return("Priority");
+		case SMB_COST:			return("Cost");
+
+		case FIDOCTRL:			return("FidoCtrl");
+		case FIDOAREA:			return("FidoArea");
+		case FIDOSEENBY:		return("FidoSeenBy");
+		case FIDOPATH:			return("FidoPath");
+		case FIDOMSGID:			return("FidoMsgID");
+		case FIDOREPLYID:		return("FidoReplyID");
+		case FIDOPID:			return("FidoPID");
+		case FIDOFLAGS:			return("FidoFlags");
+		case FIDOTID:			return("FidoTID");
+
+		case RFC822HEADER:		return("RFC822Header");
+		case RFC822MSGID:		return("RFC822MsgID");
+		case RFC822REPLYID:		return("RFC822ReplyID");
+		case RFC822TO:			return("RFC822To");
+		case RFC822FROM:		return("RFC822From");
+		case RFC822REPLYTO:		return("RFC822ReplyTo");
+
+		case USENETPATH:		return("UsenetPath");
+		case USENETNEWSGROUPS:	return("UsenetNewsgroups");
+
+		case SMTPCOMMAND:		return("SMTPCommand");
+		case SMTPREVERSEPATH:	return("SMTPReversePath");
+
+		case SMTPSYSMSG:		return("SMTPSysMsg");
+
+		case UNKNOWN:			return("UNKNOWN");
+		case UNKNOWNASCII:		return("UNKNOWNASCII");
+		case UNUSED:			return("UNUSED");
+	}
+	sprintf(str,"%02Xh",type);
+	return(str);
+}
+
+ushort SMBCALL smb_hfieldtypelookup(const char* str)
+{
+	ushort type;
+
+	if(isdigit(*str))
+		return((ushort)strtol(str,NULL,0));
+
+	for(type=0;type<=UNUSED;type++)
+		if(stricmp(str,smb_hfieldtype(type))==0)
+			return(type);
+
+	return(UNKNOWN);
+}
+
+char* SMBCALL smb_dfieldtype(ushort type)
+{
+	static char str[8];
+
+	switch(type) {
+		case TEXT_BODY: return("TEXT_BODY");
+		case TEXT_TAIL: return("TEXT_TAIL");
+		case UNUSED:	return("UNUSED");
+	}
+	sprintf(str,"%02Xh",type);
+	return(str);
+}
+
+char* SMBCALL smb_hashsource(uchar type)
+{
+	if(type==TEXT_BODY || type==TEXT_TAIL)
+		return(smb_dfieldtype(type));
+	return(smb_hfieldtype(type));
+}
+
+/****************************************************************************/
+/* Converts when_t.zone into ASCII format                                   */
+/****************************************************************************/
+char* SMBCALL smb_zonestr(short zone, char* outstr)
+{
+	char*		plus;
+    static char str[32];
+
+	switch((ushort)zone) {
+		case 0:     return("UTC");
+		case AST:   return("AST");
+		case EST:   return("EST");
+		case CST:   return("CST");
+		case MST:   return("MST");
+		case PST:   return("PST");
+		case YST:   return("YST");
+		case HST:   return("HST");
+		case BST:   return("BST");
+		case ADT:   return("ADT");
+		case EDT:   return("EDT");
+		case CDT:   return("CDT");
+		case MDT:   return("MDT");
+		case PDT:   return("PDT");
+		case YDT:   return("YDT");
+		case HDT:   return("HDT");
+		case BDT:   return("BDT");
+		case MID:   return("MID");
+		case VAN:   return("VAN");
+		case EDM:   return("EDM");
+		case WIN:   return("WIN");
+		case BOG:   return("BOG");
+		case CAR:   return("CAR");
+		case RIO:   return("RIO");
+		case FER:   return("FER");
+		case AZO:   return("AZO");
+		case LON:   return("LON");
+		case BER:   return("BER");
+		case ATH:   return("ATH");
+		case MOS:   return("MOS");
+		case DUB:   return("DUB");
+		case KAB:   return("KAB");
+		case KAR:   return("KAR");
+		case BOM:   return("BOM");
+		case KAT:   return("KAT");
+		case DHA:   return("DHA");
+		case BAN:   return("BAN");
+		case HON:   return("HON");
+		case TOK:   return("TOK");
+		case SYD:   return("SYD");
+		case NOU:   return("NOU");
+		case WEL:   return("WEL");
+		}
+
+	if(!OTHER_ZONE(zone)) {
+		if(zone&(WESTERN_ZONE|US_ZONE))	/* West of UTC? */
+			zone=-(zone&0xfff);
+		else
+			zone&=0xfff;
+	}
+
+	if(zone>0)
+		plus="+";
+	else
+		plus="";
+	sprintf(str,"UTC%s%d:%02u", plus, zone/60, zone<0 ? (-zone)%60 : zone%60);
+
+	if(outstr==NULL)
+		return(str);
+	strcpy(outstr,str);
+	return(outstr);
+}
+
+/****************************************************************************/
+/* Returns an ASCII string for FidoNet address 'addr'                       */
+/****************************************************************************/
+char* SMBCALL smb_faddrtoa(fidoaddr_t* addr, char* outstr)
+{
+	static char str[64];
+    char point[25];
+
+	if(addr==NULL)
+		return("0:0/0");
+	sprintf(str,"%hu:%hu/%hu",addr->zone,addr->net,addr->node);
+	if(addr->point) {
+		sprintf(point,".%hu",addr->point);
+		strcat(str,point); 
+	}
+	if(outstr==NULL)
+		return(str);
+	strcpy(outstr,str);
+	return(outstr);
+}
+
+/****************************************************************************/
+/* Returns ASCIIZ representation of network address (net_t)					*/
+/****************************************************************************/
+char* SMBCALL smb_netaddr(net_t* net)
+{
+	if(net->type==NET_FIDO)
+		return(smb_faddrtoa((fidoaddr_t*)net->addr,NULL));
+	return(net->addr);
+}
-- 
GitLab