-- AccessOp.mesa
-- edited by Schroeder, May 4, 1981 3:09 PM.
-- edited by Brotz, February 19, 1981 11:28 AM
-- edited by Levin, February 24, 1981 4:51 PM.

DIRECTORY
BodyDefs,
csD: FROM "CoreStreamDefs",
exD: FROM "ExceptionDefs",
gsD: FROM "GlobalStorageDefs",
InlineDefs,
intCommon,
MailParse,
mfD: FROM "MailFormatDefs",
opD: FROM "OperationsDefs",
ovD: FROM "OverviewDefs",
ProcessDefs,
RetrieveDefs,
StringDefs,
vmD: FROM "VirtualMgrDefs";


AccessOp: PROGRAM
IMPORTS csD, exD, gsD, InlineDefs, intC: intCommon, mfD, ProcessDefs,
RetrieveDefs, StringDefs, vmD
EXPORTS opD
= BEGIN


AccessNewMailOperation: PUBLIC PROCEDURE
RETURNS [messagesRead: BOOLEAN] =
BEGIN

bufferBytes: CARDINAL = 512;
breakBuffers: [0..177777B/bufferBytes) = 125;

bufferPages: CARDINAL = (bufferBytes+511)/512;
buffer: POINTER TO PACKED ARRAY OF CHARACTER ← NIL;

parseHandle: mfD.Handle ← NIL;

mailFileSH: csD.StreamHandle ← NIL;
flashWanted: BOOLEAN ← FALSE;

nextCharLimit: CARDINAL;
charCount: CARDINAL;

BoxNextChar: PROCEDURE RETURNS [lastChar: CHARACTER] =
BEGIN
lastChar ← IF charCount >= nextCharLimit
THEN MailParse.endOfInput -- off the end
ELSE csD.Read[mailFileSH];
charCount ← charCount + 1;
END;

BoxBackupChar: PROCEDURE =
BEGIN
charCount ← charCount - 1;
IF charCount < nextCharLimit
THEN csD.SetPosition[mailFileSH, csD.GetPosition[mailFileSH] - 1];
END;

BoxPutChar: PROCEDURE [c: CHARACTER] = {csD.Write[mailFileSH, c]};

BoxPositionNextChar: PROCEDURE [page, byte, limit: CARDINAL] =
BEGIN
nextCharLimit ← limit;
charCount ← 0;
csD.SetPosition[mailFileSH,csD.MapPageByteToPosition[page,byte]];
END;

lastLineUsed: [0..2] ← 0;

ReportProgressString: PROCEDURE [s: STRING] =
BEGIN
lastLineUsed ← 1;
exD.AppendStringToExceptionLine[s, 1
! exD.ExceptionLineOverflow => CONTINUE ];
END;

ReportProgress: PROCEDURE [e: exD.Exception] =
BEGIN
lastLineUsed ← 1;
exD.AppendExceptionToExceptionLine[e, 1
! exD.ExceptionLineOverflow => CONTINUE ];
END;

lastException: exD.Exception ← exD.nil;

ReportException: PROCEDURE [e:exD.Exception,
f:{flash, dontFlash} ← dontFlash] =
BEGIN
IF lastLineUsed = 2
THEN exD.DisplayExceptionLine[lastException, 1]
ELSE lastLineUsed ← lastLineUsed + 1;
exD.DisplayExceptionLine[e, lastLineUsed];
lastException ← e;
IF f = flash THEN flashWanted ← TRUE;
END;


-- Code for AccessNewMailOperation

messagesRead ← FALSE;

BEGIN

serverKnown: BOOLEAN ← FALSE;
utilityString: STRING = [opD.maxTOCStringLength];
-- [MAX[opD.maxTOCStringLength, BodyDefs.maxRNameLength, 60]]
tOCEntry: vmD.TOCFixedPart;
tb: vmD.TOCFixedPartPtr = @tOCEntry;

tb.deleted ← tb.changed ← tb.seen ← FALSE;
tb.mark ← ’ ;
tb.offsetToHeader ← 0;

SELECT RetrieveDefs.MailboxState[intC.retrieveHandle] FROM
badName, badPwd => GOTO credentialsError;
cantAuth => GOTO noServers;
ENDCASE; --ok to try

mailFileSH ← csD.Open[fh:intC.mailFileHandle, type:byte, mode:append, nPages:1
! csD.Error => GOTO csError ];
buffer ← gsD.GetMemoryPages[bufferPages];

DO -- next server

ENABLE csD.Error => BEGIN
UNTIL RetrieveDefs.NextServer[intC.retrieveHandle].noMore DO ENDLOOP;
SELECT reason FROM
ovD.diskFull, ovD.fileTooBig => BEGIN
ReportProgress[exD.overflow];
csD.Reset[mailFileSH ! csD.Error => GOTO csError ];
csD.Checkpoint[mailFileSH ! csD.Error => GOTO csError ];
ReportException[exD.newMailOverfill];
EXIT;
END;
ENDCASE;
GOTO csError;
END;

messages: CARDINAL ← 0;
connected, archivedReported: BOOLEAN ← FALSE;
retProcs: RetrieveDefs.AccessProcs;
whyFailed: RetrieveDefs.FailureReason;

BEGIN
noMore: BOOLEAN;
serverState: RetrieveDefs.ServerState;
ProcessDefs.Yield[];
[noMore, serverState, retProcs] ← RetrieveDefs.NextServer[intC.retrieveHandle];
IF noMore THEN EXIT;
serverKnown ← TRUE;
RetrieveDefs.ServerName[intC.retrieveHandle, utilityString];
ReportProgressString[utilityString];
IF StringDefs.EquivalentString[utilityString, intC.user.registry]
THEN ReportProgressString[" inbox server "L];
IF serverState # notEmpty THEN BEGIN
ReportProgress[IF serverState = empty
THEN exD.dotsEmpty ELSE exD.dotsDidntRespond];
LOOP;
END;
END;

