Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

smbutil.c 52 KB
Newer Older
1 2 3 4 5 6
/* Synchronet message base (SMB) utility */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *																			*
 * 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.	*
 ****************************************************************************/

22
#define SMBUTIL_VER "3.19"
23
char	compiler[32];
24

25 26 27 28 29
const char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
            ,"Jul","Aug","Sep","Oct","Nov","Dec"};


30
#define NOANALYSIS		(1L<<0)
31
#define NOCRC			(1L<<1)
32 33 34 35 36 37 38

#ifdef __WATCOMC__
	#define ffblk find_t
    #define findfirst(x,y,z) _dos_findfirst(x,z,y)
	#define findnext(x) _dos_findnext(x)
#endif

39 40 41 42 43
#if defined(_WIN32)
	#include <conio.h>	/* getch() */
#endif

/* ANSI */
44
#include <stdio.h>
rswindell's avatar
rswindell committed
45 46 47 48
#include <time.h>		/* time */
#include <errno.h>		/* errno */
#include <string.h>		/* strrchr */
#include <ctype.h>		/* toupper */
49

50 51 52
#include "fidodefs.h"
#include "smblib.h"
#include "str_util.h"
53
#include "utf8.h"
54
#include "conwrap.h"
55 56 57
#include "xpdatetime.h"
#include "git_branch.h"
#include "git_hash.h"
58

59
/* gets is dangerous */
60
#define gets(str)  fgets((str), sizeof(str), stdin)
61

62 63
#define CHSIZE_FP(fp,size)	if(chsize(fileno(fp),size)) printf("chsize failed!\n");

64 65 66 67
/********************/
/* Global variables */
/********************/

68 69 70 71 72 73 74 75 76
smb_t		smb;
ulong		mode=0L;
ushort		tzone=0;
ushort		xlat=XLAT_NONE;
FILE*		nulfp;
FILE*		errfp;
FILE*		statfp;
BOOL		pause_on_exit=FALSE;
BOOL		pause_on_error=FALSE;
77
char*		beep="";
rswindell's avatar
rswindell committed
78
long		msgtxtmode = GETMSGTXT_ALL|GETMSGTXT_PLAIN;
79 80 81 82 83 84

/************************/
/* Program usage/syntax */
/************************/

char *usage=
85
"usage: smbutil [-opts] cmd <filespec.shd>\n"
86 87 88 89
"\n"
"cmd:\n"
"       l[n] = list msgs starting at number n\n"
"       r[n] = read msgs starting at number n\n"
90
"       x[n] = dump msg index at number n\n"
91
"       v[n] = view msg headers starting at number n\n"
92
"       V[n] = view msg headers starting at number n verbose\n"
93 94 95
"       i[f] = import msg from text file f (or use stdin)\n"
"       e[f] = import e-mail from text file f (or use stdin)\n"
"       n[f] = import netmail from text file f (or use stdin)\n"
rswindell's avatar
rswindell committed
96
"       h    = dump hash file\n"
97 98
"       s    = display msg base status\n"
"       c    = change msg base status\n"
rswindell's avatar
rswindell committed
99
"       R    = re-initialize/repair SMB/status headers\n"
100 101 102
"       D    = delete and remove all msgs (not reversable)\n"
"       d    = flag all msgs for deletion\n"
"       u    = undelete all msgs (remove delete flag)\n"
103 104
"       m    = maintain msg base - delete old msgs and msgs over max\n"
"       p[k] = pack msg base (k specifies minimum packable Kbytes)\n"
rswindell's avatar
rswindell committed
105 106
"       L    = lock a msg base for exclusive-access/backup\n"
"       U    = unlock a msg base\n"
107 108 109 110 111
"\n"
"            [n] may represent 1-based message index offset, or\n"
"            [#n] actual message number, or\n"
"            [-n] message age (in days)\n"
"\n"
112
"opts:\n"
rswindell's avatar
rswindell committed
113 114 115 116 117 118 119 120 121
"      -c[m] = create message base if it doesn't exist (m=max msgs)\n"
"      -a    = always pack msg base (disable compression analysis)\n"
"      -i    = ignore dupes (do not store CRCs or search for duplicate hashes)\n"
"      -d    = use default values (no prompt) for to, from, and subject\n"
"      -l    = LZH-compress message text\n"
"      -o    = print errors on stdout (instead of stderr)\n"
"      -p    = wait for keypress (pause) on exit\n"
"      -!    = wait for keypress (pause) on error\n"
"      -b    = beep on error\n"
rswindell's avatar
rswindell committed
122
"      -r    = display raw message body text (not MIME-decoded)\n"
rswindell's avatar
rswindell committed
123 124 125 126 127 128 129 130
"      -C    = continue after some (normally fatal) error conditions\n"
"      -t<s> = set 'to' user name for imported message\n"
"      -n<s> = set 'to' netmail address for imported message\n"
"      -u<s> = set 'to' user number for imported message\n"
"      -f<s> = set 'from' user name for imported message\n"
"      -e<s> = set 'from' user number for imported message\n"
"      -s<s> = set 'subject' for imported message\n"
"      -z[n] = set time zone (n=min +/- from UT or 'EST','EDT','CST',etc)\n"
131
#ifdef __unix__
132
"      -U[n] = set umask to specified value (use leading 0 for octal, e.g. 022)\n"
133
#endif
rswindell's avatar
rswindell committed
134
"      -#    = set number of messages to view/list (e.g. -1)\n"
135 136
;

