-- Transport Mechanism: User: MTP retrieval -- [Juniper]<Grapevine>User>RetrieveMTP.mesa -- Mike Schroeder 13-Nov-80 17:04:12 -- Andrew Birrell 8-Sep-82 15:12:09 DIRECTORY BodyDefs USING [ItemHeader], FTPDefs USING [ CommunicationError, CredentialError, FTPBeginRetrievalOfMessages, FTPCreateUser, FTPDestroyUser, FTPEndRetrievalOfMessages, FTPError, FTPIdentifyNextMessage, FTPInitialize, FTPOpenConnection, FTPRetrieveBlockOfMessage, FTPSetCredentials, MailError, MessageInfoObject, PupCommunicationPrimitives], PupDefs USING [AppendPupAddress], RetrieveDefs USING [Failed], RetrieveXDefs USING [Handle, ServerAddress]; RetrieveMTP: MONITOR LOCKS handle USING handle: RetrieveXDefs.Handle IMPORTS FTPDefs, PupDefs, RetrieveDefs, RetrieveXDefs EXPORTS RetrieveXDefs = BEGIN OPEN FTPDefs; MTPNextMessage: PUBLIC ENTRY PROCEDURE [handle: RetrieveXDefs.Handle] RETURNS [msgExists: BOOLEAN, archived: BOOLEAN, deleted: BOOLEAN] = BEGIN ENABLE UNWIND => NULL; dateString: STRING = [40]; -- arbitrary number, but big enough! -- m: MessageInfoObject ← [ byteCount:, deliveryDate: dateString, opened:, deleted:]; SELECT handle.state FROM end => ERROR; beginning => BEGIN addressString: STRING = [22] --377#377#177777|177777-- ; handle.ftpUser ← FTPCreateUser[NIL, PupCommunicationPrimitives[]]; FTPSetCredentials[ handle.ftpUser, primary, handle.userName, handle.userPwd]; PupDefs.AppendPupAddress[ addressString, RetrieveXDefs.ServerAddress[handle]]; FTPOpenConnection[ handle.ftpUser, addressString, mail, NIL ! FTPError => BEGIN handle.state ← end; SELECT ftpError FROM noSuchHost => ERROR RetrieveDefs.Failed[noSuchServer]; connectionRejected => ERROR RetrieveDefs.Failed[connectionRejected]; IN CommunicationError => ERROR RetrieveDefs.Failed[communicationFailure]; ENDCASE => ERROR RetrieveDefs.Failed[unknownFailure]; END]; FTPBeginRetrievalOfMessages[ handle.ftpUser, handle.userName ! FTPError => BEGIN handle.state ← end; SELECT ftpError FROM IN CredentialError, IN MailError => ERROR RetrieveDefs.Failed[badCredentials]; unidentifiedTransientError, fileBusy => --mailbox busy ERROR RetrieveDefs.Failed[connectionRejected]; IN CommunicationError => ERROR RetrieveDefs.Failed[communicationFailure]; ENDCASE => ERROR RetrieveDefs.Failed[unknownFailure]; END]; END; ENDCASE; FTPIdentifyNextMessage[ handle.ftpUser, @m ! FTPError => BEGIN handle.state ← end; ERROR RetrieveDefs.Failed[ IF ftpError IN CommunicationError THEN communicationFailure ELSE unknownFailure]; END]; msgExists ← m.byteCount # 0; deleted ← m.deleted; archived ← FALSE; handle.header.length ← m.byteCount; handle.header.type ← Text; handle.state ← IF msgExists THEN message ELSE lastMessage; END; --NextMessage-- MTPNextItem: PUBLIC ENTRY PROCEDURE [handle: RetrieveXDefs.Handle] RETURNS [itemHeader: BodyDefs.ItemHeader] = BEGIN ENABLE UNWIND => NULL; SELECT handle.state FROM message => { itemHeader ← handle.header; handle.state ← block; handle.spareByte ← FALSE}; block, lastBlock => {itemHeader ← [LastItem, 0]; handle.state ← lastBlock}; ENDCASE => ERROR; END; --NextMessage-- MTPNextBlock: PUBLIC ENTRY PROCEDURE [ handle: RetrieveXDefs.Handle, buffer: DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] RETURNS [bytes: CARDINAL] = BEGIN ENABLE UNWIND => NULL; SELECT handle.state FROM block => BEGIN -- much of this code is dedicated to the design of FTPDefs. -- -- "handle" may contain an extra byte from previous call -- IF handle.spareByte THEN BEGIN buffer[0] ← handle.spareByteValue; handle.spareByte ← FALSE; bytes ← 1; END ELSE bytes ← 0; WHILE bytes < LENGTH[buffer] DO extraWord: PACKED ARRAY [0..1] OF CHARACTER; bWordsUsed: CARDINAL = (bytes + 1) / 2; firstFree: POINTER = BASE[buffer] + bWordsUsed; bWordsFree: CARDINAL = LENGTH[buffer] / 2 - bWordsUsed; base: POINTER = IF bWordsFree = 0 THEN @extraWord ELSE firstFree; length: CARDINAL = IF bWordsFree = 0 THEN LENGTH[extraWord] / 2 ELSE bWordsFree; given: CARDINAL = FTPRetrieveBlockOfMessage[ handle.ftpUser, base, length ! FTPError => BEGIN handle.state ← end; ERROR RetrieveDefs.Failed[ IF ftpError IN CommunicationError THEN communicationFailure ELSE unknownFailure]; END]; IF given = 0 THEN {handle.state ← lastBlock; EXIT}; IF bWordsFree = 0 THEN BEGIN -- data was placed in extraWord -- buffer[bytes] ← extraWord[0]; bytes ← bytes + 1; IF given > 1 THEN BEGIN handle.spareByteValue ← extraWord[1]; handle.spareByte ← TRUE; END; END ELSE BEGIN -- data was placed in buffer -- IF bytes MOD 2 # 0 THEN FOR i: CARDINAL IN [bytes..bytes + given) DO buffer[i] ← buffer[i + 1]; ENDLOOP; bytes ← bytes + given; END; ENDLOOP; END; lastBlock => bytes ← 0; ENDCASE => ERROR; END; --NextBlock-- MTPAccept: PUBLIC INTERNAL PROCEDURE [handle: RetrieveXDefs.Handle] = BEGIN IF handle.state # lastMessage THEN ERROR; handle.state ← end; FTPEndRetrievalOfMessages[ handle.ftpUser ! FTPError => ERROR RetrieveDefs.Failed[ IF ftpError IN CommunicationError THEN communicationFailure ELSE unknownFailure]]; END; --Accept-- MTPClose: PUBLIC INTERNAL PROCEDURE [handle: RetrieveXDefs.Handle] = BEGIN IF handle.ftpUser # NIL THEN BEGIN FTPDestroyUser[handle.ftpUser]; handle.ftpUser ← NIL; handle.state ← beginning; END; END; --Close-- FTPInitialize[]; END. -- RetrieveMTP--