smbutil.c 46.6 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
120
"       m    = maintain msg base - delete old msgs and msgs over max\n"
"       p[k] = pack msg base (k specifies minimum packable Kbytes)\n"
"opts:\n"
rswindell's avatar
rswindell committed
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"      -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"
"      -#    = set number of messages to view/list (e.g. -1)\n"
139
140
;

141
142
143
144
145
146
147
148
149
150
151
152
153
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);
}

154
/*****************************************************************************/
155
/* Expands Unix LF to CRLF													 */
156
/*****************************************************************************/
157
ulong lf_expand(uchar* inbuf, uchar* outbuf)
158
159
{
	ulong	i,j;
rswindell's avatar
rswindell committed
160

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

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
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;
}

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

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

227
	if(msgtxt!=NULL) {
228
229
230

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

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

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

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

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

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

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

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

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

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

356
357
	smb_hfield_str(&msg, RFC822MSGID, gen_msgid(&smb, &msg, str, sizeof(str)-1));

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

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

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
415
416
{
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;

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

/****************************************************************************/
/* 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
484
485
	if(!start)
		start=1;
486
487
	if(!count)
		count=~0;
rswindell's avatar
rswindell committed
488
489
490
491
492
493
	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) {
494
495
			fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
496
497
			break; 
		}
rswindell's avatar
rswindell committed
498
499
500
		i=smb_getmsghdr(&smb,&msg);
		smb_unlockmsghdr(&smb,&msg);
		if(i) {
501
502
			fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
503
504
			break; 
		}
deuce's avatar
deuce committed
505
		printf("%4"PRIu32" %-25.25s %-25.25s %s\n"
rswindell's avatar
rswindell committed
506
507
			,msg.hdr.number,msg.from,msg.to,msg.subj);
		smb_freemsgmem(&msg);
rswindell's avatar
rswindell committed
508
509
		l++; 
	}
510
511
512
513
514
515
}

/****************************************************************************/
/* Generates a 24 character ASCII string that represents the time_t pointer */
/* Used as a replacement for ctime()                                        */
/****************************************************************************/
516
char *my_timestr(time_t intime)
517
518
519
520
521
{
    static char str[256];
    char mer[3],hour;
    struct tm *gm;

522
	gm=localtime(&intime);
rswindell's avatar
rswindell committed
523
524
	if(gm==NULL) {
		strcpy(str,"Invalid Time");
rswindell's avatar
rswindell committed
525
526
		return(str); 
	}
rswindell's avatar
rswindell committed
527
528
529
530
531
	if(gm->tm_hour>=12) {
		if(gm->tm_hour==12)
			hour=12;
		else
			hour=gm->tm_hour-12;
rswindell's avatar
rswindell committed
532
533
		strcpy(mer,"pm"); 
	}
rswindell's avatar
rswindell committed
534
535
536
537
538
	else {
		if(gm->tm_hour==0)
			hour=12;
		else
			hour=gm->tm_hour;
rswindell's avatar
rswindell committed
539
540
		strcpy(mer,"am"); 
	}
rswindell's avatar
rswindell committed
541
542
543
544
	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);
545
546
}

547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
/****************************************************************************/
/****************************************************************************/
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;
562
		printf("%10"PRIu32"  ", idx.number);
563
		if(idx.attr&MSG_VOTE && !(idx.attr&MSG_POLL))
564
			printf("V  %04hX  %-10"PRIu32, idx.votes,idx.remsg);
565
		else
566
			printf("%c  %04hX  %04hX  %04X"
567
568
				,(idx.attr&MSG_POLL_VOTE_MASK) == MSG_POLL_CLOSURE ? 'C' : (idx.attr&MSG_POLL ? 'P':'M')
				,idx.from, idx.to, idx.subj);
569
		printf("  %04X  %06X  %s\n", idx.attr, idx.offset, my_timestr(idx.time));
570
571
572
573
		l++; 
	}
}