137 138 139 140 141 142 143 144 145 146 147 148 149
void bail(int code)
{

	if(pause_on_exit || (code && pause_on_error)) {
		fprintf(statfp,"\nHit enter to continue...");
		getchar();
	}

	if(code)
		fprintf(statfp,"\nReturning error code: %d\n",code);
	exit(code);
}

150
/*****************************************************************************/
151
/* Expands Unix LF to CRLF													 */
152
/*****************************************************************************/
153
ulong lf_expand(uchar* inbuf, uchar* outbuf)
154 155
{
	ulong	i,j;
rswindell's avatar
rswindell committed
156

157
	for(i=j=0;inbuf[i];i++) {
158 159 160 161
		if(inbuf[i]=='\n' && (!i || inbuf[i-1]!='\r'))
			outbuf[j++]='\r';
		outbuf[j++]=inbuf[i];
	}
162
	outbuf[j]=0;
163 164
	return(j);
}
rswindell's avatar
rswindell committed
165

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
char* gen_msgid(smb_t* smb, smbmsg_t* msg, char* msgid, size_t maxlen)
{
	char* host = getenv(
#if defined(_WIN32)
		"COMPUTERNAME"
#else
		"HOSTNAME"
#endif
	);
	if(host == NULL)
		host = getenv(
#if defined(_WIN32)
		"USERNAME"
#else
		"USER"
#endif
	);
	safe_snprintf(msgid, maxlen
		,"<%08lX.%lu.%s@%s>"
185 186
		,(ulong)msg->hdr.when_imported.time
		,(ulong)smb->status.last_msg + 1
187 188 189 190 191
		,getfname(smb->file)
		,host);
	return msgid;
}

192 193 194
/****************************************************************************/
/* Adds a new message to the message base									*/
/****************************************************************************/
195 196
void postmsg(char type, char* to, char* to_number, char* to_address, 
			 char* from, char* from_number, char* subject, FILE* fp)
197
{
198 199
	char		str[128];
	char		buf[1024];
rswindell's avatar
rswindell committed
200 201
	char*		msgtxt=NULL;
	char*		newtxt;
202
	const char* charset = NULL;
203 204
	long		msgtxtlen;
	int 		i;
205
	ushort		agent=AGENT_SMBUTIL;
206
	smbmsg_t	msg;
207
	long		dupechk_hashes=SMB_HASH_SOURCE_DUPE;
208

209 210 211
	/* Read message text from stream (file or stdin) */
	msgtxtlen=0;
	while(!feof(fp)) {
212
		i=fread(buf,1,sizeof(buf),fp);
213 214
		if(i<1)
			break;
rswindell's avatar
rswindell committed
215
		if((msgtxt = realloc(msgtxt,msgtxtlen+i+1))==NULL) {
216
			fprintf(errfp,"\n%s!realloc(%ld) failure\n",beep,msgtxtlen+i+1);
217
			bail(1);
218
		}
219
		memcpy(msgtxt+msgtxtlen,buf,i);
220 221 222
		msgtxtlen+=i;
	}

223
	if(msgtxt!=NULL) {
224 225 226

		msgtxt[msgtxtlen]=0;	/* Must be NULL-terminated */

rswindell's avatar
rswindell committed
227
		if((newtxt = malloc((msgtxtlen*2)+1))==NULL) {
228
			fprintf(errfp,"\n%s!malloc(%ld) failure\n",beep,(msgtxtlen*2)+1);
229
			bail(1);
230
		}
rswindell's avatar
rswindell committed
231

232
		/* Expand LFs to CRLFs */
rswindell's avatar
rswindell committed
233
		msgtxtlen=lf_expand((uchar*)msgtxt, (uchar*)newtxt);
234 235
		free(msgtxt);
		msgtxt=newtxt;
236 237 238 239 240 241 242

		if(!str_is_ascii(msgtxt) && utf8_str_is_valid(msgtxt))
			charset = FIDO_CHARSET_UTF8;
		else if(str_is_ascii(msgtxt))
			charset = FIDO_CHARSET_ASCII;
		else
			charset = FIDO_CHARSET_CP437;
243
	}
rswindell's avatar
rswindell committed
244 245

	memset(&msg,0,sizeof(smbmsg_t));
246
	msg.hdr.when_written.time=(uint32_t)time(NULL);
rswindell's avatar
rswindell committed
247 248 249
	msg.hdr.when_written.zone=tzone;
	msg.hdr.when_imported=msg.hdr.when_written;

250 251
	if((to==NULL || stricmp(to,"All")==0) && to_address!=NULL)
		to=to_address;
252 253
	if(to==NULL) {
		printf("To User Name: ");
254
		fgets(str,sizeof(str),stdin); 
255
	} else
256
		SAFECOPY(str,to);
rswindell's avatar
rswindell committed
257
	truncsp(str);
258 259

	if((i=smb_hfield_str(&msg,RECIPIENT,str))!=SMB_SUCCESS) {
260 261
		fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
			,beep,RECIPIENT,i,smb.last_error);
262
		bail(1); 
rswindell's avatar
rswindell committed
263
	}
