chksmb.c 25.91 KiB
/* chksmb.c */
/* Synchronet message base (SMB) validity checker */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2008 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 *
* *
* 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. *
****************************************************************************/
/* ANSI */
#include <stdio.h>
#include <stdlib.h> /* exit */
#include <string.h> /* strrchr */
#include <time.h> /* ctime */
#include <ctype.h> /* toupper */
/* SMB-specific */
#include "genwrap.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];
signed char i,j,k;
sprintf(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--]=',';
}
return(string);
}
/****************************************************************************/
/* 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);
}
return(str);
}
char* DLLCALL strip_ctrl(char *str)
{
char tmp[1024];
int i,j;
for(i=j=0;str[i] && j<sizeof(tmp)-1;i++) {
if(str[i]==CTRL_A && str[i+1]!=0)
i++;
else if((uchar)str[i]>=' ')
tmp[j++]=str[i];
}
if(i!=j) {
tmp[j]=0;
strcpy(str,tmp);
}
return(str);
}
char *usage="\nusage: chksmb [-opts] <filespec.SHD>\n"
"\n"
" opts:\n"
" b - beep on error\n"
" s - stop after errored message base\n"
" p - pause after errored messsage base\n"
" h - don't check hash file\n"
" a - don't check allocation files\n"
" t - don't check translation strings\n"
" e - display extended info on corrupted msgs\n";
int main(int argc, char **argv)
{
char str[128],*p,*s,*beep="";
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;
uint16_t xlat;
uint32_t m;
ulong l,n,length,size,total=0,orphan,deleted,headers
,*offset,*number,xlaterr
,delidx
,delhdrblocks,deldatblocks,hdrerr,lockerr,hdrnumerr,hdrlenerr
,getbodyerr,gettailerr
,hasherr
,acthdrblocks,actdatblocks
,dfieldlength,dfieldoffset
,dupenum,dupenumhdr,dupeoff,attr,actalloc
,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;
smb_t smb;
idxrec_t idx;
smbmsg_t msg;
hash_t** hashes;
char revision[16];
sscanf("$Revision$", "%*s %s", revision);
fprintf(stderr,"\nCHKSMB v2.20-%s (rev %s) SMBLIB %s - Check Synchronet Message Base\n"
,PLATFORM_DESC,revision,smb_lib_ver());
if(argc<2) {
printf("%s",usage);
exit(1);
}
errlast=errors=0;
for(x=1;x<argc;x++) {
if(stop_on_error && errors)
break;
if(pause_on_error && errlast!=errors) {
fprintf(stderr,"\7\nHit any key to continue...");
if(!getch())
getch();
printf("\n");
}
errlast=errors;
if(argv[x][0]=='-'
#if !defined(__unix__) /* just for backwards compatibility */
|| argv[x][0]=='/'
#endif
) {
for(y=1;argv[x][y];y++)
switch(toupper(argv[x][y])) {
case 'Q':
beep="";
break;
case 'B':
beep="\a";
break;
case 'P':
pause_on_error=TRUE;
break;
case 'S':
stop_on_error=TRUE;
break;
case 'T':
chkxlat=FALSE;
break;
case 'A':
chkalloc=FALSE;
break;
case 'H':
chkhash=FALSE;
break;
case 'E':
extinfo=TRUE;
break;
default:
printf("%s",usage);
exit(1);
}
continue;
}
SAFECOPY(smb.file,argv[x]);
p=strrchr(smb.file,'.');
s=strrchr(smb.file,'\\');
if(p>s) *p=0;
sprintf(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;
}
length=filelength(fileno(smb.shd_fp));
if(length<sizeof(smbhdr_t)) {
printf("Empty\n");
smb_close(&smb);
continue;
}
if((i=smb_locksmbhdr(&smb))!=0) {
smb_close(&smb);
printf("smb_locksmbhdr returned %d: %s\n",i,smb.last_error);
errors++;
continue;
}
if((length/SHD_BLOCK_LEN)*sizeof(ulong)) {
if((number=(ulong *)malloc(((length/SHD_BLOCK_LEN)+2)*sizeof(ulong)))
==NULL) {
printf("Error allocating %lu bytes of memory\n"
,(length/SHD_BLOCK_LEN)*sizeof(ulong));
return(++errors);
}
}
else
number=NULL;
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);
return(++errors);
}
if((i=smb_open_da(&smb))!=0) {
printf("smb_open_da returned %d: %s\n",i,smb.last_error);
return(++errors);
}
}
headers=deleted=orphan=dupenumhdr=attr=zeronum=timeerr=lockerr=hdrerr=0;
subjcrc=fromcrc=tocrc=0;
hdrnumerr=hdrlenerr=0;
actalloc=datactalloc=deldatblocks=delhdrblocks=xlaterr=0;
lzhblocks=lzhsaved=acthdrblocks=actdatblocks=0;
getbodyerr=gettailerr=0;
hasherr=0;
unvalidated=0;
intransit=0;
acthdrblocks=actdatblocks=0;
dfieldlength=dfieldoffset=0;
for(l=smb.status.header_offset;l<length;l+=size) {
size=SHD_BLOCK_LEN;
fprintf(stderr,"\r%2lu%% ",(long)(100.0/((float)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); */
delhdrblocks++;
}
continue;
}
smb_unlockmsghdr(&smb,&msg);
size=smb_hdrblocks(smb_getmsghdrlen(&msg))*SHD_BLOCK_LEN;
truncsp(msg.from);
strip_ctrl(msg.from);
fprintf(stderr,"#%-5lu (%06lX) %-25.25s ",msg.hdr.number,l,msg.from);
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 calculcated length (%lu)\n"
,msg.hdr.length,smb_getmsghdrlen(&msg));
hdrlenerr++;
}
/* Test reading of the message text (body and tails) */
if(msg.hdr.attr&MSG_DELETE)
body=tail=NULL;
else {
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(!(smb.status.attr&SMB_EMAIL) && chkhash) {
/* Look-up the message hashes */
hashes=smb_msghashes(&msg,body);
if(hashes!=NULL
&& hashes[0]!=NULL
&& (i=smb_findhash(&smb,hashes,NULL,SMB_HASH_SOURCE_ALL,/* 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));
#ifdef _DEBUG
printf("\n");
printf("%-10s: %s\n", "Source", smb_hashsourcetype(hashes[h]->source));
printf("%-10s: %lu\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]->crc16);
if(hashes[h]->flags&SMB_HASH_CRC32)
printf("%-10s: %08lx\n","CRC-32", hashes[h]->crc32);
if(hashes[h]->flags&SMB_HASH_MD5)
printf("%-10s: %s\n", "MD5", MD5_hex(str,hashes[h]->md5));
#endif
}
hasherr++;
}
}
smb_close_hash(&smb); /* just incase */
FREE_LIST(hashes,i);
}
FREE_AND_NULL(body);
FREE_AND_NULL(tail);
lzhmsg=FALSE;
if(msg.hdr.attr&MSG_DELETE) {
deleted++;
if(number)
number[headers]=0;
if(smb.status.attr&SMB_HYPERALLOC)
deldatblocks+=smb_datblocks(smb_getmsgdatlen(&msg));
}
else {
actdatblocks+=smb_datblocks(smb_getmsgdatlen(&msg));
if(msg.hdr.number>smb.status.last_msg) {
fprintf(stderr,"%sOut-Of-Range message number\n",beep);
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Header number (%lu) greater than last (%lu)\n"
,msg.hdr.number,smb.status.last_msg);
hdrnumerr++;
}
if(smb_getmsgidx(&smb,&msg)) {
fprintf(stderr,"%sNot found in index\n",beep);
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Header number (%lu) 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++;
}
if(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(!(smb.status.attr&SMB_EMAIL)
&& 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(!(smb.status.attr&SMB_EMAIL)
&& 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&MSG_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_MODERATED) {
fprintf(stderr,"%sUnvalidated\n",beep);
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Flagged 'moderated', but not yet 'validated'\n");
unvalidated++;
}
}
if(msg.hdr.number==0) {
fprintf(stderr,"%sZero message number\n",beep);
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 message number\n",beep);
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Header number (%lu) 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 %ld)\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 %lu marked %02X\n"
,beep,m/SHD_BLOCK_LEN,i);
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Active header block %lu marked %02X "
"instead of 01\n"
,m/SHD_BLOCK_LEN,i);
actalloc++;
}
}
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: %lu\n"
,n,msg.dfield[n].offset);
dfieldoffset++;
}
if(msg.dfield[n].length&0x80000000UL) {
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Invalid Data Field [%lu] Length: %lu\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.%lu marked free\n"
,beep,n,m/SHD_BLOCK_LEN);
msgerr=TRUE;
if(extinfo)
printf("MSGERR: Active Data Block %lu.%lu "
"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("%-20s %s\n","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);
length=filelength(fileno(smb.sda_fp));
fseek(smb.sda_fp,0L,SEEK_SET);
for(l=0;l<length;l+=2) {
if((l%10)==0)
fprintf(stderr,"\r%2lu%% ",l ? (long)(100.0/((float)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=filelength(fileno(smb.sid_fp))/sizeof(idxrec_t);
dupenum=dupeoff=misnumbered=idxzeronum=idxnumerr=idxofferr=idxerr=delidx=0;
if(total) {
fprintf(stderr,"\nChecking %s Index\n\n",smb.file);
if((offset=(ulong *)malloc(total*sizeof(ulong)))==NULL) {
printf("Error allocating %lu bytes of memory\n",total*sizeof(ulong));
return(++errors);
}
if((number=(ulong *)malloc(total*sizeof(ulong)))==NULL) {
printf("Error allocating %lu bytes of memory\n",total*sizeof(ulong));
return(++errors);
}
fseek(smb.sid_fp,0L,SEEK_SET);
for(l=0;l<total;l++) {
fprintf(stderr,"\r%2lu%% %5lu ",l ? (long)(100.0/((float)total/l)) : 0,l);
if(!fread(&idx,sizeof(idxrec_t),1,smb.sid_fp))
break;
fprintf(stderr,"#%-5lu (%06lX) 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 message number\n",beep);
dupenum++;
break;
}
for(m=0;m<l;m++)
if(offset[m]==idx.offset) {
fprintf(stderr,"%sDuplicate offset: %lu\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 message number\n",beep);
idxzeronum++;
break;
}
if(idx.number>smb.status.last_msg) {
fprintf(stderr,"%sOut-Of-Range message number\n",beep);
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%% %5lu ",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 message number\n",beep);
misnumbered++;
number[n]=0;
break;
}
}
fprintf(stderr,"\r%79s\r100%%\n","");
}
FREE_AND_NULL(number);
FREE_AND_NULL(offset);
} /* if(total) */
totalmsgs+=smb.status.total_msgs;
totalmsgbytes+=(acthdrblocks*SHD_BLOCK_LEN)+(actdatblocks*SDT_BLOCK_LEN);
totaldelmsgs+=deleted;
totallzhsaved+=lzhsaved;
printf("\n");
printf("%-35.35s (=): %lu\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(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);
#define INDXERR "Index/Header Mismatch: "
if(attr)
printf("%-35.35s (!): %lu\n"
,INDXERR "Attributes"
,attr);
if(timeerr)
printf("%-35.35s (!): %lu\n"
,INDXERR "Import Time"
,timeerr);
if(subjcrc)
printf("%-35.35s (!): %lu\n"
,INDXERR "Subject CRCs"
,subjcrc);
if(fromcrc)
printf("%-35.35s (!): %lu\n"
,smb.status.attr&SMB_EMAIL ? INDXERR "From Ext" : INDXERR "From CRCs"
,fromcrc);
if(tocrc)
printf("%-35.35s (!): %lu\n"
,smb.status.attr&SMB_EMAIL ? 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(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);
printf("\n%s Message Base ",smb.file);
if(/* (headers-deleted)!=smb.status.total_msgs || */
total!=smb.status.total_msgs
|| (headers-deleted)!=total-delidx
|| idxzeronum || zeronum
|| hdrlenerr || hasherr
|| getbodyerr || gettailerr
|| orphan || dupenumhdr || dupenum || dupeoff || attr
|| lockerr || hdrerr || hdrnumerr || idxnumerr || idxofferr
|| actalloc || datactalloc || misnumbered || timeerr
|| intransit || unvalidated
|| subjcrc || fromcrc || tocrc
|| dfieldoffset || dfieldlength || xlaterr || idxerr) {
printf("%shas Errors!\n",beep);
errors++;
}
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));
if(totallzhmsgs && totalmsgs!=smb.status.total_msgs)
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,"\7\nHit any key to continue...");
if(!getch())
getch();
fprintf(stderr,"\n");
}
if(errors)
printf("\n'fixsmb' can be used to repair most message base problems.\n");
return(errors);
}