-- Copyright (C) 1981, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- RetrieveInit.mesa, Transport Mechanism: User: initial stages of mail retrieval/polling --

-- HGM: 15-Sep-85  8:03:07
-- Andrew Birrell  28-Apr-81 14:20:56 --

DIRECTORY
  BodyDefs USING [Connect, maxConnectLength, RName, Timestamp],
  Heap USING [systemZone],
  NameInfoDefs USING [Close, Enumerate, Expand, ExpandInfo, GetConnect, NameType],
  Process USING [DisableTimeout, InitializeCondition, InitializeMonitor],
  ProtocolDefs USING [Init, mailServerOutputSocket, MakeKey],
  PupDefs,
  PupTypes,
  RetrieveDefs USING [MBXState, ServerState],
  RetrieveXDefs USING [
    GVClose, Handle, HandleObject, MBXData, MBXPtr, noMBX,
    NoteChangedMBX, SendPollProcess, SetMBXState],
  String USING [AppendString];

RetrieveInit: MONITOR LOCKS handle USING handle: RetrieveXDefs.Handle
  IMPORTS
    Heap, NameInfoDefs, Process, ProtocolDefs, PupDefs, RetrieveXDefs, String
  EXPORTS RetrieveDefs, RetrieveXDefs =

  BEGIN

  Handle: PUBLIC TYPE = RetrieveXDefs.Handle;
  HandleObject: PUBLIC TYPE = RetrieveXDefs.HandleObject;



  -- low-level utilities: --

  KeepString: PROC [keep: LONG POINTER TO LONG STRING, str: LONG STRING] =
    BEGIN
    Heap.systemZone.FREE[keep];
    IF str # NIL THEN
      BEGIN
      keep↑ ← Heap.systemZone.NEW[StringBody[str.length]];
      String.AppendString[keep↑, str];
      END;
    END;


  -- Global state control by the client: --

  Create: PUBLIC PROCEDURE [
    pollingInterval: CARDINAL,
    reportChanges: PROCEDURE [RetrieveDefs.MBXState] ← NIL]
    RETURNS [handle: Handle] =
    BEGIN
    handle ← Heap.systemZone.NEW[HandleObject];
    BEGIN
    handle.MBXChain ← RetrieveXDefs.noMBX;
    handle.mbxKnown ← FALSE;
    handle.notEmptyMBXCount ← 0;
    handle.unknownMBXCount ← 0;

    -- handle.state ←         ;
    handle.spareByte ← FALSE;
    -- handle.spareByteValue ←;
    -- handle.header ←        ;
    handle.currentMBX ← RetrieveXDefs.noMBX;
    handle.messages ← 0;
    handle.currentStr ← NIL;
    handle.ftpUser ← NIL;

    handle.mbxState ← badName;
    handle.polling ← FALSE;
    handle.pollWanted ← FALSE;
    handle.newPollWanted ← FALSE;
    handle.pollReplying ← FALSE;
    -- handle.mbxStateChange ←;
    -- handle.pollCond ←      ;
    handle.pollID ← [0, 1];
    -- handle.sendPoll ←      ;
    handle.pollStarted ← 0;

    handle.interval ← pollingInterval;
    handle.changes ← reportChanges;
    handle.userName ← NIL;
    handle.userPwd ← NIL;
    handle.userKey ← [0, 0, 0, 0];
    END;
    Process.InitializeMonitor[@(handle.LOCK)];
    Process.InitializeCondition[@(handle.pollCond), 0];
    -- the polling process adjusts the timeout on handle.pollCond --
    Process.InitializeCondition[@(handle.mbxStateChange), 0];
    Process.DisableTimeout[@(handle.mbxStateChange)];
    END;

  NewUser: PUBLIC ENTRY PROCEDURE [handle: Handle, user, password: LONG STRING] =
    BEGIN
    UnsetMailboxes[handle];
    IF user = NIL OR user.length = 0 THEN
      RetrieveXDefs.SetMBXState[handle, badName]
    ELSE
      IF password = NIL OR password.length = 0 THEN
        RetrieveXDefs.SetMBXState[handle, badPwd]
      ELSE
        BEGIN
        KeepString[@(handle.userName), user];
        KeepString[@(handle.userPwd), password];
        handle.userKey ← ProtocolDefs.MakeKey[handle.userPwd];
        RestartPoll[handle];
        END;
    END;

  Destroy: PUBLIC PROCEDURE [handle: Handle] =
    BEGIN InnerDestroy[handle]; Heap.systemZone.FREE[@handle]; END;

  InnerDestroy: ENTRY PROCEDURE [handle: Handle] = INLINE
    BEGIN
    UnsetMailboxes[handle];
    KeepString[@handle.userName, NIL];
    KeepString[@handle.userPwd, NIL];
    END;

  RestartPoll: INTERNAL PROCEDURE [handle: Handle] =
    BEGIN
    handle.pollID.b ← handle.pollID.b + 1;  --to ignore old poll replies--
    IF NOT handle.polling THEN
      handle.sendPoll ← FORK RetrieveXDefs.SendPollProcess[handle];
    handle.polling ← handle.pollWanted ← TRUE;
    handle.newPollWanted ← TRUE;
    BROADCAST handle.pollCond;
    END;

  UnsetMailboxes: INTERNAL PROCEDURE [handle: Handle] =
    BEGIN
    IF handle.polling THEN
      BEGIN
      handle.pollWanted ← FALSE;
      BROADCAST handle.pollCond;
      WHILE handle.polling DO WAIT handle.pollCond ENDLOOP;
      JOIN handle.sendPoll;
      END;
    IF handle.currentMBX # RetrieveXDefs.noMBX THEN
      BEGIN
      RetrieveXDefs.GVClose[handle];
      handle.currentMBX ← RetrieveXDefs.noMBX;
      END;
    handle.unknownMBXCount ← handle.notEmptyMBXCount ← 0;
    RetrieveXDefs.SetMBXState[handle, unknown];
    handle.mbxKnown ← FALSE;
    UNTIL handle.MBXChain = RetrieveXDefs.noMBX DO
      BEGIN
      next: RetrieveXDefs.MBXPtr = handle.MBXChain.next;
      Heap.systemZone.FREE[@handle.MBXChain.name];
      Heap.systemZone.FREE[@handle.MBXChain];
      handle.MBXChain ← next;
      END;
      ENDLOOP;
    END;

  FindRegistryAndMailboxes: PUBLIC INTERNAL PROCEDURE [
    handle: RetrieveXDefs.Handle] =
    BEGIN
    IF handle.MBXChain # RetrieveXDefs.noMBX THEN ERROR;
    FindGVMailboxes[handle];
    END;

  FindGVMailboxes: INTERNAL PROC [handle: RetrieveXDefs.Handle] =
    BEGIN
    Work: INTERNAL PROCEDURE [site: BodyDefs.RName] RETURNS [done: BOOLEAN] =
      BEGIN done ← FALSE; FindAddress[handle, AddMBX[handle, site]]; END;
    info: NameInfoDefs.ExpandInfo = NameInfoDefs.Expand[handle.userName];
    WITH info SELECT FROM
      allDown => RetrieveXDefs.SetMBXState[handle, cantAuth];
      notFound => RetrieveXDefs.SetMBXState[handle, badName];
      group =>
        BEGIN -- this case include individual with forwarding
        NameInfoDefs.Close[members];
        handle.mbxKnown ← TRUE;
        END;
      individual =>
        BEGIN
        NameInfoDefs.Enumerate[sites, Work];
        NameInfoDefs.Close[sites];
        handle.mbxKnown ← TRUE;
        END;
      ENDCASE => ERROR;
    END;

  AddMBX: INTERNAL PROCEDURE [handle: RetrieveXDefs.Handle, site: BodyDefs.RName]
    RETURNS [this: RetrieveXDefs.MBXPtr] =
    BEGIN
    mbx: LONG POINTER TO RetrieveXDefs.MBXPtr ← @(handle.MBXChain);
    -- skip to end of mailbox chain --
    WHILE mbx↑ # RetrieveXDefs.noMBX DO mbx ← @(mbx↑.next); ENDLOOP;
    mbx↑ ← Heap.systemZone.NEW[RetrieveXDefs.MBXData];
    this ← mbx↑;
    this.name ← NIL;
    KeepString[@(this.name), site];
    this.next ← RetrieveXDefs.noMBX;
    this.state ← unknown;
    this.replyWanted ← TRUE;
    handle.unknownMBXCount ← handle.unknownMBXCount + 1;
    IF handle.mbxState = allEmpty THEN RetrieveXDefs.SetMBXState[handle, userOK];
    this.addrState ← unknown;
    END;

  FindAddress: PUBLIC INTERNAL PROC [
    handle: RetrieveXDefs.Handle, mbx: RetrieveXDefs.MBXPtr] =
    BEGIN
    connect: BodyDefs.Connect = [BodyDefs.maxConnectLength];
    IF mbx.addrState # unknown THEN ERROR;
    SELECT NameInfoDefs.GetConnect[mbx.name, connect] FROM
      individual => NULL;
      allDown => GOTO noAddr;
      group, notFound => GOTO badConnect;
      ENDCASE => ERROR;
    mbx↑.addr.socket ← [0, 0];
    PupDefs.GetPupAddress[
      @(mbx↑.addr), connect !
      PupDefs.PupNameTrouble =>
        IF code = errorFromServer THEN GOTO badConnect ELSE GOTO noAddr];
    mbx.addr.socket ← ProtocolDefs.mailServerOutputSocket;
    mbx.addrState ← known;
    EXITS
      badConnect => {
        RetrieveXDefs.NoteChangedMBX[handle, mbx, empty]; mbx.addrState ← bad};
      noAddr => NULL;
    END;

  WaitForMail: PUBLIC ENTRY PROCEDURE [handle: Handle] =
    BEGIN
    WHILE handle.mbxState # notEmpty DO WAIT handle.mbxStateChange ENDLOOP;
    END;

  ProtocolDefs.Init[];


  END.