Newer
Older
/* Synchronet message/file base (SMB) validity checker */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* 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 *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <stdio.h>
#include <stdlib.h> /* exit */
#include <string.h> /* strrchr */
#include <time.h> /* ctime */
#include <ctype.h> /* toupper */
#include "git_branch.h"
#include "git_hash.h"
#include "conwrap.h" /* getch */
#include "dirwrap.h" /* fexist */
#include "filewrap.h" /* filelength */
#include "smblib.h"
/****************************************************************************/
/* Returns in 'string' a character representation of the number in l with */
/* commas. */
/****************************************************************************/
char *ultoac(ulong l, char *string)
{
char str[256];
int i, j, k;
SAFEPRINTF(str, "%lu", l);
i = strlen(str) - 1;
j = i / 3 + 1 + i;
string[j--] = 0;
for (k = 1; i > -1; k++) {
string[j--] = str[i--];
if (j > 0 && !(k % 3))
string[j--] = ',';
}
/****************************************************************************/
/* Returns an ASCII string for FidoNet address 'addr' */
/****************************************************************************/
char *faddrtoa(fidoaddr_t addr)
{
static char str[25];
sprintf(str, "%hu:%hu/%hu", addr.zone, addr.net, addr.node);
if (addr.point) {
sprintf(point, ".%u", addr.point);
strcat(str, point);
for (i = j = 0; str[i] && j < sizeof(tmp) - 1; i++) {
if (str[i] == CTRL_A && str[i + 1] != 0)
else if ((uchar)str[i] >= ' ')
tmp[j++] = str[i];
if (i != j) {
tmp[j] = 0;
strcpy(str, tmp);
BOOL contains_ctrl_chars(char* str)
{
uchar* p;
return FALSE;
for (p = (uchar *)str; *p; p++)
if (*p < ' ')
return TRUE;
return FALSE;
}
void print_hash(hash_t* hash)
{
char str[128];
printf("\t%-20s = %lu\n", "hash.number", (ulong)hash->number);
printf("\t%-20s = 0x%08lX\n", "hash.time", (ulong)hash->time);
printf("\t%-20s = %lu\n", "hash.length", (ulong)hash->length);
printf("\t%-20s = 0x%02X\n", "hash.source", (unsigned)hash->source);
printf("\t%-20s = 0x%02X\n", "hash.flags", (unsigned)hash->flags);
if (hash->flags & SMB_HASH_CRC16)
printf("\t%-20s = 0x%04hX\n", "hash.crc16", hash->data.crc16);
if (hash->flags & SMB_HASH_CRC32)
printf("\t%-20s = 0x%08X\n", "hash.crc32", hash->data.crc32);
if (hash->flags & SMB_HASH_MD5)
printf("\t%-20s = %s\n", "hash.md5", MD5_hex(str, hash->data.md5));
if (hash->flags & SMB_HASH_SHA1)
printf("\t%-20s = %s\n", "hash.sha1", SHA1_hex(str, hash->data.sha1));
char *usage = "\nusage: chksmb [-opts] <path/to/base[.shd]> [...]\n"
"\n"
" opts:\n"
" b - beep on error\n"
" s - stop after errored message/file base\n"
" p - pause after errored messsage/file base\n"
" h - don't check hash file\n"
" a - don't check allocation files\n"
" t - don't check translation strings\n"
" i - don't check message IDs\n"
" S - don't check subject CRCs\n"
" N - don't check to/from name CRCs\n"
" e - display extended info on corrupted msgs\n";
int main(int argc, char **argv)
{
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
char str[128], *p, *beep = "";
char from[26];
char* body;
char* tail;
int h, i, j, x, y, lzh, errors, errlast;
BOOL stop_on_error = FALSE, pause_on_error = FALSE, chkxlat = TRUE, chkalloc = TRUE, chkhash = TRUE
, lzhmsg, extinfo = FALSE, msgerr;
BOOL chk_msgids = TRUE;
BOOL chk_subjcrc = TRUE;
BOOL chk_namecrc = TRUE;
uint16_t xlat;
uint32_t m;
ulong l, n, size, total = 0, orphan, deleted, headers
, *offset, *number, xlaterr
, delidx
, delhdrblocks, deldatblocks, hdrerr, lockerr, hdrnumerr, hdrlenerr
, getbodyerr, gettailerr
, hasherr, badhash
, acthdrblocks, actdatblocks
, dfieldlength, dfieldoffset
, dupenum, dupenumhdr, dupeoff, attr, actalloc, types
, datactalloc, misnumbered, timeerr, idxofferr, idxerr
, subjcrc, fromcrc, tocrc, fname_err
, intransit, unvalidated
, zeronum, idxzeronum, idxnumerr, packable = 0L, totallzhsaved = 0L
, totalmsgs = 0, totallzhmsgs = 0, totaldelmsgs = 0, totalmsgbytes = 0L
, lzhblocks, lzhsaved
, ctrl_chars;
ulong hdr_overlap;
off_t shd_length;
ulong oldest = 0;
ulong largest = 0;
ulong largest_msgnum = 0;
ulong msgids = 0;
smb_t smb;
idxrec_t idx;
idxrec_t* idxrec = NULL;
fileidxrec_t* fidxrec = NULL;
smbmsg_t msg;
hash_t** hashes;
time_t now = time(NULL);
fprintf(stderr, "\nCHKSMB v3.20-%s %s/%s SMBLIB %s - Check Synchronet Message/File Base\n"
, PLATFORM_DESC, GIT_BRANCH, GIT_HASH, smb_lib_ver());
if (argc < 2) {
printf("%s", usage);
errlast = errors = 0;
for (x = 1; x < argc; x++) {
if (stop_on_error && errors)
if (pause_on_error && errlast != errors) {
fprintf(stderr, "%s\nHit any key to continue...", beep);
if (!getch())
errlast = errors;
if (argv[x][0] == '-'
) {
for (y = 1; argv[x][y]; y++)
switch (argv[x][y]) {
break;
pause_on_error = TRUE;
case 'S':
chk_subjcrc = FALSE;
break;
case 'N':
chk_namecrc = FALSE;
break;
chk_msgids = FALSE;
break;
ZERO_VAR(smb);
SAFECOPY(smb.file, argv[x]);
p = getfext(smb.file);
if (p != NULL && stricmp(p, ".shd") == 0)
*p = 0;
SAFEPRINTF(str, "%s.shd", smb.file);
if (!fexist(str)) {
printf("\n%s doesn't exist.\n", smb.file);
continue;
}
fprintf(stderr, "\nChecking %s Headers\n\n", smb.file);
smb.retry_time = 30;
if ((i = smb_open(&smb)) != 0) {
printf("smb_open returned %d: %s\n", i, smb.last_error);
errors++;
continue;
}
const char* base_type = (smb.status.attr & SMB_FILE_DIRECTORY) ? "File" : "Message";
/* File size sanity checks here: */
shd_length = filelength(fileno(smb.shd_fp));
if (shd_length < sizeof(smbhdr_t)) {
printf("Empty\n");
smb_close(&smb);
continue;
}
if (shd_length < (off_t)smb.status.header_offset) {
printf("!Status header corruption (header offset: %lu)\n", (ulong)smb.status.header_offset);
smb_close(&smb);
continue;
}
size_t idxreclen = smb_idxreclen(&smb);
off_t sid_length = filelength(fileno(smb.sid_fp));
if (sid_length != smb.status.total_msgs * idxreclen) {
printf("!Size of index file (%ld) is incorrect (expected: %ld)\n", (long)sid_length, (long)(smb.status.total_msgs * idxreclen));
smb_close(&smb);
errors++;
continue;
}
FREE_AND_NULL(idxrec);
if ((idxrec = calloc(smb.status.total_msgs, idxreclen)) == NULL) {
printf("!Error allocating %lu index record\n", (ulong)smb.status.total_msgs);
smb_close(&smb);
errors++;
continue;
}
if (fread(idxrec, idxreclen, smb.status.total_msgs, smb.sid_fp) != smb.status.total_msgs) {
printf("!Error reading %lu index records\n", (ulong)smb.status.total_msgs);
smb_close(&smb);
errors++;
continue;
}
fidxrec = (fileidxrec_t*)idxrec;
off_t shd_hdrs = shd_length - smb.status.header_offset;
if (shd_hdrs && (shd_hdrs % SHD_BLOCK_LEN) != 0)
printf("!Size of msg header records in SHD file incorrect: %lu\n", (ulong)shd_hdrs);
if ((i = smb_locksmbhdr(&smb)) != 0) {
smb_close(&smb);
printf("smb_locksmbhdr returned %d: %s\n", i, smb.last_error);
errors++;
continue;
if (((shd_hdrs / SHD_BLOCK_LEN) * sizeof(*number)) != 0) {
if ((number = malloc((size_t)(((shd_hdrs / SHD_BLOCK_LEN) + 2)) * sizeof(*number)))
== NULL) {
printf("Error allocating %lu bytes of memory\n"
, (ulong)((shd_hdrs / SHD_BLOCK_LEN) * sizeof(*number)));
else
number = NULL;
off_t sdt_length = filelength(fileno(smb.sdt_fp));
if (sdt_length && (sdt_length % SDT_BLOCK_LEN) != 0)
printf("!Size of SDT file (%lu) not evenly divisible by block length (%u)\n"
, (ulong)sdt_length, SDT_BLOCK_LEN);
if (chkalloc && !(smb.status.attr & SMB_HYPERALLOC)) {
if ((i = smb_open_ha(&smb)) != 0) {
printf("smb_open_ha returned %d: %s\n", i, smb.last_error);
}
if (filelength(fileno(smb.shd_fp)) != smb.status.header_offset
+ (filelength(fileno(smb.sha_fp)) * SHD_BLOCK_LEN))
printf("!Size of SHA file (%lu) does not match SHD file (%lu)\n"
, (ulong)filelength(fileno(smb.sha_fp))
, (ulong)filelength(fileno(smb.shd_fp)));
if ((i = smb_open_da(&smb)) != 0) {
printf("smb_open_da returned %d: %s\n", i, smb.last_error);
}
if ((filelength(fileno(smb.sda_fp))) / sizeof(uint16_t) != filelength(fileno(smb.sdt_fp)) / SDT_BLOCK_LEN)
printf("!Size of SDA file (%lu) does not match SDT file (%lu)\n"
, (ulong)filelength(fileno(smb.sda_fp))
, (ulong)filelength(fileno(smb.sdt_fp)));
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
headers = deleted = orphan = dupenumhdr = attr = zeronum = timeerr = lockerr = hdrerr = 0;
types = 0;
subjcrc = fromcrc = tocrc = 0;
fname_err = 0L;
hdrnumerr = hdrlenerr = 0;
actalloc = datactalloc = deldatblocks = delhdrblocks = xlaterr = 0;
lzhblocks = lzhsaved = acthdrblocks = actdatblocks = 0;
getbodyerr = gettailerr = 0;
hasherr = badhash = 0;
unvalidated = 0;
intransit = 0;
acthdrblocks = actdatblocks = 0;
dfieldlength = dfieldoffset = 0;
msgids = 0;
ctrl_chars = 0;
hdr_overlap = 0;
oldest = 0;
largest = 0;
largest_msgnum = 0;
for (l = smb.status.header_offset; l < (uint32_t)shd_length; l += size) {
size = SHD_BLOCK_LEN;
fprintf(stderr, "\r%2lu%% ", (long)(100.0 / ((float)shd_length / l)));
fflush(stderr);
memset(&msg, 0, sizeof(msg));
msg.idx.offset = l;
msgerr = FALSE;
if ((i = smb_lockmsghdr(&smb, &msg)) != 0) {
printf("\n(%06lX) smb_lockmsghdr returned %d: %s\n", l, i, smb.last_error);
lockerr++;
headers++;
continue;
}
if ((i = smb_getmsghdr(&smb, &msg)) != 0) {
smb_unlockmsghdr(&smb, &msg);
if (chkalloc && !(smb.status.attr & SMB_HYPERALLOC)) {
fseek(smb.sha_fp
, (l - smb.status.header_offset) / SHD_BLOCK_LEN, SEEK_SET);
j = fgetc(smb.sha_fp);
if (j) { /* Allocated block or at EOF */
printf("%s\n(%06lX) smb_getmsghdr returned %d: %s\n", beep, l, i, smb.last_error);
hdrerr++;
}
else
delhdrblocks++;
else {
/* printf("%s\n(%06lX) smb_getmsghdr returned %d\n",beep,l,i); */
smb_unlockmsghdr(&smb, &msg);
size = smb_hdrblocks(smb_getmsghdrlen(&msg)) * SHD_BLOCK_LEN;
if (msg.hdr.type == SMB_MSG_TYPE_FILE)
SAFECOPY(from, msg.subj);
SAFECOPY(from, msg.from);
truncsp(from);
strip_ctrl(from);
fprintf(stderr, "#%-5" PRIu32 " (%06lX) %-25.25s ", msg.hdr.number, l, from);
for (n = 0; n < smb.status.total_msgs; n++) {
idxrec_t* idx;
if (idxreclen == sizeof(*fidxrec))
idx = &fidxrec[n].idx;
else
idx = &idxrec[n];
if (idx->number == msg.hdr.number)
continue;
if (idx->offset > l && idx->offset < l + (smb_hdrblocks(msg.hdr.length) * SHD_BLOCK_LEN)) {
fprintf(stderr, "%s%s header overlap\n", base_type, beep);
msgerr = TRUE;
if (extinfo)
printf("ERR: Header for %s #%lu overlaps with #%lu\n"
, base_type, (ulong)idxrec[n].number, (ulong)msg.hdr.number);
hdr_overlap++;
break;
}
if (msg.hdr.length != smb_getmsghdrlen(&msg)) {
fprintf(stderr, "%sHeader length mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header length (%hu) does not match calculated length (%u)\n"
, msg.hdr.length, smb_getmsghdrlen(&msg));
hdrlenerr++;
}
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
if (msg.hdr.attr & MSG_DELETE)
body = tail = NULL;
else {
if (contains_ctrl_chars(msg.to)
|| (msg.to_net.type != NET_FIDO && contains_ctrl_chars(msg.to_net.addr))
|| contains_ctrl_chars(msg.from)
|| (msg.from_net.type != NET_FIDO && contains_ctrl_chars(msg.from_net.addr))
|| contains_ctrl_chars(msg.subj)) {
fprintf(stderr, "%sHeader field contains control characters\n", beep);
msgerr = TRUE;
ctrl_chars++;
}
long age = (long)(now - msg.hdr.when_imported.time);
if (age > (long)oldest)
oldest = age;
/* Test reading of the message text (body and tails) */
if ((body = smb_getmsgtxt(&smb, &msg, GETMSGTXT_BODY_ONLY)) == NULL) {
fprintf(stderr, "%sGet text body failure\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: %s\n", smb.last_error);
getbodyerr++;
}
if ((tail = smb_getmsgtxt(&smb, &msg, GETMSGTXT_TAIL_ONLY)) == NULL) {
fprintf(stderr, "%sGet text tail failure\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: %s\n", smb.last_error);
gettailerr++;
}
if (msg.hdr.type != smb_msg_type(msg.hdr.attr)) {
fprintf(stderr, "%s%s type mismatch (%d, expected %d)\n"
, beep, base_type, msg.hdr.type, smb_msg_type(msg.hdr.attr));
msgerr = TRUE;
types++;
}
else if (msg.hdr.type == SMB_MSG_TYPE_NORMAL && chk_msgids && msg.from_net.type == NET_NONE && msg.id == NULL) {
fprintf(stderr, "%sNo Message-ID\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header missing Message-ID\n");
msgids++;
}
if (!(smb.status.attr & (SMB_EMAIL | SMB_NOHASH | SMB_FILE_DIRECTORY)) && chkhash) {
/* Look-up the message hashes */
hashes = smb_msghashes(&msg, (uchar*)body, SMB_HASH_SOURCE_DUPE);
if (hashes != NULL
&& hashes[0] != NULL
&& (i = smb_findhash(&smb, hashes, NULL, SMB_HASH_SOURCE_DUPE, /* mark */ TRUE ))
!= SMB_SUCCESS) {
for (h = 0; hashes[h] != NULL; h++) {
if (hashes[h]->flags & SMB_HASH_MARKED)
continue;
fprintf(stderr, "%sFailed to find %s hash\n"
, beep, smb_hashsourcetype(hashes[h]->source));
msgerr = TRUE;
if (extinfo) {
printf("MSGERR: %d searching for %s: %s\n"
, i
, smb_hashsourcetype(hashes[h]->source)
, smb_hashsource(&msg, hashes[h]->source));
printf("\n");
printf("%-10s: %s\n", "Source", smb_hashsourcetype(hashes[h]->source));
printf("%-10s: %" PRIu32 "\n", "Length", hashes[h]->length);
printf("%-10s: %x\n", "Flags", hashes[h]->flags);
if (hashes[h]->flags & SMB_HASH_CRC16)
printf("%-10s: %04x\n", "CRC-16", hashes[h]->data.crc16);
if (hashes[h]->flags & SMB_HASH_CRC32)
printf("%-10s: %08" PRIx32 "\n", "CRC-32", hashes[h]->data.crc32);
if (hashes[h]->flags & SMB_HASH_MD5)
printf("%-10s: %s\n", "MD5", MD5_hex(str, hashes[h]->data.md5));
if (hashes[h]->flags & SMB_HASH_SHA1)
printf("%-10s: %s\n", "SHA-1", SHA1_hex(str, hashes[h]->data.sha1));
smb_close_hash(&smb); /* just incase */
FREE_LIST(hashes, i);
}
FREE_AND_NULL(body);
FREE_AND_NULL(tail);
lzhmsg = FALSE;
ulong data_length = smb_getmsgdatlen(&msg);
if (data_length > largest) {
largest = data_length;
largest_msgnum = msg.hdr.number;
if (msg.hdr.attr & MSG_DELETE) {
deleted++;
if (number)
number[headers] = 0;
if (smb.status.attr & SMB_HYPERALLOC)
deldatblocks += smb_datblocks(data_length);
actdatblocks += smb_datblocks(data_length);
if (msg.hdr.number > smb.status.last_msg) {
fprintf(stderr, "%sOut-Of-Range %s number\n", beep, base_type);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header number (%" PRIu32 ") greater than last (%" PRIu32 ")\n"
, msg.hdr.number, smb.status.last_msg);
hdrnumerr++;
if (smb_getmsgidx(&smb, &msg) || msg.idx.offset != l) {
fprintf(stderr, "%sNot found in index\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header number (%" PRIu32 ") not found in index\n"
, msg.hdr.number);
orphan++;
else {
if (msg.hdr.attr != msg.idx.attr) {
fprintf(stderr, "%sAttributes mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header attributes (%04X) do not match index "
"attributes (%04X)\n"
, msg.hdr.attr, msg.idx.attr);
attr++;
if (msg.hdr.when_imported.time != msg.idx.time) {
fprintf(stderr, "%sImport date/time mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header import date/time does not match "
"index import date/time\n");
timeerr++;
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
if (chk_subjcrc && (msg.hdr.type == SMB_MSG_TYPE_NORMAL || msg.hdr.type == SMB_MSG_TYPE_POLL)
&& msg.idx.subj != smb_subject_crc(msg.subj)) {
fprintf(stderr, "%sSubject CRC mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Subject (%04X) does not match index "
"CRC (%04X)\n"
, smb_subject_crc(msg.subj), msg.idx.subj);
subjcrc++;
}
if (smb.status.attr & SMB_EMAIL
&& (msg.from_ext != NULL || msg.idx.from)
&& (msg.from_ext == NULL || msg.idx.from != atoi(msg.from_ext))) {
fprintf(stderr, "%sFrom extension mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: From extension (%s) does not match index "
"(%u)\n"
, msg.from_ext, msg.idx.from);
fromcrc++;
}
if (chk_namecrc && !(smb.status.attr & SMB_EMAIL)
&& (msg.hdr.type == SMB_MSG_TYPE_NORMAL || msg.hdr.type == SMB_MSG_TYPE_POLL)
&& msg.idx.from != smb_name_crc(msg.from)) {
fprintf(stderr, "%sFrom CRC mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: From (%04X) does not match index "
"CRC (%04X)\n"
, smb_name_crc(msg.from), msg.idx.from);
fromcrc++;
}
if (smb.status.attr & SMB_EMAIL
&& (msg.to_ext != NULL || msg.idx.to)
&& (msg.to_ext == NULL || msg.idx.to != atoi(msg.to_ext))) {
fprintf(stderr, "%sTo extension mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: To extension (%s) does not match index "
"(%u)\n"
, msg.to_ext, msg.idx.to);
tocrc++;
}
if (chk_namecrc && !(smb.status.attr & SMB_EMAIL)
&& (msg.hdr.type == SMB_MSG_TYPE_NORMAL || msg.hdr.type == SMB_MSG_TYPE_POLL)
&& msg.to_ext == NULL && msg.idx.to != smb_name_crc(msg.to)) {
fprintf(stderr, "%sTo CRC mismatch\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: To (%04X) does not match index "
"CRC (%04X)\n"
, smb_name_crc(msg.to), msg.idx.to);
tocrc++;
if (msg.hdr.netattr & NETMSG_INTRANSIT) {
fprintf(stderr, "%sIn transit\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Flagged 'in transit'\n");
intransit++;
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
if ((msg.hdr.attr & (MSG_MODERATED | MSG_VALIDATED | MSG_DELETE)) == MSG_MODERATED) {
fprintf(stderr, "%sUnvalidated\n", beep);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Flagged 'moderated', but not yet 'validated'\n");
unvalidated++;
}
if (msg.hdr.type == SMB_MSG_TYPE_FILE) {
char fname[SMB_FILEIDX_NAMELEN + 1];
smb_fileidxname(msg.name, fname, sizeof fname);
if (strcmp(fname, msg.file_idx.name) != 0) {
fprintf(stderr, "%sFilename mismatch: '%s' != '%s'\n", beep, fname, msg.file_idx.name);
msgerr = true;
fname_err++;
}
}
}
if (msg.hdr.number == 0) {
fprintf(stderr, "%sZero %s number\n", beep, base_type);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header number is zero (invalid)\n");
zeronum++;
}
if (number) {
for (m = 0; m < headers; m++)
if (number[m] && msg.hdr.number == number[m]) {
fprintf(stderr, "%sDuplicate %s number\n", beep, base_type);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Header number (%" PRIu32 ") duplicated\n"
, msg.hdr.number);
dupenumhdr++;
break;
}
number[headers] = msg.hdr.number;
}
if (chkxlat) { /* Check translation strings */
for (i = 0; i < msg.hdr.total_dfields; i++) {
fseek(smb.sdt_fp, msg.hdr.offset + msg.dfield[i].offset, SEEK_SET);
if (!fread(&xlat, 2, 1, smb.sdt_fp))
xlat = 0xffff;
lzh = 0;
if (xlat == XLAT_LZH) {
lzh = 1;
if (!fread(&xlat, 2, 1, smb.sdt_fp))
xlat = 0xffff;
}
if (xlat != XLAT_NONE) {
fprintf(stderr, "%sUnsupported Xlat %04X dfield[%u]\n"
, beep, xlat, i);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Unsupported translation type (%04X) "
"in dfield[%u] (offset %" PRIu32 ")\n"
, xlat, i, msg.dfield[i].offset);
xlaterr++;
}
else {
if (lzh) {
lzhmsg = TRUE;
if (fread(&m, 4, 1, smb.sdt_fp)) { /* Get uncompressed len */
lzhsaved += (smb_datblocks(m + 2)
- smb_datblocks(msg.dfield[i].length))
* SDT_BLOCK_LEN;
lzhblocks += smb_datblocks(msg.dfield[i].length);
}
}
}
}
}
}
if (chkalloc && !(smb.status.attr & SMB_HYPERALLOC)) {
fseek(smb.sha_fp, (l - smb.status.header_offset) / SHD_BLOCK_LEN, SEEK_SET);
for (m = 0; m < size; m += SHD_BLOCK_LEN) {
/***
if(msg.hdr.attr&MSG_DELETE && (i=fgetc(smb.sha_fp))!=0) {
fprintf(stderr,"%sDeleted Header Block %lu marked %02X\n"
,beep,m/SHD_BLOCK_LEN,i);
msgerr=TRUE;
delalloc++;
}
***/
if (!(msg.hdr.attr & MSG_DELETE) && (i = fgetc(smb.sha_fp)) != 1) {
fprintf(stderr, "%sActive Header Block %" PRIu32 " marked %02X\n"
, beep, m / SHD_BLOCK_LEN, i);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Active header block %" PRIu32 " marked %02X "
"instead of 01\n"
, m / SHD_BLOCK_LEN, i);
actalloc++;
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
if (!(msg.hdr.attr & MSG_DELETE)) {
acthdrblocks += (size / SHD_BLOCK_LEN);
for (n = 0; n < msg.hdr.total_dfields; n++) {
if (msg.dfield[n].offset & 0x80000000UL) {
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Invalid Data Field [%lu] Offset: %" PRIu32 "\n"
, n, msg.dfield[n].offset);
dfieldoffset++;
}
if (msg.dfield[n].length & 0x80000000UL) {
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Invalid Data Field [%lu] Length: %" PRIu32 "\n"
, n, msg.dfield[n].length);
dfieldlength++;
}
fseek(smb.sda_fp
, ((msg.hdr.offset + msg.dfield[n].offset) / SDT_BLOCK_LEN) * 2
, SEEK_SET);
for (m = 0; m < msg.dfield[n].length; m += SDT_BLOCK_LEN) {
/* TODO: LE Only */
i = 0;
if (!fread(&i, 2, 1, smb.sda_fp) || !i) {
fprintf(stderr
, "%sActive Data Block %lu.%" PRIu32 " marked free\n"
, beep, n, m / SHD_BLOCK_LEN);
msgerr = TRUE;
if (extinfo)
printf("MSGERR: Active Data Block %lu.%" PRIu32 " "
"marked free\n"
, n, m / SHD_BLOCK_LEN);
datactalloc++;
}
else
delhdrblocks += (size / SHD_BLOCK_LEN);
else { /* Hyper Alloc */
if (msg.hdr.attr & MSG_DELETE)
delhdrblocks += (size / SHD_BLOCK_LEN);
else
acthdrblocks += (size / SHD_BLOCK_LEN);
}
totallzhmsgs += lzhmsg;
headers++;
if (msgerr && extinfo) {
printf("\n");
printf("%-16s %s\n", (smb.status.attr & SMB_FILE_DIRECTORY) ? "file base":"message base", smb.file);
smb_dump_msghdr(stdout, &msg);
printf("\n");
}
smb_freemsgmem(&msg);
FREE_AND_NULL(number);
fprintf(stderr, "\r%79s\r100%%\n", "");
if (chkalloc && !(smb.status.attr & SMB_HYPERALLOC)) {
fprintf(stderr, "\nChecking %s Data Blocks\n\n", smb.file);
off_t sda_length = filelength(fileno(smb.sda_fp));
fseek(smb.sda_fp, 0L, SEEK_SET);
for (l = 0; l < (ulong)sda_length; l += 2) {
if ((l % 10) == 0)
fprintf(stderr, "\r%2lu%% ", l ? (long)(100.0 / ((float)sda_length / l)) : 0);
/* TODO: LE Only */
i = 0;
if (!fread(&i, 2, 1, smb.sda_fp))
break;
if (!i)
deldatblocks++;
}
smb_close_ha(&smb);
smb_close_da(&smb);
fprintf(stderr, "\r%79s\r100%%\n", "");
}
total = (ulong)filelength(fileno(smb.sid_fp)) / smb_idxreclen(&smb);
dupenum = dupeoff = misnumbered = idxzeronum = idxnumerr = idxofferr = idxerr = delidx = 0;
fprintf(stderr, "\nChecking %s Index\n\n", smb.file);
if ((offset = malloc(total * sizeof(*offset))) == NULL) {
printf("Error allocating %lu bytes of memory\n", total * sizeof(*offset));
}
if ((number = malloc(total * sizeof(*number))) == NULL) {
printf("Error allocating %lu bytes of memory\n", total * sizeof(*number));
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
for (l = 0; l < total; l++) {
fseek(smb.sid_fp, l * smb_idxreclen(&smb), SEEK_SET);
fprintf(stderr, "\r%2lu%% %5lu ", l ? (long)(100.0 / ((float)total / l)) : 0, l);
if (!fread(&idx, sizeof(idx), 1, smb.sid_fp))
break;
fprintf(stderr, "#%-5" PRIu32 " (%06" PRIX32 ") 1st Pass ", idx.number, idx.offset);
if (idx.attr & MSG_DELETE) {
/* Message Disabled... why? ToDo */
/* fprintf(stderr,"%sMarked for deletion\n",beep); */
delidx++;
}
for (m = 0; m < l; m++)
if (number[m] == idx.number) {
fprintf(stderr, "%sDuplicate %s number\n", beep, base_type);
dupenum++;
break;
}
for (m = 0; m < l; m++)
if (offset[m] == idx.offset) {
fprintf(stderr, "%sDuplicate offset: %" PRIu32 "\n", beep, idx.offset);
dupeoff++;
break;
}
if (idx.offset < smb.status.header_offset) {
fprintf(stderr, "%sInvalid offset\n", beep);
idxofferr++;
break;
}
if (idx.number == 0) {
fprintf(stderr, "%sZero %s number\n", beep, base_type);
idxzeronum++;
break;
}
if (idx.number > smb.status.last_msg) {
fprintf(stderr, "%sOut-Of-Range %s number\n", beep, base_type);
idxnumerr++;
break;
}
number[l] = idx.number;
offset[l] = idx.offset;
}
if (l < total) {
fprintf(stderr, "%sError reading index record\n", beep);
idxerr = 1;
else {
fprintf(stderr, "\r%79s\r", "");
for (m = 0; m < total; m++) {
fprintf(stderr, "\r%2lu%% %5" PRIu32 " ", m ? (long)(100.0 / ((float)total / m)) : 0, m);
fprintf(stderr, "#%-5lu (%06lX) 2nd Pass ", number[m], offset[m]);
for (n = 0; n < m; n++)
if (number[m] && number[n] && number[m] < number[n]) {
fprintf(stderr, "%sMisordered %s number\n", beep, base_type);
misnumbered++;
number[n] = 0;
break;
}
}
fprintf(stderr, "\r%79s\r100%%\n", "");
FREE_AND_NULL(number);
FREE_AND_NULL(offset);
} /* if(total) */
if (!(smb.status.attr & SMB_EMAIL) && chkhash && smb_open_hash(&smb) == SMB_SUCCESS) {
hash_t hash;
fprintf(stderr, "\nChecking %s Hashes\n\n", smb.file);
off_t hash_length = filelength(fileno(smb.hash_fp));
fseek(smb.hash_fp, 0L, SEEK_SET);
for (l = 0; l < (ulong)hash_length; l += sizeof(hash_t)) {
if (((l / sizeof(hash_t)) % 10) == 0)
fprintf(stderr, "\r%2lu%% ", l ? (long)(100.0 / ((float)hash_length / l)) : 0);
if (!fread(&hash, sizeof(hash), 1, smb.hash_fp))
if (hash.number == 0 || hash.number > smb.status.last_msg)
fprintf(stderr, "\r%sInvalid %s number (%u > %u)\n", beep, base_type, hash.number, smb.status.last_msg), badhash++, print_hash(&hash);
else if (hash.time < 0x40000000 || hash.time > (ulong)now + (60 * 60))
fprintf(stderr, "\r%sInvalid time (0x%08" PRIX32 ")\n", beep, hash.time), badhash++, print_hash(&hash);
else if (hash.length < 1 || hash.length > 1024 * 1024 * 1024)
fprintf(stderr, "\r%sInvalid length (%" PRIu32 ")\n", beep, hash.length), badhash++, print_hash(&hash);
else if (hash.source >= SMB_HASH_SOURCE_TYPES)
fprintf(stderr, "\r%sInvalid source type (%u)\n", beep, hash.source), badhash++, print_hash(&hash);
}
fprintf(stderr, "\r%79s\r100%%\n", "");
}
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
totalmsgs += smb.status.total_msgs;
totalmsgbytes += (acthdrblocks * SHD_BLOCK_LEN) + (actdatblocks * SDT_BLOCK_LEN);
totaldelmsgs += deleted;
totallzhsaved += lzhsaved;
printf("\n");
printf("%-35.35s (=): %" PRIu32 "\n"
, "Status Total"
, smb.status.total_msgs);
printf("%-35.35s (=): %lu\n"
, "Total Indexes"
, total);
printf("%-35.35s (=): %lu\n"
, "Active Indexes"
, total - delidx);
printf("%-35.35s (=): %lu\n"
, "Active Headers"
, headers - deleted);
printf("%-35.35s ( ): %-8lu %13s bytes used\n"
, "Active Header Blocks"
, acthdrblocks, ultoac(acthdrblocks * SHD_BLOCK_LEN, str));
printf("%-35.35s ( ): %-8lu %13s bytes used\n"
, "Active Data Blocks"
, actdatblocks, ultoac(actdatblocks * SDT_BLOCK_LEN, str));
if (lzhblocks)
printf("%-35.35s ( ): %-8lu %13s bytes saved\n"
, "Active LZH Compressed Data Blocks"
, lzhblocks, ultoac(lzhsaved, str));
printf("%-35.35s ( ): %lu\n"
, "Header Records"
, headers);
printf("%-35.35s ( ): %lu\n"
, "Deleted Indexes"
, delidx);
printf("%-35.35s ( ): %lu\n"
, "Deleted Headers"
, deleted);
printf("%-35.35s ( ): %-8lu %13s bytes used\n"
, "Deleted Header Blocks"
, delhdrblocks, ultoac(delhdrblocks * SHD_BLOCK_LEN, str));
packable += (delhdrblocks * SHD_BLOCK_LEN);
printf("%-35.35s ( ): %-8lu %13s bytes used\n"
, "Deleted Data Blocks"
, deldatblocks, ultoac(deldatblocks * SDT_BLOCK_LEN, str));
packable += (deldatblocks * SDT_BLOCK_LEN);
if (oldest)
printf("%-35.35s ( ): %lu days (%u max)\n"
, "Oldest Message (import)"
, oldest / (24 * 60 * 60), smb.status.max_age);
if (largest)
printf("%-35.35s ( ): %lu bytes (#%lu)\n"
, "Largest Message (data)"
, largest, largest_msgnum);
if (orphan)
printf("%-35.35s (!): %lu\n"
, "Orphaned Headers"
, orphan);