smbutil.c 47.5 KB
Newer Older
1
2
3
/* Synchronet message base (SMB) utility */

/* $Id$ */
4
// vi: tabstop=4
5
6
7
8
9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 *																			*
 * 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										*
 *																			*
 * 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.	*
 ****************************************************************************/

rswindell's avatar
rswindell committed
37
#define SMBUTIL_VER "2.34"
38
39
char	revision[16];
char	compiler[32];
40

41
42
43
44
45
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"};


46
#define NOANALYSIS		(1L<<0)
47
#define NOCRC			(1L<<1)
48
49
50
51
52
53
54

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

55
56
57
58
59
60
#if defined(_WIN32)
	#include <ctype.h>	/* isdigit() */
	#include <conio.h>	/* getch() */
#endif

/* ANSI */
61
#include <stdio.h>
rswindell's avatar
rswindell committed
62
63
64
65
#include <time.h>		/* time */
#include <errno.h>		/* errno */
#include <string.h>		/* strrchr */
#include <ctype.h>		/* toupper */
66

67
#include "genwrap.h"	/* stricmp */
rswindell's avatar
rswindell committed
68
#include "dirwrap.h"	/* fexist */
rswindell's avatar
rswindell committed
69
#include "conwrap.h"	/* getch */
70
#include "filewrap.h"
71
#include "smblib.h"
72
#include "gen_defs.h"	/* MAX_PATH */
73
74
75
76
77

#ifdef __WATCOMC__
	#include <dos.h>
#endif

78
/* gets is dangerous */
79
#define gets(str)  fgets((str), sizeof(str), stdin)
80

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

83
84
85
86
/********************/
/* Global variables */
/********************/

87
88
89
90
91
92
93
94
95
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;
96
char*		beep="";
97
98
99
100
101
102

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

char *usage=
103
"usage: smbutil [-opts] cmd <filespec.shd>\n"
104
105
106
107
"\n"
"cmd:\n"
"       l[n] = list msgs starting at number n\n"
"       r[n] = read msgs starting at number n\n"
108
"       x[n] = dump msg index at number n\n"
109
"       v[n] = view msg headers starting at number n\n"
110
111
112
"       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
113
"       h    = dump hash file\n"
114
115
"       s    = display msg base status\n"
"       c    = change msg base status\n"
rswindell's avatar
rswindell committed
116
"       R    = re-initialize/repair SMB/status headers\n"
117
"       d    = delete all msgs\n"
118
119
"       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
120
121
"       L    = lock a msg base for exclusive-access/backup\n"
"       U    = unlock a msg base\n"
122
"opts:\n"
rswindell's avatar
rswindell committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"      -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"
"      -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"
140
141
142
#ifdef __unix__
"      -U[n] = set umask to specified value\n"
#endif
rswindell's avatar
rswindell committed
143
"      -#    = set number of messages to view/list (e.g. -1)\n"
144
145
;

146
147
148
149
150
151
152
153
154
155
156
157
158
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);
}

159
/*****************************************************************************/
160
/* Expands Unix LF to CRLF													 */
161
/*****************************************************************************/
162
ulong lf_expand(uchar* inbuf, uchar* outbuf)
163
164
{
	ulong	i,j;
rswindell's avatar
rswindell committed
165

166
	for(i=j=0;inbuf[i];i++) {
167
168
169
170
		if(inbuf[i]=='\n' && (!i || inbuf[i-1]!='\r'))
			outbuf[j++]='\r';
		outbuf[j++]=inbuf[i];
	}
171
	outbuf[j]=0;
172
173
	return(j);
}
rswindell's avatar
rswindell committed
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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>"
		,msg->hdr.when_imported.time
		,smb->status.last_msg + 1
		,getfname(smb->file)
		,host);
	return msgid;
}

201
202
203
/****************************************************************************/
/* Adds a new message to the message base									*/
/****************************************************************************/
204
205
void postmsg(char type, char* to, char* to_number, char* to_address, 
			 char* from, char* from_number, char* subject, FILE* fp)
