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>

rswindell
committed
#include <string.h> /* strrchr */
#include <time.h> /* ctime */
#include "git_branch.h"
#include "git_hash.h"

rswindell
committed
#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];
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];
char point[25];
sprintf(str,"%hu:%hu/%hu",addr.zone,addr.net,addr.node);
if(addr.point) {
sprintf(point,".%u",addr.point);
strcat(str,point);
{
char tmp[1024];
int i,j;
if(str[i]==CTRL_A && str[i+1]!=0)
i++;
else if((uchar)str[i]>=' ')
BOOL contains_ctrl_chars(char* str)
{
uchar* p;
if(str==NULL)
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] <filespec.SHD>\n"
" b - beep on error\n"
" s - stop after errored message/file base\n"
" p - pause after errored messsage/file base\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)
{
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_namecrc = TRUE;
ulong l,n,size,total=0,orphan,deleted,headers
,*offset,*number,xlaterr
,delidx
,delhdrblocks,deldatblocks,hdrerr,lockerr,hdrnumerr,hdrlenerr
,getbodyerr,gettailerr
,hasherr,badhash
,dfieldlength,dfieldoffset
,dupenum,dupenumhdr,dupeoff,attr,actalloc,types
,datactalloc,misnumbered,timeerr,idxofferr,idxerr
,subjcrc,fromcrc,tocrc
,intransit,unvalidated
,zeronum,idxzeronum,idxnumerr,packable=0L,totallzhsaved=0L
,totalmsgs=0,totallzhmsgs=0,totaldelmsgs=0,totalmsgbytes=0L
,lzhblocks,lzhsaved
,ctrl_chars;
off_t shd_length;
ulong oldest=0;
ulong largest=0;
ulong largest_msgnum=0;
ulong msgids = 0;
idxrec_t* idxrec = NULL;
fileidxrec_t* fidxrec = NULL;
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)
break;
if(pause_on_error && errlast!=errors) {
fprintf(stderr,"%s\nHit any key to continue...", beep);
if(!getch())
getch();
if(argv[x][0]=='-'
) {
switch(argv[x][y]) {
case 'q':
beep="\a";
break;
case 'S':
chk_subjcrc = FALSE;
break;
case 'N':
chk_namecrc = FALSE;
break;
chk_msgids = FALSE;
break;
break;
default:
printf("%s",usage);
SAFECOPY(smb.file,argv[x]);
p = getfext(smb.file);
if(p != NULL && stricmp(p, ".shd") == 0) *p=0;
if(!fexist(str)) {
printf("\n%s doesn't exist.\n",smb.file);
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);
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);
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);
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)));
return(++errors);
}
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)));
headers=deleted=orphan=dupenumhdr=attr=zeronum=timeerr=lockerr=hdrerr=0;
types = 0;
subjcrc=fromcrc=tocrc=0;
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;
largest = 0;
largest_msgnum = 0;
for(l=smb.status.header_offset; l < (uint32_t)shd_length;l+=size) {
fprintf(stderr,"\r%2lu%% ",(long)(100.0/((float)shd_length/l)));
memset(&msg,0,sizeof(msg));
if((i=smb_lockmsghdr(&smb,&msg))!=0) {
printf("\n(%06lX) smb_lockmsghdr returned %d: %s\n",l,i,smb.last_error);
lockerr++;
headers++;
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);
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);
else
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)
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);
printf("MSGERR: Header length (%hu) does not match calculated length (%u)\n"
,msg.hdr.length,smb_getmsghdrlen(&msg));
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);
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);
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) {
hashes=smb_msghashes(&msg,(uchar*)body,SMB_HASH_SOURCE_DUPE);
&& (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;
printf("MSGERR: %d searching for %s: %s\n"
,i
,smb_hashsourcetype(hashes[h]->source)
,smb_hashsource(&msg,hashes[h]->source));
#ifdef _DEBUG
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_AND_NULL(body);
FREE_AND_NULL(tail);
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);
printf("MSGERR: Header number (%"PRIu32") greater than last (%"PRIu32")\n"
,msg.hdr.number,smb.status.last_msg);
if(smb_getmsgidx(&smb,&msg) || msg.idx.offset != l) {
fprintf(stderr,"%sNot found in index\n",beep);
printf("MSGERR: Header number (%"PRIu32") not found in index\n"
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);
}
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");
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);
if(smb.status.attr & SMB_EMAIL
&& (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);
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);
if(smb.status.attr & SMB_EMAIL
&& (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);
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);
if(msg.hdr.netattr&NETMSG_INTRANSIT) {
fprintf(stderr,"%sIn transit\n",beep);
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Flagged 'in transit'\n");
intransit++;
}
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++;
}
fprintf(stderr,"%sZero %s number\n",beep, base_type);
if(extinfo)
printf("MSGERR: Header number is zero (invalid)\n");
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);
printf("MSGERR: Header number (%"PRIu32") duplicated\n"
,msg.hdr.number);
dupenumhdr++;
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))
if(xlat!=XLAT_NONE) {
fprintf(stderr,"%sUnsupported Xlat %04X dfield[%u]\n"
,beep,xlat,i);
if(extinfo)
printf("MSGERR: Unsupported translation type (%04X) "
,xlat,i,msg.dfield[i].offset);
xlaterr++;
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);
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);
printf("MSGERR: Active header block %"PRIu32" marked %02X "
"instead of 01\n"
,m/SHD_BLOCK_LEN,i);
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) {
printf("MSGERR: Invalid Data Field [%lu] Offset: %"PRIu32"\n"
,n,msg.dfield[n].offset);
if(msg.dfield[n].length&0x80000000UL) {
printf("MSGERR: Invalid Data Field [%lu] Length: %"PRIu32"\n"
,n,msg.dfield[n].length);
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) {
if(!fread(&i,2,1,smb.sda_fp) || !i) {
fprintf(stderr
,beep,n,m/SHD_BLOCK_LEN);
"marked free\n"
,n,m/SHD_BLOCK_LEN);
else { /* Hyper Alloc */
if(msg.hdr.attr&MSG_DELETE)
delhdrblocks+=(size/SHD_BLOCK_LEN);
else
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);
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);
i=0;
if(!fread(&i,2,1,smb.sda_fp))
break;
if(!i)
smb_close_ha(&smb);
smb_close_da(&smb);
total=(ulong)filelength(fileno(smb.sid_fp))/smb_idxreclen(&smb);
dupenum=dupeoff=misnumbered=idxzeronum=idxnumerr=idxofferr=idxerr=delidx=0;
if(total) {
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));
return(++errors);
if((number=malloc(total*sizeof(*number)))==NULL) {
printf("Error allocating %lu bytes of memory\n",total*sizeof(*number));
return(++errors);
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))
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); */
for(m=0;m<l;m++)
if(number[m]==idx.number) {
fprintf(stderr,"%sDuplicate %s number\n",beep, base_type);
for(m=0;m<l;m++)
if(offset[m]==idx.offset) {
fprintf(stderr,"%sDuplicate offset: %"PRIu32"\n",beep,idx.offset);
if(idx.offset<smb.status.header_offset) {
fprintf(stderr,"%sInvalid offset\n",beep);
idxofferr++;
fprintf(stderr,"%sZero %s number\n",beep, base_type);
if(idx.number>smb.status.last_msg) {
fprintf(stderr,"%sOut-Of-Range %s number\n",beep, base_type);
if(l<total) {
fprintf(stderr,"%sError reading index record\n",beep);
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;
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))
break;
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);
}
smb_close_hash(&smb);
}
totalmsgs+=smb.status.total_msgs;
totalmsgbytes+=(acthdrblocks*SHD_BLOCK_LEN)+(actdatblocks*SDT_BLOCK_LEN);
totaldelmsgs+=deleted;
totallzhsaved+=lzhsaved;
940
941
942
943
944
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
,"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);
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
if(orphan)
printf("%-35.35s (!): %lu\n"
,"Orphaned Headers"
,orphan);
if(idxzeronum)
printf("%-35.35s (!): %lu\n"
,"Zeroed Index Numbers"
,idxzeronum);
if(zeronum)
printf("%-35.35s (!): %lu\n"
,"Zeroed Header Numbers"
,zeronum);
if(idxofferr)
printf("%-35.35s (!): %lu\n"
,"Invalid Index Offsets"
,idxofferr);
if(dupenum)
printf("%-35.35s (!): %lu\n"
,"Duplicate Index Numbers"
,dupenum);
if(dupeoff)
printf("%-35.35s (!): %lu\n"
,"Duplicate Index Offsets"
,dupeoff);
if(dupenumhdr)
printf("%-35.35s (!): %lu\n"
,"Duplicate Header Numbers"
,dupenumhdr);
if(misnumbered)
printf("%-35.35s (!): %lu\n"
,"Misordered Index Numbers"
,misnumbered);
if(lockerr)
printf("%-35.35s (!): %lu\n"
,"Unlockable Header Records"
,lockerr);
if(hdrerr)
printf("%-35.35s (!): %lu\n"
,"Unreadable Header Records"
,hdrerr);
if(idxnumerr)
printf("%-35.35s (!): %lu\n"
,"Out-Of-Range Index Numbers"
,idxnumerr);
if(hdrnumerr)
printf("%-35.35s (!): %lu\n"
,"Out-Of-Range Header Numbers"
,hdrnumerr);
if(hdrlenerr)
printf("%-35.35s (!): %lu\n"
,"Mismatched Header Lengths"
,hdrlenerr);
if(types)
printf("%-35.35s (!): %lu\n"
,"Mismatched Attribute/MsgTypes"
,types);
#define INDXERR "Index/Header Mismatch: "
if(attr)
printf("%-35.35s (!): %lu\n"
,attr);
if(timeerr)
printf("%-35.35s (!): %lu\n"
,INDXERR "Import Time"
if(subjcrc)
printf("%-35.35s (!): %lu\n"
,INDXERR "Subject CRCs"
,subjcrc);
if(fromcrc)
printf("%-35.35s (!): %lu\n"
,smb.status.attr&(SMB_EMAIL|SMB_FILE_DIRECTORY) ? INDXERR "From Ext" : INDXERR "From CRCs"
,fromcrc);
if(tocrc)
printf("%-35.35s (!): %lu\n"
,smb.status.attr&(SMB_EMAIL|SMB_FILE_DIRECTORY) ? INDXERR "To Ext" : INDXERR "To CRCs"
,tocrc);
if(intransit)
printf("%-35.35s (?): %lu\n"
,"Message flagged as 'In Transit'"
,intransit);
if(unvalidated)
printf("%-35.35s (?): %lu\n"
,"Moderated message not yet validated"
,unvalidated);
if(getbodyerr)
printf("%-35.35s (!): %lu\n"
,"Message Body Text Read Failures"
,getbodyerr);
if(gettailerr)
printf("%-35.35s (!): %lu\n"
,"Message Tail Text Read Failures"
,gettailerr);
if(xlaterr)
printf("%-35.35s (!): %lu\n"
,"Unsupported Translation Types"
,xlaterr);
if(hasherr)
printf("%-35.35s (!): %lu\n"
,"Missing Hash Records"
,hasherr);
if(msgids)
printf("%-35.35s (!): %lu\n"
,"Missing Message-IDs"
,msgids);
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
if(datactalloc)
printf("%-35.35s (!): %lu\n"
,"Misallocated Active Data Blocks"
,datactalloc);
if(actalloc)
printf("%-35.35s (!): %lu\n"
,"Misallocated Active Header Blocks"
,actalloc);
/***
if(delalloc)
printf("%-35.35s (!): %lu\n"
,"Misallocated Deleted Header Blocks"
,delalloc);
***/
if(dfieldoffset)
printf("%-35.35s (!): %lu\n"
,"Invalid Data Field Offsets"
,dfieldoffset);
if(dfieldlength)
printf("%-35.35s (!): %lu\n"
,"Invalid Data Field Lengths"
,dfieldlength);
if(badhash)
printf("%-35.35s (!): %lu\n"
,"Invalid Hash Entries"
,badhash);
if(ctrl_chars)
printf("%-35.35s (!): %lu\n"
,"Control Characters in Header Fields"
,ctrl_chars);
if(hdr_overlap)
printf("%-35.35s (!): %lu\n"
,"Overlapping Headers"
,hdr_overlap);
printf("\n%s %s Base ",smb.file, base_type);
if(/* (headers-deleted)!=smb.status.total_msgs || */
total!=smb.status.total_msgs
|| (headers-deleted)!=total-delidx
|| idxzeronum || zeronum
|| hdrlenerr || hasherr || badhash
|| getbodyerr || gettailerr
|| orphan || dupenumhdr || dupenum || dupeoff || attr
|| lockerr || hdrerr || hdrnumerr || idxnumerr || idxofferr
|| actalloc || datactalloc || misnumbered || timeerr
|| intransit || unvalidated || ctrl_chars
|| subjcrc || fromcrc || tocrc
|| dfieldoffset || dfieldlength || xlaterr || idxerr) {
printf("%shas Errors!\n",beep);
else
printf("is OK\n");
smb_unlocksmbhdr(&smb);
smb_close(&smb);
}
if((totalmsgs && (totalmsgs!=smb.status.total_msgs || totallzhmsgs))
|| packable)
printf("\n");
if(totalmsgs && totalmsgs!=smb.status.total_msgs)
printf("%-39.39s: %-8lu %13s bytes used\n"
,"Total Active Messages"
,totalmsgs,ultoac(totalmsgbytes,str));
printf("%-39.39s: %-8lu %13s bytes saved\n"
,"Total LZH Compressed Messages"
,totallzhmsgs,ultoac(totallzhsaved,str));
if(packable)
printf("%-39.39s: %-8lu %13s bytes used\n"
,"Total Deleted Messages"
,totaldelmsgs,ultoac(packable,str));
if(pause_on_error && errlast!=errors) {
fprintf(stderr,"%s\nHit any key to continue...", beep);
if(!getch())
getch();
if(errors)
printf("\n'fixsmb' can be used to repair many message/file base problems.\n");