// PupEFTPReceive.bcpl -- by J. Shoch
// Copyright Xerox Corporation 1979, 1980

// Last modified September 30, 1980  1:45 PM, by Swinehart

get "PupEFTP.decl"

external
[
// outgoing procedures
ReceiveEFTPBlock; ReceiveEFTPPacket

// incoming procedures
SendEFTPAbort
GetPBI; ReleasePBI; CompletePup
SetTimer; TimerHasExpired; Block; Dequeue; MultEq
MoveBlock
]


//----------------------------------------------------------------------------
let ReceiveEFTPBlock(soc, addr, timeout) = valof
//----------------------------------------------------------------------------
// Returns a byte count (>=0) or an error code (<0)
[
let inPbi = 0
let receiveResult = ReceiveEFTPPacket(soc, timeout, lv inPbi)
if receiveResult ge 0 then // succeeded
   [
   MoveBlock(addr, lv inPbi>>PBI.pup.words↑1, (receiveResult+1) rshift 1)
   if inPbi then  // ignore zero-length packets
      [ ReleasePBI(inPbi); if receiveResult eq 0 loop ]
   resultis receiveResult // count of dataBytes
   ]
unless receiveResult eq EFTPNotFirstSynch resultis receiveResult
] repeat

//----------------------------------------------------------------------------
and ReceiveEFTPPacket(soc, timeout, lvPbi) = valof
//----------------------------------------------------------------------------
// Returns a byte count (>=0) or an error code (<0)
// If a byte count, @lvPbi contains the pbi with the data
// Note that when we start, we may not yet know the socket
//  number of the foreign socket.....
[
let syncMsg = "Your sender is out of sync."
let busyMsg = "Receiver busy, please try again later."
let longTimer = nil; SetTimer(lv longTimer, timeout)
   [ // repeat
   // wait for an incoming packet
   Block()
   if timeout ne -1 & TimerHasExpired(lv longTimer) resultis EFTPTimeout
   let inPbi = Dequeue(lv soc>>EFTPSoc.iQ)
   if inPbi eq 0 loop
   let firstPacket = inPbi>>PBI.pup.id↑2 eq 0
   let dataPup= inPbi>>PBI.pup.type eq typeEFTPData

   // See if we are already committed to a file transfer
   if soc>>EFTPSoc.TransferNotStarted then
      [
      // No established file transfer is in progress.
      // To start a file transfer, (1) his must be a valid packet 0,
      //  and either (2) its source must match our socket's foreign port
      //  or (3) we must be promiscuous.
      if dataPup & firstPacket &
       (soc>>EFTPSoc.frnPort.host eq 0 % // (2)
       (soc>>EFTPSoc.frnPort.host eq inPbi>>PBI.pup.sPort.host & // (3)
       soc>>EFTPSoc.frnPort.net eq inPbi>>PBI.pup.sPort.net)) then // (3)
         [ // establish a "connection"
         soc>>EFTPSoc.TransferNotStarted = false
         soc>>EFTPSoc.SeqNum = 0
         MoveBlock(lv soc>>EFTPSoc.frnPort, lv inPbi>>PBI.pup.sPort, lenPort)
         ]
      ]

   // Do source filtering
   unless MultEq(lv soc>>EFTPSoc.frnPort, lv inPbi>>PBI.pup.sPort, lenPort) do
    [ // Did not pass source filter, generate Abort
    if dataPup then
      [
      test firstPacket
         ifso
            [
            // Attempted first packet of transfer.
            // Either we are busy or we are listening and not promiscuous.
            SendEFTPAbort(soc, ReceiverBusyAbort, busyMsg, inPbi)
            soc>>EFTPSoc.SomeoneElseWaiting = true
            ]
         ifnot
            [
            // Not legal first packet of transfer.
            SendEFTPAbort(soc, OutOfSynchAbort, syncMsg, inPbi)
            ]
      ]
    ReleasePBI(inPbi)
    if soc>>EFTPSoc.TransferNotStarted resultis EFTPNotFirstSynch
    loop   //wait for next input
    ]

// ReceiveEFTPPacket (cont'd)

   // Packet has passed all filters.  Now process it.
   switchon inPbi>>PBI.pup.type into
      [
      case typeEFTPData:
         [
         let seq=CheckSequence(soc, inPbi)
         switchon seq into
             [ case 1: loop; case 0: endcase; default: resultis seq ]
         soc>>EFTPSoc.SeqNum = soc>>EFTPSoc.SeqNum+1
         @lvPbi = inPbi
         resultis inPbi>>PBI.pup.length - pupOvBytes   //and return
         ]
      case typeEFTPEnd:
         [
         let seq=CheckSequence(soc, inPbi)
         switchon seq into
             [ case 1: loop; case 0: endcase; default: resultis seq ]
         ReleasePBI(inPbi)
         soc>>EFTPSoc.SeqNum = soc>>EFTPSoc.SeqNum+1
         let dallyTimer = nil; SetTimer(lv dallyTimer, dallyTimeout)
            [ // repeat
            Block()
            if TimerHasExpired(lv dallyTimer) resultis EFTPEndReceived
            inPbi = Dequeue(lv soc>>EFTPSoc.iQ)
            if inPbi eq 0 loop

            if inPbi>>PBI.pup.type ne typeEFTPEnd then
               [ ReleasePBI(inPbi); loop ]
            if soc>>EFTPSoc.SeqNum eq inPbi>>PBI.pup.id↑2 then   
               [ ReleasePBI(inPbi); resultis EFTPEndReceived ]

            // Was a retransmission of first end, ack it
            AckEFTPPacket(soc, inPbi); ReleasePBI(inPbi)
            ] repeat
         endcase
         ]
      case typeEFTPAbort:
      case typeError: // low-level error pup, if "no such port" treat as abort else ignore
         [
	     if inPbi>>PBI.pup.words↑11 ne ecNoSuchPort endcase // ignore the packet
         if soc>>EFTPSoc.AbortPBI ne 0 then ReleasePBI(soc>>EFTPSoc.AbortPBI)
         soc>>EFTPSoc.AbortPBI = inPbi // includes error Pups?
         resultis EFTPAbortReceived
         ]
       default: // unrecognized Pup on current connection, abort
         [
         SendEFTPAbort(soc, ExternalSenderAbort,
             "Unrecognized Pup type on EFTP connection", inPbi)
         ReleasePBI(inPbi);
         resultis EFTPAbortSent
         ]
      ]
   ReleasePBI(inPbi)
   ] repeat
]
   

 and CheckSequence(soc, inPbi) = valof
   [
   let inSeqNum=inPbi>>PBI.pup.id↑2
   // Check the sequence number -- should be expected or expected-1
   // Acknowledge it.  Then, if expected-1, release and return 1
   //          If expected, return 0 -- next data packet
   // If not expected sequence, issue appropriate abort and return its code.
   let dif=(soc>>EFTPSoc.SeqNum-inSeqNum)
   if (dif rshift 1) eq 0 then
      [
      AckEFTPPacket(soc, inPbi)
      if dif eq 1 then ReleasePBI(inPbi)
      resultis dif // 0 if expected sequence #, 1 if previous one
      ]
   // Sequence number incorrect.
   // If we receive a data packet with sequence number 0
   //    from our partner, then assume he wants to restart.
   // Otherwise abort the transfer and return an error.
   unless inSeqNum eq 0 do
         SendEFTPAbort(soc, OutOfSynchAbort,
                  "Your sender is out of sync.", inPbi)
   ReleasePBI(inPbi)
   resultis inSeqNum eq 0? EFTPResetReceived, EFTPAbortSent
   ]


//----------------------------------------------------------------------------
and AckEFTPPacket(soc, pbi) be
//----------------------------------------------------------------------------
[
// construct the outgoing ack packet
let ackPbi = GetPBI(soc)
ackPbi>>PBI.pup.id↑2 = pbi>>PBI.pup.id↑2
CompletePup(ackPbi, typeEFTPAck, pupOvBytes)
]