eudora-mac/filtmng.c

1 line
21 KiB
C
Executable File

/* 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. */
#include "filtmng.h"
/************************************************************************
* Filters window - Copyright (C) 1994 QUALCOMM Incorporated
************************************************************************/
#define FILE_NUM 68
#ifdef TWO
#pragma segment FiltMng
OSErr GetFilterLine(FilterKeywordEnum *key,PStr value,LineIOP lip);
OSErr FilterVerb(PStr s, MatchEnum *mbm);
OSErr FilterConj(PStr s, ConjunctionEnum *cj);
OSErr WriteFilter(short refN,FRPtr fr);
void FUWeed(void);
short FindFilterById(long id);
short FilterCount(Handle hFilters);
extern OSErr FGlobalErr;
/**********************************************************************
* FindFilterById - find which filter has the given id
**********************************************************************/
short FindFilterById(long id)
{
short nf;
for (nf=NFilters-1;nf>=0;nf--)
if (FR[nf].fu.id==id) break;
return(nf);
}
/**********************************************************************
* FUWeed - weed out filter use records for non-existent filters
**********************************************************************/
void FUWeed(void)
{
short nf = NFilters;
FUHandle fuh = GetResource_(FU_TYPE,FU_ID);
FUPtr fup, fuEnd;
OSErr err;
/*
* more use records than filters?
*/
if (fuh && GetHandleSize_(fuh)/sizeof(FilterUse)>nf)
{
HNoPurge_(fuh);
err = fnfErr;
fup = (FUPtr)LDRef(fuh);
fuEnd = fup+GetHandleSize_(fuh)/sizeof(FilterUse);
for (fup=fuEnd;fup>=*fuh;fup--)
{
nf=FindFilterById(fup->id);
/*
* didn't find it
*/
if (nf<0)
{
/* move others down */
if (fuEnd-fup>1) BMD(fup+1,fup,(fuEnd-fup-1)*sizeof(FilterUse));
/* shrink handle */
SetHandleBig_(fuh,GetHandleSize_(fuh)-sizeof(FilterUse));
/* watch the end */
fuEnd--;
/* mark as changed */
ChangedResource((Handle)fuh);
}
}
UL(fuh);
PurgeIfClean(fuh);
}
}
/**********************************************************************
* FilterNewId - get a new id for a filter.
**********************************************************************/
long FilterNewId(void)
{
short f;
long id = 1;
for (f=NFilters;f;)
{
f--;
if (id<=FR[f].fu.id) id = FR[f].fu.id+1;
}
return(id);
}
/**********************************************************************
* GeneratePluginFilters - generate plugin filters
**********************************************************************/
void GeneratePluginFilters(void)
{
if (!PreFilters && !PostFilters)
{
Str31 name;
FSSpec folderSpec;
CInfoPBRec hfi;
long dirID;
Handle *pFilterSet;
#define TYPE hfi.hFileInfo.ioFlFndrInfo.fdType
// Load any plug-in filters
ETLGetPluginFolderSpec(&folderSpec,PLUGIN_FILTERS);
dirID = SpecDirId(&folderSpec);
// iterate through all the plugin filter files
hfi.hFileInfo.ioNamePtr = name;
hfi.hFileInfo.ioFDirIndex = 0;
while (!DirIterate(folderSpec.vRefNum,dirID,&hfi))
{
pFilterSet = (TYPE==PRE_FILTER_TYPE || TYPE=='TEXT' && EndsWithR(name,PREFILTER_SUFFIX)) ? &PreFilters :
(TYPE==POST_FILTER_TYPE || TYPE=='TEXT' && EndsWithR(name,POSTFILTER_SUFFIX)) ? &PostFilters : nil;
if (pFilterSet)
{
if (!*pFilterSet) *pFilterSet = NuHandle(0);
if (*pFilterSet) ReadFilters(*pFilterSet,folderSpec.vRefNum,dirID,name);
}
}
FiltersRefCount++;
}
}
/************************************************************************
* RegnerateFilters - read the filters list
************************************************************************/
OSErr RegenerateFilters(void)
{
short err = noErr;
Str255 s;
if (!Filters || !*Filters)
{
if (Filters) ZapHandle(Filters);
Filters = NuHandle(0);
if (!Filters)
{
WarnUser(READ_FILTERS,err=MemError());
return(err);
}
ProgressR(NoBar,NoBar,0,READING_FILTERS,"");
err = ReadFilters(Filters,Root.vRef,Root.dirId,GetRString(s,FILTERS_NAME));
HNoPurge_(Filters);
if (!err)
GeneratePluginFilters();
}
if (!err)
{
short nf;
short t;
FiltersRefCount++;
for (nf=NFilters-1;nf>=0;nf--)
{
for (t=0;t<2;t++)
{
ZapHandle(FR[nf].terms[t].nickExpanded);
ZapHandle(FR[nf].terms[t].nickAddresses);
ZapHandle(FR[nf].terms[t].regex);
}
}
}
else
ZapFilters();
FGlobalErr = err;
return(err);
}
/************************************************************************
* ReadFilters - read the filters database
************************************************************************/
OSErr ReadFilters(Handle hFilters,short vRef,long dirId,StringPtr name)
{
short err;
FilterRecord fr;
FilterKeywordEnum key;
Str255 s;
short term=0;
LineIOD lid;
FActionProc *fap;
FActionHandle fa=nil;
Boolean proOnlyKeywordFound = false;
Zero(lid);
FRInit(&fr);
err = OpenLine(vRef,dirId,name,fsRdWrPerm,&lid);
if (err==fnfErr) return(noErr); /* vaccuous success */
else if (err) return(FileSystemError(READ_FILTERS,s,err));
while(!(err=GetFilterLine(&key,s,&lid)))
{
//Enhanced Filters - warn user ONCE if the filters file contains a Pro-only keyword
if (!HasFeature (featureFilters) && !proOnlyKeywordFound && FAProOnly(key)) proOnlyKeywordFound = true;
/*
* prescan for obsolete keys
*/
switch(key)
{
case flkRaise:
PCopy(s,"\p7");
key = flkPriority;
break;
case flkLower:
PCopy(s,"\p8");
key = flkPriority;
break;
}
/*
* look for actions
*/
if (fap = FATable(key))
{
if (!(fa=NewZH(FAction)))
{
WarnUser(READ_FILTERS,err=MemError());
goto done;
}
(*fa)->action = key;
if (err = (*fap)(faeRead,fa,nil,s)) goto done;
LL_Queue(fr.actions,fa,(FActionHandle));
fa = nil;
}
/*
* nope; other stuff
*/
else
{
switch(key)
{
case flkRule:
if (*fr.name && (err=AppendFilter(&fr,hFilters))) goto done;
FRInit(&fr);
term = 0;
PSCopy(fr.name,s);
break;
case flkIncoming:
fr.incoming = True;
break;
case flkOutgoing:
fr.outgoing = True;
break;
case flkManual:
fr.manual = True;
break;
case flkCopyInstead:
for (fa=fr.actions;fa && (*fa)->next;fa = (*fa)->next);
if (fa && (*fa)->action==flkTransfer) (*fa)->action=flkCopy;
break;
case flkId:
StringToNum(s,&fr.fu.id);
break;
case flkHeader:
if (EqualStrRes(s,FILTER_ADDRESSEE_OLD)) GetRString(s,FILTER_ADDRESSEE);
PSCopy(fr.terms[term].header,s);
break;
case flkVerb:
err = FilterVerb(s,&fr.terms[term].verb);
break;
case flkValue:
PSCopy(fr.terms[term].value,s);
break;
case flkConjunction:
err = FilterConj(s,&fr.conjunction);
if (term) WarnUser(FILTER_OVERTERM,0);
else term++;
break;
}
}
}
if (*fr.name) err = AppendFilter(&fr,hFilters);
fa = fr.actions = nil; /* success! */
done:
ZapFAction(fa);
ZapActions(fr.actions);
CloseLine(&lid);
//Enhanced Filters - warn user if the filters file contains a Pro-only keyword
if (proOnlyKeywordFound&&(!PrefIsSet(PREF_PRO_FILT_WARN))&&hFilters==Filters) Wife(0, PREF_PRO_FILT_WARN, PRO_FILT_WARNING);
return(err==eofErr ? noErr : err);
}
/**********************************************************************
* AppendFilter - add a filter to the list
**********************************************************************/
OSErr AppendFilter(FRPtr fr,Handle hFilters)
{
short na=0;
FActionHandle fa;
OSErr err=0;
StudyFilter(fr);
for (fa=fr->actions;fa;fa=(*fa)->next) na++;
/*
* fill out the actions
*/
while (na<MAX_ACTIONS)
{
fa = NewZH(FAction);
if (!fa) {err = MemError();break;}
(*fa)->action = flkNone;
LL_Queue(fr->actions,fa,(FActionHandle));
na++;
}
if (!err) err = PtrPlusHand_(fr,hFilters,sizeof(*fr));
if (err) WarnUser(READ_FILTERS,err);
return(err);
}
/**********************************************************************
* TellFiltMBRename - Tell filters that a mailbox name has changed
* When called with "will" true, the effect is to read filters in
* When called with "will" false, the filters are then updated
**********************************************************************/
OSErr TellFiltMBRename(FSSpecPtr spec,FSSpecPtr newSpec,Boolean folder,Boolean will,Boolean dontWarn)
{
short n;
Boolean care=False;
FActionHandle fa;
MBRenamePB rnPB;
FilterRenameEnum warn = GetPrefLong(PREF_MB_FILT_WARN);
OSErr err = noErr;
if (warn==fRenameIgnore) return noErr; // We don't care
if (RegenerateFilters()) return noErr;
if (!will)
{
/*
* Ok, now all the mailboxes are in FSSpec's in the actions.
* If we're renaming a folder, the FSSpec's are unaffected and we just
* have to make sure they get written out if we're using pathnames.
* If we have renamed a mailbox, still work to do.
*/
if (folder)
{
care = True;
warn = fRenameChange;
}
else
{
n = NFilters;
rnPB.oldSpec = *spec;
rnPB.newSpec = *newSpec;
if (warn==fRenameAsk)
{
care = False;
while (n-- && !care)
for (fa=FR[n].actions;!care && fa;fa=(*fa)->next)
care = 0!=CallAction(faeMBWillRename,fa,nil,&rnPB);
if (!care) return noErr;
}
}
if (care)
{
if (dontWarn) warn = fRenameChange;
else if (warn==fRenameAsk)
warn = ReallyDoAnAlert(FILT_MB_RENAME_ALRT,Note);
if (warn!=fRenameIgnore)
{
if (!folder)
{
n = NFilters;
while(n--)
for (fa=FR[n].actions;fa;fa=(*fa)->next)
CallAction(faeMBDidRename,fa,nil,&rnPB);
}
if (!FiltWinOpen()) SaveFilters();
else FilterWindowClean(False);
}
else
err = userCanceledErr;
}
FiltersDecRef();
}
return err;
}
/**********************************************************************
* StudyFilter - study a filter, hopefully to speed execution
**********************************************************************/
void StudyFilter(FRPtr fr)
{
short term;
for (term=0;term<sizeof(fr->terms)/sizeof(fr->terms[0]);term++)
{
if (EqualStrRes(fr->terms[term].header,FILTER_BODY))
fr->terms[term].headerID = FILTER_BODY;
else if (EqualStrRes(fr->terms[term].header,FILTER_ANY))
fr->terms[term].headerID = FILTER_ANY;
else if (EqualStrRes(fr->terms[term].header,FILTER_ADDRESSEE))
fr->terms[term].headerID = FILTER_ADDRESSEE;
else fr->terms[term].headerID = 0;
if (fr->terms[term].verb==mbmRegEx)
{
Str255 s;
PCopy(s,fr->terms[term].value);
PTerminate(s);
if (fr->terms[term].regex) ZapHandle(fr->terms[term].regex);
fr->terms[term].regex = regcomp(s+1);
}
}
}
/**********************************************************************
* DisposeFaction - dispose of a filter action
**********************************************************************/
void DisposeFAction(FActionHandle fa)
{
if (fa!=nil)
{
if ((*fa)->data!=nil) DisposeHandle((Handle)(*fa)->data);
DisposeHandle((Handle)fa);
}
}
/**********************************************************************
* ZapFilters - kill all filters
**********************************************************************/
void ZapFilters(void)
{
DisposeFilters(&Filters);
DisposeFilters(&PreFilters);
DisposeFilters(&PostFilters);
FiltersRefCount = 0;
}
/**********************************************************************
* DisposeFilters - dispose filters
**********************************************************************/
void DisposeFilters(Handle *hFilters)
{
if (*hFilters)
{
short i = FilterCount(*hFilters);
while (i--) ZapActions((*(FRHandle)*hFilters)[i].actions);
ZapHandle(*hFilters);
}
}
/**********************************************************************
* ZapActions - zap a list of filter actions
**********************************************************************/
void ZapActions(FActionHandle fa)
{
FActionHandle thisfa;
while (thisfa=fa)
{
LL_Remove(fa,thisfa,(FActionHandle));
DisposeFAction(thisfa);
}
}
/**********************************************************************
* FilterCount - return number of filters
**********************************************************************/
short FilterCount(Handle hFilters)
{
return hFilters ? GetHandleSize_(hFilters)/sizeof(FilterRecord) : 0;
}
/************************************************************************
* FRInit - intialize an FR
************************************************************************/
void FRInit(FRPtr fr)
{
WriteZero(fr,sizeof(*fr));
fr->conjunction = fr->terms[0].verb = fr->terms[1].verb = 1;
}
/************************************************************************
* FilterVerb - interpret a filter verb
************************************************************************/
OSErr FilterVerb(PStr s, MatchEnum *mbm)
{
if (!*s) return(1); /* error; no token */
*mbm = FindSTRNIndex(VERB_STRN,s);
if (!*mbm) return(1);
return(*mbm<mbmLimit ? noErr : 1);
}
/************************************************************************
* FilterConj - interpret a filter conjunction
************************************************************************/
OSErr FilterConj(PStr s, ConjunctionEnum *cj)
{
if (!*s) return(1); /* error; no token */
*cj = FindSTRNIndex(CONJ_STRN,s);
if (!*cj) return(1);
return(*cj<=cjUnless ? noErr : 1);
}
/************************************************************************
* FiltersDecRef - decrement the filters ref count, and make purgeable if 0
************************************************************************/
void FiltersDecRef(void)
{
short n;
if (FiltersRefCount) FiltersRefCount--;
if (!Filters) return;
/*
* get rid of cached info
*/
if (!FiltersRefCount) for (n=NFilters;n--;)
{
ZapHandle(FR[n].terms[0].nickExpanded);
ZapHandle(FR[n].terms[1].nickExpanded);
ZapHandle(FR[n].terms[0].nickAddresses);
ZapHandle(FR[n].terms[1].nickAddresses);
}
}
/************************************************************************
* GetFilterLine - read a line from the filter file
************************************************************************/
OSErr GetFilterLine(FilterKeywordEnum *key,PStr value, LineIOP lip)
{
Str255 line;
short code;
static UHandle cmdH;
UPtr spot;
#ifdef TWO
if (!cmdH || !*cmdH)
{
if (cmdH) LoadResource(cmdH);
cmdH = GetResource_('STR#',FILT_CMD_STRN);
if (!cmdH || !*cmdH) return(ResError());
HNoPurge(cmdH);
}
#endif
do
{
code = GetLine(line+1,sizeof(line)-2,nil,lip);
if (code==eofErr || !code || !line[1]) return(eofErr);
else if (code<0) return(FileSystemError(READ_FILTERS,"",code));
} while (line[1]=='\015' || line[1]=='#');
for(spot=line+1;*spot && *spot!=' ' && *spot!='\015';spot++);
line[0] = spot-line-1;
*key = FindSTRNIndexRes(cmdH,line);
if (!*key)
ComposeStdAlert(Caution,FILT_BADKEY_FMT,line);
else
{
*value = strlen(line+*line+2);
BMD(line+*line+2,value+1,*value);
if (value[*value]=='\015') --*value;
value[*value+1] = 0; /* null terminate, just for fun */
}
return(noErr);
}
/************************************************************************
* SaveFilters - save the filters
************************************************************************/
OSErr SaveFiltersLo(FSSpecPtr toSpec,Boolean all,Boolean clean)
{
FSSpec realSpec, tmpSpec;
Str31 name, sfx;
short refN;
short err;
short i;
short n = NFilters;
long eof;
FInfo info;
SaveCurrentFilter();
// What's our filename?
if (toSpec)
{
realSpec = *toSpec;
PSCopy(name,toSpec->name);
}
else
{
GetRString(name,FILTERS_NAME);
FSMakeFSSpec(Root.vRef,Root.dirId,name,&realSpec);
IsAlias(&realSpec,&realSpec); /* resolve */
}
// Setup a temp file
GetRString(sfx,TEMP_SUFFIX);
PSCat(name,sfx);
FSMakeFSSpec(realSpec.vRefNum,realSpec.parID,name,&tmpSpec);
/*
* open temp file
*/
(void) FSpCreate(&realSpec,CREATOR,FILTER_FTYPE,smSystemScript);
(void) FSpCreate(&tmpSpec,CREATOR,FILTER_FTYPE,smSystemScript);
if (err=FSpOpenDF(&tmpSpec,fsRdWrPerm,&refN))
return(FileSystemError(SAVE_FILTERS,tmpSpec.name,err));
/*
* write the filters
*/
LDRef(Filters);
for (i=0;i<n;i++)
if (all || FilterIsSelected(i))
if (err=WriteFilter(refN,&FR[i])) goto done;
/*
* truncate, just in case
*/
if (err = GetFPos(refN,&eof))
{
FileSystemError(SAVE_FILTERS,tmpSpec.name,err);
goto done;
}
SetEOF(refN,eof);
/*
* close
*/
if (err=MyFSClose(refN))
{
refN = 0;
FileSystemError(SAVE_FILTERS,tmpSpec.name,err);
goto done;
}
refN = 0;
/*
* exchange
*/
err=ExchangeAndDel(&tmpSpec,&realSpec);
/*
* set file type
*/
if (!err && !(err=AFSpGetFInfo(&realSpec,&tmpSpec,&info)))
{
info.fdType = FILTER_FTYPE;
FSpSetFInfo(&tmpSpec,&info);
}
/*
* hurray!
*/
done:
UL(Filters);
if (refN) MyFSClose(refN);
if (!err && clean) FilterWindowClean(True);
if (!err) FUWeed(); /* get rid of extra use records */
return(err);
}
/************************************************************************
* WriteFilter - write a given filter
************************************************************************/
OSErr WriteFilter(short refN,FRPtr fr)
{
short err;
Str15 string;
FActionHandle fa;
if (err=FWriteKey(refN,flkRule,fr->name)) return(err);
if (!fr->fu.id) fr->fu.id = FilterNewId();
NumToString(fr->fu.id,string);
if (err=FWriteKey(refN,flkId,string)) return(err);
if (err=FWriteBool(refN,flkIncoming,fr->incoming)) return(err);
if (err=FWriteBool(refN,flkOutgoing,fr->outgoing)) return(err);
if (err=FWriteBool(refN,flkManual,fr->manual)) return(err);
if (err=FWriteKey(refN,flkHeader,fr->terms[0].header)) return(err);
if (err=FWriteEnum(refN,flkVerb,fr->terms[0].verb)) return(err);
if (err=FWriteKey(refN,flkValue,fr->terms[0].value)) return(err);
if (fr->conjunction && fr->conjunction!=cjIgnore)
{
if (err=FWriteEnum(refN,flkConjunction,fr->conjunction)) return(err);
if (err=FWriteKey(refN,flkHeader,fr->terms[1].header)) return(err);
if (err=FWriteEnum(refN,flkVerb,fr->terms[1].verb)) return(err);
if (err=FWriteKey(refN,flkValue,fr->terms[1].value)) return(err);
}
for (fa=fr->actions;!err && fa;fa = (*fa)->next)
err = CallAction(faeWrite,fa,nil,&refN);
return(err);
}
/************************************************************************
* FWriteKey - write a keyword and value
************************************************************************/
OSErr FWriteKey(short refN,FilterKeywordEnum flk,PStr value)
{
Str255 s;
short err=noErr;
if (*value)
{
err = FSWriteP(refN,ComposeString(s,"\p%r %p\015",FILT_CMD_STRN+flk,value));
if (err) FileSystemError(SAVE_FILTERS,"",err);
}
return(err);
}
/************************************************************************
* FWriteBool - write a Boolean
************************************************************************/
OSErr FWriteBool(short refN,FilterKeywordEnum flk,Boolean value)
{
Str255 s;
short err=noErr;
if (value)
{
err = FSWriteP(refN,ComposeString(s,"\p%r\015",FILT_CMD_STRN+flk));
if (err) FileSystemError(SAVE_FILTERS,"",err);
}
return(err);
}
/************************************************************************
* FWriteEnum - write an enum
************************************************************************/
OSErr FWriteEnum(short refN,FilterKeywordEnum flk,short e)
{
Str255 s;
short err=noErr;
err = FSWriteP(refN,ComposeString(s,"\p%r %r\015",FILT_CMD_STRN+flk,
(flk==flkVerb ? VERB_STRN:CONJ_STRN)+e));
if (err) FileSystemError(SAVE_FILTERS,"",err);
return(err);
}
Boolean FilterExists(DescType form, long selector)
{
return(False);
}
OSErr CountFilters(long *howMany)
{
return(noErr);
}
OSErr GetFilterProperty(AEDescPtr token,AppleEvent *reply,long refCon)
{
return(noErr);
}
OSErr GetTermProperty(AEDescPtr token,AppleEvent *reply,long refCon)
{
return(noErr);
}
OSErr AECreateFilter(DescType theClass,AEDescPtr inContainer,AppleEvent *event, AppleEvent *reply)
{
return(noErr);
}
OSErr SetFilterProperty(AEDescPtr token, AEDescPtr data)
{
return(noErr);
}
OSErr SetTermProperty(AEDescPtr token, AEDescPtr data)
{
return(noErr);
}
OSErr AEDeleteFilter(FilterTokenPtr fp)
{
return(noErr);
}
#endif