eudora-mac/mailxfer.c

1 line
65 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 "mailxfer.h"
#include "myssl.h"
#define FILE_NUM 52
/* Copyright (c) 1993 by QUALCOMM Incorporated */
#pragma segment Mailxfer
void NewMailSound(void);
short CheckForMail(TransStream stream,short *gotSome,XferFlags *flags);
short SendTheQueue(TransStream stream,XferFlags flags);
void ResetCheckTime(Boolean force);
OSErr SpecialXfer(struct XferFlags *flags);
OSErr POPHostLimit(void);
short XferMailLo(Boolean check, Boolean send, Boolean manual, XferFlags flags,long *totalGot,OSErr *dialErrPtr);
Boolean NeedPassword(Boolean check, Boolean send);
void ResetPersCheckTime(Boolean force);
#ifdef THREADING_ON
Boolean OKToThread(Boolean check, Boolean send, Boolean manual, Boolean ae);
long FindTotalQueuedSize(TOCHandle tocH,long gmtSecs);
Boolean AddSigIntro(PETEHandle pte,UHandle text);
Boolean RemoveSigIntro(PETEHandle pte,UHandle text);
pascal Boolean SpecialXferFilter(DialogPtr dgPtr,EventRecord *event,short *item);
PersHandle SMTPRelayPers(void);
OSErr RememberMID(uLong midHash);
long GlobalOpenUnreadCount();
long GlobalInUnreadCount();
/************************************************************************
* OKToThread - returns false if threading should be used because of
* transport limitations
*
* (a) Do not thread if the connection method is the CTB. The CTB
* supports one and only one connection at a time.
*
* (b) Do not thread if any personality is set to uucpOut and is allowed to
* send mail.
*
* (c) Do not thread if any personality is set to uucpIn and is allowed to
* check mail manually or automatically.
*
************************************************************************/
Boolean OKToThread(Boolean check, Boolean send, Boolean manual, Boolean ae)
{
Boolean threadOK = true;
// Str255 pass;
if (UseCTB)
threadOK = false;
// go ahead and thread uucp stuff
#if 0
else
{
for (CurPers=PersList;CurPers&&threadOK;CurPers=(*CurPers)->next)
{
if ((*CurPers)->uupcOut || (*CurPers)->uupcIn)
{
(*CurPers)->checkMeNow = (*CurPers)->sendMeNow = (*CurPers)->checked = 0;
if (check && *GetPref(pass,PREF_POP))
{
if ((manual||ae) && !PrefIsSet(PREF_JUST_SAY_NO))
(*CurPers)->checkMeNow = True;
}
if (send && (*CurPers)->sendQueue && !PrefIsSet(PREF_PERS_NO_SEND))
(*CurPers)->sendMeNow = True;
if ((*CurPers)->uupcOut && (*CurPers)->checkMeNow)
threadOK = false;
if ((*CurPers)->uupcIn && ((*CurPers)->sendMeNow || (*CurPers)->autoCheck))
threadOK = false;
}
}
}
#endif
return (threadOK);
}
#endif //THREADING_ON
/************************************************************************
* XferMail - do the right thing
* if thread is true, try to do it
*
* To support separate send and check threads:
*
* - don't allow more than one check thread at a time
* - don't allow more than one send thread at a time
* - if user tries to send while a send thread is running, queue the request up (via SendImmediately flag)
* and let the check go
* - before sending, set the SendQueue. if it's zero, don't send and reset SendImmediately flag
*
************************************************************************/
short XferMail(Boolean check, Boolean send, Boolean manual,Boolean ae,Boolean thread,short modifiers)
{
XferFlags flags;
OSErr err = noErr;
PersHandle pers;
Boolean persSend = false;
Boolean didWeUseMultiplePersonalities = false;
// archive the junk mailbox? Only if we're desperate
if (JunkPrefBoxArchive() && JunkTrimOK()) ArchiveJunk(GetJunkTOC());
// Cmd-Shift-M resyncs frontmost IMAP mailbox.
if (!PrefIsSet(PREF_ALTERNATE_CHECK_MAIL_CMD))
if (!(modifiers&optionKey) && (modifiers&shiftKey) && ResyncCurrentIMAPMailbox())
return (noErr);
// Don't allow more than one checkmail or sendmail thread
if ((CheckThreadRunning && check) || (PrefIsSet(PREF_POP_SEND) && SendThreadRunning))
return noErr;
// if "send when check" pref set, spawn the check and send later
if ((SendThreadRunning && send) || (PrefIsSet(PREF_POP_SEND) && CheckThreadRunning))
{
SendImmediately = true; // = SendImmediately || send;
if (check)
send = false;
else
return noErr;
}
// clear the subfolder cache; as good a place as any
SubFolderSpec(0,nil);
if (err=XferMailSetup (&check, &send, manual, ae, &flags, modifiers))
return err;
// no need in spawning send thread if none of the queued messages belong to a personality
// marked for sending whenever sends are done
for (pers=PersList;pers;pers=(*pers)->next)
if ((*pers)->sendQueue && (*pers)->sendMeNow)
persSend = true;
if (!SendQueue || !persSend)
{
send = false;
SendImmediately = false;
}
if (check)
{
GetDateTime(&LastCheckTime);
TaskProgressRefresh();
}
if (!(check || send))
return noErr;
FlushTOCs(True,True); /* flush unnecessary TOC's */
RememberOpenWindows(); /* for good measure */
ActiveTicks = 0; // we're pretending; manual check mail should not wait for idle time to filter
// fetch any mailbox lists we need for this mailcheck
for (pers=PersList;pers;pers=(*pers)->next)
{
PersHandle oldPers = CurPers;
if ((*pers)->checkMeNow)
{
CurPers = pers;
if (PrefIsSet(PREF_IS_IMAP))
{
if (!MailboxTreeGood(CurPers))
{
if (CreateLocalCache() == noErr)
EnsureSpecialMailboxes(CurPers);
}
}
CurPers = oldPers;
}
}
#ifdef THREADING_ON
if (PrefIsSet(PREF_THREADING_OFF) || !OKToThread(check, send, manual, ae) || !ThreadsAvailable() || !thread)
err = XferMailRun (check, send, manual, ae, flags, nil);
else
err = SetupXferMailThread (check, send, manual, ae, flags, nil);
#else
err = XferMailRun (check, send, manual, ae);
#endif
if (send) SetSendQueue(); // redo sendqueue if need be
SendImmediately = false; // clear this for next time around
#ifdef NAG
if (check && nagState)
CheckNagging ((*nagState)->state);
#endif
#ifdef AD_WINDOW
if (check && IsAdwareMode())
AdCheckingMail();
#endif
return(err);
}
/************************************************************************
* XferMailSetup -
* 12-8-98 JDB
* The check and send parameters may be modified by the SpecialXfer
* dialog, in which case the caller should know not to check/send.
************************************************************************/
short XferMailSetup(Boolean *check, Boolean *send, Boolean manual,Boolean ae, XferFlags *xFlags, short modifiers)
{
PersHandle oldCur = CurPers;
XferFlags flags;
Boolean special = 0!=(modifiers&optionKey);
Str255 pass;
uLong ticks, ivalTicks;
PersHandle pers;
CHECK_EXPIRE;
CHECK_DEMO;
if (Offline && GoOnline()) return(OFFLINE);
if (*send) // 5/15/97 ccw -- fixes "Send queued messages" deactive menu and timed mesg bugs
{
SetSendQueue();
ETLIdle(EMSFIDLE_PRE_SEND);
}
//PeteSetRudeColor();
Zero(flags);
flags.check = flags.servFetch = flags.servDel = *check;
flags.send = *send;
//flags.nuke = check && !PrefIsSet(PREF_LMOS);
flags.isAuto = *check && !(manual||ae);
CheckNow = False; // clear flag
if (!SelectXferMailPers(*check, *send, manual)) // Set if checking/sending from Personalities window
{
ticks = TickCount() + TICKS2MINS * GetRLong(PERS_CHECK_SLOP);
for (pers=PersList;pers;pers=(*pers)->next)
{
CurPers = pers;
(*CurPers)->doMeNow = (*CurPers)->checkMeNow = (*CurPers)->sendMeNow = (*CurPers)->checked = 0;
if (*check && *GetPOPPref(pass))
{
ivalTicks = (*CurPers)->autoCheck ? (*CurPers)->ivalTicks : 0;
if (ivalTicks && (!(*CurPers)->checkTicks || (*CurPers)->checkTicks+ivalTicks<ticks))
(*CurPers)->doMeNow = (*CurPers)->checkMeNow = True;
else if ((manual||ae) && !PrefIsSet(PREF_JUST_SAY_NO))
(*CurPers)->doMeNow = (*CurPers)->checkMeNow = True;
}
if (*send && (*CurPers)->sendQueue && !PrefIsSet(PREF_PERS_NO_SEND))
(*CurPers)->doMeNow = (*CurPers)->sendMeNow = True;
ASSERT(CurPers==pers);
}
}
if (manual&&!ae && special && SpecialXfer(&flags))
{
CurPers = oldCur;
return(userCancelled);
}
*send = flags.send;
*check = flags.check||flags.servFetch||flags.servDel||flags.nuke||flags.nukeHard||flags.stub;
if (!*send && !*check)
{
CurPers = oldCur;
return(userCancelled);
}
// collect passwords
for (pers=PersList;pers;pers=(*pers)->next)
{
CurPers = pers;
// Check for checking mail first
if (NeedPassword(*check && (*CurPers)->checkMeNow,false))
{
if (PersFillPw(CurPers,0)!=noErr)
{
ResetCheckTime(True);
CurPers = oldCur;
return(1);
}
}
// Double check that all the special things IMAP needs are in place
if (*check && (*CurPers)->checkMeNow && PrefIsSet(PREF_IS_IMAP) && !EnsureSpecialMailboxes(CurPers))
{
ResetCheckTime(True);
CurPers = oldCur;
return(1);
}
// Now check for checking mail
if (*send && (*CurPers)->sendMeNow)
{
PersHandle relayPers = SMTPRelayPers();
Boolean oldDoMeNow;
// if we're using a relay personality, switch to it
if (relayPers)
{
// gotta force doMeNow or we won't ask for a password
oldDoMeNow = (*relayPers)->doMeNow;
(*relayPers)->doMeNow = true;
PushPers(relayPers);
}
if (NeedPassword(false,true))
{
if (PersFillPw(CurPers,0)!=noErr)
{
if (relayPers) PopPers();
CurPers = oldCur;
return(1);
}
}
// put back the personality, and reset the doMeNow flag
if (relayPers)
{
PopPers();
(*relayPers)->doMeNow = oldDoMeNow;
}
}
ASSERT(CurPers==pers);
}
BlockMoveData(&flags,xFlags,sizeof(flags));
CurPers = oldCur;
return(noErr);
}
/************************************************************************
* XferMailReal - transfer mail; check or send, for each personality
************************************************************************/
short XferMailRun(Boolean check, Boolean send, Boolean manual,Boolean ae, XferFlags flags, IMAPTransferPtr imapInfo)
{
PersHandle oldCur = CurPers;
PersHandle pers;
OSErr err, anyErr;
long gotSome;
short dialErr;
Boolean offlineWas;
Boolean popChecked = false;
ASSERT(!InAThread() || CurThreadGlobals != &ThreadGlobals);
anyErr = err = noErr;
#ifdef THREADING_ON
if (InAThread())
{
if (PrefIsSet(PREF_TASK_PROGRESS_AUTO) && !TaskProgressWindow && !FindOpenWazoo(TASKS_WIN))
{
if (imapInfo && imapInfo->command!=UndefinedTask && !PrefIsSet(PREF_IMAP_TP_BRING_TO_FRONT))
OpenTasksWinBehind(nil);
else
OpenTasksWinBehind(manual ? BehindModal : nil);
}
}
NoXfer = flags.stub;
#endif THREADING_ON
gotSome = 0;
dialErr = noErr;
offlineWas = Offline;
Offline = False;
gPPPConnectFailed = false;
// is this an IMAP operation of some kind?
if (imapInfo && imapInfo->command!=UndefinedTask)
{
#if __profile__
// ProfilerClear();
#endif
SetCurrentTaskKind(imapInfo->command);
switch (imapInfo->command)
{
// if a mailbox was specified, then we're checking mail for an IMAP personality.
case IMAPResyncTask:
{
err = DoFetchNewMessages(&(imapInfo->targetSpec), true, false) ? noErr : 1;
check = true;
gotSome++;
break;
}
// if a tocH and uid is specified, then we should fetch a message
case IMAPFetchingTask:
{
err = DoDownloadMessages(imapInfo->destToc, imapInfo->uids, imapInfo->attachmentsToo) ? noErr : 1; // some error is needed here
break;
}
// if a delToc and uid is specified, delete the sepcified message(s)
case IMAPDeleteTask:
case IMAPUndeleteTask:
{
err = DoDeleteMessage(imapInfo->delToc, imapInfo->uids, imapInfo->nuke, imapInfo->expunge, (imapInfo->command==IMAPUndeleteTask)) ? noErr : 1;
break;
}
// if a source, destination, and uid list is specified, then do a transfer
case IMAPTransferTask:
{
err = DoTransferMessages(imapInfo->sourceToc, imapInfo->destToc, imapInfo->uids, imapInfo->copy);
break;
}
case IMAPExpungeTask:
{
err = DoExpungeMailbox(imapInfo->delToc) ? noErr : 1;
break;
}
case IMAPAttachmentFetch:
{
err = DoDownloadIMAPAttachments(imapInfo->attachments, imapInfo->targetBox) ? noErr : 1;
break;
}
case IMAPSearchTask:
{
err = DoIMAPServerSearch(imapInfo->destToc, imapInfo->boxesToSearch, imapInfo->toSearch, imapInfo->searchC, imapInfo->matchAll, imapInfo->firstUID) ? noErr : 1;
break;
}
case IMAPMultResyncTask:
case IMAPMultExpungeTask:
{
err = DoIMAPProcessMailboxes(imapInfo->toResync, imapInfo->command) ? noErr : 1;
break;
}
case IMAPUploadTask:
{
err = DoTransferMessagesToServer(imapInfo->destToc, imapInfo->appendData, imapInfo->copy, false);
break;
}
case IMAPPollingTask:
{
IMAPPollMailboxes(imapInfo->targetBox);
break;
}
case IMAPFilterTask:
{
err = DoIMAPFilterProgress();
break;
}
default:
{
// err = something went horribly wrong to get here
break;
}
}
Offline = Offline || offlineWas;
#if __profile__
// ProfilerDump("\pthreadedimap-profile");
// ProfilerClear();
#endif
}
// otherwise, it's a mailcheck, sweet and boring.
else
{
IMAPCheckThreadRunning = gNewMessages = 0; // forget about all past IMAP mail checks
NoNewMailMe = false; // also forget about any pending NoNewMail alerts we may have around.
gStayConnected = true; // Tell the dial code to keep the connection up until further notice.
for (pers=PersList;pers && !Offline && !gPPPConnectFailed && !CommandPeriod;pers=(*pers)->next)
{
CurPers = pers;
// only display the new mail alert if a pop personality is being checked
if ((*CurPers)->checkMeNow && !PrefIsSet(PREF_IS_IMAP)) popChecked = true;
err = XferMailLo(check && (*CurPers)->checkMeNow,send && (*CurPers)->sendMeNow,manual||ae,flags,&gotSome,&dialErr);
if (!anyErr) anyErr = err;
ASSERT(CurPers==pers);
}
CurPers = oldCur;
Offline = Offline || offlineWas;
gStayConnected = false; // the dialup connection can be torn down when no longer needed.
}
#ifdef THREADING_ON
if (InAThread())
{
#ifndef BATCH_DELIVERY_ON
if (send)
SetNeedToFilterOut();
if (check)
{
TOCHandle tempInTocH=GetTempInTOC();
TempInCount = tempInTocH ? (*tempInTocH)->count : 0;
if (TempInCount)
AddFilterTask();
}
#endif
// silly no mail alert; here is as good a place as any
if (!dialErr && flags.check && manual)
// do NoNewMailMe only if a pop personality was checked, and there aren't any other check threads going.
if (popChecked && !IMAPCheckThreadRunning && !gNewMessages)
NoNewMailMe = gotSome==0;
}
else
#endif
{
/*
* notify the user if new mail arrived (or not, in some cases)
*/
if (!dialErr && flags.check && (manual || gotSome>0))
{
NotifyNewMail(gotSome,flags.stub,GetRealInTOC(),nil);
}
NotifyHelpers(0,eHasConnected,nil);
}
if (check) ResetCheckTime(true); // Reset intervals for *all* personalities that were supposed to be checked. JDB 12-16-98
ZapHandle(LastAttSpec);
ASSERT(!InAThread() || CurThreadGlobals != &ThreadGlobals);
return(err);
}
/**********************************************************************
* XferMailLo - transfer mail for a given personality
**********************************************************************/
short XferMailLo(Boolean check, Boolean send, Boolean manual, XferFlags flags,long *totalGot,OSErr *dialErrPtr)
{
short err = 0;
short gotSome = 0;
#ifdef CTB
Boolean needDial;
Boolean need2ndPW = False;
short dialErr = 0;
Boolean needPW;
Str31 pass;
#endif
short anyErr = 0;
Boolean offlineWas;
Str255 s;
TransStream mailStream = 0;
Boolean willCheck = false,
willSend = false;
if (PrefIsSet(PREF_THREADING_OFF) || UseCTB) PhKill();
offlineWas = Offline;
Offline = False;
/*
* clear the decks
*/
if (send && !(*CurPers)->sendQueue) send = False;
#ifdef THREADING_ON
/*
* Set which task we're about to do in case we need to report it if we're low on memory
*/
SetCurrentTaskKind((check && !((*CurPers)->popSecure && send)) ? CheckingTask : SendingTask);
#endif
FlushTOCs(True,True); /* flush unnecessary TOC's */
if (MonitorGrow(True) || CommandPeriod) goto done;
if (check) HesOK = True; // force re-fetch of hesiod info
GetPOPInfo(s,s+127);
HesOK = False;
/*
* figure out what we need to do our duty
* We need the password if we're checking (or sending POPSecure)
* We need to dial the phone if we're going to use the CTB for
* sending or checking (or verifying the account under POPSecure)
*/
if (!send && !check) goto done; /* nothing to do */
if (CountResources(NOTIFY_TYPE)) NotifyHelpers(0,eWillConnect,nil);
if (check && (anyErr=POPHostLimit())) goto done;
#ifdef CTB
needDial = UseCTB && (send && !UUPCOut || check && !UUPCIn);
if (needDial) CheckNavPW(&needPW,&need2ndPW);
#endif
/*
* Set up our TransStream
*/
if (anyErr=NewTransStream(&mailStream)) goto done;
/*
* Doing network stuff; alerts should timeout
*/
#ifdef CTB
if (need2ndPW)
{
if (GetSecondPass(pass)) return(1);
PSCopy((*CurPers)->secondPass,pass);
}
#endif
/*
* Do we need to dial the phone?
*/
OpenProgress();
#ifdef CTB
if (needDial) dialErr = err = DialThePhone(mailStream);
#endif
/*
* Now, do the mail transfers
*/
if (!err)
{
if (MonitorGrow(True)) return(0);
if (check) (*CurPers)->checked = True; // don't retry instantly
/*
* check for mail, if need be
*/
willCheck = !Offline && // Check to see if we just went offline
!CommandPeriod && check && (!UseCTB || !err);
#ifdef THREADING_ON
/*
* Set task we're about to do in case we need to report it if we're low on memory
*/
if (willCheck)
SetCurrentTaskKind(CheckingTask);
#endif
if (MonitorGrow(True)) return(0);
if (willCheck)
{
if (IsIMAPPers(CurPers))
{
//checking mail on IMAP server
MailboxNodeHandle imapNode;
TOCHandle tocH;
// locate the inbox, and resync it.
if (imapNode = LocateInboxForPers(CurPers))
{
FSSpec inboxSpec = (*imapNode)->mailboxSpec;
tocH = TOCBySpec(&inboxSpec);
IMAPCheckThreadRunning++;
if (FetchNewMessages(tocH,true, true, true, !manual))
{
// remember if this was a manual mail check. the No New mail dialog might have to be shown.
gWasManualIMAPCheck = manual;
// resync all open IMAP mailboxes as well, if we should
if (PrefIsSet(IMAP_RESYNC_OPEN_MAILBOXES))
ResyncOpenMailboxes(CurPers);
// poll mailboxes if we ought to
IMAPPoll(CurPers);
}
IMAPCheckThreadRunning--;
}
}
else
{
AuditCheckStart(++gCheckSessionID,(*CurPers)->persId,!manual);
StartStreamAudit(mailStream, kAuditBytesReceived);
err = CheckForMail(mailStream,&gotSome,&flags) || err;
StopStreamAudit(mailStream);
AuditCheckDone(gCheckSessionID,gotSome,gotSome?ReportStreamAudit(mailStream):0);
}
(*CurPers)->checked = True;
#ifdef CTB
if (needDial && send && !err) err = CTBNavigateSTRN(NAVMID);
#endif
}
#ifdef POPSECURE
/* if the password has been invalidated, we can't send */
if (!POPSecure) send = False;
#endif
/*
* send mail
* For CTB connections, we only do this if the POP check went ok,
* since we don't know what state the line may be in. For MacTCP or UUPC,
* the two are independent so we really don't care.
*/
willSend = !Offline && !CommandPeriod && send && (!UseCTB || !err) && !gPPPConnectFailed;
#ifdef THREADING_ON
/*
* Set task we're about to do in case we need to report it if we're low on memory
*/
if (willSend)
SetCurrentTaskKind(SendingTask);
#endif
if (MonitorGrow(True)) return(0);
if (willSend)
err = SendTheQueue(mailStream,flags) || err;
}
anyErr = err || CommandPeriod;
/*
* cleanup connection
*/
#ifdef CTB
if (needDial) HangUpThePhone();
#endif
//CloseProgress();
done:
Offline = Offline || offlineWas;
#ifdef CTB
if (dialErrPtr) *dialErrPtr = dialErr;
#endif
if (totalGot) *totalGot += gotSome;
if (mailStream) ZapTransStream(&mailStream);
NonNullTicks = TickCount(); // We pretend that finishing a mailcheck is a meaningful "event"
return(anyErr);
}
/**********************************************************************
*
**********************************************************************/
Boolean NeedPassword(Boolean check,Boolean send)
{
Boolean needPW;
Str255 s;
Boolean doesAuth = PrefIsSet(PREF_SMTP_DOES_AUTH);
Boolean authOK = !PrefIsSet(PREF_SMTP_AUTH_NOTOK);
Boolean xtndXmit = PrefIsSet(PREF_POP_SEND);
Boolean doggieStyle = PrefIsSet(PREF_KERBEROS);
Boolean gave530 = ShouldSMTPAuth();
if (!(*CurPers)->doMeNow) return(False);
needPW = !PrefIsSet(PREF_KERBEROS) && !UUPCIn && check && *GetPOPPref(s); /* canonical; a POP check */
// Are we going to send in a potentially auth-able way?
if (!needPW && (doesAuth || xtndXmit) && send && !UUPCOut && !doggieStyle)
{
needPW = authOK || xtndXmit;
// We'd like to auth.
if (!xtndXmit && doesAuth)
{
// If the server says yes but the user says no,
// better ask the user to change their mind
if (gave530 && !authOK)
{
PCopy(s,(*CurPers)->name);
switch (ComposeStdAlert(Note,RECONSIDER_AUTH,s))
{
// user will give us the password. Yippee.
case kAlertStdAlertOKButton:
SetPref(PREF_SMTP_AUTH_NOTOK,NoStr);
needPW = true;
break;
// user wants to abort the send. Ok.
case kAlertStdAlertCancelButton:
(*CurPers)->sendMeNow = false;
(*CurPers)->doMeNow = (*CurPers)->checkMeNow;
break;
// user thinks he can spit at god. Good luck.
default:
needPW = false;
break;
}
}
}
}
return(needPW && !*(*CurPers)->password);
}
typedef enum {
sxfOK = 1,
sxfCancel,
sxfCheck,
sxfSend,
sxfServDel,
sxfServFetch,
sxfNuke,
sxfNukeHard,
sxfStub,
sxfIcon=11,
sxfLabel=14,
sxfVertical,
sxfList=17
} SXFDialogEnum;
/**********************************************************************
* POPHostLimit - limit the domain of hosts the user is allowed to connect to
**********************************************************************/
OSErr POPHostLimit(void)
{
Str255 user,host;
Str63 sub;
short i;
if (*GetRString(sub,OnlyHostsStrn+2))
{
GetPOPInfo(user,host);
for (i=2;*GetRString(sub,OnlyHostsStrn+i);i++)
if (PFindSub(sub,host)) return(noErr);
WarnUser(OnlyHostsStrn+1,0);
return(fnfErr);
}
return(noErr);
}
/************************************************************************
* SpecialXferFilter - support arrow keys in special xfer dialog
************************************************************************/
pascal Boolean SpecialXferFilter(DialogPtr dgPtr,EventRecord *event,short *item)
{
short key = (event->message & charCodeMask);
ControlHandle cntl=nil;
ListHandle list;
short type;
Rect r;
short selectMe;
short last;
static uLong lastTicks;
static Point lastWhere;
Type2Select(event);
if (event->what==mouseDown)
{
if (TickCount()-lastTicks < GetDblTime())
if (ABS(event->where.h-lastWhere.h)<GetRLong(DOUBLE_TOLERANCE))
if (ABS(event->where.v-lastWhere.v)<GetRLong(DOUBLE_TOLERANCE))
{
Point localWhere = event->where;
GlobalToLocal(&localWhere);
GetDialogItem(dgPtr,sxfList,&type,(void*)&cntl,&r);
if (cntl)
if (list = LCGetList(cntl))
{
Rect rView = (*list)->rView;
if (PtInRect(localWhere,&rView) && Next1Selected(0,list))
{
*item = 1; // hit ok on 2-click
return true;
}
}
}
lastTicks = event->when;
lastWhere = event->where;
}
if ((event->what==keyDown || event->what==autoKey) && (key==upArrowChar || key==downArrowChar))
{
GetDialogItem(dgPtr,sxfList,&type,(void*)&cntl,&r);
if (cntl)
if (list = LCGetList(cntl))
{
if (key==upArrowChar)
{
selectMe = Next1Selected(0,list);
selectMe = MAX(selectMe-1,1);
}
else
{
for (last=selectMe=Next1Selected(0,list);selectMe=Next1Selected(selectMe,list);last=selectMe);
if (!last)
last = (*list)->dataBounds.bottom;
selectMe = MIN(last+1,(*list)->dataBounds.bottom);
}
SelectSingleCell(selectMe-1,list);
event->what=nullEvent;
Draw1Control(cntl);
}
}
// type 2 select
else if (event->what==keyDown && !(event->modifiers&cmdKey) && Type2SelString[0])
{
GetDialogItem(dgPtr,sxfList,&type,(void*)&cntl,&r);
if (cntl)
if (list = LCGetList(cntl))
{
PersHandle pers;
short i;
short score,selectScore;
UPtr spot;
selectMe = i = 0;
selectScore = 1000;
for (pers=PersList;pers;pers=(*pers)->next)
{
i++;
spot = PFindSub(Type2SelString,LDRef(pers)->name);
if (spot)
{
// we want the leftmost match
score = spot-(*pers)->name-1;
// but if it's in the middle of a word, it's less tasty
if (spot>(*pers)->name+1 && IsWordChar[spot[-1]]) score += 100;
if (score<selectScore)
{
selectMe = i;
selectScore = score;
}
}
UL(pers);
}
if (selectMe)
{
SelectSingleCell(selectMe-1,list);
event->what=nullEvent;
Draw1Control(cntl);
}
}
}
return(DlgFilter(dgPtr,event,item));
}
/**********************************************************************
* SpecialXfer - set the special transfer flags
**********************************************************************/
OSErr SpecialXfer(struct XferFlags *flags)
{
MyWindowPtr dlogWin;
DialogPtr dlog;
short res;
Boolean stub, fetch, del;
PersHandle pers;
short type;
Rect r;
ControlHandle cntl;
short item;
ListHandle list;
Point c;
Str63 name;
short nPers = PersCount();
DECLARE_UPP(SpecialXferFilter,ModalFilter);
short vOffset = 0;
#define N_PERS_START 9
#define N_PERS_MAX 25
SetDialogFont(SmallSysFontID());
dlogWin = GetNewMyDialog(SPECIAL_CHECK_ALRT,nil,nil,InFront);
SetDialogFont(0);
dlog = GetMyWindowDialogPtr (dlogWin);
if (!dlog) return(WarnUser(MEM_ERR,ResError()));
SetPort(GetDialogPort(dlog));
ConfigFontSetup(dlogWin);
/*
* add multiple personality stuff
*/
if (nPers>1)
{
AppendDITL(dlog, GetResource('DITL',PERS_LIST_DITL), overlayDITL);
GetDialogItem(dlog,sxfList,&type,(void*)&cntl,&r);
if (nPers > N_PERS_START)
{
vOffset = MIN(nPers,N_PERS_MAX) - N_PERS_START;
vOffset *= RectHi(r)/N_PERS_START;
r.bottom += vOffset;
SetDialogItem(dlog,sxfList,type,(void*)cntl,&r);
if ((type&ctrlItem) == ctrlItem)
MySetCntlRect((ControlHandle)cntl,&r);
GetPortBounds(GetDialogPort(dlog),&r);
SizeWindow(GetDialogWindow(dlog),RectWi(r),RectHi(r)+vOffset,true);
}
// Move old panel to right
GetDialogItem(dlog,sxfVertical,&type,(void*)&cntl,&r);
r.bottom += vOffset;
SetDialogItem(dlog,sxfVertical,type,(void*)cntl,&r);
if ((type&ctrlItem) == ctrlItem)
MySetCntlRect((ControlHandle)cntl,&r);
for (item=1;item<sxfVertical;item++)
{
if (item!=sxfIcon) OffsetDItem(dlog,item,r.left,0);
if (item==sxfIcon || item==sxfOK || item==sxfCancel)
OffsetDItem(dlog,item,0,vOffset);
}
GetDialogItem(dlog,sxfList,&type,(void*)&cntl,&r);
if (list = LCGetList(cntl))
{
r = (*list)->rView;
LSize(RectWi(r),RectHi(r)+vOffset,list);
c.h = c.v = 0;
LAddRow(nPers,0,list);
for (pers=PersList;pers;pers=(*pers)->next)
{
PSCopy(name,(*pers)->name);
LSetCell(name+1,*name,c,list);
if ((*pers)->doMeNow) LSetSelect(True,c,list);
c.v++;
}
}
}
/*
* fix the dialog controls
*/
ReplaceAllControls(dlog);
/*
* set the appropriate boxes
*/
fetch = del = False;
for (pers=PersList;pers;pers=(*pers)->next)
{
if (!fetch) fetch = GetResource(PERS_POPD_TYPE(pers),FETCH_ID)!=nil;
if (!del) del = GetResource(PERS_POPD_TYPE(pers),DELETE_ID)!=nil;
}
EnableDItemIf(dlog,sxfServFetch,fetch);
EnableDItemIf(dlog,sxfServDel,del);
SetDItemState(dlog,sxfCheck,flags->check);
SetDItemState(dlog,sxfSend,flags->send);
SetDItemState(dlog,sxfServDel,del && flags->servDel);
SetDItemState(dlog,sxfServFetch,fetch && flags->servFetch);
SetDItemState(dlog,sxfNuke,flags->nuke);
SetDItemState(dlog,sxfNukeHard,flags->nukeHard);
SetDItemState(dlog,sxfStub,flags->stub);
/*
* put up the alert
*/
StartMovableModal(dlog);
ShowWindow(GetDialogWindow(dlog));
SetMyCursor(arrowCursor);
INIT_UPP(SpecialXferFilter,ModalFilter);
do
{
MovableModalDialog(dlog,SpecialXferFilterUPP,&res);
if (res>sxfOK&&res<=sxfStub) SetDItemState(dlog,res,!GetDItemState(dlog,res));
if (res==sxfStub)
{
stub = GetDItemState(dlog,sxfStub);
EnableDItemIf(dlog,sxfCheck,!stub);
EnableDItemIf(dlog,sxfSend,!stub);
EnableDItemIf(dlog,sxfServDel,del && !stub);
EnableDItemIf(dlog,sxfServFetch,fetch && !stub);
EnableDItemIf(dlog,sxfNuke,!stub);
EnableDItemIf(dlog,sxfNukeHard,!stub);
}
}
while (res>sxfCancel);
if (PersCount()>1)
{
item = 1;
for (pers=PersList;pers;pers=(*pers)->next)
if (Cell1Selected(item++,list))
(*pers)->doMeNow = (*pers)->checkMeNow = (*pers)->sendMeNow = True;
else
(*pers)->doMeNow = (*pers)->checkMeNow = (*pers)->sendMeNow = False;
}
/*
* read the flags
*/
flags->stub = GetDItemState(dlog,sxfStub);
flags->check = !flags->stub && GetDItemState(dlog,sxfCheck);
flags->send = !flags->stub && GetDItemState(dlog,sxfSend);
flags->servDel = del && !flags->stub && GetDItemState(dlog,sxfServDel);
flags->servFetch = fetch && !flags->stub && GetDItemState(dlog,sxfServFetch);
flags->nuke = !flags->stub && GetDItemState(dlog,sxfNuke);
flags->nukeHard = !flags->stub && GetDItemState(dlog,sxfNukeHard);
/*
* close
*/
EndMovableModal(dlog);
DisposeDialog_(dlog);
return(res==1?noErr:userCancelled);
}
/**********************************************************************
* GoOnline - does the user wish to go online?
**********************************************************************/
OSErr GoOnline(void)
{
#ifdef TWO
OnlineAlertEnum button = ReallyDoAnAlert(ONLINE_ALRT,Note);
if (button==olaOnline) SetPref(PREF_OFFLINE,"");
if (button==olaOnline || button==olaConnect) return(noErr);
#endif
return(OFFLINE);
}
/************************************************************************
* SendTheQueue - send queued messages, assuming cnxn is setup
************************************************************************/
short SendTheQueue(TransStream stream, XferFlags flags)
{
WindowPtr tocMessWinWP;
TOCHandle tocH=nil;
int sumNum = -1;
Str255 server;
long port;
MessHandle messH;
Handle table;
short err,code=0;
short tableId;
uLong gmtSecs = GMTDateTime();
short defltTableId;
UPtr tablePtr=NuPtr(256);
short lastId = 0;
uLong lastSig = 0xffffffff;
short stayed = 0;
long count;
CSpecHandle fccList = NuHandle(0);
Boolean openedFilters=False;
#ifdef THREADING_ON
TOCHandle realTocH=nil;
Boolean inThread = InAThread();
short realSumNum = -1;
#endif
static uLong sessionID;
uLong numSent;
uLong beforeBytes,
actualBytes,
approxBytes;
uLong beforeTicks;
Str255 s;
PersHandle relayPers = nil;
#ifdef THREADING_ON
if (inThread)
{
SetCurrentTaskKind(SendingTask);
RemoveTaskErrors(SendingTask,(*CurPers)->persId);
}
if (PersCount()==1) GetRString(s,SENDING_MAIL);
else
{
LDRef(CurPers);
ComposeRString(s,PERS_SENDING_MAIL,(*CurPers)->name);
UL(CurPers);
}
ProgressMessage(kpTitle,s);
#endif
/*
* clear this stupid error condition
*/
CommandPeriod = False;
defltTableId = GetPrefLong(PREF_OUT_XLATE);
if (!NewTables || defltTableId==DEFAULT_TABLE) defltTableId = TransOutTablID();
if (!PrefIsSet(PREF_NO_FLATTEN))
{
Flatten = GetFlatten();
}
// If using a relay personality, switch to it now
if (relayPers = SMTPRelayPers()) PushPers(relayPers);
#ifdef ESSL
stream->ESSLSetting = GetPrefLong(PREF_SSL_SMTP_SETTING);
if(stream->ESSLSetting & esslUseAltPort)
port = GetRLong(SMTP_SSL_PORT);
else
#endif
port = GetSMTPPort();
GetSMTPInfoLo(server,&port);
#ifdef POPSECURE
if (!POPSecure && (err=VetPOP())) goto done; //not used. Must change for multi-connect
#endif
ComposeLogR(LOG_SEND,nil,START_SEND_LOG,server,port);
AuditSendStart(++sessionID,(*CurPers)->persId,flags.isAuto);
numSent = 0;
StartStreamAudit(stream,kAuditBytesSent);
#ifdef TWO
if (!UUPCOut && !UUPCIn && PrefIsSet(PREF_POP_SEND))
{
#ifdef ESSL
stream->ESSLSetting = GetPrefLong(PREF_SSL_POP_SETTING);
if(stream->ESSLSetting & esslUseAltPort)
port = GetRLong(POP_SSL_PORT);
else
#endif
port = PrefIsSet(PREF_KERBEROS)?GetRLong(KERB_POP_PORT):GetRLong(POP_PORT);
if (err=GetPOPInfoLo(server+128,server,&port)) goto done;
if (err=StartPOP(stream,server,port)) goto done;
POPIntroductions(stream,server+128,nil);
if (POPrror()) goto done;
}
else
#endif
err=StartSMTP(stream,server,port);
// if using a relay personality, kill it now
if (relayPers) {PopPers();relayPers=nil;}
if (err) goto done;
if (!(tocH=GetOutTOC())) goto done;
if (!NewTables && !TransOut && (table=GetResource_('taBL',TransOutTablID())))
{
BMD(*table,tablePtr,256);
TransOut = tablePtr;
lastId = TransOutTablID();
}
count = (*CurPers)->sendQueue;
if (!inThread) TotalQueuedSize = FindTotalQueuedSize(tocH,gmtSecs);
ByteProgress(nil,0,TotalQueuedSize);
#ifdef THREADING_ON
if (inThread || (openedFilters=!RegenerateFilters()))
#else
if (openedFilters=!RegenerateFilters())
#endif
for (sumNum=0; sumNum<(*tocH)->count && code<600 && !CommandPeriod && !EjectBuckaroo; sumNum++)
if (!(*tocH)->sums[sumNum].messH && IsQueued(tocH,sumNum) && (*tocH)->sums[sumNum].persId==(*CurPers)->persId && (*tocH)->sums[sumNum].seconds<=gmtSecs)
{
//TimeStamp(tocH,sumNum,0,0);
// ProgressR(NoBar,count--,0,LEFT_TO_TRANSFER,nil);
ProgressR(NoChange,count--,0,LEFT_TO_TRANSFER,nil); // clarence
/*
* ready a translation table, if needed
*/
tableId = EffectiveTID((*tocH)->sums[sumNum].tableId);
if (tableId != lastId)
{
if (tableId!=NO_TABLE && tablePtr && (table = GetResource_('taBL',tableId)))
{
BMD(*table,tablePtr,256);
TransOut = tablePtr;
lastId = tableId; /* so we don't have to fetch it next time */
}
else
TransOut = nil; /* no table */
}
/*
* signature
*/
if (lastSig != (*tocH)->sums[sumNum].sigId)
GrabSignature(lastSig = (*tocH)->sums[sumNum].sigId);
beforeBytes = GetProgressBytes();
beforeTicks = TickCount();
/*
* actually send the message
*/
if (!(code=(UUPCOut ? UUPCSendMessage(tocH,sumNum,fccList) :
MySendMessage(stream,tocH,sumNum,fccList))))
{
OutTypeEnum outType;
#ifdef NAG
#else
RegisterSuccess(1); // note success in registration
#endif
numSent++;
messH = (*tocH)->sums[sumNum].messH;
if (outType = (*tocH)->sums[sumNum].outType)
UpdateNumStat(outType==OUT_FORWARD?kStatForwardMsg:outType==OUT_REPLY?kStatReplyMsg:kStatRedirectMsg,1);
// adjust progress bar
if (messH)
{
long rate;
actualBytes = GetProgressBytes() - beforeBytes;
rate = (actualBytes*600)/((TickCount()-beforeTicks+1)*1024);
ComposeLogS(LOG_TPUT,nil,"\p%d.%d KBps",rate/10,rate%10);
approxBytes = (ApproxMessageSize(messH) K);
if (actualBytes < approxBytes)
ByteProgress(nil,actualBytes - approxBytes,0);
else
ByteProgressExcess(approxBytes - actualBytes);
}
SetState(tocH,sumNum,SENT);
if (!PrefIsSet(PREF_CORVAIR) && WriteTOC(tocH)) {code=600;break;} /* happy, Dave? */
#ifdef TWO
#ifdef THREADING_ON
/* fcc and filtering of outgoing messages should be done after all messages sent in the main thread */
if (!inThread)
{
#endif
if (fccList && GetHandleSize_(fccList))
DoFcc(tocH,sumNum,fccList);
if (messH)
{
err = FilterMessage(flkOutgoing,tocH,sumNum);
if (err == euFilterXfered)
{
sumNum--;
continue;
}
else stayed++;
}
#ifdef THREADING_ON
}
else
stayed++;
#endif
#else
stayed++;
#endif
#ifdef THREADING_ON
/* don't delete message if we're in a thread. we'll do that from the main thread. */
if (!inThread && ((*tocH)->sums[sumNum].flags&FLAG_KEEP_COPY)==0)
#else
if (((*tocH)->sums[sumNum].flags&FLAG_KEEP_COPY)==0)
#endif
{
if (messH && MessOptIsSet(messH,OPT_ATT_DEL)) CompAttDel(messH);
if (messH && (*messH)->win) CloseMyWindow(GetMyWindowWindowPtr((*messH)->win));
DeleteMessage(tocH,sumNum,False);
sumNum--; /* back up, since we deleted the message */
RedoTOC(tocH); /* keep nit-pickers happy */
stayed--;
}
else if (messH && (*messH)->win) CloseMyWindow(GetMyWindowWindowPtr((*messH)->win));
}
else
{
if ((*tocH)->sums[sumNum].messH) {
tocMessWinWP = GetMyWindowWindowPtr ((*(*tocH)->sums[sumNum].messH)->win);
if (tocMessWinWP && !IsWindowVisible (tocMessWinWP))
CloseMyWindow(tocMessWinWP);
}
if (IsAddrErr(code))
{
(*tocH)->sums[sumNum].flags |= FLAG_ADDRERR;
OpenAddrErrs = true;
}
}
#ifdef THREADING_ON
// update outgoing message status if we're in a thread
if (inThread)
{
if (realTocH=GetRealOutTOC())
{
realSumNum = FindSumByHash(realTocH,(*tocH)->sums[sumNum].uidHash);
if (realSumNum!=-1)
{
SetState(realTocH,realSumNum,(*tocH)->sums[sumNum].state);
if ((*tocH)->sums[sumNum].state==MESG_ERR)
{
(*realTocH)->sums[realSumNum].flags |= FLAG_ADDRERR;
OpenAddrErrs = true;
}
(*realTocH)->sums[realSumNum].flags |= FLAG_UNFILTERED;
#ifdef BATCH_DELIVERY_ON
NeedToFilterOut++;
#endif
if (!PrefIsSet(PREF_CORVAIR))
WriteTOC(realTocH);
}
}
}
#endif
}
done:
if (relayPers) PopPers();
StopStreamAudit(stream);
AuditSendDone(sessionID,numSent,ReportStreamAudit(stream));
UpdateNumStat(kStatSentMail,numSent);
if (openedFilters) FiltersDecRef();
ZapHandle(fccList);
ProgressMessageR(kpSubTitle,CLEANUP_CONNECTION);
if (tablePtr) ZapPtr(tablePtr);
ZapPtr(Flatten);
TransOut = nil;
#ifdef TWO
if (!UUPCOut && !UUPCIn && PrefIsSet(PREF_POP_SEND))
{
(void) EndPOP(stream);
}
else
#endif
(void) EndSMTP(stream);
if (tocH && (*tocH)->win && sumNum>=0)
BoxSelectAfter((*tocH)->win,sumNum);
FlushTOCs(True,False); /* save memory, in case Out and Trash are large,
and we're going on to do a check */
NotifyHelpers(stayed,eMailSent,nil);
return(err);
}
/************************************************************************
* FindTotalQueuedSize - find out how big all the messages are
************************************************************************/
long FindTotalQueuedSize(TOCHandle tocH,long gmtSecs)
{
short sumNum;
long size = 0;
MyWindowPtr win;
for (sumNum=0; sumNum<(*tocH)->count; sumNum++)
if (!(*tocH)->sums[sumNum].messH && IsQueued(tocH,sumNum) && (*tocH)->sums[sumNum].persId==(*CurPers)->persId && (*tocH)->sums[sumNum].seconds<=gmtSecs)
{
if (win=GetAMessage(tocH,sumNum,nil,nil,false))
{
size += ApproxMessageSize(Win2MessH(win)) K;
CloseMyWindow(GetMyWindowWindowPtr (win));
}
}
return(size);
}
/**********************************************************************
* CompAttDel - delete attachments with an outgoing message
**********************************************************************/
void CompAttDel(MessHandle messH)
{
short index;
FSSpec spec;
OSErr err;
for (index=1;!(err=GetIndAttachment(messH,index,&spec,nil));index++)
FSpTrash(&spec);
}
/**********************************************************************
* DoFcc - do the fcc's for a message
**********************************************************************/
OSErr DoFcc(TOCHandle tocH,short sumNum,CSpecHandle list)
{
CSpec spec;
short n = HandleCount(list);
OSErr err=noErr;
OSErr oneErr;
UseFeature (featureFcc);
while (n--)
{
spec = (*list)[n];
if (oneErr=MoveMessageLo(tocH,sumNum,&spec.spec,True,false,true))
{
(*tocH)->sums[sumNum].flags |= FLAG_KEEP_COPY;
TOCSetDirty(tocH,true);
if (!err) err = oneErr;
}
}
SetHandleBig_(list,0);
return(err);
}
/************************************************************************
* CheckForMail - need I say more?
************************************************************************/
short CheckForMail(TransStream stream,short *gotSome,XferFlags *flags)
{
long interval=TICKS2MINS * GetPrefLong(PREF_INTERVAL);
Handle table;
short err = 0;
Str255 s;
#ifdef DEBUG
if (BUG15)
{
Str63 dts;
Dprintf("\p;log Log.%p;g",LocalDateTimeShortStr(dts));
}
#endif
#ifdef THREADING_ON
if (InAThread())
{
SetCurrentTaskKind(CheckingTask);
RemoveTaskErrors(CheckingTask,(*CurPers)->persId);
}
if (PersCount()==1) GetRString(s,CHECKING_MAIL);
else
{
LDRef(CurPers);
ComposeRString(s,PERS_CHECKING_MAIL,(*CurPers)->name);
UL(CurPers);
}
ProgressMessage(kpTitle,s);
#endif
Headering = flags->stub;
/*
* is there enough room on this volume?
*/
if (err=VolumeMargin(MailRoot.vRef,0)) return(WarnUser(NOT_ENOUGH_ROOM,err));
/*
* clear this stupid error condition
*/
CommandPeriod = False;
/*
* you know, I don't remember why this was done, but I'm going to
* leave it alone for now
*/
ResetAlertStage ();
/*
* set up translation table
*/
if (!NewTables && !TransIn && (table=GetResource_('taBL',TRANS_IN_TABL)))
{
HNoPurge_(table);
if (TransIn=NuPtr(256)) BMD(*table,TransIn,256);
HPurge(table);
}
/*
* check mail
*/
err = UUPCIn ? GetUUPCMail(True,gotSome) : GetMyMail(stream,True,gotSome,flags);
#ifdef NAG
#else
if (gotSome) RegisterSuccess(2);
#endif
#ifdef DEBUG
if (BUG15) Dprintf("\p%d;sc;g",err);
#endif
/*
* get rid of table
*/
if (TransIn) {ZapPtr(TransIn); TransIn=nil;}
#ifdef DEBUG
if (BUG15)
{
Str63 dts;
Dprintf("\pp;log;g",LocalDateTimeShortStr(dts));
}
#endif
return(err);
}
/**********************************************************************
* ResetCheckTime - reset the mail check interval
**********************************************************************/
void ResetCheckTime(Boolean force)
{
PushPers(CurPers);
for (CurPers=PersList;CurPers;CurPers=(*CurPers)->next)
ResetPersCheckTime(force);
PopPers();
}
/************************************************************************
* ResetPersCheckTime - reset the mail check interval for a personality
************************************************************************/
void ResetPersCheckTime(Boolean force)
{
long interval = PrefIsSet(PREF_AUTO_CHECK) ? TICKS2MINS * GetPrefLong(PREF_INTERVAL) : 0;
if (interval)
{
/*
* setup the initial check interval
*/
if (!(*CurPers)->checkTicks) (*CurPers)->checkTicks = TickCount();
/*
* manage the mail check interval
*/
if (force&&(*CurPers)->doMeNow || (*CurPers)->checked)
{
if ((*CurPers)->checkTicks+interval<TickCount()+45*60)
{
(*CurPers)->checkTicks += interval*((TickCount()-(*CurPers)->checkTicks+1)/interval);
if ((*CurPers)->checkTicks+interval<TickCount()+45*60) (*CurPers)->checkTicks += interval;
}
}
}
else (*CurPers)->checkTicks = 0;
}
/************************************************************************
* NotifyNewMail - notify the user that new mail has arrived, via the
* notification manager.
************************************************************************/
// DO NOT FIX THIS ROUTINE
// IF IT MUST BE FIXED, THROW IT AWAY AND REWRITE IT & ALL RELATED TO IT
// SD 8/20/02
void NotifyNewMail(short gotSome,Boolean noXfer,TOCHandle tocH, FilterPB *fpbDelivery)
{
NotifyNewMailLo(gotSome, noXfer, tocH, fpbDelivery, true);
}
void NotifyNewMailLo(short gotSome,Boolean noXfer,TOCHandle tocH, FilterPB *fpbDelivery, Boolean OpenIn)
{
Boolean justTrash = False;
Boolean soundAnyway = False;
WindowPtr oldFront = FrontWindow_();
FilterPB fpb;
// display NoNewMail alert if no mail was received, and no other check threads are running
if (!gotSome && !POPrror() && !IMAPCheckThreadRunning && !CheckThreadRunning && !gNewMessages)
{
CloseProgress();
if (PrefIsSet(PREF_NEW_ALERT))
{
#ifdef NONEWMAIL_ALERTS
(void) ReallyDoAnAlert(NO_MAIL_ALRT,Normal);
TendNotificationManager(true);
#endif //NONEWMAIL_ALERTS
}
}
else if (gotSome || gNewMessages)
{
CommandPeriod = False; /* if mail check cancelled, don't cancel filtering */
if (fpbDelivery && tocH)
{
FilterPostprocess(flkIncoming,fpbDelivery);
gotSome = fpbDelivery->notify;
soundAnyway = fpbDelivery->doNotifyThing > 0;
}
else if (tocH && !InitFPB(&fpb,false,true))
{
if ((*tocH)->imapTOC) // filter flagged messages only if this is an IMAP mailbox
{
// Score the incoming mail all at once.
// This is ok, we're in the foreground anyway.
if (HasFeature(featureJunk) && JunkPrefBoxHold() && CanScoreJunk())
{
JunkScoreIMAPBox(tocH, -1, -1, true);
MoveToIMAPJunk(tocH, -1, GetRLong(JUNK_MAILBOX_THRESHHOLD), &fpb);
}
// filter what's left over
FilterFlaggedMessages(flkIncoming, tocH, &fpb);
//
// clean up fpb
//
// after an IMAP mailcheck, if messages aren't to be filtered anywhere, the list of mailboxes to be opened
// is allocated, but zero. FilterPostprocess will do a GetSpecialTOC(IN), which causes the In mailbox to be
// opened even though it doesn't need to be. This causes some window layering confusion in the Carbon version. -jdboyd
//
if (fpb.mailbox && (GetHandleSize(fpb.mailbox)==0)) ZapHandle(fpb.mailbox);
// Show NoNewMail if no mail arrived, and there's no other check threads running
// only do this if a manual IMAP check happened recently
if (gWasManualIMAPCheck && !IMAPCheckThreadRunning && !CheckThreadRunning && !gNewMessages && !NeedToFilterIn && !NeedToNotify && !CountFlaggedMessages(tocH))
{
gWasManualIMAPCheck = false;
NoNewMailMe = true;
}
}
else
FilterMessagesFrom(flkIncoming,tocH,(*tocH)->count-gotSome,&fpb,noXfer);
FilterPostprocess(flkIncoming,&fpb);
gotSome = fpb.notify;
soundAnyway = fpb.doNotifyThing > 0;
// keep track of all new messages we're receiving
gNewMessages += gotSome;
// make sure notification happens if soundAnyway
if (soundAnyway) gNewMessages++;
}
// process any reg files we received ...
ProcessReceivedRegFiles();
if (!gotSome && !soundAnyway && !gNewMessages) return;
/*
* let the helper apps know
*/
if (gotSome)
{
if ((tocH && (*tocH)->imapTOC) || (tocH = GetRealInTOC()))
{
NotifyHelpers(gotSome,eMailArrive,nil);
if (OpenIn && !PrefIsSet(PREF_NO_OPEN_IN))
{
WindowPtr tocWinWP = GetMyWindowWindowPtr ((*tocH)->win);
ShowBoxAt(tocH,(*tocH)->previewPTE ? -1:FumLub(tocH),OpenBehindMePlease());
if (PrefIsSet(PREF_ZOOM_OPEN)) ReZoomMyWindow(tocWinWP);
UpdateMyWindow(tocWinWP);
}
}
}
// display the new mail alert if there are no check threads running, and we received some new messages.
if (CheckThreadRunning || IMAPCheckThreadRunning || !gNewMessages) return;
else gNewMessages = 0;
AttentionNeeded = true;
if (PrefIsSet(PREF_NEW_SOUND)) NewMailSound();
if (InBG && PrefIsSet(PREF_NEW_ALERT) || !PrefIsSet(PREF_NO_APPLE_FLASH))
{
if (MyNMRec) return; /* already done */
MyNMRec = New(struct NMRec);
if (!MyNMRec) return; /* couldn't allocate memory (bad) */
WriteZero(MyNMRec,sizeof(*MyNMRec));
MyNMRec->qType = nmType;
MyNMRec->nmMark = 1;
MyNMRec->nmRefCon = TickCount();
if (!PrefIsSet(PREF_NO_APPLE_FLASH))
{
GetIconSuite(&MyNMRec->nmIcon,FLAG_SICN,svAllSmallData);
if (MyNMRec->nmIcon) HNoPurge(MyNMRec->nmIcon);
}
if (InBG && PrefIsSet(PREF_NEW_ALERT))
{
Str255 scratch;
GetRString(scratch,NEW_MAIL);
MyNMRec->nmStr = NuPtr(*scratch+1);
if (MyNMRec->nmStr) PCopy(MyNMRec->nmStr,scratch);
CloseProgress();
}
if (NMInstall(MyNMRec))
{
if (MyNMRec->nmIcon) DisposeIconSuite(MyNMRec->nmIcon,True);
if (MyNMRec->nmSound) HPurge(MyNMRec->nmSound);
ZapPtr(MyNMRec->nmStr);
ZapPtr(MyNMRec);
}
}
if (!InBG && PrefIsSet(PREF_NEW_ALERT))
{
CloseProgress();
(void) ReallyDoAnAlert(NEW_MAIL_ALRT,Normal);
TendNotificationManager(true);
}
}
if (!InBG && oldFront && FrontWindow_()!=oldFront)
FlushEvents(everyEvent&~highLevelEventMask,0);
}
/************************************************************************
* OpenBehindMePlease - Travel down the windowlist until we find where we
* should open new windows
************************************************************************/
WindowPtr OpenBehindMePlease(void)
{
MyWindowPtr win;
WindowPtr winWP,
frontWP,
returnWinWP = nil;
frontWP = MyFrontNonFloatingWindow();
switch (GetPrefLong(PREF_OPEN_WHERE))
{
case 0: // last comp on top
for (winWP = frontWP; winWP; winWP = GetNextWindow (winWP))
if (IsWindowVisibleClassic (winWP))
if (GetWindowKind(winWP)==COMP_WIN) returnWinWP = winWP;
else break;
break;
case 1: // on top
returnWinWP = nil;
break;
case 2: // behind In
for (winWP = frontWP; winWP; winWP = GetNextWindow (winWP))
if (IsWindowVisibleClassic (winWP))
if (GetWindowKind(winWP)==MBOX_WIN)
if ((*(TOCHandle)GetWindowPrivateData(winWP))->which==IN)
{
returnWinWP = winWP;
break;
}
if (returnWinWP) break;
// if no visible In box, fall through to behind current mailbox
case 3: // behind current mailbox
for (winWP = frontWP; winWP; winWP = GetNextWindow (winWP)) if (IsWindowVisibleClassic (winWP)) break;
win = GetWindowMyWindowPtr (winWP);
if (GetWindowKind(winWP)==MBOX_WIN || GetWindowKind(winWP)==CBOX_WIN)
{
returnWinWP = winWP;
break;
}
else if (GetWindowKind(winWP)==MESS_WIN || GetWindowKind(winWP)==COMP_WIN)
{
returnWinWP = winWP; // just in case the toc's not visible
win = (*(*Win2MessH(win))->tocH)->win;
winWP = GetMyWindowWindowPtr (win);
if (IsWindowVisibleClassic (winWP))
{
returnWinWP = winWP;
break;
}
}
break;
case 4: // in the back
for (winWP = frontWP; winWP; winWP = GetNextWindow (winWP))
if (IsWindowVisibleClassic (winWP))
returnWinWP = winWP;
break;
case 5: // behind frontmost window
for (winWP = frontWP; winWP; winWP = GetNextWindow (winWP))
if (IsWindowVisibleClassic (winWP))
{
returnWinWP = winWP;
break;
}
break;
}
// If there's a modal window open, make sure the next window isn't opened on top of it.
if ((returnWinWP==nil) && ModalWindow) returnWinWP = ModalWindow;
win = GetWindowMyWindowPtr (frontWP);
if (win && win->isNag)
returnWinWP = frontWP;
return(returnWinWP);
}
/************************************************************************
* ShowBoxSel - show the mailbox with a selection
************************************************************************/
void ShowBoxAt(TOCHandle tocH,short selectMe,WindowPtr behindWin)
{
WindowPtr tocWinWP = GetMyWindowWindowPtr((*tocH)->win);
if (selectMe>=0) SelectBoxRange(tocH,selectMe,selectMe,False,-1,-1);
RedoTOC(tocH);
ScrollIt((*tocH)->win,0,SortedDescending(tocH)?REAL_BIG:-REAL_BIG);
if (IsWindowVisible(tocWinWP))
{
if (behindWin)
{
if (behindWin!=tocWinWP)
SendBehind(tocWinWP,behindWin);
}
else SelectWindow_(tocWinWP);
}
else
ShowMyWindowBehind(tocWinWP,behindWin);
}
/************************************************************************
* FumLub - find the FumLub
************************************************************************/
short FumLub(TOCHandle tocH)
{
short i;
if (!tocH) return(-1);
RedoTOC(tocH);
if (SortedDescending(tocH)) return(0);
for (i=(*tocH)->count-1;i>=0;i--)
if ((*tocH)->sums[i].state!=UNREAD)
{
i++;
break;
}
return(i<(*tocH)->count ? MAX(i,0) : (*tocH)->count-1);
}
/************************************************************************
* NewMailSound - play the sound for new mail
************************************************************************/
void NewMailSound(void)
{
Str255 name;
GetPref(name,PREF_NEWMAIL_SOUND);
if (!*name)
GetResName(name,'snd ',NEW_MAIL_SND);
PlayNamedSound(name);
}
/************************************************************************
* GrabSignature - read the signature file
************************************************************************/
void GrabSignature(uLong fid)
{
short err;
FSSpec spec;
Accumulator enriched, html;
MyWindowPtr win=nil;
Boolean iOpened = false;
Boolean oldDirty;
Boolean addedIntro;
ZapHandle(eSignature);
ZapHandle(RichSignature);
ZapHandle(HTMLSignature);
if (fid==SIG_NONE) return;
if (AccuInit(&enriched)) return;
if (AccuInit(&html)) {AccuZap(enriched);return;}
if (!(err = SigSpec(&spec,fid)))
if (!(win=FindText(&spec)))
if (win=OpenText(&spec,nil,nil,nil,False,nil,False,False))
iOpened = true;
if (win)
{
PeteGetTextAndSelection(win->pte,&eSignature,nil,nil);
if (!(err=MyHandToHand(&eSignature)))
{
RemoveCharHandle(0,eSignature);
// add sig intro if it's not there
PeteCalcOff(win->pte);
oldDirty = 0!=PeteIsDirty(win->pte);
addedIntro = AddSigIntro(win->pte,eSignature);
// build the styled versions
SigStyled = HasStyles(win->pte,0,PETEGetTextLen(PETE,win->pte),false);
err = BuildEnriched(&enriched,win->pte,nil,PETEGetTextLen(PETE,win->pte),0,nil,False);
if (!err)
{
err = BuildHTML(&html,win->pte,nil,PETEGetTextLen(PETE,win->pte),0,nil,nil,2,nil,nil,nil);
if (!err)
err = HTMLPostamble(&html,False);
}
// remove sig intro from window
if (addedIntro)
{
RemoveSigIntro(win->pte,nil);
if (!oldDirty)
{
PeteCleanList(win->pteList);
win->isDirty = false;
}
}
PeteCalcOn(win->pte);
}
else eSignature = nil;
}
else err = 1;
if (!err)
{
AccuTrim(&enriched);
RichSignature = enriched.data;
enriched.data = 0;
AccuTrim(&html);
HTMLSignature = html.data;
html.data = 0;
}
if (win && iOpened) CloseMyWindow(GetMyWindowWindowPtr(win));
AccuZap(enriched);
AccuZap(html);
if (err) FileSystemError(CANT_READ_SIG,"",err);
}
/************************************************************************
* AddSigIntro - add the sig introducer to a petehandle or a text handle or both
************************************************************************/
Boolean AddSigIntro(PETEHandle pte,UHandle text)
{
Str31 sigIntro;
Boolean didIt = false;
uLong len;
if (!*GetRString(sigIntro,SIG_INTRO)) return(false);
// do the text block first
if (text)
{
if (0>SearchStrHandle(sigIntro,text,0,false,false,nil))
{
len = GetHandleSize(text);
if (len && !IsAllWhitePtr(LDRef(text),len))
{
didIt = true;
UL(text);
SetHandleBig(text,len+*sigIntro);
LDRef(text);
if (!MemError())
{
BMD(*text,*text+*sigIntro,len);
BMD(sigIntro+1,*text,*sigIntro);
}
}
UL(text);
}
}
// now the petehandle
if (pte)
{
PeteGetTextAndSelection(pte,&text,nil,nil);
if (0>SearchStrHandle(sigIntro,text,0,false,false,nil))
{
len = GetHandleSize(text);
if (len && !IsAllWhitePtr(LDRef(text),len))
{
didIt = true;
PETEInsertTextPtr(PETE,pte,0,sigIntro+1,*sigIntro,nil);
}
UL(text);
}
}
// all done
return didIt;
}
/************************************************************************
* RemoveSigIntro - take the sig introducer from a petehandle or a text handle or both
************************************************************************/
Boolean RemoveSigIntro(PETEHandle pte,UHandle text)
{
Str31 sigIntro;
long len;
Boolean didIt = false;
if (!*GetRString(sigIntro,SIG_INTRO)) return(false);
// do the text block first
if (text)
{
len = GetHandleSize(text);
if (len && (len>=*sigIntro && *(long*)(sigIntro+1) == *(long*)*text)) // check first four chars only
{
didIt = true;
BMD(*text+*sigIntro,*text,len-*sigIntro);
SetHandleBig(text,len-*sigIntro);
}
}
// now the petehandle
PeteGetTextAndSelection(pte,&text,nil,nil);
len = GetHandleSize(text);
if (len && (len>=*sigIntro && *(long*)(sigIntro+1) == *(long*)*text)) // check first four chars only
{
didIt = true;
PeteDelete(pte,0,*sigIntro);
}
// all done
return didIt;
}
/************************************************************************
* SigSpec - get the FSSpec for the (standard) signature file
************************************************************************/
OSErr SigSpec(FSSpecPtr spec,long fid)
{
OSErr err;
Str63 s;
short i;
err = SubFolderSpec(SIG_FOLDER,spec);
if (fid<0) return(noErr);
if (!err)
{
if (fid<=1)
{
err = FSMakeFSSpec(spec->vRefNum,spec->parID,
#ifdef TWO
GetRString(s,fid==1?ALT_SIG:SIGNATURE),spec);
#else
GetRString(s,SIGNATURE),spec);
#endif
}
else
{
for (i=SignatureCount();i;i--)
{
GetSignatureName(i,s);
MyLowerStr(s);
if (Hash(s)==fid)
{
err = FSMakeFSSpec(spec->vRefNum,spec->parID,s,spec);
break;
}
}
if (!i) return(fnfErr);
}
}
return(err);
}
/**********************************************************************
* TransmitMessageHi - send a message, possibly with PGP
**********************************************************************/
OSErr TransmitMessageHi(TransStream stream,MessHandle messH,Boolean chatter,Boolean sendDataCmd)
{
OSErr err;
#ifdef OLDPGP
if (SumOf(messH)->flags&(FLAG_ENCRYPT|FLAG_SIGN))
err = PGPSendMessage(stream,messH,chatter);
else
#endif
#ifdef ETL
if ((*messH)->hTranslators)
err = ETLSendMessage(stream,messH,chatter,sendDataCmd);
else
#endif
err = TransmitMessage(stream,messH,chatter,True,True,nil,sendDataCmd);
if (!err) RememberMID(SumOf(messH)->msgIdHash);
return err;
}
/**********************************************************************
* ProcessReceivedRegFiles - process any received registration files
**********************************************************************/
void ProcessReceivedRegFiles(void)
{
if(gRegFiles)
{
FSSpec spec, **list;
long count;
int pnPolicyCode;
Boolean needsRegistration;
list = gRegFiles;
gRegFiles = nil;
for(count = GetHandleSize(list)/sizeof(FSSpec); --count >= 0; )
{
spec = (*list)[count];
// Parse the file we received, but do it silently if a box user has received a reg file
ParseRegFile(&spec,IsBoxUser()?parseMaybeSilent:parseDoDialog,&needsRegistration,&pnPolicyCode);
}
DisposeHandle(list);
}
}
/************************************************************************
* SMTPRelayPers - do we have a relay personality?
************************************************************************/
PersHandle SMTPRelayPers(void)
{
Str63 persName;
if (PrefIsSet(PREF_NO_RELAY_PARTICIPATE)) return nil;
return FindPersByName(GetPref(persName,PREF_RELAY_PERSONALITY));
}
/**********************************************************************
* RememberMID - remember that we sent this message
**********************************************************************/
OSErr RememberMID(uLong midHash)
{
OutgoingMIDListDirty = true;
return AccuAddPtr(&OutgoingMIDList,&midHash,sizeof(midHash));
}
/**********************************************************************
* OutgoingMIDListSave - save our list of outgoing message id's
**********************************************************************/
OSErr OutgoingMIDListSave(void)
{
OSErr err = noErr;
if (OutgoingMIDListDirty)
{
ZapResource(OUTGOING_MSG_MID_LIST,1001);
if (OutgoingMIDList.data)
{
short count = OutgoingMIDList.offset/sizeof(uLong);
short limit = GetRLong(OUTGOING_MID_LIST_SIZE);
if (count>limit)
{
UPtr spot = (UPtr)(((uLong*)*OutgoingMIDList.data) + count - limit);
BMD(spot,*OutgoingMIDList.data,limit*sizeof(uLong));
OutgoingMIDList.offset = limit*sizeof(uLong);
}
AccuTrim(&OutgoingMIDList);
AddMyResource(OutgoingMIDList.data,OUTGOING_MSG_MID_LIST,1001,"");
err = ResError();
if (!err)
{
UpdateResFile(SettingsRefN);
DetachResource(OutgoingMIDList.data);
OutgoingMIDListDirty = false;
}
}
}
return err;
}
/**********************************************************************
* OutgoingMIDListLoad - load our list of outgoing message id's
**********************************************************************/
OSErr OutgoingMIDListLoad(void)
{
OSErr err = noErr;
AccuZap(OutgoingMIDList);
OutgoingMIDListDirty = false;
OutgoingMIDList.data = Get1Resource(OUTGOING_MSG_MID_LIST,1001);
if (OutgoingMIDList.data)
{
OutgoingMIDList.offset = OutgoingMIDList.size = GetHandleSize(OutgoingMIDList.data);
DetachResource(OutgoingMIDList.data);
}
else
err = ResError();
return err;
}
/**********************************************************************
* BadgeTheStupidDock - put a badge on the dock with the # of unread messages in it
**********************************************************************/
void BadgeTheSupidDock(short count, PStr text, Boolean attentionColor)
{
Str15 localText;
static Str15 lastLocalText;
static Boolean lastAttentionColor;
if (text) PSCopy(localText,text);
else if (count < 0) PCopy(localText,lastLocalText);
else if (count) NumToString(count,localText);
else *localText = 0;
if (StringSame(localText,lastLocalText) && lastAttentionColor==attentionColor) return;
lastAttentionColor = attentionColor;
PCopy(lastLocalText,localText);
RestoreApplicationDockTileImage();
if (!*localText) return;
// update stupid dock
{
CGrafPtr theTilePort;
RGBColor color;
PushGWorld();
theTilePort = BeginQDContextForApplicationDockTile();
if ( theTilePort != nil )
{
Rect r;
short wi;
short hi;
short div;
GetPortBounds( theTilePort, &r );
PenNormal();
for (div=3;div<7;div++)
{
TextFont(FontID);
TextSize(hi=RectHi(r)/div);
wi = StringWidth(localText);
if (wi + 4*INSET < RectWi(r)) break;
}
RGBForeColor(GetRColor(&color,attentionColor?BADGE_ATTENTION_COLOR:BADGE_NORMAL_COLOR));
SetRect(&r,r.right-3*INSET-wi,r.bottom-2*INSET-hi,r.right-INSET,r.bottom-INSET);
PaintRect(&r);
MoveTo(r.left+INSET, r.bottom-INSET);
RGBForeColor(GetRColor(&color,BADGE_TEXT_COLOR));
DrawString(localText);
QDFlushPortBuffer( theTilePort, NULL );
EndQDContextForApplicationDockTile( theTilePort );
}
PopGWorld();
}
}
/**********************************************************************
* GlobalUnreadCount - how many unread messages are there?
**********************************************************************/
long GlobalUnreadCount(void)
{
return PrefBadgeOpenBoxes() ? GlobalOpenUnreadCount() : GlobalInUnreadCount();
}
/**********************************************************************
* GlobalOpenUnreadCount - how many unread messages are there in open mailboxes?
**********************************************************************/
long GlobalOpenUnreadCount()
{
long count = 0;
TOCHandle tocH;
for (tocH=TOCList;tocH;tocH=(*tocH)->next)
if (!(*tocH)->virtualTOC && (*tocH)->win && IsWindowVisible(GetMyWindowWindowPtr((*tocH)->win)))
count += TOCUnreadCount(tocH,PrefBadgeRecent());
return count;
}
/**********************************************************************
* GlobalInUnreadCount - how many unread messages are there in all In mailboxes?
**********************************************************************/
long GlobalInUnreadCount()
{
long count = TOCUnreadCount(GetInTOC(),PrefBadgeRecent());
Boolean allIMAPPers = EqualStrRes("\p*",BADGE_PERS_LIST);
PersHandle pers;
Str63 persName;
for (pers=PersList;pers;pers=(*pers)->next)
{
MailboxNodeHandle node;
if (!IsIMAPPers(pers)) continue;
if (!allIMAPPers)
{
if (pers==PersList) GetRString(persName,DOMINANT);
else PSCopy(persName,(*pers)->name);
if (!StrIsItemFromRes(persName,BADGE_PERS_LIST,",")) continue;
}
// ok, we have a candidate
if (node=LocateInboxForPers(pers))
{
FSSpec inboxSpec = (*node)->mailboxSpec;
count += TOCUnreadCount(TOCBySpec(&inboxSpec),PrefBadgeRecent());
}
}
return count;
}