From 17a8513e17af9e65ff2faad922c649859c8bcc3e Mon Sep 17 00:00:00 2001
From: Rob Swindell <rob@synchro.net>
Date: Sun, 30 Aug 2020 22:04:19 -0700
Subject: [PATCH] SMBLIB support for filebases

With very little change, SMB can accommodate file bases in addition to message bases.
---
 src/smblib/smbadd.c  |  15 ++++-
 src/smblib/smbdefs.h | 103 +++++++++++++++++++++---------
 src/smblib/smblib.c  | 145 +++++++++++++++++++++++++++++--------------
 src/smblib/smblib.h  |   4 ++
 4 files changed, 189 insertions(+), 78 deletions(-)

diff --git a/src/smblib/smbadd.c b/src/smblib/smbadd.c
index 3ba1ac5d02..1af2a17b71 100644
--- a/src/smblib/smbadd.c
+++ b/src/smblib/smbadd.c
@@ -168,7 +168,11 @@ int SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, long dupechk_hash
 			}
 			msg->hdr.offset=offset;
 
-			smb_fseek(smb->sdt_fp,offset,SEEK_SET);
+			if(smb_fseek(smb->sdt_fp,offset,SEEK_SET) != 0) {
+				sprintf(smb->last_error, "%s seek error %d", __FUNCTION__, errno);
+				retval=SMB_ERR_SEEK;
+				break;
+			}
 
 			if(bodylen) {
 				if((retval=smb_dfield(msg,TEXT_BODY,bodylen))!=SMB_SUCCESS)
@@ -300,7 +304,7 @@ int SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, long dupechk_hash
 		if(!(smb->status.attr&(SMB_EMAIL|SMB_NOHASH))
 			&& smb_addhashes(smb,hashes,/* skip_marked? */FALSE)==SMB_SUCCESS)
 			msg->flags|=MSG_FLAG_HASHED;
-		if(msg->to==NULL)	/* no recipient, don't add header (required for bulkmail) */
+		if(msg->hdr.type == SMB_MSG_TYPE_NORMAL && msg->to == NULL)	/* no recipient, don't add header (required for bulkmail) */
 			break;
 
 		retval=smb_addmsghdr(smb,msg,storage); /* calls smb_unlocksmbhdr() */
@@ -463,3 +467,10 @@ int SMBCALL smb_addpollclosure(smb_t* smb, smbmsg_t* msg, int storage)
 
 	return retval;
 }
+
+int SMBCALL smb_addfile(smb_t* smb, smbfile_t* file, int storage, const uchar* extdesc)
+{
+	file->hdr.type = SMB_MSG_TYPE_FILE;
+
+	return smb_addmsg(smb, file, storage, SMB_HASH_SOURCE_NONE, XLAT_NONE, /* body: */extdesc, /* tail: */NULL);
+}
diff --git a/src/smblib/smbdefs.h b/src/smblib/smbdefs.h
index 96cf8c9b31..59d6e37db8 100644
--- a/src/smblib/smbdefs.h
+++ b/src/smblib/smbdefs.h
@@ -92,9 +92,10 @@
 #define SMB_FASTALLOC		1			/* Fast allocation */
 
 										/* status.attr bit flags: */
-#define SMB_EMAIL			1			/* User numbers stored in Indexes */
-#define SMB_HYPERALLOC		2			/* No allocation (also storage value for smb_addmsghdr) */
-#define SMB_NOHASH			4			/* Do not calculate or store hashes */
+#define SMB_EMAIL			(1<<0)		/* User numbers stored in Indexes */
+#define SMB_HYPERALLOC		(1<<1)		/* No allocation (also storage value for smb_addmsghdr) */
+#define SMB_NOHASH			(1<<2)		/* Do not calculate or store hashes */
+#define SMB_FILE_DIRECTORY	(1<<3)		/* Storage for a file area (for file transfers/downloads) */
 
 										/* Time zone macros for when_t.zone */
 #define DAYLIGHT			0x8000		/* Daylight savings is active */
@@ -236,6 +237,9 @@
 #define SMB_TAGS			0x69	/* List of tags (ala hash-tags) related to this message */
 #define SMB_TAG_DELIMITER	" "
 
