DIRECTORY GVRetrieve, GVBasics, IO, Process, Rope, RopeIO, CIFS, Time USING [Current], UserExec USING [GetNameAndPassword], PeanutRetrieve, PeanutSendMail, PeanutWindow, ViewerClasses, ViewerOps, ViewerTools, VirtualDesktops, TextEdit, TextNode, TEditDocument, PutGet, TEditDisplay, TEditImpl, TiogaFileOps, TiogaMenuOps, TiogaOps, UserProfile, Icons; PeanutRetrieveImpl: CEDAR MONITOR IMPORTS GVRetrieve, IO, CIFS, Time, UserExec, PeanutSendMail, PeanutWindow, TEditDisplay, VirtualDesktops, Process, PutGet, TextNode, TiogaFileOps, TiogaMenuOps, TiogaOps, TextEdit, TEditImpl, UserProfile, ViewerOps, ViewerTools, Rope EXPORTS PeanutRetrieve = BEGIN OPEN IO, PeanutRetrieve; Viewer: TYPE = ViewerClasses.Viewer; TiogaCTRL: GVBasics.ItemType; ReportRope: PROCEDURE [r: ROPE] = { PeanutWindow.OutputRope[r] }; GetNewMsgs: PUBLIC ENTRY PROC = -- entry proc so only have one retrieve at a time BEGIN ENABLE UNWIND => NULL; InternalGetNewMsgs[]; END; tiogaContents: ViewerTools.TiogaContents _ NEW[ViewerTools.TiogaContentsRec]; GetMailViewer: PUBLIC PROC [name: ROPE] RETURNS [mailViewer: Viewer] = { style: ROPE; mailDoc: TextNode.Ref; newFile: BOOL _ FALSE; mailViewer _ VirtualDesktops.FindViewer[name].viewer; IF mailViewer#NIL THEN RETURN; mailDoc _ PutGet.FromFile[name ! CIFS.Error => { newFile _ TRUE; mailDoc _ TextEdit.DocFromNode[TextEdit.FromRope[name]]; TRUSTED { TiogaOps.PutProp[LOOPHOLE[mailDoc], $Prefix, style _ "(mail) style"] }; CONTINUE }]; mailViewer _ ViewerTools.MakeNewTextViewer[info: [iconic: TRUE, name: name]]; mailViewer.file _ name; mailViewer.data _ mailDoc; TEditImpl.InitTEditDocument[mailViewer]; TiogaMenuOps.DefaultMenus[mailViewer]; TiogaMenuOps.FirstLevelOnly[mailViewer]; }; InternalGetNewMsgs: INTERNAL PROC = BEGIN allOK: BOOL_ TRUE; mailViewer: Viewer; numRetrieved: INT; firstHeader: TiogaFileOps.Ref; StashNewMessages: PROC [retrieveOK: BOOL] RETURNS[doRemoteFlush: BOOL] = BEGIN allOK _ allOK AND retrieveOK; TiogaMenuOps.Save[mailViewer]; RETURN[retrieveOK AND flushRemoteMail]; END; mailViewer _ GetMailViewer["Active.mail"]; [numRetrieved, firstHeader] _ AddNewMessages[StashNewMessages, mailViewer]; IF numRetrieved = 0 THEN { ReportRope["\nNo messages were retrieved"]; RETURN}; TRUSTED { tdd: TEditDocument.TEditDocumentData _ NARROW[mailViewer.data]; TEditDisplay.EstablishLine[tdd, [LOOPHOLE[firstHeader],0]]; ViewerOps.PaintViewer[mailViewer, client] }; IF ~allOK THEN ReportRope["\nSome messages may not have been retrieved"]; IF mailViewer.iconic THEN ViewerOps.OpenIcon[mailViewer]; END; CopyMessages: PUBLIC PROC [to: ROPE, delete: BOOL] = { OPEN TiogaOps; sourceViewer, destViewer: Viewer; destDoc, sourceDoc, destLast, sourceLast, afterSource: Ref; start, end: Location; level: SelectionGrain; caretBefore, pendingDelete: BOOL; lockedPrimary, lockedSecondary, lockedDest, lockedSource: BOOL _ FALSE; TopParent: PROC [node, root: Ref] RETURNS [parent: Ref] = { DO parent _ Parent[node]; IF parent=root THEN RETURN [node]; node _ parent; ENDLOOP }; Cleanup: PROC = { IF lockedPrimary THEN UnlockSel[primary]; IF lockedSecondary THEN UnlockSel[secondary]; IF lockedDest THEN Unlock[destDoc]; IF lockedSource THEN Unlock[sourceDoc]; }; destViewer _ GetMailViewer[to]; LockSel[primary]; lockedPrimary _ TRUE; [sourceViewer, start, end, level, caretBefore, pendingDelete] _ TiogaOps.GetSelection[]; IF sourceViewer=NIL THEN { UnlockSel[primary]; ReportRope["\nSelect message(s)."]; RETURN }; LockSel[secondary]; lockedSecondary _ TRUE; destDoc _ ViewerDoc[destViewer]; Lock[destDoc]; lockedDest _ TRUE; sourceDoc _ ViewerDoc[sourceViewer]; IF sourceDoc#destDoc THEN { Lock[sourceDoc]; lockedSource _ TRUE }; destLast _ LastChild[destDoc]; sourceLast _ TopParent[end.node, sourceDoc]; afterSource _ Next[sourceLast]; SelectBranches[ -- source viewer: sourceViewer, level: branch, caretBefore: FALSE, pendingDelete: delete, which: primary, start: TopParent[start.node, sourceDoc], end: sourceLast]; SelectBranches[ -- destination viewer: destViewer, start: destLast, end: destLast, level: branch, caretBefore: FALSE, pendingDelete: FALSE, which: secondary]; ToSecondary[]; IF ~delete THEN -- restore original selection SetSelection[sourceViewer, start, end, level, caretBefore, pendingDelete, primary] ELSE IF afterSource # NIL THEN SelectPoint[sourceViewer, [afterSource,0], primary] ELSE CancelSelection[primary]; ReportRope[IF delete THEN "\nMoved to " ELSE "\nCopied to "]; ReportRope[to]; Cleanup[]; }; 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, UserExec.GetNameAndPassword[].password ] ; } ; lastStateReported: GVRetrieve.MBXState_ unknown; WatchMailBox: PROC[newState: GVRetrieve.MBXState] = BEGIN 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 UserProfile.Boolean["Peanut.AutomaticNewMail"] THEN TRUSTED {Process.Detach[FORK ReadMail[]]}}; ENDCASE => status_ "\nBad State!"; lastStateReported _ newState; IF status # NIL THEN ReportRope[IF showTime THEN Rope.Concat[status, IO.PutFR[" at %g", time[]]] ELSE status]; END; ReadMail: PROC = TRUSTED {Process.SetPriority[Process.priorityBackground]; GetNewMsgs[] }; MessageState: TYPE = { noMore, wasArchived, wasDeleted, OK, retrieveFailed } ; AddNewMessages: PUBLIC PROC[ FinishedWithServer: PROC[BOOL] RETURNS[BOOL], mailViewer: Viewer] RETURNS[numRetrieved: INT, firstHeader: TiogaFileOps.Ref] = { serverKnown: BOOLEAN _ FALSE; mailDoc: TiogaOps.Ref _ TiogaOps.ViewerDoc[mailViewer]; IF gvRetrieveHandle = NIL THEN { -- Open the connection if it's closed. gvRetrieveHandle _ GVRetrieve.Create[ msgPollingInterval, WatchMailBox ] ; GVRetrieve.NewUser[gvRetrieveHandle, PeanutSendMail.userRName, UserExec.GetNameAndPassword[].password]; } ; SELECT gvRetrieveHandle.MailboxState[] FROM badName, badPwd => GOTO credentialsError; cantAuth => GOTO noServers; ENDCASE; --ok to try numRetrieved_ 0; 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: TiogaFileOps.Ref; serverName: ROPE ; TRUSTED {headerNode _ LOOPHOLE[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. }; TiogaOps.Lock[mailDoc]; DO ENABLE UNWIND => TiogaOps.Unlock[mailDoc]; [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]] }; GetFieldContents: PROC [name: ROPE, maxLen: INT] RETURNS [contents: ROPE] = { start: INT _ Rope.Find[m, name]; IF start<0 THEN contents _ "?" ELSE { end: INT; start _ start + Rope.Size[name]; end _ Rope.Find[m, "\n", start]; IF end <= start THEN contents _ "?" ELSE IF end > start+maxLen THEN contents _ Rope.Concat[Rope.Substr[m, start, maxLen-3], "..."] ELSE contents _ Rope.Substr[m, start, end-start] }}; fullDate, headerDate, headerName, headerSubject, header: ROPE; messageNode: TiogaFileOps.Ref; headerNode _ TiogaFileOps.InsertNode[headerNode, FALSE]; IF firstHeader=NIL THEN firstHeader _ headerNode; TiogaFileOps.SetFormat[headerNode, "header"]; fullDate _ IO.PutFR[NIL, IO.time[LOOPHOLE[timeStamp.time]]]; headerDate _ Rope.Substr[fullDate, 0, Rope.Find[fullDate, " ", 2]]; IF Rope.Equal[gvSender, PeanutSendMail.userRName, FALSE] OR Rope.Equal[gvSender, PeanutSendMail.simpleUserName, FALSE] THEN headerName _ Rope.Concat["To: ", GetFieldContents["\nTo: ", 15]] ELSE headerName _ gvSender; headerSubject _ GetFieldContents["\nSubject: ", 50]; header _ Rope.Cat["\t", headerDate, "\t", headerName, "\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: TextNode.Ref; messageRoot _ PutGet.FromRope[Rope.Cat[Rope.FromChar['\n],m,formatting]]; messageFirst _ TextNode.FirstChild[messageRoot]; TRUSTED { headerN _ LOOPHOLE[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; }; ENDLOOP ; -- Finished reading messages from this server. TiogaOps.Unlock[mailDoc]; 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[PutFR[": retrieved %g messages.", 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"]; }; ReadMessageRecord: PROC RETURNS [messageState: MessageState, m, formatting: ROPE, timeStamp: GVBasics.Timestamp, gvSender: RName] = { ENABLE GVRetrieve.Failed => { ReportRope[ SELECT why FROM communicationFailure => "communication failure", noSuchServer => "no such server", connectionRejected => "server busy", badCredentials => "bad credentials", unknownFailure => "unknown Failure", ENDCASE => "unknown Error" ]; GOTO gvFailed; } ; msgExists, archived, deleted: BOOLEAN; item: GVBasics.ItemHeader; 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 item _ GVRetrieve.NextItem[gvRetrieveHandle]; SELECT item.type FROM PostMark => ERROR; Sender => ERROR; ReturnTo => ERROR; Recipients => NULL; Text => m _ GVRetrieve.ReadItem[gvRetrieveHandle]; TiogaCTRL => formatting _ GVRetrieve.ReadItem[gvRetrieveHandle]; Capability => NULL; Audio => NULL; updateItem => NULL; reMail => NULL; LastItem => EXIT; ENDCASE => LOOP; ENDLOOP; EXITS gvFailed => messageState_ retrieveFailed; } ; TRUSTED { TiogaCTRL _ LOOPHOLE[1013B] }; END. *File: PeanutRetrieveImpl.mesa April 1, 1983 2:17 pm ************************************************************************ mailFile _ RopeIO.FromFile[name ! CIFS.Error => { newFile _ TRUE; mailFile _ name; CONTINUE }]; mailViewer _ ViewerTools.MakeNewTextViewer[info: [iconic: TRUE, name: name]]; TiogaMenuOps.DefaultMenus[mailViewer]; TiogaMenuOps.FirstLevelOnly[mailViewer]; tiogaContents.contents _ mailFile; ViewerTools.SetTiogaContents[viewer: mailViewer, contents: tiogaContents]; mailViewer.file _ name; IF newFile THEN TiogaOps.PutProp[TiogaOps.ViewerDoc[mailViewer], $Prefix, style _ "(mail) style"]; reads any new mail don't do remote flush if ~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 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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. 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. Ê í˜Jšœ8™8J˜šÏk ˜ J˜ Jšœ ˜ Jšœ˜J˜J˜J˜J˜Jšœœ ˜Jšœ œ˜$J˜J˜Jšœ ˜ Jšœ˜J˜ J˜ Jšœ˜J˜ J˜ J˜J˜J˜ J˜ Jšœ ˜ Jšœ ˜ Jšœ ˜ J˜ šœ˜J˜——šœ ˜!Jšœ œÔ˜êJšœ˜J˜—Jšœœœ˜J˜JšœH™HJšœœ˜$J˜Jšœ˜J˜JšÏn œ œœ#˜AJ˜š ž œœœœÏc1˜QJš œœœœœ˜7J˜—Jšœ+œ˜MJ˜š ž œœœœœ˜HJšœ˜ J˜Jšœ œœ˜Jšœ5˜5Jšœ œœœ˜šœ!œ ˜0Jšœ œ˜Jšœ8˜8Jšœœ.˜QJšœ˜ —Jšœ:œ˜MJšœ˜Jšœ˜Jšœ(˜(Jšœ&˜&Jšœ(˜(J˜J˜šœ!™!Jšœœœ™=—Jšœ:œ™MJšœ&™&Jšœ(™(Jšœ"™"JšœJ™JJšœ™šœ ™JšœR™R—Jšœ˜—J˜šžœœœ˜#Jšœ™Jš˜Jšœœœ˜J˜Jšœœ˜Jšœ˜J˜š žœœœœœ˜HJšœ˜Jšœœ ˜Jšœ˜Jšœ$™$Jšœ œ˜'Jšœ˜—J˜Jšœ*˜*JšœK˜Kšœœ˜Jšœ,œ˜4—šœ˜ Jšœ'œ˜?Jšœ!œ˜;Jšœ,˜,—Jšœœ;˜IJšœœ ˜9Jšœ˜J˜—JšœG™GJ™š ž œœœœ œœ ˜EJšœ"˜"Jšœ;˜;J˜J˜Jšœœ˜!Jšœ:œœ˜GJ˜šž œœœ˜;š˜J˜Jšœ œœ˜"J˜Jšœ˜ ——J˜šžœœ˜Jšœœ˜)Jšœœ˜-Jšœ œ˜#Jšœœ˜'J˜—J˜Jšœ˜Jšœ"œ˜'JšœX˜Xšœœœ˜Jšœ8œ˜A—Jšœ&œ˜+Jšœ ˜ Jšœœ˜!Jšœ$˜$Jšœœ#œ˜CJšœ˜Jšœ,˜,Jšœ˜šœŸ ˜Jšœ2œ˜8Jšœ&˜&Jšœ:˜:—šœŸ˜Jšœ3˜3Jšœœœ˜K—J˜šœ œŸ˜-J˜R—Jšœœœœ4˜RJšœ˜Jšœ œœœ˜=Jšœ˜J˜ J˜—J™JšœG™GJ˜JšœœŸ-˜LJšœœœ˜Jšœ%œŸ!˜KJ˜Jšžœœœ˜,šœG™GJ˜J˜J˜J˜—Jšžœœœ˜"šœB™Bšœœœ˜Jšœ6œ˜;—J˜J˜—šžœœœ˜%Jšœ)™)šœœœ˜J˜I—J˜UJ˜J˜—J˜0J˜šž œœ!˜3Jšœ8™8Jš˜Jšœ œœ˜Jšœœ˜ Jšœœœ˜"šœ œœ˜XJšœ'œ˜/—š œ ˜JšœNœ˜UJšœ>œ˜EJšœOœ˜VJšœ=œ˜DJ˜:J˜CJšœ3œ˜;šœ3œ˜9šœœ0˜UJšœœ˜+——Jšœ˜$—J˜šœ œ˜Jš œ œ œœœ ˜Y—Jšœ˜J˜—JšžœœœB˜ZJ˜JšœO™OJšœœ&œ˜NJ˜šžœœœ˜Jš žœœœœœ˜BJšœœ$˜=JšœG™GJšœ[™[Jšœ œœ˜J˜7J˜šœœœŸ&˜I˜J˜>J˜(——J˜J˜—šœ!˜+Jšœœ˜)Jšœ œ ˜JšœŸ ˜J˜—˜J˜—šœŸ˜Jšœœ˜J˜J˜J˜Jšœ œŸ+˜EJšœœœŸ˜@J˜Jšœ<™˜>—Jšœ0˜4——Jšœ9œ˜>Jšœ˜Jšœ1œ˜8Jšœ œœ˜1Jšœ-˜-Jš œ œœœœ˜˜IJ™*—J˜0Jšœ œ˜+Jšœ˜Jšœ1˜1Jšœœ˜4šœœœ˜1J™D—J˜—J˜$J˜J˜J˜—JšœŸ.˜8—J˜J™Jšœ9™9šœ1˜7˜/˜SJšœ˜ ———Jšœœ?˜bJ˜&JšœŸ˜'—J˜Jšœœ œœ ˜)J˜šœŸ'˜.J˜,J˜5J˜2—J˜J˜—šžœœ˜Jšœ,œ˜1Jšœ3˜3JšœS™SJšœ™šœ˜šœ œ˜J˜0J˜)J˜&J˜*J˜)Jšœ˜$—Jšœ˜J˜J˜—Jšœœ˜&J˜Jšœœ˜J˜J˜LJ˜Jšœ œœœ˜BJšœ œœ˜5Jšœœ œœ˜7J˜JšœG™GJšœ3™3—˜J˜Gš˜J˜-šœ ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜8J˜AJšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜—Jšœ˜—š˜J˜)—J˜—J˜šœœ ˜(J˜—Jšœ˜J˜J˜—…—1F1