rswindell's avatar
rswindell committed
264 265 266
	if(type=='E' || type=='N')
		smb.status.attr|=SMB_EMAIL;
	if(smb.status.attr&SMB_EMAIL) {
267 268 269 270 271 272 273 274 275 276 277 278
		if(to_address==NULL) {
			if(to_number==NULL) {
				printf("To User Number: ");
				gets(str);
			} else
				SAFECOPY(str,to_number);
			truncsp(str);
			if((i=smb_hfield_str(&msg,RECIPIENTEXT,str))!=SMB_SUCCESS) {
				fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
					,beep,RECIPIENTEXT,i,smb.last_error);
				bail(1); 
			}
279 280
		}
	}
rswindell's avatar
rswindell committed
281

282
	if(smb.status.attr&SMB_EMAIL && (type=='N' || to_address!=NULL)) {
283
		if(to_address==NULL) {
284
			printf("To Address (e.g. user@host or 1:2/3): ");
285 286
			gets(str);
		} else
287
			SAFECOPY(str,to_address);
rswindell's avatar
rswindell committed
288 289
		truncsp(str);
		if(*str) {
290 291
			if((i=smb_hfield_netaddr(&msg,RECIPIENTNETADDR,str,NULL))!=SMB_SUCCESS) {
				fprintf(errfp,"\n%s!smb_hfield_netaddr(0x%02X) returned %d: %s\n"
292
					,beep,RECIPIENTNETADDR,i,smb.last_error);
293
				bail(1); 
294
			}
295
		}
296
	}
rswindell's avatar
rswindell committed
297

298 299 300 301
	if(from==NULL) {
		printf("From User Name: ");
		gets(str);
	} else
302
		SAFECOPY(str,from);
rswindell's avatar
rswindell committed
303
	truncsp(str);
304
	if((i=smb_hfield_str(&msg,SENDER,str))!=SMB_SUCCESS) {
305 306
		fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
			,beep,SENDER,i,smb.last_error);
307
		bail(1); 
rswindell's avatar
rswindell committed
308
	}
309
	if((smb.status.attr&SMB_EMAIL) || from_number!=NULL) {
310 311 312 313
		if(from_number==NULL) {
			printf("From User Number: ");
			gets(str);
		} else
314
			SAFECOPY(str,from_number);
rswindell's avatar
rswindell committed
315
		truncsp(str);
316
		if((i=smb_hfield_str(&msg,SENDEREXT,str))!=SMB_SUCCESS) {
317 318
			fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
				,beep,SENDEREXT,i,smb.last_error);
319
			bail(1); 
320 321
		}
	}
322
	if((i=smb_hfield(&msg, SENDERAGENT, sizeof(agent), &agent))!=SMB_SUCCESS) {
323 324
		fprintf(errfp,"\n%s!smb_hfield(0x%02X) returned %d: %s\n"
			,beep,SENDERAGENT,i,smb.last_error);
325
		bail(1);
326
	}
rswindell's avatar
rswindell committed
327

328 329 330 331
	if(subject==NULL) {
		printf("Subject: ");
		gets(str);
	} else
332
		SAFECOPY(str,subject);
rswindell's avatar
rswindell committed
333
	truncsp(str);
334
	if((i=smb_hfield_str(&msg,SUBJECT,str))!=SMB_SUCCESS) {
335 336
		fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
			,beep,SUBJECT,i,smb.last_error);
337
		bail(1); 
338
	}