DO -- next message

firstChunk: BOOLEAN ← TRUE;
msgExists, archived, deleted: BOOLEAN;
item: BodyDefs.ItemHeader;

[msgExists, archived, deleted] ← retProcs.nextMessage[intC.retrieveHandle
! RetrieveDefs.Failed => {whyFailed ← why; GOTO retrieveError} ];
IF NOT connected THEN
{connected ← TRUE; ReportProgress[exD.dots]};
IF NOT msgExists THEN EXIT;
IF archived AND NOT archivedReported THEN
{archivedReported ← TRUE; ReportProgressString["archived messages .. "L]};
IF deleted THEN LOOP;
DO
item ← retProcs.nextItem[intC.retrieveHandle
! RetrieveDefs.Failed => {whyFailed ← why; GOTO retrieveError} ];
IF item.type = Text OR item.type = LastItem THEN EXIT;
ENDLOOP;
IF item.type = LastItem THEN LOOP;
ProcessDefs.Yield[];

UNTIL item.length = 0 DO -- message chunks
breakBytes: CARDINAL = breakBuffers*bufferBytes;
tb.textLength ← IF item.length <= breakBytes
THEN InlineDefs.LowHalf[item.length] ELSE breakBytes;
item.length ← item.length - tb.textLength;
IF firstChunk
THEN mfD.CreateStamp[tb, BoxPutChar]
ELSE BEGIN
exD.GetExceptionString[exD.continuationHeader, utilityString];
tb.textLength ← tb.textLength + utilityString.length;
mfD.CreateStamp[tb, BoxPutChar];
csD.WriteBlock[mailFileSH, @utilityString.text, 0, utilityString.length];
END;
firstChunk ← FALSE;
FOR blockCounter: CARDINAL IN [0 .. breakBuffers) DO -- next block
bytes: CARDINAL = retProcs.nextBlock[intC.retrieveHandle,
DESCRIPTOR[buffer, bufferBytes] ! RetrieveDefs.Failed =>
{whyFailed ← why; GOTO retrieveError} ];
IF bytes = 0 THEN EXIT;
csD.WriteBlock[mailFileSH, buffer, 0, bytes];
ENDLOOP; -- next block
ENDLOOP; -- message chunk

messages ← messages + 1;

REPEAT retrieveError => BEGIN
IF NOT connected THEN ReportProgress[exD.dots];
ReportProgress[ SELECT whyFailed FROM
communicationFailure => exD.failedSemi,
noSuchServer => exD.badServerNameSemi,
connectionRejected => exD.busySemi,
badCredentials => exD.badCredentialsSemi,
ENDCASE => exD.unknownErrorSemi ];
csD.Reset[mailFileSH];
csD.Checkpoint[mailFileSH];
LOOP; -- goes to start of next server loop
END;

ENDLOOP; -- next message

csD.Checkpoint[mailFileSH];
retProcs.accept[intC.retrieveHandle ! RetrieveDefs.Failed => CONTINUE ];
IF messages = 0
THEN ReportProgress[exD.emptySemi]
ELSE BEGIN
numString: STRING = [6];
StringDefs.AppendNumber[numString,messages,10];
ReportProgressString[numString];
ReportProgress[exD.semi];
messagesRead ← TRUE;
END;

ENDLOOP; -- next server

gsD.ReturnMemoryPages[bufferPages,buffer];
buffer ← NIL;
IF NOT serverKnown THEN GOTO noMailboxes;

IF messagesRead
THEN BEGIN -- extend toc to include new messages
ENABLE csD.Error => GOTO csError;
p: csD.Position;
vmD.GetTOCFixedPart[vmD.GetFirstFreeTOCIndex[] - 1, tb];
BoxPositionNextChar[tb.firstPage,
tb.firstByte+tb.offsetToHeader+tb.textLength, 24];
parseHandle ← mfD.InitializeParseHeader[BoxNextChar, BoxBackupChar];
WHILE (p ← csD.GetPosition[mailFileSH]) < csD.GetLength[mailFileSH] DO
[tb.firstPage, tb.firstByte] ← csD.MapPositionToPageByte[p];
IF mfD.ParseStamp[BoxNextChar, tb] # ovD.ok THEN exD.SysBug[];
BoxPositionNextChar[tb.firstPage,
tb.firstByte+tb.offsetToHeader, tb.textLength];
mfD.ParseHeaderForTOC[utilityString, parseHandle];
IF vmD.ExtendTOC[tb, utilityString] # ovD.ok THEN GOTO tocError;
BoxPositionNextChar[tb.firstPage,
tb.firstByte+tb.offsetToHeader+tb.textLength, 24];
ENDLOOP;
END
ELSE ReportException[exD.noNewMail];

EXITS
noMailboxes => ReportException[exD.noMailboxes, flash];
credentialsError => ReportException[exD.loginTryAgain, flash];
noServers => ReportException[exD.cantConnect, flash];
tocError => ReportException[exD.tocOverflow, flash];
csError => ReportException[exD.probDiskError, flash];

END; -- of EXITS block

IF parseHandle ~= NIL THEN mfD.FinalizeParseHeader[parseHandle];
IF mailFileSH # NIL THEN csD.Destroy[mailFileSH];
IF buffer # NIL THEN gsD.ReturnMemoryPages[bufferPages,buffer];
IF flashWanted THEN exD.FlashExceptionsRegion[];

RETURN;

END; -- of AccessNewMailOperation --


END. -- of AccessOp --