<> <> <> <> <> <<>> <> <<>> DIRECTORY Ascii USING [Digit, Letter], Convert USING [AppendTimeRFC822], IO, MailBasics, MailBasicsItemTypes, MailBasicsMoreItemTypes, MailRetrieve, MailUtils, Process, RefText, Rope; MailRetrieveImpl: CEDAR MONITOR IMPORTS Ascii, Convert, IO, MailUtils, Process, RefText, Rope EXPORTS MailRetrieve ~ BEGIN OPEN MailRetrieve; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; MailRetrieveProcsRef: TYPE ~ MailRetrieve.MailRetrieveProcsRef; <> <> mailHandles: LIST OF RetrieveHandle; -- all currently active mail handles AnyMailBox: TYPE ~ REF AnyMailBoxObject; AnyMailBoxObject: TYPE ~ RECORD[ procs: MailRetrieve.MailRetrieveProcsRef, ref: REF ]; AnyMailBoxList: TYPE ~ LIST OF AnyMailBox; RetrieveHandle: TYPE ~ REF RetrieveHandleObject; RetrieveHandleObject: TYPE ~ RECORD [ mailboxes: AnyMailBoxList ¬ NIL, -- list of GVInfo, MSInfo, etc. pollingInterval: CARDINAL, -- time between change reports reportChangesProc: PROC [Handle, MBXState], -- user provided procedure lastReportedState: MBXState ¬ unknown, -- last reported state currentItem: STREAM ¬ NIL, -- current item stream unreadMailboxes: AnyMailBoxList ¬ NIL -- mboxes being read ]; CreateHandle: PROC [] RETURNS [h: RetrieveHandle] ~ { h ¬ NEW[RetrieveHandleObject]; mailHandles ¬ CONS[h, mailHandles]; }; DeleteHandle: PROC [h: RetrieveHandle] RETURNS [] ~ { IF h = mailHandles.first THEN mailHandles ¬ mailHandles.rest ELSE FOR l: LIST OF RetrieveHandle ¬ mailHandles, l.rest WHILE l.rest#NIL DO IF h = l.rest.first THEN { l.rest ¬ l.rest.rest; EXIT; }; ENDLOOP; }; <> Failed: PUBLIC ERROR [why: FailureReason, text: ROPE] = CODE; <> Create: PUBLIC PROC [pollingInterval: CARDINAL, reportChanges: PROC [Handle, MBXState] ¬ NIL] RETURNS [Handle] ~ { <> h: RetrieveHandle ¬ CreateHandle[]; h.pollingInterval ¬ pollingInterval; h.reportChangesProc ¬ reportChanges; RETURN[h]; }; <<>> Close: PUBLIC PROC [handle: Handle] RETURNS [] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { <> FOR ml: AnyMailBoxList ¬ h.mailboxes, ml.rest WHILE ml#NIL DO ml.first.procs.Close[ml.first.ref]; ENDLOOP; }; ENDCASE => ERROR; }; <<>> NewUser: PUBLIC PROC [handle: Handle, user: MailBasics.RName, password: ROPE] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { thisBox: AnyMailBox ¬ NEW[AnyMailBoxObject]; thisBox.procs ¬ GetRegisteredRetrieveMailProcs[user.ns]; IF thisBox.procs = NIL THEN ERROR Failed[$unregisteredMailProtocol, IO.PutFR1["%g not registered", [atom[user.ns]]] ]; thisBox.ref ¬ thisBox.procs.NewUser[handle, user, password, h.pollingInterval, ReportChangesProc]; h.mailboxes ¬ CONS[thisBox, h.mailboxes]; }; ENDCASE => ERROR; }; <<>> MailboxState: PUBLIC PROC [handle: Handle] RETURNS [state: MBXState ¬ unknown] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { <> FOR ml: AnyMailBoxList ¬ h.mailboxes, ml.rest WHILE ml#NIL DO mboxState: MBXState; mboxState ¬ ml.first.procs.MailboxState[ml.first.ref]; state ¬ MergeStates[state, mboxState]; ENDLOOP; }; ENDCASE => ERROR; }; <<>> NextServer: PUBLIC PROC [handle: Handle, reset: BOOLEAN ¬ FALSE] RETURNS [noMore: BOOL ¬ TRUE, state: ServerState ¬ unknown, type: ServerType] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF reset OR h.unreadMailboxes=NIL THEN -- start new sequence of mail retrieval h.unreadMailboxes ¬ h.mailboxes; WHILE h.unreadMailboxes #NIL DO this: AnyMailBox = h.unreadMailboxes.first; [noMore, state, type] ¬ this.procs.NextServer[this.ref]; IF noMore THEN h.unreadMailboxes ¬ h.unreadMailboxes.rest -- try next mailbox if any ELSE EXIT; -- return what we found ENDLOOP; }; ENDCASE => ERROR; }; <<>> ServerName: PUBLIC PROC [handle: Handle] RETURNS [serverName: MailBasics.RName] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN [[NIL, NIL]]; RETURN[h.unreadMailboxes.first.procs.ServerName[h.unreadMailboxes.first.ref]]; }; ENDCASE => ERROR; }; <<>> UserName: PUBLIC PROC [handle: Handle] RETURNS [userName: MailBasics.RName] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN [[NIL, NIL]]; RETURN[h.unreadMailboxes.first.procs.UserName[h.unreadMailboxes.first.ref]]; }; ENDCASE => ERROR; }; <<>> NextMessage: PUBLIC PROC [handle: Handle] RETURNS [msgExists, archived, deleted, read: BOOLEAN] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN [FALSE, FALSE, FALSE, FALSE]; [msgExists, archived, deleted, read] ¬ h.unreadMailboxes.first.procs.NextMessage[h.unreadMailboxes.first.ref]; }; ENDCASE => ERROR; }; <<>> StartMessage: PUBLIC PROC [handle: Handle] RETURNS [postmark: MailBasics.Timestamp, sender: MailBasics.RName, returnTo: MailBasics.RName] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN [NIL, [NIL, NIL], [NIL, NIL]]; [postmark, sender, returnTo] ¬ h.unreadMailboxes.first.procs.StartMessage[h.unreadMailboxes.first.ref]; }; ENDCASE => ERROR; }; <<>> NextItem: PUBLIC PROC [handle: Handle] RETURNS [MailBasics.ItemHeader] ~ { <> <> WITH handle SELECT FROM h: RetrieveHandle => { ih: MailBasics.ItemHeader; IF h.unreadMailboxes=NIL THEN RETURN [[type: MailBasicsItemTypes.lastItem, length: 0]]; [ih, h.currentItem] ¬ h.unreadMailboxes.first.procs.NextItem[h.unreadMailboxes.first.ref]; RETURN[ih]; }; ENDCASE => ERROR; }; <<>> GetItem: PUBLIC PROC [handle: Handle] RETURNS [STREAM] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN [NIL]; IF h.currentItem#NIL THEN RETURN[h.currentItem]; RETURN[h.unreadMailboxes.first.procs.GetItem[h.unreadMailboxes.first.ref]]; }; ENDCASE => ERROR; }; <<>> GetItemAsRope: PUBLIC PROC [handle: Handle] RETURNS [ROPE] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN [NIL]; IF h.currentItem#NIL THEN RETURN[MailUtils.RopeFromStream[h.currentItem]]; RETURN[h.unreadMailboxes.first.procs.GetItemAsRope[h.unreadMailboxes.first.ref]]; }; ENDCASE => ERROR; }; <<>> GetItemViaCallback: PUBLIC PROC [handle: Handle, proc: MailRetrieve.GetItemCallback] ~ { <> CheckForAbort: PROC RETURNS [BOOL] ~ { RETURN[FALSE]; }; WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN; IF h.currentItem#NIL THEN { [] ¬ proc[h.currentItem, CheckForAbort]; RETURN; }; h.unreadMailboxes.first.procs.GetItemViaCallback[h.unreadMailboxes.first.ref, proc]; }; ENDCASE => ERROR; }; <<>> MarkMessage: PUBLIC PROC [handle: Handle] RETURNS [] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN []; h.unreadMailboxes.first.procs.MarkMessage[h.unreadMailboxes.first.ref]; }; ENDCASE => ERROR; }; DeleteMessage: PUBLIC PROC [handle: Handle] RETURNS [] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN []; h.unreadMailboxes.first.procs.DeleteMessage[h.unreadMailboxes.first.ref]; }; ENDCASE => ERROR; }; Accept: PUBLIC PROC [handle: Handle] RETURNS [] ~ { <> WITH handle SELECT FROM h: RetrieveHandle => { IF h.unreadMailboxes=NIL THEN RETURN []; h.unreadMailboxes.first.procs.Accept[h.unreadMailboxes.first.ref]; }; ENDCASE => ERROR; }; ReportChangesProc: PROC[handle: Handle, state: MBXState] = { <> WITH handle SELECT FROM h: RetrieveHandle => { h.lastReportedState ¬ state; IF h.reportChangesProc # NIL THEN TRUSTED { Process.Detach[ FORK h.reportChangesProc[h, state] ]; }; }; ENDCASE => ERROR; }; Intelligible: PROC[timeStamp: MailBasics.Timestamp, sender: ROPE] RETURNS[ROPE] = { nameCount: INT ¬ 0; timeBuf.length ¬ 0; FOR i: INT IN [0..sender.Length[]) DO c: CHAR = sender.Fetch[i]; IF ~(Ascii.Letter[c] OR Ascii.Digit[c]) THEN LOOP; timeBuf ¬ RefText.AppendChar[timeBuf, c]; IF (nameCount ¬ nameCount + 1) >= 8 THEN EXIT; ENDLOOP; timeBuf ¬ RefText.AppendChar[timeBuf, '-]; -- after name timeBuf ¬ Convert.AppendTimeRFC822[timeBuf, MailUtils.GetTimeFromPostmark[timeStamp]]; FOR i: NAT IN [0..timeBuf.length) DO SELECT timeBuf[i] FROM ' , '\t => timeBuf[i] ¬ '-; ': => timeBuf[i] ¬ '.; ENDCASE; ENDLOOP; RETURN[Rope.FromRefText[timeBuf]]; }; timeBuf: REF TEXT ¬ NEW[TEXT[RefText.line]]; <> retrieveProcsList: LIST OF MailRetrieveProcsRef ¬ NIL; RegisterMailRetrieveProcs: PUBLIC ENTRY PROC [procs: MailRetrieveProcsRef] = { retrieveProcsList ¬ CONS[procs, retrieveProcsList]; }; GetRegisteredRetrieveProcsList: PUBLIC ENTRY PROC RETURNS [LIST OF MailRetrieveProcsRef] = { RETURN [ retrieveProcsList ]; }; GetRegisteredRetrieveMailProcs: PUBLIC ENTRY PROC[which: ATOM] RETURNS [MailRetrieveProcsRef] = { FOR rL: LIST OF MailRetrieveProcsRef ¬ retrieveProcsList, rL.rest UNTIL rL = NIL DO IF rL.first.which = which THEN RETURN[rL.first]; ENDLOOP; RETURN[NIL]; }; <<>> <> <<- bad news (badName, badPwd, cantAuth) always wins >> <<- no news (unknown, userOK) always loses>> <<- notEmpty wins over any other definite news>> <<- someEmpty wins over allDown or allEmpty>> stateCombiningTable: ARRAY MBXState OF ARRAY MBXState OF MBXState = [ [ unknown, badName, badPwd, cantAuth, userOK, allDown, someEmpty, allEmpty, notEmpty ] , [ badName, badName, badName, badName, badName, badName, badName, badName, badName ] , [ badPwd, badName, badPwd, badPwd, badPwd, badPwd, badPwd, badPwd, badPwd ] , [ cantAuth, badName, badPwd, cantAuth, cantAuth, cantAuth, cantAuth, cantAuth, cantAuth ] , [ userOK, badName, badPwd, cantAuth, userOK, allDown, someEmpty, allEmpty, notEmpty ] , [ allDown, badName, badPwd, cantAuth, allDown, allDown, someEmpty, someEmpty, notEmpty ] , [ someEmpty, badName, badPwd, cantAuth, someEmpty, someEmpty, someEmpty, someEmpty, notEmpty ] , [ allEmpty, badName, badPwd, cantAuth, allEmpty, someEmpty, someEmpty, allEmpty, notEmpty ] , [ notEmpty, badName, badPwd, cantAuth, notEmpty, notEmpty, notEmpty, notEmpty, notEmpty ] ]; MergeStates: PROC [state, new: MBXState] RETURNS [combined: MBXState] ~ { combined ¬ stateCombiningTable[state][new]; }; <<>> <<>> END.