/* Copyright (c) 2017, Computer History Museum All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define FILE_NUM 72 #include "toc.h" #pragma segment TOC OSErr WriteRForkTOC(TOCHandle tocH); Boolean HasExternalTOC(FSSpecPtr spec); OSErr PeekRTOC(FSSpecPtr spec,TOCType *tocPart); void FixBoxUnread(TOCHandle tocH); TOCHandle FixErrantTOC(FSSpecPtr spec,TOCHandle tocH,short why); Boolean WantRebuildTOC(UPtr boxName,OSErr why, Boolean isIMAP); OSErr PeekDTOC(FSSpecPtr spec,TOCType *tocPart); void CheckStringLen(UPtr s, char maxLen, short fillLen); #define TOCSizeShouldBe(tocH) (sizeof(TOCType)+MAX(0,(*(tocH))->count-1)*sizeof(MSumType)) #define TOCCountShouldBe(tocH) (1+(GetHandleSize_(tocH)-sizeof(TOCType))/sizeof(MSumType)) /************************************************************************ * TOCBySpec - take a spec, return a TOC ************************************************************************/ TOCHandle TOCBySpec(FSSpecPtr spec) { if (!GetMailbox(spec,False)) return(FindTOC(spec)); return(nil); } /************************************************************************ * GetTOCByFSS - open a toc from an FSS ************************************************************************/ short GetTOCByFSS(FSSpecPtr specPtr,TOCHandle *tocH) { *tocH = TOCBySpec(specPtr); return(*tocH ? noErr : 1); } /********************************************************************** * KillTOC - kill the toc for a file **********************************************************************/ OSErr KillTOC(short refN,FSSpecPtr spec) { Boolean sane; OSErr err; Handle r; OSType type; Str255 name; short id; short oldResF = CurResFile(); /* * if the resource fork is bad or we can't open it, just remove it */ if (!refN && (FSpRFSane(spec,&sane) || !sane || -1==(refN=FSpOpenResFile(spec,fsRdWrPerm)))) err = FSpKillRFork(spec); else { /* * delete the backup resource */ Zap1Resource(TOC_TYPE,1002); if (PrefIsSet(PREF_NO_RF_TOC_BACKUP)) { /* * remove old resource */ Zap1Resource(TOC_TYPE,1001); err = ResError(); } else { /* * try to move the old resource */ SetResLoad(False); r = Get1Resource(TOC_TYPE,1001); err = ResError(); if (!r && !err) err = resNotFound; SetResLoad(True); if (!err) { GetResInfo(r, &id, &type, name); if (!(err = ResError())) { SetResInfo(r,1002,name); if (spec && !(err = ResError())) err = MyUpdateResFile(refN); } } } if (spec) CloseResFile(refN); /* * if we couldn't delete the resource, kill the resource fork */ if (spec && err && err!=resNotFound) err = FSpKillRFork(spec); } UseResFile (oldResF); return(err); } /********************************************************************** * CheckTOC - check a file for a table of contents, and build it if * necessary. * slated for removal **********************************************************************/ TOCHandle CheckTOC(FSSpecPtr spec) { uLong box, res, file, mod; Boolean resource; OSErr err; if ((err=TOCDates(spec,&box,&res,&file)) && err!=fnfErr) return(nil); /* * Which one do we want? */ if (res && res>=file) // resource exists and is newer { resource = True; mod = res; } else if (file) // file exists and is newer { resource = False; mod = file; } else return(BuildTOC(spec)); // no dice #ifdef NEVER /* * Is it new enough? */ if (!PrefIsSet(PREF_IGNORE_OUTDATE) && box>mod) switch (AlertStr(NEW_TOC_ALRT,Caution,spec->name)) { case TOC_CREATE_NEW: return(RebuildTOC(spec,nil,resource)); break; case CANCEL_ITEM: case TOC_CANCEL: return(nil); break; default: AFSpSetMod(spec,mod); break; } #endif return(ReadTOC(spec,resource)); } /********************************************************************** * TOCDates - get the dates off a mailbox **********************************************************************/ OSErr TOCDates(FSSpecPtr spec, uLong *box, uLong *res, uLong *file) { FSSpec tocSpec; TOCType partTOC; OSErr err; long boxSize = FSpDFSize(spec); *box = AFSpGetMod(spec); Box2TOCSpec(spec,&tocSpec); *file = AFSpGetMod(&tocSpec); if (!(err=PeekRTOC(spec,&partTOC))) { *res = partTOC.writeDate; if (boxSize==partTOC.boxSize-1) *res = *box; // if the sizes match, forget the date if (!*res) { // somebody didn't fill in writeDate if (*file) ;// we have a file-based toc. Let's assume it has the good info else *res = *box-1; // no date in resource toc; might be bad } } else *res = 0; if (err && boxSize>0) ComposeLogS(LOG_TOC|LOG_ALRT,nil,"\pTOCDates(%p): %d",spec->name,err); return(err); } /************************************************************************ * Box2TOCSpec - make a toc spec out of a mailbox spec ************************************************************************/ FSSpecPtr Box2TOCSpec(FSSpecPtr boxSpec,FSSpecPtr tocSpec) { Str31 suffix; *tocSpec = *boxSpec; PCat(tocSpec->name,GetRString(suffix,TOC_SUFFIX)); return(tocSpec); } /********************************************************************** * ReadTOC - read the toc file for a mailbox **********************************************************************/ TOCHandle ReadTOC(FSSpecPtr spec, Boolean resource) { TOCHandle tocH=nil; OSErr insane = noErr; insane = resource ? ReadRForkTOC(spec,&tocH) : ReadDForkTOC(spec,&tocH); if (tocH) { /* * don't take these for granted... */ (*tocH)->durty = (*tocH)->reallyDirty = False; (*tocH)->mailbox.spec = *spec; (*tocH)->refN = 0; (*tocH)->win = nil; (*tocH)->volumeFree = 0; (*tocH)->previewID = 0; (*tocH)->previewPTE = 0; (*tocH)->lastSameTicks = 0; (*tocH)->mouseTicks = 0; (*tocH)->mouseSpot.h = (*tocH)->mouseSpot.v = 0; (*tocH)->userActive = 0; (*tocH)->drawerWin = 0; #ifdef IMAP LDRef(tocH); (*tocH)->imapTOC = IsIMAPMailboxFileLo(spec, &((*tocH)->imapMBH))?1:0; UL(tocH); #endif /* * and make sure it hasn't become special or unspecial */ (*tocH)->which = GetMailboxType(spec); /* * make sure we don't have any leftovers */ CleanseTOC(tocH); /* * check toc for reasonableness */ if (insane=InsaneTOC(tocH)) return(FixErrantTOC(spec,tocH,insane)); /* * and sizes */ GetTOCK(tocH,&(*tocH)->usedK,&(*tocH)->totalK); (*tocH)->updateBoxSizes = true; /* * check unread status */ (*tocH)->unread = TOCUnread(tocH); /* * hurray for our side! */ UL(tocH); return(tocH); } else if (CommandPeriod || insane==memFullErr) return(nil); else return (FixErrantTOC(spec,nil,euCorruptTOC)); } /********************************************************************** * **********************************************************************/ OSErr ReadDForkTOC(FSSpecPtr aSpec,TOCHandle *inTOC) { long count; int err=noErr; short refN=0; TOCHandle tocH=nil; FSSpec tocSpec; /* * toc is kept in name.toc; try to open it */ Box2TOCSpec(aSpec,&tocSpec); if (!(err=AFSpOpenDF(&tocSpec,&tocSpec,fsRdPerm,&refN))) { /* * allocate space for the toc */ if (!(err = GetEOF(refN, &count))) if (count < sizeof(TOCType)) err = sizeof(TOCType)-count; else if (tocH = NuHTempOK(count)) { /* * try to read it */ LDRef(tocH); err = ARead(refN,&count,(void*)*tocH); (void) MyFSClose(refN), refN = 0; if (err) ZapHandle(tocH); } else { MyFSClose(refN); WarnUser(MEM_ERR,err=MemError()); } } if (err) FileSystemError(READ_TOC,aSpec->name,err); *inTOC = tocH; ComposeLogS(err ? LOG_ALRT|LOG_TOC : LOG_TOC,nil,"\pReadDForkTOC(%p): %d",aSpec->name,err); return(err); } /********************************************************************** * ReadRForkTOC - read a TOC from the resource fork **********************************************************************/ OSErr ReadRForkTOC(FSSpecPtr aSpec,TOCHandle *inTOC) { short refN=0; Handle r; TOCHandle tocH=nil; long size; OSErr err; FSSpec lSpec = *aSpec; short oldResF = CurResFile(); IsAlias(aSpec,&lSpec); if (-1!=(refN=FSpOpenResFile(&lSpec,fsRdPerm))) { SetResLoad(False); r = Get1Resource(TOC_TYPE,1001); if (!r) r = Get1Resource(TOC_TYPE,1002); SetResLoad(True); if (r) { size = GetResourceSizeOnDisk(r); if (err = ResError()) FileSystemError(CORRUPT_RFORK,&lSpec.name,err); else { tocH = NuHTempOK(size); if (!tocH) WarnUser(MEM_ERR,err=MemError()); else { ReadPartialResource(r,0,LDRef(tocH),size); if (err = ResError()) FileSystemError(CORRUPT_RFORK,&lSpec.name,err); } } } else FileSystemError(CORRUPT_RFORK,&lSpec.name,resNotFound); } if (refN) CloseResFile(refN); if (err) ZapHandle(tocH); ComposeLogS(err ? LOG_ALRT|LOG_TOC : LOG_TOC,nil,"\pReadRForkTOC(%p): %d",aSpec->name,err); *inTOC = tocH; UseResFile (oldResF); return(err); } /********************************************************************** * HasExternalTOC - does a file have an external table of contents? **********************************************************************/ Boolean HasExternalTOC(FSSpecPtr spec) { FSSpec tocSpec; Box2TOCSpec(spec,&tocSpec); return(noErr==FSpExists(&tocSpec)); } /********************************************************************** * WriteTOC - write a toc to the proper file **********************************************************************/ int WriteTOC(TOCHandle tocH) { int err; short refN=0; long count; FSSpec tocSpec, spec; uLong writeDate = LocalDateTime() & ~(1L); /* make it even because of idiot novell */ uLong size; uLong deadTicks; // Avoid multiple simultaneous writes ASSERT(!(*tocH)->beingWritten); deadTicks = TickCount(); while ((*tocH)->beingWritten && (TickCount()-deadTicks< 5*60)) // wait up to 5 seconds { MyYieldToAnyThread(); MiniEvents(); } ASSERT(!(*tocH)->beingWritten); if ((*tocH)->beingWritten++) { // Oh, sxxt. Somebody either left the beingWritten flag set, or there's a verrrrrrryyyyy long // write going on. (*tocH)->beingWritten = 1; // rest the counter and hope for the best } // Save this dainty little morsel if ((*tocH)->which==OUT) BMD(&NoAdsRec,&(*tocH)->internalUseOnly,sizeof((*tocH)->internalUseOnly)); spec = GetMailboxSpec(tocH,-1); size = FSpDFSize(&spec); (*tocH)->boxSize = size+1; /* add 1 to signal that we know it's ok */ (*tocH)->writeDate = writeDate; if (PrefIsSet(PREF_NEW_TOC)) err = WriteRForkTOC(tocH); LDRef(tocH); /* * build the name */ Box2TOCSpec(&spec,&tocSpec); if (PrefIsSet(PREF_NEW_TOC)) { if (!err) { ChainDelete(&tocSpec); AFSpSetMod(&spec,writeDate); } } else { /* * open the file */ err = AFSpOpenDF(&tocSpec,&tocSpec,fsRdWrPerm,&refN); if (err == fnfErr) { if (err=FSpCreate(&tocSpec,CREATOR,TOC_TYPE,smSystemScript)) goto failure; err = FSpOpenDF(&tocSpec,fsRdWrPerm,&refN); } if (err) goto failure; /* * write the file */ count = GetHandleSize_(tocH); (*tocH)->unreadBase = (*tocH)->count; if (err = AWrite(refN,&count,(void*)*tocH)) goto failure; /* * fix up menu items */ FixBoxUnread(tocH); /* * done */ GetFPos(refN,&count); SetEOF(refN,count); (void) MyFSClose(refN); (*tocH)->durty = (*tocH)->reallyDirty = False; AFSpSetMod(&tocSpec,writeDate); AFSpSetMod(&spec,writeDate); ComposeLogS(LOG_FLOW,nil,"\pWriteTOC %p\015",tocSpec.name); UL(tocH); /* * the size area */ if ((*tocH)->win&&IsWindowVisible(GetMyWindowWindowPtr((*tocH)->win))) InvalBoxSizeBox((*tocH)->win); (*tocH)->beingWritten--; ComposeLogS(err ? LOG_ALRT|LOG_TOC : LOG_TOC,nil,"\pWriteTOC(%p): %d",tocSpec.name,err); return(noErr); failure: ComposeLogS(err ? LOG_ALRT|LOG_TOC : LOG_TOC,nil,"\pWriteTOC(%p): %d",tocSpec.name,err); FileSystemError(WRITE_TOC,tocSpec.name,err); UL(tocH); if (refN) { /* get rid of partial file */ (void) MyFSClose(refN); (void) FSpDelete(&tocSpec); } } UL(tocH); (*tocH)->beingWritten--; return(err); } /************************************************************************ * FixBoxUnread - fix the unread status of a mailbox in the menus ************************************************************************/ void FixBoxUnread(TOCHandle tocH) { Boolean unread=TOCUnread(tocH); short myMenu; short myItem; FSSpec spec; uLong total, used; (*tocH)->unread = unread; (*tocH)->unreadBase = (*tocH)->count; myItem = 0; spec = GetMailboxSpec(tocH,-1); Spec2Menu(&spec,False,&myMenu,&myItem); if (myItem>0) { FixSpecUnread(&spec,unread); FixMenuUnread(GetMHandle(myMenu),myItem,unread); } GetTOCK(tocH,&used,&total); if ((*tocH)->usedK!=used || (*tocH)->totalK!=total) { (*tocH)->usedK = used; (*tocH)->totalK = total; (*tocH)->updateBoxSizes = true; } } /********************************************************************** * WriteRForkTOC - write a table of contents to the resource fork of a file **********************************************************************/ OSErr WriteRForkTOC(TOCHandle tocH) { FSSpec spec = GetMailboxSpec(tocH,-1); short refN; OSErr err; Boolean sane; short oldResF = CurResFile(); IsAlias(&spec,&spec); if (FSpRFSane(&spec,&sane) || !sane) FSpKillRFork(&spec); FSpCreateResFile(&spec,CREATOR,'TEXT',smSystemScript); if (-1!=(refN=FSpOpenResFile(&spec,fsRdWrPerm))) { KillTOC(refN,nil); (*tocH)->unreadBase = (*tocH)->count; AddResource((Handle)tocH,TOC_TYPE,1001,""); err = ResError(); if (!err) MyUpdateResFile(refN); DetachResource((Handle)tocH); CloseResFile(refN); } else err = ResError(); if (err) FileSystemError(WRITE_TOC,spec.name,err); else { /* * fix up menu items */ FixBoxUnread(tocH); /* * the size area */ if ((*tocH)->win&&IsWindowVisible(GetMyWindowWindowPtr((*tocH)->win))) InvalBoxSizeBox((*tocH)->win); /* * done */ (*tocH)->durty = (*tocH)->reallyDirty = False; } UseResFile (oldResF); ComposeLogS(err ? LOG_ALRT|LOG_TOC : LOG_TOC,nil,"\pWriteRForkTOC(%p): %d",spec.name,err); return(err); } /********************************************************************** * TOCUnread - does a table of contents contain unread messages? **********************************************************************/ Boolean TOCUnread(TOCHandle tocH) { Boolean unread = False; uLong minDate; short myItem; // don't underline Junk if the user has asked us not to. if ((((*tocH)->which==JUNK) || (IsIMAPJunkMailbox(TOCToMbox(tocH)))) && JunkPrefBoxNoUnread()) return false; minDate = GetRLong(UNREAD_LIMIT)*24*3600; if (minDate) minDate = GMTDateTime()-minDate; for (myItem=0;myItem<(*tocH)->count;myItem++) if ((*tocH)->sums[myItem].state==UNREAD && (!minDate || (*tocH)->sums[myItem].seconds>minDate)) { unread = True; break; } return(unread); } /************************************************************************ * TOCUnreadCount - how many messages are unread in this toc? ************************************************************************/ short TOCUnreadCount(TOCHandle tocH,Boolean recentOnly) { short count = 0; short i; uLong minDate; if (!tocH) return 0; if (recentOnly) { minDate = GetRLong(UNREAD_LIMIT)*24*3600; if (minDate) minDate = GMTDateTime()-minDate; } if (recentOnly && minDate) { for (i=(*tocH)->count-1;i>=0;i--) if ((*tocH)->sums[i].state==UNREAD && (*tocH)->sums[i].seconds>minDate) count++; } else { for (i=(*tocH)->count-1;i>=0;i--) if ((*tocH)->sums[i].state==UNREAD) count++; } return count; } /********************************************************************** * FindTOC - find a TOC in the TOC window list **********************************************************************/ TOCHandle FindTOC(FSSpecPtr spec) { TOCHandle tocH; FSSpec boxSpec; FSSpec newSpec; for (tocH=TOCList; tocH; tocH = (*tocH)->next) { boxSpec = GetMailboxSpec(tocH,-1); if (SameSpec(&boxSpec,spec)) break; if (IsAlias(&boxSpec,&boxSpec) && SameSpec(&boxSpec,spec)) break; } if (!tocH && IsAlias(spec,&newSpec) && !SameSpec(spec,&newSpec)) return(FindTOC(&newSpec)); else return(tocH); } /************************************************************************ * FlushTOCs - make sure all toc's are quiescent ************************************************************************/ int FlushTOCs(Boolean andClose,Boolean canSkip) { TOCHandle tocH, nextTocH; WindowPtr tocWinWP; int shdBe; short err; static long lastTime; static short delay; Boolean dontCloseIMAPToc; #ifdef THREADING_ON if (GetNumBackgroundThreads()) // thread might be using toc, so don't close 'em return (noErr); #endif if (canSkip && lastTime && TickCount()-lastTime < delay) return(noErr); err = 0; for (tocH=TOCList; tocH; tocH = nextTocH) { if ((long)*tocH & 1) { #ifdef DEBUG if (RunType != Production) DebugStr("\pbad tocH!"); #endif DEBUG if (tocH==TOCList) TOCList = nil; else { for (nextTocH=TOCList; (*nextTocH)->next!=tocH;nextTocH=(*nextTocH)->next); (*nextTocH)->next = nil; } break; } shdBe = TOCSizeShouldBe(tocH); if (GetHandleSize_(tocH)!=shdBe) { #ifdef DEBUG if (RunType != Production) { Str127 debug; Str32 name; GetMailboxName(tocH,-1,name); ComposeString(debug,"\p%p: size %d, should be %d (%d + (%d-1)*%d)", name,GetHandleSize_(tocH),shdBe,sizeof(TOCType),(*tocH)->count, sizeof(MSumType)); SysBreakStr(debug); } #endif DEBUG SetHandleBig_(tocH,shdBe); } nextTocH = (*tocH)->next; if (err=BoxFClose(tocH,true)) break; if ((*tocH)->reallyDirty) { if (err=WriteTOC(tocH)) break; } else if ((*tocH)->unreadBase!=(*tocH)->count) FixBoxUnread(tocH); tocWinWP = GetMyWindowWindowPtr((*tocH)->win); // Make sure the toc we're about to close isn't an IMAP toc that's in use somewhere ... if (andClose) dontCloseIMAPToc = (*tocH)->imapTOC && IMAPTOCInUse(TOCToMbox(tocH)); if (andClose && !IsWindowVisibleClassic(tocWinWP) && !(MyNMRec && (*tocH)->which==IN) && !dontCloseIMAPToc) { int sNum; for (sNum=0;sNum<(*tocH)->count;sNum++) if ((*tocH)->sums[sNum].messH) break; if (sNum==(*tocH)->count) CloseMyWindow(tocWinWP); } if (NEED_YIELD) break; } if (err) { lastTime = TickCount(); if (!delay) delay = 60*30; else delay = MIN(60*60*5,(delay*3)/2); } else lastTime = delay = 0; return(err); } /************************************************************************ * GetSpecialTOC - find the toc of a special mailbox ************************************************************************/ TOCHandle GetSpecialTOC(short nameId) { FSSpec spec; Str31 name; GetRString(name,nameId); #ifdef THREADING_ON if (nameId == IN_TEMP || nameId == OUT_TEMP) { OSErr err; FSSpec spool; long dirID; err=SubFolderSpec(SPOOL_FOLDER,&spool); if (!err) { dirID = SpecDirId(&spool); err = FSMakeFSSpec(spool.vRefNum,spool.parID,name,&spec); } ASSERT (!err); if (err) return nil; } else #endif FSMakeFSSpec(MailRoot.vRef,MailRoot.dirId,name,&spec); return(TOCBySpec(&spec)); } /********************************************************************** * PeekRTOC - peek at a resource fork toc **********************************************************************/ OSErr PeekRTOC(FSSpecPtr spec,TOCType *tocPart) { FSSpec tocSpec; Boolean sane; OSErr err; Handle res; short refN; short oldResF = CurResFile(); Boolean emptyBox = FSpDFSize(spec)==0; IsAlias(spec,&tocSpec); if (err=FSpRFSane(&tocSpec,&sane)) ComposeLogS(LOG_TOC|LOG_ALRT,nil,"\pFSpRFSane(%p): %d",spec->name,err); else { if (!sane) { ComposeLogS(LOG_TOC|LOG_ALRT,nil,"\pTOC %p insane",spec->name); err = fnfErr; } else if (-1!=(refN=FSpOpenResFile(&tocSpec,fsRdWrPerm))) { SetResLoad(False); res = Get1Resource(TOC_TYPE,1001); SetResLoad(True); if (!res && !emptyBox) ComposeLogS(LOG_TOC|LOG_ALRT,nil,"\p%p primary toc missing",spec->name); if (!res) { SetResLoad(False); res = Get1Resource(TOC_TYPE,1002); SetResLoad(True); if (!res && !emptyBox) ComposeLogS(LOG_TOC|LOG_ALRT,nil,"\p%p secondary toc missing",spec->name); } if (!res) err = fnfErr; else { ReadPartialResource(res,0,tocPart,sizeof(TOCType)); err = ResError(); if (err) ComposeLogS(LOG_TOC|LOG_ALRT,nil,"\pReadPartialResource(%p): %d",spec->name,err); } CloseResFile(refN); } else { ComposeLogS(LOG_TOC|LOG_ALRT,nil,"\p%p can't open resource fork",spec->name); err = fnfErr; } } UseResFile (oldResF); return(err); } #ifdef BATCH_DELIVERY_ON //NEVER /********************************************************************** * PeekDTOC - peek at a data fork toc **********************************************************************/ OSErr PeekDTOC(FSSpecPtr spec,TOCType *tocPart) { FSSpec tocSpec; OSErr err; short refN; long bytes; Box2TOCSpec(spec,&tocSpec); if (!(err=AFSpOpenDF(&tocSpec,&tocSpec,fsRdPerm,&refN))) { bytes = sizeof(TOCType); err = FSRead(refN,&bytes,tocPart); if (!err && bytes!=sizeof(TOCType)) err = eofErr; // close it FSClose(refN); } return(err); } #endif /********************************************************************** * PeekTOC - peek into a .toc file **********************************************************************/ OSErr PeekTOC(FSSpecPtr spec,TOCType *tocPart) { uLong box, file, res; OSErr err; TOCHandle tocH = FindTOC(spec); if (tocH) // already open; I hope the caller is being careful { *tocPart = **tocH; return(noErr); } err = TOCDates(spec,&box,&res,&file); if (err) return(err); if (res && res>=file) return(PeekRTOC(spec,tocPart)); else if (file) return(PeekDTOC(spec,tocPart)); else return(fnfErr); } /************************************************************************ * InsaneTOC - see if a TOC is nuts ************************************************************************/ OSErr InsaneTOC(TOCHandle tocH) { long boxSize; MSumPtr start,stop; Str31 name; short tocSize = sizeof(TOCType); short sumSize = sizeof(MSumType); long handleSize = GetHandleSize_(tocH); FSSpec spec = GetMailboxSpec(tocH,-1); PCopy(name,spec.name); /* * is the toc the right size? */ if ((*tocH)->count<0) return(True); if (sizeof(TOCType)+((*tocH)->count?(*tocH)->count-1:0)*sizeof(MSumType)!= GetHandleSize_(tocH)) { ComposeLogS(LOG_ALRT|LOG_TOC,nil,"\p%p toc/count size mismatch; #%d != %d",name, (*tocH)->count,GetHandleSize_(tocH)); return(euCorruptTOC); } /* * figure out how big the mailbox is */ boxSize = FSpDFSize(&spec); /* * right size? */ if ((*tocH)->boxSize && (*tocH)->boxSize-1!=boxSize) { ComposeLogS(LOG_ALRT|LOG_TOC,nil,"\p%p file size mismatch; #%d != %d",name, (*tocH)->boxSize,boxSize); return(euMismatchTOC); } /* * right date? */ // don't do the date check anymore, since it breaks under HFS+ //if ((*tocH)->writeDate && (*tocH)->writeDate!=AFSpGetMod(&spec)) return(euMismatchTOC); /* * check for out of range pointers */ for (start=(*tocH)->sums,stop=start+(*tocH)->count;startoffset<0&&!(*tocH)->imapTOC) ||start->length<0 ||start->bodyOffset<0 || start->bodyOffset>start->length || ((start->offset+start->length > boxSize)&&!(*tocH)->imapTOC)) { ComposeLogS(LOG_ALRT|LOG_TOC,nil,"\p%p bad sum; o%d b%d l%d s%d",name, start->offset,start->bodyOffset,start->length,boxSize); return(euCorruptTOC); } } /* * wrong version number? */ if ((*tocH)->version > CURRENT_TOC_VERS) { ComposeLogS(LOG_ALRT|LOG_TOC,nil,"\p%p version mismatch; #%d != %d",name, (*tocH)->version,CURRENT_TOC_VERS); return(euBadVersion); } /* * everything looks ok */ return(noErr); } /************************************************************************ * GetMailboxType ************************************************************************/ short GetMailboxType(FSSpecPtr spec) { short which = 0; if (IsRoot(spec)) { if (EqualStrRes(spec->name,IN)) which = IN; else if (EqualStrRes(spec->name,OUT)) which = OUT; else if (EqualStrRes(spec->name,TRASH)) which = TRASH; else if (EqualStrRes(spec->name,JUNK)) which = JUNK; } #ifdef THREADING_ON else if (IsSpool(spec)) { if (EqualStrRes(spec->name,IN_TEMP)) which = IN_TEMP; else if (EqualStrRes(spec->name,OUT_TEMP)) which = OUT_TEMP; } #ifdef BATCH_DELIVERY_ON else if (IsDelivery(spec)) which = DELIVERY_BATCH; #endif #endif return which; } /********************************************************************** * FixErrantTOC - we have determined that something is wrong with a TOC * fix or cancel **********************************************************************/ TOCHandle FixErrantTOC(FSSpecPtr spec,TOCHandle tocH,short why) { short result = -1; #ifdef THREADING_ON // if we're any of the temp tocs, automatically rebuild them short which = GetMailboxType(spec); if (which == IN_TEMP || which == OUT_TEMP || which == DELIVERY_BATCH ) return(RebuildTOC(spec,tocH,False,True)); #endif switch (result = WantRebuildTOC(spec->name,why,((*tocH)->imapTOC!=0))) { case 0: if (tocH) { TOCSetDirty(tocH,true); (*tocH)->reallyDirty = True; /* see that it gets updated */ } return(tocH); case 1: return(RebuildTOC(spec,tocH,False, False)); case 2: ZapHandle(tocH); return(nil); } ASSERT (result != -1); return nil; } /************************************************************************ * WantRebuildTOC - see if the user wants us to remake the toc ************************************************************************/ Boolean WantRebuildTOC(UPtr boxName,OSErr why, Boolean isIMAP) { if (!PrefIsSet(PREF_TOC_REBUILD_ALERTS)) return true; if (why==euMismatchTOC) { short whatToDo; if (isIMAP) whatToDo = AlertStr(NEW_IMAP_TOC_ALRT,Caution,boxName); else { // "Encourage" users to rebuild if (TOC_USE_OLD==(whatToDo = AlertStr(NEW_TOC_ALRT,Caution,boxName)) && TOC_USE_OLD==(whatToDo = ComposeStdAlert(Caution,REBUILD_TOC_ALRT_2,boxName))) whatToDo = ComposeStdAlert(Stop,REBUILD_TOC_ALRT_3,boxName); } switch (whatToDo) { case TOC_CREATE_NEW: return(1); case TOC_USE_OLD: return(0); case CANCEL_ITEM: case TOC_CANCEL: return(2); } } else switch (AlertStr(REB_TOC_ALRT,Caution,boxName)) { case 1: return(1); default: return(2); } ASSERT ( false ); return true; } /************************************************************************ * GetTOCK - grab the K counts for a mailbox ************************************************************************/ short GetTOCK(TOCHandle tocH,uLong *usedK, uLong *totalK) { MSumPtr sum; long used=0; short err; CInfoPBRec info; FSSpec spec; for (sum=(*tocH)->sums;sum<(*tocH)->sums+(*tocH)->count;sum++) { if (sum->offset >= 0) used += sum->length; } *usedK = used/1024; LDRef(tocH); spec = GetMailboxSpec(tocH,-1); *totalK = (err=AFSpGetHFileInfo(&spec,&info)) ? 0:info.hFileInfo.ioFlLgLen/1024; UL(tocH); ASSERT(*totalK<4096 K); return(err); } /************************************************************************ * CheckStringLen - make sure string field is not too long ************************************************************************/ void CheckStringLen(UPtr s, char maxLen, short fillLen) { if (*s > fillLen) { // total junk; destroy WriteZero(s,fillLen); } else if (*s>maxLen) { // oops, too long WriteZero(s+maxLen+1,fillLen-maxLen-1); // zero out remainder *s = maxLen; } } /************************************************************************ * IsTOCValid - is the toc on the list of currently-opened toc's ************************************************************************/ TOCHandle IsTOCValid(TOCHandle testTOC) { TOCHandle tocH; for (tocH=TOCList;tocH;tocH=(*tocH)->next) if (tocH==testTOC) return(tocH); return(nil); } /************************************************************************ * CleanseTOC - free a newly-read toc of vestiges of its past life ************************************************************************/ void CleanseTOC(TOCHandle tocH) { short count, i; MSumPtr sum; Byte vers = (*tocH)->version; short minor = (*tocH)->minorVersion; long size = GetHandleSize((Handle)tocH); Boolean needSerialNum = vers < 1 || (vers==1 && minor<2); Boolean zeroMood = vers < 1 || (vers==1 && minornextSerialNum = 1; /* * unset the selected flags; they might have been saved from last time * unset the messH; it may have been filled the last time */ count = TOCCountShouldBe(tocH); count = MIN((*tocH)->count,count); for (i=0;isums+i; sum->mesgErrH = 0; sum->messH = 0; // let's see about restoring the selection... */ sum->selected = False; sum->cache = 0; if (vers<1) { DBNoteUIDHash(sum->uidHash,kNeverHashed); sum->uidHash = sum->msgIdHash = kNeverHashed; } if (minor<1) sum->msgIdHash = kNeverHashed; if (zeroMood) sum->score = 0; // make sure older versions don't have strings that are too long CheckStringLen(sum->from, sizeof(sum->from)-1,64); CheckStringLen(sum->subj, sizeof(sum->subj)-1,64); if ((*tocH)->minorVersion < TOC_MINOR_NO_DATE_STRING) { short j; sum->spamScore = -1; sum->spamBecause = 0; sum->spare21 = 0; sum->arrivalSeconds = 0; for (j=0;jspare)/sizeof(sum->spare[0]);j++) sum->spare[j] = 0; } if (fixOutlookisms) { PSCopy(subj,sum->subj); BeautifySubj(subj,sizeof(sum->subj)); PSCopy(sum->subj,subj); } if (fixUTF8) RemoveUTF8FromSum(sum); if (!sum->arrivalSeconds) sum->arrivalSeconds = sum->seconds; if (needSerialNum) sum->serialNum = (*tocH)->nextSerialNum++; } if ((*tocH)->minorVersionoldKValues = 0; if ((*tocH)->minorVersionprofile[0] = (*tocH)->profile[1] = 0; if ((*tocH)->minorVersion < TOC_MINOR_NO_DATE_STRING) (*tocH)->spareLong = 0; (*tocH)->version = CURRENT_TOC_VERS; (*tocH)->minorVersion = CURRENT_TOC_MINOR; (*tocH)->virtualTOC = false; // not a virtual toc, nossir! (*tocH)->beingWritten = false; // not being written at the moment (*tocH)->analScanned = false; // need to do a scan for flames (*tocH)->hFileView = nil; // Junk processing if ((*tocH)->which==JUNK) JunkTOCCleanse(tocH); } #pragma segment Main /************************************************************************ * RedoTOCs - make sure right info is displayed in all TOC's ************************************************************************/ void RedoTOCs(void) { TOCHandle tocH = TOCList; overAgain: for (tocH=TOCList; tocH && !NeedYield(); tocH = (*tocH)->next) // only do this for visible windows if (IsWindowVisible(GetMyWindowWindowPtr((*tocH)->win)) && RedoTOC(tocH)) { // we just did some work. if (NeedYield()) break; // need to go process events goto overAgain; // take it from the top; trying to avoid // race condition with threading. } }