Skip to content
Snippets Groups Projects
fixsmb.c 9.21 KiB
/* Synchronet message base (SMB) index re-generator */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>	/* atoi, qsort */
#include <stdbool.h>
#include <string.h>	/* strnicmp */
#include <ctype.h>	/* toupper */

#include "smblib.h"
#include "genwrap.h"	/* PLATFORM_DESC */
#include "str_list.h"	/* strList API */
#include "crc16.h"
#include "git_branch.h"
#include "git_hash.h"

smb_t	smb;
BOOL	renumber=FALSE;
BOOL	rehash=FALSE;
BOOL	fixnums=FALSE;
BOOL	smb_undelete=FALSE;
char*	usage="usage: fixsmb [-renumber] [-undelete] [-fixnums] [-rehash] <smb_file> [[smb_file] [...]]";

int compare_index(const idxrec_t* idx1, const idxrec_t* idx2)
{
	return(idx1->number - idx2->number);
}

void sort_index(smb_t* smb)
{
	ulong		l;
	uint8_t*	idxbuf;
	size_t		idxreclen = smb_idxreclen(smb);

	printf("Sorting index... ");
	if((idxbuf = malloc(idxreclen * smb->status.total_msgs))==NULL) {
		perror("malloc");
		return;
	}

	rewind(smb->sid_fp);
	for(l=0;l<smb->status.total_msgs;l++)
		if(smb_fread(smb, idxbuf + (l * idxreclen), idxreclen, smb->sid_fp) != idxreclen) {
			perror("reading index");
			break;
		}

	qsort(idxbuf, l, idxreclen
		,(int(*)(const void*, const void*))compare_index);

	rewind(smb->sid_fp);
	chsize(fileno(smb->sid_fp),0L);			/* Truncate the index */

	printf("\nRe-writing index... \n");
	smb->status.total_msgs=l;
	for(l=0;l<smb->status.total_msgs;l++) {
		if(smb_fwrite(smb, idxbuf + (l * idxreclen), idxreclen, smb->sid_fp) != idxreclen) {
			perror("writing index");
			break;
		}
	}
	free(idxbuf);
	printf("\n");
}

bool we_locked_the_base = false;

void unlock_msgbase(void)
{
	int i;
	if(we_locked_the_base && smb_islocked(&smb) && (i=smb_unlock(&smb))!=0)
		printf("smb_unlock returned %d: %s\n",i,smb.last_error);
	else
		we_locked_the_base = false;
}

