diff --git a/src/smblib/smbdefs.h b/src/smblib/smbdefs.h
index 6ce72f999af4f94e0b1cf824679288e58e9c8543..a4fa1a3ead05cccc06d217937b9987bcad51a229 100644
--- a/src/smblib/smbdefs.h
+++ b/src/smblib/smbdefs.h
@@ -385,7 +385,7 @@ enum {
 /* Add new ones here */
 
     ,NET_TYPES
-    };
+};
 
 enum {
      AGENT_PERSON
@@ -396,7 +396,7 @@ enum {
 /* Add new ones here */
 
     ,AGENT_TYPES
-    };
+};
 
 enum {
      XLAT_NONE              /* No translation/End of translation list */
@@ -413,7 +413,7 @@ enum {
 /* Add new ones here */
 
     ,XLAT_TYPES
-    };
+};
 
 
 /************/
@@ -439,7 +439,7 @@ typedef struct _PACK {		/* Time with time-zone */
 	ulong	time;			/* Local time (unix format) */
 	short	zone;			/* Time zone */
 
-	} when_t;
+} when_t;
 
 typedef struct _PACK {		/* Index record */
 
@@ -451,7 +451,28 @@ typedef struct _PACK {		/* Index record */
 	ulong	number; 		/* number of message (1 based) */
 	ulong	time;			/* time/date message was imported/posted */
 
-	} idxrec_t;
+} idxrec_t;
+
+									/* valid bits in hash_t.flags		*/
+#define SMB_HASH_CRC16		(1<<0)	/* CRC-16 hash is valid				*/
+#define SMB_HASH_CRC32		(1<<1)	/* CRC-32 hash is valid				*/
+#define SMB_HASH_MD5		(1<<2)	/* MD5 digest is valid				*/
+#define SMB_HASH_MASK		0x0f	/* which hashes are valid			*/
+#define SMB_HASH_UPPERCASE	(1<<4)	/* Convert a-z to A-Z first			*/
+#define SMB_HASH_LOWERCASE	(1<<5)	/* Convert A-Z to a-z first			*/
+#define SMB_HASH_STRIP_WSP	(1<<6)	/* Strip white-space chars first	*/
+
+typedef struct _PACK {
+
+	ulong	number;					/* Message number */
+	ulong	time;					/* Local time of fingerprinting */
+	uchar	source;					/* (e.g. TEXT_BODY, RFC822MSGID, FIDOMSGID) */
+	uchar	flags;					/* indications of valid hashes and pre-processing */
+	ushort	crc16;					/* CRC-16 of source */
+	ulong	crc32;					/* CRC-32 of source */
+	uchar	md5[MD5_DIGEST_SIZE];	/* MD5 digest of source */
+
+} hash_t;
 
 typedef struct _PACK {		/* Message base header (fixed portion) */
 
@@ -459,7 +480,7 @@ typedef struct _PACK {		/* Message base header (fixed portion) */
     ushort  version;        /* version number (initially 100h for 1.00) */
     ushort  length;         /* length including this struct */
 
-	} smbhdr_t;
+} smbhdr_t;
 
 typedef struct _PACK {		/* Message base status header */
 
@@ -471,7 +492,7 @@ typedef struct _PACK {		/* Message base status header */
     ushort  max_age;        /* Maximum age of message to keep in sub (in days) */
 	ushort	attr;			/* Attributes for this message base (SMB_HYPER,etc) */
 
-	} smbstatus_t;
+} smbstatus_t;
 
 typedef struct _PACK {		/* Message header */
 
@@ -495,7 +516,7 @@ typedef struct _PACK {		/* Message header */
     /* 40 */ ulong	offset;				/* Offset for buffer into data file (0 or mod 256) */
 	/* 44 */ ushort	total_dfields;		/* Total number of data fields */
 
-	} msghdr_t;
+} msghdr_t;
 
 #define thread_orig	thread_back	/* for backwards compatibility with older code */
 
