smbutil.c 47.7 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
213
	long		msgtxtlen;
	ushort		net;
	int 		i;
214
	ushort		agent=AGENT_SMBUTIL;
215
	smbmsg_t	msg;
216
	long		dupechk_hashes=SMB_HASH_SOURCE_DUPE;
217

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

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

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

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

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

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

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

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

284
	if(smb.status.attr&SMB_EMAIL && (type=='N' || to_address!=NULL)) {
285
		if(to_address==NULL) {
286
			printf("To Address (e.g. user@host): ");
287
288
			gets(str);
		} else
289
			SAFECOPY(str,to_address);
rswindell's avatar
rswindell committed
290
291
		truncsp(str);
		if(*str) {
292
			net=smb_netaddr_type(str);
293
			if((i=smb_hfield(&msg,RECIPIENTNETTYPE,sizeof(net),&net))!=SMB_SUCCESS) {
294
295
				fprintf(errfp,"\n%s!smb_hfield(0x%02X) returned %d: %s\n"
					,beep,RECIPIENTNETTYPE,i,smb.last_error);
296
				bail(1); 
rswindell's avatar
rswindell committed
297
			}
298
			if((i=smb_hfield_str(&msg,RECIPIENTNETADDR,str))!=SMB_SUCCESS) {
299
300
				fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
					,beep,RECIPIENTNETADDR,i,smb.last_error);
301
				bail(1); 
302
303
304
			} 
		} 
	}
rswindell's avatar
rswindell committed
305

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

336
337
338
339
	if(subject==NULL) {
		printf("Subject: ");
		gets(str);
	} else
340
		SAFECOPY(str,subject);
rswindell's avatar
rswindell committed
341
	truncsp(str);
342
	if((i=smb_hfield_str(&msg,SUBJECT,str))!=SMB_SUCCESS) {
343
344
		fprintf(errfp,"\n%s!smb_hfield_str(0x%02X) returned %d: %s\n"
			,beep,SUBJECT,i,smb.last_error);
345
		bail(1); 
346
	}
rswindell's avatar
rswindell committed
347

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

361
362
	smb_hfield_str(&msg, RFC822MSGID, gen_msgid(&smb, &msg, str, sizeof(str)-1));

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

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

374
	fprintf(statfp, "Message (%u bytes) added to %s successfully\n", strlen((char *)msgtxt), 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
441
442
	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
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;
rswindell's avatar
rswindell committed
473
474
475
476
477
478
479
480
481
482
483
	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);
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
498
}

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

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

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

562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
/****************************************************************************/
/****************************************************************************/
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;
577
		printf("%10"PRIu32"  ", idx.number);
578
		if(idx.attr&MSG_VOTE && !(idx.attr&MSG_POLL))
579
			printf("V  %04hX  %-10"PRIu32, idx.votes,idx.remsg);
580
		else
581
			printf("%c  %04hX  %04hX  %04X"
582
				,(idx.attr&MSG_POLL_VOTE_MASK) == MSG_POLL_CLOSURE ? 'C' : (idx.attr&MSG_POLL ? 'P':' ')
583
				,idx.from, idx.to, idx.subj);
584
		printf("  %04X  %06X  %s\n", idx.attr, idx.offset, my_timestr(idx.time));
585
586
587
588
		l++; 
	}
}

589
590
591
/****************************************************************************/
/* Displays message header information										*/
/****************************************************************************/
592
void viewmsgs(ulong start, ulong count, BOOL verbose)
593
{
594
	int i,j;
595
596
597
	ulong l=0;
	smbmsg_t msg;

rswindell's avatar
rswindell committed
598
599
	if(!start)
		start=1;
600
601
	if(!count)
		count=~0;
rswindell's avatar
rswindell committed
602
603
604
605
606
607
	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) {
608
609
			fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
610
611
			break; 
		}
rswindell's avatar
rswindell committed
612
613
614
		i=smb_getmsghdr(&smb,&msg);
		smb_unlockmsghdr(&smb,&msg);
		if(i) {
615
616
			fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
617
618
			break; 
		}
619

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

638
639
640
641
/****************************************************************************/
/****************************************************************************/
void dump_hashes(void)
{
rswindell's avatar
rswindell committed
642
	char	tmp[128];
643
644
645
646
	int		retval;
	hash_t	hash;

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

	smb_close_hash(&smb);
670
671
}

