-- file: [Igor]<Emerson>STP>UnsafeSTPsA.mesa - Simple/Stream Transfer Protocol
-- Initialization and Error stuff in here
-- Edited by:
-- Smokey, 16-Jul-81 14:54:32
-- Karlton, 9-Dec-81 18:23:36
-- Evans, Nov 14, 1980 9:35 AM
-- Davirro, 24-Sep-82 9:03:45
-- Schmidt January 20, 1983 6:05 pm
DIRECTORY
Format USING [Octal, StringProc],
Heap USING [systemZone],
LongString USING [AppendString],
UnsafeSTP USING [Error, ErrorCode, FileInfoObject],
UnsafeSTPOps USING [
CollectCode, CollectString, ErrorIfNextNotEOC, GetCommand, GetServerType,
Handle, markComment, markIAmVersion, markNo, maxStringLength, Object,
PList, PListArray, ProtocolError, PutCommand, UserProperties, ValidProperties],
UnsafeSTPReplyCode USING [ReplyCode],
UnsafeSTPRubicon USING[AppendStringAndGrow, EmptyString, StringLength],
PupStream USING [
CloseReason, GetPupAddress, PupAddress, PupByteStreamCreate, PupNameTrouble,
PupPackageDestroy, PupPackageMake, StreamClosing, veryLongWait],
PupTypes USING [fillInHostID, fillInNetID],
Stream USING [
GetProcedure, Handle, InputOptions, PutProcedure, SendAttentionProcedure,
SetSSTProcedure, SubSequenceType, WaitAttentionProcedure];
UnsafeSTPsA: PROGRAM
IMPORTS Format, Heap, PupStream, LongString,
STP: UnsafeSTP, STPOps: UnsafeSTPOps, STPRubicon: UnsafeSTPRubicon
EXPORTS UnsafeSTP, UnsafeSTPOps =
BEGIN OPEN PupStream, STPOps;
-- Global Data
Error: PUBLIC SIGNAL [
stp: STPOps.Handle, code: STP.ErrorCode, error: LONG STRING, reply: CHARACTER ← 0C] = CODE;
Object: PUBLIC TYPE = STPOps.Object;
z: UNCOUNTED ZONE = Heap.systemZone;
-- Public Interface Routines
Close: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN
nilByteStream: BOOLEAN;
IF stp = NIL THEN RETURN;
nilByteStream ← stp.byteStream = NIL;
CloseInternal[stp];
IF nilByteStream THEN
ERROR Error[stp: stp, code: noConnection, error: "Attempt to Close a NIL connection"L];
END;
CloseInternal: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN
IF stp = NIL THEN RETURN;
stp.plist ← DestroyPList[stp.plist];
IF stp.info ~= NIL THEN z.FREE[@stp.info];
IF stp.remoteString ~= NIL THEN z.FREE[@stp.remoteString];
IF stp.byteStream # NIL THEN
BEGIN
stp.byteStream.delete[stp.byteStream ! PupStream.StreamClosing => CONTINUE];
stp.byteStream ← NIL;
END;
END;
Create: PUBLIC PROCEDURE RETURNS [stp: STPOps.Handle] =
BEGIN OPEN PupTypes;
ENABLE UNWIND => z.FREE[@stp];
stp ← z.NEW[STPOps.Object];
PupStream.PupPackageMake[];
stp↑ ← STPOps.Object[];
RETURN[stp];
END;
Destroy: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [Handle] =
BEGIN
IF stp = NIL THEN RETURN[NIL];
IF stp.byteStream # NIL THEN Close[stp];
IF stp.host ~= NIL THEN z.FREE[@stp.host];
FOR i: STPOps.UserProperties IN STPOps.UserProperties DO
IF stp.userState[i] ~= NIL THEN z.FREE[@stp.userState[i]];
ENDLOOP;
z.FREE[@stp];
PupStream.PupPackageDestroy[];
RETURN[NIL];
END;
Open: PUBLIC PROCEDURE [stp: STPOps.Handle, host: LONG STRING]
RETURNS [herald: LONG STRING] =
BEGIN
reason: PupStream.CloseReason;
IF stp = NIL THEN RETURN[NIL];
IF stp.byteStream # NIL THEN
ERROR Error[stp, alreadyAConnection, "You already have a connection?"L];
BEGIN
shortHost: STRING ← [40];
server: PupStream.PupAddress ← [PupTypes.fillInNetID, PupTypes.fillInHostID, [0, 3]];
stpOptions: Stream.InputOptions = [
-- signalAttention: TRUE,--
terminateOnEndPhysicalRecord: FALSE,
signalLongBlock: FALSE,
signalShortBlock: FALSE,
signalSSTChange: TRUE,
signalEndOfStream: TRUE];
server.socket ← [0, 3];
LongString.AppendString[shortHost, host];
PupStream.GetPupAddress[
@server, shortHost !
PupStream.PupNameTrouble => GenerateErrorString[stp, noSuchHost, e]];
stp.byteStream ← PupStream.PupByteStreamCreate[
server, PupStream.veryLongWait !
PupStream.StreamClosing => {reason ← why; GOTO streamClosing}];
stp.byteStream.options ← stpOptions;
stp.remoteString ← z.NEW[StringBody[maxStringLength]];
stp.plist ← MakePList[];
stp.info ← z.NEW[STP.FileInfoObject];
stp.info↑ ← [];
stp.gotMark ← FALSE;
stp.serverType ← GetServerType[host];
PutCommand[stp, markIAmVersion, 1C, "STP calling"L !
PupStream.StreamClosing => {reason ← why; GOTO streamClosing}];
herald ← z.NEW[StringBody[80]];
BEGIN ENABLE UNWIND => z.FREE[@herald];
code: CHARACTER;
mark: Stream.SubSequenceType;
[mark, code] ← GetCommand[stp, @herald];
IF mark # markIAmVersion THEN GenerateProtocolError[stp, badVersion, mark, code];
ErrorIfNextNotEOC[stp];
END;
RETURN[herald];
EXITS streamClosing => {CloseInternal[stp]; GenerateStreamClosingError[stp, reason]};
END;
END;
-- PList Utilities
DestroyPList: PUBLIC PROCEDURE [plist: PList] RETURNS [PList] =
BEGIN
i: STPOps.ValidProperties;
IF plist # NIL THEN
BEGIN
FOR i IN STPOps.ValidProperties DO
IF plist[i] ~= NIL THEN z.FREE[@plist[i]];
ENDLOOP;
z.FREE[@plist];
END;
RETURN[NIL]
END;
MakePList: PUBLIC PROCEDURE RETURNS [plist: PList] =
BEGIN
i: STPOps.ValidProperties;
plist ← z.NEW[PListArray];
FOR i IN STPOps.ValidProperties DO plist[i] ← NIL; ENDLOOP;
END;
-- Error generation routines
ErrorCodeToSTPErrorCode: PUBLIC PROCEDURE [
errorCode: STP.ErrorCode, code: CHARACTER]
RETURNS [STP.ErrorCode] = {
replyCode: UnsafeSTPReplyCode.ReplyCode = LOOPHOLE[code];
RETURN[SELECT replyCode FROM
null => errorCode,
badCommand => protocolError,
noUserName => illegalUserName,
illegalCommand => protocolError,
badPList => protocolError,
illegalServerFilename => illegalFileName,
illegalDirectory => illegalFileName,
illegalNameBody => illegalFileName,
illegalVersion => illegalFileName,
illegalType => accessError,
illegalCharacterSize => accessError,
illegalEOLConversion => accessError,
illegalUserName => illegalUserName,
illegalUserPassword => illegalUserPassword,
illegalUserAccount => illegalUserAccount,
illegalConnectName => illegalConnectName,
illegalConnectPassword => illegalConnectPassword,
illegalCreationDate => illegalFileName,
illegalWriteDate => illegalFileName,
illegalReadDate => illegalFileName,
illegalAuthor => illegalFileName,
illegalDevice => illegalFileName,
fileNotFound => noSuchFile,
accessDenied => accessDenied,
inconsistent => protocolError,
fileDataError => errorCode,
tooLong => errorCode,
dontSend => errorCode,
notCompleted => errorCode,
transientError => errorCode,
permanentError => errorCode,
fileBusy => errorCode,
ENDCASE => errorCode] -- can't do any better--};
GenerateErrorString: PUBLIC PROCEDURE [
stp: STPOps.Handle, errorCode: STP.ErrorCode, string: LONG STRING , code: CHARACTER ← 0C] =
BEGIN
ERROR Error[stp,
ErrorCodeToSTPErrorCode[errorCode, code],
IF STPRubicon.StringLength[string] # 0 THEN string
ELSE
SELECT errorCode FROM
noSuchHost => "No such host!"L,
noRouteToNetwork => "No route to network!"L,
noNameLookupResponse => "Name lookup server is not responding"L,
alreadyAConnection => "You already have a connection!"L,
noConnection => "Please open a connection!"L,
connectionClosed => "Connection closed (local or remote)!"L,
connectionRejected => "Connection rejected by remote host!"L,
connectionTimedOut => "Connection timed out!"L,
accessDenied => "Access denied by remote server!"L,
illegalUserName => "Invalid or illegal UserName!"L,
illegalUserPassword => "Invalid or illegal UserPassword!"L,
illegalUserAccount => "Invalid or illegal UserAccount!"L,
illegalConnectName => "Invalid or illegal ConnectName!"L,
illegalConnectPassword => "Invalid or illegal ConnectPassword!"L,
credentailsMissing => "Name and/or Password not supplied!"L,
protocolError => "Internal FTP protocol error!"L,
illegalFileName => "Illegal filename!"L,
noSuchFile => "File not found!"L,
requestRefused => "Request refused by remote host!"L,
accessError => "Illegal access attempt on remote stream!"L,
undefinedError => "Undefined error!"L,
ENDCASE => ERROR, code];
END;
GenerateStreamClosingError: PUBLIC PROCEDURE [stp: STPOps.Handle, why: PupStream.CloseReason] =
BEGIN
GenerateErrorString[stp,
SELECT why FROM
localClose, remoteClose => connectionClosed,
noRouteToNetwork => noRouteToNetwork,
transmissionTimeout => connectionTimedOut,
remoteReject => connectionRejected,
ENDCASE => ERROR, NIL];
END;
GenerateProtocolError: PUBLIC PROCEDURE [
stp: STPOps.Handle, type: ProtocolError, mark: Stream.SubSequenceType, code: CHARACTER ← 0C] =
BEGIN
string: LONG STRING ← NIL;
MyAppend: Format.StringProc =
BEGIN STPRubicon.AppendStringAndGrow[to: @string, from: s, z: z]; END;
STPRubicon.AppendStringAndGrow[
to: @string,
from:
SELECT type FROM
badVersion => "Incompatable protocol version"L,
badMark => "Invalid or undefined mark byte"L,
badPList => "Invalid or malformed property list"L,
eocExpected => "End-Of-Command mark byte expected"L,
noCode => "error code is required after error mark byte"L,
ENDCASE => ERROR,
z: z];
STPRubicon.AppendStringAndGrow[to: @string, from: ", mark ="L, z: z];
Format.Octal[proc: MyAppend, n: mark];
STPRubicon.AppendStringAndGrow[to: @string, from: ", code ="L, z: z];
Format.Octal[proc: MyAppend, n: code];
ERROR Error[stp,
protocolError, string, code ! UNWIND => z.FREE[@string]];
END;
SelectError: PUBLIC PROCEDURE [
stp: STPOps.Handle, s: LONG STRING , mark: Stream.SubSequenceType] =
BEGIN
code: CHARACTER ← 0C;
IF mark = markNo OR mark = markComment THEN
BEGIN
IF mark # markComment THEN code ← CollectCode[stp];
CollectString[stp, @stp.remoteString];
GenerateErrorString[
stp, requestRefused,
IF STPRubicon.EmptyString[stp.remoteString] THEN s ELSE stp.remoteString,
code];
END
ELSE GenerateProtocolError[stp, badMark, mark, code];
END;
-- NOP and ERROR Stream routines
GetError: PUBLIC Stream.GetProcedure = {
ERROR STP.Error[NIL, accessError, "Attempt to Get from a store stream"L]};
PutError: PUBLIC Stream.PutProcedure = {
ERROR STP.Error[NIL, accessError, "Attempt to Put on a retrieve stream"L]};
SetSSTNop: PUBLIC Stream.SetSSTProcedure = BEGIN END;
SendAttentionNop: PUBLIC Stream.SendAttentionProcedure = BEGIN END;
WaitAttentionNop: PUBLIC Stream.WaitAttentionProcedure = BEGIN RETURN[0B] END;
END. -- of STPsA