DIRECTORY BasicTime USING [FromPupTime, GMT, Now, Unpack, Unpacked], CedarProcess USING [SetPriority], Convert USING [RopeFromUnpackedTime], FS USING [Error, ExpandName, GetName, nullOpenFile, Open, OpenFile], GVBasics USING [ItemHeader, ItemType, RName, Timestamp], GVRetrieve USING [Accept, Close, Create, Failed, GetItem, Handle, MailboxState, MBXState, NewUser, NextItem, NextMessage, NextServer, ServerName, ServerState, StartMessage], IO USING [GetBlock, int, PutFR, rope, STREAM, time], MessageWindow USING [Append, Blink], PeanutProfile USING [activeMailFile, automaticNewMail, workingDirectory], PeanutRetrieve USING [], PeanutSendMail USING [simpleUserName, userRName], PeanutWindow USING [dirtyMessageSetIcon, messageSetIcon, OutputRope, SetNewMail], Process USING [Detach], PutGet USING [FromFileC, FromRope], Rope USING [Cat, Concat, Equal, Fetch, Find, FromChar, FromRefText, Index, Length, ROPE, Size, Substr], TEditDisplay USING [EstablishLine], TEditDocument USING [TEditDocumentData], TextEdit USING [DocFromNode, FromRope], TextNode USING [Body, FirstChild, LastSibling], TiogaFileOps USING [InsertNode, SetContents, SetFormat], TiogaMenuOps USING [DefaultMenus, FirstLevelOnly], TiogaOps USING [CancelSelection, GetSelection, LastChild, Location, Lock, LockSel, Next, Parent, PutProp, SelectBranches, SelectionGrain, SelectPoint, SetSelection, ToSecondary, Unlock, UnlockSel, ViewerDoc], TiogaOpsDefs USING [], UserCredentials USING [Get], ViewerClasses USING [Lock, Viewer], ViewerEvents USING [RegisterEventProc, ViewerEvent], ViewerOps USING [AddProp, EnumerateViewers, EnumProc, FetchProp, OpenIcon, PaintViewer, SaveViewer, SetNewVersion], ViewerTools USING [MakeNewTextViewer]; PeanutRetrieveImpl: CEDAR MONITOR IMPORTS BasicTime, CedarProcess, Convert, FS, GVRetrieve, IO, MessageWindow, PeanutProfile, PeanutSendMail, PeanutWindow, Process, PutGet, Rope, TEditDisplay, TextEdit, TextNode, TiogaFileOps, TiogaMenuOps, TiogaOps, UserCredentials, ViewerEvents, ViewerOps, ViewerTools EXPORTS PeanutRetrieve, TiogaFileOps, TiogaOpsDefs = BEGIN ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; RName: TYPE = GVBasics.RName; RefTextNode: TYPE = REF NodeBody; NodeBody: PUBLIC TYPE = TextNode.Body; TiogaCTRL: GVBasics.ItemType = Tioga1; -- an item containing tioga formatting ReportRope: PROCEDURE [r: ROPE] = { PeanutWindow.OutputRope[r] }; GetNewMsgs: PUBLIC ENTRY PROC [open: BOOL _ TRUE] = { ENABLE UNWIND => NULL; [] _ InternalGetNewMsgs[open]; }; peanutProp: ATOM = $PeanutMailFileName; SetMailFileName: PROC[v: Viewer, name: ROPE] = { ViewerOps.AddProp[viewer: v, prop: peanutProp, val: name]; ViewerOps.AddProp[viewer: v, prop: $IconLabel, val: v.label]; }; GetMailFileName: PROC[v: Viewer] RETURNS[ROPE] = { WITH ViewerOps.FetchProp[viewer: v, prop: peanutProp] SELECT FROM rope: ROPE => RETURN[rope]; ENDCASE => RETURN[NIL]; }; FindMailViewer: PUBLIC PROC[name: ROPE] RETURNS[viewer: Viewer _ NIL] = { Test: ViewerOps.EnumProc -- PROC[v: Viewer] RETURNS[continue: BOOL _ TRUE] -- = { rope: ROPE = GetMailFileName[v]; IF rope#NIL AND Rope.Equal[rope, name, FALSE] THEN { viewer _ v; RETURN[FALSE] }; }; ViewerOps.EnumerateViewers[Test]; }; GetMailViewer: PUBLIC ENTRY PROC[name: ROPE] RETURNS [mailViewer: Viewer] = { ENABLE UNWIND => NULL; mailViewer _ InternalGetMailViewer[name]; }; MakeMailDoc: PROC[fileName: ROPE] RETURNS [mailDoc: RefTextNode] = { styleProp: ROPE = "(mail) style"; mailDoc _ TextEdit.DocFromNode[TextEdit.FromRope[fileName]]; TiogaOps.PutProp[mailDoc, $Prefix, styleProp]; }; InternalGetMailViewer: INTERNAL PROC[name: ROPE] RETURNS[mailViewer: Viewer] = { mailViewer _ FindMailViewer[name]; IF mailViewer=NIL THEN { wDir: ROPE _ PeanutProfile.workingDirectory; shortName: ROPE _ name.Concat[".mail"]; longName: ROPE _ FS.ExpandName[name: shortName, wDir: wDir].fullFName; fileName: ROPE _ longName; mailDoc: RefTextNode _ NIL; file: FS.OpenFile _ FS.nullOpenFile; file _ FS.Open[longName ! FS.Error => IF error.code=$unknownFile THEN CONTINUE]; IF file=FS.nullOpenFile THEN { styleProp: ROPE = "(mail) style"; mailDoc _ TextEdit.DocFromNode[TextEdit.FromRope[shortName]]; TiogaOps.PutProp[mailDoc, $Prefix, styleProp]; } ELSE { mailDoc _ PutGet.FromFileC[file]; fileName _ FS.GetName[file].fullFName; }; mailViewer _ ViewerTools.MakeNewTextViewer[info: [ name: longName, file: fileName, label: name, data: mailDoc, icon: PeanutWindow.messageSetIcon, iconic: TRUE], paint: FALSE]; SetMailFileName[mailViewer, name]; ViewerOps.PaintViewer[mailViewer, all]; [] _ ViewerEvents.RegisterEventProc[proc: MessageSetHasBeenEdited, event: edit, filter: mailViewer, before: TRUE]; [] _ ViewerEvents.RegisterEventProc[proc: MessageSetHasBeenSaved, event: save, filter: mailViewer, before: FALSE]; TiogaMenuOps.DefaultMenus[mailViewer]; TiogaMenuOps.FirstLevelOnly[mailViewer]; }; }; InternalGetNewMsgs: INTERNAL PROC [open: BOOL _ TRUE] RETURNS [numRetrieved: INT] = { allOK: BOOL_ TRUE; mailViewer: Viewer; firstHeader: RefTextNode; StashNewMessages: PROC [retrieveOK: BOOL] RETURNS[doRemoteFlush: BOOL] = { allOK _ allOK AND retrieveOK; ViewerOps.SaveViewer[mailViewer]; -- doesn't return until the save is complete RETURN[retrieveOK AND flushRemoteMail AND mailViewer#NIL AND NOT mailViewer.destroyed]; }; mailViewer _ InternalGetMailViewer[PeanutProfile.activeMailFile]; [numRetrieved, firstHeader] _ AddNewMessages[StashNewMessages, mailViewer]; IF numRetrieved = 0 THEN { ReportRope["\nNo messages were retrieved"]; RETURN}; IF mailViewer.iconic AND PeanutProfile.automaticNewMail THEN NULL ELSE { WITH mailViewer.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { TEditDisplay.EstablishLine[tdd, [firstHeader,0]]; ViewerOps.PaintViewer[mailViewer, client]; }; ENDCASE => NULL; }; IF NOT allOK THEN ReportRope["\nSome messages may not have been retrieved"]; IF open AND mailViewer.iconic THEN ViewerOps.OpenIcon[mailViewer]; }; MessageSetHasBeenEdited: PROC[viewer: Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL _ FALSE] = { IF before THEN { viewer.icon _ PeanutWindow.dirtyMessageSetIcon; IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; }; }; MessageSetHasBeenSaved: PROC [viewer: Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS[abort: BOOL _ FALSE] = { IF NOT before THEN { viewer.icon _ PeanutWindow.messageSetIcon; IF viewer.iconic THEN ViewerOps.PaintViewer[viewer: viewer, hint: all]; }; }; CopyMessages: PUBLIC PROC [to: ROPE, delete: BOOL] = { sourceViewer, destViewer: Viewer; destDoc, sourceDoc, destLast, sourceLast, afterSource: RefTextNode; start, end: TiogaOps.Location; level: TiogaOps.SelectionGrain; caretBefore, pendingDelete: BOOL; lockedPrimary, lockedSecondary, lockedDest, lockedSource: BOOL _ FALSE; TopParent: PROC [node, root: RefTextNode] RETURNS [parent: RefTextNode] = { DO parent _ TiogaOps.Parent[node]; IF parent=root THEN RETURN [node]; node _ parent; ENDLOOP }; Cleanup: PROC = { IF lockedPrimary THEN TiogaOps.UnlockSel[primary]; IF lockedSecondary THEN TiogaOps.UnlockSel[secondary]; IF lockedDest THEN TiogaOps.Unlock[destDoc]; IF lockedSource THEN TiogaOps.Unlock[sourceDoc]; }; destViewer _ GetMailViewer[to]; TiogaOps.LockSel[primary]; lockedPrimary _ TRUE; [sourceViewer, start, end, level, caretBefore, pendingDelete] _ TiogaOps.GetSelection[]; IF sourceViewer=NIL OR sourceViewer.class.flavor#$Text THEN { TiogaOps.UnlockSel[primary]; ReportRope["\nSelect message(s)."]; RETURN }; TiogaOps.LockSel[secondary]; lockedSecondary _ TRUE; destDoc _ TiogaOps.ViewerDoc[destViewer]; TiogaOps.Lock[destDoc]; lockedDest _ TRUE; sourceDoc _ TiogaOps.ViewerDoc[sourceViewer]; IF sourceDoc#destDoc THEN { TiogaOps.Lock[sourceDoc]; lockedSource _ TRUE }; destLast _ TiogaOps.LastChild[destDoc]; sourceLast _ TopParent[end.node, sourceDoc]; afterSource _ TiogaOps.Next[sourceLast]; TiogaOps.SelectBranches[ -- source viewer: sourceViewer, level: branch, caretBefore: FALSE, pendingDelete: delete, which: primary, start: TopParent[start.node, sourceDoc], end: sourceLast]; TiogaOps.SelectBranches[ -- destination viewer: destViewer, start: destLast, end: destLast, level: branch, caretBefore: FALSE, pendingDelete: FALSE, which: secondary]; TiogaOps.ToSecondary[]; IF NOT delete THEN -- restore original selection TiogaOps.SetSelection[sourceViewer, start, end, level, caretBefore, pendingDelete, primary] ELSE IF afterSource # NIL THEN TiogaOps.SelectPoint[sourceViewer, [afterSource,0], primary] ELSE TiogaOps.CancelSelection[primary]; Cleanup[]; ReportRope[IF delete THEN "\nMoved to " ELSE "\nCopied to "]; ReportRope[to]; }; msgPollingInterval: INT_ 300; -- Number of seconds between mailbox polling. flushRemoteMail: BOOLEAN_ TRUE; gvRetrieveHandle: GVRetrieve.Handle_ NIL; -- cookie for receiving messages. OpenConnection: PUBLIC PROC[user: RName] = { CloseConnection[]; NewUser[user] ; }; CloseConnection: PUBLIC PROC[] = { IF gvRetrieveHandle # NIL THEN{ GVRetrieve.Close[gvRetrieveHandle]; gvRetrieveHandle_ NIL}; }; NewUser: PUBLIC PROC[user: RName] = { IF gvRetrieveHandle = NIL THEN gvRetrieveHandle _ GVRetrieve.Create[ msgPollingInterval, WatchMailBox ]; GVRetrieve.NewUser[gvRetrieveHandle, user, UserCredentials.Get[].password ] ; } ; lastStateReported: GVRetrieve.MBXState _ unknown; WatchMailBox: PROC[newState: GVRetrieve.MBXState] = { status: ROPE _ NIL; IF newState = unknown THEN RETURN; PeanutWindow.SetNewMail[newState=notEmpty]; 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, someEmpty, allEmpty, notEmpty => NULL; ENDCASE => status _ "Bad MBXState!"; IF status#NIL THEN { PeanutWindow.OutputRope["\n"]; PeanutWindow.OutputRope[status]; }; IF newState=notEmpty AND lastStateReported#notEmpty AND PeanutProfile.automaticNewMail THEN { mailViewer: Viewer = FindMailViewer[PeanutProfile.activeMailFile]; IF mailViewer=NIL OR mailViewer.iconic THEN TRUSTED{Process.Detach[FORK ReadMail[]]}; }; }; ReadMail: ENTRY PROC = { ENABLE UNWIND => NULL; numRetrieved: INT; CedarProcess.SetPriority[background]; IF (numRetrieved _ InternalGetNewMsgs[FALSE])=0 THEN RETURN; MessageWindow.Append[IO.PutFR["New mail retrieved: %g", IO.time[]], TRUE]; MessageWindow.Blink[] }; MessageState: TYPE = { noMore, wasArchived, wasDeleted, OK, retrieveFailed } ; AddNewMessages: PUBLIC PROC[ FinishedWithServer: PROC[BOOL] RETURNS[BOOL], mailViewer: Viewer] RETURNS[numRetrieved: INT, firstHeader: RefTextNode] = { serverKnown: BOOLEAN _ FALSE; mailDoc: RefTextNode _ TiogaOps.ViewerDoc[mailViewer]; IF gvRetrieveHandle = NIL THEN { -- Open the connection if it's closed. gvRetrieveHandle _ GVRetrieve.Create[ msgPollingInterval, WatchMailBox ] ; GVRetrieve.NewUser[gvRetrieveHandle, PeanutSendMail.userRName, UserCredentials.Get[].password]; } ; SELECT gvRetrieveHandle.MailboxState[] FROM badName, badPwd => GOTO credentialsError; cantAuth => GOTO noServers; ENDCASE; --ok to try numRetrieved_ 0; ReportRope[IO.PutFR["\nCheck for new mail for %g: %g", IO.rope[PeanutSendMail.userRName], IO.time[]]]; DO -- Loops over servers. m, formatting: ROPE; messageState: MessageState; timeStamp: GVBasics.Timestamp; gvSender: RName; messages: CARDINAL _ 0; -- the number of messages read from server. archivedReported: BOOLEAN _ FALSE; -- seen an archived message? noMore: BOOLEAN; -- TRUE if no more servers. serverState: GVRetrieve.ServerState; -- The state of the server. headerNode: RefTextNode; serverName: ROPE ; headerNode _ TiogaOps.LastChild[mailDoc]; [noMore, serverState] _ gvRetrieveHandle.NextServer[]; IF noMore THEN EXIT; -- Last server? Then done. serverKnown _ TRUE; serverName _ gvRetrieveHandle.ServerName[]; ReportRope["\n"]; ReportRope[serverName]; ReportRope[": "]; IF serverState # notEmpty THEN { IF serverState = empty THEN ReportRope["empty"] ELSE ReportRope["didn't respond"] ; LOOP; -- Skip to the next server. }; DO [messageState, m, formatting, timeStamp, gvSender] _ ReadMessageRecord[] ; SELECT messageState FROM noMore => EXIT ; wasArchived => IF NOT archivedReported THEN { archivedReported _ TRUE; ReportRope["(archived messages exist)"]}; OK => NULL; wasDeleted => NULL; retrieveFailed => EXIT; ENDCASE => ERROR; IF NOT (messageState = wasDeleted) THEN { DeleteTrailingCRs: PROC [m: ROPE] RETURNS [ROPE] = { len: INT _ Rope.Size[m]; WHILE len > 0 AND Rope.Fetch[m, len-1] = '\n DO len _ len-1; ENDLOOP; RETURN [Rope.Substr[m,0,len]] }; GetField: PROC[message, fieldName: ROPE] RETURNS[contents: ROPE] = { start: INT _ 0; DO cr: INT ~ Rope.Index[s1: message, pos1: start, s2: "\n"]; IF cr=start THEN EXIT; IF Rope.Find[s1: message, s2: fieldName, pos1: start, case: FALSE]=start THEN { i: INT _ start+Rope.Length[fieldName]; IF Rope.Fetch[message, i]=': THEN { i _ i+1; IF Rope.Fetch[message, i]=' THEN i _ i+1; RETURN[Rope.Substr[message, i, cr-i]]; }; }; start _ cr+1; ENDLOOP; RETURN[NIL]; }; GetFieldContents: PROC [name: ROPE, maxLen: INT] RETURNS [ROPE] = { contents: ROPE ~ GetField[m, name]; len: INT ~ Rope.Length[contents]; IF len=0 THEN RETURN["?"] ELSE IF len<=maxLen THEN RETURN[contents] ELSE RETURN[Rope.Concat[Rope.Substr[base: contents, len: maxLen-3], "..."]] }; GetDateFromGMT: PROC[time: BasicTime.GMT] RETURNS[ROPE] = { unpacked: BasicTime.Unpacked = BasicTime.Unpack[time]; day: NAT = unpacked.day; month: ROPE = Convert.RopeFromUnpackedTime[unpacked, months, months]; year: NAT = unpacked.year; RETURN[IO.PutFR["%2g-%g-%02g", IO.int[day], IO.rope[month.Substr[len: 3]], IO.int[year MOD 100]]]; }; AppendNextMessage: PROC = { headerDate, headerName, headerSubject, header: ROPE; messageNode: RefTextNode; headerNode _ TiogaFileOps.InsertNode[headerNode, FALSE]; IF firstHeader=NIL THEN firstHeader _ headerNode; TiogaFileOps.SetFormat[headerNode, "header"]; headerDate _ GetDateFromGMT[BasicTime.FromPupTime[timeStamp.time]]; IF Rope.Equal[gvSender, PeanutSendMail.userRName, FALSE] OR Rope.Equal[gvSender, PeanutSendMail.simpleUserName, FALSE] THEN headerName _ Rope.Concat["To: ", GetFieldContents["To", 15]] ELSE headerName _ GetFieldContents["From", 32]; headerSubject _ GetFieldContents["Subject", 45]; header _ Rope.Cat[ Rope.Concat["\t", headerDate], Rope.Concat["\t", headerName], Rope.Concat["\t", headerSubject] ]; TiogaFileOps.SetContents[headerNode, header]; IF formatting=NIL THEN { messageNode _ TiogaFileOps.InsertNode[headerNode, TRUE]; TiogaFileOps.SetContents[messageNode, DeleteTrailingCRs[m]] } ELSE { messageRoot, messageFirst, messageLast, headerN: RefTextNode; messageRoot _ PutGet.FromRope[Rope.Cat[Rope.FromChar['\n],m,formatting]]; messageFirst _ TextNode.FirstChild[messageRoot]; headerN _ headerNode; headerN.child _ messageFirst; messageLast _ TextNode.LastSibling[messageFirst]; messageLast.last _ TRUE; messageLast.next _ headerN; messageRoot.child _ NIL; messageRoot.props _ NIL; }; ViewerOps.SetNewVersion[mailViewer]; ReportRope["."]; messages _ messages + 1; }; TiogaOps.Lock[mailDoc]; AppendNextMessage[ ! UNWIND => TiogaOps.Unlock[mailDoc] ]; TiogaOps.Unlock[mailDoc]; }; ENDLOOP ; -- Finished reading messages from this server. IF FinishedWithServer[messageState#retrieveFailed] THEN gvRetrieveHandle.Accept[ ! GVRetrieve.Failed => {ReportRope["\nFlush of remote messages failed; you may get these messages again"]; CONTINUE}]; IF messageState#retrieveFailed THEN ReportRope[ IO.PutFR[": retrieved %g messages.", IO.int[messages] ]]; numRetrieved_ numRetrieved + messages; ENDLOOP ; -- End of servers loop, exit. IF NOT serverKnown THEN GOTO noMailboxes; EXITS -- The error reporter for this routine. noMailboxes => ReportRope[" No mail boxes"]; credentialsError => ReportRope[" Credentials error"]; noServers => ReportRope[" No servers responding"]; }; bogusItems: INT _ 0; ReadMessageRecord: PROC RETURNS [messageState: MessageState, m, formatting: ROPE, timeStamp: GVBasics.Timestamp, gvSender: RName] = { ENABLE GVRetrieve.Failed --[why: FailureReason, text: ROPE]-- => { ReportRope[SELECT why FROM communicationFailure => "Communication failure", noSuchServer => "No such server", connectionRejected => "Connection rejected", badCredentials => "Bad credentials", unknownFailure => "Unknown failure", ENDCASE => "Undefined error"]; IF text.Size[]>0 THEN { ReportRope[" -- "]; ReportRope[text] }; GOTO gvFailed; }; msgExists, archived, deleted: BOOLEAN; item: GVBasics.ItemHeader; blockSize: NAT = 500; block: REF TEXT _ NIL; m _ NIL; [msgExists, archived, deleted] _ GVRetrieve.NextMessage[ gvRetrieveHandle ]; IF archived THEN messageState _ wasArchived ELSE messageState_ OK; IF deleted THEN { messageState _ wasDeleted; RETURN}; IF NOT msgExists THEN { messageState _ noMore; RETURN}; [timeStamp, gvSender, ] _ GVRetrieve.StartMessage[ gvRetrieveHandle ] ; DO ReadItem: PROC[h: GVRetrieve.Handle] RETURNS[rope: ROPE _ NIL] = { stream: IO.STREAM = GVRetrieve.GetItem[h]; IF block=NIL THEN block _ NEW[TEXT[blockSize]]; WHILE stream.GetBlock[block]>0 DO rope _ rope.Concat[Rope.FromRefText[block]]; ENDLOOP; }; item _ GVRetrieve.NextItem[gvRetrieveHandle]; SELECT item.type FROM PostMark, Sender, ReturnTo => bogusItems _ bogusItems+1; Recipients => NULL; Text => m _ ReadItem[gvRetrieveHandle]; TiogaCTRL => formatting _ ReadItem[gvRetrieveHandle]; Capability => NULL; Audio => NULL; updateItem => NULL; reMail => NULL; LastItem => EXIT; ENDCASE => LOOP; ENDLOOP; EXITS gvFailed => messageState_ retrieveFailed; } ; END. êPeanutRetrieveImpl.mesa Copyright c 1984, 1985, 1986 Xerox Corporation. All rights reserved. Created by Paxton, April 1, 1983 2:17 pm Last edited by Bill Paxton, May 12, 1983 4:11 pm Last edited by: Pausch, July 14, 1983 3:26 pm Michael Plass, July 30, 1986 3:02:43 pm PDT Doug Wyatt, September 6, 1985 2:43:38 pm PDT Last Edited by: Gasbarro June 16, 1986 5:03:42 pm PDT Pier, May 21, 1986 3:23:37 pm PDT ************************************************************************ Enumerate top level viewers, looking for one with a matching $PeanutMailFile property Make this an entry so don't have two Get's at once for same mail file reads any new mail don't do remote flush if NOT retrieveOK *********************************************************************** *********************************************************************** This establishes a retrieve connection, and sets up a Mail Polling proc This closes the connection, and invalidates the connection handle. Establish a new user on this connection. This is called when the condition of the mailbox changes OldWatchMailBox: PROC[newState: GVRetrieve.MBXState] = { This is called when the condition of the mailbox changes ActiveMailFileNotOpen: PROC RETURNS [BOOL] = { mailViewer: Viewer = FindMailViewer[PeanutProfile.activeMailFile]; RETURN [mailViewer=NIL OR mailViewer.iconic] }; showTime: BOOL_ TRUE; status: ROPE; IF newState = unknown THEN RETURN; IF (lastStateReported = notEmpty) AND (newState = someEmpty OR newState = allEmpty) THEN { status_ NIL; PeanutWindow.SetNewMail[FALSE] } ELSE SELECT newState FROM badName => {status_ "\nYour user name is invalid, please log in"; showTime_ FALSE}; badPwd => {status_ "\nYour password is invalid"; showTime_ FALSE}; cantAuth => {status_ "\nCan't check your credentials at this time"; showTime_ FALSE}; userOK => {status_ "\nYour credentials are OK"; showTime_ FALSE}; allDown => status_ "\nAll of the mail servers are down"; someEmpty => status_ "\nAll of the mail servers checked are empty"; allEmpty => {status_ NIL; PeanutWindow.SetNewMail[FALSE]}; notEmpty => {status_ NIL; PeanutWindow.SetNewMail[TRUE]; IF lastStateReported#notEmpty AND PeanutProfile.automaticNewMail AND ActiveMailFileNotOpen[] THEN TRUSTED {Process.Detach[FORK ReadMail[]]} }; ENDCASE => status_ "\nBad State!"; lastStateReported _ newState; IF status # NIL THEN { ReportRope[status]; IF showTime THEN ReportRope[IO.PutFR[" at %g", IO.time[]]]; }; }; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This is the routine that actually reads the mail & makes the log entry. It calls FinishedWithServer to commit the log before it flushes any particular mail server. Cycle through the servers, until you find one that has mail. If it has mail, then go for it. Step through the servers. Returns a string of the form dd-mmm-yy (for example, 17-Nov-83) Restore leading CR. Get back a root node. Clear out the links from the messageRoot to help garbage collection. Flush the mailbox if desired, we've stashed the messages. This routine reads the messages on this connection, returning messageState = noMore when there aren't any more. Now read all the items in the message, terminating on the LastItem, and skipping the ones that we're not yet interested in. ÊŸ˜codešœ™Kšœ Ïmœ:™EKšœ(™(K™0Kšœ-™-K™+K™,K™5K™!—K˜šÏk ˜ Kšœ žœžœ˜:Kšœ žœ˜!Kšœžœ˜%Kšžœžœ<˜DKšœ žœ*˜8Kšœ žœ˜­Kšžœžœžœ˜4Kšœžœ˜$Kšœžœ6˜IKšœžœ˜Kšœžœ˜1Kšœ žœ?˜QKšœžœ ˜Kšœžœ˜#KšœžœIžœ˜gKšœ žœ˜#Kšœžœ˜(Kšœ žœ˜'Kšœ žœ!˜/Kšœ žœ&˜8Kšœ žœ ˜2Kšœ žœÂ˜ÐKšœ žœ˜Kšœžœ˜Kšœžœ˜#Kšœ žœ"˜4Kšœ žœd˜sKšœ žœ˜&K˜—KšÐlnœžœž˜!Kšžœ#žœžœÒ˜ŽKšžœ+˜2Kšœž˜K˜KšœH™HKšžœžœžœ˜Kšœžœ˜$Kšœžœ˜K˜Kšœ žœžœ ˜!Kšœ ž œ˜&K˜Kšœ'Ïc&˜MK˜KšÏn œž œžœ#˜AK˜š ¡ œžœžœžœžœžœ˜3Kšœžœžœžœ#˜:K˜—Kšœ žœ˜'K˜š¡œžœžœ˜0Kšœ:˜:Kšœ=˜=K˜K˜—š¡œžœ žœžœ˜2šžœ2žœž˜AKšœžœžœ˜Kšžœžœžœ˜—K˜K˜—š ¡œžœžœžœžœžœ˜IKšœU™Ušœ 4œ˜QKšœžœ˜ Kšžœžœžœžœžœžœžœ˜QK˜—K˜!K˜K˜—š ¡ œžœž œžœžœ˜MK™EKšžœžœžœ˜Kšœ)˜)K˜K˜—š¡ œžœ žœžœ˜DKšœ žœ˜!Kšœ<˜žœ™EKšœOžœ™VKšœ=žœ™DK™:K™CKšœžœžœ™;šœžœžœ™9Kšžœžœ™@Kšžœžœžœžœ ™JKšœ™—Kšžœ™$—K™šžœ žœžœ™Kšœ™Kšžœ žœ žœžœ ™;K™—Kšœ™K™—š¡œžœžœ˜Kšžœžœžœ˜Kšœžœ˜Kšœ%˜%Kšžœ$žœžœžœ˜K˜ ——K˜K˜—šžœ!ž˜+Kšœžœ˜)Kšœ žœ ˜Kšžœ  ˜K˜—K˜K˜šœ žœ+žœ-˜gK˜—šžœ ˜Kšœžœ˜K˜K˜K˜Kšœ žœ +˜EKšœžœžœ ˜@K˜Kšœ<™