206
{
207
208
	char		str[128];
	char		buf[1024];
209
210
	uchar*		msgtxt=NULL;
	uchar*		newtxt;
211
212
	long		msgtxtlen;
	int 		i;
213
	ushort		agent=AGENT_SMBUTIL;
214
	smbmsg_t	msg;
215
	long		dupechk_hashes=SMB_HASH_SOURCE_DUPE;
216

217
218
219
220
221
222
	/* Read message text from stream (file or stdin) */
	msgtxtlen=0;
	while(!feof(fp)) {
		i=fread(buf,1,sizeof(buf),fp);
		if(i<1)
			break;
223
		if((msgtxt=(uchar*)realloc(msgtxt,msgtxtlen+i+1))==NULL) {
224
			fprintf(errfp,"\n%s!realloc(%ld) failure\n",beep,msgtxtlen+i+1);
225
			bail(1);
226
		}
227
		memcpy(msgtxt+msgtxtlen,buf,i);
228
229
230
		msgtxtlen+=i;
	}

231
	if(msgtxt!=NULL) {
232
233
234

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

235
		if((newtxt=(uchar*)malloc((msgtxtlen*2)+1))==NULL) {
236
			fprintf(errfp,"\n%s!malloc(%ld) failure\n",beep,(msgtxtlen*2)+1);
237
			bail(1);
238
		}
rswindell's avatar
rswindell committed
239

240
241
242
243
244
		/* Expand LFs to CRLFs */
		msgtxtlen=lf_expand(msgtxt, newtxt);
		free(msgtxt);
		msgtxt=newtxt;
	}
rswindell's avatar
rswindell committed
245
246

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

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

	if((i=smb_hfield_str(&msg,RECIPIENT,str))!=SMB_SUCCESS) {
261
262
		fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
			,beep,RECIPIENT,i,smb.last_error);
263
		bail(1); 
rswindell's avatar
rswindell committed
264
	}
rswindell's avatar
rswindell committed
265
266
267
	if(type=='E' || type=='N')
		smb.status.attr|=SMB_EMAIL;
	if(smb.status.attr&SMB_EMAIL) {
268
269
270
271
272
273
274
275
276
277
278
279
		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); 
			}
280
281
		}
	}
rswindell's avatar
rswindell committed
282

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

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

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

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

354
355
	smb_hfield_str(&msg, RFC822MSGID, gen_msgid(&smb, &msg, str, sizeof(str)-1));

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

359
	if((i=smb_addmsg(&smb,&msg,smb.status.attr&SMB_HYPERALLOC
360
		,dupechk_hashes,xlat,msgtxt,NULL))!=SMB_SUCCESS) {
361
362
		fprintf(errfp,"\n%s!smb_addmsg returned %d: %s\n"
			,beep,i,smb.last_error);
363
		bail(1); 
rswindell's avatar
rswindell committed
364
	}
rswindell's avatar
rswindell committed
365
	smb_freemsgmem(&msg);
366

deuce's avatar
deuce committed
367
368
	// MSVC can't do %zu for size_t until MSVC 2017 it seems...
	fprintf(statfp, "Message (%" PRIu64 " bytes) added to %s successfully\n", (uint64_t)strlen((char *)msgtxt), smb.file);
369
	FREE_AND_NULL(msgtxt);
370
371
372
373
374
}

/****************************************************************************/
/* Shows the message base header											*/
/****************************************************************************/
375
void showstatus(void)
376
377
378
{
	int i;

rswindell's avatar
rswindell committed
379
380
	i=smb_locksmbhdr(&smb);
	if(i) {
381
382
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
383
384
		return; 
	}
rswindell's avatar
rswindell committed
385
386
387
	i=smb_getstatus(&smb);
	smb_unlocksmbhdr(&smb);
	if(i) {
388
389
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
390
391
		return; 
	}
deuce's avatar
deuce committed
392
393
394
395
396
	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
397
398
399
400
401
402
403
404
405
406
		   "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
		   );
407
408
409
410
411
}