@@ -505,14 +526,14 @@ typedef struct _PACK {		/* Data field */
     ulong   offset;         /* Offset into buffer */ 
     ulong   length;         /* Length of data field */
 
-    } dfield_t;
+} dfield_t;
 
 typedef struct _PACK {		/* Header field */
 
 	ushort	type;
 	ushort	length; 		/* Length of buffer */
 
-	} hfield_t;
+} hfield_t;
 
 typedef struct _PACK {		/* FidoNet address (zone:net/node.point) */
 
@@ -521,7 +542,7 @@ typedef struct _PACK {		/* FidoNet address (zone:net/node.point) */
 	ushort	node;
 	ushort	point;
 
-    } fidoaddr_t;
+} fidoaddr_t;
 
 #if defined(PRAGMA_PACK)
 #pragma pack(pop)		/* original packing */
@@ -532,7 +553,7 @@ typedef struct _PACK {		/* Network (type and address) */
     ushort  type;
 	void	*addr;
 
-	} net_t;
+} net_t;
 
 typedef struct {				/* Message */
 
@@ -574,16 +595,17 @@ typedef struct {				/* Message */
 	ulong		priority;		/* Message priority (0 is lowest) */
 	ulong		cost;			/* Cost to download/read */
 
-	} smbmsg_t;
+} smbmsg_t;
 
 typedef struct {			/* Message base */
 
     char    file[128];      /* Path and base filename (no extension) */
-    FILE    *sdt_fp;        /* File pointer for data (.sdt) file */
-    FILE    *shd_fp;        /* File pointer for header (.shd) file */
-    FILE    *sid_fp;        /* File pointer for index (.sid) file */
-    FILE    *sda_fp;        /* File pointer for data allocation (.sda) file */
-    FILE    *sha_fp;        /* File pointer for header allocation (.sha) file */
+    FILE*	sdt_fp;			/* File pointer for data (.sdt) file */
+    FILE*	shd_fp;			/* File pointer for header (.shd) file */
+    FILE*	sid_fp;			/* File pointer for index (.sid) file */
+    FILE*	sda_fp;			/* File pointer for data allocation (.sda) file */
+    FILE*	sha_fp;			/* File pointer for header allocation (.sha) file */
+	FILE*	hash_fp;		/* File pointer for hash (.hash) file */
 	ulong	retry_time; 	/* Maximum number of seconds to retry opens/locks */
 	ulong	retry_delay;	/* Time-slice yield (milliseconds) while retrying */
 	smbstatus_t status; 	/* Status header record */
@@ -596,6 +618,6 @@ typedef struct {			/* Message base */
 	long	msgs;			/* Number of messages loaded (for user) */
 	long	curmsg;			/* Current message number (for user) */
 
-    } smb_t;
+} smb_t;
 
 #endif /* Don't add anything after this #endif statement */
diff --git a/src/smblib/smblib.c b/src/smblib/smblib.c
index e36ce2d4c56a559059a16efe3474501b0e4094b2..c6fd005edfa5e8d643d8bb832362a029290a0128 100644
--- a/src/smblib/smblib.c
+++ b/src/smblib/smblib.c
@@ -53,7 +53,7 @@
 #include "filewrap.h"
 
 /* Use smb_ver() and smb_lib_ver() to obtain these values */
-#define SMBLIB_VERSION		"2.31"      /* SMB library version */
+#define SMBLIB_VERSION		"2.40"      /* SMB library version */
 #define SMB_VERSION 		0x0121		/* SMB format version */
 										/* High byte major, low byte minor */
 
@@ -106,8 +106,9 @@ int SMBCALL smb_open(smb_t* smb)
 		|| smb->retry_delay>(smb->retry_time*100))	/* at least ten retries */
 		smb->retry_delay=250;	/* milliseconds */
 	smb->shd_fp=smb->sdt_fp=smb->sid_fp=NULL;
+	smb->sha_fp=smb->sda_fp=smb->hash_fp=NULL;
 	smb->last_error[0]=0;
-	sprintf(str,"%s.shd",smb->file);
+	SAFEPRINTF(str,"%s.shd",smb->file);
 	if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,S_IREAD|S_IWRITE))==-1) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%d (%s) opening %s"
