<> <> <> DIRECTORY Rope USING [ROPE], Process USING [SetTimeout, MsecToTicks], CommUtilDefs USING [CopyLong], PupDefs USING [DataWordsPerPupBuffer, GetFreePupBuffer, ReturnFreePupBuffer, GetPupContentsBytes, SetPupContentsBytes, PupAddress, PupBuffer, ReturnPup, PupSocket, PupSocketMakeFull, PupSocketDestroy, SecondsToTocks], PupTypes USING [maxDataBytesPerGatewayPup, fillInPupAddress], EFTPDefs USING [EFTPTimeOut, EFTPAbortCode]; EFTPRecv: MONITOR IMPORTS Process, CommUtilDefs, EFTPDefs, PupDefs EXPORTS EFTPDefs = BEGIN OPEN PupDefs, EFTPDefs; EFTPAlreadyReceiving: PUBLIC ERROR = CODE; EFTPNotReceiving: PUBLIC ERROR = CODE; EFTPEndReceiving: PUBLIC ERROR = CODE; EFTPTroubleReceiving: PUBLIC ERROR [e: EFTPAbortCode, s: Rope.ROPE] = CODE; recv: CONDITION; recverStop: BOOLEAN; recverFork: PROCESS; receiveSocket: PupSocket _ NIL; recvStarted: BOOLEAN; recvHim: PupAddress; receiveSeqNumber: CARDINAL; receivePupBuffer: PupBuffer _ NIL; receiveGobbled: CARDINAL _ 0; -- in words ! EFTPSetRecvTimeout: PUBLIC PROCEDURE [ms: CARDINAL] = BEGIN Process.SetTimeout[@recv, Process.MsecToTicks[ms]]; END; EFTPOpenForReceiving: PUBLIC PROCEDURE [me: PupAddress] RETURNS [PupAddress] = BEGIN EFTPOpenCheck[me]; BEGIN ENABLE UNWIND => EFTPKillReceiver[]; EFTPOpenLocked[]; END; RETURN[recvHim]; END; EFTPOpenCheck: ENTRY PROCEDURE [me: PupAddress] = BEGIN ENABLE UNWIND => NULL; IF receiveSocket # NIL THEN ERROR EFTPAlreadyReceiving; recvStarted _ FALSE; recverStop _ FALSE; receiveSocket _ PupSocketMakeFull[ me, PupTypes.fillInPupAddress, SecondsToTocks[1]]; recverFork _ FORK EFTPRecvDataProcess[]; receiveSeqNumber _ 0; END; EFTPOpenLocked: ENTRY PROCEDURE = BEGIN ENABLE UNWIND => NULL; UNTIL receivePupBuffer # NIL DO THROUGH [0..10) UNTIL receivePupBuffer # NIL DO WAIT recv; ENDLOOP; IF receivePupBuffer # NIL THEN EXIT; SIGNAL EFTPTimeOut; -- 10*1 sec ENDLOOP; receiveSocket.setRemoteAddress[recvHim]; END; EFTPGetBlock: PUBLIC ENTRY PROCEDURE [p: LONG POINTER, l: CARDINAL] RETURNS [CARDINAL] = BEGIN ENABLE UNWIND => NULL; n: CARDINAL; -- bytes to transfer pupLength: CARDINAL; IF receiveSocket = NIL THEN ERROR EFTPNotReceiving; IF (l MOD 2) = 1 THEN l _ l - 1; UNTIL receivePupBuffer # NIL DO WAIT recv; IF receivePupBuffer # NIL THEN EXIT; SIGNAL EFTPTimeOut; -- 1*1 sec ENDLOOP; IF receivePupBuffer.pupType = eEnd OR receivePupBuffer.pupType = eAbort THEN BEGIN b: PupBuffer _ receivePupBuffer; receivePupBuffer _ NIL; EFTPSendAck[b, receiveSeqNumber]; RETURN WITH ERROR EFTPEndReceiving; END; pupLength _ GetPupContentsBytes[receivePupBuffer]; n _ pupLength - (receiveGobbled*2); n _ MIN[l, n]; CommUtilDefs.CopyLong[ from: @receivePupBuffer.pupWords[receiveGobbled], nwords: (n + 1)/2, to: p]; receiveGobbled _ receiveGobbled + (n/2); IF l > n OR receiveGobbled*2 = pupLength THEN BEGIN -- done with this buffer, use it to send back the ack b: PupBuffer _ receivePupBuffer; receivePupBuffer _ NIL; EFTPSendAck[b, receiveSeqNumber]; receiveSeqNumber _ receiveSeqNumber + 1; END; RETURN[n]; END; EFTPFinishReceiving: PUBLIC PROCEDURE = BEGIN IF receiveSocket = NIL THEN ERROR EFTPNotReceiving; EFTPKillReceiver[]; END; EFTPAbortReceiving: PUBLIC PROCEDURE [s: STRING] = BEGIN IF receiveSocket = NIL THEN RETURN; EFTPAbortReceivingLocked[s]; EFTPKillReceiver[]; END; EFTPAbortReceivingLocked: ENTRY PROCEDURE [s: STRING] = BEGIN end: CARDINAL; b: PupBuffer; ec: EFTPAbortCode = eftpExternalReceiverAbort; b _ GetFreePupBuffer[]; b.pupType _ eAbort; b.pupID _ [0, receiveSeqNumber]; b.pupWords[0] _ LOOPHOLE[ec]; end _ MIN[ s.length + 2, 2*DataWordsPerPupBuffer[], PupTypes.maxDataBytesPerGatewayPup]; FOR i: CARDINAL _ 2, i + 1 UNTIL i = end DO b.pupChars[i] _ s[i - 2]; ENDLOOP; SetPupContentsBytes[b, end]; receiveSocket.put[b]; END; <> EFTPRecvDataProcess: PROCEDURE = BEGIN b: PupBuffer; UNTIL recverStop DO b _ receiveSocket.get[]; IF b # NIL THEN EFTPRecvDataLocked[b]; ENDLOOP; END; EFTPRecvDataLocked: ENTRY PROCEDURE [b: PupBuffer] = BEGIN -- better not SIGNAL here IF b.pupType NOT IN [eData..eAbort] THEN -- not EFTP type packet BEGIN ReturnFreePupBuffer[b]; GOTO AllDone; END; IF b.source = recvHim AND b.pupType = eData AND b.pupID = [0, receiveSeqNumber - 1] THEN BEGIN -- duplicate: use the buffer to send back the ack EFTPSendAck[b, receiveSeqNumber - 1]; GOTO AllDone; END; IF b.source # recvHim OR b.pupID # [0, receiveSeqNumber] THEN BEGIN -- from wrong place IF ~recvStarted AND b.pupID = [0, 0] THEN BEGIN -- we are listening, and this is the first packet IF receivePupBuffer = NIL THEN -- take this new user BEGIN receivePupBuffer _ b; receiveGobbled _ 0; recvStarted _ TRUE; recvHim _ receivePupBuffer.source; NOTIFY recv; GOTO AllDone; END; END; IF b.pupType = eData THEN BEGIN -- use the buffer to send back an abort ec: EFTPAbortCode; ec _ IF b.pupID # [0, 0] THEN eftpOutOfSyncAbort ELSE eftpReceiverBusyAbort; b.pupType _ eAbort; b.pupWords[0] _ LOOPHOLE[ec]; ReturnPup[b, eAbort, 2]; -- should send some text GOTO AllDone; END; ReturnFreePupBuffer[b]; GOTO AllDone; END; <> IF receivePupBuffer # NIL THEN BEGIN <> ReturnFreePupBuffer[b]; GOTO AllDone; END; receivePupBuffer _ b; receiveGobbled _ 0; NOTIFY recv; EXITS AllDone => NULL; END; EFTPSendAck: PROCEDURE [b: PupBuffer, id: CARDINAL] = BEGIN b.pupType _ eAck; b.pupID _ [0, id]; SetPupContentsBytes[b, 0]; receiveSocket.put[b]; END; EFTPKillReceiver: PROCEDURE = BEGIN recverStop _ TRUE; JOIN recverFork; IF receiveSocket # NIL THEN PupSocketDestroy[receiveSocket]; receiveSocket _ NIL; IF receivePupBuffer # NIL THEN BEGIN b: PupBuffer _ receivePupBuffer; receivePupBuffer _ NIL; ReturnFreePupBuffer[b]; END; END; <> EFTPSetRecvTimeout[1000]; END.