574
575
576
/****************************************************************************/
/* Displays message header information										*/
/****************************************************************************/
577
void viewmsgs(ulong start, ulong count, BOOL verbose)
578
{
579
	int i,j;
580
581
582
	ulong l=0;
	smbmsg_t msg;

rswindell's avatar
rswindell committed
583
584
	if(!start)
		start=1;
585
586
	if(!count)
		count=~0;
rswindell's avatar
rswindell committed
587
588
589
590
591
592
	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) {
593
594
			fprintf(errfp,"\n%s!smb_lockmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
595
596
			break; 
		}
rswindell's avatar
rswindell committed
597
598
599
		i=smb_getmsghdr(&smb,&msg);
		smb_unlockmsghdr(&smb,&msg);
		if(i) {
600
601
			fprintf(errfp,"\n%s!smb_getmsghdr returned %d: %s\n"
				,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
602
603
			break; 
		}
604

605
606
607
		printf("--------------------\n");
		printf("%-20.20s %ld\n"		,"index record",ftell(smb.sid_fp)/sizeof(idxrec_t));
		smb_dump_msghdr(stdout,&msg);
608
609
610
611
612
613
614
615
616
		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
617
618
		printf("\n");
		smb_freemsgmem(&msg);
rswindell's avatar
rswindell committed
619
620
		l++; 
	}
621
622
}

623
624
625
626
/****************************************************************************/
/****************************************************************************/
void dump_hashes(void)
{
rswindell's avatar
rswindell committed
627
	char	tmp[128];
628
629
630
631
	int		retval;
	hash_t	hash;

	if((retval=smb_open_hash(&smb))!=SMB_SUCCESS) {
632
633
		fprintf(errfp,"\n%s!smb_open_hash returned %d: %s\n"
			,beep, retval, smb.last_error);
634
635
636
637
638
639
		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
640
		printf("\n");
deuce's avatar
deuce committed
641
		printf("%-10s: %"PRIu32"\n","Number",	hash.number);
642
		printf("%-10s: %s\n",		"Source",	smb_hashsourcetype(hash.source));
deuce's avatar
deuce committed
643
		printf("%-10s: %"PRIu32"\n","Length",	hash.length);
644
		printf("%-10s: %s\n",		"Time",		my_timestr(hash.time));
645
		printf("%-10s: %02x\n",		"Flags",	hash.flags);
rswindell's avatar
rswindell committed
646
647
648
		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
649
			printf("%-10s: %08"PRIx32"\n","CRC-32",	hash.crc32);
rswindell's avatar
rswindell committed
650
		if(hash.flags&SMB_HASH_MD5)
651
			printf("%-10s: %s\n",	"MD5",		MD5_hex((BYTE*)tmp,hash.md5));
652
	}
653
654

	smb_close_hash(&smb);
655
656
}

657
658
659
660
661
662
663
664
665
666
/****************************************************************************/
/* 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
667
	idxrec_t *idx;
668
669
670
671
672

	printf("Maintaining %s\r\n",smb.file);
	now=time(NULL);
	i=smb_locksmbhdr(&smb);
	if(i) {
673
674
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
675
676
		return; 
	}
677
678
679
	i=smb_getstatus(&smb);
	if(i) {
		smb_unlocksmbhdr(&smb);
680
681
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
682
683
		return; 
	}
684
	if((smb.status.max_msgs || smb.status.max_crcs) && smb_open_hash(&smb) == SMB_SUCCESS)
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
	{
		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);
	}
712
713
714
	if(!smb.status.total_msgs) {
		smb_unlocksmbhdr(&smb);
		printf("Empty\n");
rswindell's avatar
rswindell committed
715
716
		return; 
	}
717
	printf("Loading index...\n");
deuce's avatar
deuce committed
718
	if((idx=(idxrec_t *)malloc(sizeof(idxrec_t)*smb.status.total_msgs))
719
720
		==NULL) {
		smb_unlocksmbhdr(&smb);
deuce's avatar
deuce committed
721
		fprintf(errfp,"\n%s!Error allocating %" XP_PRIsize_t "u bytes of memory\n"
722
			,beep,sizeof(idxrec_t)*smb.status.total_msgs);
rswindell's avatar
rswindell committed
723
724
		return; 
	}
725
726
	fseek(smb.sid_fp,0L,SEEK_SET);
	for(l=0;l<smb.status.total_msgs;l++) {
deuce's avatar
deuce committed
727
		printf("%lu of %"PRIu32"\r"
728
729
			,l+1,smb.status.total_msgs);
		if(!fread(&idx[l],1,sizeof(idxrec_t),smb.sid_fp))
rswindell's avatar
rswindell committed
730
731
			break; 
	}
732
733
734
735
	printf("\nDone.\n\n");

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

	printf("Scanning for read messages to be killed...\n");
	for(m=f=0;m<l;m++) {
rswindell's avatar
rswindell committed
761
		printf("\r%2lu%%",m ? (long)(100.0/((float)l/m)) : 0);
762
763
764
765
766
		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
767
768
769
			idx[m].attr|=MSG_DELETE; 
		} 
	}
770
	printf("\r100%% (%lu flagged for deletion due to read status)\n",f);
771

772
	if(smb.status.max_msgs && l-flagged>smb.status.max_msgs) {
773
774
775
776
777
778
		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
779
780
781
782
			idx[m].attr|=MSG_DELETE; 
		}			/* mark for deletion */
		printf("\nDone.\n\n"); 
	}
783
784

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

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

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

	printf("Re-writing index...\n");
	rewind(smb.sid_fp);
859
	CHSIZE_FP(smb.sid_fp,0);
860
861
862
863
	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
864
865
		fwrite(&idx[m],sizeof(idxrec_t),1,smb.sid_fp); 
	}
866
867
868
	printf("\nDone.\n\n");
	fflush(smb.sid_fp);

deuce's avatar
deuce committed
869
	free(idx);
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
	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)
{
885
	uchar	buf[SDT_BLOCK_LEN],ch;
886
	char	fname[MAX_PATH+1],tmpfname[MAX_PATH+1];
887
	int i,size;
888
	ulong l,m,n,datoffsets=0,length,total;
889
	FILE *tmp_sdt,*tmp_shd,*tmp_sid;
890
	BOOL		error=FALSE;
891
892
893
	smbhdr_t	hdr;
	smbmsg_t	msg;
	datoffset_t *datoffset=NULL;
894
	time_t		now;
895

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

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

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


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

rswindell's avatar
rswindell committed
954
		length=filelength(fileno(smb.sda_fp));
955

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

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

rswindell's avatar
rswindell committed
969
		length=filelength(fileno(smb.sha_fp));
970

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

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

rswindell's avatar
rswindell committed
989
		if(packable && (m*SDT_BLOCK_LEN)+(n*SHD_BLOCK_LEN)<packable*1024L) {
990
			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
991
992
993
			smb_close_ha(&smb);
			smb_close_da(&smb);
			smb_unlocksmbhdr(&smb);
rswindell's avatar
rswindell committed
994
995
			return; 
		}
rswindell's avatar
rswindell committed
996
997
998

		printf("\rCompressing %lu data blocks (%lu bytes)\n"
				 "        and %lu header blocks (%lu bytes)\n"
rswindell's avatar
rswindell committed
999
1000
				  ,m,m*SDT_BLOCK_LEN,n,n*SHD_BLOCK_LEN); 
	}
rswindell's avatar
rswindell committed
1001
1002
1003

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

	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
1015
			printf("\r%2lu%%  ",(long)(100.0/((float)length/l)));
rswindell's avatar
rswindell committed
1016
1017
1018
1019
			msg.idx.offset=l;
			if((i=smb_lockmsghdr(&smb,&msg))!=0) {
				printf("\n(%06lX) smb_lockmsghdr returned %d\n",l,i);
				size=SHD_BLOCK_LEN;
rswindell's avatar
rswindell committed
1020
1021
				continue; 
			}
rswindell's avatar
rswindell committed
1022
1023
1024
1025
			if((i=smb_getmsghdr(&smb,&msg))!=0) {
				smb_unlockmsghdr(&smb,&msg);
				m++;
				size=SHD_BLOCK_LEN;
rswindell's avatar
rswindell committed
1026
1027
				continue; 
			}
rswindell's avatar
rswindell committed
1028
1029
1030