+#define SMB_FILENAME		SUBJECT
+#define	SMB_FILEDESC		SMB_SUMMARY
+
 #define FILEATTACH			0x70
 #define DESTFILE			0x71
 #define FILEATTACHLIST		0x72
@@ -290,8 +294,8 @@
 
 #define SMB_POLL_ANSWER		0xe0		/* the subject is the question */
 
-#define UNKNOWN 			0xf1
-#define UNKNOWNASCII		0xf2
+#define UNKNOWN 			0xf1		/* specified as 0xf0 in smb.txt/html - oops */
+#define UNKNOWNASCII		0xf2		/* specified as 0xf1 in smb.txt/html - oops */
 #define UNUSED				0xff
 
 										/* Valid dfield_t.types */
@@ -439,14 +443,14 @@ typedef struct _PACK {		/* Time with time-zone */
 typedef struct _PACK {		/* Index record */
 
 	union {
-		struct _PACK {
-			uint16_t	to; 		/* 16-bit CRC of recipient name (lower case) or user # */
-			uint16_t	from;		/* 16-bit CRC of sender name (lower case) or user # */
-			uint16_t	subj;		/* 16-bit CRC of subject (lower case, w/o RE:) */
+		struct _PACK {			/* when msg.type != BALLOT */
+			uint16_t	to; 	/* 16-bit CRC of recipient name (lower case) or user # */
+			uint16_t	from;	/* 16-bit CRC of sender name (lower case) or user # */
+			uint16_t	subj;	/* 16-bit CRC of subject (lower case, w/o RE:) */
 		};
-		struct _PACK {
-			uint16_t	votes;		/* votes value */
-			uint32_t	remsg;		/* number of message this vote is in response to */
+		struct _PACK {			/* when msg.type == BALLOT */
+			uint16_t	votes;	/* votes value */
+			uint32_t	remsg;	/* number of message this vote is in response to */
 		};
 	};
 	uint16_t	attr;			/* attributes (read, permanent, etc.) */
@@ -456,6 +460,15 @@ typedef struct _PACK {		/* Index record */
 
 } idxrec_t;
 
+typedef struct _PACK {		/* File index record */
+	union {
+		struct {
+			idxrec_t	idx;
+			char		filename[64];
+		};
+		uint8_t	padding[128];
+	};
+} smbfileidxrec_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				*/
@@ -511,15 +524,24 @@ typedef struct _PACK {		/* Message base header (fixed portion) */
 
 } smbhdr_t;
 
-typedef struct _PACK {		/* Message base status header */
+typedef struct _PACK {		/* Message/File base status header */
 
-	uint32_t	last_msg;		/* last message number */
-	uint32_t	total_msgs; 	/* total messages */
+	union {
+		uint32_t	last_msg;	/* last message number */
+		uint32_t	last_file;	/* last file number */
+	};
+	union {
+		uint32_t	total_msgs; /* total messages */
+		uint32_t	total_files; /* total files */
+	};
 	uint32_t	header_offset;	/* byte offset to first header record */
 	uint32_t	max_crcs;		/* Maximum number of CRCs to keep in history */
-    uint32_t	max_msgs;       /* Maximum number of message to keep in sub */
-    uint16_t	max_age;        /* Maximum age of message to keep in sub (in days) */
-	uint16_t	attr;			/* Attributes for this message base (SMB_HYPER,etc) */
+	union {
+		uint32_t	max_msgs;	/* Maximum number of message to keep in sub */
+		uint32_t	max_files;	/* Maximum number of files to keep in dir */
+	};
+    uint16_t	max_age;        /* Maximum age of message/file to keep in sub (in days) */
+	uint16_t	attr;			/* Attributes for this message/file base (SMB_HYPER,etc) */
 
 } smbstatus_t;
 
@@ -528,9 +550,10 @@ enum smb_msg_type {
 	,SMB_MSG_TYPE_POLL			/* A poll question  */
 	,SMB_MSG_TYPE_BALLOT		/* Voter response to poll or normal message */
 	,SMB_MSG_TYPE_POLL_CLOSURE	/* Closure of an existing poll */
+	,SMB_MSG_TYPE_FILE			/* A file (e.g. for download) */
 };
 
-typedef struct _PACK {		/* Message header */
+typedef struct _PACK {		/* Message/File header */
 
 	/* 00 */ uchar		id[LEN_HEADER_ID];	/* SHD<^Z> */
     /* 04 */ uint16_t	type;				/* Message type (enum smb_msg_type) */
@@ -545,7 +568,10 @@ typedef struct _PACK {		/* Message header */
     /* 24 */ uint32_t	thread_back;		/* Message number for backwards threading (aka thread_orig) */
     /* 28 */ uint32_t	thread_next;		/* Next message in thread */
     /* 2c */ uint32_t	thread_first;		/* First reply to this message */
-	/* 30 */ uint16_t	delivery_attempts;	/* Delivery attempt counter */
+	union {
+	/* 30 */ uint16_t	delivery_attempts;	/* Delivery attempt counter (for SMTP) */
+	/* 30 */ uint16_t	altpath;			/* Alternate file storage path (when non-zero) */
+	};
 	/* 32 */ int16_t	votes;				/* Votes value (response to poll) or maximum votes per ballot (poll) */
 	/* 34 */ uint32_t	thread_id;			/* Number of original message in thread (or 0 if unknown) */
 	/* 38 */ uint32_t	times_downloaded;	/* Total number of times downloaded (moved Mar-6-2012) */
@@ -597,7 +623,7 @@ typedef struct {		/* Network (type and address) */
 								/* Valid bits in smbmsg_t.flags					*/
 #define MSG_FLAG_HASHED	(1<<0)	/* Message has been hashed with smb_hashmsg()	*/
 
-typedef struct {				/* Message */
+typedef struct {				/* Message or File */
 
 	idxrec_t	idx;			/* Index */
 	msghdr_t	hdr;			/* Header record (fixed portion) */
@@ -623,9 +649,18 @@ typedef struct {				/* Message */
 				*ftn_flags,		/* FTN FLAGS */
 				*ftn_msgid,		/* FTN MSGID */
 				*ftn_reply;		/* FTN REPLY */
-	char*		summary;		/* Summary  */
-	char*		subj;			/* Subject  */
-	char*		tags;			/* Message tags (space-delimited) */
+	union {
+		char*	summary;		/* Message Summary  */
+		char*	desc;			/* File description */
+	};
+	union {
+		char*	subj;			/* Subject  */
+		char*	filename;		/* Filename */
+	};
+	union {
+		uchar*	text;			/* Message body text (optional) */
+		uchar*	extdesc;		/* File extended description */
+	};
 	uint16_t	to_agent,		/* Type of agent message is to */
 				from_agent, 	/* Type of agent message is from */
 				replyto_agent;	/* Type of agent replies should be sent to */
@@ -646,10 +681,14 @@ typedef struct {				/* Message */
 	uint32_t	upvotes;		/* Vote tally for this message */
 	uint32_t	downvotes;		/* Vote tally for this message */
 	uint32_t	total_votes;	/* Total votes for this message or poll */
+								/* Not written to the database: */
+	uint32_t	dir;			/* Directory number */
+	off_t		size;			/* File size */
+	time_t		date;			/* File date/timestamp (current) */
 
-} smbmsg_t;
+} smbmsg_t, smbfile_t;
 
-typedef struct {				/* Message base */
+typedef struct {				/* Message/File base */
 
     char		file[128];      /* Path and base filename (no extension) */
     FILE*		sdt_fp;			/* File pointer for data (.sdt) file */
@@ -662,12 +701,18 @@ typedef struct {				/* Message base */
 	uint32_t	retry_delay;	/* Time-slice yield (milliseconds) while retrying */
 	smbstatus_t status; 		/* Status header record */
 	BOOL		locked;			/* SMB header is locked */
-	BOOL		continue_on_error;			/* Attempt recovery after some normaly fatal errors */
+	BOOL		continue_on_error;			/* Attempt recovery after some normally fatal errors */
 	char		last_error[MAX_PATH*2];		/* Last error message */
 
 	/* Private member variables (not initialized by or used by smblib) */
-	uint32_t	subnum;			/* Sub-board number */
-	uint32_t	msgs;			/* Number of messages loaded (for user) */
+	union {
+		uint32_t	subnum;		/* Sub-board number */
+		uint32_t	dirnum;		/* Directory number */
+	};
+	union {
+		uint32_t	msgs;		/* Number of messages loaded (for user) */
+		uint32_t	files;		/* Number of files loaded */
+	};
 	uint32_t	curmsg;			/* Current message number (for user, 0-based) */
 
 } smb_t;
diff --git a/src/smblib/smblib.c b/src/smblib/smblib.c
index 45f82a701f..89ec363b7f 100644
--- a/src/smblib/smblib.c
+++ b/src/smblib/smblib.c
@@ -505,6 +505,7 @@ int SMBCALL smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
 	long		byte_offset;
 	ulong		l,total,bot,top;
 	long		length;
+	size_t		idxreclen = smb_idxreclen(smb);
 
 	if(smb->sid_fp==NULL) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s index not open", __FUNCTION__);
@@ -513,12 +514,12 @@ int SMBCALL smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
 	clearerr(smb->sid_fp);
 
 	length=filelength(fileno(smb->sid_fp));
-	if(length<(long)sizeof(idxrec_t)) {
+	if(length<(long)idxreclen) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s invalid index file length: %ld", __FUNCTION__,length);
 		return(SMB_ERR_FILE_LEN);
 	}
-	total=length/sizeof(idxrec_t);
+	total=length/idxreclen;
 	if(!total) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s invalid index file length: %ld", __FUNCTION__,length);
@@ -527,9 +528,9 @@ int SMBCALL smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
 
 	if(!msg->hdr.number) {
 		if(msg->offset<0)
-			byte_offset=length-((-msg->offset)*sizeof(idxrec_t));
+			byte_offset=length-((-msg->offset)*idxreclen);
 		else
-			byte_offset=msg->offset*sizeof(idxrec_t);
+			byte_offset=msg->offset*idxreclen;
 		if(byte_offset>=length) {
 			safe_snprintf(smb->last_error,sizeof(smb->last_error)
 				,"%s invalid index offset: %ld, byte offset: %ld, length: %ld", __FUNCTION__
@@ -543,14 +544,14 @@ int SMBCALL smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
 				,msg->offset,byte_offset);
 			return(SMB_ERR_SEEK);
 		}
-		if(smb_fread(smb,&msg->idx,sizeof(idxrec_t),smb->sid_fp)!=sizeof(idxrec_t)) {
+		if(smb_fread(smb,&msg->idx,sizeof(msg->idx),smb->sid_fp)!=sizeof(msg->idx)) {
 			safe_snprintf(smb->last_error,sizeof(smb->last_error)
 				,"%s reading index at offset %ld (byte %lu)", __FUNCTION__
 				,msg->offset,byte_offset);
 			return(SMB_ERR_READ);
 		}
 		/* Save the correct offset (from the beginning of the file) */
-		msg->offset=byte_offset/sizeof(idxrec_t);
+		msg->offset=byte_offset/idxreclen;
 		return(SMB_SUCCESS); 
 	}
 
@@ -563,17 +564,17 @@ int SMBCALL smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
 				, __FUNCTION__,msg->hdr.number);
 			return(SMB_ERR_NOT_FOUND);
 		}
-		if(fseek(smb->sid_fp,l*sizeof(idxrec_t),SEEK_SET)) {
+		if(fseek(smb->sid_fp,l*idxreclen,SEEK_SET)) {
 			safe_snprintf(smb->last_error,sizeof(smb->last_error)
 				,"%s %d '%s' seeking to offset %lu (byte %lu) in index file", __FUNCTION__
 				,get_errno(),STRERROR(get_errno())
-				,l,l*sizeof(idxrec_t));
+				,l,l*idxreclen);
 			return(SMB_ERR_SEEK);
 		}
-		if(smb_fread(smb,&idx,sizeof(idxrec_t),smb->sid_fp)!=sizeof(idxrec_t)) {
+		if(smb_fread(smb,&idx,sizeof(idx),smb->sid_fp)!=sizeof(idx)) {
 			safe_snprintf(smb->last_error,sizeof(smb->last_error)
 				,"%s reading index at offset %lu (byte %lu)", __FUNCTION__
-				,l,l*sizeof(idxrec_t));
+				,l,l*sizeof(idx));
 			return(SMB_ERR_READ);
 		}
 		if(bot==top-1 && idx.number!=msg->hdr.number) {
@@ -598,6 +599,35 @@ int SMBCALL smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
 	return(SMB_SUCCESS);
 }
 
+/****************************************************************************/
+/* Count the number of msg index records with specific attribute flags		*/
+/****************************************************************************/
+uint32_t SMBCALL smb_count_idx_records(smb_t* smb, uint16_t mask, uint16_t cmp)
+{
+	int32_t offset = 0;
+	uint32_t count = 0;
+	for(offset = 0; ;offset++) {
+		smbmsg_t msg;
+		memset(&msg, 0, sizeof(msg));
+		msg.offset = offset;
+		if(smb_getmsgidx(smb, &msg) != SMB_SUCCESS)
+			break;
+		if((msg.idx.attr & mask) == cmp)
+			count++;
+	}
+	return count;
+}
+
+/****************************************************************************/
+/* Returns the length (in bytes) of the SMB's index records					*/
+/****************************************************************************/
+size_t SMBCALL smb_idxreclen(smb_t* smb)
+{
+	if(smb->status.attr&SMB_FILE_DIRECTORY)
+		return sizeof(smbfileidxrec_t);
+	return sizeof(idxrec_t);
+}
+
 /****************************************************************************/
 /* Reads the first index record in the open message base 					*/
 /****************************************************************************/
@@ -614,7 +644,7 @@ int SMBCALL smb_getfirstidx(smb_t* smb, idxrec_t *idx)
 			,get_errno(),STRERROR(get_errno()));
 		return(SMB_ERR_SEEK);
 	}
-	if(smb_fread(smb,idx,sizeof(idxrec_t),smb->sid_fp)!=sizeof(idxrec_t)) {
+	if(smb_fread(smb,idx,sizeof(*idx),smb->sid_fp)!=sizeof(*idx)) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s reading first index", __FUNCTION__);
 		return(SMB_ERR_READ);
@@ -628,6 +658,7 @@ int SMBCALL smb_getfirstidx(smb_t* smb, idxrec_t *idx)
 int SMBCALL smb_getlastidx(smb_t* smb, idxrec_t *idx)
 {
 	long length;
+	size_t idxreclen = smb_idxreclen(smb);
 
 	if(smb->sid_fp==NULL) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s index not open", __FUNCTION__);
@@ -635,19 +666,19 @@ int SMBCALL smb_getlastidx(smb_t* smb, idxrec_t *idx)
 	}
 	clearerr(smb->sid_fp);
 	length=filelength(fileno(smb->sid_fp));
-	if(length<(long)sizeof(idxrec_t)) {
+	if(length<(long)idxreclen) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s invalid index file length: %ld", __FUNCTION__,length);
 		return(SMB_ERR_FILE_LEN);
 	}
-	if(fseek(smb->sid_fp,length-sizeof(idxrec_t),SEEK_SET)) {
+	if(fseek(smb->sid_fp,length-idxreclen,SEEK_SET)) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s %d '%s' seeking to %u in index file", __FUNCTION__
 			,get_errno(),STRERROR(get_errno())
 			,(unsigned)(length-sizeof(idxrec_t)));
 		return(SMB_ERR_SEEK);
 	}
-	if(smb_fread(smb,idx,sizeof(idxrec_t),smb->sid_fp)!=sizeof(idxrec_t)) {
+	if(smb_fread(smb,idx,sizeof(*idx),smb->sid_fp)!=sizeof(*idx)) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s reading last index", __FUNCTION__);
 		return(SMB_ERR_READ);
@@ -667,6 +698,7 @@ long SMBCALL smb_getmsgidx_by_time(smb_t* smb, idxrec_t* match, time_t t)
 	long		match_offset;
 	ulong		total, bot, top;
 	idxrec_t	idx;
+	size_t		idxreclen = smb_idxreclen(smb);
 
 	if(match == NULL)
 		return SMB_BAD_PARAMETER;
@@ -676,7 +708,7 @@ long SMBCALL smb_getmsgidx_by_time(smb_t* smb, idxrec_t* match, time_t t)
 	if(t <= 0)
 		return SMB_BAD_PARAMETER;
 
-	total = filelength(fileno(smb->sid_fp))/sizeof(idxrec_t);
+	total = filelength(fileno(smb->sid_fp))/idxreclen;
 
 	if(!total)	/* Empty base */
 		return SMB_ERR_NOT_FOUND;
@@ -696,9 +728,9 @@ long SMBCALL smb_getmsgidx_by_time(smb_t* smb, idxrec_t* match, time_t t)
 	clearerr(smb->sid_fp);
 	while(bot <= top) {
 		long idx_offset = (bot + top) / 2;
-		if(fseek(smb->sid_fp, idx_offset * sizeof(idxrec_t), SEEK_SET) != 0)
+		if(fseek(smb->sid_fp, idx_offset * idxreclen, SEEK_SET) != 0)
 			return SMB_ERR_SEEK;
-		if(fread(&idx, 1, sizeof(idx), smb->sid_fp) != sizeof(idxrec_t))
+		if(fread(&idx, 1, sizeof(idx), smb->sid_fp) != sizeof(idx))
 			return SMB_ERR_READ;
 		if((time_t)idx.time < t) {
 			bot = idx_offset + 1;
@@ -961,7 +993,7 @@ int SMBCALL smb_getmsghdr(smb_t* smb, smbmsg_t* msg)
 	rewind(smb->shd_fp);
 	if(fseek(smb->shd_fp,msg->idx.offset,SEEK_SET)) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"%s %d '%s' seeking to %lu in header file", __FUNCTION__
+			,"%s %d '%s' seeking to offset %lu in header file", __FUNCTION__
 			,get_errno(),STRERROR(get_errno())
 			,msg->idx.offset);
 		return(SMB_ERR_SEEK);
@@ -974,7 +1006,7 @@ int SMBCALL smb_getmsghdr(smb_t* smb, smbmsg_t* msg)
 	msg->offset=offset;
 	if(smb_fread(smb,&msg->hdr,sizeof(msghdr_t),smb->shd_fp)!=sizeof(msghdr_t)) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"%s reading msg header", __FUNCTION__);
+			,"%s reading msg header at offset %lu", __FUNCTION__, msg->idx.offset);
 		return(SMB_ERR_READ);
 	}
 	if(memcmp(msg->hdr.id,SHD_HEADER_ID,LEN_HEADER_ID)) {
@@ -989,8 +1021,8 @@ int SMBCALL smb_getmsghdr(smb_t* smb, smbmsg_t* msg)
 	}
 	if(msg->hdr.version<0x110) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"%s insufficient header version: %X", __FUNCTION__
-			,msg->hdr.version);
+			,"%s insufficient header version: %X at offset %lu", __FUNCTION__
+			,msg->hdr.version, msg->idx.offset);
 		return(SMB_ERR_HDR_VER);
 	}
 	l=sizeof(msghdr_t);
@@ -1116,6 +1148,10 @@ void SMBCALL smb_freemsgmem(smbmsg_t* msg)
 	}
 	msg->hdr.total_dfields=0;
 	smb_freemsghdrmem(msg);
+	if(msg->text != NULL) {
+		free(msg->text);
+		msg->text = NULL;
+	}
 }
 
 /****************************************************************************/
@@ -1538,6 +1574,7 @@ int SMBCALL smb_addmsghdr(smb_t* smb, smbmsg_t* msg, int storage)
 	long	l;
 	ulong	hdrlen;
 	long	idxlen;
+	size_t	idxreclen = smb_idxreclen(smb);
 
 	if(smb->shd_fp==NULL) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msgbase not open", __FUNCTION__);
@@ -1562,10 +1599,10 @@ int SMBCALL smb_addmsghdr(smb_t* smb, smbmsg_t* msg, int storage)
 	}
 
 	idxlen = filelength(fileno(smb->sid_fp));
-	if(idxlen != (smb->status.total_msgs * sizeof(idxrec_t))) {
+	if(idxlen != (smb->status.total_msgs * idxreclen)) {
 		safe_snprintf(smb->last_error, sizeof(smb->last_error)
-			,"%s index file length (%ld) unexpected (%ld)", __FUNCTION__
-			,idxlen, smb->status.total_msgs * sizeof(idxrec_t));
+			,"%s index file length (%ld), expected (%ld)", __FUNCTION__
+			,idxlen, smb->status.total_msgs * idxreclen);
 		smb_unlocksmbhdr(smb);
 		return SMB_ERR_FILE_LEN;
 	}
@@ -1660,25 +1697,25 @@ int SMBCALL smb_putmsg(smb_t* smb, smbmsg_t* msg)
 /****************************************************************************/
 int SMBCALL smb_init_idx(smb_t* smb, smbmsg_t* msg)
 {
-	msg->idx.subj=smb_subject_crc(msg->subj);
-
-	if(smb->status.attr&SMB_EMAIL) {
-		if(msg->to_ext)
-			msg->idx.to=atoi(msg->to_ext);
-		else
-			msg->idx.to=0;
-		if(msg->from_ext)
-			msg->idx.from=atoi(msg->from_ext);
-		else
-			msg->idx.from=0; 
-	} else if(msg->hdr.type == SMB_MSG_TYPE_BALLOT) {
+	if(msg->hdr.type == SMB_MSG_TYPE_BALLOT) {
 		msg->idx.votes = msg->hdr.votes;
 		msg->idx.remsg = msg->hdr.thread_back;
 	} else {
-		msg->idx.to=smb_name_crc(msg->to);
-		msg->idx.from=smb_name_crc(msg->from);
+		msg->idx.subj = smb_subject_crc(msg->subj);
+		if(smb->status.attr&(SMB_EMAIL|SMB_FILE_DIRECTORY)) {
+			if(msg->to_ext)
+				msg->idx.to = atoi(msg->to_ext);
+			else
+				msg->idx.to = 0;
+			if(msg->from_ext)
+				msg->idx.from = atoi(msg->from_ext);
+			else
+				msg->idx.from = 0; 
+		} else {
+			msg->idx.to = smb_name_crc(msg->to);
+			msg->idx.from = smb_name_crc(msg->from);
+		}
 	}
-
 	/* Make sure these index/header fields are always *nsync */
 	msg->idx.number	= msg->hdr.number;
 	msg->idx.attr	= msg->hdr.attr;	
@@ -1722,6 +1759,7 @@ uint16_t SMBCALL smb_voted_already(smb_t* smb, uint32_t msgnum, const char* name
 			,get_errno(), STRERROR(get_errno()));
 		return SMB_ERR_SEEK;
 	}
+	memset(&msg, 0, sizeof(msg));
 	while(!votes && smb_fread(smb, &msg.idx, sizeof(msg.idx), smb->sid_fp) == sizeof(msg.idx)) {
 		if(!(msg.idx.attr&MSG_VOTE) || msg.idx.attr&MSG_POLL)
 			continue;
@@ -1756,6 +1794,7 @@ uint16_t SMBCALL smb_voted_already(smb_t* smb, uint32_t msgnum, const char* name
 int SMBCALL smb_putmsgidx(smb_t* smb, smbmsg_t* msg)
 {
 	long length;
+	size_t	idxreclen = smb_idxreclen(smb);
 
 	if(smb->sid_fp==NULL) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s index not open", __FUNCTION__);
@@ -1763,23 +1802,35 @@ int SMBCALL smb_putmsgidx(smb_t* smb, smbmsg_t* msg)
 	}
 	clearerr(smb->sid_fp);
 	length = filelength(fileno(smb->sid_fp));
-	if(length < (long)(msg->offset*sizeof(idxrec_t))) {
+	if(length < (long)(msg->offset*idxreclen)) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s invalid index offset: %ld, byte offset: %lu, length: %lu", __FUNCTION__
-			,msg->offset, msg->offset*sizeof(idxrec_t), length);
+			,msg->offset, msg->offset*idxreclen, length);
 		return(SMB_ERR_HDR_OFFSET);
 	}
-	if(fseek(smb->sid_fp,msg->offset*sizeof(idxrec_t),SEEK_SET)) {
+	if(fseek(smb->sid_fp,msg->offset*idxreclen,SEEK_SET)) {
 		safe_snprintf(smb->last_error,sizeof(smb->last_error)
 			,"%s %d '%s' seeking to %u in index file", __FUNCTION__
 			,get_errno(),STRERROR(get_errno())
-			,(unsigned)(msg->offset*sizeof(idxrec_t)));
+			,(unsigned)(msg->offset*idxreclen));
 		return(SMB_ERR_SEEK);
 	}
-	if(!fwrite(&msg->idx,sizeof(idxrec_t),1,smb->sid_fp)) {
-		safe_snprintf(smb->last_error,sizeof(smb->last_error)
-			,"%s writing index", __FUNCTION__);
-		return(SMB_ERR_WRITE);
+	if(msg->hdr.type == SMB_MSG_TYPE_FILE) {
+		smbfileidxrec_t fidx = {0};
+		fidx.idx = msg->idx;
+		strncpy(fidx.filename, msg->filename, sizeof(fidx.filename));
+		if(!fwrite(&fidx, sizeof(fidx), 1, smb->sid_fp)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%s %d '%s' writing index", __FUNCTION__
+				,get_errno(),STRERROR(get_errno()));
+			return(SMB_ERR_WRITE);
+		}
+	} else {
+		if(!fwrite(&msg->idx,sizeof(msg->idx),1,smb->sid_fp)) {
+			safe_snprintf(smb->last_error,sizeof(smb->last_error)
+				,"%s writing index", __FUNCTION__);
+			return(SMB_ERR_WRITE);
+		}
 	}
 	return fflush(smb->sid_fp);	/* SMB_SUCCESS == 0 */
 }
diff --git a/src/smblib/smblib.h b/src/smblib/smblib.h
index eb58628b09..9e6731fbc1 100644
--- a/src/smblib/smblib.h
+++ b/src/smblib/smblib.h
@@ -71,6 +71,7 @@
 #define SMB_SUCCESS			0			/* Successful result/return code */
 #define SMB_FAILURE			-1			/* Generic error (discouraged) */
 #define SMB_BAD_PARAMETER	-2			/* Invalid API function parameter value */
+
 										/* Standard smblib errors values */
 #define SMB_ERR_NOT_OPEN	-100		/* Message base not open */
 #define SMB_ERR_HDR_LEN		-101		/* Invalid message header length (>64k) */
@@ -193,6 +194,8 @@ SMBEXPORT uint32_t	SMBCALL smb_first_in_thread(smb_t*, smbmsg_t*, msghdr_t*);
 SMBEXPORT uint32_t	SMBCALL smb_next_in_thread(smb_t*, smbmsg_t*, msghdr_t*);
 SMBEXPORT uint32_t	SMBCALL smb_last_in_branch(smb_t*, smbmsg_t*);
 SMBEXPORT uint32_t	SMBCALL smb_last_in_thread(smb_t*, smbmsg_t*);
+SMBEXPORT size_t	SMBCALL smb_idxreclen(smb_t*);
+SMBEXPORT uint32_t	SMBCALL	smb_count_idx_records(smb_t*, uint16_t mask, uint16_t cmp);
 
 /* smbadd.c */
 SMBEXPORT int		SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, long dupechk_hashes
@@ -200,6 +203,7 @@ SMBEXPORT int		SMBCALL smb_addmsg(smb_t* smb, smbmsg_t* msg, int storage, long d
 SMBEXPORT int		SMBCALL smb_addvote(smb_t* smb, smbmsg_t* msg, int storage);
 SMBEXPORT int		SMBCALL smb_addpoll(smb_t* smb, smbmsg_t* msg, int storage);
 SMBEXPORT int		SMBCALL smb_addpollclosure(smb_t* smb, smbmsg_t* msg, int storage);
+SMBEXPORT int		SMBCALL smb_addfile(smb_t* smb, smbfile_t* file, int storage, const uchar* extdesc);
 
 /* smballoc.c */
 SMBEXPORT long		SMBCALL smb_allochdr(smb_t* smb, ulong length);
-- 
GitLab