-- 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--