1 line
147 KiB
C
Executable File
1 line
147 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 "message.h"
|
||
#define FILE_NUM 25
|
||
/* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
|
||
|
||
#pragma segment Message
|
||
UHandle GetMessText(MessHandle messH);
|
||
int MessErr;
|
||
#ifndef IMAP
|
||
void MovingAttachments(TOCHandle tocH,short sumNum,Boolean attach,Boolean wipe,Boolean nuke,Boolean IMAPStubsOnly);
|
||
#endif
|
||
int FindAndCopyHeader(MessHandle origMH,MessHandle newMH,UPtr fromHead,short toHead);
|
||
void WeedHeaders(UHandle buffer,long *weeded,short toWeed,AccuPtr weeds);
|
||
#ifdef IMAP
|
||
MyWindowPtr OpenMessage(TOCHandle tocH,short sumNum,WindowPtr winWP,MyWindowPtr win, Boolean showIt, Boolean preview);
|
||
#else
|
||
MyWindowPtr OpenMessage(TOCHandle tocH,short sumNum,WindowPtr winWP,MyWindowPtr win, Boolean showIt);
|
||
#endif
|
||
OSErr RemoveSelf(MessHandle messH,short head,Boolean wantErrors);
|
||
void FindFrom(UPtr who, PETEHandle pte);
|
||
void Attribute(short attrId,MessHandle origMessH,MessHandle newMessH,Boolean atEnd);
|
||
void XferCustomTable(MessHandle origMessH,MessHandle newMessH);
|
||
void WeedXAttachments(MessHandle messH,Boolean errReport);
|
||
void RemoveIndAttachment(MessHandle messH, short index);
|
||
void PeteApplyStyles(PETEHandle pte,OffsetAndStyleHandle styles);
|
||
OSErr CopyToOut(TOCHandle fromTocH,short sumNum,TOCHandle toTocH);
|
||
OSErr UniqueHeader(MessHandle messH,short head,Boolean wantErrors);
|
||
OSErr FindMessageByMID(uLong mid,TOCHandle *tocH,short *sumNum);
|
||
OSErr WipeMessage(TOCHandle tocH,short sumNum);
|
||
long StripTrailingNewlines(Handle buffer,long stop);
|
||
OSErr MessageWarnings(TOCHandle tocH, short sumNum,Boolean toTrash,Boolean nuke,
|
||
Boolean *queuedWarning,Boolean *unsentWarning,Boolean *unreadWarning,Boolean *busyWarning);
|
||
OSErr SelectedWarnings(TOCHandle tocH,Boolean toTrash,Boolean nuke);
|
||
OSErr SingleWarnings(TOCHandle tocH,short sumNum,Boolean toTrash,Boolean nuke);
|
||
void DeleteMessageLo(TOCHandle tocH, int sumNum,Boolean nuke);
|
||
long CompBodyOffset(MessHandle messH);
|
||
OSErr SpoolAttachments(MessHandle messH);
|
||
OSErr CopyAttachments(MessHandle messH);
|
||
OSErr ReplyReferences(MessHandle origMessH,MessHandle newMessH);
|
||
void HTMLifyText(MyWindowPtr win,Handle text);
|
||
Boolean DoMessageMenu(short item,TOCHandle tocH,short sumNum,short toWhom,TextAddrHandle addr,long modifiers,Boolean nuke,Boolean *busy);
|
||
OSErr CopyNewsgroups(MessHandle origMH,MessHandle newMH);
|
||
Boolean AttStillInFolder(FSSpecPtr att, FSSpecPtr folder);
|
||
PStr MessCurAddr(MyWindowPtr win, PStr addr);
|
||
|
||
void DoCFStringGlobalReplace(CFMutableStringRef theString, CFStringRef stringToFind, CFStringRef replacement);
|
||
CFStringRef MakeCFMessTitle(TOCHandle tocH,int sumNum);
|
||
|
||
/************************************************************************
|
||
* GetAMessage - grab a message
|
||
************************************************************************/
|
||
MyWindowPtr GetAMessageLo(TOCHandle origTocH,int origSumNum,WindowPtr winWP, MyWindowPtr win, Boolean showIt, Boolean *newWin)
|
||
{
|
||
WindowPtr messWinWP;
|
||
MessHandle messH;
|
||
TOCHandle tocH;
|
||
short sumNum;
|
||
|
||
if(newWin) *newWin = true;
|
||
tocH = GetRealTOC(origTocH,origSumNum,&sumNum);
|
||
if (!tocH || (*tocH)->count<=sumNum) return(nil);
|
||
if (messH = (*tocH)->sums[sumNum].messH)
|
||
{
|
||
messWinWP = GetMyWindowWindowPtr ((*messH)->win);
|
||
if(newWin) *newWin = false;
|
||
if (showIt)
|
||
{
|
||
if (!IsWindowVisible (messWinWP))
|
||
ShowMyWindow(messWinWP);
|
||
UserSelectWindow(messWinWP);
|
||
}
|
||
UsingWindow(messWinWP);
|
||
win = (*messH)->win;
|
||
}
|
||
else if ((*tocH)->which==OUT)
|
||
win = OpenComp(tocH,sumNum,winWP,win,showIt,False);
|
||
#ifdef NEVER
|
||
else if (IsALink(tocH,sumNum))
|
||
win = OpenLink(tocH,sumNum,winWP,win,showIt);
|
||
#endif
|
||
else
|
||
#ifdef IMAP
|
||
win = OpenMessage(tocH,sumNum,winWP,win,showIt,false);
|
||
#else
|
||
win = OpenMessage(tocH,sumNum,winWP,win,showIt);
|
||
#endif
|
||
if (win)
|
||
{
|
||
(*Win2MessH(win))->openedFromTocH = origTocH;
|
||
(*Win2MessH(win))->openedFromSerialNum = (*origTocH)->sums[origSumNum].serialNum;
|
||
}
|
||
return(win);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* OpenMessage - open a message in its own window
|
||
**********************************************************************/
|
||
#ifdef IMAP
|
||
MyWindowPtr OpenMessage(TOCHandle tocH,short sumNum,WindowPtr winWP,MyWindowPtr win, Boolean showIt, Boolean preview)
|
||
#else
|
||
MyWindowPtr OpenMessage(TOCHandle tocH,short sumNum,WindowPtr winWP,MyWindowPtr win, Boolean showIt)
|
||
#endif
|
||
{
|
||
MessHandle messH;
|
||
Boolean turvy = showIt && (CurrentModifiers()&optionKey)!=0;
|
||
UHandle text=nil;
|
||
short ezOpenSum;
|
||
PETEDocInitInfo pdi;
|
||
OSErr err;
|
||
long size;
|
||
long partial = GetRLong(PETE_NIBBLE) K;
|
||
StackHandle stack;
|
||
Boolean useLizzie,disableButtons = false;
|
||
PETETextStyle style;
|
||
|
||
CycleBalls();
|
||
|
||
if (!(tocH = GetRealTOC(tocH,sumNum,&sumNum)))
|
||
return nil;
|
||
|
||
if ((messH = (MessHandle)NuHandleClear(sizeof(MessType)))==nil)
|
||
return(nil);
|
||
|
||
win = GetNewMyWindow(MESSAGE_WIND,winWP,win,BehindModal,False,False,MESS_WIN);
|
||
if (!win)
|
||
{
|
||
ZapHandle(messH);
|
||
return(nil);
|
||
}
|
||
|
||
winWP = GetMyWindowWindowPtr (win);
|
||
|
||
(*tocH)->sums[sumNum].messH = messH;
|
||
(*messH)->win = win;
|
||
(*messH)->sumNum = sumNum;
|
||
(*messH)->tocH = tocH;
|
||
/* apply FLAG_OUT ex post facto */
|
||
if ((*tocH)->sums[sumNum].state==SENT || (*tocH)->sums[sumNum].state==UNSENT)
|
||
(*tocH)->sums[sumNum].flags |= FLAG_OUT;
|
||
|
||
SetMyWindowPrivateData(win, (long)messH);
|
||
win->close = MessClose;
|
||
LL_Push(MessList,messH);
|
||
|
||
if (MessOptIsSet(messH,OPT_WRITE)) ClearMessOpt(messH,OPT_WRITE);
|
||
|
||
#ifdef IMAP
|
||
// Actually go fetch this message if we must
|
||
if ((*tocH)->imapTOC && !IMAPMessageDownloaded(tocH,sumNum))
|
||
{
|
||
// threading is off. Download message now
|
||
if (PrefIsSet(PREF_THREADING_OFF) || !ThreadsAvailable())
|
||
{
|
||
if (EnsureMsgDownloaded(tocH,sumNum,true))
|
||
text = GetMessText(messH);
|
||
}
|
||
else
|
||
{
|
||
Str255 scratch;
|
||
Boolean reallyGetIt = !preview || (!Offline && AutoCheckOK());
|
||
|
||
// start the message download thread. If we're opening this message to be previewed ONLY, don't fetch it if we're offline.
|
||
err = noErr;
|
||
if (!IMAPMessageBeingDownloaded(tocH, sumNum) && !IMAPMessageDownloaded(tocH, sumNum) && reallyGetIt) err = UIDDownloadMessage(tocH, (*tocH)->sums[sumNum].uidHash, false, false);
|
||
if (err) goto Abort;
|
||
|
||
// put the "waiting for download" text in the message window.
|
||
if (!reallyGetIt) GetRString(scratch,IMAP_GETTING_MESSAGE_OFFLINE);
|
||
else GetRString(scratch,IMAP_GETTING_MESSAGE);
|
||
text = PStr2Handle(scratch);
|
||
disableButtons = true;
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
text = GetMessText(messH);
|
||
useLizzie = false;
|
||
|
||
DefaultPII(win,0==((*tocH)->sums[sumNum].opts&OPT_WRITE),useLizzie?peGrowBox:peVScroll|peGrowBox,&pdi);
|
||
pdi.inParaInfo.paraFlags = (!PrefIsSet(PREF_SEND_ENRICHED_NEW) ? 0:peTextOnly);
|
||
pdi.docWidth = MessWi(win)-(useLizzie?0:PETE_SCROLLY_DIFFERENCE)+9;
|
||
err=PeteCreate(win,&win->pte,0,&pdi);
|
||
CleanPII(&pdi);
|
||
if (err || !text) goto Abort;
|
||
TheBody = win->pte;
|
||
if ( MessFlagIsSet ( messH, FLAG_FIXED_WIDTH )) {
|
||
short fixedID, fixedSize;
|
||
|
||
#ifdef USEFIXEDDEFAULTFONT
|
||
fixedSize = FixedSize;
|
||
fixedID = FixedID;
|
||
#else
|
||
// what size to use?
|
||
fixedSize = GetRLong(DEF_FIXED_SIZE);
|
||
if (!fixedSize) fixedSize = FontSize;
|
||
// what font to use?
|
||
fixedID = GetFontID(GetRString(s,FIXED_FONT));
|
||
if (fixedID==applFont)
|
||
fixedID = (ScriptVar(smScriptMonoFondSize)>>16)&0xffff;
|
||
#endif
|
||
PeteFontAndSize(TheBody, fixedID, fixedSize);
|
||
}
|
||
|
||
{
|
||
StackInit(sizeof(PartDesc),&stack);
|
||
(*PeteExtra(win->pte))->partStack = stack;
|
||
|
||
PeteCalcOff(win->pte);
|
||
if (!showIt||MessIsRich(messH)||MessOptIsSet(messH,OPT_CHARSET)) partial = 0;
|
||
size = GetHandleSize(text);
|
||
if (!partial || size<partial)
|
||
{
|
||
// stick in the text
|
||
if (!MessFlagIsSet(messH,FLAG_SHOW_ALL) &&
|
||
(MessFlagIsSet(messH,FLAG_RICH)
|
||
#ifndef OLDHTML
|
||
|| MessOptIsSet(messH,OPT_HTML)
|
||
#endif
|
||
|| MessOptIsSet(messH,OPT_FLOW)
|
||
|| MessOptIsSet(messH,OPT_CHARSET)
|
||
))
|
||
{
|
||
err = InsertRichLo(text,0,-1,-1,True,True,TheBody,stack,messH,MessOptIsSet(messH,OPT_DELSP));
|
||
ZapHandle(text);
|
||
}
|
||
else
|
||
err = PETEInsertPara(PETE,TheBody,kPETELastPara,nil,text,nil);
|
||
|
||
if (!err)
|
||
{
|
||
text = nil; // so we don't dispose of it
|
||
PeteTrimTrailingReturns(TheBody,true);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
err = PETEInsertTextPtr(PETE,win->pte,kPETELastPara,LDRef(text),partial,nil);
|
||
UL(text);
|
||
}
|
||
}
|
||
if (err)
|
||
{
|
||
if (err != userCanceledErr)
|
||
WarnUser(PETE_ERR,err);
|
||
}
|
||
else
|
||
{
|
||
CFStringRef cfTitle;
|
||
|
||
cfTitle = MakeCFMessTitle(tocH, sumNum);
|
||
SetWindowTitleWithCFString(winWP, cfTitle);
|
||
CFRelease(cfTitle);
|
||
win->menu = MessMenu;
|
||
win->gonnaShow = MessGonnaShow;
|
||
win->position = MessagePosition;
|
||
win->cursor = MessCursor;
|
||
win->button = CompButton; /* it will do */
|
||
win->app1 = MessApp1;
|
||
win->find = MessFind;
|
||
win->curAddr = MessCurAddr;
|
||
|
||
#ifdef OLDHTML
|
||
/*
|
||
* interpret rich text
|
||
*/
|
||
if (!MessFlagIsSet(messH,FLAG_SHOW_ALL))
|
||
{
|
||
if (MessOptIsSet(messH,OPT_HTML))
|
||
PeteHTML(TheBody,SumOf(messH)->bodyOffset-(*messH)->weeded+1,0,True);
|
||
}
|
||
#endif
|
||
|
||
if (showIt)
|
||
{
|
||
/*
|
||
* prepare for easy open
|
||
*/
|
||
ezOpenSum = EzOpenFind(tocH,sumNum);
|
||
if (ShowMyWindow(winWP)) {return(nil);}
|
||
|
||
/*
|
||
* scroll to show first line of body
|
||
*/
|
||
ShowMessageSeparator(TheBody,true);
|
||
PeteFocus(win,TheBody,True);
|
||
|
||
/*
|
||
* rest of the text?
|
||
*/
|
||
if (text && !useLizzie)
|
||
{
|
||
PeteCalcOff(win->pte);
|
||
err = PETEInsertTextPtr(PETE,win->pte,partial,LDRef(text)+partial,size-partial,nil);
|
||
PeteSmallParas(win->pte);
|
||
PeteCalcOn(win->pte);
|
||
ZapHandle(text);
|
||
PeteSetURLRescan(win->pte,0);
|
||
if (err)
|
||
{
|
||
if (err != userCanceledErr)
|
||
WarnUser(PETE_ERR,err);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* prepare for ezopen
|
||
*/
|
||
if (!err && ezOpenSum >= 0)
|
||
{
|
||
(*messH)->ezOpenSerialNum = (*tocH)->sums[ezOpenSum].serialNum;
|
||
CacheMessage(tocH,ezOpenSum);
|
||
}
|
||
if (disableButtons)
|
||
EnableMsgButtons(win,false);
|
||
}
|
||
}
|
||
|
||
style.tsLock = peModLock;
|
||
PETESetTextStyle(PETE,TheBody,0,0x7fffffff,&style,peLockValid);
|
||
PETEMarkDocDirty(PETE,TheBody,False);
|
||
win->isDirty = False;
|
||
|
||
if (err)
|
||
{
|
||
Abort:
|
||
PeteCleanList(win->pte);
|
||
win->isDirty = False;
|
||
CloseMyWindow(winWP);
|
||
win = nil;
|
||
if (text) ZapHandle(text);
|
||
}
|
||
|
||
return(win);
|
||
}
|
||
#pragma segment Message
|
||
|
||
/**********************************************************************
|
||
* MessCurAddr - return the address most closely associated with this message
|
||
**********************************************************************/
|
||
PStr MessCurAddr(MyWindowPtr win, PStr addr)
|
||
{
|
||
MessHandle messH = Win2MessH(win);
|
||
extern PStr CompCurAddr(MyWindowPtr win, PStr addr);
|
||
|
||
*addr = 0;
|
||
|
||
if (messH)
|
||
{
|
||
if (MessFlagIsSet(messH,FLAG_OUT))
|
||
CompCurAddr(win,addr);
|
||
else
|
||
{
|
||
if (win->hasSelection) return CurAddrSel(win,addr);
|
||
else
|
||
{
|
||
SuckHeaderText(messH,addr,sizeof(Str255),FROM_HEAD);
|
||
ShortAddr(addr,addr);
|
||
}
|
||
}
|
||
}
|
||
|
||
return *addr ? addr : nil;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MakeMessTitle - make a reasonable message title from a summary
|
||
**********************************************************************/
|
||
void MakeMessTitle(UPtr title,TOCHandle tocH,int sumNum, Boolean useSummary)
|
||
{
|
||
Str63 from, date, time, mailbox, subject;
|
||
Str63 pattern;
|
||
Str63 datetime;
|
||
long secs;
|
||
Str31 zoneStr;
|
||
long zone;
|
||
|
||
PCopy(from,(*tocH)->sums[sumNum].from);
|
||
if (useSummary) *from = MIN(*from,(*BoxWidths)[blFrom-1]);
|
||
|
||
if (useSummary)
|
||
{
|
||
if ((*BoxWidths)[blDate-1]>1)
|
||
{
|
||
ComputeLocalDate((*tocH)->sums+sumNum,datetime);
|
||
*datetime = MIN(*datetime,(*BoxWidths)[blDate-1]);
|
||
}
|
||
else *datetime = 0;
|
||
}
|
||
else
|
||
{
|
||
if (secs = (*tocH)->sums[sumNum].seconds)
|
||
{
|
||
zone = PrefIsSet(PREF_LOCAL_DATE) ? ZoneSecs() : 60*(*tocH)->sums[sumNum].origZone;
|
||
secs += zone;
|
||
FormatZone(zoneStr,zone);
|
||
TimeString(secs,False,time,nil);
|
||
DateString(secs,shortDate,date,nil);
|
||
utl_PlugParams(GetRString(pattern,DATE_SUM_FMT),datetime,date,time,zoneStr,"");
|
||
}
|
||
else *datetime = 0;
|
||
}
|
||
|
||
GetMailboxName(tocH,sumNum,mailbox);
|
||
PCopy(subject,(*tocH)->sums[sumNum].subj);
|
||
|
||
utl_PlugParams(GetRString(pattern,MESS_TITLE_PLUG),title,mailbox,from,datetime,subject);
|
||
}
|
||
|
||
void DoCFStringGlobalReplace(CFMutableStringRef theString, CFStringRef stringToFind, CFStringRef replacement)
|
||
{
|
||
CFArrayRef ranges;
|
||
CFRange wholeString, *range;
|
||
CFIndex i;
|
||
|
||
wholeString.location = 0;
|
||
wholeString.length = CFStringGetLength(theString);
|
||
ranges = CFStringCreateArrayWithFindResults(NULL, theString, stringToFind, wholeString, 0);
|
||
if(!ranges) return;
|
||
for(i = CFArrayGetCount(ranges); --i >= 0; )
|
||
{
|
||
range = (CFRange *)CFArrayGetValueAtIndex(ranges, i);
|
||
if(replacement != NULL)
|
||
CFStringReplace(theString, *range, replacement);
|
||
else
|
||
CFStringDelete(theString, *range);
|
||
}
|
||
CFRelease(ranges);
|
||
}
|
||
|
||
CFStringRef MakeCFMessTitle(TOCHandle tocH,int sumNum)
|
||
{
|
||
CFStringRef temp;
|
||
CFMutableStringRef pattern;
|
||
CFRange wholeString;
|
||
static CFStringRef upZero = NULL, upOne = NULL, upTwo = NULL, upThree = NULL;
|
||
Str63 tempS;
|
||
long secs;
|
||
long zone;
|
||
|
||
if(!upZero) upZero = CFSTR("^0");
|
||
if(!upOne) upOne = CFSTR("^1");
|
||
if(!upTwo) upTwo = CFSTR("^2");
|
||
if(!upThree) upThree = CFSTR("^3");
|
||
|
||
pattern = CFStringCreateMutable(NULL, 0);
|
||
|
||
if (secs = (*tocH)->sums[sumNum].seconds)
|
||
{
|
||
GetRString(tempS,DATE_SUM_FMT);
|
||
CFStringAppendPascalString(pattern, tempS, CFStringGetSystemEncoding());
|
||
|
||
zone = PrefIsSet(PREF_LOCAL_DATE) ? ZoneSecs() : 60*(*tocH)->sums[sumNum].origZone;
|
||
secs += zone;
|
||
FormatZone(tempS,zone);
|
||
temp = CFStringCreateWithPascalString(NULL, tempS, CFStringGetSystemEncoding());
|
||
DoCFStringGlobalReplace(pattern, upTwo, temp);
|
||
CFRelease(temp);
|
||
|
||
TimeString(secs,False,tempS,nil);
|
||
temp = CFStringCreateWithPascalString(NULL, tempS, CFStringGetSystemEncoding());
|
||
DoCFStringGlobalReplace(pattern, upOne, temp);
|
||
CFRelease(temp);
|
||
|
||
DateString(secs,shortDate,tempS,nil);
|
||
temp = CFStringCreateWithPascalString(NULL, tempS, CFStringGetSystemEncoding());
|
||
DoCFStringGlobalReplace(pattern, upZero, temp);
|
||
CFRelease(temp);
|
||
|
||
}
|
||
temp = CFStringCreateCopy(NULL, pattern);
|
||
wholeString.location = 0;
|
||
wholeString.length = CFStringGetLength(pattern);
|
||
if(wholeString.length != 0)
|
||
CFStringDelete(pattern, wholeString);
|
||
|
||
GetRString(tempS,MESS_TITLE_PLUG);
|
||
CFStringAppendPascalString(pattern, tempS, CFStringGetSystemEncoding());
|
||
|
||
DoCFStringGlobalReplace(pattern, upTwo, temp);
|
||
|
||
temp = CFStringCreateWithPascalString(NULL, (*tocH)->sums[sumNum].from, ((*tocH)->sums[sumNum].flags & FLAG_UTF8) ? kCFStringEncodingUTF8 : CFStringGetSystemEncoding());
|
||
if(temp == NULL)
|
||
{
|
||
temp = CFStringCreateWithPascalString(NULL, (*tocH)->sums[sumNum].from, kCFStringEncodingMacRoman);
|
||
}
|
||
DoCFStringGlobalReplace(pattern, upOne, temp);
|
||
CFRelease(temp);
|
||
|
||
temp = CFStringCreateWithPascalString(NULL, (*tocH)->sums[sumNum].subj, ((*tocH)->sums[sumNum].flags & FLAG_UTF8) ? kCFStringEncodingUTF8 : CFStringGetSystemEncoding());
|
||
if(temp == NULL)
|
||
{
|
||
temp = CFStringCreateWithPascalString(NULL, (*tocH)->sums[sumNum].subj, kCFStringEncodingMacRoman);
|
||
}
|
||
DoCFStringGlobalReplace(pattern, upThree, temp);
|
||
CFRelease(temp);
|
||
|
||
GetMailboxName(tocH,sumNum,tempS);
|
||
temp = CFStringCreateWithPascalString(NULL, tempS, CFStringGetSystemEncoding());
|
||
DoCFStringGlobalReplace(pattern, upZero, temp);
|
||
CFRelease(temp);
|
||
|
||
return pattern;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* PeteApplyStyles - apply a saved set of styles
|
||
**********************************************************************/
|
||
void PeteApplyStyles(PETEHandle pte,OffsetAndStyleHandle styles)
|
||
{
|
||
short n;
|
||
long endOffset;
|
||
OffsetAndStyle current;
|
||
|
||
if (!styles || !*styles) return;
|
||
|
||
n = HandleCount(styles);
|
||
if (!n) return;
|
||
|
||
endOffset = 0x7fffffff;
|
||
|
||
while (n--)
|
||
{
|
||
current = (*styles)[n];
|
||
PETESetTextStyle(PETE,pte,current.offset,endOffset,¤t.style,current.validBits);
|
||
endOffset = current.offset;
|
||
}
|
||
PETEMarkDocDirty(PETE,pte,False);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GenerateReceipt - generate a receipt for a message, if need be
|
||
**********************************************************************/
|
||
OSErr GenerateReceipt(MessHandle messH,short forWhat,short forWhatLocal,short actionId,short sentId)
|
||
{
|
||
Accumulator a;
|
||
Str255 scratch;
|
||
Str63 date, subject, realname;
|
||
Str15 boundary;
|
||
OSErr err = userCancelled;
|
||
Str15 returnStr;
|
||
long len;
|
||
MyWindowPtr win;
|
||
|
||
PushPers(PERS_FORCE(MESS_TO_PERS(messH)));
|
||
|
||
// ok, the user wants to do it
|
||
PCopy(boundary,"\p--_");
|
||
PCopy(returnStr,Cr);
|
||
if (!(err=AccuInit(&a)))
|
||
{
|
||
// multipart/report
|
||
AccuComposeR(&a,MIME_MP_FMT,
|
||
InterestHeadStrn+hContentType,
|
||
MIME_MULTIPART,
|
||
MIME_REPORT,
|
||
AttributeStrn+aBoundary,
|
||
"\p_",
|
||
returnStr);
|
||
AccuAddStr(&a,GetRString(scratch,MDN_REPORT_PARAM));
|
||
AccuAddChar(&a,'\015');
|
||
|
||
// inital boundary
|
||
AccuAddStr(&a,boundary);
|
||
AccuAddChar(&a,'\015');
|
||
|
||
// text part
|
||
AccuComposeR(&a,MIME_TEXTPLAIN,InterestHeadStrn+hContentType,
|
||
MIME_TEXT,MIME_PLAIN,"",returnStr);
|
||
AccuAddChar(&a,'\015');
|
||
CompHeadGetStr(messH,DATE_HEAD,date);
|
||
CompHeadGetStr(messH,SUBJ_HEAD,subject);
|
||
GetRealname(realname);
|
||
AccuComposeR(&a,MDN_DESCRIP,date,subject,forWhatLocal,realname);
|
||
|
||
// next boundary
|
||
AccuAddStr(&a,boundary);
|
||
AccuAddChar(&a,'\015');
|
||
|
||
// disposition part
|
||
AccuComposeR(&a,MIME_TEXTPLAIN,InterestHeadStrn+hContentType,
|
||
MIME_MESSAGE,MDN_DISPO_NOTIFY,"",returnStr);
|
||
AccuAddChar(&a,'\015');
|
||
|
||
//AccuComposeR(&a,MDN_REPORTING_UA,returnStr);
|
||
AccuComposeR(&a,MDN_FINAL_RECIP,GetReturnAddr(scratch,False),returnStr);
|
||
if (!GetRHeaderAnywherePtr(messH,HeaderStrn+MSGID_HEAD,scratch+1,sizeof(scratch)-1,&len))
|
||
{
|
||
*scratch = len;
|
||
AccuComposeR(&a,MDN_ORIG_MID,scratch,returnStr);
|
||
}
|
||
AccuComposeR(&a,MDN_DISPOSITION,actionId,sentId,forWhat,returnStr);
|
||
AccuAddChar(&a,'\015');
|
||
AccuAddChar(&a,'\015');
|
||
|
||
//final boundary
|
||
AccuAddStr(&a,boundary);
|
||
AccuAddStr(&a,"\p--\015");
|
||
|
||
/*
|
||
* message built, now create & address it
|
||
*/
|
||
if (!GetRHeaderAnywherePtr(messH,InterestHeadStrn+hMDN,scratch,sizeof(scratch)-1,&len))
|
||
{
|
||
*scratch = len-1;
|
||
if (win=DoComposeNew(0))
|
||
{
|
||
MessHandle newMessH = Win2MessH(win);
|
||
SetMessText(newMessH,TO_HEAD,scratch+1,*scratch);
|
||
SetMessText(newMessH,0,LDRef(a.data),a.offset);
|
||
ComposeRString(scratch,MDN_SUBJECT,subject);
|
||
SetMessText(newMessH,SUBJ_HEAD,scratch+1,*scratch);
|
||
SetMessOpt(newMessH,OPT_RECEIPT);
|
||
SetMessOpt(newMessH,OPT_BULK);
|
||
SetSig((*newMessH)->tocH,(*newMessH)->sumNum,SIG_NONE);
|
||
QueueMessage((*newMessH)->tocH,(*newMessH)->sumNum,kEuQueue,0,true,false);
|
||
}
|
||
}
|
||
AccuZap(a);
|
||
}
|
||
PopPers();
|
||
return(err);
|
||
}
|
||
|
||
|
||
/**********************************************************************
|
||
* EnsureMID - make sure a message has an id
|
||
**********************************************************************/
|
||
OSErr EnsureMID(TOCHandle tocH,short sumNum)
|
||
{
|
||
OSErr err = noErr;
|
||
|
||
if ((*tocH)->sums[sumNum].uidHash==kNeverHashed || (*tocH)->sums[sumNum].msgIdHash==kNeverHashed)
|
||
{
|
||
if (!(err=CacheMessage(tocH,sumNum)))
|
||
RehashLo(tocH,sumNum,(*tocH)->sums[sumNum].cache,true);
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* EnsureFromHash - make sure a message has a from id
|
||
**********************************************************************/
|
||
OSErr EnsureFromHash(TOCHandle tocH,short sumNum)
|
||
{
|
||
OSErr err = noErr;
|
||
Str255 scratch, shortAddr;
|
||
uLong addrHash;
|
||
|
||
if ((*tocH)->sums[sumNum].fromHash==kNeverHashed)
|
||
{
|
||
if (!(err=CacheMessage(tocH,sumNum)))
|
||
{
|
||
HNoPurge((*tocH)->sums[sumNum].cache);
|
||
|
||
HeaderName(FROM_HEAD); // weird--goes into scratch
|
||
TrimWhite(scratch);
|
||
if (*HandleHeadGetPStr((*tocH)->sums[sumNum].cache,HEADER_STRN+FROM_HEAD,scratch))
|
||
{
|
||
ShortAddr(shortAddr,scratch);
|
||
MyLowerStr(shortAddr);
|
||
addrHash = Hash(shortAddr);
|
||
}
|
||
else
|
||
addrHash = kNoMessageId;
|
||
|
||
HPurge((*tocH)->sums[sumNum].cache);
|
||
|
||
(*tocH)->sums[sumNum].fromHash = addrHash;
|
||
TOCSetDirty(tocH,true);
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* CacheMessage - put a message into the cache
|
||
**********************************************************************/
|
||
OSErr CacheMessage(TOCHandle tocH, short sumNum)
|
||
{
|
||
Handle cache;
|
||
OSErr err = noErr;
|
||
|
||
if (0>sumNum || sumNum>=(*tocH)->count) return(fnfErr);
|
||
|
||
#ifdef IMAP
|
||
// don't do anything with IMAP messages that have not been downloaded yet.
|
||
if ((*tocH)->sums[sumNum].offset < 0) return (fnfErr);
|
||
#endif
|
||
|
||
/*
|
||
* is it there?
|
||
*/
|
||
if ((*tocH)->sums[sumNum].cache)
|
||
{
|
||
if (*(*tocH)->sums[sumNum].cache) return(noErr); /* in the cache */
|
||
else ZapHandle((*tocH)->sums[sumNum].cache); /* wipe out remnant */
|
||
}
|
||
|
||
/*
|
||
* allocate it
|
||
*/
|
||
if (cache=NuHTempOK(GetMessageLength(tocH,sumNum)))
|
||
{
|
||
/*
|
||
* read it
|
||
*/
|
||
err = ReadMessage(tocH,sumNum,LDRef(cache));
|
||
if (err) ZapHandle(cache);
|
||
else
|
||
{
|
||
UL(cache);
|
||
HPurge(cache);
|
||
(*tocH)->sums[sumNum].cache = cache;
|
||
ASSERT((*cache)[GetHandleSize(cache)-1]=='\015');
|
||
}
|
||
}
|
||
else
|
||
err = MemError();
|
||
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetMessText - put the text of a message
|
||
**********************************************************************/
|
||
UHandle GetMessText(MessHandle messH)
|
||
{
|
||
MyWindowPtr win = (*messH)->win;
|
||
TOCHandle tocH = (*messH)->tocH;
|
||
int sumNum = (*messH)->sumNum;
|
||
UHandle buffer = nil;
|
||
long weeded;
|
||
short tableId;
|
||
Handle table;
|
||
Handle cache=nil;
|
||
Accumulator weeds;
|
||
|
||
Zero(weeds);
|
||
|
||
/*
|
||
* grab cached text, if any
|
||
*/
|
||
if ((*tocH)->sums[sumNum].cache && *(*tocH)->sums[sumNum].cache)
|
||
{
|
||
cache = (*tocH)->sums[sumNum].cache;
|
||
HNoPurge(cache);
|
||
}
|
||
else
|
||
ZapHandle((*tocH)->sums[sumNum].cache);
|
||
|
||
/*
|
||
* allocate buffer
|
||
*/
|
||
if (GetMessageLength(tocH,sumNum) > (GetRLong(PETE_NIBBLE) K))
|
||
buffer = NuHTempBetter(GetMessageLength(tocH,sumNum));
|
||
else
|
||
buffer = NuHTempOK(GetMessageLength(tocH,sumNum));
|
||
if (buffer==nil)
|
||
{
|
||
if (!cache)
|
||
{
|
||
WarnUser(NO_MESS_BUF,MemError());
|
||
return(nil);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* read it
|
||
*/
|
||
if (cache)
|
||
{
|
||
if (buffer)
|
||
{
|
||
BMD(*cache,*buffer,GetHandleSize(buffer));
|
||
HPurge(cache);
|
||
}
|
||
else
|
||
{
|
||
buffer = cache;
|
||
cache = nil;
|
||
(*tocH)->sums[sumNum].cache = nil;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
LDRef(buffer);
|
||
if (MessErr=ReadMessage(tocH,sumNum,*buffer))
|
||
goto failure;
|
||
UL(buffer);
|
||
|
||
if (cache = NuHTempOK(GetMessageLength(tocH,sumNum)))
|
||
{
|
||
HPurge(cache);
|
||
BMD(*buffer,*cache,GetMessageLength(tocH,sumNum));
|
||
(*tocH)->sums[sumNum].cache = cache;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* set hash, if we haven't already
|
||
*/
|
||
if ((*tocH)->sums[sumNum].uidHash == kNeverHashed)
|
||
Rehash(tocH,sumNum,buffer);
|
||
|
||
/*
|
||
* weed headers?
|
||
*/
|
||
if (!(SumOf(messH)->flags & FLAG_SHOW_ALL))
|
||
{
|
||
WeedHeaders(buffer,&weeded,BadHeadStrn,&weeds);
|
||
(*messH)->weeded = weeded;
|
||
StripTrailingNewlines(buffer,SumOf(messH)->bodyOffset-weeded+1);
|
||
}
|
||
else
|
||
(*messH)->weeded = 0;
|
||
WeedHeaders(buffer,&weeded,FROM_STRN,nil);
|
||
(*messH)->weeded += weeded;
|
||
AccuTrim(&weeds);
|
||
(*messH)->extras = weeds;
|
||
|
||
/*
|
||
* translate?
|
||
*/
|
||
tableId = (*tocH)->sums[sumNum].tableId;
|
||
if (tableId==DEFAULT_TABLE) tableId = GetPrefLong(PREF_IN_XLATE);
|
||
if (tableId!=NO_TABLE)
|
||
{
|
||
if (!(table=GetResource_('taBL',tableId)))
|
||
{
|
||
tableId -= 1000; /* try 1000 lower */
|
||
table=GetResource_('taBL',tableId);
|
||
}
|
||
if (table)
|
||
{
|
||
Byte *p,*end,*t;
|
||
long size = GetHandleSize_(buffer);
|
||
end = *buffer+size;
|
||
t = *table;
|
||
for (p=*buffer;p<end;p++) *p = t[*p];
|
||
}
|
||
}
|
||
|
||
win->ro = !MessOptIsSet(messH,OPT_WRITE);
|
||
return(buffer);
|
||
|
||
failure:
|
||
if (buffer) ZapHandle(buffer);
|
||
return(nil);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* ReadMessage - read a given message into a preallocated buffer
|
||
**********************************************************************/
|
||
int ReadMessage(TOCHandle tocH,int sumN,UPtr buffer)
|
||
{
|
||
long count;
|
||
Str63 name;
|
||
short sumNum;
|
||
|
||
tocH = GetRealTOC(tocH,sumN,&sumNum);
|
||
if (!tocH) return fnfErr; // unable to find real TOC from virtual TOC
|
||
GetMailboxName(tocH,sumNum,name);
|
||
count = (*tocH)->sums[sumNum].length;
|
||
|
||
if (!(MessErr=BoxFOpenLo(tocH,sumNum)))
|
||
if ((MessErr=SetFPos((*tocH)->refN,fsFromStart,
|
||
(*tocH)->sums[sumNum].offset)) ||
|
||
(MessErr=ARead((*tocH)->refN,&count,buffer)))
|
||
FileSystemError(READ_MBOX,name,MessErr);
|
||
|
||
return(MessErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MoveMessage - transfer a message from one box to another
|
||
* called when the transfer menu is invoked with a message frontmost
|
||
**********************************************************************/
|
||
int MoveMessage(TOCHandle tocH,int sumNum,FSSpecPtr toSpec,Boolean copy)
|
||
{
|
||
TOCHandle toTocH;
|
||
|
||
CycleBalls();
|
||
|
||
if ((toTocH = TOCBySpec(toSpec))==nil)
|
||
return(1);
|
||
|
||
if ((*toTocH)->which==OUT && !copy && !((*tocH)->sums[sumNum].flags&FLAG_OUT))
|
||
if (ReallyDoAnAlert(XFER_TO_OUT,Caution)!=1) return(1);
|
||
|
||
if (!copy)
|
||
{
|
||
if (SingleWarnings(tocH,sumNum,(*toTocH)->which==TRASH,False))
|
||
return(1);
|
||
}
|
||
|
||
return(MoveMessageLo(tocH,sumNum,toSpec,copy,false,true));
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MoveMessageLo - transfer a message from one box to another, no warnings
|
||
**********************************************************************/
|
||
int MoveMessageLo(TOCHandle tocH,int sumNum,FSSpecPtr toSpec,Boolean copy,Boolean toTemp,Boolean holdOpen)
|
||
{
|
||
TOCHandle toTocH;
|
||
MessHandle messH = (*tocH)->sums[sumNum].messH;
|
||
Str32 name;
|
||
long serialNum;
|
||
short realSumNum;
|
||
Boolean isIMAPtoPopTransfer = false;
|
||
|
||
#ifdef THREADING_ON
|
||
// can't transfer a message we're sending
|
||
if (!copy && ((*tocH)->sums[sumNum].state == BUSY_SENDING))
|
||
return (-1);
|
||
#endif
|
||
if (LogLevel&LOG_MOVE)
|
||
ComposeLogS(LOG_MOVE,nil,"\p%p <20>%p,%p<> <20>%p<>-><3E>%p<>\015", copy ? "\pCopy" : "\pTransfer",(*tocH)->sums[sumNum].from,(*tocH)->sums[sumNum].subj,GetMailboxName(tocH,sumNum,name),toSpec->name);
|
||
|
||
CycleBalls();
|
||
|
||
if ((toTocH = TOCBySpec(toSpec))==nil)
|
||
return(1);
|
||
|
||
tocH = GetRealTOC(tocH,sumNum,&realSumNum);
|
||
sumNum = realSumNum;
|
||
|
||
#ifdef IMAP
|
||
// handle special transfer cases for IMAP mailbox transfers
|
||
if ((*toTocH)->imapTOC)
|
||
{
|
||
OSErr err = noErr;
|
||
|
||
// IMAP to IMAP. Do an IMAP transfer.
|
||
if ((*toTocH)->imapTOC && (*tocH)->imapTOC)
|
||
{
|
||
err = IMAPTransferMessage(tocH, toTocH, (*tocH)->sums[sumNum].uidHash, copy, false);
|
||
}
|
||
// POP to IMAP
|
||
else if ((*toTocH)->imapTOC && !(*tocH)->imapTOC)
|
||
{
|
||
err = IMAPTransferMessageToServer(tocH, toTocH, sumNum, copy, false);
|
||
}
|
||
|
||
return (err);
|
||
}
|
||
#endif
|
||
|
||
if ((*tocH)->which==OUT && !copy) FixSourceStatus(tocH,sumNum); /* in case we're deleting a reply, set orig state back */
|
||
|
||
if (messH && (*messH)->subPTE && PeteIsDirty((*messH)->subPTE))
|
||
MessSaveSub(messH);
|
||
|
||
#ifdef IMAP
|
||
// if this is an IMAP to POP transfer, close the message window
|
||
if ((*tocH)->imapTOC)
|
||
{
|
||
// IMAP to POP. Download the message.
|
||
Boolean downloaded = EnsureMsgDownloaded(tocH,sumNum,true);
|
||
|
||
// drop the message if a translator asked to delete it
|
||
if ((*tocH)->sums[sumNum].opts&OPT_EMSR_DELETE_REQUESTED)
|
||
{
|
||
// message was fetched, but a translator deleted it.
|
||
(*tocH)->sums[sumNum].opts |= OPT_ORPHAN_ATT; // be real sure the attachments are left alone
|
||
#ifdef DEBUG
|
||
ComposeLogS(LOG_PLUG,nil,"\pA plugin has deleted an IMAP message: '%p' in '%p'",(*tocH)->sums[sumNum].subj,(*tocH)->mailbox.spec.name);
|
||
#endif
|
||
return (noErr);
|
||
}
|
||
else if (!downloaded) return (1);
|
||
|
||
// if the message is open, close it.
|
||
if (!copy && messH)
|
||
CloseMyWindow(GetMyWindowWindowPtr((*messH)->win));
|
||
}
|
||
#endif
|
||
|
||
CycleBalls();
|
||
|
||
#ifdef IMAP
|
||
// if this is an IMAP to POP transfer, the POP copy will point to the attachment
|
||
isIMAPtoPopTransfer = tocH && (*tocH)->imapTOC && toTocH && !(*toTocH)->imapTOC;
|
||
#endif //IMAP
|
||
|
||
MessErr=AppendMessage(tocH,sumNum,toTocH,copy,toTemp,isIMAPtoPopTransfer);
|
||
|
||
if (MessErr) return(MessErr);
|
||
if (!holdOpen)
|
||
{
|
||
(void) BoxFClose(tocH,false);
|
||
(void) BoxFClose(toTocH,true);
|
||
}
|
||
|
||
CycleBalls();
|
||
|
||
#ifdef IMAP
|
||
// if we moved an IMAP message to a POP mailbox, forget about its attachments so they don't get tidied up.
|
||
if ((*tocH)->imapTOC && !(*toTocH)->imapTOC) (*tocH)->sums[sumNum].opts |= OPT_ORPHAN_ATT;
|
||
#endif
|
||
|
||
if (!copy)
|
||
#ifdef IMAP
|
||
{
|
||
serialNum = (*tocH)->sums[sumNum].serialNum;
|
||
// if this was an IMAP message, we'll need to delete it from the server.
|
||
if ((*tocH)->imapTOC)
|
||
{
|
||
MessErr = IMAPDeleteMessage(tocH, (*tocH)->sums[sumNum].uidHash, false, false, false) ? noErr : 1;
|
||
}
|
||
else DeleteSum(tocH,sumNum);
|
||
// Check for updates to search results
|
||
SearchUpdateSum(toTocH,(*toTocH)->count-1,tocH,serialNum,true,false);
|
||
}
|
||
#else
|
||
{
|
||
oldSerialNum = (*tocH)->sums[sumNum].serialNum;
|
||
DeleteSum(tocH,sumNum);
|
||
// Check for updates to search results
|
||
SearchUpdateSum(toTocH,(*toTocH)->count-1,fromTocH,serialNum,true,false);
|
||
}
|
||
#endif
|
||
|
||
CheckBox(GetWindowMyWindowPtr(FrontWindow_()),False);
|
||
return(MessErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* AppendMessage - add a message to a mailbox. Message comes from another
|
||
* mailbox. Things in here are a little touchy, as there are several
|
||
* things to do, any one of which could fail. In order, this is
|
||
* what is done:
|
||
* 1. Move the bytes from one mailbox to the other.
|
||
* 2. Copy the message summary from one toc to the other, updating
|
||
* tocH and sumNum in the message handle (if any).
|
||
* 3. If the message window is open, fix pointers so the message
|
||
* belongs to the new box, not the old one.
|
||
* Steps 1 and 2 could fail. In either case, no real harm is done,
|
||
* except that we might waste some space in the new mailbox.
|
||
* Step 3 shouldn't ever fail.
|
||
**********************************************************************/
|
||
int AppendMessage(TOCHandle fromTocH,int fromN,TOCHandle toTocH,Boolean copy,Boolean toTemp,Boolean destHasAtt)
|
||
{
|
||
UHandle buffer = nil;
|
||
MSumType sum;
|
||
long eof;
|
||
long count;
|
||
short err=0;
|
||
MessHandle fromMH;
|
||
long newBodyOffset, newLength;
|
||
mesgErrorHandle mesgErrH;
|
||
FSSpec toSpec;
|
||
/*
|
||
* if it's an outgoing message, save it first
|
||
*/
|
||
fromMH = (*fromTocH)->sums[fromN].messH;
|
||
if (fromMH)
|
||
{
|
||
MyWindowPtr win = (*fromMH)->win;
|
||
if (win->saveSize && win->position) (*win->position)(True,win);
|
||
if ((*fromTocH)->which==OUT)
|
||
{
|
||
MyWindowPtr win = (*fromMH)->win;
|
||
if ((win->isDirty||!(*fromTocH)->sums[fromN].length) &&
|
||
!SaveComp(win)) return(1);
|
||
}
|
||
else if (win->isDirty)
|
||
{
|
||
MessFocus(fromMH,(*fromMH)->bodyPTE);
|
||
if (!SaveMessHi(win,False)) return(1);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* open the relevant mailboxes
|
||
*/
|
||
MessErr = BoxFOpen(fromTocH);
|
||
if (MessErr) return(MessErr);
|
||
MessErr = BoxFOpen(toTocH);
|
||
if (MessErr) return(MessErr);
|
||
|
||
/*
|
||
* is there space?
|
||
*/
|
||
toSpec = GetMailboxSpec(toTocH,-1);
|
||
if (MessErr = VolumeMargin(toSpec.vRefNum,(*fromTocH)->sums[fromN].length))
|
||
return(MessErr);
|
||
|
||
/*
|
||
* Are there too many messages in the destination?
|
||
*/
|
||
if ((*toTocH)->count >= MAX_MESSAGES_PER_MAILBOX)
|
||
{
|
||
Str255 s;
|
||
ComposeStdAlert(Stop,TOO_MANY_MESSAGES,PCopy(s,(*toTocH)->mailbox.spec.name));
|
||
return MessErr = 1;
|
||
}
|
||
|
||
/*
|
||
* copy the message from one to the other
|
||
*/
|
||
if (!copy) (*fromTocH)->sums[fromN].flags &= ~FLAG_SKIPWARN;
|
||
eof = FindTOCSpot(toTocH,(*fromTocH)->sums[fromN].length);
|
||
if ((*toTocH)->which == OUT && !((*fromTocH)->sums[fromN].flags&FLAG_OUT))
|
||
{
|
||
if (MessErr = CopyToOut(fromTocH,fromN,toTocH)) return(MessErr);
|
||
}
|
||
else
|
||
{
|
||
#ifdef TWO
|
||
if (!copy && ((*fromTocH)->which==TRASH || (*toTocH)->which==TRASH))
|
||
{
|
||
if (PrefIsSet(PREF_TIDY_FOLDER) && ((*fromTocH)->sums[fromN].flags & FLAG_HAS_ATT) &&
|
||
!((*fromTocH)->sums[fromN].flags & FLAG_OUT))
|
||
MovingAttachments(fromTocH,fromN,True,False,False,false);
|
||
if ((*fromTocH)->sums[fromN].flags & FLAG_HAS_ATT)
|
||
MovingAttachments(fromTocH,fromN,False,False,False,false);
|
||
}
|
||
#endif
|
||
if ((*fromTocH)->sums[fromN].cache && *(*fromTocH)->sums[fromN].cache)
|
||
{
|
||
count = (*fromTocH)->sums[fromN].length;
|
||
HNoPurge((*fromTocH)->sums[fromN].cache);
|
||
MessErr = SetFPos((*toTocH)->refN,fsFromStart,eof);
|
||
if (!MessErr)
|
||
MessErr = AWrite((*toTocH)->refN,&count,LDRef((*fromTocH)->sums[fromN].cache));
|
||
#ifdef DEBUG
|
||
if (RunType!=Production)
|
||
{
|
||
long controls=0;
|
||
UPtr dbspot = *(*fromTocH)->sums[fromN].cache;
|
||
UPtr dbend = dbspot + (*fromTocH)->sums[fromN].length;
|
||
for (;dbspot<dbend;dbspot++)
|
||
if (*dbspot!='\015' && *dbspot<' ' && *dbspot!='\t') controls++;
|
||
if (controls*20>(*fromTocH)->sums[fromN].length)
|
||
{
|
||
Str255 buffer;
|
||
ComposeString(buffer,"\pWarning: %d control characters; may be a problem",controls);
|
||
AlertStr(OK_ALRT,Stop,buffer);
|
||
}
|
||
}
|
||
#endif
|
||
UL((*fromTocH)->sums[fromN].cache);
|
||
HPurge((*fromTocH)->sums[fromN].cache);
|
||
}
|
||
else
|
||
MessErr = CopyFBytes((*fromTocH)->refN,(*fromTocH)->sums[fromN].offset,
|
||
(*fromTocH)->sums[fromN].length,(*toTocH)->refN,eof);
|
||
if (MessErr)
|
||
{
|
||
LDRef(toTocH);
|
||
FileSystemError(COPY_FAILED,toSpec.name,MessErr);
|
||
UL(toTocH);
|
||
return(MessErr);
|
||
}
|
||
(void) SetEOF((*toTocH)->refN,eof+(*fromTocH)->sums[fromN].length);
|
||
newBodyOffset = (*fromTocH)->sums[fromN].bodyOffset;
|
||
newLength = (*fromTocH)->sums[fromN].length;
|
||
|
||
/*
|
||
* now, create a new summary for the copied message, and put it in the
|
||
* new TOC.
|
||
*/
|
||
sum = (*fromTocH)->sums[fromN];
|
||
sum.offset = eof;
|
||
sum.length = newLength;
|
||
sum.bodyOffset = newBodyOffset;
|
||
sum.selected = False;
|
||
sum.messH = nil; /* break connection with open message window */
|
||
sum.mesgErrH = nil; /* clear mesgErrH. add it below */
|
||
sum.serialNum = (*toTocH)->nextSerialNum++;
|
||
|
||
// Junk processing
|
||
if ((*toTocH)->which==JUNK && sum.spamBecause==0)
|
||
{
|
||
sum.spamScore = GetRLong(JUNK_XFER_SCORE);
|
||
sum.spamBecause = JUNK_BECAUSE_XFER;
|
||
}
|
||
else if ((*fromTocH)->which==JUNK && (*fromTocH)->sums[fromN].spamBecause == JUNK_BECAUSE_XFER)
|
||
{
|
||
sum.spamScore = 0;
|
||
sum.spamBecause = 0;
|
||
}
|
||
|
||
if (copy)
|
||
sum.cache = nil; // because we're copying, leave the cache alone
|
||
else
|
||
(*fromTocH)->sums[fromN].cache = nil; // let the cache go with the transferred message
|
||
|
||
// the copy won't have an attachment. That way, if it gets deleted, the attachment
|
||
// will remain until the original is deleted. - jdboyd12/14/04
|
||
if (copy && !destHasAtt)
|
||
sum.flags &= ~FLAG_HAS_ATT;
|
||
|
||
if (!sum.seconds) sum.seconds = GMTDateTime();
|
||
if ((*fromTocH)->which==OUT && !toTemp)
|
||
{
|
||
if (sum.state!=SENT) sum.state=UNSENT;
|
||
sum.tableId = NO_TABLE; /* don't translate */
|
||
sum.flags |= FLAG_OUT;
|
||
}
|
||
|
||
if ((*toTocH)->count)
|
||
{
|
||
MessErr = PtrPlusHand_(&sum,toTocH,sizeof(sum));
|
||
if (MessErr)
|
||
{
|
||
ZapHandle(sum.cache);
|
||
return(MessErr);
|
||
}
|
||
}
|
||
else
|
||
(*toTocH)->sums[0] = sum;
|
||
TOCSetDirty(toTocH,true);
|
||
(*toTocH)->reallyDirty = True;
|
||
(*toTocH)->resort = kResortWhenever;
|
||
(*toTocH)->count++;
|
||
(*toTocH)->analScanned = false;
|
||
}
|
||
TOCSetDirty(toTocH,true);
|
||
(*toTocH)->needRedo = MIN((*toTocH)->needRedo,(*toTocH)->count-1);
|
||
|
||
/*
|
||
* add mesg error to new toc entry
|
||
*/
|
||
if (mesgErrH=(*fromTocH)->sums[fromN].mesgErrH)
|
||
{
|
||
Str255 errorStr;
|
||
AddMesgError (toTocH, (*toTocH)->count-1,PCopy(errorStr,(*mesgErrH)->errorStr), (*mesgErrH)->errorCode);
|
||
}
|
||
|
||
/*
|
||
* the message window, if any, should be closed
|
||
*/
|
||
if (!copy && (*fromTocH)->sums[fromN].messH)
|
||
CloseMyWindow(GetMyWindowWindowPtr((*(MessHandle)(*fromTocH)->sums[fromN].messH)->win));
|
||
|
||
return (MessErr); //return(noErr); // 12/11/97 ccw
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MoveSelectedMessages - transfer all selected messages from one mail
|
||
* box to another.
|
||
**********************************************************************/
|
||
int MoveSelectedMessages(TOCHandle tocH,FSSpecPtr toSpec,Boolean copy)
|
||
{
|
||
return (MoveSelectedMessagesLo(tocH, toSpec, copy, false, true, true));
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MoveSelectedMessages - transfer all selected messages from one mail
|
||
* box to another.
|
||
**********************************************************************/
|
||
int MoveSelectedMessagesLo(TOCHandle tocH,FSSpecPtr toSpec,Boolean copy,Boolean delete,Boolean undo,Boolean warnings)
|
||
{
|
||
TOCHandle toTocH;
|
||
int sumNum;
|
||
int lastSelected = -1;
|
||
Str31 trashName;
|
||
short oldCount;
|
||
Boolean toTrash = IsRoot(toSpec) && StringSame(toSpec->name,GetRString(trashName,TRASH));
|
||
long needRoom=0;
|
||
Boolean outWarning;
|
||
long count;
|
||
uLong pTicks = TickCount();
|
||
#ifdef IMAP
|
||
Handle uidsH = nil;
|
||
OSErr err = noErr;
|
||
#endif
|
||
TOCHandle realTocH;
|
||
short realSum;
|
||
Str255 name;
|
||
|
||
if ((toTocH = TOCBySpec(toSpec))==nil)
|
||
return(1);
|
||
outWarning = !copy && (*toTocH)->which==OUT;
|
||
|
||
//
|
||
// Do transfers from the search window. Fall through to handle POP messages.
|
||
//
|
||
|
||
if ((*tocH)->virtualTOC)
|
||
{
|
||
if (delete) IMAPDeleteMessagesFromSearchWindow(tocH);
|
||
else
|
||
{
|
||
// do search window transfers
|
||
err = IMAPTransferMessagesFromSearchWindow(tocH, toTocH, copy);
|
||
if (err != noErr) return (err);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Perform transfers to IMAP mailboxes. There will be nothing left to do after this.
|
||
//
|
||
|
||
if ((*toTocH)->imapTOC)
|
||
{
|
||
return (IMAPMoveIMAPMessages(tocH, toTocH, copy));
|
||
}
|
||
|
||
//
|
||
// Handle the rest of the messages that have not been transferred yet.
|
||
//
|
||
|
||
if (!copy && warnings && SelectedWarnings(tocH,toTrash,False)) return(0);
|
||
|
||
count = CountSelectedMessages(tocH);
|
||
for (sumNum=0;sumNum<(*tocH)->count;sumNum++)
|
||
if ((*tocH)->sums[sumNum].selected)
|
||
{
|
||
FixSourceStatus(tocH,sumNum); /* in case we're deleting a reply, set orig state back */
|
||
needRoom += (*tocH)->sums[sumNum].length+sizeof(MSumType);
|
||
if (outWarning && !((*tocH)->sums[sumNum].flags&FLAG_OUT))
|
||
{
|
||
outWarning = False; /* don't need anymore */
|
||
if (ReallyDoAnAlert(XFER_TO_OUT,Caution)!=1) return(0);
|
||
else break;
|
||
}
|
||
}
|
||
|
||
NoteFreeSpace(toTocH);
|
||
if (needRoom>(*toTocH)->volumeFree-GetRLong(VOLUME_MARGIN)-sizeof(MSumType)*count)
|
||
{
|
||
WarnUser(NOT_ENOUGH_ROOM,dskFulErr);
|
||
return(0);
|
||
}
|
||
|
||
#ifdef TWO
|
||
if (!copy && undo) AddXfUndo(tocH,toTocH,-1);
|
||
#endif
|
||
|
||
if (count>10) OpenProgress();
|
||
ProgressMessageR(kpSubTitle,LEFT_TO_TRANSFER);
|
||
Progress(NoChange,count,nil,nil,nil);
|
||
|
||
//
|
||
// If we are transferring from an IMAP mailbox to a POP mailbox,
|
||
// ensure that the messages have been fetched.
|
||
//
|
||
|
||
if ((*tocH)->imapTOC)
|
||
{
|
||
short c, totalToDownload;
|
||
|
||
// must be online to do an IMAP to POP transfer, no matter if the message is downloaded or not.
|
||
if (!copy && Offline && GoOnline()) return (0);
|
||
|
||
// go through selected messages, and make sure they've all been downloaded.
|
||
if ((totalToDownload = CountSelectedMessages(tocH))>0)
|
||
{
|
||
// figure out how many of the selected messages need to be downloaded
|
||
for (sumNum=0;sumNum<(*tocH)->count;sumNum++)
|
||
if ((*tocH)->sums[sumNum].selected)
|
||
if (IMAPMessageBeingDownloaded(tocH, sumNum) || IMAPMessageDownloaded(tocH, sumNum)) totalToDownload--;
|
||
|
||
// Make a handle big enough for them
|
||
if (totalToDownload > 0)
|
||
{
|
||
uidsH = NuHandleClear(totalToDownload*sizeof(unsigned long));
|
||
if (uidsH)
|
||
{
|
||
// and stick them in the handle
|
||
c = totalToDownload;
|
||
for (sumNum=0;sumNum<(*tocH)->count;sumNum++)
|
||
if ((*tocH)->sums[sumNum].selected)
|
||
if (!IMAPMessageBeingDownloaded(tocH, sumNum) && !IMAPMessageDownloaded(tocH, sumNum))
|
||
BMD(&((*tocH)->sums[sumNum].uidHash),&((unsigned long *)(*uidsH))[--c],sizeof(unsigned long));
|
||
|
||
// fetch them all in the foreground
|
||
err = UIDDownloadMessages(tocH, uidsH, true, false);
|
||
|
||
if (err == noErr)
|
||
{
|
||
short i;
|
||
|
||
// put ALL of the messages in the mailbox.
|
||
for (i=0; i < totalToDownload; i++) UpdateIMAPMailbox(tocH);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
err = MemError();
|
||
}
|
||
}
|
||
}
|
||
|
||
if (err==noErr)
|
||
{
|
||
// make a new handle to store uids of deleted messages. We'll build this on the fly.
|
||
uidsH = NuHandleClear(0);
|
||
if (!uidsH) err = MemError();
|
||
}
|
||
|
||
if (err != noErr)
|
||
{
|
||
if (!CommandPeriod && (err!=OFFLINE)) WarnUser(err, MEM_ERR);
|
||
return (err);
|
||
}
|
||
}
|
||
for (sumNum = 0; sumNum < (*tocH)->count; sumNum++)
|
||
{
|
||
if ((*tocH)->sums[sumNum].selected)
|
||
{
|
||
CycleBalls();
|
||
if (count>1) MiniEvents();
|
||
if (EjectBuckaroo || CommandPeriod) break;
|
||
if (!(--count%10) || TickCount()-pTicks>30)
|
||
{
|
||
Progress(NoBar,count,nil,nil,nil);
|
||
pTicks = TickCount();
|
||
}
|
||
|
||
#ifdef IMAP
|
||
// if this is a virtual TOC, and the message has already been processed, skip it.
|
||
if ((*tocH)->virtualTOC && ((*tocH)->sums[sumNum].u.virtualMess.virtualMBIdx == -1)) continue;
|
||
|
||
if ((*tocH)->imapTOC && !(*tocH)->virtualTOC)
|
||
{
|
||
// Make sure IMAP message has been downloaded and stuck in the mailbox
|
||
if (!EnsureMsgDownloaded(tocH,sumNum,true))
|
||
continue; // Couldn't download message
|
||
}
|
||
#endif
|
||
oldCount = (*tocH)->count;
|
||
if (!(realTocH = GetRealTOC(tocH,sumNum,&realSum))) continue;
|
||
#ifdef IMAP
|
||
// if this is a virtual mailbox, and the real mailbox is an IMAP mailbox, don't do anything with this message
|
||
if ((*tocH)->virtualTOC && (*realTocH)->imapTOC) MessErr = noErr;
|
||
else
|
||
#endif
|
||
MessErr=AppendMessage(realTocH,realSum,toTocH,copy,false,false);
|
||
if (MessErr) {if (MessErr!=userCanceledErr) WarnUser(WRITE_MBOX,MessErr); break;}
|
||
|
||
#ifdef IMAP
|
||
// if we moved this IMAP message to a POP mailbox, forget about its attachments so they don't get tidied up.
|
||
if ((*tocH)->imapTOC && !(*toTocH)->imapTOC) (*tocH)->sums[sumNum].opts |= OPT_ORPHAN_ATT;
|
||
#endif
|
||
|
||
// Log the transfer
|
||
if (LogLevel&LOG_MOVE)
|
||
ComposeLogS(LOG_MOVE,nil,"\p%p <20>%p,%p<> <20>%p<>-><3E>%p<>\015", copy ? "\pCopy" : "\pTransfer",(*realTocH)->sums[realSum].from,(*realTocH)->sums[realSum].subj,GetMailboxName(realTocH,realSum,name),toSpec->name);
|
||
|
||
if (oldCount!=(*tocH)->count)
|
||
{
|
||
lastSelected = sumNum;
|
||
sumNum--;
|
||
}
|
||
else if (!copy)
|
||
{
|
||
long serialNum = (*realTocH)->sums[realSum].serialNum;
|
||
|
||
if (!(*tocH)->virtualTOC)
|
||
{
|
||
#ifdef IMAP
|
||
if ((*tocH)->imapTOC && uidsH)
|
||
{
|
||
// If not copying, we'll need to delete original IMAP message when done.
|
||
if (uidsH && !copy)
|
||
{
|
||
unsigned long uid = (*tocH)->sums[sumNum].uidHash;
|
||
err = PtrAndHand(&uid,uidsH,sizeof(uid));
|
||
if (err != noErr)
|
||
{
|
||
WarnUser(err,MEM_ERR);
|
||
ZapHandle(uidsH);
|
||
return(err);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
else if (!DeleteSum(tocH,sumNum))
|
||
{
|
||
lastSelected = sumNum;
|
||
sumNum--; /* back up, so we can try again */
|
||
}
|
||
else
|
||
lastSelected = sumNum;
|
||
}
|
||
|
||
if (realTocH != tocH)
|
||
// delete real message summary
|
||
DeleteSum(realTocH,realSum);
|
||
|
||
// Check for updates to search results
|
||
SearchUpdateSum(toTocH,(*toTocH)->count-1,realTocH,serialNum,true,false);
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef IMAP
|
||
// now delete the IMAP messages that were transferred, if the transfer completed successfully.
|
||
if (!copy && (*tocH)->imapTOC && (MessErr==noErr) && !CommandPeriod && !EjectBuckaroo)
|
||
{
|
||
// uidsH contains uids of messages that have been successfully transferred.
|
||
IMAPDeleteMessages(tocH,uidsH,false,false,false,false);
|
||
}
|
||
#endif
|
||
|
||
(void) BoxFClose(tocH,false);
|
||
(void) BoxFClose(toTocH,true);
|
||
CloseProgress();
|
||
|
||
if ((*tocH)->win && !copy && !CommandPeriod) BoxSelectAfter((*tocH)->win,lastSelected);
|
||
CheckBox(GetWindowMyWindowPtr(FrontWindow_()),False);
|
||
#ifdef TWO
|
||
if (MessErr) NukeXfUndo();
|
||
#endif
|
||
ShowBoxSizes((*tocH)->win);
|
||
return(MessErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SelectedWarnings - Warnings for the selected message
|
||
**********************************************************************/
|
||
OSErr SelectedWarnings(TOCHandle tocH,Boolean toTrash,Boolean nuke)
|
||
{
|
||
Boolean queuedWarning, unsentWarning, unreadWarning, busyWarning;
|
||
OSErr err = noErr;
|
||
short i;
|
||
|
||
MessageWarnings(tocH,-1,toTrash,nuke,&queuedWarning,&unsentWarning,&unreadWarning, &busyWarning);
|
||
|
||
for (i=0;i<(*tocH)->count && !err;i++)
|
||
if ((*tocH)->sums[i].selected)
|
||
err = MessageWarnings(tocH,i,toTrash,nuke,
|
||
&queuedWarning,&unsentWarning,&unreadWarning,&busyWarning);
|
||
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SingleWarnings - Give warnings for a single message
|
||
**********************************************************************/
|
||
OSErr SingleWarnings(TOCHandle tocH,short sumNum,Boolean toTrash,Boolean nuke)
|
||
{
|
||
Boolean queuedWarning, unsentWarning, unreadWarning, busyWarning;
|
||
|
||
MessageWarnings(tocH,-1,toTrash,nuke,&queuedWarning,&unsentWarning,&unreadWarning,&busyWarning);
|
||
|
||
return(MessageWarnings(tocH,sumNum,toTrash,nuke,
|
||
&queuedWarning,&unsentWarning,&unreadWarning,&busyWarning));
|
||
}
|
||
|
||
|
||
/**********************************************************************
|
||
*
|
||
**********************************************************************/
|
||
OSErr MessageWarnings(TOCHandle tocH, short sumNum,Boolean toTrash,Boolean nuke,
|
||
Boolean *queuedWarning,Boolean *unsentWarning,Boolean *unreadWarning,Boolean *busyWarning)
|
||
{
|
||
if (sumNum<0)
|
||
{
|
||
*queuedWarning = (*tocH)->which==OUT && !PrefIsSet(PREF_EASY_DEL_QUEUED);
|
||
*unreadWarning = !PrefIsSet(PREF_EASY_DEL_UNREAD);
|
||
*unsentWarning = (*tocH)->which==OUT && !PrefIsSet(PREF_EASY_DEL_UNSENT);
|
||
*busyWarning = true; // for now, until/if we get a pref for it
|
||
}
|
||
else
|
||
{
|
||
short button, verb;
|
||
|
||
if (toTrash)
|
||
if (nuke)
|
||
{
|
||
button = NUKE_BTN;
|
||
verb = NUKE_VERB;
|
||
}
|
||
else
|
||
{
|
||
button = TRASH_BTN;
|
||
verb = TRASH_VERB;
|
||
}
|
||
else
|
||
{
|
||
button = XFER_BTN;
|
||
verb = XFER_VERB;
|
||
}
|
||
|
||
// clarence 4/25/97
|
||
if ((*tocH)->sums[sumNum].state == BUSY_SENDING) {
|
||
WarnUser (SENDING_WARNING, 0);
|
||
*busyWarning = False;
|
||
return(1);
|
||
}
|
||
|
||
if ((*tocH)->sums[sumNum].flags&FLAG_SKIPWARN) return(noErr);
|
||
if (toTrash && *unreadWarning && ((*tocH)->sums[sumNum].state==UNREAD))
|
||
{
|
||
if (!Mom(button,0,PREF_EASY_DEL_UNREAD,UNREAD_WARNING,verb)) return(1);
|
||
*unreadWarning = False; /* been there, done that. */
|
||
}
|
||
if (*queuedWarning && IsQueued(tocH,sumNum))
|
||
{
|
||
if (!Mom(button,0,PREF_EASY_DEL_QUEUED,QUEUED_WARNING,verb)) return(1);
|
||
*queuedWarning = *unsentWarning = False; /* been there, done that. */
|
||
}
|
||
if (*unsentWarning && ((*tocH)->sums[sumNum].state!=SENT))
|
||
{
|
||
if (!Mom(button,0,PREF_EASY_DEL_UNSENT,UNSENT_WARNING,verb)) return(1);
|
||
*unsentWarning = False; /* been there, done that. */
|
||
}
|
||
}
|
||
return(noErr);
|
||
}
|
||
|
||
|
||
/**********************************************************************
|
||
* FixSourceStatus - fix the status of a source message
|
||
**********************************************************************/
|
||
void FixSourceStatus(TOCHandle tocH,short sumNum)
|
||
{
|
||
MessHandle messH = (*tocH)->sums[sumNum].messH;
|
||
TOCHandle sourceTocH;
|
||
short sourceNum;
|
||
uLong **midList;
|
||
short i;
|
||
|
||
if ((*tocH)->which == OUT && messH && SumOf(messH)->state != SENT &&(*messH)->aSourceMID.offset)
|
||
{
|
||
midList = (*messH)->aSourceMID.data;
|
||
for (i=(*messH)->aSourceMID.offset/(3*sizeof(long))-1;i>=0;i--)
|
||
{
|
||
uLong sourceMID = (*midList)[i*3];
|
||
short sourceOrigState = (*midList)[i*3+1];
|
||
short sourceNewState = (*midList)[i*3+2];
|
||
if (sourceMID && sourceMID != kNeverHashed && sourceOrigState != sourceNewState)
|
||
{
|
||
if (!FindMessageByMID(sourceMID,&sourceTocH,&sourceNum))
|
||
{
|
||
if (sourceNewState == (*sourceTocH)->sums[sourceNum].state)
|
||
SetState(sourceTocH,sourceNum,sourceOrigState);
|
||
}
|
||
}
|
||
}
|
||
AccuZap((*messH)->aSourceMID);
|
||
}
|
||
}
|
||
|
||
/**********************************************************************
|
||
* FindMessageByMID - find a message by mid. Works only on open toc's at the moment
|
||
**********************************************************************/
|
||
OSErr FindMessageByMID(uLong mid,TOCHandle *tocH,short *sumNum)
|
||
{
|
||
TOCHandle lTocH;
|
||
long lsum;
|
||
|
||
for (lTocH=TOCList;lTocH;lTocH = (*lTocH)->next)
|
||
{
|
||
if (!TOCFindMessByMID(mid,lTocH,&lsum))
|
||
{
|
||
*sumNum = lsum;
|
||
*tocH = lTocH;
|
||
return(noErr);
|
||
}
|
||
}
|
||
return(fnfErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* TOCFindMessByMID - find a message in a toc by uid hash
|
||
**********************************************************************/
|
||
OSErr TOCFindMessByMID(uLong mid,TOCHandle tocH,long *sumNum)
|
||
{
|
||
short lSumNum;
|
||
|
||
for (lSumNum=(*tocH)->count-1;lSumNum>=0;lSumNum--)
|
||
if ((*tocH)->sums[lSumNum].uidHash == mid)
|
||
{
|
||
*sumNum = lSumNum;
|
||
return(noErr);
|
||
}
|
||
return(fnfErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* TOCFindMessByMsgID - find a message in a toc by message id
|
||
**********************************************************************/
|
||
OSErr TOCFindMessByMsgID(uLong mid,TOCHandle tocH,long *sumNum)
|
||
{
|
||
short lSumNum;
|
||
|
||
for (lSumNum=(*tocH)->count-1;lSumNum>=0;lSumNum--)
|
||
if ((*tocH)->sums[lSumNum].msgIdHash == mid)
|
||
{
|
||
*sumNum = lSumNum;
|
||
return(noErr);
|
||
}
|
||
return(fnfErr);
|
||
}
|
||
|
||
#ifdef TWO
|
||
/************************************************************************
|
||
* MovingAttachments - we're moving attachments into or out of the trash
|
||
************************************************************************/
|
||
void MovingAttachments(TOCHandle tocH,short sumNum,Boolean attach,Boolean wipe,Boolean nuke,Boolean IMAPStubsOnly)
|
||
{
|
||
FSSpec attFolder, trashFolder;
|
||
FSSpecPtr fromSpec, toSpec;
|
||
|
||
if (attach)
|
||
{
|
||
if (GetAttFolderSpec(&attFolder)) return; /* no folder set */
|
||
}
|
||
else
|
||
SubFolderSpec(PARTS_FOLDER,&attFolder);
|
||
if (GetTrashSpec(attFolder.vRefNum,&trashFolder)) return; /* oh well; unimportant error */
|
||
if (!nuke && (*tocH)->which==TRASH)
|
||
{
|
||
fromSpec = &trashFolder;
|
||
toSpec = &attFolder;
|
||
}
|
||
else
|
||
{
|
||
fromSpec = &attFolder;
|
||
toSpec = &trashFolder;
|
||
}
|
||
MovingAttachmentsLo(tocH,sumNum,attach,wipe,nuke,IMAPStubsOnly,fromSpec,toSpec);
|
||
}
|
||
|
||
/************************************************************************
|
||
* MovingAttachmentsLo - we're moving attachments to specific folders
|
||
************************************************************************/
|
||
void MovingAttachmentsLo(TOCHandle tocH,short sumNum,Boolean attach,Boolean wipe,Boolean nuke,Boolean IMAPStubsOnly,FSSpecPtr fromSpec,FSSpecPtr toSpec)
|
||
{
|
||
Handle text;
|
||
long offset;
|
||
FSSpec attSpec, dupSpec, newSpec;
|
||
Boolean iOpened = (*tocH)->sums[sumNum].messH == nil;
|
||
TOCHandle attTOCH;
|
||
FInfo info;
|
||
|
||
CacheMessage(tocH,sumNum);
|
||
if (!(text=(*tocH)->sums[sumNum].cache)) return;
|
||
HNoPurge(text);
|
||
offset = (*tocH)->sums[sumNum].bodyOffset-1;
|
||
|
||
while (0<=(offset = FindAnAttachment(text,offset+1,&attSpec,attach,nil,nil,nil)))
|
||
{
|
||
#ifdef IMAP
|
||
if (IsIMAPAttachmentStub(&attSpec))
|
||
{
|
||
// Just delete IMAP attachment stubs
|
||
if (wipe || nuke) FSpDelete(&attSpec);
|
||
continue;
|
||
}
|
||
if (IMAPStubsOnly)
|
||
continue;
|
||
#endif
|
||
if (SameVRef(attSpec.vRefNum,toSpec->vRefNum) &&
|
||
(!fromSpec || AttStillInFolder(&attSpec,fromSpec)))
|
||
{
|
||
/*
|
||
* is it a mailbox?
|
||
*/
|
||
FSpGetFInfo(&attSpec,&info);
|
||
if (!(info.fdFlags&kIsAlias) && IsMailbox(&attSpec))
|
||
{
|
||
dupSpec = attSpec;
|
||
|
||
/*
|
||
* if it's open, we must close it
|
||
*/
|
||
if (attTOCH = FindTOC(&attSpec))
|
||
{
|
||
Boolean oldSuper = PrefIsSet(PREF_SUPERCLOSE);
|
||
if (!oldSuper) TogglePref(PREF_SUPERCLOSE);
|
||
CloseMyWindow(GetMyWindowWindowPtr((*attTOCH)->win));
|
||
if (!oldSuper) TogglePref(PREF_SUPERCLOSE);
|
||
}
|
||
Box2TOCSpec(&attSpec,&dupSpec);
|
||
|
||
if (wipe) WipeSpec(&dupSpec);
|
||
else SpecMove(&dupSpec,toSpec); /* if toc move fails, at least we tried */
|
||
|
||
/*
|
||
* now, if in the menus, kill the menu item
|
||
*/
|
||
if (!FSMakeFSSpec(MailRoot.vRef,MailRoot.dirId,attSpec.name,&dupSpec))
|
||
{
|
||
if (IsAlias(&dupSpec,&dupSpec) && SameSpec(&attSpec,&dupSpec))
|
||
{
|
||
FSMakeFSSpec(MailRoot.vRef,MailRoot.dirId,attSpec.name,&dupSpec);
|
||
RemoveBoxHigh(&dupSpec);
|
||
FSpDelete(&dupSpec); /* get rid of alias */
|
||
Box2TOCSpec(&dupSpec,&dupSpec);
|
||
FSpDelete(&dupSpec); /* and toc alias */
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* move the file into the trash
|
||
*/
|
||
if (wipe) WipeSpec(&attSpec);
|
||
else if (dupFNErr==SpecMove(&attSpec,toSpec))
|
||
{
|
||
/* dup filename. rename. */
|
||
dupSpec = *toSpec;
|
||
PCopy(dupSpec.name,attSpec.name);
|
||
newSpec = dupSpec;
|
||
UniqueSpec(&newSpec,31);
|
||
if (!FSpRename(&dupSpec,newSpec.name))
|
||
SpecMove(&attSpec,toSpec);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* AttStillInFolder - is an attachment still in the attachments folder?
|
||
************************************************************************/
|
||
Boolean AttStillInFolder(FSSpecPtr att, FSSpecPtr folder)
|
||
{
|
||
if (PrefIsSet(PREF_ATT_SUBFOLDER_TRASH))
|
||
return(SpecInSubfolderOf(att,folder));
|
||
else
|
||
return(SameVRef(att->vRefNum,folder->vRefNum) && att->parID==folder->parID);
|
||
}
|
||
#endif
|
||
|
||
/************************************************************************
|
||
* FindAnAttachment - find an attachment from a line of text
|
||
************************************************************************/
|
||
long FindAnAttachment(Handle text,long offset,FSSpecPtr spec,Boolean attach,uLong *cid, uLong *relURL, uLong *absURL)
|
||
{
|
||
UPtr spot,newLine,end;
|
||
Boolean result=False;
|
||
Str255 line;
|
||
|
||
LDRef(text);
|
||
end = *text + GetHandleSize_(text);
|
||
spot=*text+offset;
|
||
while ( spot<end && *spot++!='\015');
|
||
|
||
for (;spot<end;spot=newLine+1)
|
||
{
|
||
for (newLine=spot;newLine<end && *newLine!='\015';newLine++);
|
||
if (newLine-spot>24 && newLine-spot<255)
|
||
{
|
||
MakePStr(line,spot,newLine-spot);
|
||
if (attach)
|
||
{
|
||
result = !AttLine2Spec(line,spec,False);
|
||
if (result) break;
|
||
}
|
||
else
|
||
{
|
||
result = !RelLine2Spec(line,spec,cid,relURL,absURL);
|
||
if (result) break;
|
||
}
|
||
}
|
||
}
|
||
offset = result ? spot-*text : -1;
|
||
UL(text);
|
||
return(offset);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* InitAttachmentFinder - initialize data for finding attachments in
|
||
* outgoing or received messages
|
||
**********************************************************************/
|
||
void InitAttachmentFinder(FindAttPtr pData,Handle text,Boolean attach,TOCHandle tocH,MSumPtr sum)
|
||
{
|
||
pData->text = text;
|
||
pData->outgoing = (*tocH)->which==OUT || sum->state==SENT || sum->state==UNSENT || sum->flags & FLAG_OUT;
|
||
pData->attach = attach;
|
||
if (pData->outgoing)
|
||
{
|
||
// Outgoing message. Find Attachments header
|
||
Str63 hdrName;
|
||
|
||
GetRString(hdrName,HeaderStrn+ATTACH_HEAD);
|
||
HandleHeadFindStr(text,hdrName,&pData->hs);
|
||
}
|
||
else
|
||
{
|
||
// Received message. Start at beginning of message body
|
||
pData->offset = sum->bodyOffset-1;
|
||
}
|
||
}
|
||
|
||
/**********************************************************************
|
||
* GetNextAttachment - find next attachment
|
||
**********************************************************************/
|
||
Boolean GetNextAttachment(FindAttPtr pData,FSSpecPtr spec)
|
||
{
|
||
Boolean result = false;
|
||
|
||
if (pData->text)
|
||
{
|
||
if (pData->outgoing)
|
||
{
|
||
result = pData->attach ? (GetIndAttachmentLo(pData->text,1,spec,nil,&pData->hs)!=1) : false;
|
||
}
|
||
else
|
||
{
|
||
pData->offset = FindAnAttachment(pData->text,pData->offset,spec,pData->attach,nil,nil,nil);
|
||
result = pData->offset != -1;
|
||
}
|
||
if (!result)
|
||
// No more attachments. Signal we are done
|
||
pData->text = nil;
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* DeleteMessage - delete a summary from a toc, and fix the screen, too
|
||
**********************************************************************/
|
||
void DeleteMessage(TOCHandle tocH, int sumNum,Boolean nuke)
|
||
{
|
||
#ifdef IMAP
|
||
if ((*tocH)->imapTOC)
|
||
{
|
||
// close the IMAP message to be deleted, even if it's just going to be marked.
|
||
MessHandle messH = (MessHandle)(*tocH)->sums[sumNum].messH;
|
||
if (messH) CloseMyWindow(GetMyWindowWindowPtr((*messH)->win));
|
||
|
||
IMAPDeleteMessage(tocH, (*tocH)->sums[sumNum].uidHash, nuke, false, false);
|
||
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
if (SingleWarnings(tocH,sumNum,True,nuke||(*tocH)->which==TRASH)) return;
|
||
DeleteMessageLo(tocH,sumNum,nuke);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* DeleteMessage - delete a summary from a toc, and fix the screen, too
|
||
**********************************************************************/
|
||
void DeleteMessageLo(TOCHandle tocH, int sumNum,Boolean nuke)
|
||
{
|
||
MessHandle messH = (MessHandle)(*tocH)->sums[sumNum].messH;
|
||
Boolean dirt = 0;
|
||
FSSpec trashSpec;
|
||
int oldN = (*tocH)->count;
|
||
Boolean wipe = PrefIsSet(PREF_WIPE)&&((*tocH)->sums[sumNum].opts&OPT_WIPE);
|
||
|
||
if ((*tocH)->which!=TRASH && !wipe && !nuke)
|
||
{
|
||
trashSpec.vRefNum = MailRoot.vRef;
|
||
trashSpec.parID = MailRoot.dirId;
|
||
GetRString(trashSpec.name,TRASH);
|
||
MoveMessageLo(tocH,sumNum,&trashSpec,False,False,true);
|
||
}
|
||
else
|
||
{
|
||
#ifdef TWO
|
||
if (!(*tocH)->imapTOC) NukeXfUndo();
|
||
#endif
|
||
if (wipe) WipeMessage(tocH,sumNum);
|
||
#ifdef TWO
|
||
if ((!(*tocH)->imapTOC) && PrefIsSet(PREF_SERVER_DEL))
|
||
{
|
||
AddTSToPOPD(DELETE_ID,tocH,sumNum,False);
|
||
}
|
||
if ((*tocH)->sums[sumNum].opts & OPT_HAS_SPOOL) RemSpoolFolder((*tocH)->sums[sumNum].uidHash);
|
||
if (nuke &&((*tocH)->sums[sumNum].flags&FLAG_HAS_ATT))
|
||
{
|
||
#ifdef IMAP
|
||
// move the attachment to the trash if the pref is set, as long as they haven't been orphaned.
|
||
if (PrefIsSet(PREF_TIDY_FOLDER) && !((*tocH)->sums[sumNum].opts & OPT_ORPHAN_ATT))
|
||
MovingAttachments(tocH,sumNum,True,wipe,True,false);
|
||
else
|
||
MovingAttachments(tocH,sumNum,True,wipe,True,true);
|
||
#else
|
||
if (PrefIsSet(PREF_TIDY_FOLDER)) MovingAttachments(tocH,sumNum,True,wipe,True,false);
|
||
#endif
|
||
|
||
#ifdef IMAP
|
||
// move the inline parts to the trash, unless this is an IMAP message that has been copied to a local mailbox
|
||
if (!((*tocH)->sums[sumNum].opts & OPT_ORPHAN_ATT))
|
||
#endif
|
||
MovingAttachments(tocH,sumNum,False,wipe,True,false);
|
||
}
|
||
#endif
|
||
if (messH) CloseMyWindow(GetMyWindowWindowPtr((*messH)->win));
|
||
if ((*tocH)->count==oldN) DeleteSum(tocH,sumNum);
|
||
}
|
||
}
|
||
|
||
/**********************************************************************
|
||
* WipeMessage - clear the contents of a message, really
|
||
**********************************************************************/
|
||
OSErr WipeMessage(TOCHandle tocH,short sumNum)
|
||
{
|
||
OSErr err;
|
||
|
||
MovingAttachments(tocH,sumNum,True,True,False,false);
|
||
MovingAttachments(tocH,sumNum,False,True,False,false);
|
||
|
||
if (err=BoxFOpen(tocH)) return(err);
|
||
|
||
err = WipeDiskArea((*tocH)->refN,(*tocH)->sums[sumNum].offset,(*tocH)->sums[sumNum].length);
|
||
return err;
|
||
}
|
||
|
||
/************************************************************************
|
||
* MessageError - return the most recent error code from these functions
|
||
************************************************************************/
|
||
int MessageError(void)
|
||
{
|
||
return(MessErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* StripTrailingNewlines - strip the newlines off the end of a text block
|
||
**********************************************************************/
|
||
long StripTrailingNewlines(Handle buffer,long stop)
|
||
{
|
||
long size = GetHandleSize(buffer);
|
||
Ptr spot = *buffer + size;
|
||
long newSize;
|
||
|
||
while (spot[-1]=='\015' && spot>*buffer+stop) spot--;
|
||
|
||
newSize = spot - *buffer;
|
||
SetHandleSize(buffer,newSize);
|
||
return(size-newSize);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* WeedHeaders - weed a message's headers, leaving only the interesting
|
||
* ones.
|
||
************************************************************************/
|
||
void WeedHeaders(UHandle buffer,long *weeded,short toWeed,AccuPtr weeds)
|
||
{
|
||
long size;
|
||
short bad;
|
||
Handle badH = GetResource('STR#',toWeed);
|
||
UPtr badSpot; //pointer to bad header string we're currently looking for
|
||
Byte old;
|
||
short res;
|
||
|
||
if (badH)
|
||
{
|
||
UPtr spot; // char we're examining
|
||
UPtr done; // just past end of good chars that have been copied
|
||
UPtr end; // end of the whole shebang
|
||
UPtr found; // beginning of text we found
|
||
short badN;
|
||
|
||
HNoPurge(badH);
|
||
PtrAndHand("",badH,1); /* room for a null terminator */
|
||
LDRef(badH);
|
||
|
||
done = spot = LDRef(buffer);
|
||
badN = CountStrn(toWeed);
|
||
end = spot + GetHandleSize_(buffer);
|
||
|
||
// while there are chars left
|
||
while (spot<end)
|
||
{
|
||
if (*spot=='\015') break; //two returns in a row are the end of the headers
|
||
|
||
// search for the header name
|
||
badSpot = *badH+2;
|
||
for (bad=0;bad<badN;bad++)
|
||
{
|
||
old = badSpot[*badSpot+1];
|
||
badSpot[*badSpot+1] = 0;
|
||
res = striscmp(spot,badSpot+1);
|
||
badSpot[*badSpot+1] = old;
|
||
// found it!
|
||
if (!res)
|
||
{
|
||
// skip the header
|
||
found = spot;
|
||
while (spot<end)
|
||
if (*spot++=='\015')
|
||
if (*spot!=' ' && *spot!='\t') break;
|
||
// copy?
|
||
if (weeds) AccuAddPtr(weeds,found,spot-found);
|
||
goto nextHead; // continue looking at next header
|
||
}
|
||
else badSpot += *badSpot+1;
|
||
}
|
||
// copy the current header
|
||
while (spot<end)
|
||
if ((*done++ = *spot++)=='\015')
|
||
if (*spot!=' ' && *spot!='\t') break;
|
||
nextHead:;
|
||
}
|
||
|
||
while (spot<end) *done++ = *spot++;
|
||
size = done - *buffer;
|
||
if (weeded) *weeded = end-done;
|
||
UL(badH);
|
||
HPurge(badH);
|
||
HUnlock(buffer);
|
||
SetHandleBig_(buffer,size);
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* SetMessText - stick some text into one of the fields of a message.
|
||
************************************************************************/
|
||
OSErr SetMessText(MessHandle messH,short whichTXE,UPtr string,long size)
|
||
{
|
||
HeadSpec hs;
|
||
|
||
if (CompHeadFind(messH,whichTXE,&hs))
|
||
return(CompHeadSetPtr(TheBody,&hs,string,size));
|
||
else return(fnfErr);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* Fix1MessServerArea - fix the server display of a single message
|
||
**********************************************************************/
|
||
void Fix1MessServerArea(MyWindowPtr win)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr(win);
|
||
uLong uidHash;
|
||
MessHandle messH;
|
||
ControlHandle fetch;
|
||
ControlHandle trash;
|
||
Boolean onFetch, onDelete, lmos;
|
||
OSType popdType;
|
||
|
||
PushGWorld();
|
||
|
||
if (GetWindowKind(winWP)==MESS_WIN && IsWindowVisible(winWP))
|
||
{
|
||
messH = Win2MessH(win);
|
||
uidHash = SumOf(messH)->uidHash;
|
||
popdType = PERS_POPD_TYPE(MESS_TO_PPERS(messH));
|
||
|
||
fetch = FindControlByRefCon(win,mcFetch);
|
||
trash = FindControlByRefCon(win,mcTrash);
|
||
|
||
if (IdIsOnPOPD(popdType,POPD_ID,uidHash))
|
||
{
|
||
lmos = PrefIsSet(PREF_LMOS);
|
||
onFetch = IdIsOnPOPD(popdType,FETCH_ID,uidHash);
|
||
onDelete = IdIsOnPOPD(popdType,DELETE_ID,uidHash);
|
||
if ((*messH)->hasFetchIcon = MessFlagIsSet(messH,FLAG_SKIPPED))
|
||
if (fetch) {SetControlValue(fetch,onFetch); ShowControl(fetch);}
|
||
(*messH)->hasDelIcon = True;
|
||
if (trash)
|
||
{
|
||
SetControlValue(trash,onDelete);
|
||
HiliteControl(trash,onFetch && !lmos ? 255 : 0);
|
||
ShowControl(trash);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
(*messH)->hasFetchIcon = (*messH)->hasDelIcon = False;
|
||
if (fetch) HideControl(fetch);
|
||
if (trash) HideControl(trash);
|
||
}
|
||
}
|
||
else if (GetWindowKind(winWP)==MBOX_WIN || GetWindowKind(winWP)==CBOX_WIN)
|
||
{
|
||
InvalTocBox((TOCHandle)GetMyWindowPrivateData(win),-2,blServer);
|
||
}
|
||
PopGWorld();
|
||
}
|
||
|
||
/**********************************************************************
|
||
* RecordTransAttachments - record the fact that we have attachments from a translator
|
||
**********************************************************************/
|
||
OSErr RecordTransAttachments(FSSpecPtr spec)
|
||
{
|
||
WindowPtr InsertWinWP = GetMyWindowWindowPtr (InsertWin);
|
||
MessHandle messH;
|
||
FSSpecHandle h;
|
||
|
||
if (InsertWin && GetWindowKind(InsertWinWP)==MESS_WIN)
|
||
{
|
||
messH = Win2MessH(InsertWin);
|
||
if (!(*messH)->etlFiles)
|
||
{
|
||
h = NuHTempBetter(0);
|
||
if (!h) return(MemError());
|
||
(*messH)->etlFiles = h;
|
||
}
|
||
#ifdef NEVER
|
||
if (RunType!=Production)
|
||
{
|
||
Str255 title;
|
||
MyGetWTitle(InsertWinWP,title);
|
||
Dprintf("\p;log RecordTransAttachments;sc;g");
|
||
Dprintf("\p<EFBFBD>%p<>;g",title);
|
||
Dprintf("\p;log;g",spec->name);
|
||
}
|
||
#endif
|
||
return(PtrPlusHand_(spec,(*messH)->etlFiles,sizeof(*spec)));
|
||
}
|
||
return noErr;
|
||
}
|
||
|
||
/************************************************************************
|
||
* CleanSpoolFolder - clean up the spool folder
|
||
************************************************************************/
|
||
OSErr CleanSpoolFolder(uLong age)
|
||
{
|
||
CInfoPBRec hfi;
|
||
FSSpec spec;
|
||
|
||
if (SubFolderSpec(SPOOL_FOLDER,&spec))
|
||
return(noErr);
|
||
|
||
Zero(hfi);
|
||
hfi.hFileInfo.ioNamePtr = spec.name;
|
||
age = LocalDateTime()-24*3600*age;
|
||
while (!DirIterate(spec.vRefNum,spec.parID,&hfi))
|
||
{
|
||
if (EventPending()) return(userCanceledErr);
|
||
MiniEvents();
|
||
if (AllDigits(spec.name+1,*spec.name)) // we have a spool folder
|
||
if (hfi.dirInfo.ioDrMdDat<age)
|
||
if (!RemoveDir(&spec)) hfi.hFileInfo.ioFDirIndex--;
|
||
}
|
||
return(noErr);
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* AppendMessText - stick some text after one of the fields of a message.
|
||
************************************************************************/
|
||
OSErr AppendMessText(MessHandle messH,short whichTXE,UPtr string,long size)
|
||
{
|
||
HeadSpec hs;
|
||
|
||
if (CompHeadFind(messH,whichTXE,&hs))
|
||
{
|
||
return(CompHeadAppendPtr(TheBody,&hs,string,size));
|
||
}
|
||
else return(fnfErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* MessPlainBytes - make sure bytes are plain
|
||
************************************************************************/
|
||
OSErr MessPlainBytes(MessHandle messH,short whichTXE,short bytes)
|
||
{
|
||
OSErr err = noErr;
|
||
HeadSpec hs;
|
||
long start,stop;
|
||
|
||
if (!CompHeadFind(messH,whichTXE,&hs))
|
||
err = fnfErr;
|
||
else
|
||
{
|
||
if (bytes<0)
|
||
{
|
||
start = hs.stop+bytes;
|
||
start = MAX(start,hs.value);
|
||
stop = hs.stop;
|
||
}
|
||
else
|
||
{
|
||
start = hs.value;
|
||
stop = start+bytes;
|
||
}
|
||
PeteParaConvert(TheBody,start,stop);
|
||
PetePlain(TheBody,start,stop,peAllValid);
|
||
PeteParaRange(TheBody,&start,&stop);
|
||
if (!whichTXE && bytes<0) PetePlainPara(TheBody,kPETELastPara);
|
||
PetePlainParaAt(TheBody,start,stop);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
/************************************************************************
|
||
* DoIterativeThingyLo - do something over all selected messages
|
||
************************************************************************/
|
||
void DoIterativeThingyLo(TOCHandle tocH,int item,long modifiers,TextAddrHandle addr,Boolean warnings)
|
||
{
|
||
int sumNum;
|
||
short toWhom = (short) addr;
|
||
short lastSelected = -1;
|
||
Str255 title;
|
||
FSSpec trashSpec;
|
||
long count;
|
||
long size;
|
||
long gran;
|
||
uLong pTicks = TickCount();
|
||
Boolean nuke = item==MESSAGE_DELETE_ITEM&&((*tocH)->which==TRASH || (optionKey|shiftKey)==(modifiers&(optionKey|shiftKey)))&&(!(*tocH)->imapTOC||PrefIsSet(PREF_ALLOW_IMAP_NUKE));
|
||
#ifdef THREADING_ON
|
||
Boolean busy = false;
|
||
#endif
|
||
uLong oldEzOpenSerialNum = (*tocH)->previewID ? (*tocH)->ezOpenSerialNum: 0;
|
||
|
||
#ifdef PERF
|
||
PerfControl(ThePGlobals,True);
|
||
#endif PERF
|
||
|
||
#ifdef IMAP
|
||
if (item==MESSAGE_DELETE_ITEM && (*tocH)->imapTOC && !nuke)
|
||
{
|
||
Handle uids = nil;
|
||
long c = CountSelectedMessages(tocH);
|
||
long sumNum;
|
||
|
||
// only do something if there are some selected messages
|
||
if (c)
|
||
{
|
||
// build a list of uids to be deleted
|
||
uids = NuHandleClear(c*sizeof(unsigned long));
|
||
if (uids)
|
||
{
|
||
LDRef(tocH);
|
||
for (sumNum=0;sumNum<(*tocH)->count && c;sumNum++)
|
||
if ((*tocH)->sums[sumNum].selected)
|
||
{
|
||
// Close this message if it's open
|
||
if ((*tocH)->sums[sumNum].messH) CloseMyWindow(GetMyWindowWindowPtr((*(*tocH)->sums[sumNum].messH)->win));
|
||
|
||
BMD(&((*tocH)->sums[sumNum].uidHash),&((unsigned long *)(*uids))[--c],sizeof(unsigned long));
|
||
}
|
||
UL(tocH);
|
||
|
||
// and delete them.
|
||
IMAPDeleteMessages(tocH, uids, nuke, false, false, false);
|
||
|
||
// preview the next message ...
|
||
if (oldEzOpenSerialNum && (sumNum=FindSumBySerialNum(tocH,oldEzOpenSerialNum))>=0 && !(modifiers&shiftKey))
|
||
Preview(tocH,sumNum);
|
||
}
|
||
else
|
||
{
|
||
WarnUser(MemError(), MEM_ERR);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
if (item==MESSAGE_DELETE_ITEM && !nuke)
|
||
{
|
||
trashSpec.vRefNum = MailRoot.vRef;
|
||
trashSpec.parID = MailRoot.dirId;
|
||
GetRString(trashSpec.name,TRASH);
|
||
MoveSelectedMessagesLo(tocH,&trashSpec,False,True,true,warnings);
|
||
if (oldEzOpenSerialNum && (sumNum=FindSumBySerialNum(tocH,oldEzOpenSerialNum))>=0 && !(modifiers&shiftKey))
|
||
Preview(tocH,sumNum);
|
||
(*tocH)->userActive = !(modifiers&shiftKey);
|
||
return;
|
||
}
|
||
|
||
if (nuke && warnings && SelectedWarnings(tocH,True,True)) return;
|
||
|
||
// memory preflight
|
||
count = CountSelectedMessages(tocH);
|
||
if (item==MESSAGE_FORWARD_ITEM || item==MESSAGE_REDISTRIBUTE_ITEM || item==MESSAGE_REPLY_ITEM || item==MESSAGE_SALVAGE_ITEM)
|
||
{
|
||
if (item==MESSAGE_REDISTRIBUTE_ITEM && PrefIsSetOrNot(PREF_TURBO_REDIRECT,modifiers,optionKey) && toWhom)
|
||
; // nevermind, turbo redirect
|
||
else
|
||
{
|
||
size = count * 7 K; // account just for the windows
|
||
if (item!=MESSAGE_REPLY_ITEM || !(modifiers&shiftKey))
|
||
size += SizeSelectedMessages(tocH,true);
|
||
if (MemoryPreflight(size)) return; // too much memory asked for
|
||
|
||
if (count > GetRLong(WIN_GEN_WARNING_THRESH))
|
||
if (!MultiMessageOpOK(WIN_GEN_WARNING,count))
|
||
return;
|
||
}
|
||
}
|
||
|
||
|
||
#ifdef TWO
|
||
if (item == STATE_HIER_MENU)
|
||
toWhom = Item2Status(toWhom);
|
||
#endif
|
||
|
||
/*
|
||
* progress stuff
|
||
*/
|
||
switch (item)
|
||
{
|
||
case PERS_HIER_MENU:
|
||
//No Personalities menu in Light
|
||
if (HasFeature (featureMultiplePersonalities))
|
||
gran = (*tocH)->which==OUT ? 1 : 50;
|
||
break;
|
||
case MESSAGE_DELETE_ITEM:
|
||
gran = 10;
|
||
break;
|
||
case STATE_HIER_MENU:
|
||
case LABEL_HIER_MENU:
|
||
case PRIOR_HIER_MENU:
|
||
case TABLE_HIER_MENU:
|
||
case SERVER_HIER_MENU:
|
||
gran = 50;
|
||
break;
|
||
default:
|
||
gran = 1;
|
||
break;
|
||
}
|
||
|
||
#ifdef IMAP
|
||
if ((*tocH)->imapTOC // some IMAP operations will open their own progress window
|
||
&& (item==MESSAGE_DELETE_ITEM // no progress for deletes from the message menu, or server menu options
|
||
|| (item == SERVER_HIER_MENU)));
|
||
else
|
||
{
|
||
#endif
|
||
if (count>gran) OpenProgress();
|
||
ProgressMessageR(kpSubTitle,LEFT_TO_PROCESS);
|
||
Progress(NoBar,count,nil,nil,nil);
|
||
#ifdef IMAP
|
||
}
|
||
#endif
|
||
|
||
if (!nuke && item==MESSAGE_DELETE_ITEM) AddXfUndo(tocH,GetTrashTOC(),-1);
|
||
|
||
for (sumNum=(*tocH)->count-1;sumNum>=0;sumNum--)
|
||
{
|
||
if ((*tocH)->sums[sumNum].selected)
|
||
{
|
||
Boolean doVirtualMB;
|
||
TOCHandle realTOC;
|
||
short realSum;
|
||
|
||
lastSelected = sumNum;
|
||
MakeMessTitle(title,tocH,sumNum,False);
|
||
if (count>1) MiniEvents();
|
||
if (CommandPeriod) break;
|
||
if (!(--count%gran) || TickCount()-pTicks>30)
|
||
{
|
||
Progress(NoBar,count,nil,nil,title);
|
||
pTicks = TickCount();
|
||
}
|
||
realTOC = GetRealTOC(tocH,sumNum,&realSum);
|
||
if (nuke && realTOC == tocH)
|
||
SearchUpdateSum(tocH,sumNum,tocH,(*tocH)->sums[sumNum].serialNum,false,true);
|
||
doVirtualMB = DoMessageMenu(item,tocH,sumNum,toWhom,addr,modifiers,nuke,&busy);
|
||
if (!nuke && realTOC == tocH && doVirtualMB)
|
||
// Check for updates to search results
|
||
SearchUpdateSum(tocH,sumNum,tocH,(*tocH)->sums[sumNum].serialNum,false,false);
|
||
if (realTOC && realTOC != tocH && doVirtualMB)
|
||
// do real mailbox also if working in virtual mailbox
|
||
DoMessageMenu(item,realTOC,realSum,toWhom,addr,modifiers,nuke,&busy);
|
||
#ifdef IMAP
|
||
// hack: The Delete from Server, Fetch Message Text, and Fetch Attachments menu choices handle all selected messages for IMAP boxes
|
||
if ((*tocH)->imapTOC && (item == SERVER_HIER_MENU) && ((toWhom == isvmDelete) || (toWhom == isvmFetchMessage) || (toWhom == isvmFetchAttachments))) break;
|
||
#endif
|
||
}
|
||
MonitorGrow(True);
|
||
}
|
||
out:
|
||
|
||
CloseProgress();
|
||
ShowBoxSizes((*tocH)->win);
|
||
#ifdef THREADING_ON
|
||
if (busy)
|
||
WarnUser (SENDING_WARNING, 0);
|
||
#endif
|
||
if (!CommandPeriod)
|
||
{
|
||
#ifdef TWO
|
||
if (item==MESSAGE_REDISTRIBUTE_ITEM &&
|
||
PrefIsSetOrNot(PREF_TURBO_REDIRECT,modifiers,optionKey) &&
|
||
!(modifiers&shiftKey)
|
||
&& toWhom)
|
||
DoIterativeThingy(tocH,MESSAGE_DELETE_ITEM,0,0);
|
||
#endif
|
||
BoxSelectAfter((*tocH)->win,lastSelected);
|
||
}
|
||
if (oldEzOpenSerialNum && !(*tocH)->previewID && (sumNum=FindSumBySerialNum(tocH,oldEzOpenSerialNum))>=0 && !(modifiers&shiftKey))
|
||
Preview(tocH,sumNum);
|
||
CheckBox(GetWindowMyWindowPtr(FrontWindow_()),False);
|
||
#ifdef PERF
|
||
PerfControl(ThePGlobals,False);
|
||
#endif
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* DoMessageMenu - do something to a messages
|
||
************************************************************************/
|
||
Boolean DoMessageMenu(short item,TOCHandle tocH,short sumNum,short toWhom,TextAddrHandle addr,long modifiers,Boolean nuke,Boolean *busy)
|
||
{
|
||
MyWindowPtr win;
|
||
Boolean doVirtualMB = true;
|
||
Str255 s;
|
||
|
||
switch(item)
|
||
{
|
||
case MESSAGE_DELETE_ITEM:
|
||
DeleteMessageLo(tocH,sumNum,nuke);
|
||
break;
|
||
case STATE_HIER_MENU:
|
||
#ifdef THREADING_ON
|
||
if ((*tocH)->sums[sumNum].state==BUSY_SENDING)
|
||
*busy = true;
|
||
else
|
||
#endif
|
||
{
|
||
SetState(tocH,sumNum,toWhom);
|
||
doVirtualMB = false; // Already took care of this
|
||
}
|
||
break;
|
||
case PERS_HIER_MENU:
|
||
//No personalities menu in Light
|
||
if (HasFeature (featureMultiplePersonalities))
|
||
SetPers(tocH,sumNum,FindPersByName(MyGetItem(GetMHandle(item),toWhom,s)),True);
|
||
break;
|
||
case LABEL_HIER_MENU:
|
||
SetSumColor(tocH,sumNum,Menu2Label(toWhom));
|
||
doVirtualMB = false; // Already took care of this
|
||
break;
|
||
case SERVER_HIER_MENU:
|
||
#ifdef IMAP
|
||
ServerMenuChoice(tocH,sumNum,toWhom, (modifiers&shiftKey)!=0);
|
||
#else
|
||
ServerMenuChoice(tocH,sumNum,toWhom);
|
||
#endif
|
||
break;
|
||
case TABLE_HIER_MENU:
|
||
SetMessTable(tocH,sumNum,toWhom);
|
||
break;
|
||
case FILE_MENU:
|
||
ExportHTMLSum(tocH,sumNum);
|
||
break;
|
||
case PRIOR_HIER_MENU:
|
||
SetPriority(tocH,sumNum,NewPrior(toWhom,(*tocH)->sums[sumNum].priority));
|
||
doVirtualMB = false; // Already took care of this
|
||
break;
|
||
default:
|
||
#ifdef IMAP
|
||
// must have the message before we can do anything here.
|
||
// right now, fetch the entire message, including attachments, unless we're just replying.
|
||
if (EnsureMsgDownloaded(tocH,sumNum,item!=MESSAGE_REPLY_ITEM))
|
||
{
|
||
#endif
|
||
if (win = GetAMessage(tocH,sumNum,nil,nil,False))
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr(win);
|
||
switch(item)
|
||
{
|
||
case MESSAGE_SALVAGE_ITEM:
|
||
if (!DoSalvageMessage(win,False)) CommandPeriod=true;
|
||
break;
|
||
case MESSAGE_FORWARD_ITEM:
|
||
if (!DoForwardMessage(win,addr,True)) CommandPeriod=true;
|
||
break;
|
||
case MESSAGE_REPLY_ITEM:
|
||
{
|
||
Boolean all, quote, self;
|
||
ReplyDefaults(modifiers,&all,&self,"e);
|
||
if (!DoReplyMessage(win,all,self,quote,True,toWhom,True,True,True)) CommandPeriod=true;
|
||
}
|
||
break;
|
||
case MESSAGE_REDISTRIBUTE_ITEM:
|
||
if (!DoRedistributeMessage(win,addr,PrefIsSetOrNot(PREF_TURBO_REDIRECT,modifiers,optionKey),False,True)) CommandPeriod=true;
|
||
break;
|
||
}
|
||
if (!IsWindowVisible(winWP))
|
||
CloseMyWindow(winWP);
|
||
else
|
||
NotUsingWindow(winWP);
|
||
}
|
||
else CommandPeriod=true;
|
||
#ifdef IMAP
|
||
}
|
||
else CommandPeriod=true;
|
||
#endif
|
||
doVirtualMB = false;
|
||
break;
|
||
|
||
}
|
||
return doVirtualMB;
|
||
}
|
||
|
||
/************************************************************************
|
||
* ReplyDefaults - get the defaults for reply options
|
||
************************************************************************/
|
||
void ReplyDefaults(short modifiers,Boolean *all, Boolean *self, Boolean *quote)
|
||
{
|
||
*all = PrefIsSetOrNot(PREF_REPLY_ALL,modifiers,optionKey);
|
||
*self = !PrefIsSet(PREF_NOT_ME);
|
||
*quote = (modifiers&shiftKey) == 0;
|
||
}
|
||
|
||
/************************************************************************
|
||
* BoxNextSelected - find first selection in a mailbox
|
||
************************************************************************/
|
||
short BoxNextSelected(TOCHandle tocH,short afterNum)
|
||
{
|
||
int sNum, count;
|
||
|
||
count = (*tocH)->count;
|
||
|
||
for (sNum=afterNum+1;sNum<count;sNum++)
|
||
if ((*tocH)->sums[sNum].selected) return(sNum);
|
||
|
||
return(-1);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SaveTextAsMessage - save a text block as a message
|
||
**********************************************************************/
|
||
OSErr SaveTextAsMessage(Handle preText,Handle text,TOCHandle tocH,long *fromLen)
|
||
{
|
||
long size = GetHandleSize(text);
|
||
OSErr err;
|
||
|
||
if (size && (*text)[size-1]!='\015')
|
||
{
|
||
PtrPlusHand("\015",text,1);
|
||
size++;
|
||
}
|
||
err = SavePtrAsMessage(preText ? LDRef(preText):nil,preText?GetHandleSize(preText):nil,LDRef(text),size,tocH,fromLen);
|
||
UL(text);
|
||
if (preText) UL(preText);
|
||
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SavePtrAsMessage - save a text block as a message
|
||
**********************************************************************/
|
||
OSErr SavePtrAsMessage(UPtr preText,long preSize,UPtr text, long size, TOCHandle tocH,long *fromLen)
|
||
{
|
||
OSErr err;
|
||
long eof;
|
||
Str31 name;
|
||
LineIOD lid;
|
||
MSumType sum;
|
||
FSSpec spec;
|
||
|
||
GetMailboxName(tocH,-1,name);
|
||
|
||
/*
|
||
* open mailbox and write the bytes
|
||
*/
|
||
if (!(err=BoxFOpen(tocH)))
|
||
{
|
||
eof = FindTOCSpot(tocH,size);
|
||
err = SetFPos((*tocH)->refN,fsFromStart,eof);
|
||
if (!err) err = PutOutFromLine((*tocH)->refN,fromLen);
|
||
if (!err && preText) err = AWrite((*tocH)->refN,&preSize,preText);
|
||
if (!err) err = AWrite((*tocH)->refN,&size,text);
|
||
if (!err) err = TruncAtMark((*tocH)->refN);
|
||
TOCSetDirty(tocH,true);
|
||
BoxFClose(tocH,true);
|
||
}
|
||
|
||
/*
|
||
* did it work?
|
||
*/
|
||
if (err)
|
||
{
|
||
FileSystemError(WRITE_MBOX,name,err);
|
||
return(err);
|
||
}
|
||
|
||
/*
|
||
* read it back
|
||
*/
|
||
spec = GetMailboxSpec(tocH,-1);
|
||
if (err=OpenLine(spec.vRefNum,spec.parID,name,fsRdWrPerm,&lid))
|
||
return(FileSystemError(READ_MBOX,name,err));
|
||
if (err=SeekLine(eof,&lid)) return(FileSystemError(READ_MBOX,name,err));
|
||
ReadSum(nil,False,&lid,True);
|
||
Zero(sum);
|
||
if (!ReadSum(&sum,False,&lid,False))
|
||
err = !SaveMessageSum(&sum,&tocH);
|
||
else err = 1;
|
||
CloseLine(&lid);
|
||
return(err);
|
||
}
|
||
|
||
#pragma segment MsgOps
|
||
/************************************************************************
|
||
* DoSalvageMessage - glean what you can from a bounced message's headers
|
||
************************************************************************/
|
||
MyWindowPtr DoSalvageMessage(MyWindowPtr win,Boolean forXfer)
|
||
{
|
||
return DoSalvageMessageLo(win, forXfer, false);
|
||
}
|
||
|
||
/************************************************************************
|
||
* DoSalvageMessageLo - glean what you can from a bounced message's headers
|
||
************************************************************************/
|
||
MyWindowPtr DoSalvageMessageLo(MyWindowPtr win,Boolean forXfer,Boolean forIMAP)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
MessHandle origMessH = (MessHandle)GetMyWindowPrivateData(win);
|
||
MessHandle newMessH;
|
||
MyWindowPtr newWin;
|
||
WindowPtr newWinWP;
|
||
Str255 scratch;
|
||
short field;
|
||
HeadSpec oldHS, newHS;
|
||
OSErr err=noErr;
|
||
UHandle text;
|
||
long newBo;
|
||
Boolean html = !PrefIsSet(PREF_SEND_ENRICHED_NEW);
|
||
|
||
NicknameWatcherFocusChange (win->pte); /* MJN */
|
||
|
||
if (GetWindowKind(winWP)!=COMP_WIN && !SaveMessHi(win,False)) return(nil);
|
||
|
||
#ifdef IMAP
|
||
// Make sure message has been downloaded if IMAP
|
||
if (!EnsureMsgDownloaded((*origMessH)->tocH,(*origMessH)->sumNum,true))
|
||
return nil;
|
||
#endif
|
||
|
||
PushPers(PERS_FORCE(MESS_TO_PERS(origMessH)));
|
||
if (newWin=DoComposeNew(0))
|
||
{
|
||
long sigLen;
|
||
|
||
newMessH = (MessHandle)GetMyWindowPrivateData(newWin);
|
||
XferCustomTable(origMessH,newMessH);
|
||
if (GetWindowKind(winWP)==COMP_WIN || MessFlagIsSet(origMessH,FLAG_OUT))
|
||
{
|
||
//if (win->qWindow.windowKind!=COMP_WIN) AlignHeaders(origMessH);
|
||
for (field=0;!err && field<=ATTACH_HEAD;field++)
|
||
{
|
||
if (field!=FROM_HEAD ||
|
||
!CompHeadGetStr(origMessH,field,scratch) && (MessOptIsSet(origMessH,OPT_REDIRECTED) || IsMe(scratch)))
|
||
{
|
||
if (CompHeadFind(origMessH,field,&oldHS))
|
||
{
|
||
// If we have the body, the oldHS will not include the sig
|
||
// This is bad for copying
|
||
// However, we do need to remember this fact, so we can
|
||
// make sure we lock things correctly later
|
||
if (field==0)
|
||
{
|
||
sigLen = PeteLen((*origMessH)->bodyPTE) - oldHS.stop;
|
||
oldHS.stop += sigLen;
|
||
}
|
||
|
||
if (oldHS.value<oldHS.stop)
|
||
{
|
||
if (CompHeadFind(newMessH,field,&newHS))
|
||
{
|
||
if (field==FROM_HEAD)
|
||
err = CompHeadSetStr((*newMessH)->bodyPTE,&newHS,scratch);
|
||
else
|
||
err = PeteCopy((*origMessH)->bodyPTE,(*newMessH)->bodyPTE,
|
||
oldHS.value,oldHS.stop,newHS.stop,nil,field);
|
||
}
|
||
if (!err && CompHeadFind(newMessH,field,&newHS))
|
||
{
|
||
PeteLock((*newMessH)->bodyPTE,newHS.value,newHS.stop,0);
|
||
if (field==0 && sigLen)
|
||
PeteLock((*newMessH)->bodyPTE,newHS.stop-sigLen,newHS.stop-sigLen+*GetRString(scratch,SIG_INTRO)+1,peModLock|peSelectLock|peClickBeforeLock);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
TOCSetDirty((*newMessH)->tocH,true);
|
||
SetSumColor((*newMessH)->tocH,(*newMessH)->sumNum,GetSumColor((*origMessH)->tocH,(*origMessH)->sumNum));
|
||
SumOf(newMessH)->flags = SumOf(origMessH)->flags;
|
||
SumOf(newMessH)->opts = SumOf(origMessH)->opts;
|
||
SumOf(newMessH)->sigId = SigValidate(SumOf(origMessH)->sigId);
|
||
SumOf(newMessH)->origPriority = SumOf(newMessH)->priority = SumOf(origMessH)->priority;
|
||
if (GetWindowKind(winWP)!=COMP_WIN && MessOptIsSet(newMessH,OPT_INLINE_SIG))
|
||
AddInlineSig(newMessH);
|
||
|
||
if (GetWindowKind(winWP)==COMP_WIN && (*origMessH)->hTranslators)
|
||
{
|
||
text = DupHandle((Handle)(*origMessH)->hTranslators);
|
||
(*newMessH)->hTranslators = (void*)text;
|
||
}
|
||
else if (!GetRHeaderAnywhere(origMessH,HEADER_STRN+TRANSLATOR_HEAD,&text))
|
||
{
|
||
AddTranslatorsFromPtr(newMessH,LDRef(text),GetHandleSize(text));
|
||
ZapHandle(text);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
UPtr spot, oldSpot, beginning, end;
|
||
long size, total;
|
||
Boolean toFound;
|
||
Str63 received;
|
||
long offset;
|
||
|
||
if (SumOf(origMessH)->flags & FLAG_OUT)
|
||
{
|
||
SumOf(newMessH)->flags = SumOf(origMessH)->flags;
|
||
SumOf(newMessH)->sigId = SumOf(origMessH)->sigId;
|
||
}
|
||
|
||
PeteGetTextAndSelection((*origMessH)->bodyPTE,&text,nil,nil);
|
||
beginning = spot = LDRef(text);
|
||
total = size = GetHandleSize_(text);
|
||
end = spot+size;
|
||
|
||
if (forXfer || SumOf(origMessH)->flags & FLAG_OUT) oldSpot = nil;
|
||
else
|
||
{
|
||
/*
|
||
* find the last "Received:" header
|
||
*/
|
||
GetRString(received,RECEIVED_HEAD); TrimWhite(received);
|
||
oldSpot = nil;
|
||
while (spot=FindHeaderString(spot,received,&size,True))
|
||
{
|
||
oldSpot=spot;
|
||
spot += size;
|
||
size = end - spot;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* copy the relevant parts
|
||
*/
|
||
if (!oldSpot) oldSpot=beginning;
|
||
{
|
||
if (oldSpot!=beginning)
|
||
SumOf(newMessH)->sigId = SIG_NONE;
|
||
size = total - (oldSpot-beginning);
|
||
TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD,0);
|
||
TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(CC_HEAD),CC_HEAD,0);
|
||
TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(ATTACH_HEAD),ATTACH_HEAD,0);
|
||
toFound = TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(TO_HEAD),TO_HEAD,0);
|
||
TextFindAndCopyHeader(oldSpot,size,newMessH,HeaderName(BCC_HEAD),BCC_HEAD,0);
|
||
/*
|
||
* find the body
|
||
*/
|
||
for (spot=oldSpot;spot<oldSpot+size-1;spot++)
|
||
if (spot[0]=='\015' && spot[1]=='\015') break;
|
||
spot += 2;
|
||
offset = spot-beginning;
|
||
UL(text);
|
||
if (spot<oldSpot+size-1)
|
||
{
|
||
newBo = PETEGetTextLen(PETE,(*newMessH)->bodyPTE);
|
||
err = PeteCopyNoLabel((*origMessH)->bodyPTE,(*newMessH)->bodyPTE,
|
||
offset,total,newBo,nil,False,html?0:0xffffffff);
|
||
//err = SetMessText(newMessH,0,spot,total-(spot-beginning));
|
||
if (!err) PeteLock((*newMessH)->bodyPTE,newBo,0x7fffffff,0);
|
||
}
|
||
if (MessFlagIsSet(origMessH,FLAG_HAS_ATT))
|
||
{
|
||
CopyAttachments(newMessH);
|
||
SpoolAttachments(newMessH);
|
||
}
|
||
}
|
||
UL(text);
|
||
}
|
||
newWinWP = GetMyWindowWindowPtr(newWin);
|
||
if (err && newWin)
|
||
{
|
||
NoSaves = True; CloseMyWindow(newWinWP); NoSaves = False;
|
||
WarnUser(PETE_ERR,err);
|
||
PopPers();
|
||
return(nil);
|
||
}
|
||
if (!forXfer || forIMAP)
|
||
{
|
||
WeedXAttachments(newMessH, !forIMAP);
|
||
if (UseInlineSig && !MessOptIsSet(newMessH,OPT_INLINE_SIG)) AddInlineSig(newMessH);
|
||
UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
|
||
if(!forIMAP)
|
||
{
|
||
ShowMyWindow(newWinWP);
|
||
newWin->isDirty = False;
|
||
PeteCleanList(newWin->pte);
|
||
}
|
||
}
|
||
}
|
||
PopPers();
|
||
return(newWin);
|
||
}
|
||
|
||
/************************************************************************
|
||
* UniqueHeader - make sure the addresses in a header are unique
|
||
************************************************************************/
|
||
OSErr UniqueHeader(MessHandle messH,short head,Boolean wantErrors)
|
||
{
|
||
Handle addresses = nil;
|
||
short oldSize;
|
||
HeadSpec hs;
|
||
OSErr err = noErr;
|
||
|
||
if (CompHeadFind(messH,head,&hs) && !CompHeadGetText(TheBody,&hs,&addresses))
|
||
{
|
||
oldSize = GetHandleSize_(addresses);
|
||
err = NickUniq(addresses,"\p, ",wantErrors);
|
||
if (oldSize != GetHandleSize_(addresses))
|
||
CompHeadSet(TheBody,&hs,addresses);
|
||
ZapHandle(addresses);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
/************************************************************************
|
||
* RemoveSelf - Remove "me" from a list of addresses
|
||
* Ray Davison, SFU
|
||
************************************************************************/
|
||
OSErr RemoveSelf(MessHandle messH,short head,Boolean wantErrors)
|
||
{
|
||
Str63 dummy;
|
||
Str255 temp;
|
||
EAL_VARS_DECL;
|
||
UHandle rawMyself=nil, cookedMyself=nil;
|
||
UHandle rawAddress=nil, spewHandle=nil;
|
||
UHandle myself=NuHTempBetter(0);
|
||
long offset, meOffset;
|
||
Boolean removed = False;
|
||
Handle text=nil;
|
||
Handle oldText=nil;
|
||
HeadSpec hs;
|
||
Boolean group;
|
||
Boolean groupWas = False;
|
||
OSErr err = noErr;
|
||
|
||
/* Get a definition of who I am */
|
||
|
||
GetRString(temp, ME);
|
||
PtrPlusHand_(temp+1, myself, temp[0]);
|
||
PtrPlusHand_(",", myself, 1);
|
||
GetPOPInfo(temp, dummy);
|
||
PtrPlusHand_(temp+1, myself, temp[0]);
|
||
PtrPlusHand_(",", myself, 1);
|
||
GetReturnAddr(temp ,True);
|
||
PtrPlusHand_(temp+1, myself, temp[0]);
|
||
if (!(err=SuckAddresses(&rawMyself,myself, False, wantErrors, False,nil)) &&
|
||
!(err=ExpandAliasesLow(&cookedMyself,rawMyself, 0, False, "",EAL_VARS))) // no autoqual
|
||
{
|
||
ZapHandle(rawMyself);
|
||
ZapHandle(myself);
|
||
|
||
/* expand the text */
|
||
|
||
if (CompHeadFind(messH,head,&hs) && !CompHeadGetText(TheBody,&hs,&oldText))
|
||
{
|
||
if (!(err=SuckAddresses(&rawAddress,oldText, True, wantErrors, False,nil)) && (text=NuHTempBetter(0L)))
|
||
{
|
||
/* Remove myself from address */
|
||
if (rawAddress && cookedMyself)
|
||
{
|
||
LDRef(cookedMyself);
|
||
for (offset=0; (*rawAddress)[offset]; offset += (*rawAddress)[offset]+2)
|
||
{
|
||
/* clean up the address */
|
||
LDRef(rawAddress);
|
||
SuckPtrAddresses(&spewHandle,(*rawAddress)+offset+1,
|
||
(*rawAddress)[offset], False, wantErrors, true,nil);
|
||
UL(rawAddress);
|
||
if (spewHandle)
|
||
{
|
||
LDRef(spewHandle);
|
||
group = (*spewHandle)[**spewHandle]==':';
|
||
/* look for this in the "me" addresses */
|
||
for (meOffset=0; (*cookedMyself)[meOffset]; meOffset += (*cookedMyself)[meOffset]+2)
|
||
{
|
||
if (StringSame(*spewHandle,*cookedMyself+meOffset))
|
||
break;
|
||
}
|
||
ZapHandle(spewHandle);
|
||
}
|
||
|
||
/* if we didn't find it, then add this address to the result */
|
||
if (!(*cookedMyself)[meOffset])
|
||
{
|
||
|
||
LDRef(rawAddress);
|
||
if (!groupWas && GetHandleSize_(text)) PtrPlusHand_(", ", text, 2);
|
||
PtrPlusHand_((*rawAddress)+offset+1,text,(*rawAddress)[offset]);
|
||
HUnlock(rawAddress);
|
||
}
|
||
else removed = True;
|
||
groupWas = group;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
ZapHandle(oldText);
|
||
ZapHandle(rawMyself);
|
||
ZapHandle(cookedMyself);
|
||
ZapHandle(rawAddress);
|
||
ZapHandle(spewHandle);
|
||
ZapHandle(myself);
|
||
|
||
if (removed) {
|
||
CompHeadSet(TheBody,&hs,text);
|
||
CompGatherRecipientAddresses (messH, true);
|
||
}
|
||
|
||
ZapHandle(text);
|
||
return err;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MessText - get the text of a message
|
||
**********************************************************************/
|
||
UHandle MessText(MessHandle messH)
|
||
{
|
||
UHandle text=nil;
|
||
PeteGetRawText(TheBody,&text);
|
||
return(text);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MessVisibleText - get only the visible text of a message
|
||
**********************************************************************/
|
||
UHandle MessVisibleText(MessHandle messH)
|
||
{
|
||
long runLen, offset;
|
||
UHandle text=nil;
|
||
PETEStyleEntry pse;
|
||
long len;
|
||
OSErr err = noErr;
|
||
Accumulator a;
|
||
|
||
err = PeteGetRawText(TheBody,&text);
|
||
if (err) return nil;
|
||
|
||
len = GetHandleSize(text);
|
||
|
||
Zero(pse);
|
||
Zero(a);
|
||
|
||
for (offset=0;offset<len;offset += runLen)
|
||
{
|
||
err = PeteGetStyle(TheBody, offset, &runLen, &pse);
|
||
if (err) break;
|
||
|
||
if (runLen==0)
|
||
{
|
||
offset++; // keep moving!
|
||
continue;
|
||
}
|
||
|
||
// add all text except invisible text (graphic with no handle)
|
||
if (!(pse.psGraphic && !pse.psStyle.graphicStyle.graphicInfo))
|
||
{
|
||
err = AccuAddFromHandle(&a,text,offset,runLen);
|
||
if (err) break;
|
||
}
|
||
}
|
||
|
||
if (err) AccuZap(a);
|
||
else AccuTrim(&a);
|
||
|
||
return a.data;
|
||
}
|
||
|
||
/************************************************************************
|
||
* ReopenMessage - reopen the current message
|
||
************************************************************************/
|
||
MyWindowPtr ReopenMessage(MyWindowPtr win)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
UHandle text;
|
||
MessHandle messH = Win2MessH(win);
|
||
OSErr err = noErr;
|
||
|
||
text = GetMessText(Win2MessH(win));
|
||
|
||
if (text)
|
||
{
|
||
// stick in the text
|
||
{
|
||
PeteSetTextPtr(TheBody,nil,0);
|
||
PeteKillUndo(TheBody);
|
||
PeteCalcOff(TheBody);
|
||
|
||
(*PeteExtra(TheBody))->emoDesired = !MessFlagIsSet(messH,FLAG_SHOW_ALL);
|
||
|
||
PetePlain(TheBody,kPETECurrentStyle,kPETECurrentStyle,peAllValid);
|
||
PetePlainPara(TheBody,0);
|
||
|
||
if (!MessFlagIsSet(messH,FLAG_SHOW_ALL) &&
|
||
(MessFlagIsSet(messH,FLAG_RICH)
|
||
#ifndef OLDHTML
|
||
|| MessOptIsSet(messH,OPT_HTML)
|
||
#endif
|
||
|| MessOptIsSet(messH,OPT_FLOW)
|
||
|| MessOptIsSet(messH,OPT_CHARSET)
|
||
))
|
||
{
|
||
err = InsertRichLo(text,0,-1,-1,True,True,TheBody,(*PeteExtra(win->pte))->partStack,messH,MessOptIsSet(messH,OPT_DELSP));
|
||
ZapHandle(text);
|
||
}
|
||
else
|
||
err = PETEInsertPara(PETE,TheBody,kPETELastPara,nil,text,nil);
|
||
|
||
if (!err)
|
||
{
|
||
PeteSmallParas(TheBody);
|
||
text = nil;
|
||
// align headers if need be
|
||
//if (!PrefIsSet(PREF_DONT_ALIGN_HEADERS)) AlignHeaders(messH);
|
||
|
||
#ifdef OLDHTML
|
||
// interpret rich text
|
||
if (!MessFlagIsSet(messH,FLAG_SHOW_ALL))
|
||
{
|
||
if (MessOptIsSet(messH,OPT_HTML))
|
||
err = PeteHTML(TheBody,SumOf(messH)->bodyOffset-(*messH)->weeded,0,True);
|
||
}
|
||
#endif
|
||
|
||
if (!err) HiliteOddReply(messH);
|
||
|
||
if (!err) PeteTrimTrailingReturns(TheBody,true);
|
||
|
||
if (!err)
|
||
{
|
||
// recalculate
|
||
PeteCalcOn(TheBody);
|
||
|
||
// add notification control if necessary
|
||
CheckAddNotifyControls(win, messH);
|
||
|
||
if (PrefIsSet(PREF_ZOOM_OPEN)) ReZoomMyWindow(winWP);
|
||
|
||
// scroll to correct position
|
||
if (!MessFlagIsSet(messH,FLAG_SHOW_ALL)) ShowMessageSeparator(TheBody,true);
|
||
InvalContent(win);
|
||
}
|
||
}
|
||
}
|
||
|
||
// mark document as clean
|
||
PETEMarkDocDirty(PETE,TheBody,False);
|
||
win->isDirty = False;
|
||
PeteSetURLRescan(TheBody,0);
|
||
|
||
// kill text if still here
|
||
ZapHandle(text);
|
||
|
||
if (err)
|
||
{
|
||
WarnUser(DOC_DAMAGED_ERR,err);
|
||
CloseMyWindow(winWP);
|
||
win=nil;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
CloseMyWindow(winWP);
|
||
win = nil;
|
||
}
|
||
if (win) PeteCalcOn(TheBody);
|
||
return(win);
|
||
}
|
||
|
||
/************************************************************************
|
||
* FindFrom - find a (nicely formatted) From address
|
||
************************************************************************/
|
||
void FindFrom(UPtr who, PETEHandle pte)
|
||
{
|
||
UPtr found;
|
||
Str31 header;
|
||
long len;
|
||
Handle text;
|
||
|
||
PeteGetRawText(pte,&text);
|
||
len = GetHandleSize(text);
|
||
GetRString(header,FROM_HEAD+HEADER_STRN);
|
||
if (found = FindHeaderString(LDRef(text),header,&len,False))
|
||
{
|
||
*who = MIN(62,len);
|
||
BMD(found,who+1,*who);
|
||
who[*who+1] = 0;
|
||
BeautifyFrom(who);
|
||
}
|
||
else *who = 0;
|
||
UL(text);
|
||
}
|
||
|
||
/************************************************************************
|
||
* QuoteLines - put a quote prefix before the specified TextEdit lines
|
||
************************************************************************/
|
||
void QuoteLines(PETEHandle pte,long from,long to,short pfid, long *qEnd)
|
||
{
|
||
long this;
|
||
Str15 prefix;
|
||
UHandle text;
|
||
long count = 0;
|
||
Boolean first = True;
|
||
PETEStyleEntry pse;
|
||
RGBColor color;
|
||
Boolean withSpace = false;
|
||
long numSpaces = 0;
|
||
Byte quoteChar;
|
||
|
||
Zero(pse);
|
||
|
||
if (qEnd) *qEnd = to;
|
||
|
||
GetRString(prefix,pfid);
|
||
if (!*prefix) return;
|
||
|
||
// if trailing space, remove and note
|
||
if (*prefix==2 && prefix[2]==' ')
|
||
{
|
||
withSpace = true;
|
||
--*prefix;
|
||
quoteChar = prefix[1];
|
||
}
|
||
|
||
PETEGetRawText(PETE,pte,&text);
|
||
this = GetHandleSize(text);
|
||
to = MIN(to,this);
|
||
|
||
for (this=to-2;this>=from;this--)
|
||
if (!this || (*text)[this]=='\015')
|
||
{
|
||
if (withSpace && (*text)[this+1]!=quoteChar)
|
||
{
|
||
PeteInsertChar(pte,this?this+1:this,' ',nil);
|
||
numSpaces++;
|
||
}
|
||
PeteInsertPtr(pte,this ? this+1:this,prefix+1,*prefix);
|
||
count++;
|
||
}
|
||
if (qEnd) *qEnd = to + count * *prefix + numSpaces; // adjust for inserted prefixes
|
||
|
||
// do the scanner's work for it
|
||
if (!Black(GetRColor(&color,QUOTE_COLOR)))
|
||
{
|
||
pse.psStyle.textStyle.tsLabel = pQuoteLabel;
|
||
PETESetTextStyle(PETE,pte,from,to + count * *prefix,&pse.psStyle.textStyle,peLabelValid);
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* PrependMessText - stick some text before one of the fields of a message.
|
||
************************************************************************/
|
||
OSErr PrependMessText(MessHandle messH,short whichTXE,UPtr string,long size)
|
||
{
|
||
HeadSpec hs;
|
||
|
||
if (CompHeadFind(messH,whichTXE,&hs))
|
||
return(CompHeadPrependPtr(TheBody,&hs,string,size));
|
||
else return(fnfErr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* FindHeaderString - pick the given header out of a message, by name
|
||
* Pointer returned is to
|
||
************************************************************************/
|
||
UPtr FindHeaderString(UPtr text,UPtr headerName,long *size,Boolean bodyToo)
|
||
{
|
||
UPtr spot,end, colon;
|
||
char header[MAX_HEADER];
|
||
short mLen = MIN(MAX_HEADER-2,*headerName);
|
||
#ifdef TWO
|
||
Boolean any = EqualStrRes(headerName,FILTER_ANY);
|
||
Boolean addressee = EqualStrRes(headerName,FILTER_ADDRESSEE);
|
||
static UHandle addrRes;
|
||
#endif
|
||
|
||
if (addressee) mLen = MAX_HEADER-2;
|
||
|
||
#ifdef TWO
|
||
if (!addrRes || !*addrRes)
|
||
{
|
||
if (addrRes) LoadResource(addrRes);
|
||
else addrRes = GetResource_('STR#',AddrHeadsStrn);
|
||
if (!addrRes || !*addrRes) return(nil);
|
||
HNoPurge(addrRes);
|
||
}
|
||
#endif
|
||
|
||
for (end = text+*size; text<end; text = spot+1)
|
||
{
|
||
for (spot=text;spot<end;spot++) if (*spot == '\015' && (spot==end-1||!IsWhite(spot[1]))) break;
|
||
if (spot==text && !bodyToo) break;
|
||
*header = MIN(mLen,end-text);
|
||
BMD(text,header+1,*header);
|
||
#ifdef TWO
|
||
if (addressee)
|
||
{
|
||
if (colon=PIndex(header,':'))
|
||
*header = colon-header;
|
||
}
|
||
#endif
|
||
if (
|
||
#ifdef TWO
|
||
any ||
|
||
addressee && FindSTRNIndexRes(addrRes,header) ||
|
||
#endif
|
||
EqualString(header,headerName,False,True))
|
||
{
|
||
#ifdef TWO
|
||
if (!any && !addressee)
|
||
#endif
|
||
text += *header;
|
||
while (IsWhite(*text)) text++;
|
||
for (spot--; IsWhite(*spot); spot--);
|
||
*size = MAX(0,spot-text+1);
|
||
return(text);
|
||
}
|
||
}
|
||
return(nil);
|
||
}
|
||
/************************************************************************
|
||
* DoReplyMessage - craft a reply to a message
|
||
************************************************************************/
|
||
MyWindowPtr DoReplyMessage(MyWindowPtr win, Boolean all, Boolean self, Boolean quote, Boolean doFcc, short withWhich,Boolean vis,Boolean station,Boolean caching)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
MessHandle origMessH = (MessHandle)GetMyWindowPrivateData(win);
|
||
MessHandle newMessH;
|
||
Str255 subj,scratch,replyTo;
|
||
long bodyOffset = -1;
|
||
MyWindowPtr newWin=nil;
|
||
WindowPtr newWinWP;
|
||
short r;
|
||
long len;
|
||
Handle text;
|
||
long origLen;
|
||
long selStart, selEnd;
|
||
Str255 soundName;
|
||
Boolean doSound;
|
||
HeadSpec hs;
|
||
OSErr err = noErr;
|
||
PETETextStyle style;
|
||
long newBo;
|
||
//PeteSaneMargin marg;
|
||
Boolean rich;
|
||
Boolean html = !PrefIsSet(PREF_SEND_ENRICHED_NEW);
|
||
PETEHandle copyFromPTE;
|
||
TOCHandle origTOCH;
|
||
Boolean listReply = MessOptIsSet(origMessH,OPT_BULK) && PrefIsSet(PREF_LIST_REPLYTO);
|
||
Boolean cacheReplyTo = !PrefIsSet (PREF_NICK_CACHE) && !PrefIsSet (PREF_NICK_CACHE_NOT_ADD_REPLY_TO);
|
||
Boolean wantErrors = true;
|
||
|
||
// Dunno why this was done
|
||
// if (GetWindowKind(winWP)!=COMP_WIN && !SaveMessHi(win,False)) return(nil);
|
||
|
||
#ifdef IMAP
|
||
// Make sure message has been downloaded if IMAP. Don't care about attachments.
|
||
if (!EnsureMsgDownloaded((*origMessH)->tocH,(*origMessH)->sumNum,false))
|
||
return nil;
|
||
#endif
|
||
|
||
PushPers(PERS_FORCE(MESS_TO_PERS(origMessH)));
|
||
if (newWin=DoComposeNew(0))
|
||
{
|
||
newWinWP = GetMyWindowWindowPtr(newWin);
|
||
newMessH = (MessHandle) GetMyWindowPrivateData(newWin);
|
||
replyTo[0] = 0;
|
||
rich = UseFlowOutExcerpt || (MessIsRich(origMessH) || MessIsRich(newMessH));
|
||
|
||
XferCustomTable(origMessH,newMessH);
|
||
|
||
PeteGetTextAndSelection((*origMessH)->bodyPTE,&text,&selStart,&selEnd);
|
||
origLen = GetHandleSize(text);
|
||
|
||
/* handle the subject */
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD);
|
||
GetRString(scratch,REPLY_INTRO);
|
||
TrimWhite(scratch);
|
||
CompHeadGetStr(newMessH,SUBJ_HEAD,subj);
|
||
if (!ReMatch(subj,scratch))
|
||
{
|
||
GetRString(scratch,REPLY_INTRO);
|
||
PrependMessText(newMessH,SUBJ_HEAD,scratch+1,*scratch);
|
||
}
|
||
|
||
/* reply to sender */
|
||
replyTo[0] = 0;
|
||
if (listReply && !all) {
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(FROM_HEAD),TO_HEAD);
|
||
if (HasFeature (featureNicknameWatching) && cacheReplyTo)
|
||
SuckHeaderText (origMessH, replyTo, sizeof (replyTo), FROM_HEAD);
|
||
}
|
||
else
|
||
for (r=1;*GetRString(scratch,ReplyStrn+r);r++)
|
||
{
|
||
TrimWhite(scratch);
|
||
if (FindAndCopyHeader(origMessH,newMessH,scratch,TO_HEAD))
|
||
{
|
||
doSound = MessOptIsSet(origMessH,OPT_WEIRD_REPLY);
|
||
if (!doSound) listReply = false; // as you were
|
||
// (jp) added to grab the reply to address
|
||
if (!PrefIsSet (PREF_NO_SPOKEN_WARNINGS) || cacheReplyTo)
|
||
SuckHeaderText (newMessH, replyTo, sizeof (replyTo), TO_HEAD);
|
||
break;
|
||
}
|
||
}
|
||
if (caching && HasFeature (featureNicknameWatching) && cacheReplyTo && replyTo[0])
|
||
CacheRecentNickname (replyTo);
|
||
|
||
/* bring over the other recipients and cc's, if desired */
|
||
if (all)
|
||
{
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(TO_HEAD),PrefIsSet(PREF_CC_REPLY)?CC_HEAD:TO_HEAD);
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(CC_HEAD),CC_HEAD);
|
||
|
||
// If we've found a reply-to header and we have a mailing list, also copy From to the cc: field
|
||
if (doSound && listReply)
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(FROM_HEAD),CC_HEAD);
|
||
|
||
/* remove self, if desired */
|
||
if (!self)
|
||
{
|
||
if (RemoveSelf(newMessH,TO_HEAD,wantErrors)) wantErrors=false;
|
||
if (RemoveSelf(newMessH,CC_HEAD,wantErrors)) wantErrors=false;
|
||
}
|
||
|
||
if (UniqueHeader(newMessH,TO_HEAD,wantErrors)) wantErrors=false;
|
||
UniqueHeader(newMessH,CC_HEAD,wantErrors);
|
||
}
|
||
|
||
/*
|
||
* for Larry's benefit
|
||
*/
|
||
if (PrefIsSet(PREF_NEWSGROUP_HANDLING)) CopyNewsgroups(origMessH,newMessH);
|
||
|
||
/*
|
||
* auto-fcc
|
||
*/
|
||
//Folder Carbon Copy - don't allow the auto FCC in Light
|
||
#ifdef IMAP
|
||
if (HasFeature (featureFcc) && doFcc && PrefIsSet(PREF_AUTO_FCC) && !(*(*origMessH)->tocH)->which && !IMAPDontAutoFccMailbox((*origMessH)->tocH))
|
||
#else
|
||
if (HasFeature (featureFcc) && doFcc && PrefIsSet(PREF_AUTO_FCC) && !(*(*origMessH)->tocH)->which)
|
||
#endif
|
||
{
|
||
FSSpec spec = GetMailboxSpec((*origMessH)->tocH,-1);
|
||
Fcc(newMessH,&spec);
|
||
}
|
||
|
||
/*
|
||
* in-reply-to and references
|
||
*/
|
||
ReplyReferences(origMessH,newMessH);
|
||
|
||
/*
|
||
* copy the body. Use the preview pane if that's what we have
|
||
*/
|
||
copyFromPTE = (*origMessH)->bodyPTE;
|
||
origTOCH = (*origMessH)->openedFromTocH ? (*origMessH)->openedFromTocH : (*origMessH)->tocH;
|
||
if ((*origTOCH)->win==GetWindowMyWindowPtr(FrontWindow_()) && (*origTOCH)->previewID &&
|
||
(*origTOCH)->previewID==(*origMessH)->openedFromSerialNum &&
|
||
!quote && *PeteSelectedString(scratch,(*origTOCH)->previewPTE))
|
||
{
|
||
copyFromPTE = (*origTOCH)->previewPTE;
|
||
PeteGetTextAndSelection(copyFromPTE,&text,&selStart,&selEnd);
|
||
}
|
||
|
||
if (quote)
|
||
{
|
||
bodyOffset = 0;
|
||
while (bodyOffset<origLen-1)
|
||
{
|
||
if ((*text)[bodyOffset+1]!='\015') bodyOffset += 2;
|
||
else if ((*text)[bodyOffset]=='\015') break;
|
||
else bodyOffset++;
|
||
}
|
||
|
||
while (bodyOffset<origLen && (*text)[bodyOffset] == '\015') bodyOffset++;
|
||
len = origLen-bodyOffset;
|
||
}
|
||
else
|
||
{
|
||
if (selEnd != selStart)
|
||
{
|
||
bodyOffset = selStart;
|
||
len = selEnd - bodyOffset;
|
||
}
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if (BUG14) {InvalContent(win);UpdateMyWindow(winWP);}
|
||
#endif
|
||
|
||
if (len > 0 && bodyOffset >= 0)
|
||
{
|
||
while (len && (*text)[bodyOffset+len-1]=='\015') len--;
|
||
newBo = PETEGetTextLen(PETE,(*newMessH)->bodyPTE);
|
||
if (len>0) err = PeteCopyNoLabel(copyFromPTE,(*newMessH)->bodyPTE,bodyOffset,bodyOffset+len,newBo,nil,!rich,html?0:0xffffffff);
|
||
HUnlock(text);
|
||
style.tsLock = 0;
|
||
#ifdef DEBUG
|
||
if (BUG14) {InvalContent(win);UpdateMyWindow(winWP);}
|
||
#endif
|
||
PETESetTextStyle(PETE,(*newMessH)->bodyPTE,newBo,0x7fffffff,&style,peLockValid);
|
||
if (rich) PeteExcerpt((*newMessH)->bodyPTE,newBo,PETEGetTextLen(PETE,(*newMessH)->bodyPTE)-1);
|
||
|
||
#ifdef DEBUG
|
||
if (BUG14) {InvalContent(win);UpdateMyWindow(winWP);}
|
||
#endif
|
||
if (err)
|
||
{
|
||
NoSaves = True; CloseMyWindow(newWinWP); NoSaves = False;
|
||
PopPers();
|
||
WarnUser(PETE_ERR,err);
|
||
return(nil);
|
||
}
|
||
|
||
/*
|
||
* make sure the last line is blank
|
||
*/
|
||
EnsureMessNewline(newMessH);
|
||
|
||
#ifdef DEBUG
|
||
if (BUG14) {InvalContent(win);UpdateMyWindow(winWP);}
|
||
#endif
|
||
if (rich) PetePlainPara((*newMessH)->bodyPTE,PeteParaAt((*newMessH)->bodyPTE,PETEGetTextLen(PETE,(*newMessH)->bodyPTE)));
|
||
#ifdef DEBUG
|
||
if (BUG14) {InvalContent(win);UpdateMyWindow(winWP);}
|
||
#endif
|
||
|
||
/*
|
||
* quote them
|
||
*/
|
||
if (!rich)
|
||
{
|
||
CompHeadFind(newMessH,0,&hs);
|
||
QuoteLines((*newMessH)->bodyPTE,hs.start,hs.stop-1,QUOTE_PREFIX,nil);
|
||
}
|
||
else if (PrefIsSet(PREF_SCHEERDER))
|
||
{
|
||
CompHeadFind(newMessH,0,&hs);
|
||
PeteSelect(nil,(*newMessH)->bodyPTE,hs.value,PeteLen((*newMessH)->bodyPTE));
|
||
PeteWrap((*newMessH)->win,(*newMessH)->bodyPTE,false);
|
||
}
|
||
#ifdef DEBUG
|
||
if (BUG14) {InvalContent(win);UpdateMyWindow(winWP);}
|
||
#endif
|
||
|
||
/*
|
||
* annotate
|
||
*/
|
||
if (rich) Attribute(QUOTH,origMessH,newMessH,False);
|
||
Attribute(all ? ATTRIBUTION:REP_SEND_ATTR,origMessH,newMessH,False);
|
||
if (rich) Attribute(UNQUOTH,origMessH,newMessH,True);
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if (BUG14) {InvalContent(win);UpdateMyWindow(winWP);}
|
||
#endif
|
||
/*
|
||
* copy priority (not my idea)
|
||
*/
|
||
if (!PrefIsSet(PREF_NO_XF_PRIOR))
|
||
SumOf(newMessH)->origPriority = SumOf(newMessH)->priority =
|
||
SumOf(origMessH)->origPriority;
|
||
|
||
SumOf(newMessH)->outType = OUT_REPLY; // for statistics
|
||
|
||
/*
|
||
* encryption
|
||
*/
|
||
if (MessOptIsSet(origMessH,OPT_WIPE)) SetMessFlag(newMessH,FLAG_ENCRYPT);
|
||
|
||
if (station)
|
||
{
|
||
if (!withWhich) ApplyDefaultStationery(newWin,True,True);
|
||
//Stationery - no support for this in Light
|
||
else if (HasFeature (featureStationery)) ApplyIndexStationery(newWin,withWhich,True,True);
|
||
}
|
||
|
||
(*PeteExtra((*newMessH)->bodyPTE))->quoteScanned = -1; // don't need to run the scanner, quotelines did it already
|
||
|
||
if (vis)
|
||
{
|
||
// shall we select the quote?
|
||
quote = quote || selEnd-selStart>=GetRLong(OPEN_AT_END_THRESH);
|
||
(*newMessH)->openToEnd = !quote;
|
||
|
||
AccuAddLong(&(*newMessH)->aSourceMID,SumOf(origMessH)->uidHash);
|
||
AccuAddLong(&(*newMessH)->aSourceMID,SumOf(origMessH)->state);
|
||
AccuAddLong(&(*newMessH)->aSourceMID,REPLIED);
|
||
SetState((*origMessH)->tocH,(*origMessH)->sumNum,REPLIED);
|
||
UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
|
||
ShowMyWindow(newWinWP);
|
||
UpdateMyWindow(newWinWP);
|
||
if (doSound)
|
||
if (PrefIsSet (PREF_NO_SPOKEN_WARNINGS))
|
||
PlayNamedSound(GetRString(soundName,REPLY_SOUND));
|
||
else
|
||
(void) ComposeStdAlert (kAlertNoteAlert, PASSIVE_REPLY_TO_ASTR, replyTo);
|
||
}
|
||
newWin->isDirty = False;
|
||
PeteCleanList(newWin->pte);
|
||
}
|
||
PopPers();
|
||
return(newWin);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* ReplyReferences
|
||
**********************************************************************/
|
||
OSErr ReplyReferences(MessHandle origMessH,MessHandle newMessH)
|
||
{
|
||
OSErr err = noErr;
|
||
UHandle /*t1=nil, */t2=nil, t3=nil;
|
||
Accumulator extras = (*newMessH)->extras;
|
||
long origLen = extras.offset;
|
||
|
||
/* Get the bodies of all three headers */
|
||
// GetRHeaderAnywhere(origMessH,HeaderStrn+IN_REPLY_TO_HEAD,&t1);
|
||
GetRHeaderAnywhere(origMessH,HeaderStrn+REFERENCES_HEAD,&t2);
|
||
GetRHeaderAnywhere(origMessH,HeaderStrn+MSGID_HEAD,&t3);
|
||
|
||
/* Strip returns - I don't know why we do this for IRT and MID, but whatever */
|
||
/* Should probably strip anything that's not an MID (like phrases) */
|
||
// if (t1)
|
||
// {
|
||
// SetHandleSize(t1,RemoveChar('\015',LDRef(t1),GetHandleSize(t1)));
|
||
// UL(t1);
|
||
// }
|
||
if (t2)
|
||
{
|
||
SetHandleSize(t2,RemoveChar('\015',LDRef(t2),GetHandleSize(t2)));
|
||
UL(t2);
|
||
}
|
||
if (t3)
|
||
{
|
||
SetHandleSize(t3,RemoveChar('\015',LDRef(t3),GetHandleSize(t3)));
|
||
UL(t3);
|
||
}
|
||
|
||
/* If there's a message ID, add IRT header */
|
||
if(t3)
|
||
{
|
||
/* Add the header and the colon */
|
||
if(!(err = AccuAddRes(&extras, HeaderStrn+IN_REPLY_TO_HEAD)))
|
||
/* Add a space */
|
||
if(!(err = AccuAddChar(&extras, ' ')))
|
||
/* Add the message ID */
|
||
if(!(err = AccuAddHandle(&extras, t3)))
|
||
/* Add a return */
|
||
err = AccuAddChar(&extras, '\015');
|
||
}
|
||
|
||
/* If there's any id's (from Refs, IRT, or MID), add Refs header */
|
||
if (!err && (/*t1 || */t2 || t3))
|
||
{
|
||
/* Add the header and the colon */
|
||
if(!(err = AccuAddRes(&extras, HeaderStrn+REFERENCES_HEAD)))
|
||
/* Add a space */
|
||
if(!(err = AccuAddChar(&extras, ' ')))
|
||
/* Add the Refs message IDs if they exist */
|
||
if (!t2 || !(err = AccuAddHandle(&extras,t2)))
|
||
{
|
||
// /* Add the IRT message ID if it exists */
|
||
// if (t1 /* && t1 does not appear in t2 */)
|
||
// /* Add a space if there were Refs message IDs */
|
||
// if (!t2 || !(err = AccuAddChar(&extras,' ')))
|
||
// err = AccuAddHandle(&extras,t1);
|
||
|
||
/* Add the MID if it exists */
|
||
if (!err && t3)
|
||
/* Add a space if there were Refs or IRT message IDs */
|
||
if ((/*!t1 && */!t2) || !(err = AccuAddChar(&extras,' ')))
|
||
err = AccuAddHandle(&extras,t3);
|
||
|
||
/* Add a return */
|
||
if (!err) err = AccuAddChar(&extras,'\015');
|
||
}
|
||
}
|
||
|
||
|
||
if (err) extras.offset = origLen;
|
||
AccuTrim(&extras);
|
||
(*newMessH)->extras = extras;
|
||
// ZapHandle(t1);
|
||
ZapHandle(t2);
|
||
ZapHandle(t3);
|
||
return(err);
|
||
}
|
||
/************************************************************************
|
||
* EnsureMessNewline - make sure a message ends in a newline
|
||
************************************************************************/
|
||
OSErr EnsureMessNewline(MessHandle messH)
|
||
{
|
||
Handle text;
|
||
long size;
|
||
OSErr err;
|
||
|
||
if (!(err=PETEGetRawText(PETE,TheBody,&text)))
|
||
{
|
||
size = GetHandleSize(text);
|
||
if (size<1||(*text)[size-1]!='\015')
|
||
err = PETEInsertTextPtr(PETE,TheBody,0x7fffffff,"\015\015",2,nil);
|
||
else if (!err && (size<2||(*text)[size-2]!='\015'))
|
||
err = PETEInsertTextPtr(PETE,TheBody,0x7fffffff,"\015",1,nil);
|
||
if (!err)
|
||
err = PETEInsertParaPtr(PETE,TheBody,kPETELastPara,nil,nil,0,nil);
|
||
MessPlainBytes(messH,0,-1);
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* Attribute - make an attribution
|
||
************************************************************************/
|
||
void Attribute(short attrId,MessHandle origMessH,MessHandle newMessH,Boolean atEnd)
|
||
{
|
||
Str255 attribution;
|
||
|
||
if (*GrabAttribution(attrId,(*origMessH)->win,attribution))
|
||
{
|
||
if (atEnd)
|
||
{
|
||
long endOfText = PeteLen((*newMessH)->bodyPTE);
|
||
PeteInsertPtr((*newMessH)->bodyPTE,endOfText-1,attribution+1,*attribution);
|
||
//if (MessIsRich(newMessH) || MessIsRich(origMessH)) //quote color leaking, so disable check
|
||
MessPlainBytes(newMessH,0,-(short)*attribution-1);
|
||
}
|
||
else
|
||
{
|
||
PCatC(attribution,'\015');
|
||
PrependMessText(newMessH,0,attribution+1,*attribution);
|
||
//if (MessIsRich(newMessH) || MessIsRich(origMessH)) //quote color leaking, so disable check
|
||
MessPlainBytes(newMessH,0,(short)*attribution);
|
||
}
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* GrabAttribution - compute the attribution for a message
|
||
************************************************************************/
|
||
PStr GrabAttribution(short attrId,MyWindowPtr win,PStr attribution)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
Str255 template;
|
||
Str63 date,time;
|
||
Str127 who;
|
||
long secs;
|
||
long zone;
|
||
long sumNum;
|
||
MSumType sum;
|
||
|
||
*attribution = 0;
|
||
if (IsMessWindow(winWP))
|
||
sum = *SumOf(Win2MessH(win));
|
||
else if (GetWindowKind(winWP)==MBOX_WIN && (0<=(sumNum=LastMsgSelected(Win2TOC(win)))))
|
||
sum = (*Win2TOC(win))->sums[sumNum];
|
||
else return(attribution);
|
||
|
||
GetRString(template,attrId);
|
||
if (*template)
|
||
{
|
||
FindFrom(who,win->pte);
|
||
if (*who)
|
||
{
|
||
zone = /* whine, whine, whine PrefIsSet(PREF_LOCAL_DATE) ? ZoneSecs() : */ 60*sum.origZone;
|
||
secs = sum.seconds + zone;
|
||
TimeString(secs,False,attribution,nil);
|
||
FormatZone(date,zone);
|
||
ComposeRString(time,ATTR_TIME_FMT,attribution,date);
|
||
DateString(secs,shortDate,date,nil);
|
||
if (date[1]==optSpace) date[1] = ' ';
|
||
utl_PlugParams(template,attribution,who,date,sum.subj,time);
|
||
}
|
||
}
|
||
return(attribution);
|
||
}
|
||
|
||
/************************************************************************
|
||
* DoRedistributeMessage - craft a reply to a message
|
||
************************************************************************/
|
||
MyWindowPtr DoRedistributeMessage(MyWindowPtr win,TextAddrHandle toWhom,Boolean turbo,Boolean andDelete,Boolean showIt)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
MessHandle origMessH = (MessHandle)GetMyWindowPrivateData(win);
|
||
MessHandle newMessH;
|
||
Str255 scratch;
|
||
int bodyOffset;
|
||
MyWindowPtr newWin;
|
||
long newBo;
|
||
OSErr err;
|
||
PETETextStyle style;
|
||
Boolean html = !PrefIsSet(PREF_SEND_ENRICHED_NEW);
|
||
|
||
if (GetWindowKind(winWP)!=COMP_WIN && !SaveMessHi(win,False)) return(nil);
|
||
|
||
#ifdef IMAP
|
||
// Make sure message has been downloaded if IMAP
|
||
if (!EnsureMsgDownloaded((*origMessH)->tocH,(*origMessH)->sumNum,true))
|
||
return nil;
|
||
#endif
|
||
|
||
PushPers(PERS_FORCE(MESS_TO_PERS(origMessH)));
|
||
|
||
if (newWin=DoComposeNew(toWhom))
|
||
{
|
||
WindowPtr newWinWP = GetMyWindowWindowPtr (newWin);
|
||
newMessH = (MessHandle)GetMyWindowPrivateData(newWin);
|
||
XferCustomTable(origMessH,newMessH);
|
||
SetMessOpt(newMessH,OPT_REDIRECTED);
|
||
SetMessText(newMessH,FROM_HEAD,"",0);
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD);
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(FROM_HEAD),FROM_HEAD);
|
||
if (MessFlagIsSet(origMessH,FLAG_OUT)) FindAndCopyHeader(origMessH,newMessH,HeaderName(ATTACH_HEAD),ATTACH_HEAD);
|
||
RedirectAnnotation(newMessH);
|
||
|
||
bodyOffset = CompBodyOffset(origMessH);
|
||
newBo = PETEGetTextLen(PETE,(*newMessH)->bodyPTE);
|
||
|
||
#ifdef DEBUG
|
||
if (BUG14) UpdateMyWindow(winWP);
|
||
#endif
|
||
err = PeteCopyNoLabel((*origMessH)->bodyPTE,(*newMessH)->bodyPTE,
|
||
bodyOffset,PETEGetTextLen(PETE,(*origMessH)->bodyPTE),newBo,nil,False,html?0:0xffffffff);
|
||
#ifdef DEBUG
|
||
if (BUG14) UpdateMyWindow(winWP);
|
||
#endif
|
||
style.tsLock = PrefIsSet(PREF_LOCK_REDIR) ? peModLock : peNoLock;
|
||
#ifdef DEBUG
|
||
if (BUG14) UpdateMyWindow(winWP);
|
||
#endif
|
||
PETESetTextStyle(PETE,(*newMessH)->bodyPTE,newBo,0x7fffffff,&style,peLockValid);
|
||
#ifdef DEBUG
|
||
if (BUG14) UpdateMyWindow(winWP);
|
||
#endif
|
||
|
||
if (err)
|
||
{
|
||
NoSaves = True; CloseMyWindow(newWinWP); NoSaves = False;
|
||
WarnUser(PETE_ERR,err);
|
||
PopPers();
|
||
return(nil);
|
||
}
|
||
if (SumOf(origMessH)->state!=SENT && SumOf(origMessH)->state!=UNSENT)
|
||
SumOf(newMessH)->sigId = SIG_NONE;
|
||
|
||
|
||
/*
|
||
* copy priority
|
||
*/
|
||
SumOf(newMessH)->origPriority = SumOf(newMessH)->priority =
|
||
SumOf(origMessH)->origPriority;
|
||
|
||
SumOf(newMessH)->outType = OUT_REDIRECT; // for statistics
|
||
|
||
/*
|
||
* state stuff
|
||
*/
|
||
AccuAddLong(&(*newMessH)->aSourceMID,SumOf(origMessH)->uidHash);
|
||
AccuAddLong(&(*newMessH)->aSourceMID,SumOf(origMessH)->state);
|
||
AccuAddLong(&(*newMessH)->aSourceMID,REDIST);
|
||
SetState((*origMessH)->tocH,(*origMessH)->sumNum,REDIST);
|
||
UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
|
||
WeedXAttachments(newMessH,true);
|
||
if (MessFlagIsSet(origMessH,FLAG_HAS_ATT)) CopyAttachments(newMessH);
|
||
SpoolAttachments(newMessH);
|
||
#ifdef TWO
|
||
if (turbo && toWhom)
|
||
{
|
||
QueueMessage((*newMessH)->tocH,(*newMessH)->sumNum,PrefIsSet(PREF_AUTO_SEND)?kEuSendNow:kEuSendNext,0,true,false);
|
||
if (andDelete) DeleteMessage((*origMessH)->tocH,(*origMessH)->sumNum,False);
|
||
}
|
||
|
||
if (showIt && (!turbo || !toWhom))
|
||
#endif
|
||
{
|
||
ShowMyWindow(newWinWP);
|
||
newWin->isDirty = False;
|
||
PeteCleanList(newWin->pte);
|
||
}
|
||
}
|
||
PopPers();
|
||
return(newWin);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* RedirectAnnotation - add proper annotation to redirect
|
||
**********************************************************************/
|
||
OSErr RedirectAnnotation(MessHandle messH)
|
||
{
|
||
Str255 scratch, who, orig;
|
||
OSErr err = noErr;
|
||
|
||
CompHeadGetStr(messH,FROM_HEAD,orig);
|
||
if (!IsMe(orig))
|
||
{
|
||
// Trim the trailing comment
|
||
UPtr spot = orig+*orig;
|
||
if (*spot==')')
|
||
{
|
||
while (spot>orig && *spot!='(' && *spot!='<') spot--;
|
||
if (spot>orig && *spot=='(') *orig = spot-orig-1;
|
||
}
|
||
|
||
// Now massage
|
||
if (*GetRealname(who))
|
||
{
|
||
ComposeRString(scratch,REDIST_ANNOTATE,who);
|
||
TrimWhite(orig);
|
||
PCat(orig,scratch);
|
||
err = SetMessText(messH,FROM_HEAD,orig+1,*orig);
|
||
}
|
||
}
|
||
return err;
|
||
}
|
||
|
||
/**********************************************************************
|
||
* CopyAttachments - copy attachments out of the message body and into
|
||
* the attachments line
|
||
**********************************************************************/
|
||
OSErr CopyAttachments(MessHandle messH)
|
||
{
|
||
long offset, absOffset;
|
||
UHandle text;
|
||
FSSpec attSpec;
|
||
UPtr spot;
|
||
HeadSpec hs;
|
||
|
||
if (!PeteGetTextAndSelection(TheBody,&text,nil,nil))
|
||
{
|
||
CompHeadFind(messH,0,&hs);
|
||
for (offset = 0;0<=(absOffset = FindAnAttachment(text,offset+hs.start,&attSpec,False,nil,nil,nil));)
|
||
{
|
||
offset = absOffset-hs.start-1;
|
||
for (spot=*text+absOffset;*spot!='\015' && spot<*text+hs.stop;spot++);
|
||
PETEInsertTextPtr(PETE,TheBody,absOffset,nil,(spot-*text)-absOffset,nil);
|
||
CompAttachSpec((*messH)->win,&attSpec);
|
||
CompHeadFind(messH,0,&hs);
|
||
}
|
||
for (offset = 0;0<=(absOffset = FindAnAttachment(text,offset+hs.start,&attSpec,True,nil,nil,nil));)
|
||
{
|
||
offset = absOffset-hs.start-1;
|
||
for (spot=*text+absOffset;spot<*text+hs.stop && *spot!='\015';spot++);
|
||
PETEInsertTextPtr(PETE,TheBody,absOffset,nil,(spot-*text)-absOffset,nil);
|
||
CompAttachSpec((*messH)->win,&attSpec);
|
||
CompHeadFind(messH,0,&hs);
|
||
}
|
||
}
|
||
return noErr; /* I have no idea what kinds of errors can happen here - mtc 11-18-03 */
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* DoForwardMessage - forward a message to someone
|
||
************************************************************************/
|
||
MyWindowPtr DoForwardMessage(MyWindowPtr win,TextAddrHandle toWhom,Boolean showIt)
|
||
{
|
||
WindowPtr winWP = GetMyWindowWindowPtr (win);
|
||
MessHandle origMessH = (MessHandle)GetMyWindowPrivateData(win);
|
||
MessHandle newMessH;
|
||
MyWindowPtr newWin=nil;
|
||
WindowPtr newWinPtr;
|
||
Str255 scratch, subj;
|
||
long offset;
|
||
PETETextStyle style;
|
||
Boolean rich;
|
||
long origBo;
|
||
HeadSpec hs;
|
||
Boolean html = !PrefIsSet(PREF_SEND_ENRICHED_NEW);
|
||
|
||
NicknameWatcherFocusChange (win->pte); /* MJN */
|
||
|
||
if (GetWindowKind(winWP)!=COMP_WIN && !SaveMessHi(win,False)) return(nil);
|
||
|
||
#ifdef IMAP
|
||
// Make sure message has been downloaded if IMAP
|
||
if (!EnsureMsgDownloaded((*origMessH)->tocH,(*origMessH)->sumNum,true))
|
||
return nil;
|
||
#endif
|
||
|
||
PushPers(PERS_FORCE(MESS_TO_PERS(origMessH)));
|
||
if (newWin=DoComposeNew(toWhom))
|
||
{
|
||
newWinPtr = GetMyWindowWindowPtr (newWin);
|
||
#ifdef DEBUG
|
||
if (BUG14)
|
||
{
|
||
ShowMyWindow(newWinPtr);
|
||
UpdateMyWindow(newWinPtr);
|
||
}
|
||
#endif
|
||
newMessH = (MessHandle)GetMyWindowPrivateData(newWin);
|
||
rich = UseFlowOutExcerpt || (MessIsRich(origMessH) || MessIsRich(newMessH));
|
||
|
||
XferCustomTable(origMessH,newMessH);
|
||
if (SumOf(origMessH)->flags&FLAG_OUT)
|
||
{
|
||
BuildDateHeader(scratch,SumOf(origMessH)->seconds);
|
||
AppendMessText(newMessH,0,scratch+1,*scratch);
|
||
AppendMessText(newMessH,0,"\015",1);
|
||
}
|
||
|
||
/* handle the subject */
|
||
FindAndCopyHeader(origMessH,newMessH,HeaderName(SUBJ_HEAD),SUBJ_HEAD);
|
||
if (*GetRString(scratch,FWD_PREFIX))
|
||
{
|
||
TrimWhite(scratch);
|
||
CompHeadGetStr(newMessH,SUBJ_HEAD,subj);
|
||
if (!ReMatch(subj,scratch))
|
||
{
|
||
GetRString(scratch,FWD_PREFIX);
|
||
PrependMessText(newMessH,SUBJ_HEAD,scratch+1,*scratch);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* stick it in
|
||
*/
|
||
offset = PETEGetTextLen(PETE,(*newMessH)->bodyPTE);
|
||
CompHeadFind(origMessH,0,&hs);
|
||
origBo = hs.value;
|
||
PeteCopy((*origMessH)->bodyPTE,(*newMessH)->bodyPTE,
|
||
0,origBo,offset,nil,True);
|
||
PETEInsertParaPtr(PETE,(*newMessH)->bodyPTE,kPETELastPara,nil,nil,0,nil); // make sure we have an empty para at end
|
||
PeteCopyNoLabel((*origMessH)->bodyPTE,(*newMessH)->bodyPTE,
|
||
origBo,PETEGetTextLen(PETE,(*origMessH)->bodyPTE),
|
||
PETEGetTextLen(PETE,(*newMessH)->bodyPTE),nil,False,html?0:0xffffffff);
|
||
#ifdef NEVER
|
||
PeteCopy((*origMessH)->bodyPTE,(*newMessH)->bodyPTE,
|
||
0,PETEGetTextLen(PETE,(*origMessH)->bodyPTE),
|
||
offset,nil,rich);
|
||
#endif
|
||
style.tsLock = 0;
|
||
PETESetTextStyle(PETE,(*newMessH)->bodyPTE,offset,0x7fffffff,&style,peLockValid);
|
||
|
||
if (rich && !(MainEvent.modifiers&optionKey) && *GetRString(scratch,FWD_QUOTE))
|
||
{
|
||
PeteExcerpt((*newMessH)->bodyPTE,offset,PETEGetTextLen(PETE,(*newMessH)->bodyPTE)-1);
|
||
if (PrefIsSet(PREF_SCHEERDER))
|
||
{
|
||
CompHeadFind(newMessH,0,&hs);
|
||
PeteSelect(nil,(*newMessH)->bodyPTE,hs.value,PeteLen((*newMessH)->bodyPTE));
|
||
PeteWrap((*newMessH)->win,(*newMessH)->bodyPTE,false);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* make sure the last line is blank
|
||
*/
|
||
EnsureMessNewline(newMessH);
|
||
|
||
/*
|
||
* deal with attachments
|
||
*/
|
||
if (SumOf(origMessH)->flags&FLAG_OUT)
|
||
{
|
||
HeadSpec hs;
|
||
UHandle text = nil;
|
||
if (CompHeadFind(origMessH,ATTACH_HEAD,&hs) && !CompHeadGetText((*origMessH)->bodyPTE,&hs,&text) &&
|
||
CompHeadFind(newMessH,ATTACH_HEAD,&hs))
|
||
{
|
||
CompHeadSet((*newMessH)->bodyPTE,&hs,text);
|
||
}
|
||
ZapHandle(text);
|
||
}
|
||
else
|
||
{
|
||
if (MessFlagIsSet(origMessH,FLAG_HAS_ATT)) CopyAttachments(newMessH);
|
||
SpoolAttachments(newMessH);
|
||
}
|
||
|
||
/*
|
||
* quote them
|
||
*/
|
||
if (!rich && !(MainEvent.modifiers&optionKey))
|
||
QuoteLines((*newMessH)->bodyPTE,CompBodyOffset(newMessH)-1,0x7fffffff,FWD_QUOTE,nil);
|
||
|
||
if (GetWindowKind(winWP)!=COMP_WIN)
|
||
{
|
||
AccuAddLong(&(*newMessH)->aSourceMID,SumOf(origMessH)->uidHash);
|
||
AccuAddLong(&(*newMessH)->aSourceMID,SumOf(origMessH)->state);
|
||
AccuAddLong(&(*newMessH)->aSourceMID,FORWARDED);
|
||
SetState((*origMessH)->tocH,(*origMessH)->sumNum,FORWARDED);
|
||
}
|
||
|
||
/*
|
||
* Attributions
|
||
*/
|
||
Attribute(FWD_INTRO,origMessH,newMessH,False);
|
||
Attribute(FWD_TRAIL,origMessH,newMessH,True);
|
||
|
||
/*
|
||
* copy priority (not my idea)
|
||
*/
|
||
if (!PrefIsSet(PREF_NO_XF_PRIOR))
|
||
SumOf(newMessH)->origPriority = SumOf(newMessH)->priority =
|
||
SumOf(origMessH)->origPriority;
|
||
|
||
SumOf(newMessH)->outType = OUT_FORWARD; // for statistics
|
||
|
||
#ifdef TWO
|
||
ApplyDefaultStationery(newWin,True,True);
|
||
#endif
|
||
UpdateSum(newMessH,SumOf(newMessH)->offset,SumOf(newMessH)->length);
|
||
if (showIt)
|
||
{
|
||
ShowMyWindow(newWinPtr);
|
||
newWin->isDirty = False;
|
||
PeteCleanList(newWin->pte);
|
||
}
|
||
}
|
||
PopPers();
|
||
return(newWin);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* CompBodyOffset - find the body in a comp message
|
||
**********************************************************************/
|
||
long CompBodyOffset(MessHandle messH)
|
||
{
|
||
HeadSpec hs;
|
||
|
||
if (CompHeadFind(messH,0,&hs)) return(hs.value);
|
||
else return(0);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* DoFordirectMessage - forward or redirect a message, silently
|
||
**********************************************************************/
|
||
OSErr DoFordirectMessage(TOCHandle tocH,short sumNum,short flk,PStr addresses,Boolean bulk)
|
||
{
|
||
Boolean iOpened = !(*tocH)->sums[sumNum].messH;
|
||
MyWindowPtr origWin = GetAMessage(tocH,sumNum,nil,nil,False);
|
||
MessHandle newMessH;
|
||
OSErr err;
|
||
MyWindowPtr win;
|
||
|
||
//Enhanced Filters no such filter functionality in Light
|
||
if (!HasFeature (flk==flkForward ? featureFilterForward : featureFilterRedirect))
|
||
return (1);
|
||
|
||
UseFeature (flk==flkForward ? featureFilterForward : featureFilterRedirect);
|
||
if (!origWin) return(1);
|
||
|
||
win = flk==flkForward ? DoForwardMessage(origWin,0,False) :
|
||
DoRedistributeMessage(origWin,0,False,False,False);
|
||
|
||
if (iOpened) CloseMyWindow(GetMyWindowWindowPtr(origWin));
|
||
|
||
if (!win) return(1);
|
||
|
||
SetState(tocH,sumNum,flk==flkForward ? FORWARDED : REDIST);
|
||
newMessH = Win2MessH(win);
|
||
if (bulk) SetMessOpt(newMessH,OPT_BULK);
|
||
SetMessText(newMessH,TO_HEAD,addresses+1,*addresses);
|
||
if (err=QueueMessage((*newMessH)->tocH,(*newMessH)->sumNum,kEuSendNext,0,true,true))
|
||
DeleteMessage((*newMessH)->tocH,(*newMessH)->sumNum,False);
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* DoReplyClosed - reply to a closed message, silently
|
||
**********************************************************************/
|
||
OSErr DoReplyClosed(TOCHandle tocH,short sumNum,Boolean all, Boolean self, Boolean quote, Boolean doFcc, short withWhich,Boolean vis,Boolean station)
|
||
{
|
||
Boolean iOpened = !(*tocH)->sums[sumNum].messH;
|
||
MyWindowPtr origWin = GetAMessage(tocH,sumNum,nil,nil,False);
|
||
MessHandle newMessH;
|
||
OSErr err;
|
||
MyWindowPtr win;
|
||
|
||
if (!origWin) return(1);
|
||
|
||
win = DoReplyMessage(origWin, all, self, quote, doFcc, withWhich,False,station,True);
|
||
|
||
if (iOpened) CloseMyWindow(GetMyWindowWindowPtr(origWin));
|
||
|
||
if (!win) return(1);
|
||
|
||
SetState(tocH,sumNum,REPLIED);
|
||
newMessH = Win2MessH(win);
|
||
SetMessOpt(newMessH,OPT_BULK);
|
||
if (err=QueueMessage((*newMessH)->tocH,(*newMessH)->sumNum,kEuSendNext,0,true,true))
|
||
DeleteMessage((*newMessH)->tocH,(*newMessH)->sumNum,False);
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* FindAndCopyHeader - pick the given header out of a message, and
|
||
* copy it to a composition message
|
||
************************************************************************/
|
||
int FindAndCopyHeader(MessHandle origMH,MessHandle newMH,UPtr fromHead,short toHead)
|
||
{
|
||
UPtr body;
|
||
long size;
|
||
int result;
|
||
Handle text;
|
||
|
||
PeteGetRawText((*origMH)->bodyPTE,&text);
|
||
|
||
body = LDRef(text);
|
||
size = BodyOffset(text);
|
||
result = TextFindAndCopyHeader(body,size,newMH,fromHead,toHead,0);
|
||
HUnlock(text);
|
||
return(result);
|
||
}
|
||
|
||
/************************************************************************
|
||
* CopyNewsgroups - copy newsgroups into new message
|
||
************************************************************************/
|
||
OSErr CopyNewsgroups(MessHandle origMH,MessHandle newMH)
|
||
{
|
||
Str255 s;
|
||
HeadSpec hs, origHS;
|
||
UHandle text=nil;
|
||
UHandle addresses = nil;
|
||
OSErr err = fnfErr;
|
||
UPtr address;
|
||
Boolean first = true;
|
||
|
||
GetRString(s,NEWSGROUPS);
|
||
|
||
if (CompHeadFindStr(origMH,s,&origHS))
|
||
if (!(err=CompHeadGetText((*origMH)->bodyPTE,&origHS,&text)))
|
||
if (!(err=SuckAddresses(&addresses,text,false,false,false,nil)) && addresses && *addresses && **addresses)
|
||
{
|
||
if (CompHeadFind(newMH,BCC_HEAD,&hs))
|
||
{
|
||
if (*GetRString(s,MAIL2NEWS))
|
||
{
|
||
if (hs.value!=hs.stop) InsertCommaIfNeedBe((*newMH)->bodyPTE,&hs);
|
||
CompHeadAppendPtr((*newMH)->bodyPTE,&hs,s+1,*s);
|
||
first = false;
|
||
}
|
||
for(address=LDRef(addresses);*address;address+=*address+2)
|
||
{
|
||
if (!first || hs.value!=hs.stop)
|
||
if (!InsertCommaIfNeedBe((*newMH)->bodyPTE,&hs))
|
||
CompHeadAppendPtr((*newMH)->bodyPTE,&hs," ",1);
|
||
|
||
if (err=CompHeadAppendPtr((*newMH)->bodyPTE,&hs,"<EFBFBD>",1)) break;
|
||
if (err=CompHeadAppendPtr((*newMH)->bodyPTE,&hs,address+1,*address)) break;
|
||
first = false;
|
||
}
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* TextFindAndCopyHeader - pick the given header out of a text block, and
|
||
* copy it to a composition message
|
||
************************************************************************/
|
||
int TextFindAndCopyHeader(UPtr body,long size,MessHandle newMH,UPtr fromHead,short toHead,short label)
|
||
{
|
||
MyWindowPtr newWin = (*newMH)->win;
|
||
UPtr bodyEnd = body+size;
|
||
UPtr spot;
|
||
Boolean first = True;
|
||
HeadSpec hs;
|
||
long labStart;
|
||
|
||
if ((body = FindHeaderString(body,fromHead,&size,False)) && size)
|
||
{
|
||
if (CompHeadFind(newMH,toHead,&hs))
|
||
{
|
||
labStart = hs.stop;
|
||
for(;;)
|
||
{
|
||
if (!first || hs.value!=hs.stop)
|
||
if (!InsertCommaIfNeedBe((*newMH)->bodyPTE,&hs))
|
||
CompHeadAppendPtr((*newMH)->bodyPTE,&hs," ",1);
|
||
|
||
CompHeadAppendPtr((*newMH)->bodyPTE,&hs,body,size);
|
||
|
||
first = False;
|
||
|
||
body += size;
|
||
while (IsWhite(*body)&&body<bodyEnd) body++; /* skip to newline */
|
||
body++; /* skip newline */
|
||
if (body<bodyEnd && IsWhite(*body)) /* continuation */
|
||
{
|
||
while (IsWhite(*body)&&body<bodyEnd) body++;
|
||
for (spot=body;spot<bodyEnd&&*spot!='\015';spot++);
|
||
do {spot--;} while (IsWhite(*spot));
|
||
size = spot-body+1;
|
||
if (!size) break;
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
if (label && CompHeadFind(newMH,toHead,&hs) && hs.stop>labStart)
|
||
PeteLabel((*newMH)->bodyPTE,labStart,hs.stop,label,label);
|
||
}
|
||
}
|
||
return(body!=nil);
|
||
}
|
||
|
||
/************************************************************************
|
||
* XferCustomTable - transfer a custom table from a message to its reply
|
||
* (or forward or redirect)
|
||
************************************************************************/
|
||
void XferCustomTable(MessHandle origMessH,MessHandle newMessH)
|
||
{
|
||
short origTable;
|
||
Boolean isOut;
|
||
|
||
/*
|
||
* under the old regime, we don't do this step
|
||
*/
|
||
if (!NewTables) return;
|
||
|
||
origTable = SumOf(origMessH)->tableId;
|
||
|
||
/*
|
||
* if the original message had the default table, give the default table
|
||
* to the new message (which has already been done, so return)
|
||
*/
|
||
if (origTable == DEFAULT_TABLE) return;
|
||
|
||
/*
|
||
* if no table was on the original mail, use the default table
|
||
* this is different from non-MIME mail
|
||
*/
|
||
if (!origTable) return;
|
||
|
||
/*
|
||
* if original was outgoing, use same table
|
||
*/
|
||
isOut = (SumOf(origMessH)->flags&FLAG_OUT)!=0;
|
||
if (isOut) SumOf(newMessH)->tableId = origTable;
|
||
|
||
/*
|
||
* Otherwise, look for an out table that corresponds to the In table
|
||
* and use that.
|
||
*/
|
||
else if (GetResource_('taBL',origTable+1))
|
||
SumOf(newMessH)->tableId = origTable+1;
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
* WeedXAttachments - figure out if all the attachments are there
|
||
************************************************************************/
|
||
void WeedXAttachments(MessHandle messH,Boolean errReport)
|
||
{
|
||
short i;
|
||
FSSpec spec;
|
||
short err;
|
||
short removed = 0;
|
||
Boolean foundAny = False;
|
||
HeadSpec hs;
|
||
|
||
for (i=1;1!=(err=GetIndAttachment(messH,i,&spec,nil));i++)
|
||
{
|
||
if (err)
|
||
{
|
||
/* attachment does not exist */
|
||
removed++;
|
||
RemoveIndAttachment(messH,i);
|
||
i--;
|
||
}
|
||
else foundAny = True;
|
||
}
|
||
if (!foundAny && CompHeadFind(messH,ATTACH_HEAD,&hs) && hs.value!=hs.stop)
|
||
{
|
||
CompHeadSetPtr(TheBody,&hs,"",0);
|
||
removed = True;
|
||
}
|
||
if (removed && errReport) WarnUser(ATTACH_REMOVED,removed);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SpoolAttachments - copy attachments to the spool area
|
||
**********************************************************************/
|
||
OSErr SpoolAttachments(MessHandle messH)
|
||
{
|
||
short n;
|
||
short i;
|
||
OSErr err = noErr;
|
||
FSSpec spec;
|
||
|
||
/*
|
||
* count the attachments
|
||
*/
|
||
for (n=0;!GetIndAttachment(messH,n+1,&spec,nil);n++);
|
||
|
||
for (i=1;i<=n;i++) if (err=SpoolIndAttachment(messH,1)) break;
|
||
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* SpoolIndAttachment - spool a single attachment
|
||
**********************************************************************/
|
||
OSErr SpoolIndAttachment(MessHandle messH,short i)
|
||
{
|
||
FSSpec spec, newSpec;
|
||
OSErr err = noErr;
|
||
|
||
|
||
/*
|
||
* make the folder
|
||
*/
|
||
if (err = MakeAttSubFolder(messH,SumOf(messH)->uidHash,&newSpec))
|
||
return(FileSystemError(COPY_ATTACHMENT,newSpec.name,err));
|
||
|
||
/*
|
||
* grab it
|
||
*/
|
||
if (!GetIndAttachment(messH,i,&spec,nil))
|
||
{
|
||
/*
|
||
* Copy the attachment
|
||
*/
|
||
PCopy(newSpec.name,spec.name);
|
||
if (!SameSpec(&spec,&newSpec) && !FSpIsItAFolder(&spec))
|
||
{
|
||
Str255 longName;
|
||
|
||
if ((err=FSpDupFile(&newSpec,&spec,False,False)))
|
||
return(FileSystemError(COPY_ATTACHMENT,spec.name,err));
|
||
|
||
// handle long filename
|
||
if (!FSpGetLongName(&spec,kTextEncodingUnknown,longName) && *longName>31)
|
||
FSpSetLongName(&newSpec,kTextEncodingUnknown,longName,&newSpec);
|
||
|
||
CompAttachSpec((*messH)->win,&newSpec);
|
||
RemoveIndAttachment(messH,i);
|
||
}
|
||
}
|
||
return(err);
|
||
}
|
||
|
||
/**********************************************************************
|
||
* MakeAttSubFolder - make the subfolder for attachment spooling
|
||
**********************************************************************/
|
||
OSErr MakeAttSubFolder(MessHandle messH,uLong uidHash,FSSpecPtr folder)
|
||
{
|
||
OSErr err;
|
||
FSSpec spool;
|
||
Str255 scratch;
|
||
long dirID;
|
||
|
||
if (messH && !MessOptIsSet(messH,OPT_HAS_SPOOL)) SetMessOpt(messH,OPT_HAS_SPOOL);
|
||
GetRString(folder->name,SPOOL_FOLDER); // in case of error
|
||
|
||
/*
|
||
* find the folder
|
||
*/
|
||
if (err=SubFolderSpec(SPOOL_FOLDER,&spool))
|
||
return(err);
|
||
|
||
/*
|
||
* message id
|
||
*/
|
||
NumToString(uidHash,scratch);
|
||
|
||
/*
|
||
* specify
|
||
*/
|
||
SimpleMakeFSSpec(spool.vRefNum,spool.parID,scratch,folder);
|
||
|
||
/*
|
||
* create
|
||
*/
|
||
err = FSpDirCreate(folder,smSystemScript,&dirID);
|
||
if (err==dupFNErr)
|
||
{
|
||
dirID = SpecDirId(folder);
|
||
if (dirID) err = noErr;
|
||
}
|
||
if (err) return(err);
|
||
|
||
/*
|
||
* and make it point into the folder
|
||
*/
|
||
folder->parID = dirID;
|
||
*folder->name = 0;
|
||
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* RemoveIndAttachment - remove a particular attachment
|
||
************************************************************************/
|
||
void RemoveIndAttachment(MessHandle messH, short index)
|
||
{
|
||
HeadSpec where;
|
||
FSSpec spec;
|
||
OSErr err = GetIndAttachment(messH,index,&spec,&where);
|
||
|
||
if (err!=1)
|
||
{
|
||
CompDelAttachment(messH,&where);
|
||
AttachSelect(messH);
|
||
}
|
||
}
|
||
|
||
/************************************************************************
|
||
* CopyToOut - copy a message to the Out mailbox
|
||
************************************************************************/
|
||
OSErr CopyToOut(TOCHandle fromTocH,short sumNum,TOCHandle toTocH)
|
||
{
|
||
MessHandle messH;
|
||
short err = 1;
|
||
MyWindowPtr win=nil, newWin=nil;
|
||
MSumType newSum,oldSum;
|
||
|
||
if (!(win = GetAMessage(fromTocH,sumNum,nil,nil,False))) return(1);
|
||
messH = Win2MessH(win);
|
||
fromTocH = (*messH)->tocH;
|
||
sumNum = (*messH)->sumNum;
|
||
|
||
if (newWin = DoSalvageMessage((*messH)->win,True))
|
||
{
|
||
if (SaveComp(newWin))
|
||
{
|
||
newSum = (*toTocH)->sums[(*toTocH)->count-1];
|
||
oldSum = (*fromTocH)->sums[sumNum];
|
||
SumInfoCpy(&newSum,&oldSum);
|
||
newSum.flags |= FLAG_OUT;
|
||
(*toTocH)->sums[(*toTocH)->count-1] = newSum;
|
||
err = noErr;
|
||
}
|
||
NoSaves = True; CloseMyWindow(GetMyWindowWindowPtr(newWin)); NoSaves = False;
|
||
}
|
||
if (win) CloseMyWindow(GetMyWindowWindowPtr(win));
|
||
return(err);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SumInfoCpy - copy non-essential summary fields
|
||
************************************************************************/
|
||
void SumInfoCpy(MSumPtr newSum, MSumPtr oldSum)
|
||
{
|
||
newSum->state = oldSum->state;
|
||
PCopy(newSum->from,oldSum->from);
|
||
PCopy(newSum->subj,oldSum->subj);
|
||
newSum->seconds = oldSum->seconds;
|
||
newSum->flags = oldSum->flags;
|
||
newSum->tableId = oldSum->tableId;
|
||
newSum->sigId = SigValidate(oldSum->sigId);
|
||
newSum->priority = oldSum->priority;
|
||
newSum->origPriority = oldSum->origPriority;
|
||
BMD(&oldSum->spareShort2,&newSum->spareShort2,sizeof(newSum->spareShort2));
|
||
if (oldSum->opts&OPT_INLINE_SIG) newSum->opts |= OPT_INLINE_SIG;
|
||
}
|
||
|
||
/* The following hashing algorithm, KRHash, is derived from Karp & Rabin,
|
||
Harvard Center for Research in Computing Technology Tech Report TR-31-81. */
|
||
/* The prime number in use is KRHashPrime. It happens to be
|
||
the largest prime number that will fit in 31 bits, except for 2^31-1 itself. */
|
||
|
||
#define KRHashPrime (2147483629)
|
||
|
||
/************************************************************************
|
||
* HashWithSeed - generate a hash from a string and a seed.
|
||
************************************************************************/
|
||
uLong HashWithSeedLo(UPtr s, uLong n, uLong seed)
|
||
{
|
||
uLong sum = seed-1;
|
||
int Bit;
|
||
|
||
for (;n;n--,s++) {
|
||
for (Bit = 0x80; Bit != 0; Bit >>= 1) {
|
||
sum += sum;
|
||
if (sum >= KRHashPrime) sum -= KRHashPrime;
|
||
if ((*s) & Bit) ++sum;
|
||
if (sum >= KRHashPrime) sum -= KRHashPrime;
|
||
}
|
||
}
|
||
return(sum+1);
|
||
}
|
||
|
||
/************************************************************************
|
||
* MIDHash - hash a message id, stripping <>'s first
|
||
************************************************************************/
|
||
uLong MIDHash(UPtr text,long size)
|
||
{
|
||
Str255 scratch;
|
||
UHandle addresses;
|
||
|
||
SuckPtrAddresses(&addresses,text,size,False,False,False,nil);
|
||
|
||
if (addresses)
|
||
{
|
||
PCopy(scratch,*addresses);
|
||
ZapHandle(addresses);
|
||
return(Hash(scratch));
|
||
}
|
||
else return(kNoMessageId);
|
||
}
|
||
|
||
/************************************************************************
|
||
* SetHash - set a message's hash function
|
||
************************************************************************/
|
||
void SetHashLo(TOCHandle tocH,short sumNum,uLong hash,Boolean soft)
|
||
{
|
||
DBNoteUIDHash((*tocH)->sums[sumNum].uidHash,hash);
|
||
if (!soft || !ValidHash((*tocH)->sums[sumNum].uidHash))
|
||
(*tocH)->sums[sumNum].uidHash = hash;
|
||
if (!soft || !ValidHash((*tocH)->sums[sumNum].msgIdHash))
|
||
(*tocH)->sums[sumNum].msgIdHash = hash;
|
||
TOCSetDirty(tocH,true);
|
||
}
|
||
|
||
/************************************************************************
|
||
* Rehash - recompute the hash for a message
|
||
************************************************************************/
|
||
void RehashLo(TOCHandle tocH,short sumNum,UHandle text,Boolean soft)
|
||
{
|
||
Str255 scratch;
|
||
UPtr spot;
|
||
long size = GetHandleSize_(text);
|
||
uLong hash;
|
||
|
||
spot = LDRef(text);
|
||
GetRString(scratch,HEADER_STRN+MSGID_HEAD);
|
||
if (spot = FindHeaderString(spot,scratch,&size,False))
|
||
hash = MIDHash(spot,size);
|
||
else
|
||
{
|
||
NewMessageId(scratch);
|
||
hash = MIDHash(scratch+1,*scratch);
|
||
}
|
||
SetHashLo(tocH,sumNum,hash,soft);
|
||
UL(text);
|
||
}
|
||
|
||
#define PREVIEW_ID_MULT_REDO (-3)
|
||
#define PREVIEW_ID_MULT (-2)
|
||
/************************************************************************
|
||
* Preview - preview a message, if desired
|
||
************************************************************************/
|
||
void Preview(TOCHandle tocH,short sumNum)
|
||
{
|
||
WindowPtr tocWinWP = GetMyWindowWindowPtr ((*tocH)->win);
|
||
MessHandle messH;
|
||
long id;
|
||
PETEHandle pte;
|
||
MyWindowPtr messWin = nil;
|
||
Boolean active = false;
|
||
short ezOpenSum;
|
||
OSErr err;
|
||
Str63 profileName;
|
||
#ifdef IMAP
|
||
short oldPreview;
|
||
#endif
|
||
|
||
if (!(pte = (*tocH)->previewPTE)) return;
|
||
|
||
messH = (*tocH)->sums[sumNum].messH;
|
||
if (sumNum>=0)
|
||
{
|
||
EnsureMID(tocH,sumNum);
|
||
id = (*tocH)->sums[sumNum].serialNum;
|
||
}
|
||
else if (sumNum==-2)
|
||
{
|
||
if (!(*tocH)->conConMultiScan) id = (*tocH)->previewID;
|
||
else if (ConConMultipleAppropriate(tocH))
|
||
{
|
||
(*tocH)->previewID = PREVIEW_ID_MULT_REDO;
|
||
id = PREVIEW_ID_MULT;
|
||
}
|
||
else
|
||
id = 0;
|
||
}
|
||
else
|
||
id = 0;
|
||
|
||
if ((*tocH)->previewID==id)
|
||
{
|
||
PeteCalcOn((*tocH)->previewPTE);
|
||
if (id && (*tocH)->lastSameTicks!=1 && id!=PREVIEW_ID_MULT) // we have a message and we're not already done
|
||
{
|
||
if (!InBG // front only
|
||
&& IsWindowVisible(tocWinWP) // visible
|
||
&& tocWinWP==FrontWindow_() // frontmost
|
||
&& (*tocH)->userActive) // user has recently clicked or typed at me
|
||
{
|
||
if (TickCount()-(*tocH)->lastSameTicks>10 // it's been a while since we last checked
|
||
&& PreviewReadTimer // the user wants this marking
|
||
&& TickCount()-(*tocH)->lastSameTicks>GetRLong(PREVIEW_READ_SECS)*60) // and it's been long enough
|
||
{
|
||
(*tocH)->lastSameTicks = 1;
|
||
#ifdef IMAP
|
||
// do not automatically mark IMAP minimal headers as read, ever.
|
||
if (!(*tocH)->imapTOC || IMAPMessageDownloaded(tocH,sumNum))
|
||
#endif
|
||
BeenThereDoneThat(tocH,sumNum);
|
||
}
|
||
}
|
||
else
|
||
(*tocH)->lastSameTicks = TickCount(); // reset counter if user not active
|
||
}
|
||
return;
|
||
}
|
||
else (*tocH)->lastSameTicks = TickCount();
|
||
|
||
#ifdef IMAP
|
||
if ((*tocH)->previewID && (*tocH)->imapTOC)
|
||
{
|
||
// Cancel the IMAP download if this is an imap message ...
|
||
FindRealSummary(tocH, (*tocH)->previewID, &oldPreview);
|
||
IMAPAbortMessageFetch(tocH, oldPreview);
|
||
}
|
||
#endif
|
||
|
||
(*tocH)->previewID = id;
|
||
(*tocH)->ezOpenSerialNum = 0;
|
||
|
||
PeteSetTextPtr(pte," ",1); // put something into it so that all the plains work
|
||
PetePlain(pte,0,1,peAllValid);
|
||
PetePlainParaAt(pte,0,1);
|
||
PeteLock(pte,0,1,peModLock|peSelectLock);
|
||
PeteSetTextPtr(pte,"",0); // clear it now
|
||
|
||
if (id && id!=PREVIEW_ID_MULT)
|
||
{
|
||
SelectBoxRange(tocH,sumNum,sumNum,false,-1,-1);
|
||
|
||
// need to open?
|
||
if (!messH)
|
||
{
|
||
short realSumNum;
|
||
TOCHandle realTocH;
|
||
if (!(realTocH = GetRealTOC(tocH,sumNum,&realSumNum)))
|
||
return;
|
||
|
||
if (!(messH = (*realTocH)->sums[realSumNum].messH))
|
||
{
|
||
messWin = (*realTocH)->which==OUT ? OpenComp(realTocH,realSumNum,nil,nil,false,false) :
|
||
#ifdef IMAP
|
||
OpenMessage(realTocH,realSumNum,nil,nil,false,true);
|
||
#else
|
||
OpenMessage(realTocH,realSumNum,nil,nil,false);
|
||
#endif
|
||
if (messWin) messH = Win2MessH(messWin);
|
||
}
|
||
#ifdef IMAP
|
||
//
|
||
// fire off a thread to fetch the next message as well, if it's not already there.
|
||
// ... but don't do this for the search window.
|
||
//
|
||
|
||
if (!Offline && AutoCheckOK() && !((*tocH)->virtualTOC))
|
||
{
|
||
short sumToOpenNext;
|
||
|
||
// open the next summary according to EZOpen
|
||
sumToOpenNext = EzOpenFind(realTocH,realSumNum);
|
||
if (sumToOpenNext >= 0)
|
||
{
|
||
if (!IMAPMessageDownloaded(realTocH, sumToOpenNext) && !IMAPMessageBeingDownloaded(realTocH, sumToOpenNext))
|
||
UIDDownloadMessage(realTocH, (*realTocH)->sums[sumToOpenNext].uidHash,false, false);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
if (messH)
|
||
{
|
||
Boolean junk = SumOf(messH)->spamScore >= GetRLong(JUNK_MAILBOX_THRESHHOLD);
|
||
|
||
(*PeteExtra(pte))->containsJunkMail = junk;
|
||
PeteCalcOff(pte);
|
||
PETEAllowUndo(PETE,pte,false,true);
|
||
if (!ConConMess(messH,pte,BoxPreviewProfile(profileName,tocH,CONCON_PREVIEW_PROFILE),nil,nil))
|
||
{
|
||
// concentrator did it!
|
||
// lock all the text
|
||
PeteLock(pte,0,PeteLen(pte),peModLock);
|
||
PeteCalcOn(pte);
|
||
}
|
||
else
|
||
{
|
||
long body = SumOf(messH)->bodyOffset - (*messH)->weeded;
|
||
long len;
|
||
long scanned;
|
||
UHandle text;
|
||
long oldID;
|
||
long para;
|
||
PETEParaInfo pinfo;
|
||
long bite;
|
||
|
||
// no drawing, please
|
||
PeteCalcOff(pte);
|
||
|
||
// Make sure we examine it for funny business
|
||
HiliteOddReply(messH);
|
||
|
||
// figure out what we need to copy
|
||
bite = len = PETEGetTextLen(PETE,TheBody);
|
||
|
||
// find a paragraph that includes 3K of text
|
||
if (bite>3 K) bite = 3 K;
|
||
PeteParaConvert(TheBody,bite-100,bite+100);
|
||
para = PeteParaAt(TheBody,bite-1);
|
||
Zero(pinfo);
|
||
PETEGetParaInfo(PETE,TheBody,para,&pinfo);
|
||
bite = pinfo.paraOffset+pinfo.paraLength;
|
||
|
||
// Reset the copy mask to include the reply-to label for now
|
||
PETESetLabelCopyMask(PETE,LABEL_COPY_MASK|pReplyLabel);
|
||
|
||
PeteGetTextAndSelection(TheBody,&text,nil,nil);
|
||
if (bite==len) while (bite && (*text)[bite-1]=='\015' || IsWhite((*text)[bite-1])) bite--;
|
||
|
||
// do the copy, but fool the graphics into thinking it's the same Pete record
|
||
oldID = (*PeteExtra(pte))->id;
|
||
(*PeteExtra(pte))->id = (*PeteExtra(TheBody))->id;
|
||
PeteCopy(TheBody,pte,0,bite,0,nil,false);
|
||
(*PeteExtra(pte))->id = oldID;
|
||
|
||
// pre-scan for some url's and attachments
|
||
PeteSetURLRescan(pte,0);
|
||
for (scanned=(*PeteExtra(pte))->urlScanned;
|
||
scanned!=-1 && scanned<3 K;
|
||
scanned=(*PeteExtra(pte))->urlScanned)
|
||
PeteURLScan((*tocH)->win,pte);
|
||
for (scanned=(*PeteExtra(pte))->quoteScanned;
|
||
scanned!=-1 && scanned<3 K;
|
||
scanned=(*PeteExtra(pte))->quoteScanned)
|
||
PeteQuoteScan(pte);
|
||
|
||
#ifdef ATT_ICONS
|
||
// Note: code assumes preview shows same headers as message
|
||
if (MessFlagIsSet(messH,FLAG_OUT))
|
||
{
|
||
FSSpec spec;
|
||
short i;
|
||
HeadSpec hs;
|
||
|
||
for (i=1;1!=GetIndAttachment(messH,i,&spec,&hs);i++)
|
||
PeteFileGraphicRange(pte,hs.start,hs.stop,&spec,fgAttachment);
|
||
}
|
||
#endif
|
||
|
||
// scroll to proper spot
|
||
PeteSelect(nil,pte,body+1,body+1);
|
||
PETESetCallback(PETE,pte,(void*)PeteCancelOnEvents,peProgressLoop);
|
||
if (err=PeteCalcOn(pte))
|
||
{
|
||
(*tocH)->previewID = 0;
|
||
PeteSetTextPtr(pte,"",0);
|
||
}
|
||
PETESetCallback(PETE,pte,(void*)PeteBusy,peProgressLoop);
|
||
if (!err)
|
||
{
|
||
ShowMessageSeparator(pte,((*tocH)->previewHi/(*tocH)->win->vPitch)>GetRLong(PREVIEW_HEADER_HIDE));
|
||
|
||
// Now copy the remainder
|
||
if (bite<len)
|
||
{
|
||
PeteCalcOff(pte);
|
||
PeteGetTextAndSelection(TheBody,&text,nil,nil);
|
||
PeteSetURLRescan(pte,3 K - 255);
|
||
while (len && (*text)[len-1]=='\015') len--;
|
||
if (bite<len)
|
||
{
|
||
(*PeteExtra(pte))->id = (*PeteExtra(TheBody))->id;
|
||
PeteCopy(TheBody,pte,bite,len,PETEGetTextLen(PETE,pte),nil,false);
|
||
(*PeteExtra(pte))->id = oldID;
|
||
PETESetCallback(PETE,pte,(void*)PeteCancelOnEvents,peProgressLoop);
|
||
if (PeteCalcOn(pte))
|
||
{
|
||
(*tocH)->previewID = 0;
|
||
PeteSetTextPtr(pte,"",0);
|
||
}
|
||
PETESetCallback(PETE,pte,(void*)PeteBusy,peProgressLoop);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Reset the copy mask to include the reply-to label for now
|
||
PETESetLabelCopyMask(PETE,LABEL_COPY_MASK);
|
||
|
||
// And lock all the text
|
||
PeteLock(pte,0,PeteLen(pte),peModLock);
|
||
}
|
||
}
|
||
|
||
if (messWin) CloseMyWindow(GetMyWindowWindowPtr(messWin));
|
||
if (!PrefIsSet(PREF_NO_EZ_OPEN))
|
||
{
|
||
ezOpenSum = EzOpenFind(tocH,sumNum);
|
||
if (ezOpenSum>=0)
|
||
{
|
||
(*tocH)->ezOpenSerialNum = (*tocH)->sums[ezOpenSum].serialNum;
|
||
CacheMessage(tocH,ezOpenSum);
|
||
}
|
||
}
|
||
|
||
if (AnalSpeakPhrases())
|
||
{
|
||
(*PeteExtra(pte))->taeDesired = true;
|
||
(*PeteExtra(pte))->taeSpeak = true;
|
||
}
|
||
}
|
||
else if (id==PREVIEW_ID_MULT)
|
||
{
|
||
ConConMultiple(tocH,pte,BoxPreviewProfile(profileName,tocH,CONCON_MULTI_PREVIEW_PROFILE),conConOutSeparatorRule,nil,nil);
|
||
(*tocH)->previewID = id;
|
||
}
|
||
else
|
||
BoxPreviewProfile(nil,tocH,0);
|
||
|
||
PeteActivate(pte,(*tocH)->win->isActive && !(*tocH)->listFocus,false);
|
||
}
|
||
|
||
/************************************************************************
|
||
* HTMLifyText - insert appropriate BR's, process and remove "related:" lines, etc.
|
||
************************************************************************/
|
||
void HTMLifyText(MyWindowPtr win,Handle text)
|
||
{
|
||
Accumulator a;
|
||
Ptr spot,end,lastSpot;
|
||
long len;
|
||
Str32 sBR;
|
||
char lastChar;
|
||
long offset = 0;
|
||
PartDesc pd;
|
||
StackHandle stack;
|
||
|
||
// Insert <BR> after each header line
|
||
if (!AccuInit(&a))
|
||
{
|
||
|
||
GetRString(sBR,HTMLTagsStrn+htmlBR);
|
||
|
||
len = 0;
|
||
spot = LDRef(text);
|
||
lastChar = 0;
|
||
lastSpot = spot;
|
||
for(end = spot + GetHandleSize(text); spot < end; spot++)
|
||
{
|
||
if (*spot=='\r')
|
||
{
|
||
AccuAddPtr(&a,lastSpot,spot-lastSpot);
|
||
AccuAddChar(&a,'<'); // Add <BR>
|
||
AccuAddStr(&a,sBR);
|
||
AccuAddChar(&a,'>');
|
||
if (lastChar == '\r')
|
||
break; // Hit 2 newlines in a row. End of headers.
|
||
lastSpot = spot+1;
|
||
}
|
||
lastChar = *spot;
|
||
}
|
||
|
||
UL(text);
|
||
Munger(text,0,nil,spot-*text,LDRef(a.data),a.offset);
|
||
offset = a.offset;
|
||
AccuZap(a);
|
||
}
|
||
|
||
// Build parts stack from "related:" lines and remove them
|
||
StackInit(sizeof(PartDesc),&stack);
|
||
while (0<=(offset = FindAnAttachment(text,offset,&pd.spec,false,&pd.cid,&pd.relURL,&pd.absURL)))
|
||
{
|
||
Ptr lineEnd,end;
|
||
long len;
|
||
|
||
StackQueue(&pd,stack);
|
||
// Remove related line
|
||
end = *text + GetHandleSize_(text);
|
||
for (lineEnd=*text+offset;lineEnd<end && *lineEnd!='\015';lineEnd++);
|
||
len = lineEnd-*text;
|
||
if (lineEnd<end) len++; // Get past CR
|
||
Munger(text,offset,nil,len,end,nil); // Delete the line
|
||
}
|
||
(*PeteExtra(win->pte))->partStack = stack;
|
||
}
|
||
|
||
|
||
#ifdef IMAP
|
||
/************************************************************************
|
||
* EnsureMsgDownloaded - if IMAP message, make sure it is downloaded
|
||
************************************************************************/
|
||
Boolean EnsureMsgDownloaded(TOCHandle tocH,short sumNum,Boolean attachmentsToo)
|
||
{
|
||
// Actually go fetch this message if we must
|
||
if ((*tocH)->imapTOC)
|
||
{
|
||
short n;
|
||
Boolean result;
|
||
|
||
// make sure this message has already been downloaded ...
|
||
if (!IMAPMessageDownloaded(tocH, sumNum))
|
||
{
|
||
if (IMAPMessageBeingDownloaded(tocH,sumNum))
|
||
{
|
||
// This message is currently being downloaded. Wait for it to finish
|
||
while (IMAPMessageBeingDownloaded(tocH,sumNum))
|
||
{
|
||
CycleBalls();
|
||
if (MyYieldToAnyThread())
|
||
break;
|
||
if (CommandPeriod) return (false);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Download this message in the foreground
|
||
if (UIDDownloadMessage(tocH, (*tocH)->sums[sumNum].uidHash, true, false)!=noErr)
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// put the message in the mailbox
|
||
for(n=100;n && !IMAPMessageDownloaded(tocH,sumNum);n--)
|
||
// call a reasonable number of times to get the job done
|
||
// there may be other messages that also need to be processed
|
||
UpdateIMAPMailbox(tocH);
|
||
|
||
// did we get the message ok?
|
||
if ((result=IMAPMessageDownloaded(tocH,sumNum)) && attachmentsToo)
|
||
{
|
||
// go fetch all the attachments for this message. Wait for them.
|
||
if (!FetchAllIMAPAttachments(tocH, sumNum, true))
|
||
result = false;
|
||
}
|
||
return result;
|
||
}
|
||
return true;
|
||
}
|
||
#endif
|
||
|
||
/************************************************************************
|
||
* EnableMsgButtons - enable or disable message buttons
|
||
************************************************************************/
|
||
void EnableMsgButtons(MyWindowPtr win,Boolean enable)
|
||
{
|
||
MControlsEnum cIndex;
|
||
ControlHandle ctl;
|
||
|
||
for (cIndex=mcWrite;cIndex<=mcTrash;cIndex++)
|
||
{
|
||
if (ctl=FindControlByRefCon(win,cIndex))
|
||
{
|
||
if (enable) ActivateControl(ctl);
|
||
else DeactivateControl(ctl);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
/************************************************************************
|
||
* GetMessageLength - return length of message
|
||
************************************************************************/
|
||
uLong GetMessageLength(TOCHandle tocH,short sumNum)
|
||
{
|
||
short realSum;
|
||
TOCHandle realTOC;
|
||
|
||
return (realTOC = GetRealTOC(tocH,sumNum,&realSum)) ? (*realTOC)->sums[realSum].length : 0;
|
||
|
||
}
|
||
|
||
/************************************************************************
|
||
* RedateTS - redate a message
|
||
************************************************************************/
|
||
OSErr RedateTS(TOCHandle tocH,short sumNum)
|
||
{
|
||
Str255 dateStr;
|
||
uLong secs;
|
||
uLong zoneSecs;
|
||
OSErr err = CacheMessage(tocH,sumNum);
|
||
|
||
if (!err && (*tocH)->sums[sumNum].cache)
|
||
{
|
||
HNoPurge((*tocH)->sums[sumNum].cache);
|
||
HandleHeadGetPStr((*tocH)->sums[sumNum].cache,HeaderStrn+DATE_HEAD,dateStr);
|
||
if (!*dateStr) return fnfErr;
|
||
secs = BeautifyDate(dateStr,&zoneSecs);
|
||
TimeStamp(tocH,sumNum,secs,zoneSecs);
|
||
HPurge((*tocH)->sums[sumNum].cache);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
/************************************************************************
|
||
* CurAddr - extract the current address from a window, if we have one
|
||
************************************************************************/
|
||
PStr CurAddr(MyWindowPtr win,PStr addr)
|
||
{
|
||
if (win->curAddr) return win->curAddr(win,addr);
|
||
else return CurAddrSel(win,addr);
|
||
}
|
||
|
||
/************************************************************************
|
||
* CurAddrSel - extract the current address from the selection, if we have one
|
||
************************************************************************/
|
||
PStr CurAddrSel(MyWindowPtr win,PStr addr)
|
||
{
|
||
if (win->pte && *PeteSelectedString(addr,win->pte) && *ShortAddr(addr,addr)) return addr;
|
||
return nil;
|
||
}
|