672
673
674
675
676
677
678
679
680
681
/****************************************************************************/
/* 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
682
	idxrec_t *idx;
683
684
685
686
687

	printf("Maintaining %s\r\n",smb.file);
	now=time(NULL);
	i=smb_locksmbhdr(&smb);
	if(i) {
688
689
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
690
691
		return; 
	}
692
693
694
	i=smb_getstatus(&smb);
	if(i) {
		smb_unlocksmbhdr(&smb);
695
696
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
697
698
		return; 
	}
699
	if((smb.status.max_msgs || smb.status.max_crcs) && smb_open_hash(&smb) == SMB_SUCCESS)
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
	{
		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);
	}
727
728
729
	if(!smb.status.total_msgs) {
		smb_unlocksmbhdr(&smb);
		printf("Empty\n");
rswindell's avatar
rswindell committed
730
731
		return; 
	}
732
	printf("Loading index...\n");
deuce's avatar
deuce committed
733
	if((idx=(idxrec_t *)malloc(sizeof(idxrec_t)*smb.status.total_msgs))
734
735
		==NULL) {
		smb_unlocksmbhdr(&smb);
deuce's avatar
deuce committed
736
		fprintf(errfp,"\n%s!Error allocating %" XP_PRIsize_t "u bytes of memory\n"
737
			,beep,sizeof(idxrec_t)*smb.status.total_msgs);
rswindell's avatar
rswindell committed
738
739
		return; 
	}
740
	fseek(smb.sid_fp,0L,SEEK_SET);
741
742
	l = fread(idx, sizeof(idxrec_t), smb.status.total_msgs, smb.sid_fp);
	l /= sizeof(idxrec_t);
743

744
	printf("\nDone.\n\n");
745
746
	printf("Scanning for pre-flagged messages...\n");
	for(m=0;m<l;m++) {
747
//		printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
748
		if(idx[m].attr&MSG_DELETE)
rswindell's avatar
rswindell committed
749
750
			flagged++; 
	}
751
752
753
754
755
756
	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++) {
757
//			printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
758
759
760
761
762
763
			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
764
765
766
				idx[m].attr|=MSG_DELETE; 
			} 
		}  /* mark for deletion */
767
		printf("\r100%% (%lu flagged for deletion due to age)\n",f); 
rswindell's avatar
rswindell committed
768
	}
769
770
771

	printf("Scanning for read messages to be killed...\n");
	for(m=f=0;m<l;m++) {
772
//		printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
773
774
775
776
777
		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
778
779
780
			idx[m].attr|=MSG_DELETE; 
		} 
	}
781
	printf("\r100%% (%lu flagged for deletion due to read status)\n",f);
782

783
	if(smb.status.max_msgs && l-flagged>smb.status.max_msgs) {
784
785
786
787
788
789
		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
790
791
792
793
			idx[m].attr|=MSG_DELETE; 
		}			/* mark for deletion */
		printf("\nDone.\n\n"); 
	}
794
795

	if(!flagged) {				/* No messages to delete */
deuce's avatar
deuce committed
796
		free(idx);
797
		smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
798
799
		return; 
	}
800
801
802
803
804
805
806
807

	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);
808
809
				fprintf(errfp,"\n%s!smb_open_da returned %d: %s\n"
					,beep,i,smb.last_error);
810
				bail(1); 
rswindell's avatar
rswindell committed
811
			}
812
813
814
			i=smb_open_ha(&smb);
			if(i) {
				smb_unlocksmbhdr(&smb);
815
816
				fprintf(errfp,"\n%s!smb_open_ha returned %d: %s\n"
					,beep,i,smb.last_error);
817
				bail(1); 
rswindell's avatar
rswindell committed
818
819
			} 
		}
820
821
822
823
824
825
826

		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) {
827
828
					fprintf(errfp,"\n%s!smb_getmsgidx returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
829
830
					continue; 
				}
831
832
				i=smb_lockmsghdr(&smb,&msg);
				if(i) {
833
834
					fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
835
836
					break; 
				}
837
838
				if((i=smb_getmsghdr(&smb,&msg))!=0) {
					smb_unlockmsghdr(&smb,&msg);
839
840
					fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
841
842
					break; 
				}
843
844
845
846
				msg.hdr.attr|=MSG_DELETE;			/* mark header as deleted */
				if((i=smb_putmsg(&smb,&msg))!=0) {
					smb_freemsgmem(&msg);
					smb_unlockmsghdr(&smb,&msg);
847
848
					fprintf(errfp,"\n%s!smb_putmsg returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
849
850
					break; 
				}
851
852
853
				smb_unlockmsghdr(&smb,&msg);
				if((i=smb_freemsg(&smb,&msg))!=0) {
					smb_freemsgmem(&msg);
854
855
					fprintf(errfp,"\n%s!smb_freemsg returned %d: %s\n"
						,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
856
857
858
859
860
					break; 
				}
				smb_freemsgmem(&msg); 
			} 
		}
861
862
		if(!(smb.status.attr&SMB_HYPERALLOC)) {
			smb_close_ha(&smb);
rswindell's avatar
rswindell committed
863
864
865
866
			smb_close_da(&smb); 
		}
		printf("\nDone.\n\n"); 
	}
867
868
869

	printf("Re-writing index...\n");
	rewind(smb.sid_fp);
870
	CHSIZE_FP(smb.sid_fp,0);
871
872
873
874
	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
875
876
		fwrite(&idx[m],sizeof(idxrec_t),1,smb.sid_fp); 
	}