@@ -165,7 +166,7 @@ int SMBCALL smb_open(smb_t* smb)
 
 	setvbuf(smb->shd_fp,smb->shd_buf,_IOFBF,SHD_BLOCK_LEN);
 
-	sprintf(str,"%s.sdt",smb->file);
+	SAFEPRINTF(str,"%s.sdt",smb->file);
 	if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,S_IREAD|S_IWRITE))==-1) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%d (%s) opening %s"
@@ -185,7 +186,7 @@ int SMBCALL smb_open(smb_t* smb)
 
 	setvbuf(smb->sdt_fp,NULL,_IOFBF,2*1024);
 
-	sprintf(str,"%s.sid",smb->file);
+	SAFEPRINTF(str,"%s.sid",smb->file);
 	if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,S_IREAD|S_IWRITE))==-1) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%d (%s) opening %s"
@@ -225,74 +226,41 @@ void SMBCALL smb_close(smb_t* smb)
 }
 
 /****************************************************************************/
-/* Opens the data block allocation table message base 'smb->file'           */
+/* 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_da(smb_t* smb)
+int SMBCALL smb_open_fp(smb_t* smb, FILE** fp)
 {
 	int 	file;
-	char	str[MAX_PATH+1];
+	char	path[MAX_PATH+1];
+	char*	ext;
 	time_t	start=0;
 
-	sprintf(str,"%s.sda",smb->file);
-	while(1) {
-		if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYRW,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),str);
-			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)"
-					,str,smb->retry_time);
-				return(SMB_ERR_TIMEOUT); 
-			}
-		SLEEP(smb->retry_delay);
-	}
-	if((smb->sda_fp=fdopen(file,"r+b"))==NULL) {
+	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)
-			,"%d (%s) fdopening %s (%d)"
-			,errno,STRERROR(errno),str,file);
-		close(file);
-		return(SMB_ERR_OPEN); 
+			,"opening %s: Illegal FILE* pointer argument: %p"
+			,smb->file, fp);
+		return(SMB_ERR_OPEN);
 	}
-	setvbuf(smb->sda_fp,NULL,_IOFBF,2*1024);
-	return(SMB_SUCCESS);
-}
-
-void SMBCALL smb_close_da(smb_t* smb)
-{
-	if(smb->sda_fp!=NULL)
-		fclose(smb->sda_fp);
-	smb->sda_fp=NULL;
-}
+	SAFEPRINTF2(path,"%s.%s",smb->file,ext);
 
-/****************************************************************************/
-/* Opens the header block allocation table for message base 'smb.file'      */
-/* Retrys for smb.retry_time number of seconds								*/
-/* Return 0 on success, non-zero otherwise									*/
-/****************************************************************************/
-int SMBCALL smb_open_ha(smb_t* smb)
-{
-	int 	file;
-	char	str[MAX_PATH+1];
-	time_t	start=0;
+	if(*fp!=NULL)	/* Already open! */
+		return(SMB_SUCCESS);
 
-	sprintf(str,"%s.sha",smb->file);
 	while(1) {
-		if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYRW,S_IREAD|S_IWRITE))!=-1)
+		if((file=sopen(path,O_RDWR|O_CREAT|O_BINARY,SH_DENYRW,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),str);
+				,errno,STRERROR(errno),path);
 			return(SMB_ERR_OPEN);
 		}
 		if(!start)
@@ -301,27 +269,29 @@ int SMBCALL smb_open_ha(smb_t* smb)
 			if(time(NULL)-start>=(time_t)smb->retry_time) {
 				safe_snprintf(smb->last_error,sizeof(smb->last_error)
 					,"timeout opening %s (retry_time=%ld)"
-					,str,smb->retry_time);
+					,path,smb->retry_time);
 				return(SMB_ERR_TIMEOUT); 
 			}
 		SLEEP(smb->retry_delay);
 	}
