smbutil.c 48.4 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
68
#include "sbbs.h"
#include "conwrap.h"
69

70
/* gets is dangerous */
71
#define gets(str)  fgets((str), sizeof(str), stdin)
72

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

75
76
77
78
/********************/
/* Global variables */
/********************/

79
80
81
82
83
84
85
86
87
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;
88
char*		beep="";
rswindell's avatar
rswindell committed
89
long		msgtxtmode = GETMSGTXT_ALL|GETMSGTXT_PLAIN;
90
91
92
93
94
95

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

char *usage=
96
"usage: smbutil [-opts] cmd <filespec.shd>\n"
97
98
99
100
"\n"
"cmd:\n"
"       l[n] = list msgs starting at number n\n"
"       r[n] = read msgs starting at number n\n"
101
"       x[n] = dump msg index at number n\n"
102
"       v[n] = view msg headers starting at number n\n"
103
104
105
"       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
106
"       h    = dump hash file\n"
107
108
"       s    = display msg base status\n"
"       c    = change msg base status\n"
rswindell's avatar
rswindell committed
109
"       R    = re-initialize/repair SMB/status headers\n"
110
"       d    = delete all msgs\n"
111
112
"       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
113
114
"       L    = lock a msg base for exclusive-access/backup\n"
"       U    = unlock a msg base\n"
115
116
117
118
119
"\n"
"            [n] may represent 1-based message index offset, or\n"
"            [#n] actual message number, or\n"
"            [-n] message age (in days)\n"
"\n"
120
"opts:\n"
rswindell's avatar
rswindell committed
121
122
123
124
125
126
127
128
129
"      -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"
rswindell's avatar
rswindell committed
130
"      -r    = display raw message body text (not MIME-decoded)\n"
rswindell's avatar
rswindell committed
131
132
133
134
135
136
137
138
"      -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"
139
#ifdef __unix__
140
"      -U[n] = set umask to specified value (use leading 0 for octal, e.g. 022)\n"
141
#endif
rswindell's avatar
rswindell committed
142
"      -#    = set number of messages to view/list (e.g. -1)\n"
143
144
;

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

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

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

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
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>"
193
194
		,(ulong)msg->hdr.when_imported.time
		,(ulong)smb->status.last_msg + 1
195
196
197
198
199
		,getfname(smb->file)
		,host);
	return msgid;
}

200
201
202
/****************************************************************************/
/* Adds a new message to the message base									*/
/****************************************************************************/
203
204
void postmsg(char type, char* to, char* to_number, char* to_address, 
			 char* from, char* from_number, char* subject, FILE* fp)
205
{
206
207
	char		str[128];
	char		buf[1024];
208
209
	uchar*		msgtxt=NULL;
	uchar*		newtxt;
210
211
	long		msgtxtlen;
	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 or 1:2/3): ");
285
286
			gets(str);
		} else
287
			SAFECOPY(str,to_address);
rswindell's avatar
rswindell committed
288
289
		truncsp(str);
		if(*str) {
290
291
			if((i=smb_hfield_netaddr(&msg,RECIPIENTNETADDR,str,NULL))!=SMB_SUCCESS) {
				fprintf(errfp,"\n%s!smb_hfield_netaddr(0x%02X) returned %d: %s\n"
292
					,beep,RECIPIENTNETADDR,i,smb.last_error);
293
				bail(1); 
294
			}
295
296
		} 
	}
rswindell's avatar
rswindell committed
297

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

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

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

353
354
	smb_hfield_str(&msg, RFC822MSGID, gen_msgid(&smb, &msg, str, sizeof(str)-1));

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

358
	if((i=smb_addmsg(&smb,&msg,smb.status.attr&SMB_HYPERALLOC
359
		,dupechk_hashes,xlat,msgtxt,NULL))!=SMB_SUCCESS) {
360
361
		fprintf(errfp,"\n%s!smb_addmsg returned %d: %s\n"
			,beep,i,smb.last_error);
362
		bail(1); 
rswindell's avatar
rswindell committed
363
	}
rswindell's avatar
rswindell committed
364
	smb_freemsgmem(&msg);
365

deuce's avatar
deuce committed
366
367
	// 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);
368
	FREE_AND_NULL(msgtxt);
369
370
371
372
373
}

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

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

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

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

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

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

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

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

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

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

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

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

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

	smb_close_hash(&smb);
663
664
}

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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