-- Transport Mechanism: User: initial stages of mail retrieval/polling --

-- [Juniper]<DMS>MS>RetrieveInit.mesa

-- Andrew Birrell  23-Jan-81 11:14:09 --

DIRECTORY
BodyDefs	USING[ Connect, maxConnectLength,
                       maxRNameLength, oldestTime, RName, Timestamp ],
NameInfoDefs	USING[ Close, Enumerate,
                       Expand, ExpandInfo, GetConnect, NameType ],
Process		USING[ DisableTimeout, InitializeCondition,
		       InitializeMonitor ],
ProtocolDefs	USING[ Init, mailServerOutputSocket, MakeKey ],
PupDefs,
PupTypes,
RetrieveDefs	USING[ MBXState, ServerState,
		       ServerType ],
RetrieveXDefs	USING[ GVClose, Handle, HandleObject,
		       MBXData, MBXPtr, MTPClose, noMBX, NoteChangedMBX,
		       SendPollProcess, SetMBXState ],
String		USING[ AppendString, EquivalentString ],
Storage		USING[ Node, String,
		       Free, FreeString ];

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

BEGIN

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



-- low-level utilities: --

KeepString: PROC[keep: POINTER TO STRING, str: STRING] =
   BEGIN
   IF keep↑ # NIL THEN Storage.FreeString[keep↑];
   IF str # NIL
   THEN BEGIN
        keep↑ ← Storage.String[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 ← Storage.Node[SIZE[HandleObject]];
   BEGIN
      handle.MBXChain ←         RetrieveXDefs.noMBX;
      handle.mbxKnown ←         FALSE;
      handle.notEmptyMBXCount ← 0;
      handle.unknownMBXCount ←  0;

      handle.registry ←         GV;

      -- 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: 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];
   Storage.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
        IF handle.currentMBX.type = MTP
        THEN RetrieveXDefs.MTPClose[handle]
        ELSE 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;
      IF handle.MBXChain.name # NIL
      THEN Storage.FreeString[handle.MBXChain.name];
      Storage.Free[handle.MBXChain]; handle.MBXChain ← next;
      END;
   ENDLOOP;
   END;


defaultHost: STRING ← NIL;
defaultReg: STRING ← NIL;

SetMTPRetrieveDefault: PUBLIC --ENTRY?-- PROC[host, reg: STRING] =
   BEGIN
   KeepString[@defaultHost, IF reg=NIL THEN NIL ELSE host];
   KeepString[@defaultReg, IF host=NIL THEN NIL ELSE reg];
   END;


FindRegistryAndMailboxes:
                PUBLIC INTERNAL PROCEDURE[handle: RetrieveXDefs.Handle] =
   BEGIN
   registry: BodyDefs.RName = [BodyDefs.maxRNameLength];
   registryAddr: PupDefs.PupAddress;
   BEGIN
      rStart: CARDINAL ← 0;
      FOR index: CARDINAL DECREASING IN [0..handle.userName.length)
      DO IF handle.userName[index] = '. THEN {rStart ← index+1; EXIT};
      ENDLOOP;
      FOR index: CARDINAL IN [rStart..handle.userName.length)
      WHILE registry.length < registry.maxlength
      DO registry[registry.length] ← handle.userName[index];
         registry.length ← registry.length + 1;
      ENDLOOP;
   END;
   BEGIN
      called: BOOLEAN ← FALSE;
      Work: INTERNAL PROC[addr:PupDefs.PupAddress] RETURNS[stop:BOOLEAN] =
         BEGIN
         IF called
         THEN { handle.registry ← GV; stop ← TRUE }
         ELSE { handle.registry ← MTP; called ← TRUE;
                registryAddr ← addr; stop ← FALSE };
         END;
      handle.registry ← GV;
      [] ← PupDefs.EnumeratePupAddresses[registry, Work ! PupDefs.PupNameTrouble =>
               IF code = errorFromServer THEN GOTO badReg ELSE GOTO noReg ];
   END;
   IF handle.MBXChain # RetrieveXDefs.noMBX THEN ERROR;
   IF handle.registry = MTP
   THEN BEGIN
        default: BOOLEAN = defaultReg # NIL AND
                         String.EquivalentString[registry, defaultReg];
        this: RetrieveXDefs.MBXPtr = AddMBX[handle,
                              IF default THEN defaultHost ELSE registry];
        IF default
        THEN FindAddress[handle, this]
        ELSE { this.addrState ← known; this.addr ← registryAddr };
        handle.mbxKnown ← TRUE;
        END
   ELSE FindGVMailboxes[handle];
   EXITS
      badReg => RetrieveXDefs.SetMBXState[handle, badName];
      noReg =>  RetrieveXDefs.SetMBXState[handle, cantAuth];
   END;

FindGVMailboxes: INTERNAL PROC[handle: RetrieveXDefs.Handle] =
   BEGIN
   IF handle.registry # GV
   THEN ERROR
   ELSE 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;
   END;

AddMBX: INTERNAL PROCEDURE[handle: RetrieveXDefs.Handle,
                           site:BodyDefs.RName]
                   RETURNS[this: RetrieveXDefs.MBXPtr] =
      BEGIN
      mbx: POINTER TO RetrieveXDefs.MBXPtr ← @(handle.MBXChain);
      -- skip to end of mailbox chain --
      WHILE mbx↑ # RetrieveXDefs.noMBX DO mbx ← @(mbx↑.next); ENDLOOP;
      mbx↑ ← Storage.Node[SIZE[RetrieveXDefs.MBXData]];
      this ← mbx↑;
      this.name ← NIL; KeepString[@(this.name), site];
      FOR index: CARDINAL DECREASING IN [0..site.length)
      DO IF site[index] = '. THEN { this.type ← GV; EXIT };
      REPEAT FINISHED => this.type ← MTP;
      ENDLOOP;
      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;
   IF mbx.type = GV
   THEN SELECT NameInfoDefs.GetConnect[mbx.name, connect] FROM
          individual => NULL;
          allDown => GOTO noAddr;
          group, notFound => GOTO badConnect;
        ENDCASE => ERROR
   ELSE String.AppendString[connect, mbx.name];
   mbx↑.addr.socket ← [0,0];
   PupDefs.GetPupAddress[@(mbx↑.addr), connect ! PupDefs.PupNameTrouble =>
          IF code = errorFromServer THEN GOTO badConnect ELSE GOTO noAddr ];
   IF mbx.type = GV
   THEN 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.