/****************************************************************************/
/* Configure message base header											*/
/****************************************************************************/
412
void config(void)
413
{
414
	char str[128];
415
416
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;
417
	uint32_t last_msg = 0;
418

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

/****************************************************************************/
/* Lists messages' to, from, and subject                                    */
/****************************************************************************/
void listmsgs(ulong start, ulong count)
{
	int i;
	ulong l=0;
	smbmsg_t msg;

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

/****************************************************************************/
/* Generates a 24 character ASCII string that represents the time_t pointer */
/* Used as a replacement for ctime()                                        */
/****************************************************************************/
525
char *my_timestr(time_t intime)
526
527
528
529
530
{
    static char str[256];
    char mer[3],hour;
    struct tm *gm;

531
	gm=localtime(&intime);
rswindell's avatar
rswindell committed
532
533
	if(gm==NULL) {
		strcpy(str,"Invalid Time");
rswindell's avatar
rswindell committed
534
535
		return(str); 
	}
rswindell's avatar
rswindell committed
536
537
538
539
540
	if(gm->tm_hour>=12) {
		if(gm->tm_hour==12)
			hour=12;
		else
			hour=gm->tm_hour-12;
rswindell's avatar
rswindell committed
541
542
		strcpy(mer,"pm"); 
	}
rswindell's avatar
rswindell committed
543
544
545
546
547
	else {
		if(gm->tm_hour==0)
			hour=12;
		else
			hour=gm->tm_hour;
rswindell's avatar
rswindell committed
548
549
		strcpy(mer,"am"); 
	}
rswindell's avatar
rswindell committed
550
551
552
553
	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);
554
555
}

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/****************************************************************************/
/****************************************************************************/
void dumpindex(ulong start, ulong count)
{
	ulong l=0;
	idxrec_t idx;

	if(!start)
		start=1;
	if(!count)
		count=~0;
	fseek(smb.sid_fp,(start-1L)*sizeof(idxrec_t),SEEK_SET);
	while(l<count) {
		if(!fread(&idx,1,sizeof(idx),smb.sid_fp))
			break;
571
		printf("%10"PRIu32"  ", idx.number);
572
		if(idx.attr&MSG_VOTE && !(idx.attr&MSG_POLL))
573
			printf("V  %04hX  %-10"PRIu32, idx.votes,idx.remsg);
574
		else
575
			printf("%c  %04hX  %04hX  %04X"
576
				,(idx.attr&MSG_POLL_VOTE_MASK) == MSG_POLL_CLOSURE ? 'C' : (idx.attr&MSG_POLL ? 'P':' ')
577
				,idx.from, idx.to, idx.subj);
578
		printf("  %04X  %06X  %s\n", idx.attr, idx.offset, my_timestr(idx.time));
579
580
581
582
		l++; 
	}
}

583
584
585
/****************************************************************************/
/* Displays message header information										*/
/****************************************************************************/
586
void viewmsgs(ulong start, ulong count, BOOL verbose)
587
{
588
	int i,j;
589
590
591
	ulong l=0;
	smbmsg_t msg;

rswindell's avatar
rswindell committed
592
593
	if(!start)
		start=1;
594
595
	if(!count)
		count=~0;
rswindell's avatar
rswindell committed
596
597
598
599
600
601
	fseek(smb.sid_fp,(start-1L)*sizeof(idxrec_t),SEEK_SET);
	while(l<count) {
		if(!fread(&msg.idx,1,sizeof(idxrec_t),smb.sid_fp))
			break;
		i=smb_lockmsghdr(&smb,&msg);
		if(i) {
602
603
			fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
604
605
			break; 
		}
rswindell's avatar
rswindell committed
606
607
608
		i=smb_getmsghdr(&smb,&msg);
		smb_unlockmsghdr(&smb,&msg);
		if(i) {
609
610
			fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
611
612
			break; 
		}
613

614
615
616
		printf("--------------------\n");
		printf("%-20.20s %ld\n"		,"index record",ftell(smb.sid_fp)/sizeof(idxrec_t));
		smb_dump_msghdr(stdout,&msg);
617
618
619
620
621
622
623
624
625
		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
626
627
		printf("\n");
		smb_freemsgmem(&msg);
rswindell's avatar
rswindell committed
628
629
		l++; 
	}
630
631
}

632
633
634
635
/****************************************************************************/
/****************************************************************************/
void dump_hashes(void)
{
rswindell's avatar
rswindell committed
636
	char	tmp[128];
637
638
639
640
	int		retval;
	hash_t	hash;

	if((retval=smb_open_hash(&smb))!=SMB_SUCCESS) {
641
642
		fprintf(errfp,"\n%s!smb_open_hash returned %d: %s\n"
			,beep, retval, smb.last_error);
643
644
645
646
647
648
		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
649
		printf("\n");
deuce's avatar
deuce committed
650
		printf("%-10s: %"PRIu32"\n","Number",	hash.number);
651
		printf("%-10s: %s\n",		"Source",	smb_hashsourcetype(hash.source));
deuce's avatar
deuce committed
652
		printf("%-10s: %"PRIu32"\n","Length",	hash.length);
653
		printf("%-10s: %s\n",		"Time",		my_timestr(hash.time));
654
		printf("%-10s: %02x\n",		"Flags",	hash.flags);
rswindell's avatar
rswindell committed
655
656
657
		if(hash.flags&SMB_HASH_CRC16)
			printf("%-10s: %04x\n",	"CRC-16",	hash.crc16);
		if(hash.flags&SMB_HASH_CRC32)
deuce's avatar
deuce committed
658
			printf("%-10s: %08"PRIx32"\n","CRC-32",	hash.crc32);
rswindell's avatar
rswindell committed
659
		if(hash.flags&SMB_HASH_MD5)
660
			printf("%-10s: %s\n",	"MD5",		MD5_hex((BYTE*)tmp,hash.md5));
661
	}
662
663

	smb_close_hash(&smb);
664
665
}

666
667
668
669
670
671
672
673
674
675
/****************************************************************************/
/* 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
676
	idxrec_t *idx;
677
678
679
680
681

	printf("Maintaining %s\r\n",smb.file);
	now=time(NULL);
	i=smb_locksmbhdr(&smb);
	if(i) {
682
683
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
684
685
		return; 
	}
686
687
688
	i=smb_getstatus(&smb);
	if(i) {
		smb_unlocksmbhdr(&smb);
689
690
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
691
692
		return; 
	}
693
	if((smb.status.max_msgs || smb.status.max_crcs) && smb_open_hash(&smb) == SMB_SUCCESS)
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
	{
		ulong max_hashes=0;

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

		if((smb.status.attr&(SMB_EMAIL|SMB_NOHASH)) == 0) {
			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) {
						CHSIZE_FP(smb.hash_fp,0);
						rewind(smb.hash_fp);
						fwrite(hashes, sizeof(hash_t), max_hashes * SMB_HASH_SOURCE_TYPES, smb.hash_fp);
					}
					free(hashes);
				}
			}
		}
		smb_close_hash(&smb);
	}
721
722
723
	if(!smb.status.total_msgs) {
		smb_unlocksmbhdr(&smb);
		printf("Empty\n");
rswindell's avatar
rswindell committed
724
725
		return; 
	}
726
	printf("Loading index...\n");
deuce's avatar
deuce committed
727
	if((idx=(idxrec_t *)malloc(sizeof(idxrec_t)*smb.status.total_msgs))
728
729
		==NULL) {
		smb_unlocksmbhdr(&smb);
deuce's avatar
deuce committed
730
		fprintf(errfp,"\n%s!Error allocating %" XP_PRIsize_t "u bytes of memory\n"
731
			,beep,sizeof(idxrec_t)*smb.status.total_msgs);
rswindell's avatar
rswindell committed
732
733
		return; 
	}
734
	fseek(smb.sid_fp,0L,SEEK_SET);
735
	l = fread(idx, sizeof(idxrec_t), smb.status.total_msgs, smb.sid_fp);
736

737
	printf("\nDone.\n\n");
738
739
	printf("Scanning for pre-flagged messages...\n");
	for(m=0;m<l;m++) {
740
//		printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
741
		if(idx[m].attr&MSG_DELETE)
rswindell's avatar
rswindell committed
742
743
			flagged++; 
	}
744
745
746
747
748
749
	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++) {
750
//			printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
751
752
753
754
755
756
			if(idx[m].attr&(MSG_PERMANENT|MSG_DELETE))
				continue;
			if((ulong)now>idx[m].time && (now-idx[m].time)/(24L*60L*60L)
				>smb.status.max_age) {
				f++;
				flagged++;
rswindell's avatar
rswindell committed
757
758
759
				idx[m].attr|=MSG_DELETE; 
			} 
		}  /* mark for deletion */
760
		printf("\r100%% (%lu flagged for deletion due to age)\n",f); 
rswindell's avatar
rswindell committed
761
	}
