-- FTPUserMailIn.mesa, Edit: HGM September 13, 1980 1:46 PM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY FTPDefs, FTPPrivateDefs, String USING [AppendString, StringToLongNumber], MDSStorage USING [Node, Free]; FTPUserMailIn: PROGRAM IMPORTS String, Storage: MDSStorage, FTPPrivateDefs EXPORTS FTPDefs SHARES FTPDefs = BEGIN OPEN FTPDefs, FTPPrivateDefs; -- **********************! Retrieval Primitives !*********************** FTPBeginRetrievalOfMessages: PUBLIC PROCEDURE [ ftpuser: FTPUser, mailboxName: STRING] = BEGIN OPEN ftpuser; -- verify purpose and state VerifyPurposeAndState[ftpuser, mail, connected]; -- send retrieve mail command PutCommand[mtper, markRetrieveMail, 0]; -- construct property list containing credentials and mailbox name ResetPropertyList[propertyList]; WriteProperty[propertyList, userName, primaryPropertyList[userName]]; WriteProperty[propertyList, userPassword, primaryPropertyList[userPassword]]; WriteProperty[propertyList, connectName, primaryPropertyList[connectName]]; WriteProperty[ propertyList, connectPassword, primaryPropertyList[connectPassword]]; WriteProperty[propertyList, mailbox, mailboxName]; -- send property list and EOC PutPropertyList[mtper, propertyList]; PutEOC[mtper]; -- update retrieval state based upon remote server's action UpdateRetrievalState[ftpuser]; END; FTPIdentifyNextMessage: PUBLIC PROCEDURE [ ftpuser: FTPUser, messageInfo: MessageInfo] = BEGIN OPEN ftpuser; -- Note: Flushes remainder of text of previous message if necessary; -- returns messageInfo.byteCount=0 if no more messages. -- flush whatever text remains of previous message IF state = messageTextPending THEN BEGIN bufferSize: CARDINAL = 256; buffer: POINTER; -- allocate a buffer buffer ← Storage.Node[bufferSize]; -- receive blocks of message until no more remain UNTIL FTPRetrieveBlockOfMessage[ ftpuser, buffer, bufferSize ! UNWIND => Storage.Free[buffer]] = 0 DO ENDLOOP; -- release buffer Storage.Free[buffer]; END; -- receive description of next message if any IF state = messageHeaderPending THEN BEGIN -- receive message information GetMessageInfo[ftpuser, messageInfo]; -- record number of bytes expected and initialize number received numberOfBytesExpected ← messageInfo.byteCount; numberOfBytesReceived ← 0; -- prepare to receive text of message GetSpecificCommand[mtper, markHereIsFile]; -- note readiness to receive text of message state ← messageTextPending; END -- indicate no more messages ELSE BEGIN -- verify purpose and state VerifyPurposeAndState[ftpuser, mail, mailboxExhausted]; -- signal mailbox exhausted messageInfo.byteCount ← 0; END; END; FTPRetrieveBlockOfMessage: PUBLIC PROCEDURE [ ftpuser: FTPUser, destination: POINTER, maxWordCount: CARDINAL] RETURNS [actualByteCount: CARDINAL] = BEGIN OPEN ftpuser; -- Note: Returns zero if end of message. -- local variables bytePointerObject: BytePointerObject ← [ destination, FALSE, bytesPerWord * maxWordCount]; -- verify purpose and state VerifyPurposeAndState[ftpuser, mail, messageTextPending]; -- receive next block of message -- Note: The FTPUtilities procedure, ReceiveBlock, is here replicated in-line -- to reduce Alto working sets. BEGIN OPEN mtper; communicationPrimitives.ReceiveBytes[ communicationSystem, connection, @bytePointerObject, TRUE]; actualByteCount ← bytesPerWord * maxWordCount - bytePointerObject.count; END; -- open -- update running byte count numberOfBytesReceived ← numberOfBytesReceived + actualByteCount; IF numberOfBytesReceived > numberOfBytesExpected THEN Abort[messageLongerThanAdvertised]; -- determine if end of message IF actualByteCount = 0 THEN BEGIN -- verify advertised byte count IF numberOfBytesReceived < numberOfBytesExpected THEN Abort[messageShorterThanAdvertised]; -- update retrieval state based upon remote server's action UpdateRetrievalState[ftpuser]; END; END; FTPEndRetrievalOfMessages: PUBLIC PROCEDURE [ftpuser: FTPUser] = BEGIN OPEN ftpuser; -- Note: Flushes remainder of mailbox's contents if necessary. -- local constants deliveryDate: STRING = [maxStringLength]; -- local variables messageInfoObject: MessageInfoObject ← [ byteCount: 1, deliveryDate: deliveryDate, opened:, deleted:]; -- flush whatever messages remain of mailbox's contents UNTIL messageInfoObject.byteCount = 0 DO FTPIdentifyNextMessage[ftpuser, @messageInfoObject]; ENDLOOP; -- order flushing of mailbox PutCommandAndEOC[mtper, markFlushMailbox, 0]; -- wait for confirmation GetYesAndEOC[mtper]; -- note mail retrieval complete state ← connected; END; -- **********************! Retrieval Subroutines !*********************** GetMessageInfo: PROCEDURE [ftpuser: FTPUser, messageInfo: MessageInfo] = BEGIN OPEN ftpuser, messageInfo; -- Note: Caller supplies string for delivery date. -- receive property list GetPropertyList[mtper, propertyList]; -- decode byte count IF propertyList[length] = NIL THEN Abort[missingMessageLength]; byteCount ← String.StringToLongNumber[propertyList[length], 10]; -- decode delivery date deliveryDate.length ← 0; IF propertyList[dateReceived] # NIL THEN String.AppendString[deliveryDate, propertyList[dateReceived]]; -- decode opened and deleted opened ← DecodeBooleanProperty[propertyList[opened]]; deleted ← DecodeBooleanProperty[propertyList[deleted]]; END; UpdateRetrievalState: PROCEDURE [ftpuser: FTPUser] = BEGIN OPEN ftpuser; -- local variables mark, code: Byte; -- receive command [mark, code] ← GetCommand[mtper]; -- interpret command SELECT mark FROM markHereIsPropertyList => state ← messageHeaderPending; markYes => BEGIN -- receive EOC GetEOC[mtper]; -- note all messages retrieved state ← mailboxExhausted; END; markNo => BEGIN -- receive EOC GetEOC[mtper]; -- note mail retrieval complete state ← connected; -- abort AbortWithExplanation[CodeToSignal[code], mtper.inputString]; END; ENDCASE => Abort[illegalProtocolSequence]; END; END. -- of FTPUserMailIn