-	if((smb->sha_fp=fdopen(file,"r+b"))==NULL) {
+	if((*fp=fdopen(file,"r+b"))==NULL) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%d (%s) fdopening %s (%d)"
-			,errno,STRERROR(errno),str,file);
+			,errno,STRERROR(errno),path,file);
 		close(file);
 		return(SMB_ERR_OPEN); 
 	}
-	setvbuf(smb->sha_fp,NULL,_IOFBF,2*1024);
+	setvbuf(*fp,NULL,_IOFBF,2*1024);
 	return(SMB_SUCCESS);
 }
 
-void SMBCALL smb_close_ha(smb_t* smb)
+void SMBCALL smb_close_fp(FILE** fp)
 {
-	if(smb->sha_fp!=NULL)
-		fclose(smb->sha_fp);
-	smb->sha_fp=NULL;
+	if(fp!=NULL) {
+		if(*fp!=NULL)
+			fclose(*fp);
+		*fp=NULL;
+	}
 }
 
 /****************************************************************************/
@@ -330,9 +300,9 @@ void SMBCALL smb_close_ha(smb_t* smb)
 /* Currently, this is only used while smbutil packs a message base.			*/
 /* This is achieved with a semaphore lock file (e.g. mail.lock).			*/
 /****************************************************************************/
-static char* smb_lockfname(smb_t* smb, char* fname)
+static char* smb_lockfname(smb_t* smb, char* fname, size_t maxlen)
 {
-	sprintf(fname,"%s.lock",smb->file);
+	safe_snprintf(fname,maxlen,"%s.lock",smb->file);
 	return(fname);
 }
 
@@ -341,7 +311,7 @@ int SMBCALL smb_lock(smb_t* smb)
 	char	str[MAX_PATH+1];
 	int		file;
 
-	smb_lockfname(smb,str);
+	smb_lockfname(smb,str,sizeof(str)-1);
 	if((file=open(str,O_CREAT|O_EXCL|O_RDWR,S_IREAD|S_IWRITE))==-1) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%d (%s) creating %s"