877
878
879
	printf("\nDone.\n\n");
	fflush(smb.sid_fp);

deuce's avatar
deuce committed
880
	free(idx);
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
	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)
{
896
	uchar	buf[SDT_BLOCK_LEN],ch;
897
	char	fname[MAX_PATH+1],tmpfname[MAX_PATH+1];
898
	int i,size;
899
	ulong l,m,n,datoffsets=0,length,total;
900
	FILE *tmp_sdt,*tmp_shd,*tmp_sid;
901
	BOOL		error=FALSE;
902
903
904
	smbhdr_t	hdr;
	smbmsg_t	msg;
	datoffset_t *datoffset=NULL;
905
	time_t		now;
906

rswindell's avatar
rswindell committed
907
908
909
	now=time(NULL);
	printf("Packing %s\n",smb.file);
	i=smb_locksmbhdr(&smb);
910
	if(i) {
911
912
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
913
914
		return; 
	}
rswindell's avatar
rswindell committed
915
	i=smb_getstatus(&smb);
916
917
	if(i) {
		smb_unlocksmbhdr(&smb);
918
919
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
920
921
		return; 
	}
rswindell's avatar
rswindell committed
922

923
	if(!(smb.status.attr&SMB_HYPERALLOC)) {
rswindell's avatar
rswindell committed
924
925
926
		i=smb_open_ha(&smb);
		if(i) {
			smb_unlocksmbhdr(&smb);
927
928
			fprintf(errfp,"\n%s!smb_open_ha returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
929
930
			return; 
		}
rswindell's avatar
rswindell committed
931
932
933
934
		i=smb_open_da(&smb);
		if(i) {
			smb_unlocksmbhdr(&smb);
			smb_close_ha(&smb);
935
936
			fprintf(errfp,"\n%s!smb_open_da returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
937
938
939
			return; 
		} 
	}
940

rswindell's avatar
rswindell committed
941
942
943
	if(!smb.status.total_msgs) {
		printf("Empty\n");
		rewind(smb.shd_fp);
944
		CHSIZE_FP(smb.shd_fp,smb.status.header_offset);
rswindell's avatar
rswindell committed
945
		rewind(smb.sdt_fp);
946
		CHSIZE_FP(smb.sdt_fp,0);
rswindell's avatar
rswindell committed
947
		rewind(smb.sid_fp);
948
		CHSIZE_FP(smb.sid_fp,0);
rswindell's avatar
rswindell committed
949
950
		if(!(smb.status.attr&SMB_HYPERALLOC)) {
			rewind(smb.sha_fp);
951
			CHSIZE_FP(smb.sha_fp,0);
rswindell's avatar
rswindell committed
952
			rewind(smb.sda_fp);
953
			CHSIZE_FP(smb.sda_fp,0);
rswindell's avatar
rswindell committed
954
			smb_close_ha(&smb);
rswindell's avatar
rswindell committed
955
956
			smb_close_da(&smb); 
		}
rswindell's avatar
rswindell committed
957
		smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
958
959
		return; 
	}
960
961


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

rswindell's avatar
rswindell committed
965
		length=filelength(fileno(smb.sda_fp));
966

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

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

rswindell's avatar
rswindell committed
980
		length=filelength(fileno(smb.sha_fp));
981

rswindell's avatar
rswindell committed
982
983
		fseek(smb.sha_fp,0L,SEEK_SET);
		for(l=n=0;l<length;l++) {
rswindell's avatar
rswindell committed
984
			printf("\r%2lu%%  ",l ? (long)(100.0/((float)length/l)) : 0);
rswindell's avatar
rswindell committed
985
986
987
988
			ch=0;
			if(!fread(&ch,1,1,smb.sha_fp))
				break;
			if(!ch)
rswindell's avatar
rswindell committed
989
990
				n++; 
		}
rswindell's avatar
rswindell committed
991
992
993
994
995
996

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

rswindell's avatar
rswindell committed
1000
		if(packable && (m*SDT_BLOCK_LEN)+(n*SHD_BLOCK_LEN)<packable*1024L) {
1001
			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
1002
1003
1004
			smb_close_ha(&smb);
			smb_close_da(&smb);
			smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
1005
1006
			return; 
		}
rswindell's avatar
rswindell committed
1007
1008
1009

		printf("\rCompressing %lu data blocks (%lu bytes)\n"
				 "        and %lu header blocks (%lu bytes)\n"
rswindell's avatar
rswindell committed
1010
1011
				  ,m,m*SDT_BLOCK_LEN,n,n*SHD_BLOCK_LEN); 
	}
rswindell's avatar
rswindell committed
1012
1013
1014

	if(!(smb.status.attr&SMB_HYPERALLOC)) {
		rewind(smb.sha_fp);
1015
		CHSIZE_FP(smb.sha_fp,0);		/* Reset both allocation tables */
rswindell's avatar
rswindell committed
1016
		rewind(smb.sda_fp);
1017
		CHSIZE_FP(smb.sda_fp,0);