762
763
764

	printf("Scanning for read messages to be killed...\n");
	for(m=f=0;m<l;m++) {
765
//		printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
766
767
768
769
770
		if(idx[m].attr&(MSG_PERMANENT|MSG_DELETE))
			continue;
		if((idx[m].attr&(MSG_READ|MSG_KILLREAD))==(MSG_READ|MSG_KILLREAD)) {
			f++;
			flagged++;
rswindell's avatar
rswindell committed
771
772
773
			idx[m].attr|=MSG_DELETE; 
		} 
	}
774
	printf("\r100%% (%lu flagged for deletion due to read status)\n",f);
775

776
	if(smb.status.max_msgs && l-flagged>smb.status.max_msgs) {
777
778
779
780
781
782
		printf("Flagging excess messages for deletion...\n");
		for(m=n=0,f=flagged;l-flagged>smb.status.max_msgs && m<l;m++) {
			if(idx[m].attr&(MSG_PERMANENT|MSG_DELETE))
				continue;
			printf("%lu of %lu\r",++n,(l-f)-smb.status.max_msgs);
			flagged++;
rswindell's avatar
rswindell committed
783
784
785
786
			idx[m].attr|=MSG_DELETE; 
		}			/* mark for deletion */
		printf("\nDone.\n\n"); 
	}
