-- FTPPupComCool.mesa, Edit: HGM July 31, 1980 5:49 PM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
FTPDefs,
FTPPrivateDefs,
FTPPupComDefs,
PupDefs USING [NameLookupErrorCode],
PupStream USING [
AppendPupAddress, CloseReason,
CreatePupByteStreamListener, DestroyPupListener, RejectThisRequest,
GetPupAddress, PupAddress, PupByteStreamCreate, PupListener, PupNameTrouble,
PupPackageDestroy, PupPackageMake, PupSocketID, SecondsToTocks, StreamClosing, veryLongWait],
PupTypes USING [fillInHostID, fillInNetID],
Stream USING [Handle, TimeOut],
Storage USING [Node, Free];
FTPPupComCool: PROGRAM
IMPORTS PupStream, Stream, Storage, FTPDefs, FTPPrivateDefs, FTPPupComDefs
EXPORTS FTPDefs, FTPPupComDefs
SHARES FTPDefs =
BEGIN OPEN FTPDefs, FTPPrivateDefs;
PupConnection: TYPE = FTPPupComDefs.PupConnection;
PupConnectionObject: TYPE = FTPPupComDefs.PupConnectionObject;
-- pup port state information
PupPort: TYPE = POINTER TO PupPortObject;
PupPortObject: TYPE = RECORD [
deactivated: EventObject,
pH: PROCESS];
-- **********************! Constants !***********************
ftpsystem: POINTER TO FTPSystem = LocateFtpSystemObject[];
communicationPrimitivesObject: CommunicationPrimitivesObject ← [
CreateCommunicationSystem: CreateCommunicationSystem,
DestroyCommunicationSystem: DestroyCommunicationSystem,
OpenConnection: OpenConnection,
CloseConnection: CloseConnection,
ActivatePort: ActivatePort,
DeactivatePort: DeactivatePort,
SendBytes: FTPPupComDefs.SendBytes,
ReceiveBytes: FTPPupComDefs.ReceiveBytes,
SendByte: FTPPupComDefs.SendByte,
ReceiveByte: FTPPupComDefs.ReceiveByte,
ProduceDiscontinuity: FTPPupComDefs.ProduceDiscontinuity,
ConsumeDiscontinuity: FTPPupComDefs.ConsumeDiscontinuity,
ForceOutput: FTPPupComDefs.ForceOutput];
-- **********************! Communication Foothold Procedure !***********************
-- Note: These primitives use the Pup Package in such a way as to
-- maintain compatibility with Maxc, IFS, and Juniper file systems.
PupCommunicationPrimitives,
SomeCommunicationPrimitives: PUBLIC PROCEDURE RETURNS [communicationPrimitives: CommunicationPrimitives] =
BEGIN
-- return communication primitives
communicationPrimitives ← @communicationPrimitivesObject;
END;
-- **********************! Communication Primitives !***********************
CreateCommunicationSystem: PROCEDURE RETURNS [communicationSystem: CommunicationSystem] =
BEGIN
PupStream.PupPackageMake[];
END;
DestroyCommunicationSystem: PROCEDURE [communicationSystem: CommunicationSystem] =
BEGIN
-- destroy pup package
PupStream.PupPackageDestroy[];
END;
OpenConnection: PROCEDURE [communicationSystem: CommunicationSystem, remoteHost: STRING, remoteSocket: LONG INTEGER, receiveSeconds: CARDINAL] RETURNS [connection: Connection] =
BEGIN
-- local constants
-- Note: The transformation below assumes intimate knowledge
-- of Mesa's LONG INTEGER implementation.
longInteger: ARRAY {lob, hob} OF CARDINAL = LOOPHOLE[remoteSocket];
pupRemoteSocket: PupStream.PupSocketID = [longInteger[hob], longInteger[lob]];
-- local variables
pupConnection: PupConnection;
pupAddress: PupStream.PupAddress ←
[net: PupTypes.fillInNetID, host: PupTypes.fillInHostID, socket: pupRemoteSocket];
-- allocate and initialize pup connection object
pupConnection ← Storage.Node[SIZE[PupConnectionObject]];
pupConnection↑ ← PupConnectionObject[
streamHandle: ,
thirdPartyClose: FALSE, inputDiscontinuity: FALSE, outputDiscontinuity: FALSE,
inputDiscontinuityConsumed: FALSE, terminateOnEndPhysicalRecord: FALSE,
mark: ];
-- locate remote server
BEGIN ENABLE
BEGIN
PupStream.PupNameTrouble => AbortBecauseNameLookupFailed[code, e];
PupStream.StreamClosing => AbortBecauseStreamClosing[why, text];
Stream.TimeOut => Abort[connectionTimedOut];
UNWIND => Storage.Free[pupConnection];
END;
PupStream.GetPupAddress[@pupAddress, remoteHost];
-- create network stream to remote server
pupConnection.streamHandle ←
PupStream.PupByteStreamCreate[
pupAddress,
IF receiveSeconds = LAST[CARDINAL] THEN PupStream.veryLongWait
ELSE PupStream.SecondsToTocks[receiveSeconds]];
END; -- enable
-- return connection
connection ← LOOPHOLE[pupConnection];
END;
CloseConnection: PROCEDURE [communicationSystem: CommunicationSystem, connection: Connection] =
BEGIN
-- local constants
pupConnection: PupConnection = LOOPHOLE[connection];
-- close network stream to remote server
pupConnection.streamHandle.delete[pupConnection.streamHandle];
-- release pup connection object
IF ~pupConnection.thirdPartyClose THEN Storage.Free[pupConnection]
ELSE pupConnection.streamHandle ← NIL;
END;
ActivatePort: PROCEDURE [
communicationSystem: CommunicationSystem, localSocket: LONG INTEGER,
serviceConnection: PROCEDURE [UNSPECIFIED, Connection, STRING],
serviceConnectionData: UNSPECIFIED, receiveSeconds: CARDINAL,
filter: PROCEDURE [STRING,Purpose],
purpose: Purpose ] RETURNS [port: Port] =
BEGIN
-- local variables
pupPort: PupPort;
-- allocate pup port object
pupPort ← Storage.Node[SIZE[PupPortObject]];
-- prepare deactivation event
PrepareEvent[@pupPort.deactivated];
-- fork listener process
pupPort.pH ← FORK PupListenerProcess[pupPort, localSocket,
serviceConnection, serviceConnectionData, receiveSeconds, filter, purpose];
-- return port
port ← LOOPHOLE[pupPort];
END;
PupListenerProcess: PROCEDURE [pupPort: PupPort, localSocket: LONG INTEGER,
serviceConnection: PROCEDURE [UNSPECIFIED, Connection, STRING],
serviceConnectionData: UNSPECIFIED, receiveSeconds: CARDINAL,
filter: PROCEDURE [STRING,Purpose],
purpose: Purpose] =
BEGIN
-- server process
PupServerProcess: PROCEDURE [streamHandle: Stream.Handle,
pupAddress: PupStream.PupAddress] =
BEGIN
-- local constants
remoteHost: STRING = [maxStringLength];
-- local variables
pupConnection: PupConnection ← NIL;
-- allocate and initialize pup connection object
BEGIN ENABLE ANY => IF ftpsystem.catchUnidentifiedErrors THEN CONTINUE;
pupConnection ← Storage.Node[SIZE[PupConnectionObject]];
pupConnection↑ ← PupConnectionObject[
streamHandle: streamHandle,
thirdPartyClose: TRUE, inputDiscontinuity: FALSE, outputDiscontinuity: FALSE,
inputDiscontinuityConsumed: FALSE, terminateOnEndPhysicalRecord: FALSE,
mark: ];
-- identify remote host
PupStream.AppendPupAddress[remoteHost, pupAddress];
-- dispatch client procedure
serviceConnection[serviceConnectionData, LOOPHOLE[pupConnection], remoteHost];
END; -- enable
-- close connection
IF pupConnection # NIL THEN
BEGIN
-- close network stream to remote server
IF pupConnection.streamHandle # NIL THEN
pupConnection.streamHandle.delete[pupConnection.streamHandle];
-- release pup connection object
Storage.Free[pupConnection];
END;
END;
-- local constants
-- Note: The transformation below assumes intimate knowledge
-- of Mesa's LONG INTEGER implementation.
longInteger: ARRAY {lob, hob} OF CARDINAL = LOOPHOLE[localSocket];
pupLocalSocket: PupStream.PupSocketID = [longInteger[hob], longInteger[lob]];
-- local variables
pupListener: PupStream.PupListener;
-- backstop all signals
BEGIN ENABLE ANY => IF ftpsystem.catchUnidentifiedErrors THEN CONTINUE;
CheckHim: PROCEDURE [pupAddress: PupStream.PupAddress] =
BEGIN
remoteHost: STRING = [20];
PupStream.AppendPupAddress[remoteHost, pupAddress];
filter[remoteHost, purpose !
RejectThisConnection => PupStream.RejectThisRequest[error] ];
END;
pupListener ←
PupStream.CreatePupByteStreamListener[pupLocalSocket, PupServerProcess,
PupStream.SecondsToTocks[receiveSeconds],CheckHim];
-- await deactivation
AwaitEvent[@pupPort.deactivated];
-- deactivate listener
PupStream.DestroyPupListener[pupListener];
END; -- enable
END;
DeactivatePort: PROCEDURE [communicationSystem: CommunicationSystem, port: Port] =
BEGIN
-- local constants
pupPort: PupPort = LOOPHOLE[port];
-- post deactivation event
PostEvent[@pupPort.deactivated];
-- join listener process
JOIN pupPort.pH;
-- release pup port object
Storage.Free[pupPort];
END;
-- **********************! Error Subroutines !***********************
AbortBecauseNameLookupFailed: PROCEDURE [nameLookupErrorCode: PupDefs.NameLookupErrorCode, message: STRING] =
BEGIN
-- local constants
ftpError: FtpError = SELECT nameLookupErrorCode FROM
noRoute => noRouteToNetwork,
noResponse => noNameLookupResponse,
ENDCASE => noSuchHost; -- errorFromServer
-- abort
AbortWithExplanation[ftpError, message];
END;
AbortBecauseStreamClosing: PUBLIC PROCEDURE [closeReason: PupStream.CloseReason, message: STRING] =
BEGIN
-- local constants
ftpError: FtpError = SELECT closeReason FROM
noRouteToNetwork => noRouteToNetwork,
transmissionTimeout => connectionTimedOut,
remoteReject => connectionRejected,
ENDCASE => connectionClosed; -- localClose/remoteClose
-- abort
AbortWithExplanation[ftpError, message];
END;
-- **********************! Main Program !***********************
-- no operation
END. -- of FTPPupComCool