-- 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: BOOLEANTRUE;
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: BOOLEANFALSE;

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: BOOLEANFALSE;  -- 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.