-- Grapevine: Lily: interface to mail server retrieval
-- [Juniper]<Lily>LilyAccess.mesa
-- Andrew Birrell 4-Jan-82 15:24:07
-- Michael Schroeder December 4, 1981 1:40 PM
DIRECTORY
BodyDefs USING[ ItemHeader, maxRemarkLength, maxRNameLength,
oldestTime, RName, Timestamp ],
LilyAccessDefs USING[ FailureReason, lastChar, MBXState ],
LilyIODefs USING[ LogAction ],
RetrieveDefs,
String USING[ AppendString ],
Storage USING[ Node, String, Free, FreeString ];
LilyAccess: PROGRAM
IMPORTS LilyIODefs, RetrieveDefs, String, Storage
EXPORTS LilyAccessDefs =
BEGIN
Handle: PUBLIC TYPE = POINTER TO Object;
Object: TYPE = RECORD [
rHandle: RetrieveDefs.Handle,
connection: CARDINAL, -- for logging only --
state: RetrieveDefs.ServerState, -- state of current mailbox (if any) --
procs: RetrieveDefs.AccessProcs, -- for accessing current mailbox --
msg: CARDINAL, -- number of current message (from 1) --
allPrevMsgDeleted: BOOLEAN, -- all msg previous to current position are deleted --
nextMessageOK: BOOLEAN, --can call nextMessage for this inbox--
deleted: BOOLEAN, -- current mesage is deleted --
archived: BOOLEAN, -- current mesage is archived --
tocKnown: BOOLEAN, -- current mesage has a toc --
toc: STRING, -- TOC entry for current message (if known) --
sender: STRING, -- sender of current message (if any) --
next: CARDINAL, -- number of next server (from 0) --
unseen: CARDINAL, -- first server we haven't looked at --
boxCount: CARDINAL, -- number of mailbox servers --
server: SEQUENCE dummy: CARDINAL OF
RECORD[allDeleted: BOOLEAN, firstMsg: CARDINAL]
];
interval: CARDINAL = 300 -- seconds for mail polling --;
maxTocLength: CARDINAL = BodyDefs.maxRemarkLength;
Create: PUBLIC PROC[ user, password: STRING, connection: CARDINAL]
RETURNS[ state: LilyAccessDefs.MBXState, handle: Handle ] =
BEGIN
rHandle: RetrieveDefs.Handle = RetrieveDefs.Create[interval];
RetrieveDefs.NewUser[rHandle, user, password];
state ← SELECT RetrieveDefs.MailboxState[rHandle] FROM
badName => badName,
badPwd => badPwd,
cantAuth => cantAuth,
allDown => allDown,
someEmpty => someEmpty,
allEmpty => allEmpty,
notEmpty => notEmpty,
ENDCASE => ERROR;
IF state IN [allDown..notEmpty]
THEN BEGIN
servers: CARDINAL ← 0;
DO noMore: BOOLEAN;
-- avoid compiler bug with long result records --
[noMore,,] ← RetrieveDefs.NextServer[rHandle];
IF noMore THEN EXIT;
servers ← servers+1;
ENDLOOP;
handle ← Storage.Node[SIZE[Object[servers+1]]];
handle.boxCount ← servers;
END
ELSE BEGIN
handle ← Storage.Node[SIZE[Object[0+1]]];
handle.boxCount ← 0;
END;
handle.rHandle ← rHandle;
handle.connection ← connection;
handle.msg ← 0;
handle.toc ← Storage.String[maxTocLength];
handle.sender ← Storage.String[BodyDefs.maxRNameLength];
handle.next ← handle.unseen ← 0;
END; --Create--
Destroy: PUBLIC PROC[handle: Handle] =
BEGIN
TryToFlush[handle ! RetrieveDefs.Failed => CONTINUE];
RetrieveDefs.Destroy[handle.rHandle];
IF handle.toc # NIL THEN Storage.FreeString[handle.toc];
IF handle.sender # NIL THEN Storage.FreeString[handle.sender];
Storage.Free[handle];
END; --Destroy--
Failed: PUBLIC ERROR[why: LilyAccessDefs.FailureReason] = CODE;
Fail: PROC[handle: Handle, why: RetrieveDefs.FailureReason] =
BEGIN
handle.msg ← handle.server[handle.next].firstMsg-1;
handle.tocKnown ← FALSE;
handle.nextMessageOK ← FALSE;
handle.allPrevMsgDeleted ← FALSE;
ERROR Failed[communications];
END; --Fail--
TryToFlush: PROC[handle: Handle] =
BEGIN --flush the current inbox if it contains only deleted messages--
IF handle.next # 0 --not in initial state--
AND handle.state = notEmpty --this server had some messages--
AND NOT handle.server[handle.next-1].allDeleted --connection was opened--
AND handle.allPrevMsgDeleted --no previous message is undeleted--
AND (IF handle.nextMessageOK
THEN handle.deleted --current message is deleted too--
ELSE TRUE -- there isn't a current message --)
THEN WHILE handle.nextMessageOK DO
[handle.nextMessageOK, ,handle.deleted] ←
handle.procs.nextMessage[handle.rHandle];
IF handle.nextMessageOK AND NOT handle.deleted THEN EXIT;
REPEAT FINISHED => BEGIN
handle.procs.accept[handle.rHandle];
handle.server[handle.next-1].allDeleted ← TRUE;
LilyIODefs.LogAction[handle.connection, "Inbox flushed"L];
END;
ENDLOOP;
END; --TryToFlush--
Position: PUBLIC PROC[ handle: Handle, msg: CARDINAL]
RETURNS[ archived, deleted: BOOLEAN] =
BEGIN
ENABLE RetrieveDefs.Failed => Fail[handle, why];
IF msg = 0 THEN ERROR Failed[notFound];
IF msg <= handle.msg
THEN BEGIN -- skip to end of server list --
DO noMore: BOOLEAN;
-- avoid compiler bug with long result records --
[noMore, , ] ← RetrieveDefs.NextServer[handle.rHandle];
IF noMore THEN EXIT;
ENDLOOP;
handle.next ← 0; handle.msg ← 0;
END;
-- now we're at or before the required server, and the required place --
WHILE msg > handle.msg
DO
doNextServer: BOOLEAN;
IF handle.next = 0
OR msg >= handle.server[handle.next].firstMsg
OR handle.state # notEmpty
THEN doNextServer ← TRUE -- go on to next server--
ELSE IF handle.server[handle.next-1].allDeleted
THEN BEGIN --fake a deleted message--
handle.nextMessageOK ← handle.deleted ← TRUE;
doNextServer ← handle.archived ← FALSE
END
ELSE BEGIN
IF NOT handle.deleted THEN handle.allPrevMsgDeleted ← FALSE;
[handle.nextMessageOK, handle.archived, handle.deleted] ←
handle.procs.nextMessage[handle.rHandle];
doNextServer ← NOT handle.nextMessageOK
END;
IF doNextServer
THEN BEGIN
noMore: BOOLEAN;
TryToFlush[handle];
IF handle.next >= handle.boxCount THEN EXIT;
[noMore, handle.state, handle.procs] ←
RetrieveDefs.NextServer[handle.rHandle];
IF noMore
THEN BEGIN
handle.boxCount ← handle.next;
handle.next ← 0;
handle.msg ← 0;
EXIT
END
ELSE BEGIN
handle.allPrevMsgDeleted ← TRUE;
handle.nextMessageOK ← TRUE;
handle.deleted ← TRUE; -- make msg -1 in this inbox appear to be deleted--
IF handle.next = handle.unseen
THEN BEGIN
handle.server[handle.next] ← [allDeleted:FALSE, firstMsg:handle.msg+1];
handle.unseen ← handle.unseen+1;
handle.server[handle.unseen].firstMsg ← LAST[CARDINAL];
END
ELSE handle.msg ← handle.server[handle.next].firstMsg-1;
handle.next ← handle.next+1
END;
END
ELSE handle.msg ← handle.msg + 1;
ENDLOOP;
IF msg # handle.msg THEN ERROR Failed[notFound];
handle.tocKnown ← FALSE;
RETURN[handle.archived, handle.deleted]
END; --Position--
ReadTOC: PUBLIC PROC[handle: Handle] RETURNS[ toc: STRING ] =
BEGIN
ENABLE RetrieveDefs.Failed => Fail[handle, why];
IF NOT handle.tocKnown
THEN WITH p: handle.procs SELECT FROM
GV => p.readTOC[handle.rHandle, handle.toc];
ENDCASE => BEGIN
handle.toc.length ← 0 ;
String.AppendString[handle.toc,
"[Lily doesn't work for mailboxes on IFS or MAXC]"L];
END;
handle.tocKnown ← TRUE;
RETURN[handle.toc]
END; --REadTOC--
IllegalBackup: ERROR = CODE;
Read: PUBLIC PROC[handle: Handle,
reader: PROC[postmark: BodyDefs.Timestamp,
sender: BodyDefs.RName,
readChar: PROC RETURNS[CHARACTER],
backup: PROC] ] =
BEGIN
ENABLE RetrieveDefs.Failed => Fail[handle, why];
stamp: BodyDefs.Timestamp ← BodyDefs.oldestTime;
bLength: CARDINAL = 404; -- assumed to be multiple of 4 --
buffer: PACKED ARRAY [0..bLength) OF CHARACTER;
rPos, wPos: [0..bLength] ← 0; -- next char in buffer --
ReadChar: PROC RETURNS[ c: CHARACTER ] =
BEGIN
IF rPos = wPos
THEN BEGIN
IF header.length = 0 THEN RETURN[ LilyAccessDefs.lastChar ];
IF wPos = 0
THEN BEGIN
length: CARDINAL =
handle.procs.nextBlock[handle.rHandle,
DESCRIPTOR[@buffer, bLength/2] ];
header.length ← header.length - length;
wPos ← wPos + length;
END
ELSE BEGIN
length: CARDINAL =
handle.procs.nextBlock[handle.rHandle,
DESCRIPTOR[@buffer+bLength/4, bLength/2] ];
header.length ← header.length - length;
IF wPos # bLength/2 THEN ERROR;
wPos ← wPos + length;
END;
IF wPos = bLength THEN wPos ← 0;
END;
c ← buffer[rPos];
rPos ← IF rPos = bLength-1 THEN 0 ELSE rPos+1;
END; --ReadChar--
Backup: PROC =
BEGIN
rPos ← IF rPos = 0 THEN bLength-1 ELSE rPos-1;
IF rPos = wPos THEN ERROR IllegalBackup[];
END; --Backup--
header: BodyDefs.ItemHeader;
handle.sender.length←0;
WITH p: handle.procs SELECT FROM
GV => p.startMessage[handle: handle.rHandle,
sender: handle.sender,
postmark: @stamp];
ENDCASE => NULL;
-- skip to text item --
DO header ← handle.procs.nextItem[handle.rHandle];
IF header.type = Text OR header.type = LastItem THEN EXIT;
ENDLOOP;
reader[stamp, handle.sender, ReadChar, Backup];
UNTIL header.type = LastItem
DO header ← handle.procs.nextItem[handle.rHandle]; ENDLOOP;
END; --Read--
WriteTOC: PUBLIC PROC[handle: Handle] =
BEGIN
ENABLE RetrieveDefs.Failed => Fail[handle, why];
handle.tocKnown ← TRUE;
WITH p: handle.procs SELECT FROM
GV => p.writeTOC[handle.rHandle, handle.toc];
ENDCASE => NULL;
END; --WriteTOC--
Delete: PUBLIC PROC[handle: Handle] =
BEGIN
ENABLE RetrieveDefs.Failed => Fail[handle, why];
IF NOT handle.deleted
THEN WITH p: handle.procs SELECT FROM
GV => {p.deleteMessage[handle.rHandle]; handle.deleted ← TRUE};
ENDCASE => NULL;
END; --Delete--
END. --LilyAccess--