smbutil.c 48 KB
Newer Older
1
2
3
/* Synchronet message base (SMB) utility */

/* $Id$ */
4
// vi: tabstop=4
5
6
7
8
9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

rswindell's avatar
rswindell committed
37
#define SMBUTIL_VER "2.34"
38
39
char	revision[16];
char	compiler[32];
40

41
42
43
44
45
const char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
            ,"Jul","Aug","Sep","Oct","Nov","Dec"};


46
#define NOANALYSIS		(1L<<0)
47
#define NOCRC			(1L<<1)
48
49
50
51
52
53
54

#ifdef __WATCOMC__
	#define ffblk find_t
    #define findfirst(x,y,z) _dos_findfirst(x,z,y)
	#define findnext(x) _dos_findnext(x)
#endif

55
56
57
58
59
60
#if defined(_WIN32)
	#include <ctype.h>	/* isdigit() */
	#include <conio.h>	/* getch() */
#endif

/* ANSI */
61
#include <stdio.h>
rswindell's avatar
rswindell committed
62
63
64
65
#include <time.h>		/* time */
#include <errno.h>		/* errno */
#include <string.h>		/* strrchr */
#include <ctype.h>		/* toupper */
66

67
#include "genwrap.h"	/* stricmp */
rswindell's avatar
rswindell committed
68
#include "dirwrap.h"	/* fexist */
rswindell's avatar
rswindell committed
69
#include "conwrap.h"	/* getch */
70
#include "filewrap.h"
71
#include "smblib.h"
72
#include "gen_defs.h"	/* MAX_PATH */
73
74
75
76
77

#ifdef __WATCOMC__
	#include <dos.h>
#endif

78
/* gets is dangerous */
79
#define gets(str)  fgets((str), sizeof(str), stdin)
80

81
82
#define CHSIZE_FP(fp,size)	if(chsize(fileno(fp),size)) printf("chsize failed!\n");

83
84
85
86
/********************/
/* Global variables */
/********************/

87
88
89
90
91
92
93
94
95
smb_t		smb;
ulong		mode=0L;
ushort		tzone=0;
ushort		xlat=XLAT_NONE;
FILE*		nulfp;
FILE*		errfp;
FILE*		statfp;
BOOL		pause_on_exit=FALSE;
BOOL		pause_on_error=FALSE;
96
char*		beep="";
97
98
99
100
101
102

/************************/
/* Program usage/syntax */
/************************/

char *usage=
103
"usage: smbutil [-opts] cmd <filespec.shd>\n"
104
105
106
107
"\n"
"cmd:\n"
"       l[n] = list msgs starting at number n\n"
"       r[n] = read msgs starting at number n\n"
108
"       x[n] = dump msg index at number n\n"
109
"       v[n] = view msg headers starting at number n\n"
110
111
112
"       i[f] = import msg from text file f (or use stdin)\n"
"       e[f] = import e-mail from text file f (or use stdin)\n"
"       n[f] = import netmail from text file f (or use stdin)\n"
rswindell's avatar
rswindell committed
113
"       h    = dump hash file\n"
114
115
"       s    = display msg base status\n"
"       c    = change msg base status\n"
rswindell's avatar
rswindell committed
116
"       R    = re-initialize/repair SMB/status headers\n"
117
"       d    = delete all msgs\n"
118
119
"       m    = maintain msg base - delete old msgs and msgs over max\n"
"       p[k] = pack msg base (k specifies minimum packable Kbytes)\n"
rswindell's avatar
rswindell committed
120
121
"       L    = lock a msg base for exclusive-access/backup\n"
"       U    = unlock a msg base\n"
122
"opts:\n"
rswindell's avatar
rswindell committed
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"      -c[m] = create message base if it doesn't exist (m=max msgs)\n"
"      -a    = always pack msg base (disable compression analysis)\n"
"      -i    = ignore dupes (do not store CRCs or search for duplicate hashes)\n"
"      -d    = use default values (no prompt) for to, from, and subject\n"
"      -l    = LZH-compress message text\n"
"      -o    = print errors on stdout (instead of stderr)\n"
"      -p    = wait for keypress (pause) on exit\n"
"      -!    = wait for keypress (pause) on error\n"
"      -b    = beep on error\n"
"      -C    = continue after some (normally fatal) error conditions\n"
"      -t<s> = set 'to' user name for imported message\n"
"      -n<s> = set 'to' netmail address for imported message\n"
"      -u<s> = set 'to' user number for imported message\n"
"      -f<s> = set 'from' user name for imported message\n"
"      -e<s> = set 'from' user number for imported message\n"
"      -s<s> = set 'subject' for imported message\n"
"      -z[n] = set time zone (n=min +/- from UT or 'EST','EDT','CST',etc)\n"
140
141
142
#ifdef __unix__
"      -U[n] = set umask to specified value\n"
#endif
rswindell's avatar
rswindell committed
143
"      -#    = set number of messages to view/list (e.g. -1)\n"
144
145
;

146
147
148
149
150
151
152
153
154
155
156
157
158
void bail(int code)
{

	if(pause_on_exit || (code && pause_on_error)) {
		fprintf(statfp,"\nHit enter to continue...");
		getchar();
	}

	if(code)
		fprintf(statfp,"\nReturning error code: %d\n",code);
	exit(code);
}

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

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

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
char* gen_msgid(smb_t* smb, smbmsg_t* msg, char* msgid, size_t maxlen)
{
	char* host = getenv(
#if defined(_WIN32)
		"COMPUTERNAME"
#else
		"HOSTNAME"
#endif
	);
	if(host == NULL)
		host = getenv(
#if defined(_WIN32)
		"USERNAME"
#else
		"USER"
#endif
	);
	safe_snprintf(msgid, maxlen
		,"<%08lX.%lu.%s@%s>"
		,msg->hdr.when_imported.time
		,smb->status.last_msg + 1
		,getfname(smb->file)
		,host);
	return msgid;
}

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

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

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

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

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

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

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

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

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

283
	if(smb.status.attr&SMB_EMAIL && (type=='N' || to_address!=NULL)) {
284
		if(to_address==NULL) {
285
			printf("To Address (e.g. user@host or 1:2/3): ");
286
287
			gets(str);
		} else
288
			SAFECOPY(str,to_address);
rswindell's avatar
rswindell committed
289
290
		truncsp(str);
		if(*str) {
291
292
			if((i=smb_hfield_netaddr(&msg,RECIPIENTNETADDR,str,NULL))!=SMB_SUCCESS) {
				fprintf(errfp,"\n%s!smb_hfield_netaddr(0x%02X) returned %d: %s\n"
293
					,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

deuce's avatar
deuce committed
367
368
	// MSVC can't do %zu for size_t until MSVC 2017 it seems...
	fprintf(statfp, "Message (%" PRIu64 " bytes) added to %s successfully\n", (uint64_t)strlen((char *)msgtxt), smb.file);
369
	FREE_AND_NULL(msgtxt);
370
371
372
373
374
}

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

rswindell's avatar
rswindell committed
379
380
	i=smb_locksmbhdr(&smb);
	if(i) {
381
382
		fprintf(errfp,"\n%s!smb_locksmbhdr returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
383
384
		return; 
	}
rswindell's avatar
rswindell committed
385
386
387
	i=smb_getstatus(&smb);
	smb_unlocksmbhdr(&smb);
	if(i) {
388
389
		fprintf(errfp,"\n%s!smb_getstatus returned %d: %s\n"
			,beep,i,smb.last_error);
rswindell's avatar
rswindell committed
390
391
		return; 
	}
deuce's avatar
deuce committed
392
393
394
395
396
	printf("last_msg        =%"PRIu32"\n"
		   "total_msgs      =%"PRIu32"\n"
		   "header_offset   =%"PRIu32"\n"
		   "max_crcs        =%"PRIu32"\n"
		   "max_msgs        =%"PRIu32"\n"
rswindell's avatar
rswindell committed
397
398
399
400
401
402
403
404
405
406
		   "max_age         =%u\n"
		   "attr            =%04Xh\n"
		   ,smb.status.last_msg
		   ,smb.status.total_msgs
		   ,smb.status.header_offset
		   ,smb.status.max_crcs
		   ,smb.status.max_msgs
		   ,smb.status.max_age
		   ,smb.status.attr
		   );
407
408
409
410
411
}

/****************************************************************************/
/* Configure message base header											*/
/****************************************************************************/
412
void config(void)
413
{
414
	char str[128];
415
416
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;
417
	uint32_t last_msg = 0;
418

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

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

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

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

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

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

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

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

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

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

	smb_close_hash(&smb);
664
665
}

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

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

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

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

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

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

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

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

	printf("Re-writing index...\n");
	rewind(smb.sid_fp);
	for(m=n=0;m<l;m++) {
		if(idx[m].attr&MSG_DELETE)
			continue;
867
868
		n++;
		printf("%lu of %lu\r", n, l-flagged);
rswindell's avatar
rswindell committed
869
870
		fwrite(&idx[m],sizeof(idxrec_t),1,smb.sid_fp); 
	}
871
	fflush(smb.sid_fp);
872
873
	CHSIZE_FP(smb.sid_fp, n * sizeof(idxrec_t));
	printf("\nDone.\n\n");
874

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

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

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

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


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

rswindell's avatar
rswindell committed
960
		length=filelength(fileno(smb.sda_fp));
961

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

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

rswindell's avatar
rswindell committed
975
		length=filelength(fileno(smb.sha_fp));
976

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

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

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

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

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