smbutil.c 47.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
"       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
213
	long		msgtxtlen;
	ushort		net;
	int 		i;
214
	ushort		agent=AGENT_SMBUTIL;
215
	smbmsg_t	msg;
216
	long		dupechk_hashes=SMB_HASH_SOURCE_DUPE;
217

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

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

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

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

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

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

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

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

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

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

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

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

361
362
	smb_hfield_str(&msg, RFC822MSGID, gen_msgid(&smb, &msg, str, sizeof(str)-1));

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

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

deuce's avatar
deuce committed
374
375
	// 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);
376
	FREE_AND_NULL(msgtxt);
377
378
379
380
381
}

/****************************************************************************/
/* Shows the message base header											*/
/****************************************************************************/
382
void showstatus(void)
383
384
385
{
	int i;

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

/****************************************************************************/
/* Configure message base header											*/
/****************************************************************************/
419
void config(void)
420
{
421
	char str[128];
422
423
	char max_msgs[128],max_crcs[128],max_age[128],header_offset[128],attr[128];
	int i;
424
	uint32_t last_msg = 0;
425

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

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

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

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

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

590
591
592
/****************************************************************************/
/* Displays message header information										*/
/****************************************************************************/
593
void viewmsgs(ulong start, ulong count, BOOL verbose)
594
{
595
	int i,j;
596
597
598
	ulong l=0;
	smbmsg_t msg;

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

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

639
640
641
642
/****************************************************************************/
/****************************************************************************/
void dump_hashes(void)
{
rswindell's avatar
rswindell committed
643
	char	tmp[128];
644
645
646
647
	int		retval;
	hash_t	hash;

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

	smb_close_hash(&smb);
671
672
}

673
674
675
676
677
678
679
680
681
682
/****************************************************************************/
/* 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
683
	idxrec_t *idx;
684
685
686
687
688

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

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

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

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

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

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

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

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

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

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

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

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


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

rswindell's avatar
rswindell committed
965
		length=filelength(fileno(smb.sda_fp));
966

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

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

rswindell's avatar
rswindell committed
980
		length=filelength(fileno(smb.sha_fp));
981

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

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

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

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

	if(!(smb.status.attr&SMB_HYPERALLOC)) {
		rewind(smb.sha_fp);
1015
		CHSIZE_FP(smb.sha_fp,0);		/* Reset both allocation tables */
rswindell's avatar
rswindell committed
1016
		rewind(smb.sda_fp);
1017
		CHSIZE_FP(smb.sda_fp,0);