-- FTPPupComCool.mesa, Edit: HGM December 16, 1980 1:56 AM
-- Copyright Xerox Corporation 1979, 1980
DIRECTORY
FTPDefs,
FTPPrivateDefs,
FTPPupComDefs,
PupDefs USING [
AppendPupAddress, GetPupAddress, NameLookupErrorCode, PupNameTrouble,
PupPackageDestroy, PupPackageMake, SecondsToTocks, veryLongWait],
PupStream USING [
CloseReason, CreatePupByteStreamListener, DestroyPupListener, PupAddress,
PupByteStreamAbort, PupByteStreamCreate, PupListener, PupSocketID,
RejectThisRequest, StreamClosing],
PupTypes USING [fillInHostID, fillInNetID],
Stream USING [Handle, TimeOut],
MDSStorage USING [Node, Free];
FTPPupComCool: PROGRAM
IMPORTS
Stream, Storage: MDSStorage, PupDefs, PupStream, 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,
AbortConnection: AbortConnection, 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 PupDefs.PupPackageMake[]; END;
DestroyCommunicationSystem: PROCEDURE [
communicationSystem: CommunicationSystem] =
BEGIN
-- destroy pup package
PupDefs.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
PupDefs.PupNameTrouble => AbortBecauseNameLookupFailed[code, e];
PupStream.StreamClosing => {
string: STRING ← [40];
IF text # NIL THEN {
string.length ← MIN[string.maxlength, text.length];
FOR i: CARDINAL IN [0..string.length) DO string[i] ← text[i] ENDLOOP}
ELSE string ← NIL;
AbortBecauseStreamClosing[why, string]};
Stream.TimeOut => Abort[connectionTimedOut];
UNWIND => Storage.Free[pupConnection];
END;
PupDefs.GetPupAddress[@pupAddress, remoteHost];
-- create network stream to remote server
pupConnection.streamHandle ← PupStream.PupByteStreamCreate[
pupAddress,
IF receiveSeconds = LAST[CARDINAL] THEN PupDefs.veryLongWait
ELSE PupDefs.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;
AbortConnection: PROCEDURE [
communicationSystem: CommunicationSystem, connection: Connection,
text: STRING] =
BEGIN
pupConnection: PupConnection = LOOPHOLE[connection];
PupStream.PupByteStreamAbort[pupConnection.streamHandle, text];
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
PupDefs.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];
PupDefs.AppendPupAddress[remoteHost, pupAddress];
filter[
remoteHost, purpose !
RejectThisConnection => PupStream.RejectThisRequest[error]];
END;
pupListener ← PupStream.CreatePupByteStreamListener[
pupLocalSocket, PupServerProcess, PupDefs.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: LONG STRING] =
BEGIN
-- local constants
ftpError: FtpError =
SELECT nameLookupErrorCode FROM
noRoute => noRouteToNetwork,
noResponse => noNameLookupResponse,
ENDCASE => noSuchHost; -- errorFromServer
string: STRING ← [40];
IF message # NIL THEN {
string.length ← MIN[string.maxlength, message.length];
FOR i: CARDINAL IN [0..string.length) DO string[i] ← message[i] ENDLOOP;
message ← string}
ELSE string ← NIL;
-- abort
AbortWithExplanation[ftpError, string];
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