@@ -356,7 +326,7 @@ int SMBCALL smb_unlock(smb_t* smb)
 {
 	char	str[MAX_PATH+1];
 
-	smb_lockfname(smb,str);
+	smb_lockfname(smb,str,sizeof(str)-1);
 	if(remove(str)!=0) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%d (%s) removing %s"
@@ -370,7 +340,7 @@ int SMBCALL smb_islocked(smb_t* smb)
 {
 	char	str[MAX_PATH+1];
 
-	if(access(smb_lockfname(smb,str),0)!=0)
+	if(access(smb_lockfname(smb,str,sizeof(str)-1),0)!=0)
 		return(0);
 	safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s exists",str);
 	return(1);
@@ -813,7 +783,6 @@ ulong SMBCALL smb_getmsgtxtlen(smbmsg_t* msg)
 	return(length);
 }
 
-
 static void set_convenience_ptr(smbmsg_t* msg, ushort hfield_type, void* hfield_dat)
 {
 	switch(hfield_type) {	/* convenience variables */
@@ -1350,7 +1319,7 @@ int SMBCALL smb_addcrc(smb_t* smb, ulong crc)
 	if(!smb->status.max_crcs)
 		return(SMB_SUCCESS);
 
-	sprintf(str,"%s.sch",smb->file);
+	SAFEPRINTF(str,"%s.sch",smb->file);
 	while(1) {
 		if((file=sopen(str,O_RDWR|O_CREAT|O_BINARY,SH_DENYRW,S_IREAD|S_IWRITE))!=-1)
 			break;
@@ -1492,6 +1461,7 @@ int SMBCALL smb_addmsghdr(smb_t* smb, smbmsg_t* msg, int storage)
 		smb->status.last_msg++;
 		smb->status.total_msgs++;
 		smb_putstatus(smb);
+		smb_hashmsg(smb,msg,NULL);
 	}
 	smb_unlocksmbhdr(smb);
 	return(i);
@@ -1681,11 +1651,11 @@ int SMBCALL smb_create(smb_t* smb)
 	rewind(smb->sid_fp);
 	chsize(fileno(smb->sid_fp),0L);
 
-	sprintf(str,"%s.sda",smb->file);
+	SAFEPRINTF(str,"%s.sda",smb->file);
 	remove(str);						/* if it exists, delete it */
-	sprintf(str,"%s.sha",smb->file);
+	SAFEPRINTF(str,"%s.sha",smb->file);
 	remove(str);                        /* if it exists, delete it */
-	sprintf(str,"%s.sch",smb->file);
+	SAFEPRINTF(str,"%s.sch",smb->file);
 	remove(str);
 	smb_unlocksmbhdr(smb);
 	return(SMB_SUCCESS);
@@ -2384,4 +2354,194 @@ int SMBCALL smb_updatethread(smb_t* smb, smbmsg_t* remsg, ulong newmsgnum)
 	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)
+{
+	int		retval;
+	size_t	c;
+	hash_t	hash;
+
+	if(found!=NULL)
+		memset(found,0,sizeof(hash_t));
+
+	if((retval=smb_open_hash(smb))!=SMB_SUCCESS)
+		return(retval);
+
+	if(compare!=NULL) {
+
+		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.number==0 || 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&hash.flags&SMB_HASH_MASK)==0)	
+					continue;	/* no matching hashes */
+				if((compare[c]->flags&~SMB_HASH_MASK)!=(hash.flags&~SMB_HASH_MASK))
+					continue;	/* wrong pre-process flags */
+				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! */
+				if(found!=NULL)
+					memcpy(found,&hash,sizeof(hash));
+
+				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)
+{
+	int			retval;
+	size_t		h;
+
+	if((retval=smb_open_hash(smb))!=SMB_SUCCESS)
+		return(retval);
+
+	if(hashes!=NULL) {
+
+		fseek(smb->hash_fp,0,SEEK_END);
+		for(h=0;hashes[h]!=NULL;h++) {
+			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* smb_hash(ulong msgnum, ulong t, unsigned source, unsigned flags, uchar* data)
+{
+	uchar*	p=data;
+	size_t	len;
+	hash_t*	hash;
+
+	if((hash=(hash_t*)malloc(sizeof(hash_t)))==NULL)
+		return(NULL);
+
+	if(flags&~SMB_HASH_MASK) {	/* pre-processing */
+		if((p=strdup(data))==NULL)
+			return(NULL);
+		if(flags&SMB_HASH_UPPERCASE)
+			strupr(p);
+		if(flags&SMB_HASH_LOWERCASE)
+			strlwr(p);
+		if(flags&SMB_HASH_STRIP_WSP)
+			strip_chars(p," \t\r\n");
+	}
+	len=strlen(p);
+	hash->number=msgnum;
+	hash->time=t;
+	hash->source=source;
+	hash->flags=flags;
+	if(flags&SMB_HASH_CRC16)
+		hash->crc16=crc16(p,len);
+	if(flags&SMB_HASH_CRC32)
+		hash->crc32=crc32(p,len);
+	if(flags&SMB_HASH_MD5)
+		MD5_calc(hash->md5,p,len);
+
+	if(p!=data)	/* duped string */
+		free(p);
+
+	return(hash);
+}
+
+/* Allocatese and calculates all hashes for a single message				*/
+/* Returns NULL on failure													*/
+hash_t** SMBCALL smb_msghashes(smb_t* smb, smbmsg_t* msg, uchar* text)
+{
+	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_hash(msg->hdr.number, t, RFC822MSGID, flags, msg->id))!=NULL)
+		hashes[h++]=hash;
+
+	if(msg->ftn_msgid!=NULL
+		&& (hash=smb_hash(msg->hdr.number, t, FIDOMSGID, flags, msg->ftn_msgid))!=NULL)
+		hashes[h++]=hash;
+
+	if(text!=NULL
+		&& (hash=smb_hash(msg->hdr.number, t, TEXT_BODY, flags|SMB_HASH_STRIP_WSP, 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, uchar* text)
+{
+	size_t		n;
+	int			retval=SMB_SUCCESS;
+	hash_t**	hashes;	/* This is a NULL-terminated list of hashes */
+
+	hashes=smb_msghashes(smb,msg,text);
+
+	if(smb_findhash(smb, hashes, NULL)==SMB_SUCCESS)
+		retval=SMB_DUPE_MSG;
+	else
+		retval=smb_addhashes(smb,hashes);
+
+	FREE_LIST(hashes,n);
+
+	return(retval);
+}
+
 /* End of SMBLIB.C */
diff --git a/src/smblib/smblib.h b/src/smblib/smblib.h
index 6d8f110fdd0f00d0170a4fa997c3a6180b28ff23..ec530a1f0324902e0521f13cfe8dd536bfa05f94 100644
--- a/src/smblib/smblib.h
+++ b/src/smblib/smblib.h
@@ -39,6 +39,9 @@
 #define _SMBLIB_H
 
 #include "lzh.h"
+#include "md5.h"
+#include "crc16.h"
+#include "crc32.h"
 
 #ifdef SMBEXPORT
 	#undef SMBEXPORT
@@ -82,12 +85,19 @@
 
 #define GETMSGTXT_TAILS 	(1<<0)		/* Get message tail(s)				*/
 #define GETMSGTXT_NO_BODY	(1<<1)		/* Do not retrieve message body		*/
+#define GETMSGTXT_TAIL_ONLY (GETMSGTXT_TAILS|GETMSGTXT_NO_BODY)
 
 #define SMB_IS_OPEN(smb)	((smb)->shd_fp!=NULL)
 
 /* Legacy API functions */
 #define smb_incmsg(smb,msg)	smb_incmsg_dfields(smb,msg,1)
 #define smb_incdat			smb_incmsgdat
+#define smb_open_da(smb)	smb_open_fp(smb,&(smb)->sda_fp)
+#define smb_close_da(smb)	smb_close_fp(&(smb)->sda_fp)
+#define smb_open_ha(smb)	smb_open_fp(smb,&(smb)->sha_fp)
+#define smb_close_ha(smb)	smb_close_fp(&(smb)->sha_fp)
+#define smb_open_hash(smb)	smb_open_fp(smb,&(smb)->hash_fp)
+#define smb_close_hash(smb)	smb_close_fp(&(smb)->hash_fp)
 
 #ifdef __cplusplus
 extern "C" {
@@ -97,10 +107,8 @@ 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_da(smb_t* smb);
-SMBEXPORT void		SMBCALL smb_close_da(smb_t* smb);
-SMBEXPORT int 		SMBCALL smb_open_ha(smb_t* smb);
-SMBEXPORT void		SMBCALL smb_close_ha(smb_t* smb);
+SMBEXPORT int 		SMBCALL smb_open_fp(smb_t* smb, FILE**);
+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);
@@ -155,6 +163,13 @@ SMBEXPORT int		SMBCALL	smb_copymsgmem(smb_t* smb, smbmsg_t* destmsg, smbmsg_t* s
 SMBEXPORT int		SMBCALL smb_tzutc(short timezone);
 SMBEXPORT int		SMBCALL smb_updatethread(smb_t* smb, smbmsg_t* remsg, ulong newmsgnum);
 
+/* hash-related functions */
+SMBEXPORT int		SMBCALL smb_findhash(smb_t* smb, hash_t** compare_list, hash_t* found);
+SMBEXPORT int		SMBCALL smb_hashmsg(smb_t* smb, smbmsg_t* msg, uchar* text);
+SMBEXPORT hash_t*	SMBCALL	smb_hash(ulong msgnum, ulong time, unsigned source, unsigned flags, uchar* str);
+SMBEXPORT hash_t**	SMBCALL smb_msghashes(smb_t* smb, smbmsg_t* msg, uchar* text);
+SMBEXPORT int		SMBCALL smb_addhashes(smb_t* smb, hash_t** hash_list);
+
 /* smbtxt.c */
 SMBEXPORT char*		SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode);