smbutil.c 45.3 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.	*
 ****************************************************************************/

37
#define SMBUTIL_VER "2.33"
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"
116
"       d    = delete all msgs\n"
117
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"
"opts:\n"
120
"       c[m] = create message base if it doesn't exist (m=max msgs)\n"
121
"       a    = always pack msg base (disable compression analysis)\n"
122
"       i    = ignore dupes (do not store CRCs or search for duplicate hashes)\n"
123
"       d    = use default values (no prompt) for to, from, and subject\n"
124
"       l    = LZH-compress message text\n"
125
126
127
"       o    = print errors on stdout (instead of stderr)\n"
"       p    = wait for keypress (pause) on exit\n"
"       !    = wait for keypress (pause) on error\n"
128
"       b    = beep on error\n"
129
130
131
132
133
134
"       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"
135
"       z[n] = set time zone (n=min +/- from UT or 'EST','EDT','CST',etc)\n"
136
"       #    = set number of messages to view/list (e.g. -1)\n"
137
138
;

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

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

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

168
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
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;
}

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

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

225
	if(msgtxt!=NULL) {
226
227
228

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

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

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

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

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

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

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

367
	FREE_AND_NULL(msgtxt);
368
369
370
371
372
}

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

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

/****************************************************************************/
/* Configure message base header											*/
/****************************************************************************/
410
void config(void)
411
412
413
414
{
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;

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

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

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

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

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

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

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

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

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

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

	smb_close_hash(&smb);
653
654
}

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

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

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

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

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

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

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

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

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

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

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

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

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


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

rswindell's avatar
rswindell committed
952
		length=filelength(fileno(smb.sda_fp));
953

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

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

rswindell's avatar
rswindell committed
967
		length=filelength(fileno(smb.sha_fp));
968

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

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

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

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

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

	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
1013
			printf("\r%2lu%%  ",(long)(100.0/((float)length/l)));