rswindell's avatar
rswindell committed
339

340
	safe_snprintf(str,sizeof(str),"SMBUTIL %s-%s %s/%s %s %s"
341 342
		,SMBUTIL_VER
		,PLATFORM_DESC
343
		,GIT_BRANCH, GIT_HASH
344 345 346
		,__DATE__
		,compiler
		);
347
	if((i=smb_hfield_str(&msg,FIDOPID,str))!=SMB_SUCCESS) {
348 349
		fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
			,beep,FIDOPID,i,smb.last_error);
350
		bail(1); 
351
	}
rswindell's avatar
rswindell committed
352

353
	smb_hfield_str(&msg, RFC822MSGID, gen_msgid(&smb, &msg, str, sizeof(str)-1));
354 355 356 357 358 359 360
	if(charset != NULL)
		smb_hfield_str(&msg, FIDOCHARSET, charset);

	if((msg.to != NULL && !str_is_ascii(msg.to) && utf8_str_is_valid(msg.to))
		|| (msg.from != NULL && !str_is_ascii(msg.from) && utf8_str_is_valid(msg.from))
		|| (msg.subj != NULL && !str_is_ascii(msg.subj) && utf8_str_is_valid(msg.subj)))
		msg.hdr.auxattr |= MSG_HFIELDS_UTF8;
361

362 363 364
	if(mode&NOCRC || smb.status.max_crcs==0)	/* no CRC checking means no body text dupe checking */
		dupechk_hashes&=~(1<<SMB_HASH_SOURCE_BODY);

365
	if((i=smb_addmsg(&smb,&msg,smb.status.attr&SMB_HYPERALLOC
rswindell's avatar
rswindell committed
366
		,dupechk_hashes,xlat,(uchar*)msgtxt,NULL))!=SMB_SUCCESS) {
367 368
		fprintf(errfp,"\n%s!smb_addmsg returned %d: %s\n"
			,beep,i,smb.last_error);
369
		bail(1); 
rswindell's avatar
rswindell committed
370
	}
rswindell's avatar
rswindell committed
371
	smb_freemsgmem(&msg);
372

deuce's avatar
deuce committed
373
	// MSVC can't do %zu for size_t until MSVC 2017 it seems...
374
	fprintf(statfp, "Message (%ld bytes) added to %s successfully\n", msgtxtlen, smb.file);
375
	FREE_AND_NULL(msgtxt);
376 377 378 379 380
}

/****************************************************************************/
/* Shows the message base header											*/
/****************************************************************************/
381
void showstatus(void)
382 383 384
{
	int i;

rswindell's avatar
rswindell committed
385 386
	i=smb_locksmbhdr(&smb);
	if(i) {
387 388
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
389 390
		return; 
	}
rswindell's avatar
rswindell committed
391 392 393
	i=smb_getstatus(&smb);
	smb_unlocksmbhdr(&smb);
	if(i) {
394 395
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
396 397
		return; 
	}
deuce's avatar
deuce committed
398 399 400 401 402
	printf("last_msg        =%"PRIu32"\n"
		   "total_msgs      =%"PRIu32"\n"
		   "header_offset   =%"PRIu32"\n"
		   "max_crcs        =%"PRIu32"\n"
		   "max_msgs        =%"PRIu32"\n"
rswindell's avatar
rswindell committed
403 404 405 406 407 408 409 410 411 412
		   "max_age         =%u\n"
		   "attr            =%04Xh\n"
		   ,smb.status.last_msg
		   ,smb.status.total_msgs
		   ,smb.status.header_offset
		   ,smb.status.max_crcs
		   ,smb.status.max_msgs
		   ,smb.status.max_age
		   ,smb.status.attr
		   );
413 414 415 416 417
}

