-- AccessOp.mesa -- edited by Schroeder, May 4, 1981 3:09 PM. -- edited by Brotz, March 4, 1983 9:54 AM -- edited by Levin, February 24, 1981 4:51 PM. -- edited by Taft, May 17, 1983 2:43 PM DIRECTORY AltoFile USING [DiskFull], BodyDefs USING [ItemHeader], csD: FROM "CoreStreamDefs" USING [Checkpoint, Destroy, EndOfStream, GetLength, GetPosition, MapPageByteToPosition, MapPositionToPageByte, Open, Position, Read, Reset, SetPosition, StreamHandle, Write, WriteBlock], exD: FROM "exceptionDefs" USING [AppendExceptionToExceptionLine, AppendStringToExceptionLine, badCredentialsSemi, badServerNameSemi, busySemi, cantConnect, continuationHeader, DisplayExceptionLine, dots, dotsDidntRespond, dotsEmpty, emptySemi, Exception, ExceptionLineOverflow, failedSemi, FlashExceptionsRegion, GetExceptionString, loginTryAgain, newMailOverfill, nil, noMailboxes, noNewMail, overflow, semi, SysBug, tocOverflow, unknownErrorSemi], Inline USING [LowHalf], MailParseDefs USING [endOfInput], mfD: FROM "MailFormatDefs" USING [CreateStamp, ParseHeaderForTOC, ParseStamp], opD: FROM "OperationsDefs" USING [maxTOCStringLength], Process USING [Yield], RetrieveDefs USING [AccessProcs, Failed, FailureReason, Handle, MailboxState, NextServer, ServerName, ServerState], String USING [AppendNumber, EquivalentString], vmD: FROM "VirtualMgrDefs" USING [CheckpointDisplayMessage, DMList, ExtendTOC, FirstFreeTOCIndex, GetTOCFixedPart, TOCFixedPart, TOCFixedPartPtr, TOCHandle, TOCOverflow], VMDefs USING [AllocatePage, Error, Page, pageByteSize, Release]; AccessOp: PROGRAM IMPORTS AltoFile, csD, exD, Inline, mfD, Process, RetrieveDefs, String, vmD, VMDefs EXPORTS opD = BEGIN AccessNewMailOperation: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, retrieveHandle: RetrieveDefs.Handle, registry: STRING, ignoreUserFeedback: BOOLEAN _ FALSE, deleteEachMessage: BOOLEAN _ FALSE] RETURNS [messagesRead: BOOLEAN] = -- Gets new messages from the user's mailbox(es) through "retrieveHandle", appends them -- to the mail file "toc", and updates the TOC. If "deleteEachMessage" is FALSE, then -- each mailbox in turn is flushed only after successfully retrieveing all messages in -- that mailbox. If "deleteEachMessage" is TRUE, then each message is deleted from its -- mailbox after successful retrieval of that message. BEGIN breakBuffers: [0 .. 177777B / VMDefs.pageByteSize) = 125; buffer: VMDefs.Page _ NIL; mailFileSH: csD.StreamHandle _ NIL; flashWanted: BOOLEAN _ FALSE; nextCharLimit: CARDINAL; charCount: CARDINAL; BoxNextChar: PROCEDURE RETURNS [lastChar: CHARACTER] = BEGIN IF charCount >= nextCharLimit THEN lastChar _ MailParseDefs.endOfInput ELSE lastChar _ csD.Read[mailFileSH ! csD.EndOfStream => {nextCharLimit _ charCount; lastChar _ MailParseDefs.endOfInput; CONTINUE}]; charCount _ charCount + 1; END; -- of BoxNextChar -- 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; -- of BoxPositionNextChar -- lastLineUsed: [0 .. 2] _ 0; ReportProgressString: PROCEDURE [s: STRING] = BEGIN IF ignoreUserFeedback THEN RETURN; lastLineUsed _ 1; exD.AppendStringToExceptionLine[s, 1 ! exD.ExceptionLineOverflow => CONTINUE ]; END; -- of ReportProgressString -- ReportProgress: PROCEDURE [e: exD.Exception] = BEGIN IF ignoreUserFeedback THEN RETURN; lastLineUsed _ 1; exD.AppendExceptionToExceptionLine[e, 1 ! exD.ExceptionLineOverflow => CONTINUE ]; END; -- of ReportProgress -- lastException: exD.Exception _ exD.nil; ReportException: PROCEDURE [e: exD.Exception, f: {flash, dontFlash} _ dontFlash] = BEGIN IF ignoreUserFeedback THEN RETURN; 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; -- of ReportException -- -- Code for AccessNewMailOperation messagesRead _ FALSE; BEGIN -- for EXITS -- 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[retrieveHandle] FROM badName, badPwd => GOTO credentialsError; cantAuth => GOTO noServers; ENDCASE; --ok to try -- Flush buffers on the mail file held by all displayed messages. FOR dmList: vmD.DMList _ toc.dmList, dmList.next UNTIL dmList = NIL DO IF dmList.dm # NIL THEN vmD.CheckpointDisplayMessage[dmList.dm, key]; ENDLOOP; mailFileSH _ csD.Open[fh: toc.mailFile, type: byte, mode: append]; buffer _ VMDefs.AllocatePage[]; DO -- next server ENABLE BEGIN VMDefs.Error => BEGIN UNTIL RetrieveDefs.NextServer[retrieveHandle].noMore DO ENDLOOP; IF reason = resources THEN GO TO FullDisk; END; AltoFile.DiskFull => BEGIN UNTIL RetrieveDefs.NextServer[retrieveHandle].noMore DO ENDLOOP; GO TO FullDisk; END; END; -- ENABLE -- messages: CARDINAL _ 0; connected, archivedReported: BOOLEAN _ FALSE; retProcs: RetrieveDefs.AccessProcs; whyFailed: RetrieveDefs.FailureReason; noMore: BOOLEAN; serverState: RetrieveDefs.ServerState; Process.Yield[]; [noMore, serverState, retProcs] _ RetrieveDefs.NextServer[retrieveHandle]; IF noMore THEN EXIT; serverKnown _ TRUE; RetrieveDefs.ServerName[retrieveHandle, utilityString]; ReportProgressString[utilityString]; IF String.EquivalentString[utilityString, registry] THEN ReportProgressString[" inbox server "L]; IF serverState # notEmpty THEN BEGIN ReportProgress [IF serverState = empty THEN exD.dotsEmpty ELSE exD.dotsDidntRespond]; LOOP; END; DO -- next message firstChunk: BOOLEAN _ TRUE; msgExists, archived, deleted: BOOLEAN; item: BodyDefs.ItemHeader; [msgExists, archived, deleted] _ retProcs.nextMessage[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[retrieveHandle ! RetrieveDefs.Failed => {whyFailed _ why; GOTO retrieveError} ]; IF item.type = Text OR item.type = LastItem THEN EXIT; ENDLOOP; IF item.type = LastItem THEN LOOP; Process.Yield[]; UNTIL item.length = 0 DO -- message chunks breakBytes: CARDINAL = breakBuffers * VMDefs.pageByteSize; tb.textLength _ IF item.length <= breakBytes THEN Inline.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 [retrieveHandle, DESCRIPTOR[@buffer.chars, VMDefs.pageByteSize] ! RetrieveDefs.Failed => {whyFailed _ why; GOTO retrieveError}]; IF bytes = 0 THEN EXIT; csD.WriteBlock[mailFileSH, buffer, 0, bytes]; ENDLOOP; -- next block ENDLOOP; -- message chunk IF deleteEachMessage THEN BEGIN csD.Checkpoint[mailFileSH]; messagesRead _ TRUE; WITH gvRetProcs: retProcs SELECT FROM GV => gvRetProcs.deleteMessage[retrieveHandle]; ENDCASE; END; 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[retrieveHandle ! RetrieveDefs.Failed => CONTINUE ]; IF messages = 0 THEN ReportProgress[exD.emptySemi] ELSE BEGIN numString: STRING = [6]; String.AppendNumber[numString,messages,10]; ReportProgressString[numString]; ReportProgress[exD.semi]; messagesRead _ TRUE; END; REPEAT FullDisk => BEGIN ReportProgress[exD.overflow]; csD.Reset[mailFileSH]; csD.Checkpoint[mailFileSH]; ReportException[exD.newMailOverfill]; END; ENDLOOP; -- next server VMDefs.Release[buffer]; buffer _ NIL; IF NOT serverKnown THEN GOTO noMailboxes; IF messagesRead THEN BEGIN -- extend toc to include new messages p: csD.Position; vmD.GetTOCFixedPart[toc, key, vmD.FirstFreeTOCIndex[toc, key] - 1, tb]; BoxPositionNextChar[tb.firstPage, tb.firstByte + tb.offsetToHeader + tb.textLength, 24]; WHILE (p _ csD.GetPosition[mailFileSH]) < csD.GetLength[mailFileSH] DO [tb.firstPage, tb.firstByte] _ csD.MapPositionToPageByte[p]; IF ~mfD.ParseStamp[BoxNextChar, tb] THEN exD.SysBug[]; BoxPositionNextChar[tb.firstPage, tb.firstByte + tb.offsetToHeader, tb.textLength]; mfD.ParseHeaderForTOC[utilityString, BoxNextChar]; vmD.ExtendTOC[toc, key, tb, utilityString ! vmD.TOCOverflow => GO TO tocError; VMDefs.Error => IF reason = resources THEN GO TO tocError; AltoFile.DiskFull => GO TO 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]; END; -- of EXITS block IF mailFileSH # NIL THEN csD.Destroy[mailFileSH]; IF buffer # NIL THEN VMDefs.Release[buffer]; IF flashWanted AND ~ignoreUserFeedback THEN exD.FlashExceptionsRegion[]; END; -- of AccessNewMailOperation -- END. -- of AccessOp -- z19932(635)\f1