int fixsmb(char* sub)
{
	char*		p;
	char*		text;
	char		c;
	int 		i,w;
	ulong		l,size,n;
	off_t		length;
	smbmsg_t	msg;
	uint32_t*	numbers = NULL;
	long		total = 0;
	BOOL		dupe_msgnum;
	uint32_t	highest = 0;

	memset(&smb,0,sizeof(smb));

	SAFECOPY(smb.file,sub);

	if((p=getfext(smb.file))!=NULL && stricmp(p,".shd")==0)
		*p=0;	/* Chop off .shd extension, if supplied on command-line */

	char path[MAX_PATH+1];
	SAFEPRINTF(path, "%s.shd", smb.file);
	if(!fexistcase(path)) {
		printf("%s does not exist\n", path);
		exit(1);
	}

	printf("Opening %s\n",smb.file);

	if((i=smb_open(&smb))!=0) {
		printf("smb_open returned %d: %s\n",i,smb.last_error);
		exit(1);
	}

	if((i=smb_lock(&smb))!=0) {
		printf("smb_lock returned %d: %s\n",i,smb.last_error);
		exit(1);
	}
	we_locked_the_base = true;

	if((i=smb_locksmbhdr(&smb))!=0) {
		smb_close(&smb);
		printf("smb_locksmbhdr returned %d: %s\n",i,smb.last_error);
		exit(1);
	}

	if((i=smb_getstatus(&smb))!=0) {
		smb_unlocksmbhdr(&smb);
		smb_close(&smb);
		printf("smb_getstatus returned %d: %s\n",i,smb.last_error);
		exit(1);
	}

	uint32_t last_msg = smb.status.last_msg;

	if(!(smb.status.attr&SMB_HYPERALLOC)) {

		if((i=smb_open_ha(&smb))!=0) {
			smb_close(&smb);
			printf("smb_open_ha returned %d: %s\n",i,smb.last_error);
			exit(1);
		}

		if((i=smb_open_da(&smb))!=0) {
			smb_close(&smb);
			printf("smb_open_da returned %d: %s\n",i,smb.last_error);
			exit(1);
		}

		rewind(smb.sha_fp);
		chsize(fileno(smb.sha_fp),0L);		/* Truncate the header allocation file */
		rewind(smb.sda_fp);
		chsize(fileno(smb.sda_fp),0L);		/* Truncate the data allocation file */
	}

	rewind(smb.sid_fp);
	chsize(fileno(smb.sid_fp),0L);			/* Truncate the index */

	if(renumber || rehash) {
		printf("Truncating hash file (due to renumbering/rehashing)\n");
		if((i=smb_open_hash(&smb))!=SMB_SUCCESS) {
			printf("smb_open_hash returned %d: %s\n", i, smb.last_error);
			exit(1);
		}
		chsize(fileno(smb.hash_fp),0L);
	}

	if(!(smb.status.attr&SMB_HYPERALLOC)) {
		length=filelength(fileno(smb.sdt_fp));
		/* TODO: LE Only */
		w=0;
		for(l=0;l<length;l+=SDT_BLOCK_LEN)	/* Init .SDA file to NULL */
			fwrite(&w,2,1,smb.sda_fp);

		length=filelength(fileno(smb.shd_fp));
		c=0;
		for(l=smb.status.header_offset;l<length;l+=SHD_BLOCK_LEN)	/* Init .SHD file to NULL */
			fwrite(&c,1,1,smb.sha_fp);
	} else
		length=filelength(fileno(smb.shd_fp));

	n=0;	/* message offset */
	for(l=smb.status.header_offset;l<length;l+=size) {
		size=SHD_BLOCK_LEN;
		printf("\r%2lu%%  ",(long)(100.0/((float)length/l)));
		fflush(stdout);
		ZERO_VAR(msg);
		msg.idx.offset=l;
		if((i=smb_lockmsghdr(&smb,&msg))!=0) {
			printf("\n(%06lX) smb_lockmsghdr returned %d:\n%s\n",l,i,smb.last_error);
			continue;
		}
		i=smb_getmsghdr(&smb,&msg);
		smb_unlockmsghdr(&smb,&msg);
		if(i!=0) {
			printf("\n(%06lX) smb_getmsghdr returned %d:\n%s\n",l,i,smb.last_error);
			continue;
		}
		size=smb_hdrblocks(smb_getmsghdrlen(&msg))*SHD_BLOCK_LEN;
		printf("#%-5"PRIu32" (%06lX) %-25.25s ",msg.hdr.number,l
			,msg.hdr.type == SMB_MSG_TYPE_FILE ? msg.subj : msg.from);

		dupe_msgnum = FALSE;
		for(i=0; i<total && !dupe_msgnum; i++)
			if(msg.hdr.number == numbers[i])
				dupe_msgnum = TRUE;

		if(dupe_msgnum && fixnums && msg.hdr.number >= last_msg) {
			printf("Fixed message number (%lu -> %lu)\n", (ulong)msg.hdr.number, (ulong)highest + 1);
			msg.hdr.number = highest + 1;
			dupe_msgnum = FALSE;
		}
		if(!dupe_msgnum) {
			total++;
			if((numbers = realloc(numbers, total * sizeof(*numbers))) == NULL) {
				fprintf(stderr, "realloc failure: %lu\n", total * sizeof(*numbers));
				return EXIT_FAILURE;
			}
			numbers[total-1] = msg.hdr.number;
		}

		if(dupe_msgnum)
			msg.hdr.attr|=MSG_DELETE;
		else if(smb_undelete)
			msg.hdr.attr&=~MSG_DELETE;

		/* Create hash record */
		if(msg.hdr.attr&MSG_DELETE)
			text=NULL;
		else
			text=smb_getmsgtxt(&smb,&msg,GETMSGTXT_BODY_ONLY);
		i=smb_hashmsg(&smb,&msg,(uchar*)text,TRUE /* update */);
		if(i!=SMB_SUCCESS)
			printf("!ERROR %d hashing message\n", i);
		if(text!=NULL)
			free(text);

		/* Index the header */
		if(dupe_msgnum)
			printf("Not indexing duplicate message number (%u)\n", msg.hdr.number);
		else if(msg.hdr.attr&MSG_DELETE)
			printf("Not indexing deleted message\n");
		else if(msg.hdr.number==0)
			printf("Not indexing invalid message number (0)!\n");
		else {
			msg.idx_offset=n;
			if(renumber)
				msg.hdr.number=n+1;
			if(msg.hdr.number > highest)
				highest = msg.hdr.number;
			if(msg.hdr.netattr&MSG_INTRANSIT) {
				printf("Removing 'in transit' attribute\n");
				msg.hdr.netattr&=~MSG_INTRANSIT;
			}
			if((i=smb_putmsg(&smb,&msg))!=0) {
				printf("\nsmb_putmsg returned %d: %s\n",i,smb.last_error);
				continue;
			}
			n++;
		}

		if(!(smb.status.attr&SMB_HYPERALLOC)) {
			/**************************/
			/* Allocate header blocks */
			/**************************/
			fseek(smb.sha_fp,(l-smb.status.header_offset)/SHD_BLOCK_LEN,SEEK_SET);
			if(msg.hdr.attr&MSG_DELETE) c=0;		/* mark as free */
			else c=1;								/* or allocated */

			for(i=0;i<(int)(size/SHD_BLOCK_LEN);i++)
				fputc(c,smb.sha_fp);

			/************************/
			/* Allocate data blocks */
			/************************/

			if(!(msg.hdr.attr&MSG_DELETE))
				smb_incmsg_dfields(&smb,&msg,1);
		}

		smb_freemsgmem(&msg);
	}
	printf("\r%79s\r100%%\n","");
	smb.status.total_msgs=n;
	if(renumber)
		smb.status.last_msg = highest;
	else {
		if(highest > smb.status.last_msg)
			smb.status.last_msg = highest;
		sort_index(&smb);
	}
	printf("Saving message base status (%lu total messages).\n",n);
	if((i=smb_putstatus(&smb))!=0)
		printf("\nsmb_putstatus returned %d: %s\n",i,smb.last_error);
	smb_unlocksmbhdr(&smb);
	printf("Closing message base.\n");
	smb_close(&smb);
	unlock_msgbase();
	printf("Done.\n");
	FREE_AND_NULL(numbers);
	return(0);
}

int main(int argc, char **argv)
{
	int 		i;
	str_list_t	list;
	int			retval = EXIT_SUCCESS;

	printf("\nFIXSMB v3.19-%s %s/%s SMBLIB %s - Rebuild Synchronet Message Base\n\n"
		,PLATFORM_DESC, GIT_BRANCH, GIT_HASH, smb_lib_ver());

	list=strListInit();

	for(i=1;i<argc;i++) {
		if(argv[i][0]=='-') {
			if(!stricmp(argv[i],"-renumber"))
				renumber=TRUE;
			else if(!stricmp(argv[i],"-rehash"))
				rehash=TRUE;
			else if(!stricmp(argv[i],"-undelete"))
				smb_undelete=TRUE;
			else if(!stricmp(argv[i],"-fixnums"))
				fixnums=TRUE;
		} else
			strListPush(&list,argv[i]);
	}

	if(!strListCount(list)) {
		puts(usage);
		exit(1);
	}

	atexit(unlock_msgbase);
	for(i=0;list[i]!=NULL && retval == EXIT_SUCCESS;i++)
		retval = fixsmb(list[i]);

	return retval;
}