787
788

	if(!flagged) {				/* No messages to delete */
deuce's avatar
deuce committed
789
		free(idx);
790
		smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
791
792
		return; 
	}
793
794
795
796
797
798
799
800

	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);
801
802
				fprintf(errfp,"\n%s!smb_open_da returned %d: %s\n"
					,beep,i,smb.last_error);
803
				bail(1); 
rswindell's avatar
rswindell committed
804
			}
805
806
807
			i=smb_open_ha(&smb);
			if(i) {
				smb_unlocksmbhdr(&smb);
808
809
				fprintf(errfp,"\n%s!smb_open_ha returned %d: %s\n"
					,beep,i,smb.last_error);
810
				bail(1); 
rswindell's avatar
rswindell committed
811
812
			} 
		}
813
814
815
816
817
818
819

		for(m=n=0;m<l;m++) {
			if(idx[m].attr&MSG_DELETE) {
				printf("%lu of %lu\r",++n,flagged);
				msg.idx=idx[m];
				msg.hdr.number=msg.idx.number;
				if((i=smb_getmsgidx(&smb,&msg))!=0) {
820
821
					fprintf(errfp,"\n%s!smb_getmsgidx returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
822
823
					continue; 
				}
824
825
				i=smb_lockmsghdr(&smb,&msg);
				if(i) {
826
827
					fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
828
829
					break; 
				}
830
831
				if((i=smb_getmsghdr(&smb,&msg))!=0) {
					smb_unlockmsghdr(&smb,&msg);
832
833
					fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
834
835
					break; 
				}
836
837
838
839
				msg.hdr.attr|=MSG_DELETE;			/* mark header as deleted */
				if((i=smb_putmsg(&smb,&msg))!=0) {
					smb_freemsgmem(&msg);
					smb_unlockmsghdr(&smb,&msg);
840
841
					fprintf(errfp,"\n%s!smb_putmsg returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
842
843
					break; 
				}
844
845
846
				smb_unlockmsghdr(&smb,&msg);
				if((i=smb_freemsg(&smb,&msg))!=0) {
					smb_freemsgmem(&msg);
847
848
					fprintf(errfp,"\n%s!smb_freemsg returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
849
850
851
852
853
					break; 
				}
				smb_freemsgmem(&msg); 
			} 
		}
854
855
		if(!(smb.status.attr&SMB_HYPERALLOC)) {
			smb_close_ha(&smb);
rswindell's avatar
rswindell committed
856
857
858
859
			smb_close_da(&smb); 
		}
		printf("\nDone.\n\n"); 
	}
860
861
862

	printf("Re-writing index...\n");
	rewind(smb.sid_fp);
863
	CHSIZE_FP(smb.sid_fp,0);
864
865
866
867
	for(m=n=0;m<l;m++) {
		if(idx[m].attr&MSG_DELETE)
			continue;
		printf("%lu of %lu\r",++n,l-flagged);
rswindell's avatar
rswindell committed
868
869
		fwrite(&idx[m],sizeof(idxrec_t),1,smb.sid_fp); 
	}
870
871
872
	printf("\nDone.\n\n");
	fflush(smb.sid_fp);

deuce's avatar
deuce committed
873
	free(idx);
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
	smb.status.total_msgs-=flagged;
	smb_putstatus(&smb);
	smb_unlocksmbhdr(&smb);
}


typedef struct {
	ulong old,new;
	} datoffset_t;

/****************************************************************************/
/* Removes all unused blocks from SDT and SHD files 						*/
/****************************************************************************/
void packmsgs(ulong packable)
{
889
	uchar	buf[SDT_BLOCK_LEN],ch;
890
	char	fname[MAX_PATH+1],tmpfname[MAX_PATH+1];
891
	int i,size;
892
	ulong l,m,n,datoffsets=0,length,total;
893
	FILE *tmp_sdt,*tmp_shd,*tmp_sid;
894
	BOOL		error=FALSE;
895
896
897
	smbhdr_t	hdr;
	smbmsg_t	msg;
	datoffset_t *datoffset=NULL;
898
	time_t		now;
899

rswindell's avatar
rswindell committed
900
901
902
	now=time(NULL);
	printf("Packing %s\n",smb.file);
	i=smb_locksmbhdr(&smb);
903
	if(i) {
904
905
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
906
907
		return; 
	}
rswindell's avatar
rswindell committed
908
	i=smb_getstatus(&smb);
909
910
	if(i) {
		smb_unlocksmbhdr(&smb);
911
912
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
913
914
		return; 
	}
rswindell's avatar
rswindell committed
915

916
	if(!(smb.status.attr&SMB_HYPERALLOC)) {
rswindell's avatar
rswindell committed
917
918
919
		i=smb_open_ha(&smb);
		if(i) {
			smb_unlocksmbhdr(&smb);
920
921
			fprintf(errfp,"\n%s!smb_open_ha returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
922
923
			return; 
		}
rswindell's avatar
rswindell committed
924
925
926
927
		i=smb_open_da(&smb);
		if(i) {
			smb_unlocksmbhdr(&smb);
			smb_close_ha(&smb);
928
929
			fprintf(errfp,"\n%s!smb_open_da returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
930
931
932
			return; 
		} 
	}
933

rswindell's avatar
rswindell committed
934
935
936
	if(!smb.status.total_msgs) {
		printf("Empty\n");
		rewind(smb.shd_fp);
937
		CHSIZE_FP(smb.shd_fp,smb.status.header_offset);
rswindell's avatar
rswindell committed
938
		rewind(smb.sdt_fp);
939
		CHSIZE_FP(smb.sdt_fp,0);
rswindell's avatar
rswindell committed
940
		rewind(smb.sid_fp);
941
		CHSIZE_FP(smb.sid_fp,0);
rswindell's avatar
rswindell committed
942
943
		if(!(smb.status.attr&SMB_HYPERALLOC)) {
			rewind(smb.sha_fp);
944
			CHSIZE_FP(smb.sha_fp,0);
rswindell's avatar
rswindell committed
945
			rewind(smb.sda_fp);
946
			CHSIZE_FP(smb.sda_fp,0);
rswindell's avatar
rswindell committed
947
			smb_close_ha(&smb);
rswindell's avatar
rswindell committed
948
949
			smb_close_da(&smb); 
		}
rswindell's avatar
rswindell committed
950
		smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
951
952
		return; 
	}
953
954


rswindell's avatar
rswindell committed
955
956
	if(!(smb.status.attr&SMB_HYPERALLOC) && !(mode&NOANALYSIS)) {
		printf("Analyzing data blocks...\n");
957

rswindell's avatar
rswindell committed
958
		length=filelength(fileno(smb.sda_fp));
959

rswindell's avatar
rswindell committed
960
961
		fseek(smb.sda_fp,0L,SEEK_SET);
		for(l=m=0;l<length;l+=2) {
rswindell's avatar
rswindell committed
962
			printf("\r%2lu%%  ",l ? (long)(100.0/((float)length/l)) : 0);
deuce's avatar
deuce committed
963
			/* TODO: Only works on LE (size mismatch) */
rswindell's avatar
rswindell committed
964
965
966
967
			i=0;
			if(!fread(&i,2,1,smb.sda_fp))
				break;
			if(!i)
rswindell's avatar
rswindell committed
968
969
				m++; 
		}
970

rswindell's avatar
rswindell committed
971
		printf("\rAnalyzing header blocks...\n");
972

rswindell's avatar
rswindell committed
973
		length=filelength(fileno(smb.sha_fp));
974

rswindell's avatar
rswindell committed
975
976
		fseek(smb.sha_fp,0L,SEEK_SET);
		for(l=n=0;l<length;l++) {
rswindell's avatar
rswindell committed
977
			printf("\r%2lu%%  ",l ? (long)(100.0/((float)length/l)) : 0);
rswindell's avatar
rswindell committed
978
979
980
981
			ch=0;
			if(!fread(&ch,1,1,smb.sha_fp))
				break;
			if(!ch)
rswindell's avatar
rswindell committed
982
983
				n++; 
		}
rswindell's avatar
rswindell committed
984
985
986
987
988
989

		if(!m && !n) {
			printf("\rAlready compressed.\n\n");
			smb_close_ha(&smb);
			smb_close_da(&smb);
			smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
990
991
			return; 
		}
992

rswindell's avatar
rswindell committed
993
		if(packable && (m*SDT_BLOCK_LEN)+(n*SHD_BLOCK_LEN)<packable*1024L) {
994
			printf("\r%lu less than %lu compressible bytes.\n\n",(m*SDT_BLOCK_LEN)+(n*SHD_BLOCK_LEN), packable*1024L);
rswindell's avatar
rswindell committed
995
996
997
			smb_close_ha(&smb);
			smb_close_da(&smb);
			smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
998
999
			return; 
		}
rswindell's avatar
rswindell committed
1000
1001
1002

		printf("\rCompressing %lu data blocks (%lu bytes)\n"
				 "        and %lu header blocks (%lu bytes)\n"
rswindell's avatar
rswindell committed
1003
1004
				  ,m,m*SDT_BLOCK_LEN,n,n*SHD_BLOCK_LEN); 
	}
rswindell's avatar
rswindell committed
1005
1006
1007

	if(!(smb.status.attr&SMB_HYPERALLOC)) {
		rewind(smb.sha_fp);
1008
		CHSIZE_FP(smb.sha_fp,0);		/* Reset both allocation tables */
rswindell's avatar
rswindell committed
1009
		rewind(smb.sda_fp);
1010
		CHSIZE_FP(smb.sda_fp,0); 
rswindell's avatar
rswindell committed
1011
	}
rswindell's avatar
rswindell committed
1012
1013
1014
1015
1016
1017
1018

	if(smb.status.attr&SMB_HYPERALLOC && !(mode&NOANALYSIS)) {
		printf("Analyzing %s\n",smb.file);

		length=filelength(fileno(smb.shd_fp));
		m=n=0;
		for(l=smb.status.header_offset;l<length;l+=size) {
rswindell's avatar
rswindell committed
1019
			printf("\r%2lu%%  ",(long)(100.0/((float)length/l)));