smbutil.c 46.8 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
"      -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"
138
139
140
#ifdef __unix__
"      -U[n] = set umask to specified value\n"
#endif
rswindell's avatar
rswindell committed
141
"      -#    = set number of messages to view/list (e.g. -1)\n"
142
143
;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

359
360
	smb_hfield_str(&msg, RFC822MSGID, gen_msgid(&smb, &msg, str, sizeof(str)-1));

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

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

372
	FREE_AND_NULL(msgtxt);
373
374
375
376
377
}

/****************************************************************************/
/* Shows the message base header											*/
/****************************************************************************/
378
void showstatus(void)
379
380
381
{
	int i;

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

/****************************************************************************/
/* Configure message base header											*/
/****************************************************************************/
415
void config(void)
416
417
418
419
{
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;

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

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

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

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

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

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

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

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

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

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

	smb_close_hash(&smb);
658
659
}

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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