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