-- File: WalnutRetrieveImpl.mesa
-- Contents: Implements mail retrieval for Walnut
-- Created by Willie-Sue August 26, 1982
-- Lasted edited by:
-- Cattell on XXXX
-- Willie-Sue on August 29, 1983 4:42 pm
DIRECTORY
GVRetrieve USING[Failed, Handle, ServerState,
Accept, Close, Create, MailboxState, NewUser, NextMessage, NextServer,
ServerName, StartMessage],
GVBasics USING[Timestamp, RName],
IO,
Rope,
UserCredentials USING [Get],
WalnutLog USING [RName, GVMessageToLog, LogLength],
WalnutRetrieve,
WalnutWindow USING [Report, ReportRope, WatchMailBox];
WalnutRetrieveImpl: CEDAR PROGRAM
IMPORTS
GVRetrieve, IO, UserCredentials,
WalnutLog, WalnutWindow
EXPORTS WalnutRetrieve =
BEGIN
ROPE: TYPE = Rope.ROPE;
-- ************************************************************************
RetrieveGVMessages: PUBLIC PROC RETURNS[numRetrieved: INT, allOK: BOOL] =
-- reads any new mail
BEGIN
startOfNewMessages, currentPos: INT;
StashNewMessages: PROC [retrieveOK: BOOL] RETURNS[doRemoteFlush: BOOL] =
BEGIN
allOK← allOK AND retrieveOK;
currentPos← WalnutLog.LogLength[doFlush: TRUE];
-- don't do remote flush if ~retrieveOK
RETURN[retrieveOK AND flushRemoteMail];
END;
allOK← TRUE;
startOfNewMessages← currentPos← WalnutLog.LogLength[doFlush: TRUE];
numRetrieved← AddNewMessages[StashNewMessages]; -- changes currentPos
IF currentPos = startOfNewMessages THEN
{WalnutWindow.Report["No messages were retrieved"]; RETURN};
IF ~allOK THEN WalnutWindow.Report["Some messages may not have been retrieved"];
END;
-- ***********************************************************************
msgPollingInterval: INT← 300; -- Number of seconds between mailbox polling.
flushRemoteMail: BOOLEAN← TRUE;
gvRetrieveHandle: GVRetrieve.Handle← NIL; -- cookie for receiving messages.
OpenConnection: PUBLIC PROC[user: WalnutLog.RName] = {
-- This establishes a retrieve connection, and sets up a Mail Polling proc
CloseConnection[];
NewUser[user] ;
};
CloseConnection: PUBLIC PROC[] = {
-- This closes the connection, and invalidates the connection handle.
IF gvRetrieveHandle # NIL THEN
{ GVRetrieve.Close[gvRetrieveHandle]; gvRetrieveHandle← NIL};
};
NewUser: PUBLIC PROC[user: WalnutLog.RName] = {
-- Establish a new user on this connection.
IF gvRetrieveHandle = NIL THEN
gvRetrieveHandle ← GVRetrieve.Create[ msgPollingInterval, WalnutWindow.WatchMailBox ];
GVRetrieve.NewUser[gvRetrieveHandle, user, UserCredentials.Get[].password ] ;
} ;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
MessageState: TYPE = { noMore, wasArchived, wasDeleted, OK, retrieveFailed } ;
AddNewMessages: PUBLIC PROC[FinishedWithServer: PROC[BOOL] RETURNS[BOOL]]
RETURNS[numRetrieved: INT] =
{ ENABLE UNWIND =>
{IF gvRetrieveHandle # NIL THEN GVRetrieve.Close[gvRetrieveHandle];
gvRetrieveHandle← NIL;
WalnutWindow.Report["New Mail NOT retrieved"];
};
-- 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.
serverKnown: BOOLEAN ← FALSE;
IF gvRetrieveHandle = NIL THEN { -- Open the connection if it's closed.
gvRetrieveHandle ← GVRetrieve.Create[msgPollingInterval, WalnutWindow.WatchMailBox];
GVRetrieve.NewUser[gvRetrieveHandle, UserCredentials.Get[].name,
UserCredentials.Get[].password];
} ;
SELECT gvRetrieveHandle.MailboxState[] FROM
badName, badPwd => GOTO credentialsError;
cantAuth => GOTO noServers;
ENDCASE; --ok to try
numRetrieved← 0;
DO -- Loops over servers.
messageState: MessageState;
messages: CARDINAL ← 0; -- the number of messages read from server.
archivedReported: BOOLEAN ← FALSE; -- seen an archived message?
-- Cycle through the servers, until you find one that has mail.
-- If it has mail, then go for it.
noMore: BOOLEAN; -- TRUE if no more servers.
serverState: GVRetrieve.ServerState; -- The state of the server.
serverName: ROPE ;
-- Step through the servers.
[noMore, serverState] ← gvRetrieveHandle.NextServer[];
IF noMore THEN EXIT; -- Last server? Then done.
serverKnown ← TRUE;
serverName ← gvRetrieveHandle.ServerName[];
WalnutWindow.ReportRope[serverName]; WalnutWindow.ReportRope[": "];
IF serverState # notEmpty THEN {
IF serverState = empty THEN WalnutWindow.Report["empty"]
ELSE WalnutWindow.Report["didn't respond"] ;
LOOP; -- Skip to the next server.
};
DO
messageState← ReadMessageRecord[] ;
SELECT messageState FROM
noMore => EXIT ;
wasArchived => IF NOT archivedReported THEN
{ archivedReported ← TRUE; WalnutWindow.ReportRope["(archived messages exist)"]};
OK => NULL;
wasDeleted => NULL;
retrieveFailed => EXIT;
ENDCASE => ERROR;
IF NOT (messageState = wasDeleted) THEN
{ WalnutWindow.ReportRope["."]; messages ← messages + 1};
ENDLOOP ; -- Finished reading messages from this server.
-- Flush the mailbox if desired, we've stashed the messages.
IF FinishedWithServer[messageState#retrieveFailed] THEN
gvRetrieveHandle.Accept[ ! GVRetrieve.Failed =>
{WalnutWindow.Report["\nFlush of remote messages failed; you may get these messages again"];
CONTINUE}];
IF messageState#retrieveFailed THEN
WalnutWindow.Report[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 => WalnutWindow.Report[" No mail boxes"];
credentialsError => WalnutWindow.Report[" Credentials error"];
noServers => WalnutWindow.Report[" No servers responding"];
};
ReadMessageRecord: PROC RETURNS [messageState: MessageState] =
{
-- This routine reads the messages on this connection, returning messageState = noMore
-- when there anren't any more.
ENABLE GVRetrieve.Failed => {
WalnutWindow.Report[ 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, ok: BOOLEAN;
timeStamp: GVBasics.Timestamp;
gvSender: GVBasics.RName;
messageState← OK;
[msgExists, archived, deleted] ← GVRetrieve.NextMessage[ gvRetrieveHandle ];
IF archived THEN messageState ← wasArchived;
IF deleted THEN { messageState ← wasDeleted; RETURN};
IF NOT msgExists THEN { messageState ← 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, gvSender, ] ← GVRetrieve.StartMessage[ gvRetrieveHandle ] ;
ok← WalnutLog.GVMessageToLog[gvRetrieveHandle, timeStamp, gvSender];
IF ~ok THEN messageState← retrieveFailed;
EXITS
gvFailed => messageState← retrieveFailed;
} ;
END.