/****************************************************************************/
/* Configure message base header											*/
/****************************************************************************/
418
void config(void)
419
{
420
	char str[128];
421 422
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;
423
	uint32_t last_msg = 0;
424

rswindell's avatar
rswindell committed
425 426
	i=smb_locksmbhdr(&smb);
	if(i) {
427 428
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
429 430
		return; 
	}
rswindell's avatar
rswindell committed
431 432 433
	i=smb_getstatus(&smb);
	smb_unlocksmbhdr(&smb);
	if(i) {
434 435
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
436 437
		return; 
	}
438 439 440
	printf("Last Message  =%-6"PRIu32" New Value (CR=No Change): "
		,smb.status.last_msg);
	gets(str);
441
	if(IS_DIGIT(str[0]))
442
		last_msg = atol(str);
deuce's avatar
deuce committed
443
	printf("Header offset =%-5"PRIu32"  New value (CR=No Change): "
rswindell's avatar
rswindell committed
444 445
		,smb.status.header_offset);
	gets(header_offset);
deuce's avatar
deuce committed
446
	printf("Max msgs      =%-5"PRIu32"  New value (CR=No Change): "
rswindell's avatar
rswindell committed
447 448
		,smb.status.max_msgs);
	gets(max_msgs);
deuce's avatar
deuce committed
449
	printf("Max crcs      =%-5"PRIu32"  New value (CR=No Change): "
rswindell's avatar
rswindell committed
450 451 452 453 454 455 456 457 458 459
		,smb.status.max_crcs);
	gets(max_crcs);
	printf("Max age       =%-5u  New value (CR=No Change): "
		,smb.status.max_age);
	gets(max_age);
	printf("Attributes    =%-5u  New value (CR=No Change): "
		,smb.status.attr);
	gets(attr);
	i=smb_locksmbhdr(&smb);
	if(i) {
460 461
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
462 463
		return; 
	}
rswindell's avatar
rswindell committed
464 465
	i=smb_getstatus(&smb);
	if(i) {
466 467
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
468
		smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
469 470
		return; 
	}
471 472
	if(last_msg != 0)
		smb.status.last_msg = last_msg;
473
	if(IS_DIGIT(max_msgs[0]))
rswindell's avatar
rswindell committed
474
		smb.status.max_msgs=atol(max_msgs);
475
	if(IS_DIGIT(max_crcs[0]))
rswindell's avatar
rswindell committed
476
		smb.status.max_crcs=atol(max_crcs);
477
	if(IS_DIGIT(max_age[0]))
rswindell's avatar
rswindell committed
478
		smb.status.max_age=atoi(max_age);
479
	if(IS_DIGIT(header_offset[0]))
rswindell's avatar
rswindell committed
480
		smb.status.header_offset=atol(header_offset);
481
	if(IS_DIGIT(attr[0]))
rswindell's avatar
rswindell committed
482 483
		smb.status.attr=atoi(attr);
	i=smb_putstatus(&smb);
484
	smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
485
	if(i)
486 487
		fprintf(errfp,"\n%s!smb_putstatus returned %d: %s\n"
			,beep,i,smb.last_error);
488 489 490 491 492 493 494 495 496 497
}

/****************************************************************************/
/* Lists messages' to, from, and subject                                    */
/****************************************************************************/
void listmsgs(ulong start, ulong count)
{
	int i;
	ulong l=0;
	smbmsg_t msg;
498
	size_t idxreclen = smb_idxreclen(&smb);
499

rswindell's avatar
rswindell committed
500 501
	if(!start)
		start=1;
502 503
	if(!count)
		count=~0;
rswindell's avatar
rswindell committed
504
	while(l<count) {
505
		fseek(smb.sid_fp,((start-1L) + l)*idxreclen,SEEK_SET);
506
		if(!fread(&msg.idx,sizeof(msg.idx),1,smb.sid_fp))
rswindell's avatar
rswindell committed
507 508 509
			break;
		i=smb_lockmsghdr(&smb,&msg);
		if(i) {
510 511
			fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
512 513
			break; 
		}
rswindell's avatar
rswindell committed
514 515 516
		i=smb_getmsghdr(&smb,&msg);
		smb_unlockmsghdr(&smb,&msg);
		if(i) {
517 518
			fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
519 520
			break; 
		}
521 522
		printf("%4lu/#%-4"PRIu32" %-25.25s %-25.25s %s\n"
			,start + l, msg.hdr.number,msg.from,msg.to,msg.subj);
rswindell's avatar
rswindell committed
523
		smb_freemsgmem(&msg);
rswindell's avatar
rswindell committed
524 525
		l++; 
	}
526 527 528 529 530 531
}

/****************************************************************************/
/* Generates a 24 character ASCII string that represents the time_t pointer */
/* Used as a replacement for ctime()                                        */
/****************************************************************************/
532
char *my_timestr(time_t intime)
533 534 535 536 537
{
    static char str[256];
    char mer[3],hour;
    struct tm *gm;

538
	gm=localtime(&intime);
rswindell's avatar
rswindell committed
539 540
	if(gm==NULL) {
		strcpy(str,"Invalid Time");
rswindell's avatar
rswindell committed
541 542
		return(str); 
	}
rswindell's avatar
rswindell committed
543 544 545 546 547
	if(gm->tm_hour>=12) {
		if(gm->tm_hour==12)
			hour=12;
		else
			hour=gm->tm_hour-12;
rswindell's avatar
rswindell committed
548 549
		strcpy(mer,"pm"); 
	}
rswindell's avatar
rswindell committed
550 551 552 553 554
	else {
		if(gm->tm_hour==0)
			hour=12;
		else
			hour=gm->tm_hour;
rswindell's avatar
rswindell committed
555 556
		strcpy(mer,"am"); 
	}
rswindell's avatar
rswindell committed
557 558 559 560
	sprintf(str,"%s %s %02d %4d %02d:%02d %s"
		,wday[gm->tm_wday],mon[gm->tm_mon],gm->tm_mday,1900+gm->tm_year
		,hour,gm->tm_min,mer);
	return(str);
561 562
}

