DIRECTORY BasicTime USING [Now], FS, MailBasics, MailBasicsFileTypes, MailBasicsItemTypes, MailMessage USING [ReadOneMessage], MailRetrieve USING [Failed, Handle, MBXState, ServerState, Accept, Close, Create, MailboxState, NextMessage, NextServer, NewUser, ServerName, StartMessage], MailUtils USING [Credentials], Idle USING [IdleHandler, IdleRegistration, RegisterIdleHandler], IO, Process USING [Detach, Pause, SecondsToTicks], RefText, Rope, WalnutKernelDefs, WalnutNewMail USING [], WalnutDefs USING [CheckReportProc, Error, RetrieveState, ServerResponse, WalnutOpsHandle], WalnutOps USING [ EndNewMail], WalnutOpsInternal, WalnutRoot USING [FlushMailStream, GetOpsHandleList, RootHandle, RootHandleRec], WalnutStream USING [AbortStream, IdOnFileWithSender, WriteEntry]; WalnutNewMailImpl: CEDAR MONITOR IMPORTS BasicTime, FS, Idle, IO, MailMessage, MailRetrieve, Process, Rope, WalnutDefs, WalnutOps, WalnutRoot, WalnutStream EXPORTS WalnutDefs, WalnutNewMail = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; CheckReportProc: TYPE = WalnutDefs.CheckReportProc; WalnutOpsHandle: TYPE = WalnutDefs.WalnutOpsHandle; RootHandle: TYPE = WalnutRoot.RootHandle; RootHandleRec: PUBLIC TYPE = WalnutRoot.RootHandleRec; KernelHandle: TYPE = WalnutOpsInternal.KernelHandle; KernelHandleRec: PUBLIC TYPE = WalnutOpsInternal.KernelHandleRec; TiogaCTRL: MailBasics.ItemType = MailBasicsItemTypes.tioga1; MailHandle: TYPE = WalnutOpsInternal.MailHandle; MailHandleObject: PUBLIC TYPE = WalnutOpsInternal.MailHandleObject; copyBuffer: REF TEXT ¬ NEW[TEXT[RefText.page]]; newMailCreateMsg: WalnutKernelDefs.MsgLogEntry ¬ NEW[CreateMsg WalnutKernelDefs.LogEntryObject]; idled: BOOL ¬ FALSE; -- this is true if the machine has been put in the idle state idleReg: Idle.IdleRegistration; msgPollingInterval: INT ¬ 300; -- seconds between pollings checkingRope: ROPE = "Checking for new mail ..."; EnableMailRetrieval: PUBLIC ENTRY PROC[ opsHandle: WalnutOpsHandle, registeredUsers: LIST OF MailUtils.Credentials, reportProc, progressProc: CheckReportProc, getMailLog: PROC[WalnutOpsHandle] RETURNS[STREAM], recordMailInfo: PROC[opsHandle: WalnutOpsHandle, logLen: INT, server: ROPE, num: INT] RETURNS[BOOL], notifyWhenMailRetrieved: PROC[opsHandle: WalnutOpsHandle, ok: BOOL, someMail: BOOL] ] = { ENABLE UNWIND => NULL; this: MailHandle; IF idleReg = NIL THEN idleReg ¬ Idle.RegisterIdleHandler[NewMailIdleProc]; IF opsHandle.kernelHandle.mailHandle # NIL THEN RETURN; this ¬ NEW[WalnutOpsInternal.MailHandleObject ¬ [ procForReporting: reportProc, procForProgress: progressProc, notifyProc: notifyWhenMailRetrieved, registeredUsers: registeredUsers, lastStatus: checkingRope, getMailLogProc: getMailLog, recordMailInfoProc: recordMailInfo, mrHandle: MailRetrieve.Create[ msgPollingInterval, WatchMailBox ] ] ]; opsHandle.kernelHandle.mailHandle ¬ this; TRUSTED{ Process.Detach[ FORK StartIt[this.mrHandle, this.registeredUsers] ] } }; StartIt: PROC[mH: MailRetrieve.Handle, users: LIST OF MailUtils.Credentials] = { FOR rnL: LIST OF MailUtils.Credentials ¬ users, rnL.rest UNTIL rnL = NIL DO MailRetrieve.NewUser[mH, rnL.first.rName, rnL.first.password] ENDLOOP; }; DisableMailRetrieval: PUBLIC ENTRY PROC[opsHandle: WalnutOpsHandle] = { ENABLE UNWIND => NULL; this: MailHandle = opsHandle.kernelHandle.mailHandle; opsHandle.kernelHandle.mailHandle ¬ NIL; IF this # NIL THEN IF this.mailInProgress THEN this.needsClosing ¬ TRUE ELSE { this.hasBeenClosed ¬ TRUE; TRUSTED { Process.Detach[ FORK MailRetrieve.Close[this.mrHandle] ] } }; }; GetLastMailBoxStatus: PUBLIC ENTRY PROC[opsHandle: WalnutOpsHandle] RETURNS[mbxState: MailRetrieve.MBXState, status: ROPE] = { ENABLE UNWIND => NULL; this: MailHandle = opsHandle.kernelHandle.mailHandle; IF this = NIL THEN RETURN[unknown, NIL]; RETURN[this.lastStateReported, this.lastStatus] }; CheckMailBoxes: PUBLIC ENTRY PROC[opsHandle: WalnutOpsHandle] = { ENABLE UNWIND => NULL; this: MailHandle = opsHandle.kernelHandle.mailHandle; IF ( this = NIL ) OR ( this.mailInProgress ) THEN RETURN; this.tryForNewMail ¬ FALSE; this.mailInProgress ¬ TRUE; TRUSTED { Process.Detach[FORK AutoNewMailProc[opsHandle, this]] } }; CheckMailReport: CheckReportProc = { this: MailHandle = opsH.kernelHandle.mailHandle; IF ( this # NIL ) AND ( this.procForReporting # NIL ) THEN this.procForReporting[opsH, format, v1, v2, v3]; }; ProgressReport: CheckReportProc = { this: MailHandle = opsH.kernelHandle.mailHandle; IF this = NIL THEN RETURN; -- race condition IF this.procForProgress # NIL THEN this.procForProgress[opsH, format, v1, v2, v3]; }; NewMailIdleProc: ENTRY Idle.IdleHandler = { ENABLE UNWIND => NULL; idled ¬ reason = becomingIdle; FOR wL: LIST OF WalnutOpsHandle ¬ WalnutRoot.GetOpsHandleList[], wL.rest UNTIL wL = NIL DO this: MailHandle; opsH: WalnutOpsHandle = wL.first; IF ( opsH.kernelHandle = NIL ) OR ( ( this ¬ opsH.kernelHandle.mailHandle ) = NIL ) THEN LOOP; IF this.tryForNewMail AND NOT idled THEN TRUSTED { this.tryForNewMail ¬ FALSE; this.mailInProgress ¬ TRUE; Process.Detach[FORK AutoNewMailProc[wL.first, this]] }; ENDLOOP; }; WatchMailBox: ENTRY PROC[mrH: MailRetrieve.Handle, newState: MailRetrieve.MBXState] = { ENABLE UNWIND => NULL; status: ROPE; this: MailHandle; opsH: WalnutOpsHandle; IF newState = unknown THEN RETURN; opsH ¬ FindOpsHandle[mrH]; IF ( opsH = NIL ) OR ( this ¬ opsH.kernelHandle.mailHandle ) = NIL THEN RETURN; IF ( this.lastStateReported = notEmpty ) AND ( ( newState = someEmpty ) OR ( newState = allEmpty ) ) THEN status ¬ "New mail was read" ELSE { SELECT newState FROM badName => status ¬ "Your user name is invalid, please log in"; badPwd => status ¬ "Your password is invalid"; cantAuth => status ¬ "Can't check your credentials at this time"; userOK => status ¬ "Your credentials are OK"; allDown => status ¬ "All of the mail servers are down"; someEmpty => status ¬ "All of the mail servers checked are empty"; allEmpty => status ¬ "There is no new mail"; notEmpty => status ¬ "You have new mail"; ENDCASE => status ¬ "Bad State!"; }; this.lastStateReported ¬ newState; this.lastStatus ¬ status; this.tryForNewMail ¬ NOT this.mailInProgress AND ( ( this.tryForNewMail ) OR ( newState = notEmpty ) ); IF ( this.tryForNewMail ) AND ( NOT idled ) THEN TRUSTED { this.tryForNewMail ¬ FALSE; this.mailInProgress ¬ TRUE; Process.Detach[FORK AutoNewMailProc[FindOpsHandle[mrH], this]] }; }; MailFetchDone: ENTRY PROC[this: MailHandle] = { IF ( this.needsClosing ) AND ( NOT this.hasBeenClosed ) THEN { this.hasBeenClosed ¬ TRUE; TRUSTED { Process.Detach[ FORK MailRetrieve.Close[this.mrHandle] ]}; }; this.mailInProgress ¬ FALSE }; AutoNewMailProc: PROC[opsH: WalnutOpsHandle, this: MailHandle] = { ok, someMail: BOOL; IF this.registeredUsers = NIL THEN RETURN; DO [ok, someMail] ¬ DoNewMail[opsH, this]; IF ok THEN EXIT; IF ( this.notifyProc # NIL ) THEN this.notifyProc[opsH, ok, someMail]; Process.Pause[Process.SecondsToTicks[60]]; ENDLOOP; MailFetchDone[this]; IF ( this.notifyProc # NIL ) THEN this.notifyProc[opsH, ok, someMail]; }; FindOpsHandle: PROC[mrH: MailRetrieve.Handle] RETURNS[opsH: WalnutOpsHandle] = { FOR opsL: LIST OF WalnutOpsHandle ¬ WalnutRoot.GetOpsHandleList[], opsL.rest UNTIL opsL = NIL DO IF ( opsL.first.kernelHandle.mailHandle # NIL ) AND ( opsL.first.kernelHandle.mailHandle.mrHandle = mrH ) THEN RETURN[opsL.first]; ENDLOOP; RETURN[NIL]; }; DoNewMail: PROC[opsH: WalnutOpsHandle, mH: MailHandle] RETURNS[ok: BOOL, someMail: BOOL] = { mailStream: STREAM; serverKnown: BOOL ¬ FALSE; sr: WalnutDefs.ServerResponse; errMsg: ROPE; err: ROPE = " during new mail; retrieval abandoned"; someMail ¬ FALSE; ok ¬ FALSE; BEGIN ENABLE BEGIN WalnutDefs.Error => { errMsg ¬ "\n WalnutDefs Error during New Mail; retrieval abandoned"; GOTO noGood; }; FS.Error => { errMsg ¬ error.explanation.Concat[err]; GOTO noGood}; IO.Error => { ed: FS.ErrorDesc; < CONTINUE];>> IF ed.explanation # NIL THEN errMsg ¬ ed.explanation.Concat[err] ELSE errMsg ¬ "IO Error during new mail; retrieval abandoned"; GOTO noGood }; END; IF CheckForStopping[mH] THEN RETURN[TRUE, FALSE]; IF mH.getMailLogProc = NIL THEN RETURN[FALSE, FALSE]; -- had been shut down IF ( mailStream ¬ mH.getMailLogProc[opsH] ) = NIL THEN { ProgressReport[opsH, "\tCan't get newMailLog for %g @ %g\n", [rope[mH.registeredUsers.first.rName.name]], [time[BasicTime.Now[]]] ]; RETURN[FALSE, FALSE] }; IF CheckForStopping[mH] THEN RETURN[TRUE, FALSE]; ProgressReport[opsH, "\n ~~~ Retrieving mail for %g @ %g\n", [rope[mH.registeredUsers.first.rName.name]], [time[BasicTime.Now[]]] ]; mailStream.SetIndex[mailStream.GetLength[]]; SELECT MailRetrieve.MailboxState[mH.mrHandle] FROM badName => { msg: ROPE ~ "\nSome mailbox reported badName - possibly no mailBox\n"; CheckMailReport[opsH, "%g\n", [rope[errMsg]] ]; ProgressReport[opsH, "%g\n", [rope[msg]] ]; }; badPwd => { msg: ROPE ~ "\nSome mailbox reported badPwd\n"; CheckMailReport[opsH, "%g\n", [rope[errMsg]] ]; ProgressReport[opsH, "%g\n", [rope[msg]] ]; }; cantAuth => { msg: ROPE ~ "\nSome server not found\n"; CheckMailReport[opsH, "%g\n", [rope[errMsg]] ]; ProgressReport[opsH, "%g\n", [rope[msg]] ]; }; ENDCASE; --ok to try DO -- Loops over servers. state: WalnutDefs.RetrieveState; num: INT ¬ 0; -- the number of messages read from server. noMore: BOOLEAN; -- TRUE if no more servers. serverState: MailRetrieve.ServerState; -- The state of the server. thisServer: MailBasics.RName; BEGIN ENABLE UNWIND => { DO [noMore, serverState] ¬ MailRetrieve.NextServer[mH.mrHandle]; -- cycle thru servers IF ~noMore THEN LOOP; ENDLOOP; }; -- Step through the servers. IF CheckForStopping[mH] THEN RETURN[TRUE, FALSE]; [noMore, serverState] ¬ MailRetrieve.NextServer[mH.mrHandle]; IF noMore THEN EXIT; -- Last server? Then done. serverKnown ¬ TRUE; thisServer ¬ MailRetrieve.ServerName[mH.mrHandle]; ProgressReport[opsH, "(%g) %g: ", [atom[thisServer.ns]], [rope[thisServer.name]] ]; SELECT serverState FROM unknown => state ¬ $didNotRespond; empty => state ¬ $OK; notEmpty => { curPos: INT ¬ mailStream.GetLength[]; [num, state] ¬ DrainServer[mailStream, opsH, mH]; IF num = -1 THEN { mailStream.SetLength[curPos]; WalnutRoot.FlushMailStream[opsH] }; }; ENDCASE; IF serverState = notEmpty AND state = $OK AND okToFlush THEN { sr.state ¬ state; sr.num ¬ num; IF num # -1 THEN { IF ~mH.recordMailInfoProc[opsH, mailStream.GetLength[], thisServer.name, num] THEN { ReleaseNewMail[opsH, mailStream]; ok ¬ FALSE; RETURN }; MailRetrieve.Accept[mH.mrHandle ! MailRetrieve.Failed => {state ¬ $FlushFailed; CONTINUE}]; someMail ¬ TRUE; }; }; IF mH.procForProgress # NIL THEN { SELECT serverState FROM unknown => ProgressReport[opsH, " did not respond\n"]; empty => ProgressReport[opsH, " empty\n"]; notEmpty => IF num = -1 THEN ProgressReport[opsH, " retrieval abandoned\n"] ELSE ProgressReport[opsH, " %g messages\n", [integer[num]] ]; ENDCASE; IF state = $FlushFailed THEN ProgressReport[opsH, "\n Flush of (%g) %g failed; you may get some messages again\n", [atom[thisServer.ns]], [rope[thisServer.name]] ]; }; END; ENDLOOP; -- End of servers loop, exit. ok ¬ TRUE; WalnutOps.EndNewMail[opsH]; -- release the newMailLog IF NOT serverKnown THEN CheckMailReport[opsH, "NoMailboxes\n"]; EXITS noGood => { CheckMailReport[opsH, "%g\n", [rope[errMsg]] ]; ProgressReport[opsH, "%g\n", [rope[errMsg]] ]; ReleaseNewMail[opsH, mailStream]; -- release the newMailLog }; END; }; ReleaseNewMail: PROC[opsH: WalnutOpsHandle, strm: STREAM] = { IF strm # NIL THEN WalnutStream.AbortStream[strm ! IO.Error, FS.Error => CONTINUE]; -- ignore errors WalnutOps.EndNewMail[opsH ! WalnutDefs.Error => CONTINUE]; }; okToFlush: BOOL ¬ TRUE; -- for debugging DrainServer: PROC[strm: STREAM, opsH: WalnutOpsHandle, mH: MailHandle] RETURNS[num: INT, state: WalnutDefs.RetrieveState] = { ENABLE UNWIND => NULL; msgState: MsgState; num ¬ 0; DO [msgState, state] ¬ ReadMessageItems[strm, opsH, mH]; SELECT msgState FROM noMore => EXIT; OK, wasDeleted => NULL; retrieveFailed => {num ¬ -1; EXIT}; ENDCASE => ERROR; IF NOT (msgState = wasDeleted) THEN { IF (num ¬ num + 1) MOD 10 = 0 THEN ProgressReport[opsH, "! "] ELSE ProgressReport[opsH, "."]; -- report progress }; ENDLOOP; }; MsgState: TYPE = {OK, retrieveFailed, noMore, wasDeleted}; ReadMessageItems: PROC[strm: STREAM, opsH: WalnutOpsHandle, mH: MailHandle] RETURNS [msgState: MsgState, state: WalnutDefs.RetrieveState] = { -- This routine reads the next message on this connection, returning MsgState = noMore when there aren't any more. ENABLE MailRetrieve.Failed => { state ¬ why; GOTO thisFailed; }; msgExists, archived, deleted, ok: BOOLEAN; timeStamp: MailBasics.Timestamp; sender: MailBasics.RName; IF CheckForStopping[mH] THEN { ProgressReport[opsH, " stopping retrieval\n"]; RETURN[retrieveFailed, $OK]; }; msgState ¬ OK; state ¬ $OK; [msgExists, archived, deleted] ¬ MailRetrieve.NextMessage[mH.mrHandle]; IF deleted THEN { msgState ¬ wasDeleted; RETURN}; IF NOT msgExists THEN { msgState ¬ noMore; RETURN}; -- Now read all the items in the message, terminating on the LastItem, and skipping the ones that we're not yet interested in. [timeStamp, sender, ] ¬ MailRetrieve.StartMessage[mH.mrHandle] ; ok ¬ MsgToStream[strm, timeStamp, sender, opsH, mH]; IF ~ok THEN msgState ¬ retrieveFailed; EXITS thisFailed => msgState ¬ retrieveFailed; }; CheckForStopping: ENTRY PROC[mH: MailHandle] RETURNS[stop: BOOL] = { IF (stop ¬ mH.needsClosing) AND ~mH.hasBeenClosed THEN { mH.hasBeenClosed ¬ TRUE; TRUSTED { Process.Detach[ FORK MailRetrieve.Close[mH.mrHandle] ] }; }; }; MsgToStream: PROC[strm: STREAM, timeStamp: MailBasics.Timestamp, sender: MailBasics.RName, opsH: WalnutOpsHandle, mH: MailHandle] RETURNS[ok: BOOL] = { curPos: INT; msg: ROPE = WalnutStream.IdOnFileWithSender[timeStamp, sender]; newMailCreateMsg.msg ¬ msg; -- other fields are set later & entry is re-written curPos ¬ WalnutStream.WriteEntry[strm, newMailCreateMsg, -1]; [ , , newMailCreateMsg.textLen, newMailCreateMsg.formatLen, ] ¬ MailMessage.ReadOneMessage[mH.mrHandle, timeStamp, sender.name, strm]; IF newMailCreateMsg.textLen = -1 THEN { strm.SetLength[curPos]; RETURN[FALSE]}; [] ¬ WalnutStream.WriteEntry[strm, newMailCreateMsg, curPos]; WalnutRoot.FlushMailStream[opsH]; RETURN[TRUE]; }; END. ώ WalnutNewMailImpl.mesa Copyright Σ 1984, 1988, 1989, 1992 by Xerox Corporation. All rights reserved. Willie-Sue, November 27, 1989 4:16:54 pm PST Walnut New Mail Operations Implementation Terry, August 30, 1990 11:40 am PDT Willie-s, August 12, 1993 11:11 am PDT WalnutSendOps USING [TiogaCTRL], Types TiogaCTRL: MailBasics.ItemType = WalnutSendOps.TiogaCTRL; Variables Procedures Watch the mailbox & retrieve mail when available make sure that only one idle proc is registered here! This is called when the condition of the mailbox changes If there is new mail, then tryForNewMail is true, unless idle is FALSE and the new mail retrieval process succeeds Cycle through the servers, until you find one that has mail. now we can flush the server Reads mail from the next grapevine server via mH. Write a message on the newMailLog stream writes a createMsg entry, then copies the message from grapevine to the stream, returns ok if there were no errors; flushes the stream; catches errors and returns FALSE ΚG•NewlineDelimiter –(cedarcode) style™codešΟb™Kšœ ΟeœC™NK™,K™Kšœ)™)K™#K™&K™—šΟk ˜ Kšœ Ÿœ˜KšŸœ˜Kšœ ˜ K˜K˜Kšœ Ÿœ˜#Kšœ ŸœŠ˜œKšœ Ÿœ˜KšœŸœ6˜@KšŸœ˜KšœŸœ!˜.Kšœ˜Kšœ˜K˜KšœŸœ˜Kšœ ŸœJ˜ZKšœ Ÿœ˜Kšœ˜Kšœ Ÿœ@˜PKšœŸœ ™ Kšœ Ÿœ/˜AK˜—šœŸœŸ˜ K˜šŸœ ŸœŸœ[˜zK˜—šŸ˜Kšœ˜K˜—KšœŸ˜K™K™š™K˜KšŸœŸœŸœ˜KšŸœŸœŸœŸœ˜KšœŸœ˜3KšœŸœ˜3Kšœ Ÿœ˜)KšœŸ œ˜6KšœŸœ"˜4KšœŸœŸœ%˜AK˜K™9K˜KšœŸœ˜KšŸœŸœ&˜DK˜—KšœŸ˜Kšœ˜—K˜š‘œŸœ-˜BKšœŸœ˜KšŸœŸœŸœŸœ˜*šŸ˜K˜'KšŸœŸœŸœ˜KšŸœŸœŸœ%˜FKšœ*˜*KšŸœ˜—Kšœ˜KšŸœŸœŸœ%˜FKšœ˜K˜—š‘ œŸœŸœ˜Pš ŸœŸœŸœ<ŸœŸœŸ˜`Kš Ÿœ(ŸœŸœ7ŸœŸœ ˜‚KšŸœ˜—KšŸœŸœ˜ K˜K˜—š ‘ œŸœ(ŸœŸœ Ÿœ˜\Kšœ Ÿœ˜Kšœ ŸœŸœ˜K˜KšœŸœ˜ KšœŸœ+˜4Kšœ Ÿœ˜KšœŸœ˜ K˜šŸœŸœŸ˜šœ˜K˜DKšŸœ˜ K˜—KšŸœ4Ÿœ ˜CšŸœ ˜ KšœŸœ ˜Kš œŸœŸœŸœ Ÿœ˜EšŸœŸœŸ˜K˜#KšŸœ:˜>—KšŸœ˜ K˜—KšŸœ˜—K˜Kš ŸœŸœŸœŸœŸœ˜1K˜Kš ŸœŸœŸœŸœŸœŸœ ˜KšŸœ,ŸœŸœ˜8Kšœ„˜„KšŸœŸœŸœ˜K˜—K˜Kš ŸœŸœŸœŸœŸœ˜1K˜Kšœ„˜„Kšœ,˜,K˜šŸœ(Ÿ˜2šœ ˜ KšœŸœ=˜FKšœ/˜/Kšœ+˜+K˜—šœ ˜ KšœŸœ&˜/Kšœ/˜/Kšœ+˜+K˜—šœ ˜ KšœŸœ˜(Kšœ/˜/Kšœ+˜+K˜—KšŸœ  ˜—K˜šŸœ ˜Kšœ ˜ KšœŸœ +˜:—K™šœ<™ ˜SKšŸœ ŸœŸœ˜KšŸœ˜—Kšœ˜—Kš ˜Kš ŸœŸœŸœŸœŸœ˜1K˜K˜=KšŸœŸœŸœ ˜2KšœŸœ˜K˜K˜2KšœS˜SšŸœ Ÿ˜K˜"K˜šœ ˜ KšœŸœ˜%K˜1šŸœ Ÿœ˜Kšœ˜Kšœ ˜ K˜—K˜—KšŸœ˜—K˜šŸœŸœ Ÿœ Ÿœ˜>K˜K˜ šŸœ Ÿœ˜šŸœK˜MKšŸœ*ŸœŸœ˜>—Kšœ™KšœPŸœ˜[Kšœ Ÿœ˜K˜—K˜—K˜šŸœŸœŸœ˜"šŸœ Ÿ˜Kšœ6˜6Kšœ*˜*šœ ˜ šŸœ Ÿœ/˜?KšŸœ9˜=——KšŸœ˜—KšŸœŸœŠ˜¦K˜—KšŸœ˜—KšŸœ ˜&K˜KšœŸœ˜ Kšœ ˜6KšŸœŸœ Ÿœ(˜?šŸ˜šœ ˜ Kšœ/˜/Kšœ.˜.Kšœ# ˜