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