563 564 565 566
/****************************************************************************/
/****************************************************************************/
void dumpindex(ulong start, ulong count)
{
567
	char tmp[128];
568 569
	ulong l=0;
	idxrec_t idx;
570
	size_t idxreclen = smb_idxreclen(&smb);
571 572 573 574 575 576

	if(!start)
		start=1;
	if(!count)
		count=~0;
	while(l<count) {
577
		fseek(smb.sid_fp,((start-1L) + l) * idxreclen,SEEK_SET);
578
		if(!fread(&idx,sizeof(idx),1,smb.sid_fp))
579
			break;
580
		printf("%10"PRIu32"  ", idx.number);
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
		switch(smb_msg_type(idx.attr)) {
			case SMB_MSG_TYPE_FILE:
				printf("F %10lu  ", (ulong)idx.size);
				break;
			case SMB_MSG_TYPE_BALLOT:
				printf("V  %04hX  %-10"PRIu32, idx.votes,idx.remsg);
				break;
			default:
				printf("%c  %04hX  %04hX  %04X"
					,(idx.attr&MSG_POLL_VOTE_MASK) == MSG_POLL_CLOSURE ? 'C' : (idx.attr&MSG_POLL ? 'P':' ')
					,idx.from, idx.to, idx.subj);
				break;
		}
		printf("  %04X  %06X  %s", idx.attr, idx.offset
			,xpDate_to_isoDateStr(time_to_xpDate(idx.time), "-", tmp, sizeof(tmp)));
		if(smb_msg_type(idx.attr) == SMB_MSG_TYPE_FILE && idxreclen == sizeof(fileidxrec_t)) {
			fileidxrec_t fidx;
			fseek(smb.sid_fp,((start-1L) + l) * idxreclen,SEEK_SET);
599
			if(!fread(&fidx,sizeof(fidx),1,smb.sid_fp))
600 601 602 603
				break;
			printf("  %02X  %.*s", fidx.hash.flags, (int)sizeof(fidx.name), fidx.name);
		}
		printf("\n");
604 605 606 607
		l++; 
	}
}

608 609 610
/****************************************************************************/
/* Displays message header information										*/
/****************************************************************************/
611
void viewmsgs(ulong start, ulong count, BOOL verbose)
612
{
613
	int i,j;
614 615
	ulong l=0;
	smbmsg_t msg;
616
	size_t idxreclen = smb_idxreclen(&smb);
617

rswindell's avatar
rswindell committed
618 619
	if(!start)
		start=1;
620 621
	if(!count)
		count=~0;
rswindell's avatar
rswindell committed
622
	while(l<count) {
623
		fseek(smb.sid_fp,((start-1L) + l) * idxreclen,SEEK_SET);
624
		if(!fread(&msg.idx,sizeof(msg.idx),1,smb.sid_fp))
rswindell's avatar
rswindell committed
625 626 627
			break;
		i=smb_lockmsghdr(&smb,&msg);
		if(i) {
628 629
			fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
630 631
			break; 
		}
rswindell's avatar
rswindell committed
632 633 634
		i=smb_getmsghdr(&smb,&msg);
		smb_unlockmsghdr(&smb,&msg);
		if(i) {
635 636
			fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
637 638
			break; 
		}
639

640
		printf("--------------------\n");
641
		printf("%-16.16s %ld\n"		,"index record",ftell(smb.sid_fp)/idxreclen);
642
		smb_dump_msghdr(stdout,&msg);
643 644 645 646 647 648 649 650 651
		if(verbose) {
			for(i=0; i<msg.total_hfields; i++) {
				printf("hdr field[%02u]        type %02Xh, length %u, data:"
					,i, msg.hfield[i].type, msg.hfield[i].length);
				for(j=0; j<msg.hfield[i].length; j++)
					printf(" %02X", *((uint8_t*)msg.hfield_dat[i]+j));
				printf("\n");
			}
		}
rswindell's avatar
rswindell committed
652 653
		printf("\n");
		smb_freemsgmem(&msg);
rswindell's avatar
rswindell committed
654 655
		l++; 
	}
656 657
}

