<> <> <> <> DIRECTORY GVRetrieve, GVBasics, IO, MessageWindow, Process, Rope, RopeIO, CIFS, Time USING [Current], UserCredentials USING [GetUserCredentials], PeanutRetrieve, PeanutSendMail, PeanutWindow, ViewerClasses, ViewerEvents, ViewerOps, ViewerTools, VirtualDesktops, TextEdit, TextNode, PutGet, TEditDisplay, TEditDocument, TiogaFileOps, TiogaMenuOps, TiogaOps, UserProfile, Icons; PeanutRetrieveImpl: CEDAR MONITOR IMPORTS GVRetrieve, IO, CIFS, Time, MessageWindow, UserCredentials, PeanutSendMail, PeanutWindow, VirtualDesktops, Process, PutGet, TextNode, TEditDisplay, TiogaFileOps, TiogaMenuOps, TiogaOps, TextEdit, UserProfile, ViewerEvents, 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 [open: BOOL _ TRUE] = BEGIN ENABLE UNWIND => NULL; [] _ InternalGetNewMsgs[open]; END; tiogaContents: ViewerTools.TiogaContents _ NEW[ViewerTools.TiogaContentsRec]; GetMailViewer: PUBLIC ENTRY PROC [name: ROPE] RETURNS [mailViewer: Viewer] = { <> ENABLE UNWIND => NULL; mailViewer _ InternalGetMailViewer[name]; }; InternalGetMailViewer: INTERNAL PROC [name: ROPE] RETURNS [mailViewer: Viewer] = { <> ENABLE UNWIND => NULL; 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: [icon: PeanutWindow.messageSetIcon, iconic: TRUE, name: name]]; [] _ ViewerEvents.RegisterEventProc[proc: MessageSetHasBeenEdited, event: edit, filter: mailViewer, before: TRUE]; [] _ ViewerEvents.RegisterEventProc[proc: MessageSetHasBeenSaved, event: save, filter: mailViewer, before: FALSE]; mailViewer.file _ name; mailViewer.class.set[mailViewer, mailDoc, TRUE, $TiogaDocument]; TiogaMenuOps.DefaultMenus[mailViewer]; TiogaMenuOps.FirstLevelOnly[mailViewer]; }; InternalGetNewMsgs: INTERNAL PROC [open: BOOL _ TRUE] RETURNS [numRetrieved: INT] = <> BEGIN allOK: BOOL_ TRUE; mailViewer: Viewer; firstHeader: TiogaFileOps.Ref; StashNewMessages: PROC [retrieveOK: BOOL] RETURNS[doRemoteFlush: BOOL] = BEGIN allOK _ allOK AND retrieveOK; ViewerOps.SaveViewer[mailViewer]; -- doesn't return until the save is complete <> RETURN[retrieveOK AND flushRemoteMail]; END; mailViewer _ InternalGetMailViewer[UserProfile.Token[key:"Peanut.ActiveMailFile",default:"Active.mail"]]; [numRetrieved, firstHeader] _ AddNewMessages[StashNewMessages, mailViewer]; IF numRetrieved = 0 THEN { ReportRope["\nNo messages were retrieved"]; RETURN}; IF mailViewer.iconic AND UserProfile.Boolean["Peanut.AutomaticNewMail", FALSE] THEN NULL ELSE 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 open AND mailViewer.iconic THEN ViewerOps.OpenIcon[mailViewer]; END; <<***********************************************************************>> MessageSetHasBeenEdited: PROC [viewer: ViewerClasses.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: ViewerClasses.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] = { 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 OR sourceViewer.class.flavor#$Text 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, UserCredentials.GetUserCredentials[].password ] ; } ; lastStateReported: GVRetrieve.MBXState_ unknown; WatchMailBox: PROC[newState: GVRetrieve.MBXState] = <> BEGIN ActiveMailFileNotOpen: PROCEDURE RETURNS [BOOL] = { mailViewer: Viewer = VirtualDesktops.FindViewer[ UserProfile.Token[key:"Peanut.ActiveMailFile",default:"Active.mail"]].viewer; 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 UserProfile.Boolean["Peanut.AutomaticNewMail", FALSE] AND ActiveMailFileNotOpen[] 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: ENTRY PROC = TRUSTED { ENABLE UNWIND => NULL; numRetrieved: INT; Process.SetPriority[Process.priorityBackground]; IF (numRetrieved _ InternalGetNewMsgs[FALSE])=0 THEN RETURN; MessageWindow.Append[IO.PutFR["New mail retrieved: %g", 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: 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, UserCredentials.GetUserCredentials[].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: %g", 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: 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.