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
#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
123
124
125
126
"\n"
"            [n] may represent 1-based message index offset, or\n"
"            [#n] actual message number, or\n"
"            [-n] message age (in days)\n"
"\n"
127
"opts:\n"
rswindell's avatar
rswindell committed
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"      -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"
145
#ifdef __unix__
146
"      -U[n] = set umask to specified value (use leading 0 for octal, e.g. 022)\n"
147
#endif
rswindell's avatar
rswindell committed
148
"      -#    = set number of messages to view/list (e.g. -1)\n"
149
150
;

151
152
153
154
155
156
157
158
159
160
161
162
163
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);
}

164
/*****************************************************************************/
165
/* Expands Unix LF to CRLF													 */
166
/*****************************************************************************/
167
ulong lf_expand(uchar* inbuf, uchar* outbuf)
168
169
{
	ulong	i,j;
rswindell's avatar
rswindell committed
170

171
	for(i=j=0;inbuf[i];i++) {
172
173
174
175
		if(inbuf[i]=='\n' && (!i || inbuf[i-1]!='\r'))
			outbuf[j++]='\r';
		outbuf[j++]=inbuf[i];
	}
176
	outbuf[j]=0;
177
178
	return(j);
}
rswindell's avatar
rswindell committed
179

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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;
}

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

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

236
	if(msgtxt!=NULL) {
237
238
239

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

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

245
246
247
248
249
		/* Expand LFs to CRLFs */
		msgtxtlen=lf_expand(msgtxt, newtxt);
		free(msgtxt);
		msgtxt=newtxt;
	}
rswindell's avatar
rswindell committed
250
251

	memset(&msg,0,sizeof(smbmsg_t));
252
	msg.hdr.when_written.time=(uint32_t)time(NULL);
rswindell's avatar
rswindell committed
253
254
255
	msg.hdr.when_written.zone=tzone;
	msg.hdr.when_imported=msg.hdr.when_written;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	smb_close_hash(&smb);
669
670
}

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

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

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

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

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

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

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

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

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

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 */