658 659 660 661
/****************************************************************************/
/****************************************************************************/
void dump_hashes(void)
{
rswindell's avatar
rswindell committed
662
	char	tmp[128];
663 664 665 666
	int		retval;
	hash_t	hash;

	if((retval=smb_open_hash(&smb))!=SMB_SUCCESS) {
667 668
		fprintf(errfp,"\n%s!smb_open_hash returned %d: %s\n"
			,beep, retval, smb.last_error);
669 670 671 672 673 674
		return;
	}

	while(!smb_feof(smb.hash_fp)) {
		if(smb_fread(&smb,&hash,sizeof(hash),smb.hash_fp)!=sizeof(hash))
			break;
rswindell's avatar
rswindell committed
675
		printf("\n");
deuce's avatar
deuce committed
676
		printf("%-10s: %"PRIu32"\n","Number",	hash.number);
677
		printf("%-10s: %s\n",		"Source",	smb_hashsourcetype(hash.source));
deuce's avatar
deuce committed
678
		printf("%-10s: %"PRIu32"\n","Length",	hash.length);
679
		printf("%-10s: %s\n",		"Time",		my_timestr(hash.time));
680
		printf("%-10s: %02x\n",		"Flags",	hash.flags);
rswindell's avatar
rswindell committed
681
		if(hash.flags&SMB_HASH_CRC16)
682
			printf("%-10s: %04x\n",	"CRC-16",	hash.data.crc16);
rswindell's avatar
rswindell committed
683
		if(hash.flags&SMB_HASH_CRC32)
684
			printf("%-10s: %08"PRIx32"\n","CRC-32",	hash.data.crc32);
rswindell's avatar
rswindell committed
685
		if(hash.flags&SMB_HASH_MD5)
686 687 688
			printf("%-10s: %s\n",	"MD5",		MD5_hex(tmp,hash.data.md5));
		if(hash.flags&SMB_HASH_SHA1)
			printf("%-10s: %s\n",	"SHA-1",	SHA1_hex(tmp,hash.data.sha1));
689
	}
690 691

	smb_close_hash(&smb);
692 693
}

