smbutil.c 47 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
	char str[128];
418
419
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;
420
	uint32_t last_msg = 0;
421

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

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

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

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

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

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

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

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

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

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

	smb_close_hash(&smb);
667
668
}

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

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

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

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

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

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

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

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

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

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

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

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

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


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

rswindell's avatar
rswindell committed
962
		length=filelength(fileno(smb.sda_fp));
963

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

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

rswindell's avatar
rswindell committed
977
		length=filelength(fileno(smb.sha_fp));
978

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

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

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

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

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

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

		length=filelength(fileno(smb.shd_fp));
		m=n=0;
		for(