1 line
93 KiB
C
Executable File
1 line
93 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 "mailbox.h"
|
||
#define FILE_NUM 21
|
||
|
||
#pragma segment Mailbox
|
||
|
||
typedef enum { kDoAdd,kDoDelete,kDoUpdate,kDoDeleteAttachments,kDoCopy } IMAPUpdateType;
|
||
|
||
typedef struct
|
||
{
|
||
short menu;
|
||
short item;
|
||
short score;
|
||
} MenuAndScore, *MenuAndScorePtr, **MenuAndScoreHandle;
|
||
|
||
void ZeroMailbox(TOCHandle tocH);
|
||
OSErr AddBoxMap(short vRef,long dirId);
|
||
Boolean WantRebuildTOC(UPtr boxName,OSErr why);
|
||
void AddBox(short function,UPtr name,short level,Boolean unread);
|
||
void RemoveBox(short function,UPtr name,short level);
|
||
OSErr BoxSpecByNameInMenu(MenuHandle mh,FSSpecPtr spec,PStr name);
|
||
long TOCDelEmpty(TOCHandle tocH);
|
||
short FindBoxByNameIn1Menu(MenuHandle mh, PStr name);
|
||
OSErr RedoWho(TOCHandle tocH,short sumNum);
|
||
OSErr ChainTrash(FSSpecPtr spec);
|
||
void SetSumColorLo(TOCHandle tocH,short sumNum,short color);
|
||
void SetStateLo(TOCHandle tocH,int sumNum,int state);
|
||
OSErr BoxMatchMenuItems(PStr s,MenuAndScoreHandle *mashPtr,int score());
|
||
OSErr BoxMatchMenuItemsInMenu(MenuHandle mh, AccuPtr a,PStr name,int score());
|
||
OSErr BoxMatchMenuItemsIn1Menu(MenuHandle mh, AccuPtr a,PStr name,int score());
|
||
int BoxMatchScore(PStr name,PStr candidate);
|
||
int CompareMAS(MenuAndScorePtr mas1,MenuAndScorePtr mas2);
|
||
void SwapMAS(MenuAndScorePtr mas1,MenuAndScorePtr mas2);
|
||
static void ProcessIMAPChanges(Handle sumList,TOCHandle toc,IMAPUpdateType message);
|
||
static OSErr IMAPRecvLine(TransStream stream, UPtr buffer, long *size);
|
||
void DecodeIMAPMessages(TOCHandle tocH, FSSpecPtr spec);
|
||
|
||
static LineIOP Lip;
|
||
static long gIMAPMsgEnd;
|
||
|
||
/************************************************************************
|
||
* TOCSetDirty - set the dirty bit
|
||
************************************************************************/
|
||
void TOCSetDirty(TOCHandle tocH,Boolean dirty)
|
||
{
|
||
(*tocH)->durty = dirty;
|
||
AnyTOCDirty++;
|
||
}
|
||
|
||
/************************************************************************
|
||
* AddOutgoingMesgError
|
||
************************************************************************/
|
||
OSErr AddOutgoingMesgError (short sumNum, uLong uidHash, int errorCode, short template,...)
|
||
{
|
||
TOCHandle tocH = nil;
|
||
short outSumNum = sumNum;
|
||
Str255 fmtdError,
|
||
error;
|
||
va_list args;
|
||
|
||
#ifdef THREADING_ON
|
||
TOCHandle tempTocH = nil;
|
||
|
||
if (InAThread())
|
||
{
|
||
tocH = GetRealOutTOC();
|
||
tempTocH = GetTempOutTOC();
|
||
outSumNum = FindSumByHash(tocH,uidHash);
|
||
}
|
||
else
|
||
tempTocH = tocH = GetRealOutTOC();
|
||
#else
|
||
tempTocH = tocH = GetOutTOC();
|
||
#endif
|
||
|
||
if (tempTocH && tocH)
|
||
{
|
||
// get message error string
|
||
GetRString(error,template);
|
||
|
||
va_start(args,template);
|
||
(void) VaComposeStringDouble(fmtdError,sizeof(fmtdError)-1,error,args,nil,0,nil);
|
||
va_end(args);
|
||
|
||
// attach mesg resource to real out toc
|
||
if (fmtdError)
|
||
AddMesgError(tocH, outSumNum,fmtdError, errorCode);
|
||
// mark state of temp out toc entry since thread will update real out toc entry
|
||
SetState(tempTocH,sumNum, MESG_ERR);
|
||
}
|
||
else
|
||
return (-1);
|
||
return (noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* DeleteMesgError
|
||
************************************************************************/
|
||
|
||
OSErr DeleteMesgError (TOCHandle tocH, short sum)
|
||
{
|
||
mesgErrorHandle mesgErrH;
|
||
|
||
if (mesgErrH=(*tocH)->sums[sum].mesgErrH)
|
||
{
|
||
short tocRefN,
|
||
refN=CurResFile(),
|
||
resId=0;
|
||
FSSpec spec = GetMailboxSpec(tocH,-1);
|
||
Boolean sane;
|
||
Str255 uidName;
|
||
Handle mesgErrRH;
|
||
|
||
IsAlias(&spec,&spec);
|
||
if (FSpRFSane(&spec,&sane) || !sane) FSpKillRFork(&spec);
|
||
FSpCreateResFile(&spec,CREATOR,'TEXT',smSystemScript);
|
||
|
||
if (-1==(tocRefN=FSpOpenResFile(&spec,fsRdWrPerm)))
|
||
{
|
||
ZapHandle ((*tocH)->sums[sum].mesgErrH);
|
||
return(ResError());
|
||
}
|
||
|
||
UseResFile (tocRefN);
|
||
|
||
/*
|
||
* find resource in fork and remove
|
||
*/
|
||
|
||
NumToString((long)(*mesgErrH)->uidHash,uidName);
|
||
mesgErrRH = Get1NamedResource(MESG_ERR_TYPE, uidName);
|
||
ASSERT(mesgErrRH);
|
||
if (mesgErrRH)
|
||
{
|
||
RemoveResource ((Handle) mesgErrRH);
|
||
DisposeHandle ((Handle) mesgErrRH);
|
||
}
|
||
ZapHandle ((*tocH)->sums[sum].mesgErrH);
|
||
|
||
if (tocRefN!=-1) CloseResFile(tocRefN);
|
||
UseResFile (refN);
|
||
}
|
||
return noErr;
|
||
}
|
||
|
||
/************************************************************************
|
||
* AddMesgError
|
||
************************************************************************/
|
||
|
||
OSErr AddMesgError (TOCHandle tocH, short sum, PStr errorStr, int errorCode)
|
||
{
|
||
OSErr err = noErr;
|
||
mesgErrorHandle mesgErrH = nil;
|
||
|
||
/* tocH and sum should be valid, mesgErrH should be empty */
|
||
ASSERT(tocH && (sum!=-1) && !(*tocH)->sums[sum].mesgErrH && (sum < (*tocH)->count));
|
||
if (!(tocH && (sum!=-1) && (sum < (*tocH)->count)))
|
||
return -1;
|
||
|
||
/* if for some reason, mesgErrH isn't empty, overwrite it */
|
||
if (mesgErrH = (*tocH)->sums[sum].mesgErrH ? (*tocH)->sums[sum].mesgErrH : (mesgErrorHandle) NuHandleClear (sizeof (mesgError)))
|
||
{
|
||
short tocRefN,
|
||
refN=CurResFile(),
|
||
resId=0;
|
||
FSSpec spec = GetMailboxSpec(tocH,-1);
|
||
Boolean sane;
|
||
|
||
if (errorStr)
|
||
PCopyTrim ((*mesgErrH)->errorStr, errorStr, sizeof ((*mesgErrH)->errorStr));
|
||
(*mesgErrH)->uidHash = (*tocH)->sums[sum].uidHash;
|
||
(*mesgErrH)->errorCode = errorCode;
|
||
|
||
IsAlias(&spec,&spec);
|
||
if (FSpRFSane(&spec,&sane) || !sane) FSpKillRFork(&spec);
|
||
FSpCreateResFile(&spec,CREATOR,'TEXT',smSystemScript);
|
||
|
||
if (-1==(tocRefN=FSpOpenResFile(&spec,fsRdWrPerm)))
|
||
err = ResError();
|
||
|
||
UseResFile (tocRefN);
|
||
if (!err)
|
||
{
|
||
resId=Unique1ID(MESG_ERR_TYPE);
|
||
err=ResError();
|
||
}
|
||
if (!err)
|
||
{
|
||
Str255 uidName;
|
||
|
||
NumToString((long)(*mesgErrH)->uidHash,uidName);
|
||
AddResource((Handle)mesgErrH,MESG_ERR_TYPE,resId,uidName);
|
||
err=ResError();
|
||
}
|
||
if (!err)
|
||
{
|
||
WriteResource((Handle) mesgErrH);
|
||
err=ResError();
|
||
}
|
||
if (!err)
|
||
{
|
||
DetachResource ((Handle) mesgErrH);
|
||
err=ResError();
|
||
}
|
||
if (tocRefN!=-1) CloseResFile(tocRefN);
|
||
UseResFile (refN);
|
||
}
|
||
else
|
||
err = MemError();
|
||
// let's ignore the error since we can set the mesg state
|
||
(*tocH)->sums[sum].state = MESG_ERR;
|
||
(*tocH)->sums[sum].mesgErrH = mesgErrH;
|
||
TOCSetDirty(tocH,true);
|
||
(*tocH)->reallyDirty = true;
|
||
return (noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* FillMesgErrors - fill toc
|
||
************************************************************************/
|
||
|
||
OSErr FillMesgErrors (TOCHandle tocH)
|
||
{
|
||
OSErr err = noErr;
|
||
mesgErrorHandle mesgErrH = nil;
|
||
short ii = 0,
|
||
sum,
|
||
resCount = 0,
|
||
oldRefN,
|
||
refN=0;
|
||
FSSpec spec;
|
||
|
||
ASSERT (tocH);
|
||
if (!tocH)
|
||
return paramErr;
|
||
|
||
oldRefN = CurResFile();
|
||
spec = GetMailboxSpec(tocH,-1);
|
||
if (-1!=(refN=FSpOpenResFile(&spec,fsRdPerm)))
|
||
{
|
||
resCount = Count1Resources (MESG_ERR_TYPE);
|
||
while (resCount)
|
||
{
|
||
if (mesgErrH = (mesgErrorHandle) Get1IndResource (MESG_ERR_TYPE,resCount--))
|
||
{
|
||
sum = FindSumByHash (tocH,(*mesgErrH)->uidHash);
|
||
if (sum != -1)
|
||
{
|
||
(*tocH)->sums[sum].mesgErrH = mesgErrH;
|
||
DetachResource ((Handle) mesgErrH);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (refN) CloseResFile(refN);
|
||
UseResFile (oldRefN);
|
||
return (err);
|
||
}
|
||
|
||
|
||
/**********************************************************************
|
||
* GetMailbox - put a mailbox window frontmost; open if necessary
|
||
**********************************************************************/
|
||
int GetMailbox(FSSpecPtr spec,Boolean showIt)
|
||
{
|
||
TOCHandle toc;
|
||
|
||
if (ResolveAliasOrElse(spec,nil,nil)) return(userCanceledErr);
|
||
|
||
// if this is an IMAP folder we're going to open, adjust the spec so it points to the mailbox inside
|
||
if (IsIMAPCacheFolder(spec)) spec->parID = SpecDirId(spec);
|
||
|
||
if (toc=FindTOC(spec))
|
||
{
|
||
WindowPtr tocWinWP = GetMyWindowWindowPtr ((*toc)->win);
|
||
|
||
UsingWindow(tocWinWP);
|
||
if (showIt)
|
||
{
|
||
if (!IsWindowVisible(tocWinWP))
|
||
{
|
||
ShowMyWindow(tocWinWP);
|
||
|
||
// if we're showing an IMAP mailbox, resync it.
|
||
if ((*toc)->imapTOC)
|
||
{
|
||
MailboxNodeHandle mailbox = nil; // mailboxnode we're fetching mail for
|
||
|
||
// no threading. Get results immediately
|
||
if ((PrefIsSet(PREF_THREADING_OFF) || !ThreadsAvailable()))
|
||
{
|
||
mailbox = FetchNewMessages(toc, true, false, true, false);
|
||
UpdateIMAPMailbox(toc);
|
||
}
|
||
else
|
||
{
|
||
// resync the mailbox when it's convenient
|
||
FlagForResync(toc);
|
||
}
|
||
}
|
||
}
|
||
UserSelectWindow(tocWinWP);
|
||
}
|
||
return(0);
|
||
}
|
||
|
||
return OpenMailbox(spec,showIt,nil);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* OpenMailbox - open the named mailbox
|
||
**********************************************************************/
|
||
int OpenMailbox(FSSpecPtr spec, Boolean showIt,TOCHandle toc)
|
||
{
|
||
MyWindow *win;
|
||
WindowPtr winWP;
|
||
|
||
/*
|
||
* create window
|
||
*/
|
||
MyThreadBeginCritical(); // We may be in a thread. Don't yield until the window is all set up.
|
||
if ((win=GetNewMyWindow(MAILBOX_WIND,nil,nil,showIt?BehindModal:0,True,True,MBOX_WIN))==nil)
|
||
{
|
||
WarnUser(COULDNT_WIN,MemError());
|
||
MyThreadEndCritical();
|
||
return(MemError());
|
||
}
|
||
|
||
winWP = GetMyWindowWindowPtr (win);
|
||
|
||
win->hPitch = FontWidth;
|
||
win->vPitch = FontLead+FontDescent;
|
||
|
||
/*
|
||
* read or build toc for window if we don't have it yet
|
||
*/
|
||
if (!toc && !(toc=GetTOCFromSearchWin(spec)))
|
||
{
|
||
toc = CheckTOC(spec);
|
||
if (toc==nil)
|
||
{
|
||
DisposeWindow_(winWP);
|
||
MyThreadEndCritical();
|
||
return(1);
|
||
}
|
||
}
|
||
|
||
FillMesgErrors (toc);
|
||
|
||
/*
|
||
* set up window data
|
||
*/
|
||
InitMailboxWin(win, toc, showIt);
|
||
|
||
TOCDelEmpty(toc);
|
||
if (showIt && PrefIsSet(PREF_DELDUP)) TOCDelDup(toc); // don't bother deleting dups unless we're going to show the mailbox.
|
||
|
||
// Show the window if the caller wants
|
||
if (showIt)
|
||
{
|
||
ShowMyWindow(winWP);
|
||
|
||
// Open mailbox drawer?
|
||
if ((*toc)->drawer && !(*toc)->drawerWin)
|
||
{
|
||
// Don't open draw if there is one already open
|
||
// Open drawer
|
||
TOCHandle tocTemp;
|
||
|
||
for (tocTemp=TOCList;tocTemp;tocTemp=(*tocTemp)->next)
|
||
{
|
||
if ((*tocTemp)->drawerWin)
|
||
{
|
||
// Found another one. Don't open this one.
|
||
(*toc)->drawer = false;
|
||
break;
|
||
}
|
||
}
|
||
if ((*toc)->drawer)
|
||
// Open drawer
|
||
MBDrawerOpen(win);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* push it onto list of open toc's
|
||
*/
|
||
LL_Push(TOCList,toc);
|
||
|
||
MyThreadEndCritical();
|
||
|
||
// if we're opening and showing an IMAP mailbox, fetch new messages
|
||
if (showIt && (*toc)->imapTOC && AutoCheckOK() && !StartingUp)
|
||
{
|
||
MailboxNodeHandle mailbox = nil; // mailboxnode we're fetching mail for
|
||
|
||
// no threading. Get results immediately
|
||
if ((PrefIsSet(PREF_THREADING_OFF) || !ThreadsAvailable()))
|
||
{
|
||
mailbox = FetchNewMessages(toc, true, false, true, false);
|
||
UpdateIMAPMailbox(toc);
|
||
}
|
||
else
|
||
{
|
||
// resync the mailbox when it's convenient
|
||
FlagForResync(toc);
|
||
}
|
||
}
|
||
|
||
return(0);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* InitMailboxWin - initialize mailbox window data
|
||
**********************************************************************/
|
||
void InitMailboxWin(MyWindowPtr win, TOCHandle toc, Boolean showIt)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
Str255 scratch;
|
||
|
||
if (!winWP)
|
||
return;
|
||
|
||
SetTopMargin(win,win->vPitch+2*FontDescent);
|
||
|
||
SetWindowKind (winWP,(*toc)->which==OUT ? CBOX_WIN : MBOX_WIN);
|
||
SetWindowPrivateData (winWP, (long) toc);
|
||
win->close = BoxClose;
|
||
win->click = BoxClick;
|
||
win->bgClick = BoxClick;
|
||
win->activate = BoxActivate;
|
||
win->menu = BoxMenu;
|
||
win->key = BoxKey;
|
||
win->help = BoxHelp;
|
||
win->didResize = BoxDidResize;
|
||
win->gonnaShow = BoxGonnaShow;
|
||
win->position = BoxPosition;
|
||
win->cursor = BoxCursor;
|
||
win->grow = BoxGrowSize;
|
||
win->minSize.h = 2*GetRLong(BOX_SIZE_SIZE);
|
||
win->idle = BoxIdle;
|
||
win->find = BoxFind;
|
||
|
||
(*toc)->win = win;
|
||
|
||
// Title the window.
|
||
if (!IMAPMailboxTitle(toc, scratch))
|
||
PCopy(scratch,MailboxAlias((*toc)->which,PCopy(scratch,(*toc)->mailbox.spec.name)));
|
||
|
||
// Insert a NULL if the user is trying to avoid AMO
|
||
if (GetPrefLong(PREF_AMO_AVOIDANCE)==kAMOAvoidAll || !showIt && *scratch>1)
|
||
PInsertC(scratch,sizeof(scratch),0,scratch+2);
|
||
SetWTitle_(winWP,scratch);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* TOCDelDup - delete duplicate messages from a table of contents
|
||
**********************************************************************/
|
||
long TOCDelDup(TOCHandle tocH)
|
||
{
|
||
long i, j, nuke;
|
||
long count = 0;
|
||
short n, removed;
|
||
long cycleCount = 50000;
|
||
MSumPtr iSum,jSum;
|
||
|
||
// this doesn't work on IMAP mailboxes
|
||
if ((*tocH)->imapTOC) return (0);
|
||
|
||
n = (*tocH)->count;
|
||
|
||
LDRef(tocH);
|
||
for (i=0,iSum=(*tocH)->sums;i<n;i++,iSum++)
|
||
{
|
||
if (iSum->msgIdHash!=kNeverHashed && iSum->msgIdHash!=-2 && iSum->msgIdHash!=kNoMessageId)
|
||
for (j=i+1,jSum=iSum+1;j<n;j++,jSum++)
|
||
{
|
||
if (!--cycleCount)
|
||
{
|
||
// We don't need to call this very often compared to
|
||
// how quickly we go through this list
|
||
CycleBalls();
|
||
cycleCount = 50000;
|
||
}
|
||
if (iSum->msgIdHash==jSum->msgIdHash)
|
||
{
|
||
nuke = -1;
|
||
if ((iSum->flags&FLAG_SKIPPED) && !(jSum->flags&FLAG_SKIPPED))
|
||
nuke = i; // i is stub; delete
|
||
else if (!(iSum->flags&FLAG_SKIPPED) && (jSum->flags&FLAG_SKIPPED))
|
||
nuke = j; // j is stub; delete
|
||
else if (iSum->state!=UNREAD && jSum->state==UNREAD)
|
||
nuke = j; // j is unread; delete
|
||
else if (iSum->state==UNREAD && jSum->state!=UNREAD)
|
||
nuke = i; // i is unread; delete
|
||
else
|
||
nuke = j; // i and j identical; delete j
|
||
if (nuke>=0)
|
||
{
|
||
(*tocH)->sums[nuke].msgIdHash = -2;
|
||
count++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
UL(tocH);
|
||
|
||
if (count)
|
||
{
|
||
removed = 0;
|
||
for (n=(*tocH)->count;n-- && removed<count;)
|
||
{
|
||
if ((*tocH)->sums[n].msgIdHash==-2)
|
||
{
|
||
if (!DeleteSum(tocH,n)) // changed by Clarence, 4/28/97
|
||
removed++;
|
||
}
|
||
}
|
||
}
|
||
return(count);
|
||
|
||
}
|
||
|
||
/**********************************************************************
|
||
* TOCDelEmpty - delete empty messages from a table of contents
|
||
**********************************************************************/
|
||
long TOCDelEmpty(TOCHandle tocH)
|
||
{
|
||
long count = 0;
|
||
short n;
|
||
|
||
for (n=(*tocH)->count;n--;)
|
||
{
|
||
if ((*tocH)->sums[n].length==0)
|
||
{
|
||
if (!DeleteSum(tocH,n)) // changed by Clarence, 4/28/97
|
||
count++;
|
||
}
|
||
}
|
||
return(count);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* OpenFilterMessages - open messages that filters say we should
|
||
**********************************************************************/
|
||
OSErr OpenFilterMessages(FSSpecPtr spec)
|
||
{
|
||
TOCHandle tocH = TOCBySpec(spec);
|
||
short i;
|
||
Boolean openedOne;
|
||
|
||
if (!tocH) return(1);
|
||
|
||
ComposeLogS(LOG_FILT,nil,"\pOpening messages from %p",spec->name);
|
||
|
||
do
|
||
{
|
||
openedOne = false;
|
||
for (i=(*tocH)->count-1;i>=0;i--)
|
||
if ((*tocH)->sums[i].opts & OPT_OPEN)
|
||
{
|
||
(*tocH)->sums[i].opts &= ~OPT_OPEN;
|
||
(*tocH)->sums[i].opts |= OPT_AUTO_OPENED;
|
||
TOCSetDirty(tocH,true);
|
||
{
|
||
Str63 name;
|
||
PSCopy(name,(*tocH)->sums[i].subj);
|
||
ComposeLogS(LOG_FILT,nil,"\pOpening message %p",name);
|
||
}
|
||
GetAMessage(tocH,i,nil,nil,true);
|
||
openedOne = true;
|
||
}
|
||
}
|
||
while (openedOne);
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
|
||
/**********************************************************************
|
||
* SaveMessageSum - save a message summary into a TOC
|
||
**********************************************************************/
|
||
Boolean SaveMessageSum(MSumPtr sum,TOCHandle *tocH)
|
||
{
|
||
long grow;
|
||
long goal;
|
||
TOCHandle new;
|
||
OSErr err;
|
||
|
||
RemoveUTF8FromSum(sum);
|
||
sum->serialNum = (**tocH)->nextSerialNum++;
|
||
if ((**tocH)->count)
|
||
{
|
||
goal = GetHandleSize_(*tocH)+sizeof(MSumType);
|
||
SetHandleBig_(*tocH,goal);
|
||
if (err=MemError())
|
||
{
|
||
if ((**tocH)->building && TempMaxMem(&grow)>goal)
|
||
{
|
||
if (new = (TOCHandle)NuHTempBetter(goal))
|
||
{
|
||
BMD(**tocH,*new,goal-sizeof(MSumType));
|
||
DisposeHandle((Handle)*tocH);
|
||
*tocH = new;
|
||
err = noErr;
|
||
}
|
||
}
|
||
if (err)
|
||
{
|
||
WarnUser(SAVE_SUM_ERR,err);
|
||
return(False);
|
||
}
|
||
}
|
||
}
|
||
(**tocH)->needRedo = (**tocH)->count;
|
||
(**tocH)->resort = kResortWhenever;
|
||
BMD(sum,&(**tocH)->sums[(**tocH)->count++],sizeof(MSumType));
|
||
#ifdef NEVER
|
||
CalcSumLengths(*tocH,(**tocH)->count-1);
|
||
#endif
|
||
InvalSum(*tocH,(**tocH)->count-1);
|
||
TOCSetDirty(*tocH,true);
|
||
(**tocH)->reallyDirty = True;
|
||
(**tocH)->analScanned = false;
|
||
return(True);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* IsRoot - is a spec at the root of the Eudora Folder?
|
||
************************************************************************/
|
||
Boolean IsRoot(FSSpecPtr spec)
|
||
{
|
||
return(spec->parID==MailRoot.dirId && SameVRef(spec->vRefNum,MailRoot.vRef));
|
||
}
|
||
|
||
/************************************************************************
|
||
* IsSpool - is a spec in the Spool Folder?
|
||
************************************************************************/
|
||
Boolean IsSpool(FSSpecPtr spec)
|
||
{
|
||
FSSpec folderSpec;
|
||
|
||
if (SubFolderSpec(SPOOL_FOLDER,&folderSpec))
|
||
return (false);
|
||
|
||
return(spec->parID==folderSpec.parID && SameVRef(spec->vRefNum,folderSpec.vRefNum));
|
||
}
|
||
|
||
#ifdef BATCH_DELIVERY_ON
|
||
/************************************************************************
|
||
* IsDelivery - is a spec in the Delivery Folder?
|
||
************************************************************************/
|
||
Boolean IsDelivery(FSSpecPtr spec)
|
||
{
|
||
FSSpec folderSpec;
|
||
|
||
if (SubFolderSpec(DELIVERY_FOLDER,&folderSpec))
|
||
return (false);
|
||
|
||
return(spec->parID==folderSpec.parID && SameVRef(spec->vRefNum,folderSpec.vRefNum));
|
||
}
|
||
#endif
|
||
|
||
/**********************************************************************
|
||
* Spec2Menu - find the menu params for a given FSSpec
|
||
**********************************************************************/
|
||
OSErr Spec2Menu(FSSpecPtr spec,Boolean forXfer,short *menu, short *item)
|
||
{
|
||
Str63 name;
|
||
long dirID = spec->parID;
|
||
FSSpec parentSpec;
|
||
|
||
if (IsIMAPMailboxFile(spec))
|
||
{
|
||
ParentSpec(spec,&parentSpec);
|
||
dirID = parentSpec.parID;
|
||
}
|
||
|
||
if (0<=(*menu=FindDirLevel(spec->vRefNum,dirID)))
|
||
{
|
||
*menu = *menu?*menu:MAILBOX_MENU;
|
||
MailboxSpecAlias(spec,name);
|
||
*item = FindItemByName(GetMHandle(*menu),name);
|
||
if (forXfer) *menu = (*menu==MAILBOX_MENU)?TRANSFER_MENU:*menu+MAX_BOX_LEVELS;
|
||
if (*item>0) return(noErr);
|
||
}
|
||
*item = 0;
|
||
return(fnfErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* TOCH2Menu - find the menu item that corresponds to a toch
|
||
**********************************************************************/
|
||
OSErr TOCH2Menu(TOCHandle tocH,Boolean forXfer,short *mnu,short *item)
|
||
{
|
||
FSSpec spec = GetMailboxSpec(tocH,-1);
|
||
return(Spec2Menu(&spec,forXfer,mnu,item));
|
||
}
|
||
|
||
/**********************************************************************
|
||
* FixSpecUnread - fix the unread-ness of a file
|
||
**********************************************************************/
|
||
void FixSpecUnread(FSSpecPtr spec,Boolean unread)
|
||
{
|
||
FInfo info;
|
||
|
||
FSpGetFInfo(spec,&info);
|
||
if (((info.fdFlags&0xe)!=0) != unread)
|
||
{
|
||
if (unread) info.fdFlags |= 0xe;
|
||
else info.fdFlags &= ~0xe;
|
||
FSpSetFInfo(spec,&info);
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* FixMenuUnread - fix unread status in the menus
|
||
************************************************************************/
|
||
void FixMenuUnread(MenuHandle mh,short item,Boolean unread)
|
||
{
|
||
Style oldStyle;
|
||
Style newStyle;
|
||
Boolean mailboxMenu,haveIMAP;
|
||
|
||
newStyle = unread ? UnreadStyle : 0;
|
||
GetItemStyle(mh,item,&oldStyle);
|
||
|
||
if (oldStyle==newStyle) return; /* done! */
|
||
|
||
SetItemStyle(mh,item,newStyle);
|
||
MBFixUnread(mh,item,unread); // Update Mailboxes window
|
||
|
||
mailboxMenu = mh==GetMHandle(MAILBOX_MENU);
|
||
haveIMAP = IMAPExists();
|
||
|
||
if (!newStyle)
|
||
for (item=CountMenuItems(mh);item;item--)
|
||
{
|
||
if (mailboxMenu && haveIMAP)
|
||
{
|
||
// Ignore IMAP mailfolder in main mailboxes menu
|
||
short vRef;
|
||
long dirID;
|
||
short menuID;
|
||
|
||
if (menuID = SubmenuId(mh,item))
|
||
{
|
||
MenuID2VD(menuID,&vRef,&dirID);
|
||
if (IsIMAPVD(vRef,dirID))
|
||
continue;
|
||
}
|
||
haveIMAP = false; // No more IMAP
|
||
}
|
||
GetItemStyle(mh,item,&newStyle);
|
||
newStyle &= ~italic;
|
||
if (newStyle) break;
|
||
}
|
||
|
||
if (!mailboxMenu)
|
||
{
|
||
mh = ParentMailboxMenu(mh,&item);
|
||
if (mh) FixMenuUnread(mh,item,newStyle!=0);
|
||
}
|
||
else
|
||
MBFixUnread(mh,0,newStyle!=0); // Update Mailboxes window
|
||
}
|
||
|
||
/**********************************************************************
|
||
* Box2Path - walk up our menus, looking for the one true way
|
||
**********************************************************************/
|
||
OSErr Box2Path(FSSpecPtr box,PStr path)
|
||
{
|
||
short menu, item;
|
||
OSErr err = noErr;
|
||
MenuHandle mh;
|
||
Str63 name;
|
||
|
||
PCopy(path,box->name);
|
||
err=Spec2Menu(box,False,&menu,&item);
|
||
if (!err) mh = GetMHandle(menu);
|
||
|
||
while (!err && mh && GetMenuID(mh)!=MAILBOX_MENU)
|
||
{
|
||
if (mh = ParentMailboxMenu(mh,&item))
|
||
{
|
||
MyGetItem(mh,item,name);
|
||
PCatC(name,':');
|
||
PInsert(path,255,name,path+1);
|
||
}
|
||
}
|
||
|
||
if (err) return(err);
|
||
if (!mh) return(fnfErr);
|
||
|
||
PInsert(path,255,"\p:",path+1);
|
||
return(noErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* Path2Box - walk back down our menut
|
||
**********************************************************************/
|
||
OSErr Path2Box(PStr path,FSSpecPtr box)
|
||
{
|
||
OSErr err = fnfErr;
|
||
UPtr spot;
|
||
Str31 name;
|
||
|
||
box->vRefNum = MailRoot.vRef;
|
||
box->parID = MailRoot.dirId;
|
||
spot = path+2;
|
||
|
||
while (PToken(path,name,&spot,":"))
|
||
{
|
||
// does the file exist?
|
||
if (err=FSMakeFSSpec(box->vRefNum,box->parID,name,box)) break;
|
||
|
||
// if it's an alias, resolve
|
||
IsAlias(box,box);
|
||
|
||
// if it's not a folder, we're done
|
||
if (!FSpIsItAFolder(box)) break;
|
||
|
||
// get the folder's spec
|
||
box->parID = SpecDirId(box);
|
||
}
|
||
|
||
// look through the IMAP folder if we haven't found this box yet.
|
||
if (err == fnfErr)
|
||
{
|
||
box->vRefNum = IMAPMailRoot.vRef;
|
||
box->parID = IMAPMailRoot.dirId;
|
||
spot = path+2;
|
||
|
||
while (PToken(path,name,&spot,":"))
|
||
{
|
||
// does the file exist?
|
||
if (err=FSMakeFSSpec(box->vRefNum,box->parID,name,box)) break;
|
||
|
||
// if it's an alias, resolve
|
||
IsAlias(box,box);
|
||
|
||
// if it's not a folder, we're done
|
||
if (!FSpIsItAFolder(box)) break;
|
||
|
||
// get the folder's spec
|
||
box->parID = SpecDirId(box);
|
||
}
|
||
}
|
||
|
||
if (err) return(err);
|
||
if (PToken(path,name,&spot,":")) return(fnfErr); // we didn't use up all the names in the string. bad
|
||
return(noErr); // we made it!
|
||
}
|
||
|
||
#ifdef NEVER
|
||
/************************************************************************
|
||
* CalcAllSumLengths - calculate all the lengths for all the sums in a toc
|
||
************************************************************************/
|
||
void CalcAllSumLengths(TOCHandle toc)
|
||
{
|
||
int sumNum;
|
||
|
||
for (sumNum=0; sumNum<(*toc)->count; sumNum++)
|
||
CalcSumLengths(toc,sumNum);
|
||
}
|
||
|
||
/************************************************************************
|
||
* CalcSumLengths - calculcate how long the strings in a sum can be
|
||
************************************************************************/
|
||
void CalcSumLengths(TOCHandle tocH,int sumNum)
|
||
{
|
||
Str255 scratch;
|
||
short trunc;
|
||
short dWidth = (*BoxLines)[WID_DATE]-(*BoxLines)[WID_DATE-1];
|
||
short fWidth = (*BoxLines)[WID_FROM]-(*BoxLines)[WID_FROM-1];
|
||
|
||
if (FontIsFixed)
|
||
{
|
||
(*tocH)->sums[sumNum].dateTrunc = dWidth/FontWidth - 1;
|
||
(*tocH)->sums[sumNum].fromTrunc = fWidth/FontWidth - 1;
|
||
}
|
||
else
|
||
{
|
||
PCopy(scratch,(*tocH)->sums[sumNum].date);
|
||
trunc = CalcTrunc(scratch,dWidth,InsurancePort);
|
||
if (trunc && trunc<*scratch) trunc--;
|
||
(*tocH)->sums[sumNum].dateTrunc = trunc;
|
||
|
||
PCopy(scratch,(*tocH)->sums[sumNum].from);
|
||
trunc = CalcTrunc(scratch,fWidth,InsurancePort);
|
||
if (trunc && trunc<*scratch) trunc--;
|
||
(*tocH)->sums[sumNum].fromTrunc = trunc;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
/**********************************************************************
|
||
* SetState - set a message's state in its summary,
|
||
* handle virtual TOCs, too
|
||
**********************************************************************/
|
||
void SetState(TOCHandle tocH,int sumNum,int state)
|
||
{
|
||
TOCHandle realTOC;
|
||
short realSum;
|
||
|
||
SetStateLo(tocH,sumNum,state);
|
||
realTOC = GetRealTOC(tocH,sumNum,&realSum);
|
||
if (realTOC && realTOC != tocH)
|
||
{
|
||
// do real mailbox also if working in virtual mailbox
|
||
SetStateLo(realTOC,realSum,state);
|
||
tocH = realTOC;
|
||
sumNum = realSum;
|
||
}
|
||
SearchUpdateSum(tocH, sumNum, tocH, (*tocH)->sums[sumNum].serialNum, false, false); // Notify search window
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SetStateLo - set a message's state in its summary.
|
||
**********************************************************************/
|
||
void SetStateLo(TOCHandle tocH,int sumNum,int state)
|
||
{
|
||
int oldState = (*tocH)->sums[sumNum].state;
|
||
|
||
if (oldState == state)
|
||
return; /* nothing to do */
|
||
|
||
InvalTocBox(tocH,sumNum,blStat);
|
||
|
||
(*tocH)->sums[sumNum].state = state;
|
||
TOCSetDirty(tocH,true);
|
||
if ((*tocH)->sums[sumNum].selected) (*tocH)->lastSameTicks = 1; // no unreading, pal
|
||
|
||
if (oldState==UNREAD || state==UNREAD) (*tocH)->unreadBase = -1; // force update
|
||
|
||
if (IsQueuedState(oldState)||IsQueuedState(state))
|
||
(*tocH)->reallyDirty = True;
|
||
if (IsQueuedState(state))
|
||
DeleteMesgError(tocH,sumNum);
|
||
|
||
if ((*tocH)->which!=OUT)
|
||
{
|
||
if ((state==SENT || state==UNSENT) && (oldState==READ || oldState==UNREAD) ||
|
||
(oldState==SENT || oldState==UNSENT) && (state==READ || state==UNREAD))
|
||
{
|
||
// Hoo, boy. Ugly, ugly, ugly
|
||
RedoWho(tocH,sumNum);
|
||
}
|
||
}
|
||
|
||
QueueMessFlagChange(tocH, sumNum, state, false); // save the message state change, notify the server later.
|
||
|
||
if (state == READ)
|
||
{
|
||
FSSpec spec;
|
||
TOCHandle realTOC;
|
||
short realSum;
|
||
|
||
realTOC = GetRealTOC(tocH,sumNum,&realSum);
|
||
spec = GetMailboxSpec(tocH,-1);
|
||
if (!(*tocH)->imapTOC ||
|
||
PrefIsSet(PREF_COUNT_ALL_IMAP) ||
|
||
// count only those IMAP messages that are in InBox
|
||
TOCToMbox(realTOC)==LocateInboxForPers(TOCToPers(realTOC)))
|
||
UpdateNumStatWithTime(kStatReadMsg,1,(*tocH)->sums[sumNum].seconds+ZoneSecs());
|
||
}
|
||
}
|
||
|
||
short FindSumByHash(TOCHandle tocH,uLong hash)
|
||
{
|
||
short sumNum,
|
||
myCount;
|
||
|
||
#ifdef THREADING_ON
|
||
// check toc sanity-- InsaneTOC doesn't work unless we WriteTOC immediately before
|
||
myCount = ((GetHandleSize_(tocH)-sizeof(TOCType)) / sizeof(MSumType)) + 1;
|
||
if (myCount > (*tocH)->count)
|
||
#endif
|
||
myCount = (*tocH)->count;
|
||
|
||
for (sumNum=myCount-1;sumNum>=0;sumNum--)
|
||
if ((*tocH)->sums[sumNum].uidHash==hash) break;
|
||
return(sumNum);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* RedoWho - Redo the who field because of an in/out transition.
|
||
**********************************************************************/
|
||
OSErr RedoWho(TOCHandle tocH,short sumNum)
|
||
{
|
||
Str255 who;
|
||
short hState;
|
||
OSErr err = noErr;
|
||
UPtr spot;
|
||
UPtr text;
|
||
long len, hLen;
|
||
Str255 hName;
|
||
short i;
|
||
|
||
if (!(err=CacheMessage(tocH,sumNum)))
|
||
if ((*tocH)->sums[sumNum].cache)
|
||
{
|
||
hState = HGetState((*tocH)->sums[sumNum].cache);
|
||
text=LDRef((*tocH)->sums[sumNum].cache);
|
||
len = GetHandleSize((*tocH)->sums[sumNum].cache);
|
||
*who = 0;
|
||
if ((*tocH)->sums[sumNum].state==SENT || (*tocH)->sums[sumNum].state==UNSENT)
|
||
{
|
||
hLen = len;
|
||
spot = FindHeaderString(text,GetRString(hName,HEADER_STRN+TO_HEAD),&hLen,False);
|
||
if (!spot || !len)
|
||
{
|
||
hLen = len;
|
||
spot = FindHeaderString(text,GetRString(hName,HEADER_STRN+BCC_HEAD),&hLen,False);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (i=1;*GetRString(hName,SUM_SENDER_HEADS + i);i++)
|
||
{
|
||
hLen = len;
|
||
spot = FindHeaderString(text,hName,&hLen,False);
|
||
if (spot && hLen) break;
|
||
}
|
||
}
|
||
if (spot && hLen)
|
||
{
|
||
MakePStr(who,spot,hLen);
|
||
BeautifyFrom(who);
|
||
PSCopy((*tocH)->sums[sumNum].from,who);
|
||
if ((*tocH)->sums[sumNum].messH)
|
||
{
|
||
MakeMessTitle(hName,tocH,sumNum,True);
|
||
SetWTitle_(GetMyWindowWindowPtr((*(*tocH)->sums[sumNum].messH)->win),hName);
|
||
}
|
||
}
|
||
HSetState((*tocH)->sums[sumNum].cache,hState);
|
||
}
|
||
if (!err) InvalSum(tocH,sumNum);
|
||
return(err);
|
||
}
|
||
|
||
|
||
#ifdef TWO
|
||
|
||
/**********************************************************************
|
||
* GetSumColor - get a message's color from its summary.
|
||
**********************************************************************/
|
||
short GetSumColor(TOCHandle tocH,short sumNum)
|
||
{
|
||
return(SumColor((*tocH)->sums+sumNum));
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SetSumColor - set a message's color in its summary,
|
||
* handle virtual TOCs, too
|
||
**********************************************************************/
|
||
void SetSumColor(TOCHandle tocH,short sumNum,short color)
|
||
{
|
||
TOCHandle realTOC;
|
||
short realSum;
|
||
|
||
SetSumColorLo(tocH,sumNum,color);
|
||
realTOC = GetRealTOC(tocH,sumNum,&realSum);
|
||
if (realTOC && realTOC != tocH)
|
||
{
|
||
// do real mailbox also if working in virtual mailbox
|
||
SetSumColorLo(realTOC,realSum,color);
|
||
tocH = realTOC;
|
||
sumNum = realSum;
|
||
}
|
||
SearchUpdateSum(tocH, sumNum, tocH, (*tocH)->sums[sumNum].serialNum, false, false); // Notify search window
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SetSumColorLo - set a message's color in its summary.
|
||
**********************************************************************/
|
||
void SetSumColorLo(TOCHandle tocH,short sumNum,short color)
|
||
{
|
||
int oldColor = GetSumColor(tocH,sumNum);
|
||
MessHandle messH;
|
||
|
||
if (oldColor == color)
|
||
return; /* nothing to do */
|
||
|
||
InvalSum(tocH,sumNum);
|
||
|
||
(*tocH)->sums[sumNum].flags &= ~(FLAG_HUE1|FLAG_HUE2|FLAG_HUE3|FLAG_HUE4);
|
||
(*tocH)->sums[sumNum].flags |= (color<<14);
|
||
TOCSetDirty(tocH,true);
|
||
|
||
/* set the priority display in the message */
|
||
if (messH=(*tocH)->sums[sumNum].messH)
|
||
{
|
||
WindowPtr messWinWP = GetMyWindowWindowPtr ((*messH)->win);
|
||
(*messH)->win->label = color;
|
||
if (IsColorWin(messWinWP))
|
||
{
|
||
if (IsWindowVisible(messWinWP)) InvalTopMargin((*messH)->win);
|
||
AppCdefBGChange((*messH)->win);
|
||
}
|
||
}
|
||
|
||
/* If this is an IMAP message, tell the server about the label change */
|
||
if ((*tocH)->imapTOC)
|
||
QueueMessFlagChange(tocH, sumNum, (*tocH)->sums[sumNum].state, false);
|
||
}
|
||
#endif
|
||
|
||
/**********************************************************************
|
||
* BoxFOpen - open the mailbox file represented by a toc
|
||
* may be called on open mailbox, and reports error to user
|
||
**********************************************************************/
|
||
int BoxFOpenLo(TOCHandle tocH,short sumNum)
|
||
{
|
||
short refN;
|
||
int err=0;
|
||
FSSpec newSpec,spec;
|
||
|
||
if ((*tocH)->refN==0)
|
||
{
|
||
LDRef(tocH);
|
||
spec = GetMailboxSpec(tocH,sumNum);
|
||
err = AFSpOpenDF(&spec,&newSpec,fsRdWrPerm,&refN);
|
||
if (err)
|
||
FileSystemError(OPEN_MBOX,spec.name,err);
|
||
else
|
||
(*tocH)->refN = refN;
|
||
ComposeLogS(LOG_FLOW,nil,"\pBoxFOpen %p\015",spec.name);
|
||
UL(tocH);
|
||
}
|
||
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* BoxFOpen - open the mailbox file represented by a toc
|
||
* may be called on open mailbox, and reports error to user
|
||
**********************************************************************/
|
||
int BoxFOpen(TOCHandle tocH)
|
||
{
|
||
return BoxFOpenLo(tocH,-1);
|
||
}
|
||
|
||
#pragma segment Main
|
||
/**********************************************************************
|
||
* BoxFClose - close a mailbox file represented by a toc. May be
|
||
* called on open mailbox, reports any errors to user.
|
||
**********************************************************************/
|
||
int BoxFClose(TOCHandle tocH,Boolean flush)
|
||
{
|
||
int err=0;
|
||
FSSpec spec;
|
||
|
||
if ((*tocH)->refN)
|
||
{
|
||
NoteFreeSpace(tocH);
|
||
err = flush ? MyFSClose((*tocH)->refN) : FSClose((*tocH)->refN);
|
||
(*tocH)->refN = 0;
|
||
spec = GetMailboxSpec(tocH,-1);
|
||
if (err) FileSystemError(CLOSE_MBOX,spec.name,err);
|
||
UL(tocH);
|
||
}
|
||
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* NoteFreeSpace - note the free space on a volume
|
||
************************************************************************/
|
||
void NoteFreeSpace(TOCHandle tocH)
|
||
{
|
||
FSSpec newSpec;
|
||
|
||
newSpec = GetMailboxSpec(tocH,-1);
|
||
IsAlias(&newSpec,&newSpec);
|
||
(*tocH)->volumeFree = VolumeFree(newSpec.vRefNum);
|
||
}
|
||
|
||
#pragma segment Mailbox
|
||
|
||
/**********************************************************************
|
||
* DeleteSum - remove a sum from a toc
|
||
**********************************************************************/
|
||
OSErr DeleteSum(TOCHandle tocH,int sumNum)
|
||
{
|
||
MSumPtr sum;
|
||
int mNum;
|
||
Str32 name;
|
||
|
||
ASSERT(tocH);
|
||
ASSERT(sumNum<(*tocH)->count);
|
||
ASSERT(sumNum>=0);
|
||
ASSERT((*tocH)->sums[sumNum].state != BUSY_SENDING);
|
||
if (!tocH || !(sumNum<(*tocH)->count) || !(sumNum>=0) || ((*tocH)->sums[sumNum].state == BUSY_SENDING))
|
||
return -1;
|
||
|
||
if (LogLevel&LOG_MOVE)
|
||
ComposeLogS(LOG_MOVE,nil,"\pDelete <20>%p,%p<> from <20>%p<>\015",(*tocH)->sums[sumNum].from,(*tocH)->sums[sumNum].subj,GetMailboxName(tocH,sumNum,name));
|
||
|
||
(*tocH)->analScanned = false;
|
||
|
||
if ((*tocH)->previewID==(*tocH)->sums[sumNum].uidHash && (*tocH)->previewPTE)
|
||
Preview(tocH,-1);
|
||
(*tocH)->maxValid = MIN((*tocH)->maxValid,sumNum-1);
|
||
if (IsQueued(tocH,sumNum)) ForceSend = 0;
|
||
ZapHandle((*tocH)->sums[sumNum].cache);
|
||
if (!(*tocH)->virtualTOC) DeleteMesgError(tocH,sumNum);
|
||
if (sumNum < (*tocH)->count-1) /* is this not the last sum? */
|
||
{
|
||
LDRef(tocH);
|
||
sum = (*tocH)->sums + sumNum;
|
||
BMD(sum+1,sum,((*tocH)->count-1-sumNum)*sizeof(MSumType));
|
||
UL(tocH);
|
||
for (mNum=sumNum; mNum < (*tocH)->count-1; mNum++)
|
||
if ((MessHandle)(*tocH)->sums[mNum].messH)
|
||
(*(MessHandle)(*tocH)->sums[mNum].messH)->sumNum--;
|
||
}
|
||
SetHandleBig_(tocH,
|
||
MAX(sizeof(TOCType),GetHandleSize_(tocH)-sizeof(MSumType)));
|
||
if (--(*tocH)->count == 0 && !(*tocH)->virtualTOC) ZeroMailbox(tocH);
|
||
|
||
TOCSetDirty(tocH,true);
|
||
return noErr;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* InvalSum - invalidate an entire message summary line
|
||
**********************************************************************/
|
||
void InvalSum(TOCHandle tocH,int sum)
|
||
{
|
||
Rect r;
|
||
MyWindowPtr win = (*tocH)->win;
|
||
GrafPtr oldPort;
|
||
long top,bottom;
|
||
|
||
if (!win) return;
|
||
if (!IsWindowVisible(GetMyWindowWindowPtr(win))) return;
|
||
|
||
top = win->topMargin + win->vPitch * (sum-GetControlValue(win->vBar));
|
||
bottom = top + win->vPitch + 1;
|
||
if (bottom < win->contR.top || top > win->contR.bottom)
|
||
// This summary is not visible
|
||
return;
|
||
|
||
GetPort(&oldPort);
|
||
SetPort_(GetMyWindowCGrafPtr(win));
|
||
|
||
r.top = top;
|
||
r.bottom = bottom;
|
||
r.left = win->contR.left;
|
||
r.right = win->contR.right;
|
||
InvalWindowRect(GetMyWindowWindowPtr(win),&r);
|
||
(*tocH)->resort = MAX((*tocH)->resort,kNoSlowResort);
|
||
SetPort_(oldPort);
|
||
}
|
||
|
||
/************************************************************************
|
||
* AddBox - add a mailbox to the menus
|
||
************************************************************************/
|
||
void AddBox(short function,UPtr name,short level,Boolean unread)
|
||
{
|
||
short base=function*MAX_BOX_LEVELS;
|
||
short menuId = level ? base+level : (function==MAILBOX?MAILBOX_MENU:TRANSFER_MENU);
|
||
MenuHandle mh=GetMHandle(menuId);
|
||
short item, lastItem;
|
||
Style theStyle;
|
||
Str63 scratch;
|
||
Boolean skipIMAP = (menuId==MAILBOX_MENU || menuId==TRANSFER_MENU) && IMAPExists();
|
||
|
||
lastItem=CountMenuItems(mh);
|
||
for (item=lastItem;item>0;item--)
|
||
{
|
||
if (HasSubmenu(mh,item)) continue;
|
||
GetItemStyle(mh,item,&theStyle);
|
||
if (theStyle&italic) break; /* "new" is italicized */
|
||
MyGetItem(mh,item,scratch);
|
||
if (skipIMAP)
|
||
{
|
||
if (scratch[1]=='-')
|
||
skipIMAP = false;
|
||
continue;
|
||
}
|
||
else if (scratch[1]=='-') break; /* menu separator (transfer) */
|
||
|
||
if (StringComp(scratch,name)<0) break;
|
||
}
|
||
MyInsMenuItem(mh,name,item);
|
||
if (function==MAILBOX)
|
||
{
|
||
// Set unread status
|
||
FixMenuUnread(mh,item+1,unread);
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* RemoveBox - remove a mailbox from the mailbox menus
|
||
************************************************************************/
|
||
void RemoveBox(short function,UPtr name,short level)
|
||
{
|
||
short base=function*MAX_BOX_LEVELS;
|
||
short menuId = level ? base+level : (function==MAILBOX?MAILBOX_MENU:TRANSFER_MENU);
|
||
MenuHandle mh=GetMHandle(menuId);
|
||
short item;
|
||
|
||
if (item = FindItemByName(mh,name))
|
||
{
|
||
if (function==MAILBOX)
|
||
{
|
||
// Make sure unread status of parent menu is correct
|
||
FixMenuUnread(mh,item,false); // Make this one unread before removing
|
||
}
|
||
DeleteMenuItem(mh,item);
|
||
if (CountMenuItems(mh)>=31)
|
||
// Under MacOS 8.5 item 32 going to item 31 may become disabled
|
||
EnableItem(mh,31);
|
||
}
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetMBDirName - get a the name of a mail folder, with shenanigans for top-level
|
||
**********************************************************************/
|
||
short GetMBDirName(short vRef, long dirId,UPtr name)
|
||
{
|
||
short sysVRef;
|
||
long sysDirId;
|
||
FSSpec spec;
|
||
OSErr err;
|
||
Str63 sTemp;
|
||
|
||
// If we're at the mail root, pretend we're one up
|
||
if (vRef==MailRoot.vRef && dirId==MailRoot.dirId)
|
||
{
|
||
vRef = Root.vRef;
|
||
dirId = Root.dirId;
|
||
}
|
||
|
||
if (err = GetDirName(nil,vRef,dirId,name)) // Name of Mail Folder
|
||
return err;
|
||
|
||
// If the standard Eudora Folder is in use (ie, named FOLDER_NAME and in
|
||
// the system folder) show FILE_ALIAS_EUDORA_FOLDER at the top
|
||
// level of the mailboxes window.
|
||
FindFolder(kOnSystemDisk,kSystemFolderType,False,&sysVRef,&sysDirId);
|
||
if (vRef==sysVRef &&
|
||
!FSMakeFSSpec(sysVRef, sysDirId, GetRString(sTemp,FOLDER_NAME), &spec) && // Spec for standard Eudora folder
|
||
dirId==SpecDirId(&spec))
|
||
GetRString(name,FILE_ALIAS_EUDORA_FOLDER);
|
||
|
||
return noErr;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetNewMailbox - get the name of and create a new mailbox
|
||
* returns 1 for normal mb's, or else dirId
|
||
**********************************************************************/
|
||
Boolean GetNewMailbox(short vRef,long inDirId,FSSpecPtr spec,Boolean *folder,Boolean *xfer)
|
||
{
|
||
MyWindowPtr dgPtrWin;
|
||
DialogPtr dgPtr;
|
||
short item;
|
||
Str255 name;
|
||
Str63 folderName;
|
||
extern ModalFilterUPP DlgFilterUPP;
|
||
|
||
if (GetMBDirName(vRef,inDirId,folderName)) *folderName = 0;
|
||
|
||
if ((dgPtrWin = GetNewMyDialog(NEW_MAILBOX_DLOG,nil,nil,InFront))==nil)
|
||
{
|
||
WarnUser(GENERAL,MemError());
|
||
return(False);
|
||
}
|
||
|
||
dgPtr = GetMyWindowDialogPtr (dgPtrWin);
|
||
|
||
if (!xfer) HideDialogItem(dgPtr,NEW_MAILBOX_NOXF);
|
||
MyParamText(folderName,"","","");
|
||
StartMovableModal(dgPtr);
|
||
ShowWindow(GetDialogWindow(dgPtr));
|
||
HiliteButtonOne(dgPtr);
|
||
do
|
||
{
|
||
SelectDialogItemText(dgPtr,NEW_MAILBOX_NAME,0,REAL_BIG);
|
||
PushCursor(iBeamCursor);
|
||
do
|
||
{
|
||
MyParamText(folderName,"","","");
|
||
MovableModalDialog(dgPtr,DlgFilterUPP,&item);
|
||
if (item==NEW_MAILBOX_FOLDER)
|
||
SetDItemState(dgPtr,item,!GetDItemState(dgPtr,item));
|
||
else if (item==NEW_MAILBOX_NOXF)
|
||
SetDItemState(dgPtr,item,!GetDItemState(dgPtr,item));
|
||
} while (item==NEW_MAILBOX_FOLDER || item==NEW_MAILBOX_NOXF);
|
||
PopCursor();
|
||
GetDIText(dgPtr,NEW_MAILBOX_NAME,name);
|
||
*folder = GetDItemState(dgPtr,NEW_MAILBOX_FOLDER);
|
||
if (xfer) *xfer = GetDItemState(dgPtr,NEW_MAILBOX_NOXF);
|
||
spec->vRefNum = vRef;
|
||
spec->parID = inDirId;
|
||
PSCopy(spec->name,name); /* FSMakeFSSpec screws up this step if the name is too long.
|
||
We want to catch that in BadMailboxName, not here,
|
||
so don't use FSMakeFSSpec. */
|
||
}
|
||
while (item==NEW_MAILBOX_OK && BadMailboxName(spec,*folder));
|
||
|
||
EndMovableModal(dgPtr);
|
||
DisposDialog_(dgPtr);
|
||
|
||
return(item==NEW_MAILBOX_OK);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* RenameMailbox - rename a mailbox
|
||
**********************************************************************/
|
||
int RenameMailbox(FSSpecPtr spec, UPtr newName, Boolean folder)
|
||
{
|
||
int err;
|
||
Str63 oldTOCName,suffix;
|
||
Str63 newTOCName;
|
||
FSSpec tocSpec;
|
||
|
||
err = HRename(spec->vRefNum,spec->parID,spec->name,newName);
|
||
if (err) return(FileSystemError(RENAMING_BOX,spec->name,err));
|
||
|
||
if (!folder)
|
||
{
|
||
// Rename TOC file also if it exists
|
||
GetRString(suffix,TOC_SUFFIX);
|
||
PCopy(oldTOCName,spec->name);
|
||
PCopy(newTOCName,newName);
|
||
PCat(oldTOCName,suffix);
|
||
PCat(newTOCName,suffix);
|
||
|
||
// Check for existence of old .TOC file
|
||
if (!FSMakeFSSpec(spec->vRefNum, spec->parID, oldTOCName, &tocSpec))
|
||
{
|
||
if (*newTOCName>31)
|
||
{
|
||
// TOC file name too long
|
||
TooLong(newName);
|
||
err = bdNamErr;
|
||
}
|
||
else
|
||
{
|
||
err = HRename(spec->vRefNum,spec->parID,oldTOCName,newTOCName);
|
||
if (err==fnfErr) err = 0;
|
||
if (err)
|
||
{
|
||
FileSystemError(RENAMING_BOX,oldTOCName,err);
|
||
}
|
||
}
|
||
if (err)
|
||
// Restore mailbox name since we couldn't rename TOC file
|
||
(void) HRename(spec->vRefNum,spec->parID,newName,spec->name);
|
||
}
|
||
}
|
||
|
||
return(err);
|
||
}
|
||
|
||
|
||
/**********************************************************************
|
||
* BadMailboxName - figure out if a mailbox name is ok by trying to
|
||
* create the mailbox.
|
||
**********************************************************************/
|
||
Boolean BadMailboxName(FSSpecPtr spec,Boolean folder)
|
||
{
|
||
int err;
|
||
Str15 suffix;
|
||
long newDirId;
|
||
Boolean success;
|
||
|
||
// Is this box being created inside an IMAP cache folder? Then it's an IMAP mailbox.
|
||
if (IMAPAddMailbox(spec, folder, &success, false))
|
||
{
|
||
if (!success) Zero(*spec); // zero out the spec if we fail so we don't do anthing too stupid
|
||
return (false);
|
||
}
|
||
|
||
if (*spec->name>31-*GetRString(suffix,TOC_SUFFIX))
|
||
{
|
||
TooLong(spec->name);
|
||
return(True);
|
||
}
|
||
|
||
if (BadMailboxNameChars(spec))
|
||
return (True);
|
||
|
||
if (folder)
|
||
{
|
||
if (BoxMapCount>MAX_BOX_LEVELS)
|
||
{
|
||
WarnUser(TOO_MANY_LEVELS,MAX_BOX_LEVELS);
|
||
return(True);
|
||
}
|
||
if (err=FSpDirCreate(spec,smSystemScript,&newDirId))
|
||
{
|
||
FileSystemError(CREATING_MAILBOX,spec->name,err);
|
||
return(True);
|
||
}
|
||
AddBoxMap(spec->vRefNum,newDirId);
|
||
spec->parID = newDirId;
|
||
*spec->name = 0;
|
||
}
|
||
else
|
||
{
|
||
err = FSpCreate(spec,CREATOR,MAILBOX_TYPE,smSystemScript);
|
||
if (err)
|
||
{
|
||
FileSystemError(CREATING_MAILBOX,spec->name,err);
|
||
return(True);
|
||
}
|
||
}
|
||
return(False);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* BadMailboxNameChars - return TRUE if this mailbox name has some
|
||
* inappropriate characters.
|
||
**********************************************************************/
|
||
Boolean BadMailboxNameChars(FSSpecPtr spec)
|
||
{
|
||
char *cp;
|
||
|
||
if (spec->name[1]=='.')
|
||
{
|
||
WarnUser(LEADING_PERIOD,0);
|
||
return(True);
|
||
}
|
||
|
||
for (cp=spec->name+*spec->name;cp>spec->name;cp--)
|
||
{
|
||
if (*cp==':')
|
||
{
|
||
WarnUser(NO_COLONS_HERE,0);
|
||
return(True);
|
||
}
|
||
}
|
||
|
||
return (False);
|
||
}
|
||
|
||
/************************************************************************
|
||
* ZeroMailbox - set a mailbox's size to zero. Assumes box is empty
|
||
************************************************************************/
|
||
void ZeroMailbox(TOCHandle tocH)
|
||
{
|
||
if (!BoxFOpen(tocH))
|
||
{
|
||
SetEOF((*tocH)->refN,0L);
|
||
BoxFClose(tocH,false);
|
||
}
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* ChainTrash - move an entire alias chain to the trash
|
||
************************************************************************/
|
||
OSErr ChainTrash(FSSpecPtr spec)
|
||
{
|
||
FSSpec chain;
|
||
Boolean wasAlias, isFolder;
|
||
|
||
chain = *spec;
|
||
if (!ResolveAliasFile(&chain,False,&isFolder,&wasAlias) && wasAlias)
|
||
ChainTrash(&chain);
|
||
return(FSpTrash(spec));
|
||
}
|
||
|
||
/************************************************************************
|
||
* RemoveMailbox - move a mailbox to the trash
|
||
************************************************************************/
|
||
int RemoveMailbox(FSSpecPtr spec,Boolean trashChain)
|
||
{
|
||
TOCHandle tocH;
|
||
int err;
|
||
FSSpec tocSpec;
|
||
short sumNum;
|
||
|
||
/*
|
||
* open windows
|
||
*/
|
||
if (tocH = FindTOC(spec))
|
||
{
|
||
TOCSetDirty(tocH,false);
|
||
for (sumNum=0;sumNum<(*tocH)->count;sumNum++)
|
||
if ((*tocH)->sums[sumNum].messH)
|
||
CloseMyWindow(GetMyWindowWindowPtr((*(*tocH)->sums[sumNum].messH)->win));
|
||
if ((*tocH)->win) CloseMyWindow(GetMyWindowWindowPtr((*tocH)->win));
|
||
}
|
||
|
||
/*
|
||
* files
|
||
*/
|
||
if (err = trashChain ? ChainTrash(spec) : FSpTrash(spec))
|
||
return(FileSystemError(DELETING_BOX,spec->name,err));
|
||
Box2TOCSpec(spec,&tocSpec);
|
||
err = trashChain ? ChainTrash(&tocSpec) : FSpTrash(&tocSpec);
|
||
if (err==fnfErr || err==bdNamErr || err==paramErr) err = 0;
|
||
if (err) return(FileSystemError(DELETING_BOX,tocSpec.name,err));
|
||
|
||
return(noErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* MessagePosition - save/restore position for a new message window
|
||
************************************************************************/
|
||
Boolean MessagePosition(Boolean save,MyWindowPtr win)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
TOCHandle tocH = (*(MessHandle)GetMyWindowPrivateData(win))->tocH;
|
||
short sumNum = (*(MessHandle)GetMyWindowPrivateData(win))->sumNum;
|
||
Rect r;
|
||
Boolean zoomed;
|
||
Boolean res=True;
|
||
|
||
if (save)
|
||
{
|
||
if (!(*tocH)->virtualTOC)
|
||
{
|
||
utl_SaveWindowPos(winWP,&r,&zoomed);
|
||
(*tocH)->sums[sumNum].u.savedPos = r;
|
||
if (zoomed)
|
||
(*tocH)->sums[sumNum].flags |= FLAG_ZOOMED;
|
||
else
|
||
(*tocH)->sums[sumNum].flags &= ~FLAG_ZOOMED;
|
||
TOCSetDirty(tocH,true);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
r = (*tocH)->sums[sumNum].u.savedPos;
|
||
zoomed = false;
|
||
if (r.right && r.right>r.left && r.bottom && r.bottom>r.top)
|
||
zoomed = ((*tocH)->sums[sumNum].flags & FLAG_ZOOMED)!=0;
|
||
else
|
||
{
|
||
Point corner;
|
||
short val;
|
||
|
||
GetPortBounds(GetWindowPort(winWP),&r);
|
||
if (!(val=GetPrefLong(PREF_MWIDTH))) val=GetRLong(DEF_MWIDTH);
|
||
r.right = r.left + MessWi(win);
|
||
if (val=GetPrefLong(PREF_MHEIGHT))
|
||
r.bottom = r.top + win->vPitch*val;
|
||
MyStaggerWindow(winWP,&r,&corner);
|
||
OffsetRect(&r,corner.h-r.left,corner.v-r.top);
|
||
res = zoomed = False;
|
||
}
|
||
SanitizeSize(win,&r);
|
||
utl_RestoreWindowPos(winWP,&r,zoomed,1,TitleBarHeight(winWP),LeftRimWidth(winWP),(void*)FigureZoom,(void*)DefPosition);
|
||
}
|
||
return(res);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TooLong - complain about a name that's too long
|
||
************************************************************************/
|
||
void TooLong(UPtr name)
|
||
{
|
||
Str63 toolong1,toolong2;
|
||
MyParamText(GetRString(toolong1,BOX_TOO_LONG1),name,
|
||
GetRString(toolong2,BOX_TOO_LONG2),"");
|
||
ReallyDoAnAlert(OK_ALRT,Note);
|
||
}
|
||
|
||
/************************************************************************
|
||
* FindDirLevel - find the level of a particular subfolder
|
||
************************************************************************/
|
||
short FindDirLevel(short vRef,long dirId)
|
||
{
|
||
short level;
|
||
short n=BoxMapCount;
|
||
|
||
for (level=0;level<n;level++)
|
||
if ((*BoxMap)[level].dirId==dirId && SameVRef((*BoxMap)[level].vRef,vRef))
|
||
return(level && g16bitSubMenuIDs ? level+BOX_MENU_START-1 : level);
|
||
|
||
return(-1);
|
||
}
|
||
|
||
/************************************************************************
|
||
* BuildBoxCount - build the numbered list of mailboxes (for Find)
|
||
************************************************************************/
|
||
void BuildBoxCount(void)
|
||
{
|
||
if (BoxCount) ZapHandle(BoxCount);
|
||
BoxCount = NuHandle(0);
|
||
if (!BoxCount) {WarnUser(MEM_ERR,MemError()); return;}
|
||
|
||
AddBoxCountItem(MAILBOX_IN_ITEM,MailRoot.vRef,MailRoot.dirId);
|
||
AddBoxCountItem(MAILBOX_OUT_ITEM,MailRoot.vRef,MailRoot.dirId);
|
||
AddBoxCountItem(MAILBOX_JUNK_ITEM,MailRoot.vRef,MailRoot.dirId);
|
||
AddBoxCountItem(MAILBOX_TRASH_ITEM,MailRoot.vRef,MailRoot.dirId);
|
||
AddBoxCountMenu(MAILBOX_MENU,MAILBOX_FIRST_USER_ITEM,MailRoot.vRef,MailRoot.dirId,true);
|
||
}
|
||
|
||
/************************************************************************
|
||
* AddBoxMap - add an entry to the boxmap handle
|
||
************************************************************************/
|
||
OSErr AddBoxMap(short vRef,long dirId)
|
||
{
|
||
BoxMapType bmt;
|
||
short err;
|
||
|
||
bmt.vRef = vRef;
|
||
bmt.dirId = dirId;
|
||
err = PtrPlusHand_(&bmt,BoxMap,sizeof(bmt));
|
||
if (err) WarnUser(MEM_ERR,err);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* AddBoxCountMenu - add the contents of a menu to the BoxCount thingy
|
||
************************************************************************/
|
||
void AddBoxCountMenu(short menuID, short item, short vRef,long dirId,Boolean includeIMAP)
|
||
{
|
||
MenuHandle mh=GetMHandle(menuID);
|
||
short n=CountMenuItems(mh);
|
||
short it;
|
||
Str255 s;
|
||
|
||
|
||
for (;item<=n;item++)
|
||
{
|
||
if (HasSubmenu(mh,item))
|
||
{
|
||
short thisVRef;
|
||
long thisDirId;
|
||
|
||
it = SubmenuId(mh,item);
|
||
MenuID2VD(it,&thisVRef,&thisDirId);
|
||
AddBoxCountMenu(it,MAILBOX_FIRST_USER_ITEM-MAILBOX_BAR1_ITEM,
|
||
thisVRef,thisDirId,true);
|
||
}
|
||
else
|
||
{
|
||
GetMenuItemText(mh,item,s);
|
||
if (StringSame(s,"\p-"))
|
||
{
|
||
if (menuID==MAILBOX_MENU && !includeIMAP)
|
||
return; // don't add IMAP folders
|
||
}
|
||
else
|
||
AddBoxCountItem(item,vRef,dirId);
|
||
}
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* AddBoxCountItem - add a single item to the BoxCount list
|
||
************************************************************************/
|
||
void AddBoxCountItem(short item,short vRef,long dirId)
|
||
{
|
||
BoxCountElem bce;
|
||
|
||
bce.item = item;
|
||
bce.dirId = dirId;
|
||
bce.vRef = vRef;
|
||
if (PtrPlusHand_(&bce,BoxCount,sizeof(bce))) WarnUser(MEM_ERR,MemError());
|
||
}
|
||
|
||
/**********************************************************************
|
||
* IsMailboxChoice - is this menu choice for a mailbox?
|
||
**********************************************************************/
|
||
Boolean IsMailboxChoice(short menu,short item)
|
||
{
|
||
MenuHandle mh;
|
||
short levels = HandleCount(BoxMap);
|
||
|
||
if (menu==TRANSFER_MENU || menu==MAILBOX_MENU || (g16bitSubMenuIDs ?
|
||
(menu>=BOX_MENU_START && menu < BOX_MENU_START+levels || menu>=BOX_MENU_START+gMaxBoxLevels && menu<BOX_MENU_START+gMaxBoxLevels+levels)
|
||
: (menu>=1 && menu<1+levels || menu>=MAX_BOX_LEVELS && menu<MAX_BOX_LEVELS+levels)))
|
||
return(item>=1 && (mh=GetMHandle(menu)) && item<=CountMenuItems(mh));
|
||
else
|
||
return(False);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MailboxAlias - get the aliased name of a mailbox
|
||
**********************************************************************/
|
||
PStr MailboxAlias(short which,PStr name)
|
||
{
|
||
switch(which)
|
||
{
|
||
case IN: GetRString(name,FILE_ALIAS_IN); break;
|
||
case OUT: GetRString(name,FILE_ALIAS_OUT); break;
|
||
case JUNK: GetRString(name,FILE_ALIAS_JUNK); break;
|
||
case TRASH: GetRString(name,FILE_ALIAS_TRASH); break;
|
||
}
|
||
return(name);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MailboxSpecAlias - get the aliased name of a mailbox from a filespec
|
||
**********************************************************************/
|
||
PStr MailboxSpecAlias(FSSpecPtr spec,PStr name)
|
||
{
|
||
if (spec->vRefNum==MailRoot.vRef && spec->parID==MailRoot.dirId)
|
||
{
|
||
if (EqualStrRes(spec->name,IN)) GetRString(name,FILE_ALIAS_IN);
|
||
else if (EqualStrRes(spec->name,OUT)) GetRString(name,FILE_ALIAS_OUT);
|
||
else if (EqualStrRes(spec->name,JUNK)) GetRString(name,FILE_ALIAS_JUNK);
|
||
else if (EqualStrRes(spec->name,TRASH)) GetRString(name,FILE_ALIAS_TRASH);
|
||
else PCopy(name,spec->name);
|
||
}
|
||
else PCopy(name,spec->name);
|
||
return(name);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MailboxFile - get the filename of a mailbox
|
||
**********************************************************************/
|
||
PStr MailboxFile(short which,PStr name)
|
||
{
|
||
if (which) GetRString(name,which);
|
||
return(name);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MailboxMenuFile - get the filename from a menu
|
||
**********************************************************************/
|
||
PStr MailboxMenuFile(short mid,short item,PStr name)
|
||
{
|
||
Str31 prefix;
|
||
|
||
if ((mid==MAILBOX_MENU || mid==TRANSFER_MENU) && item<=MAILBOX_BAR1_ITEM)
|
||
{
|
||
switch(item)
|
||
{
|
||
case MAILBOX_IN_ITEM: GetRString(name,IN); break;
|
||
case MAILBOX_OUT_ITEM: GetRString(name,OUT); break;
|
||
case MAILBOX_JUNK_ITEM: GetRString(name,JUNK); break;
|
||
case MAILBOX_TRASH_ITEM: GetRString(name,TRASH); break;
|
||
}
|
||
if (mid==TRANSFER_MENU)
|
||
PInsert(name,31,GetRString(prefix,TRANSFER_PREFIX),name+1);
|
||
}
|
||
else
|
||
MyGetItem(GetMHandle(mid),item,name);
|
||
return(name);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* GetTransferParams - Turn menu choice into mailbox items.
|
||
*
|
||
* xfer - set to true if the choice was a transfer
|
||
* spec - the FSSpec of the mailbox in question
|
||
* function result - true if the current message(s) should be transferred to
|
||
* the chosen mailbox
|
||
************************************************************************/
|
||
Boolean GetTransferParams(short menu,short item,FSSpecPtr spec,Boolean *xfer)
|
||
{
|
||
Boolean folder=False, noxfer=False;
|
||
Str31 fix;
|
||
FSSpec newSpec;
|
||
Boolean root;
|
||
MenuHandle mh=GetMHandle(menu);
|
||
|
||
/*
|
||
* find owning directory
|
||
*/
|
||
if (xfer)
|
||
*xfer = menu==TRANSFER_MENU || menu==MAILBOX_MENU ? menu==TRANSFER_MENU : menu>MAX_BOX_LEVELS;
|
||
|
||
if (spec)
|
||
{
|
||
MenuID2VD(menu,&spec->vRefNum,&spec->parID);
|
||
root = IsRoot(spec);
|
||
if (root ? item==TRANSFER_NEW_ITEM : item==TRANSFER_NEW_ITEM-TRANSFER_BAR1_ITEM)
|
||
{
|
||
do
|
||
{
|
||
if (GetNewMailbox(spec->vRefNum,spec->parID,&newSpec,&folder,xfer?&noxfer:nil))
|
||
{
|
||
Boolean wasIMAP = IsIMAPMailboxFile(&newSpec) || IsIMAPCacheFolder(&newSpec);
|
||
|
||
// if we just added a folder or an IMAP mailbox (which is a folder), rebuild the whole mailbox tree
|
||
if (folder || wasIMAP)
|
||
{
|
||
BuildBoxMenus();
|
||
MBTickle(nil,nil);
|
||
}
|
||
// otherwise, make sure the mailbox got created and add it. It might not have gotten created if we failed to add an IMAP box.
|
||
else if (FSpExists(&newSpec)==noErr) AddBoxHigh(&newSpec);
|
||
|
||
*spec = newSpec;
|
||
}
|
||
else return(False);
|
||
}
|
||
while (folder);
|
||
}
|
||
#ifdef TWO
|
||
else if (root ? item==TRANSFER_OTHER_ITEM : item==TRANSFER_OTHER_ITEM-TRANSFER_BAR1_ITEM)
|
||
{
|
||
// if this is the "This Mailbox" item, return a spec pointing to the parent folder.
|
||
GetMenuTitle(mh,spec->name);
|
||
if (IsIMAPVD(spec->vRefNum, spec->parID))
|
||
return (true);
|
||
else
|
||
return(!AskGraft(spec->vRefNum,spec->parID,spec));
|
||
}
|
||
#endif
|
||
else
|
||
{
|
||
MailboxMenuFile(menu,item,spec->name);
|
||
TrimPrefix(spec->name,GetRString(fix,TRANSFER_PREFIX));
|
||
}
|
||
|
||
// if this was an IMAP mailbox, then the spec is pointing to the folder.
|
||
if (IsIMAPCacheFolder(spec)) spec->parID = SpecDirId(spec);
|
||
|
||
}
|
||
return(!noxfer);
|
||
}
|
||
|
||
/************************************************************************
|
||
* AppendXferSelection - append the menu item for transfer to selection, if appropriate
|
||
************************************************************************/
|
||
OSErr AppendXferSelection(PETEHandle pte,MenuHandle contextMenu)
|
||
{
|
||
Str255 s;
|
||
MenuAndScoreHandle mash;
|
||
Boolean divided = false;
|
||
Str31 name;
|
||
OSErr err = fnfErr;
|
||
short smid;
|
||
|
||
if (!AttIsSelected(nil,pte,-1,-1,0,nil,nil))
|
||
if (*CollapseLWSP(PeteSelectedString(s,pte)))
|
||
if (*s<31)
|
||
if (IsEnabled(TRANSFER_MENU,0))
|
||
if (!BoxMatchMenuItems(s,&mash,BoxMatchScore))
|
||
{
|
||
short n = HandleCount(mash);
|
||
short i = GetRLong(MAX_CONTEXT_FILE_CHOICES);
|
||
|
||
n = MIN(n,i);
|
||
|
||
for (i=0;i<n;i++)
|
||
{
|
||
short menu = (*mash)[i].menu;
|
||
short item = (*mash)[i].item;
|
||
short newItem;
|
||
short realMenu = menu==MAILBOX_MENU ? TRANSFER_MENU : menu + MAX_BOX_LEVELS;
|
||
|
||
if (IsEnabled(realMenu,item))
|
||
{
|
||
if (!divided)
|
||
{
|
||
divided = true;
|
||
if (CountMenuItems(contextMenu) && !MenuItemIsSeparator(contextMenu,CountMenuItems(contextMenu)))
|
||
AppendMenu(contextMenu,"\p-"); // add a divider
|
||
}
|
||
CopyMenuItem(GetMHandle(realMenu),item,contextMenu,REAL_BIG);
|
||
MyGetItem(GetMHandle(menu),item,name);
|
||
newItem = CountMenuItems(contextMenu);
|
||
SetMenuItemCommandID(contextMenu,newItem,(realMenu<<16)|item);
|
||
// rename for clarity
|
||
ComposeRString(s,TRANSFER_CONTEXT_FMT,name);
|
||
MySetItem(contextMenu,newItem,s);
|
||
|
||
// special case for IMAP mailboxes with children
|
||
smid = SubmenuId(contextMenu,newItem);
|
||
if (smid != 0)
|
||
{
|
||
// remove the submenu
|
||
SetMenuItemHierarchicalMenu(contextMenu,newItem,NULL);
|
||
|
||
// set the command id to "This Mailbox..." in the submeny
|
||
SetMenuItemCommandID(contextMenu,newItem,(smid<<16)|2);
|
||
}
|
||
err = noErr;
|
||
}
|
||
}
|
||
ZapHandle(mash);
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
/************************************************************************
|
||
* Menu2VD - menu into vref & dirID
|
||
************************************************************************/
|
||
void Menu2VD(MenuHandle mh,short *vRef,long *dirId)
|
||
{
|
||
MenuID2VD(GetMenuID(mh),vRef,dirId);
|
||
}
|
||
|
||
/************************************************************************
|
||
* MenuID2VD - menu into vref & dirID
|
||
************************************************************************/
|
||
void MenuID2VD(short menuID,short *vRef,long *dirId)
|
||
{
|
||
if (menuID==MAILBOX_MENU || menuID==TRANSFER_MENU)
|
||
{
|
||
*vRef = MailRoot.vRef;
|
||
*dirId = MailRoot.dirId;
|
||
}
|
||
else
|
||
{
|
||
if (g16bitSubMenuIDs)
|
||
menuID -= BOX_MENU_START-1;
|
||
if (menuID > gMaxBoxLevels)
|
||
menuID -= gMaxBoxLevels; // was from Transfer menu
|
||
*vRef = (*BoxMap)[menuID].vRef;
|
||
*dirId = (*BoxMap)[menuID].dirId;
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* VD2MenuId - turn vref & dirID into menu id
|
||
************************************************************************/
|
||
short VD2MenuId(short vRef,long dirId)
|
||
{
|
||
return((dirId==MailRoot.dirId && SameVRef(vRef,MailRoot.vRef)) ?
|
||
MAILBOX_MENU : FindDirLevel(vRef,dirId));
|
||
}
|
||
|
||
/************************************************************************
|
||
* SelectMessage - select a single message in a mailbox
|
||
************************************************************************/
|
||
void SelectMessage(TOCHandle tocH,short mNum)
|
||
{
|
||
SelectBoxRange(tocH,mNum,mNum,False,0,0);
|
||
BoxCenterSelection((*tocH)->win);
|
||
}
|
||
|
||
/************************************************************************
|
||
* BoxSpecByName - Find a given mailbox by a (possibly partial path) name
|
||
************************************************************************/
|
||
OSErr BoxSpecByName(FSSpecPtr spec,PStr name)
|
||
{
|
||
MenuHandle mh = GetMHandle(MAILBOX_MENU);
|
||
UPtr spot;
|
||
Str31 leaf;
|
||
|
||
if (*name && name[1]==':')
|
||
{
|
||
if (!Path2Box(name,spec)) return(noErr);
|
||
else
|
||
{
|
||
spot = PRIndex(name,':');
|
||
MakePStr(leaf,spot+1,*name-(spot-name));
|
||
return(BoxSpecByNameInMenu(mh,spec,leaf));
|
||
}
|
||
}
|
||
return(BoxSpecByNameInMenu(mh,spec,name));
|
||
}
|
||
|
||
/************************************************************************
|
||
* BoxMatchMenuItems - Find a list of mailboxes that more or less match a name
|
||
************************************************************************/
|
||
OSErr BoxMatchMenuItems(PStr name,MenuAndScoreHandle *mashPtr,int score())
|
||
{
|
||
MenuHandle mh = GetMHandle(MAILBOX_MENU);
|
||
Accumulator a;
|
||
OSErr err;
|
||
|
||
Zero(a);
|
||
|
||
err = BoxMatchMenuItemsInMenu(mh,&a,name,score);
|
||
|
||
if (err) AccuZap(a);
|
||
else if (a.offset)
|
||
{
|
||
AccuTrim(&a);
|
||
*mashPtr = a.data;
|
||
QuickSort(LDRef(a.data),sizeof(MenuAndScore),0,a.offset/sizeof(MenuAndScore)-1,CompareMAS,SwapMAS);
|
||
UL(a.data);
|
||
}
|
||
|
||
return err ? err : (a.data ? noErr:fnfErr);
|
||
}
|
||
|
||
int CompareMAS(MenuAndScorePtr mas1,MenuAndScorePtr mas2)
|
||
{
|
||
return (mas1->score-mas2->score);
|
||
}
|
||
|
||
void SwapMAS(MenuAndScorePtr mas1,MenuAndScorePtr mas2)
|
||
{
|
||
MenuAndScore mas;
|
||
|
||
mas = *mas2;
|
||
*mas2 = *mas1;
|
||
*mas1 = mas;
|
||
}
|
||
|
||
|
||
|
||
/************************************************************************
|
||
* BoxMatchMenuItemsInMenu - Find a list of mailboxes that more or less match a name
|
||
************************************************************************/
|
||
OSErr BoxMatchMenuItemsInMenu(MenuHandle mh, AccuPtr a,PStr name,int score())
|
||
{
|
||
short item;
|
||
short n;
|
||
short sub;
|
||
MenuHandle subMH;
|
||
OSErr err = fnfErr;
|
||
|
||
if (err=BoxMatchMenuItemsIn1Menu(mh,a,name,score)) return err;
|
||
|
||
n = CountMenuItems(mh);
|
||
for (item=1;item<=n;item++)
|
||
if (HasSubmenu(mh,item))
|
||
{
|
||
sub = SubmenuId(mh,item);
|
||
if (subMH=GetMHandle(sub))
|
||
{
|
||
err = BoxMatchMenuItemsInMenu(subMH,a,name,score);
|
||
if (err && err!=fnfErr) break;
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* BoxMatchMenuItemsIn1Menu - find a mailbox in a single menu
|
||
**********************************************************************/
|
||
OSErr BoxMatchMenuItemsIn1Menu(MenuHandle mh, AccuPtr a,PStr name,int score())
|
||
{
|
||
Str63 itemTitle;
|
||
short i;
|
||
short n = CountMenuItems(mh);
|
||
short res;
|
||
Boolean root = GetMenuID(mh) == MAILBOX_MENU;
|
||
MenuAndScore mas;
|
||
OSErr err = noErr;
|
||
|
||
mas.menu = GetMenuID(mh);
|
||
|
||
for (i=(root?1:3);i<=n;i++)
|
||
if (!root || i!=MAILBOX_BAR1_ITEM && i!=MAILBOX_NEW_ITEM && i!=MAILBOX_OTHER_ITEM)
|
||
{
|
||
if (HasSubmenu(mh,i))
|
||
{
|
||
short vRefNum;
|
||
long dirID;
|
||
|
||
Menu2VD(mh,&vRefNum,&dirID);
|
||
if (!IsIMAPVD(vRefNum,dirID))
|
||
// If not IMAP mailbox, we have reached the folders, so no more mailboxes
|
||
return(0);
|
||
}
|
||
|
||
if (root) MailboxMenuFile(MAILBOX_MENU,i,itemTitle);
|
||
else MyGetItem(mh,i,itemTitle);
|
||
res = score(name,itemTitle);
|
||
// The following optimiation check fails in IMAP mailboxes where
|
||
// the Inbox mailbox is at the top of the list so they mailboxes
|
||
// are not in alphabetical order
|
||
// if (res<0 && (!root||i>MAILBOX_BAR1_ITEM)) return(0); // hit one greater than we
|
||
if (res>=0)
|
||
{
|
||
// there is some sort of match here
|
||
mas.item = i;
|
||
mas.score = res;
|
||
err = AccuAddPtr(a,&mas,sizeof(mas));
|
||
if (err) break;
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* BoxMatchScore - score how well a candidate mailbox matches a name
|
||
* 0 is a perfect match
|
||
* 1 is a substring match (ie, the name is a substring of the mailbox)
|
||
* -1 is no match
|
||
**********************************************************************/
|
||
int BoxMatchScore(PStr name,PStr candidate)
|
||
{
|
||
UPtr spot;
|
||
int score=0;
|
||
|
||
if (spot=PFindSub(name,candidate))
|
||
{
|
||
// equal strings is best score (0)
|
||
if (*name==*candidate) return 0;
|
||
|
||
// if we don't start at the start of the string, add 50
|
||
if (spot>candidate+1)
|
||
{
|
||
score += 50;
|
||
// if the character before us is a word character, add 20
|
||
if (IsWordChar[spot[-1]]) score += 20;
|
||
}
|
||
// Now, check the end
|
||
spot += *name;
|
||
// if we don't end at the end of the string, add 50
|
||
if (spot<candidate+*candidate)
|
||
{
|
||
score += 50;
|
||
// if the character after us is a word char, add 50
|
||
if (IsWordChar[spot[1]]) score += 20;
|
||
}
|
||
// Finally, add points for any additional characters
|
||
score += *candidate - *name;
|
||
return score;
|
||
}
|
||
else return -1;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* BoxSpecByNameInMenu - search a given menu for a mailbox
|
||
**********************************************************************/
|
||
OSErr BoxSpecByNameInMenu(MenuHandle mh,FSSpecPtr spec,PStr name)
|
||
{
|
||
short item;
|
||
short n;
|
||
short sub;
|
||
MenuHandle subMH;
|
||
OSErr err = fnfErr;
|
||
|
||
if (item=FindBoxByNameIn1Menu(mh,name))
|
||
{
|
||
Menu2VD(mh,&spec->vRefNum,&spec->parID);
|
||
SimpleMakeFSSpec(spec->vRefNum,spec->parID,name,spec);
|
||
return(noErr);
|
||
}
|
||
|
||
n = CountMenuItems(mh);
|
||
for (item=1;item<=n;item++)
|
||
if (HasSubmenu(mh,item))
|
||
{
|
||
sub = SubmenuId(mh,item);
|
||
if (subMH=GetMHandle(sub))
|
||
{
|
||
err = BoxSpecByNameInMenu(subMH,spec,name);
|
||
if (!err) break;
|
||
if (err!=fnfErr) break;
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* FindBoxByNameIn1Menu - find a mailbox in a single menu
|
||
**********************************************************************/
|
||
short FindBoxByNameIn1Menu(MenuHandle mh, PStr name)
|
||
{
|
||
Str63 itemTitle;
|
||
short i;
|
||
short n = CountMenuItems(mh);
|
||
short res;
|
||
Boolean root = GetMenuID(mh) == MAILBOX_MENU;
|
||
|
||
for (i=(root?1:3);i<=n;i++)
|
||
if (!root || i!=MAILBOX_BAR1_ITEM && i!=MAILBOX_NEW_ITEM && i!=MAILBOX_OTHER_ITEM)
|
||
{
|
||
if (HasSubmenu(mh,i))
|
||
{
|
||
short vRefNum;
|
||
long dirID;
|
||
|
||
Menu2VD(mh,&vRefNum,&dirID);
|
||
if (!IsIMAPVD(vRefNum,dirID))
|
||
// If not IMAP mailbox, we have reached the folders, so no more mailboxes
|
||
return(0);
|
||
}
|
||
|
||
if (root) MailboxMenuFile(MAILBOX_MENU,i,itemTitle);
|
||
else MyGetItem(mh,i,itemTitle);
|
||
res = StringComp(name,itemTitle);
|
||
// The following optimiation check fails in IMAP mailboxes where
|
||
// the Inbox mailbox is at the top of the list so they mailboxes
|
||
// are not in alphabetical order
|
||
// if (res<0 && (!root||i>MAILBOX_BAR1_ITEM)) return(0); // hit one greater than we
|
||
if (!res) return(i); // found it!
|
||
}
|
||
return(0);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* FirstMsgSelected - return index of first message selected
|
||
************************************************************************/
|
||
short FirstMsgSelected(TOCHandle tocH)
|
||
{
|
||
short i;
|
||
|
||
for (i=0;i<(*tocH)->count;i++) if ((*tocH)->sums[i].selected) return(i);
|
||
return(-1);
|
||
}
|
||
|
||
/************************************************************************
|
||
* LastMsgSelected - return index of last message selected
|
||
************************************************************************/
|
||
short LastMsgSelected(TOCHandle tocH)
|
||
{
|
||
short i;
|
||
|
||
for (i=(*tocH)->count-1;i>=0;i--) if ((*tocH)->sums[i].selected) break;
|
||
return(i);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* CountSelectedMessages - count the number of messages selected
|
||
**********************************************************************/
|
||
long CountSelectedMessages(TOCHandle tocH)
|
||
{
|
||
short i;
|
||
long n = 0;
|
||
|
||
for (i=0;i<(*tocH)->count;i++) if ((*tocH)->sums[i].selected) n++;
|
||
return(n);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SizeSelectedMessages - figure out how big all the selected messages are
|
||
* Set countOpenOnes to false to ignore ones that are already open
|
||
**********************************************************************/
|
||
long SizeSelectedMessages(TOCHandle tocH,Boolean countOpenOnes)
|
||
{
|
||
short sum;
|
||
long size = 0;
|
||
|
||
for (sum=(*tocH)->count;sum--;)
|
||
{
|
||
if ((*tocH)->sums[sum].selected)
|
||
if (!countOpenOnes && (*tocH)->sums[sum].messH) continue;
|
||
else size += (*tocH)->sums[sum].length;
|
||
}
|
||
return size;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* CountFlaggedMessages - count the number of messages flagged for
|
||
* filtering
|
||
**********************************************************************/
|
||
long CountFlaggedMessages(TOCHandle tocH)
|
||
{
|
||
short i;
|
||
long n = 0;
|
||
|
||
for (i=0;i<(*tocH)->count;i++) if ((*tocH)->sums[i].flags&FLAG_UNFILTERED) n++;
|
||
return(n);
|
||
}
|
||
|
||
/************************************************************************
|
||
* IsMailbox - is a TEXT file a mailbox?
|
||
************************************************************************/
|
||
Boolean IsMailbox(FSSpecPtr spec)
|
||
{
|
||
Handle data;
|
||
short refN = 0;
|
||
long count;
|
||
UPtr spot;
|
||
uLong box, res, file;
|
||
Boolean from;
|
||
OSType type;
|
||
|
||
/*
|
||
* is name too long?
|
||
*/
|
||
if (*spec->name>MAX_BOX_NAME)
|
||
return(False);
|
||
|
||
/*
|
||
* is file the right type?
|
||
*/
|
||
type = FileTypeOf(spec);
|
||
if (type!='DROP' && type!='TEXT' && type!=IMAP_MAILBOX_TYPE) return(False);
|
||
|
||
/*
|
||
* toc's?
|
||
*/
|
||
TOCDates(spec,&box,&res,&file);
|
||
if (res || file) return(True);
|
||
|
||
/*
|
||
* No .toc, but maybe that's just because we need to build one
|
||
*/
|
||
if (!FSpDFSize(spec)) return(True); /* empty. vacuuously ok */
|
||
|
||
/*
|
||
* read the first line
|
||
*/
|
||
if (Snarf(spec,&data,255)) return(False); /* can't read. don't show */
|
||
|
||
/*
|
||
* is it an envelope?
|
||
*/
|
||
count = GetHandleSize(data)-1;
|
||
for (spot=LDRef(data);*spot!='\015' && spot<*data+count;spot++);
|
||
spot[1] = 0;
|
||
from = IsFromLine(*data);
|
||
ZapHandle(data);
|
||
|
||
return(from);
|
||
}
|
||
|
||
#ifdef TWO
|
||
/************************************************************************
|
||
* AskGraft - ask the user for a mailbox to graft
|
||
************************************************************************/
|
||
OSErr AskGraft(short vRef,long dirId,FSSpecPtr spec)
|
||
{
|
||
SFTypeList types;
|
||
FSSpec fetchedSpec;
|
||
Str255 prompt;
|
||
OSErr theError;
|
||
Boolean good;
|
||
|
||
types[0] = 'TEXT';
|
||
types[1] = 0;
|
||
|
||
GetRString (prompt, CHOOSE_MBOX);
|
||
|
||
/*
|
||
* fetch file
|
||
*/
|
||
theError = GetFileNav (types, CHOOSE_MAILBOX_NAV_TITLE, prompt, 0, false, &fetchedSpec, &good, nil);
|
||
/*
|
||
* is it a mailbox?
|
||
*/
|
||
if (good && !IsMailbox(&fetchedSpec))
|
||
{
|
||
FileSystemError(NOT_MAILBOX,fetchedSpec.name,0);
|
||
return(userCancelled);
|
||
}
|
||
|
||
/*
|
||
* make & return alias
|
||
*/
|
||
if (good) return(GraftMailbox(vRef,dirId,&fetchedSpec,spec,vRef==MailRoot.vRef && dirId==MailRoot.dirId));
|
||
else return(userCancelled);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* GraftMailbox - graft a mailbox into the tree
|
||
************************************************************************/
|
||
OSErr GraftMailbox(short vRef,long dirId,FSSpecPtr realSpec,FSSpecPtr boxSpec,Boolean temporary)
|
||
{
|
||
short err=1;
|
||
FSSpec tocSpec,localSpec,realTocSpec;
|
||
FInfo info;
|
||
Boolean wasThere = !HGetFInfo(vRef,dirId,realSpec->name,&info);
|
||
|
||
/*
|
||
* if mailbox exists and is in the current tree, just return, do not
|
||
* graft
|
||
*/
|
||
if (FindDirLevel(realSpec->vRefNum,realSpec->parID)>=0)
|
||
{
|
||
*boxSpec = *realSpec;
|
||
return(noErr);
|
||
}
|
||
|
||
/*
|
||
* if mailbox is currently aliased in the folder in question, just return
|
||
*/
|
||
if (!FSMakeFSSpec(vRef,dirId,realSpec->name,&localSpec) && IsAlias(&localSpec,&tocSpec)
|
||
&& SameSpec(&tocSpec,realSpec))
|
||
{
|
||
*boxSpec = localSpec;
|
||
return(noErr);
|
||
}
|
||
|
||
if (!(err = MakeAFinderAlias(realSpec,&localSpec)))
|
||
{
|
||
/*
|
||
* if the graft is temporary, mark the alias
|
||
*/
|
||
if (temporary && !FSpGetFInfo(&localSpec,&info))
|
||
{
|
||
// set the stationery bit. Yeah, it's a hack.
|
||
info.fdFlags |= kIsStationery;
|
||
FSpSetFInfo(&localSpec,&info);
|
||
}
|
||
|
||
/*
|
||
* is there an external toc?
|
||
*/
|
||
Box2TOCSpec(realSpec,&tocSpec);
|
||
if (!FSpGetFInfo(&tocSpec,&info))
|
||
{
|
||
if (PrefIsSet(PREF_NEW_TOC))
|
||
{
|
||
{
|
||
TOCHandle tocH = CheckTOC(realSpec);
|
||
if (tocH) WriteTOC(tocH);
|
||
ZapHandle(tocH);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
realTocSpec = tocSpec;
|
||
tocSpec.vRefNum = vRef;
|
||
tocSpec.parID = dirId;
|
||
err = MakeAFinderAlias(&realTocSpec,&tocSpec);
|
||
if (temporary && !FSpGetFInfo(&tocSpec,&info))
|
||
{
|
||
// set the stationery bit. Yeah, it's a hack.
|
||
info.fdFlags |= kIsStationery;
|
||
FSpSetFInfo(&tocSpec,&info);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!err)
|
||
{
|
||
/*
|
||
* ok. aliases in place; all is well with the world
|
||
*/
|
||
/*BuildBoxMenus();*/
|
||
AddBoxHigh(&localSpec);
|
||
}
|
||
}
|
||
|
||
if (err) FileSystemError(OPEN_MBOX,realSpec->name,err);
|
||
else *boxSpec = localSpec;
|
||
return(err);
|
||
}
|
||
|
||
#endif
|
||
|
||
/**********************************************************************
|
||
* RemoveBoxHigh - remove a box from the menus
|
||
**********************************************************************/
|
||
void RemoveBoxHigh(FSSpecPtr spec)
|
||
{
|
||
short level = FindDirLevel(spec->vRefNum,spec->parID);
|
||
Str63 xferName;
|
||
|
||
RemoveBox(MAILBOX,spec->name,level);
|
||
|
||
GetRString(xferName,TRANSFER_PREFIX);
|
||
PSCat(xferName,spec->name);
|
||
RemoveBox(TRANSFER,xferName,level);
|
||
BuildBoxCount();
|
||
MBTickle(nil,nil);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* AddBoxHigh - add a box to the menus
|
||
**********************************************************************/
|
||
void AddBoxHigh(FSSpecPtr spec)
|
||
{
|
||
short level = FindDirLevel(spec->vRefNum,spec->parID);
|
||
Str63 xferName;
|
||
FInfo info;
|
||
|
||
FSpGetFInfo(spec,&info); // Get unread status of box
|
||
AddBox(MAILBOX,spec->name,level,(info.fdFlags&0xe)!=0);
|
||
GetRString(xferName,TRANSFER_PREFIX);
|
||
PSCat(xferName,spec->name);
|
||
AddBox(TRANSFER,xferName,level,false);
|
||
BuildBoxCount();
|
||
MBTickle(nil,nil);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* PopupMailboxPath - popup a list of mailboxes and folders
|
||
**********************************************************************/
|
||
void PopupMailboxPath(MyWindowPtr win,TOCHandle tocH,short sum,Point pt)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
MenuHandle hMenu;
|
||
short top, left;
|
||
Rect rStruct;
|
||
Str255 s;
|
||
Boolean fMessage=false;
|
||
short menuIdx = 0;
|
||
FSSpec spec;
|
||
Boolean IsIMAP=false;
|
||
enum { kSelNone,kSelMailbox,kSelFolder };
|
||
short selection;
|
||
|
||
if (hMenu = NewMenu(MB_POPUP_MENU, ""))
|
||
{
|
||
// Build menu
|
||
// Start with current window
|
||
short menu, item;
|
||
MessHandle messH;
|
||
|
||
if (win)
|
||
{
|
||
if (fMessage = IsMessWindow(winWP))
|
||
{
|
||
// This is a message.
|
||
|
||
GetWTitle(winWP, s); // Add name of message
|
||
MyAppendMenu(hMenu, s);
|
||
messH = Win2MessH(win);
|
||
tocH = (*messH)->tocH;
|
||
sum = (*messH)->sumNum;
|
||
}
|
||
else
|
||
{
|
||
// This is a mailbox.
|
||
tocH = (TOCHandle)GetMyWindowPrivateData(win);
|
||
if ((*tocH)->virtualTOC)
|
||
// no popup on virtual mailboxes
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Build all folders
|
||
if (!TOCH2Menu(tocH,false,&menu,&item))
|
||
{
|
||
MenuHandle hAddMenu;
|
||
|
||
for(hAddMenu = GetMHandle(menu);hAddMenu;hAddMenu = ParentMailboxMenu(hAddMenu,&item))
|
||
{
|
||
MyGetItem(hAddMenu, item, s);
|
||
MyAppendMenu(hMenu, s);
|
||
}
|
||
}
|
||
|
||
// Name of Eudora Folder
|
||
spec = GetMailboxSpec(tocH,-1);
|
||
IsIMAP = (*tocH)->imapTOC;
|
||
|
||
if (!IsIMAP)
|
||
{
|
||
GetDirName(nil,Root.vRef,Root.dirId,s);
|
||
MyAppendMenu(hMenu, s);
|
||
}
|
||
|
||
InsertMenu(hMenu,-1);
|
||
|
||
selection = kSelNone;
|
||
if (win)
|
||
{
|
||
winWP = GetMyWindowWindowPtr (win);
|
||
// Popup from window title
|
||
GetWindowStructureBounds(winWP,&rStruct);
|
||
top = rStruct.top + 2;
|
||
left = rStruct.right + rStruct.left - MyGetWindowTitleWidth(winWP) - 29;
|
||
left = left/2;
|
||
if (left < rStruct.left + 19)
|
||
// Don't go too far to the left
|
||
left = rStruct.left + 19;
|
||
item = PopUpMenuSelect(hMenu, top, left, 0);
|
||
if (item > 1)
|
||
selection = item==2 && fMessage ? kSelMailbox : kSelFolder;
|
||
}
|
||
else
|
||
{
|
||
// Popup in mailbox summary
|
||
item = AFPopUpMenuSelect(hMenu,pt.v,pt.h,0);
|
||
if (item)
|
||
selection = item==1 ? kSelMailbox : kSelFolder;
|
||
}
|
||
|
||
switch (selection)
|
||
{
|
||
WindowPtr tocWinWP;
|
||
Handle hStringList;
|
||
|
||
case kSelMailbox:
|
||
// Open mailbox window
|
||
tocWinWP = GetMyWindowWindowPtr ((*tocH)->win);
|
||
ShowMyWindow(tocWinWP);
|
||
UserSelectWindow(tocWinWP);
|
||
SelectMessage(tocH,sum);
|
||
if (MainEvent.modifiers&optionKey) BoxSelectSame(tocH,SORT_SUBJECT_ITEM,sum);
|
||
break;
|
||
|
||
case kSelFolder:
|
||
|
||
// Create a list of names of mailbox folders starting with the highest
|
||
// level not including the Eudora folder and working on down to the item
|
||
// the was selected
|
||
if (hStringList = NuHandle(0))
|
||
{
|
||
short itemIdx;
|
||
|
||
itemIdx = CountMenuItems(hMenu);
|
||
for (; itemIdx >= item; itemIdx--)
|
||
{
|
||
MyGetItem(hMenu, itemIdx, s);
|
||
PtrAndHand(s, hStringList, *s+1);
|
||
}
|
||
// Add a null
|
||
*s = 0;
|
||
PtrAndHand(s, hStringList, 1);
|
||
|
||
MBOpenFolder(hStringList,IsIMAP);
|
||
DisposeHandle(hStringList);
|
||
}
|
||
break;
|
||
}
|
||
DeleteMenu(MB_POPUP_MENU);
|
||
DisposeMenu(hMenu);
|
||
}
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetMailboxSpec - get the filespec for the indicated mailbox (and message)
|
||
**********************************************************************/
|
||
FSSpec GetMailboxSpec(TOCHandle tocH,short sum)
|
||
{
|
||
FSSpec spec;
|
||
|
||
if (tocH)
|
||
{
|
||
if ((*tocH)->virtualTOC)
|
||
{
|
||
// virtual mailbox
|
||
short index;
|
||
|
||
if (sum < 0 || sum > (*tocH)->count) goto error;
|
||
index = (*tocH)->sums[sum].u.virtualMess.virtualMBIdx;
|
||
if (index < 0 || index >= (*tocH)->mailbox.virtualMB.specListCount) goto error;
|
||
return (*(*tocH)->mailbox.virtualMB.specList)[index];
|
||
}
|
||
|
||
// normal mailbox
|
||
return (*tocH)->mailbox.spec;
|
||
}
|
||
|
||
// no tocH--shouldn't happen
|
||
error:
|
||
Zero(spec);
|
||
return spec;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetMailboxName - get the name of the indicated mailbox (and message)
|
||
**********************************************************************/
|
||
UPtr GetMailboxName(TOCHandle tocH,short sum,UPtr name)
|
||
{
|
||
FSSpec spec;
|
||
|
||
spec = GetMailboxSpec(tocH,sum);
|
||
PCopy(name,spec.name);
|
||
return name;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetRealSummary - find real summary from a message serial number
|
||
**********************************************************************/
|
||
MSumPtr FindRealSummary(TOCHandle tocH, long serialNum, short *realSum)
|
||
{
|
||
short i,count;
|
||
MSumPtr sum;
|
||
|
||
if (tocH)
|
||
{
|
||
count = (*tocH)->count;
|
||
for(i=0,sum=(*tocH)->sums;i<count;i++,sum++)
|
||
if (serialNum==sum->serialNum)
|
||
{
|
||
// found it!
|
||
*realSum = i;
|
||
return sum;
|
||
}
|
||
}
|
||
return nil; // not found!
|
||
}
|
||
|
||
/**********************************************************************
|
||
* FindSumBySerialNum - find real summary from a message serial number
|
||
**********************************************************************/
|
||
short FindSumBySerialNum(TOCHandle tocH,long serialNum)
|
||
{
|
||
short sumNum;
|
||
|
||
return FindRealSummary(tocH, serialNum, &sumNum) ? sumNum : -1;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetRealTOC - if virtual TOC, return real one
|
||
**********************************************************************/
|
||
TOCHandle GetRealTOC(TOCHandle tocH,short sum,short *realSum)
|
||
{
|
||
*realSum = sum;
|
||
if (tocH)
|
||
{
|
||
if ((*tocH)->virtualTOC)
|
||
{
|
||
// virtual mailbox
|
||
FSSpec spec = GetMailboxSpec(tocH,sum);
|
||
TOCHandle realTocH;
|
||
|
||
if (!spec.vRefNum) goto error;
|
||
|
||
realTocH = FindTOC(&spec);
|
||
if (!realTocH)
|
||
{
|
||
if (GetMailbox(&spec,false)) goto error;
|
||
realTocH = FindTOC(&spec);
|
||
}
|
||
if (!realTocH) goto error;
|
||
if (sum < 0 || sum > (*tocH)->count) goto error;
|
||
|
||
// search for the message in the real TOC by message serial number
|
||
return FindRealSummary(realTocH, (*tocH)->sums[sum].u.virtualMess.linkSerialNum,realSum) ? realTocH: nil;
|
||
}
|
||
else
|
||
{
|
||
// not virtual TOC
|
||
return tocH;
|
||
}
|
||
}
|
||
|
||
error:
|
||
return nil;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* ProcessIMAPChanges - process IMAP adds, deletes, or updates
|
||
**********************************************************************/
|
||
static void ProcessIMAPChanges(Handle sumList,TOCHandle toc,IMAPUpdateType message)
|
||
{
|
||
short count;
|
||
MSumPtr pSum;
|
||
short sum;
|
||
OSErr err=noErr;
|
||
Boolean selected;
|
||
MailboxNodeHandle mbox = TOCToMbox(toc);
|
||
UIDCopyPtr pCopy;
|
||
TOCHandle toTocH;
|
||
TOCHandle tocH, hidTocH = NULL;
|
||
long numMessages, numUidResponses;
|
||
long j, newUid;
|
||
// NOTE: reverse PREF_IMAP_VISIBLE_SUM_FILTER once we're comfortable with hiding summaries while filtering.
|
||
Boolean bHideUnfilteredSums = !PrefIsSet(PREF_FOREGROUND_IMAP_FILTERING) && PrefIsSet(PREF_IMAP_VISIBLE_SUM_FILTER);
|
||
short flaggedColor = GetRLong(IMAP_FLAGGED_LABEL);
|
||
|
||
if (!sumList) return;
|
||
|
||
// find the hidden toc if needed
|
||
hidTocH = GetHiddenCacheMailbox(mbox, false, true);
|
||
|
||
// Handle copies first. The sumList doesn't actually contains summaries.
|
||
// As this deals with local cached message only, a best shot approach should suffice
|
||
if (message == kDoCopy)
|
||
{
|
||
count = GetHandleSize_(sumList)/sizeof(UIDCopyStruct);
|
||
for(pCopy=LDRef(sumList);count--;pCopy++)
|
||
{
|
||
toTocH = TOCBySpec(&(pCopy->toSpec));
|
||
|
||
if (toTocH && pCopy->hOldSums && pCopy->hNewUIDs)
|
||
{
|
||
numMessages = GetHandleSize_(pCopy->hOldSums)/sizeof(MSumType);
|
||
numUidResponses = GetHandleSize_(pCopy->hNewUIDs)/sizeof(long);
|
||
|
||
for (j = 0; j < numUidResponses; j++)
|
||
{
|
||
newUid = ((long *)(*(pCopy->hNewUIDs)))[j];
|
||
pSum = &(((MSumPtr)(*(pCopy->hOldSums)))[numMessages - numUidResponses + j]);
|
||
IMAPTransferLocalCache(toc, pSum, toTocH, newUid, pCopy->copy);
|
||
}
|
||
if (!IMAPFilteringUnderway() && (!pCopy->copy)) AddIMAPXfUndoUIDs(toc,toTocH,pCopy->hNewUIDs,false);
|
||
}
|
||
|
||
// Cleanup
|
||
ZapHandle(pCopy->hOldSums);
|
||
ZapHandle(pCopy->hNewUIDs);
|
||
}
|
||
|
||
goto done;
|
||
}
|
||
|
||
count = GetHandleSize_(sumList)/sizeof(MSumType);
|
||
for(pSum=LDRef(sumList);count--;pSum++)
|
||
{
|
||
Boolean found;
|
||
|
||
// Spin the cursor every 100 messages or so.
|
||
if (count && !(count%100)) CycleBalls();
|
||
|
||
// search for summary with same UID hash
|
||
if (message != kDoAdd)
|
||
{
|
||
found = false;
|
||
|
||
tocH = toc;
|
||
for(sum=0;sum<(*tocH)->count;sum++)
|
||
{
|
||
if (pSum->uidHash==(*tocH)->sums[sum].uidHash)
|
||
{
|
||
// found it!
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// look in the hidden toc if appropriate
|
||
if (!found && hidTocH)
|
||
{
|
||
tocH = hidTocH;
|
||
for(sum=0;sum<(*tocH)->count;sum++)
|
||
{
|
||
if (pSum->uidHash==(*tocH)->sums[sum].uidHash)
|
||
{
|
||
// found it!
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
switch (message)
|
||
{
|
||
case kDoAdd:
|
||
// does this message already exist in the cache? Maybe it was copied. Skip it.
|
||
if ((FindSumByHash(toc, pSum->uidHash) != -1) || (hidTocH && (FindSumByHash(hidTocH, pSum->uidHash) != -1)))
|
||
break;
|
||
|
||
// is this message a deleted or unfiltered message and should we hide it?
|
||
if (hidTocH && ((pSum->opts&OPT_DELETED) || (bHideUnfilteredSums && (pSum->flags&FLAG_UNFILTERED))))
|
||
tocH = hidTocH;
|
||
else
|
||
tocH = toc;
|
||
|
||
if (!SaveMessageSum(pSum,&tocH))
|
||
{
|
||
// end the resync
|
||
IMAPAbortResync(toc);
|
||
goto done;
|
||
}
|
||
|
||
// did we add a deleted message?
|
||
if (pSum->opts&OPT_DELETED)
|
||
SetIMAPMailboxNeeds(TOCToMbox(tocH), kNeedsAutoExp, true);
|
||
|
||
if (PrefIsSet(PREF_COUNT_ALL_IMAP) ||
|
||
// count only those messages received in InBox
|
||
TOCToMbox(toc)==LocateInboxForPers(TOCToPers(toc)))
|
||
UpdateNumStatWithTime(kStatReceivedMail,1,pSum->seconds+ZoneSecs());
|
||
break;
|
||
|
||
case kDoDeleteAttachments:
|
||
// go trash this message's attachments, if we should
|
||
if (found) CleanUpAttachmentsAfterIMAPTransfer(tocH, sum);
|
||
break;
|
||
|
||
case kDoDelete:
|
||
if (found)
|
||
{
|
||
selected = (*tocH)->sums[sum].selected;
|
||
DeleteIMAPSum(tocH,sum); // delete the summary
|
||
|
||
// select the next summary if we oughtta
|
||
if ((*tocH)->win && selected && !IMAPFilteringUnderway()) BoxSelectAfter((*tocH)->win,sum);
|
||
}
|
||
break;
|
||
|
||
case kDoUpdate:
|
||
// Don't update this message if it's in the list of pending changes.
|
||
if (found && !PendingMessFlagChange((*tocH)->sums[sum].uidHash, mbox))
|
||
{
|
||
// update the message state, unless it's in a state we want to keep
|
||
if (UpdatableIMAPState((*tocH)->sums[sum].state))
|
||
(*tocH)->sums[sum].state = pSum->state;
|
||
|
||
// update the message label if we ought to
|
||
if (SumColor(pSum) == flaggedColor) // new label is flagged, update the existing label
|
||
SetSumColor(tocH, sum,flaggedColor);
|
||
else if (SumColor((*tocH)->sums+sum) == flaggedColor) // old label is flagged, new label is not, turn off label
|
||
SetSumColor(tocH, sum,0);
|
||
//else
|
||
// leave the label alone
|
||
|
||
// update the deleted status
|
||
if (pSum->opts&OPT_DELETED) MarkSumAsDeleted(tocH, sum, true);
|
||
else MarkSumAsDeleted(tocH, sum, false);;
|
||
|
||
// make sure the summary is in the right toc.
|
||
if (hidTocH) HideShowSummary(tocH, toc, hidTocH, sum);
|
||
|
||
// redraw the summary
|
||
InvalSum(tocH,sum);
|
||
|
||
// save the changes
|
||
TOCSetDirty(tocH,true);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
done:
|
||
if (message != kDoDeleteAttachments) ZapHandle(sumList);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* IMAPRecvLine - read a line at a time from the spool file. Returns ".\015"
|
||
* at the ends of messages.
|
||
************************************************************************/
|
||
static OSErr IMAPRecvLine(TransStream stream, UPtr buffer, long *size)
|
||
{
|
||
static Boolean wasFrom;
|
||
static Boolean wasNl=True;
|
||
short lineType;
|
||
|
||
if (!buffer) {Boolean retVal = wasFrom; wasFrom = False; return(retVal);}
|
||
wasFrom=False;
|
||
(*size)--;
|
||
|
||
lineType = GetLine(buffer,*size,size,Lip);
|
||
if (*buffer=='\012')
|
||
{
|
||
// remove linefeed char
|
||
BMD(buffer+1,buffer,*size-1);
|
||
(*size)--;
|
||
buffer[*size] = 0;
|
||
}
|
||
if (!*size || !lineType || /*wasNl&&(wasFrom=IsFromLine(buffer)) ||*/ TellLine(Lip)>=gIMAPMsgEnd)
|
||
{
|
||
// signal end-of-message
|
||
*size = 2;
|
||
buffer[0]='.'; buffer[1]='\015'; buffer[2]=0;
|
||
}
|
||
else if (lineType && wasNl && *buffer=='.')
|
||
{
|
||
// insert '.' at beginning of line
|
||
BMD(buffer,buffer+1,*size);
|
||
(*size)++;
|
||
*buffer = '.';
|
||
buffer[*size] = 0;
|
||
}
|
||
wasNl = !lineType || buffer[*size-1]=='\015';
|
||
return(noErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* UpdateIMAPMailbox - check for any changes to the local IMAP mailbox
|
||
**********************************************************************/
|
||
void UpdateIMAPMailbox(TOCHandle toc)
|
||
{
|
||
Handle toAdd = nil, toUpdate = nil, toDelete = nil, toCopy = nil;
|
||
IMAPSResultHandle results = nil;
|
||
short sumNum;
|
||
MailboxNodeHandle mbox = nil;
|
||
FSSpec spec;
|
||
Boolean filter = false;
|
||
Boolean checkAttachments = false;
|
||
|
||
//
|
||
// Resync this mailbox if it needs it, and filtering is NOT currently underway
|
||
//
|
||
|
||
if (!IMAPFilteringUnderway() && (mbox = TOCToMbox(toc)))
|
||
{
|
||
if (DoesIMAPMailboxNeed(mbox, kNeedsResync))
|
||
{
|
||
// wait if there's already a resync operation underway
|
||
if (!IsIMAPOperationUnderway(IMAPResyncTask))
|
||
{
|
||
if (PrefIsSet(PREF_FOREGROUND_IMAP_FILTERING))
|
||
SetIMAPMailboxNeeds(mbox, kNeedsResync, false);
|
||
|
||
// is it visible?
|
||
LockMailboxNodeHandle(mbox);
|
||
if ((*toc)->win && IsWindowVisible(GetMyWindowWindowPtr ((*toc)->win)))
|
||
{
|
||
FetchNewMessages(toc, true, false, true, false);
|
||
SetIMAPMailboxNeeds(mbox, kNeedsResync, false);
|
||
}
|
||
UnlockMailboxNodeHandle(mbox);
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check for changes to TOC summaries
|
||
//
|
||
|
||
if (IMAPDelivery(toc,&toAdd,&toUpdate,&toDelete,&toCopy,&filter,&results,&mbox,&checkAttachments))
|
||
{
|
||
// Install IMAP summary changes
|
||
|
||
// process copies first. Deletes could step on them later.
|
||
ProcessIMAPChanges(toCopy,toc,kDoCopy);
|
||
|
||
if (toDelete==MSUM_DELETE_ALL)
|
||
{
|
||
// delete everything
|
||
for(sumNum=(*toc)->count;sumNum--;)
|
||
DeleteIMAPSum(toc,sumNum);
|
||
}
|
||
else
|
||
{
|
||
if (checkAttachments) ProcessIMAPChanges(toDelete,toc,kDoDeleteAttachments);
|
||
ProcessIMAPChanges(toDelete,toc,kDoDelete);
|
||
}
|
||
|
||
ProcessIMAPChanges(toAdd,toc,kDoAdd);
|
||
ProcessIMAPChanges(toUpdate,toc,kDoUpdate);
|
||
|
||
// if we succeeded ...
|
||
if (mbox)
|
||
{
|
||
// update the mailbox information ...
|
||
spec = (*toc)->mailbox.spec;
|
||
WriteIMAPMailboxInfo(&spec, mbox);
|
||
|
||
if (toc && (*toc)->win && IsWindowVisible (GetMyWindowWindowPtr ((*toc)->win)))
|
||
{
|
||
// resort mailbox if needed
|
||
if ((*toc)->resort) MBResort(toc);
|
||
|
||
/// do message selection
|
||
if (DoesIMAPMailboxNeed(mbox, kNeedsSelect))
|
||
{
|
||
SetIMAPMailboxNeeds(mbox, kNeedsSelect, true);
|
||
|
||
// make selection if nothing is selected.
|
||
if (LastMsgSelected(toc) < 0)
|
||
ShowBoxAt(toc,(*toc)->previewPTE ? -1:FumLub(toc),GetMyWindowWindowPtr ((*toc)->win));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// move downloaded messages from their spool file to the mailbox
|
||
//
|
||
|
||
while (IMAPMessagesWaiting(toc,&spec))
|
||
{
|
||
TOCHandle hidTocH = NULL;
|
||
|
||
// first, decode messages in the visible portion of the toc
|
||
DecodeIMAPMessages(toc, &spec);
|
||
|
||
// next, do the messages in the hidden tocH
|
||
if ((hidTocH = GetHiddenCacheMailbox(mbox, false, false))!=NULL)
|
||
DecodeIMAPMessages(hidTocH, &spec);
|
||
|
||
// mark this temp file as having been processed ...
|
||
MarkAsProcessed(&spec);
|
||
|
||
// Don't need spool file anymore
|
||
FSpDelete(&spec);
|
||
}
|
||
|
||
//
|
||
// do filtering
|
||
//
|
||
|
||
if (filter)
|
||
{
|
||
// the user has asked us filter the old fashioned foreground way
|
||
if (PrefIsSet(PREF_FOREGROUND_IMAP_FILTERING))
|
||
{
|
||
PersHandle oldPers = CurPers;
|
||
|
||
// Make sure this personality is set to filter incoming IMAP mail
|
||
mbox = TOCToMbox(toc);
|
||
CurPers = TOCToPers(toc);
|
||
|
||
if (CurPers && !PrefIsSet(PREF_IMAP_NO_FILTER_INBOX))
|
||
{
|
||
// if there are any no new mail alerts pending, forget them.
|
||
NoNewMailMe = false;
|
||
|
||
// filter the mailbox, display the mail alerts
|
||
NotifyNewMail(1,false,toc,nil);
|
||
}
|
||
|
||
CurPers = oldPers;
|
||
|
||
// reset the filter flags on all messages, whether filtering happened or not.
|
||
ResetFilterFlags(toc);
|
||
}
|
||
else
|
||
{
|
||
// start background IMAP filtering ...
|
||
NeedToFilterIMAP = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**********************************************************************
|
||
* DeleteIMAPSum - remove an IMAP summary from a toc
|
||
**********************************************************************/
|
||
void DeleteIMAPSum(TOCHandle tocH,int sumNum)
|
||
{
|
||
SearchUpdateSum(tocH, sumNum, tocH, (*tocH)->sums[sumNum].serialNum, false, true);
|
||
DeleteMessageLo(tocH,sumNum,true);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* UpdateIMAPMailbox - check for any changes to the local IMAP mailbox
|
||
**********************************************************************/
|
||
Boolean IsMailboxSubmenu(short menu)
|
||
{
|
||
return g16bitSubMenuIDs ? menu>=BOX_MENU_START&&menu<BOX_MENU_LIMIT : menu>=1&&menu<FIND_HIER_MENU;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* UpdateIMAPMailbox - check for any changes to the local IMAP mailbox
|
||
*
|
||
* Note, this needs to be COMPLETELY REMOVED. This is extremely slow
|
||
* and should be done another way. That's abig project for another
|
||
* day, though.
|
||
**********************************************************************/
|
||
void DecodeIMAPMessages(TOCHandle toc, FSSpecPtr spec)
|
||
{
|
||
OSErr err;
|
||
short count;
|
||
short fileRef;
|
||
IndexStruct IMAPIdx;
|
||
IndexStruct **hIMAPIndex;
|
||
short curIMAPIndex;
|
||
short countIMAP;
|
||
TransVector saveCurTrans = CurTrans;
|
||
short saveRefN=CurResFile();
|
||
PersHandle oldPers = CurPers;
|
||
static TransVector IMAPTrans = {nil,nil,nil,nil,nil,nil,nil,nil,nil,IMAPRecvLine,nil};
|
||
MailboxNodeHandle mbox = TOCToMbox(toc);
|
||
short sumNum;
|
||
|
||
/*
|
||
* allocate the lineio pointer
|
||
*/
|
||
if (!Lip) Lip = NuPtrClear(sizeof(*Lip));
|
||
if (!Lip)
|
||
{
|
||
(WarnUser(MEM_ERR,MemError()));
|
||
goto msgDone;
|
||
}
|
||
if (FSpOpenLine(spec,fsRdWrPerm,Lip))
|
||
goto msgDone;
|
||
|
||
// CurPers must be set to the owning personality
|
||
mbox = TOCToMbox(toc);
|
||
CurPers = TOCToPers(toc);
|
||
|
||
// just trash this file if the personality it belongs to has gone away.
|
||
if (!CurPers) goto msgDone;
|
||
|
||
// Get the IMAP index resource
|
||
hIMAPIndex = nil;
|
||
if ((fileRef = FSpOpenResFile(spec,fsRdPerm))!=-1)
|
||
{
|
||
hIMAPIndex = (IndexStruct **)Get1Resource(INDEX_RES_TYPE,INDEX_RES_ID);
|
||
DetachResource((Handle)hIMAPIndex);
|
||
CloseResFile(fileRef);
|
||
}
|
||
if (!hIMAPIndex)
|
||
goto msgDone;
|
||
|
||
// now, grab messages
|
||
TOCSetDirty(toc,true);
|
||
countIMAP = GetHandleSize_(hIMAPIndex)/sizeof(IndexStruct);
|
||
|
||
// open the destination mailbox
|
||
err = BoxFOpen(toc);
|
||
if (err != noErr)
|
||
{
|
||
FileSystemError(OPEN_MBOX,(*toc)->mailbox.spec.name,err);
|
||
}
|
||
else
|
||
{
|
||
for(curIMAPIndex=0;curIMAPIndex<countIMAP;curIMAPIndex++)
|
||
{
|
||
BadBinHex = False;
|
||
BadEncoding = 0;
|
||
if (!AttachedFiles) AttachedFiles=NuHandle(0);
|
||
SetHandleBig_(AttachedFiles,0);
|
||
|
||
IMAPIdx = (*hIMAPIndex)[curIMAPIndex];
|
||
// search for the summary
|
||
count = 0;
|
||
for(sumNum=0;sumNum<(*toc)->count;sumNum++)
|
||
{
|
||
if ((*toc)->sums[sumNum].uidHash == IMAPIdx.uid)
|
||
{
|
||
MessHandle messH;
|
||
MyWindowPtr win;
|
||
|
||
SeekLine(IMAPIdx.offset,Lip);
|
||
gIMAPMsgEnd = IMAPIdx.offset+IMAPIdx.length;
|
||
CurTrans = IMAPTrans;
|
||
count=FetchMessageTextLo(nil,(*toc)->sums[sumNum].length,nil,sumNum,toc,true, false);
|
||
CurTrans = saveCurTrans;
|
||
SetHandleBig_(AttachedFiles,0);
|
||
SaveAbomination(nil,0);
|
||
#ifdef BAD_ENCODING_HANDLING
|
||
if (BadBinHex || BadEncoding) NoAttachments = True;
|
||
else
|
||
#endif
|
||
NoAttachments = False;
|
||
|
||
// set the sum options if this message needs to have an attachment downloaded.
|
||
if (HasStubFileAttachment(toc, sumNum)) (*toc)->sums[sumNum].opts |= OPT_FETCH_ATTACHMENTS;
|
||
else (*toc)->sums[sumNum].opts &= ~OPT_FETCH_ATTACHMENTS;
|
||
|
||
// moodmail?
|
||
if (AnalDoIncoming()) AnalBox(toc, sumNum, sumNum);
|
||
|
||
// spamwatch?
|
||
if (HasFeature(featureJunk) && JunkPrefBoxHold() && CanScoreJunk())
|
||
{
|
||
// only score message if it hasn't been manually scored before
|
||
if ((*toc)->sums[sumNum].spamBecause != JUNK_BECAUSE_USER)
|
||
JunkScoreIMAPBox (toc, sumNum, sumNum, false);
|
||
}
|
||
|
||
// redraw the summary
|
||
InvalSum(toc,sumNum);
|
||
|
||
// Redisplay message
|
||
if ((messH=(*toc)->sums[sumNum].messH) && (*messH)->bodyPTE && (win=(*messH)->win))
|
||
RedisplayIMAPMessage(win);
|
||
|
||
// Update the preview pane.
|
||
if ((*toc)->previewID==(*toc)->sums[sumNum].serialNum)
|
||
(*toc)->previewID = 0; // redraw previewed message
|
||
else
|
||
(*toc)->conConMultiScan = true; // run concentrator.
|
||
|
||
// delete the message if a translator has asked us to
|
||
if (ETLDeleteRequest)
|
||
{
|
||
(*toc)->sums[sumNum].opts |= OPT_ORPHAN_ATT; // don't delete its attachments
|
||
|
||
// jdboyd 7/30/04
|
||
//
|
||
// No matter what the download options and fitlering type are, this message
|
||
// gets deleted during the filtering process. Turning off the unfiltered
|
||
// flag keeps it around. I don't know what this was there before, but it's
|
||
// causing problems removing ESP commands.
|
||
//
|
||
// (*toc)->sums[sumNum].flags &= ~FLAG_UNFILTERED; // don't run filters on this message
|
||
|
||
(*toc)->sums[sumNum].opts |= OPT_EMSR_DELETE_REQUESTED; // mark this message so we know to delete later
|
||
ETLDeleteRequest = false;
|
||
}
|
||
|
||
// mark this toc as dirty so the changes get saved.
|
||
TOCSetDirty(toc,true);
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// close and flush the new messages to disk
|
||
err = BoxFClose(toc,true);
|
||
|
||
msgDone:
|
||
if (Lip && Lip->refN)
|
||
{
|
||
CloseLine(Lip);
|
||
DisposePtr((Ptr)Lip);
|
||
Lip = nil;
|
||
}
|
||
NoAttachments = False;
|
||
ZapHandle(hIMAPIndex);
|
||
|
||
UseResFile(saveRefN);
|
||
|
||
CurPers = oldPers;
|
||
|
||
// take care of any registration files that may have come with this message
|
||
ProcessReceivedRegFiles();
|
||
}
|
||
|