694 695 696 697 698 699 700 701 702 703
/****************************************************************************/
/* Maintain message base - deletes messages older than max age (in days)	*/
/* or messages that exceed maximum											*/
/****************************************************************************/
void maint(void)
{
	int i;
	ulong l,m,n,f,flagged=0;
	time_t now;
	smbmsg_t msg;
deuce's avatar
deuce committed
704
	idxrec_t *idx;
705 706
	size_t idxreclen = smb_idxreclen(&smb);
	uint8_t* idxbuf;
707 708 709 710 711

	printf("Maintaining %s\r\n",smb.file);
	now=time(NULL);
	i=smb_locksmbhdr(&smb);
	if(i) {
712 713
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
714 715
		return; 
	}
716 717 718
	i=smb_getstatus(&smb);
	if(i) {
		smb_unlocksmbhdr(&smb);
719 720
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
721 722
		return; 
	}
723
	if((smb.status.max_msgs || smb.status.max_crcs) && smb_open_hash(&smb) == SMB_SUCCESS)
724 725 726 727 728
	{
		ulong max_hashes=0;

		printf("Maintaining %s hash file\r\n", smb.file);

729
		if((smb.status.attr&(SMB_EMAIL|SMB_NOHASH|SMB_FILE_DIRECTORY)) == 0) {
730 731 732 733 734 735 736 737 738 739 740 741 742
			max_hashes = smb.status.max_msgs;
			if(smb.status.max_crcs > max_hashes)
				max_hashes = smb.status.max_crcs;
		}
		if(!max_hashes) {
			CHSIZE_FP(smb.hash_fp,0);
		} else if(filelength(fileno(smb.hash_fp)) > (long)(max_hashes * SMB_HASH_SOURCE_TYPES * sizeof(hash_t))) {
			if(fseek(smb.hash_fp, -((long)(max_hashes * SMB_HASH_SOURCE_TYPES * sizeof(hash_t))), SEEK_END) == 0) {
				hash_t*	hashes = malloc(max_hashes * SMB_HASH_SOURCE_TYPES * sizeof(hash_t));
				if(hashes != NULL) {
					if(fread(hashes, sizeof(hash_t), max_hashes * SMB_HASH_SOURCE_TYPES, smb.hash_fp) == max_hashes * SMB_HASH_SOURCE_TYPES) {
						rewind(smb.hash_fp);
						fwrite(hashes, sizeof(hash_t), max_hashes * SMB_HASH_SOURCE_TYPES, smb.hash_fp);
743 744
						fflush(smb.hash_fp);
						CHSIZE_FP(smb.hash_fp, sizeof(hash_t) * max_hashes * SMB_HASH_SOURCE_TYPES);
745 746 747 748 749 750 751
					}
					free(hashes);
				}
			}
		}
		smb_close_hash(&smb);
	}
752 753 754
	if(!smb.status.total_msgs) {
		smb_unlocksmbhdr(&smb);
		printf("Empty\n");
rswindell's avatar
rswindell committed
755 756
		return; 
	}
757
	printf("Loading index...\n");
758
	if((idxbuf = malloc(idxreclen * smb.status.total_msgs))
759 760
		==NULL) {
		smb_unlocksmbhdr(&smb);
deuce's avatar
deuce committed
761
		fprintf(errfp,"\n%s!Error allocating %" XP_PRIsize_t "u bytes of memory\n"
762
			,beep,idxreclen * smb.status.total_msgs);
rswindell's avatar
rswindell committed
763 764
		return; 
	}
765
	fseek(smb.sid_fp,0L,SEEK_SET);
766
	l = fread(idxbuf, idxreclen, smb.status.total_msgs, smb.sid_fp);
767

768
	printf("\nDone.\n\n");
769 770
	printf("Scanning for pre-flagged messages...\n");
	for(m=0;m<l;m++) {
771
		idx = (idxrec_t*)(idxbuf + (m * idxreclen));
772
//		printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
773
		if(idx->attr&MSG_DELETE)
rswindell's avatar
rswindell committed
774 775
			flagged++; 
	}
776 777 778 779 780 781
	printf("\r100%% (%lu pre-flagged for deletion)\n",flagged);

	if(smb.status.max_age) {
		printf("Scanning for messages more than %u days old...\n"
			,smb.status.max_age);
		for(m=f=0;m<l;m++) {
782
			idx = (idxrec_t*)(idxbuf + (m * idxreclen));
783
//			printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
784
			if(idx->attr&(MSG_PERMANENT|MSG_DELETE))
785
				continue;
786
			if((ulong)now>idx->time && (now-idx->time)/(24L*60L*60L)
787 788 789
				>smb.status.max_age) {
				f++;
				flagged++;
790
				idx->attr|=MSG_DELETE; 
rswindell's avatar
rswindell committed
791 792
			} 
		}  /* mark for deletion */
793
		printf("\r100%% (%lu flagged for deletion due to age)\n",f); 
rswindell's avatar
rswindell committed
794
	}
795 796

	printf("Scanning for read messages to be killed...\n");
797
	uint32_t total_msgs = 0;
798
	for(m=f=0;m<l;m++) {
799 800
		idx = (idxrec_t*)(idxbuf + (m * idxreclen));
		enum smb_msg_type type = smb_msg_type(idx->attr);
801
		if(type == SMB_MSG_TYPE_NORMAL || type == SMB_MSG_TYPE_POLL)
802
			total_msgs++;
803
//		printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
804
		if(idx->attr&(MSG_PERMANENT|MSG_DELETE))
805
			continue;
806
		if((idx->attr&(MSG_READ|MSG_KILLREAD))==(MSG_READ|MSG_KILLREAD)) {
807 808
			f++;
			flagged++;
809 810
			idx->attr|=MSG_DELETE; 
		} 
rswindell's avatar
rswindell committed
811
	}
812
	printf("\r100%% (%lu flagged for deletion due to read status)\n",f);
813

814
	if(smb.status.max_msgs && total_msgs - flagged > smb.status.max_msgs) {
815
		printf("Flagging excess messages for deletion...\n");
816 817 818
		for(m=n=0,f=flagged;l-flagged>smb.status.max_msgs && m<l;m++) {
			idx = (idxrec_t*)(idxbuf + (m * idxreclen));
			if(idx->attr&(MSG_PERMANENT|MSG_DELETE))
819
				continue;
820
			printf("%lu of %lu\r",++n,(total_msgs - f)-smb.status.max_msgs);
821
			flagged++;
822
			idx->attr|=MSG_DELETE; 
rswindell's avatar
rswindell committed
823 824 825
		}			/* mark for deletion */
		printf("\nDone.\n\n"); 
	}
826 827

	if(!flagged) {				/* No messages to delete */
828
		free(idxbuf);
829
		smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
830 831
		return; 
	}
832 833 834 835 836 837 838 839

	if(!(mode&NOANALYSIS)) {

		printf("Freeing allocated header and data blocks for deleted messages...\n");
		if(!(smb.status.attr&SMB_HYPERALLOC)) {
			i=smb_open_da(&smb);
			if(i) {
				smb_unlocksmbhdr(&smb);
840 841
				fprintf(errfp,"\n%s!smb_open_da returned %d: %s\n"
					,beep,i,smb.last_error);
842
				bail(1); 
rswindell's avatar
rswindell committed