-- file: UnsafeSTPsD.mesa - Simple/Stream Transfer Protocol
-- Common and protocol stuff in here
-- Edited by:
-- Mark, Feb 12, 1981 11:39 PM
-- Smokey, 17-Jul-81 7:20:16
-- Karlton, 17-Jul-81 17:45:12
-- JGS, 14-Aug-81 10:35:26
-- Loretta 27-Oct-82 10:45:58
-- Daniels 21-Sep-82 13:07:12
-- Davirro 15-Dec-82 15:32:52
DIRECTORY
Ascii USING [SP],
Heap USING [systemZone],
PupStream USING[StreamClosing],
UnsafeSTP USING [DesiredProperties, Error, ErrorCode, FileInfo, Open, Type],
UnsafeSTPOps USING [
DestroyPList, ErrorCodeToSTPErrorCode, FileProperties, FilenameType,
GenerateErrorString, GenerateProtocolError, Handle, markAbort, markComment, markEOC, markHereIsPList, markIAmVersion, markNo, markYes, markYouAreUser, maxStringLength, Object, PList, PListArray, ServerType, SmashClosed, UserProperties, ValidProperties],
Stream USING [
Block, GetChar, Handle, Object, PutBlock, PutChar, SetSST,
SSTChange, SubSequenceType, TimeOut],
UnsafeSTPRubicon USING[AppendCharAndGrow, AppendStringAndGrow, CopyToNewString,
EmptyString, FreeString, Replace],
LongString USING [EquivalentString, StringToLongNumber],
Time USING [Append, Packed, Unpack];
UnsafeSTPsD: PROGRAM
IMPORTS
Heap, PupStream, STP: UnsafeSTP, STPOps: UnsafeSTPOps, STPRubicon: UnsafeSTPRubicon,
Stream, LongString, Time
EXPORTS UnsafeSTP, UnsafeSTPOps =
BEGIN OPEN STPOps;
-- Exported Types
Object: PUBLIC TYPE = STPOps.Object;
-- Global Data
z: UNCOUNTED ZONE = Heap.systemZone;
MarkEncountered: PUBLIC ERROR = CODE;
BadProperty: PUBLIC ERROR = CODE;
propertyStrings: PListArray = [
userName: "User-Name", userPassword: "User-Password",
connectName: "Connect-Name", connectPassword: "Connect-Password",
byteSize: "Byte-Size", type: "Type", size: "Size", directory: "Directory",
nameBody: "Name-Body", version: "Version", createDate: "Creation-Date",
readDate: "Read-Date", writeDate: "Write-Date", author: "Author",
eolConversion: "End-of-Line-Convention", account: "Account",
userAccount: "User-Account", device: "Device",
serverName: "Server-Filename"];
desiredPropString: STRING = "Desired-property";
-- Commonly used stuff
Connect: PUBLIC PROCEDURE [stp: STPOps.Handle, name, password: LONG STRING] =
BEGIN
STPRubicon.Replace[@stp.userState[connectName], name, z];
STPRubicon.Replace[@stp.userState[connectPassword], password, z];
END;
IsOpen: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [yes: BOOLEAN] = {
yes ← stp.byteStream # NIL};
GetProperty: PUBLIC PROCEDURE [stp: STPOps.Handle, prop: ValidProperties] RETURNS [LONG STRING] = {
RETURN[stp.plist[prop]]};
Login: PUBLIC PROCEDURE [stp: STPOps.Handle, name, password: LONG STRING] =
BEGIN
STPRubicon.Replace[@stp.userState[userName], name, z];
STPRubicon.Replace[@stp.userState[userPassword], password, z];
END;
SetHost: PUBLIC PROCEDURE [stp: STPOps.Handle, host: LONG STRING] =
BEGIN STPRubicon.Replace[@stp.host, host, z]; END;
SetDirectory: PUBLIC PROCEDURE [stp: STPOps.Handle, directory: LONG STRING] =
BEGIN STPRubicon.Replace[@stp.userState[directory], directory, z]; END;
-- Procedures for doing FTP protocol operations
GetCommand: PUBLIC PROCEDURE [stp: STPOps.Handle, ps: LONG POINTER TO LONG STRING]
RETURNS [mark: Stream.SubSequenceType, code: CHARACTER] =
BEGIN
code ← 0C;
CheckConnection[stp];
mark ← MyGetMark[stp];
SELECT mark FROM
markAbort, markComment, markYouAreUser => CollectString[stp, ps];
markIAmVersion, markNo, markYes => {code ← CollectCode[stp]; CollectString[stp, ps]};
ENDCASE => GenerateProtocolError[stp, badMark, mark, code];
RETURN[mark, code]
END;
GetHereIsAndPList: PUBLIC PROCEDURE [stp: STPOps.Handle, gobbleEOC: BOOLEAN ← TRUE] =
BEGIN
mark: Stream.SubSequenceType;
DO
SELECT (mark ← MyGetMark[stp]) FROM
markComment => CollectString[stp, @stp.remoteString];
markHereIsPList => {GetPList[stp, gobbleEOC, FALSE]; EXIT};
markNo =>
BEGIN
code: CHARACTER = CollectCode[stp];
errorCode: STP.ErrorCode =
ErrorCodeToSTPErrorCode[requestRefused, code];
CollectString[stp, @stp.remoteString];
ErrorIfNextNotEOC[stp];
GenerateErrorString[stp, errorCode, stp.remoteString, code];
END;
ENDCASE => GenerateProtocolError[stp, badMark, mark, 0C];
ENDLOOP;
END;
GetPList: PUBLIC PROCEDURE [stp: STPOps.Handle, gobbleEOC: BOOLEAN, propertiesOk: BOOL] =
BEGIN
property: LONG STRING ← z.NEW[StringBody[maxStringLength]];
value: LONG STRING ← z.NEW[StringBody[maxStringLength]];
mark: Stream.SubSequenceType;
CheckConnection[stp];
BEGIN ENABLE UNWIND => {z.FREE[@property]; z.FREE[@value]};
parens: INTEGER ← 0;
char: CHARACTER;
string: LONG STRING ← property;
IF stp.plist[nameBody] # NIL THEN z.FREE[@stp.plist[nameBody]];
DO
char ← MyGetChar[stp ! MarkEncountered => IF NOT propertiesOk THEN GOTO BadPList];
SELECT char FROM
'( =>
BEGIN parens ← parens + 1; string ← property; string.length ← 0; END;
Ascii.SP =>
IF string = property THEN BEGIN string ← value; string.length ← 0; END
ELSE STPRubicon.AppendCharAndGrow[@string, char, z];
') =>
BEGIN
IF property.length # 0 AND value.length # 0 THEN
BEGIN
SetPListItem[
stp.plist, property, value ! BadProperty => CONTINUE];
property.length ← value.length ← 0;
END;
IF (parens ← parens - 1) = 0 THEN EXIT;
END;
ENDCASE => STPRubicon.AppendCharAndGrow[@string, char, z];
ENDLOOP;
IF (property.length # 0 AND value.length # 0) THEN GOTO BadPList;
IF gobbleEOC AND MyGetMark[stp] # markEOC THEN GOTO BadPList;
EXITS
BadPList => {ResetPList[stp.plist]; GenerateProtocolError[stp, badPList, mark]};
END;
z.FREE[@property];
z.FREE[@value];
END;
PutCommand: PUBLIC PROCEDURE [
stp: STPOps.Handle, mark: Stream.SubSequenceType, code: CHARACTER,
string: LONG STRING, sendEOC: BOOLEAN ← TRUE] =
BEGIN
CheckConnection[stp];
Stream.SetSST[stp.byteStream, mark];
Stream.PutChar[stp.byteStream, code];
IF ~STPRubicon.EmptyString[string] THEN MyPutString[stp.byteStream, string];
IF sendEOC THEN Stream.SetSST[stp.byteStream, markEOC];
END;
PutPList: PUBLIC PROCEDURE [
stp: STPOps.Handle, mark: Stream.SubSequenceType, sendEOC: BOOLEAN ← TRUE] =
BEGIN
state: {okay, tryOpen, triedOpen} ← okay;
DoIt: PROCEDURE =
BEGIN
CheckConnection[stp];
IF mark # 0B THEN Stream.SetSST[stp.byteStream, mark];
Stream.PutChar[stp.byteStream, '(];
FOR i: ValidProperties IN ValidProperties DO
IF ~STPRubicon.EmptyString[stp.plist[i]] THEN
PutPListItem[stp.byteStream, i, stp.plist[i]];
ENDLOOP;
IF stp.desiredProps # ALL[TRUE] THEN PutDesiredProps[stp];
Stream.PutChar[stp.byteStream, ')];
IF sendEOC THEN Stream.SetSST[stp.byteStream, markEOC];
END;
DO
DoIt[! PupStream.StreamClosing, Stream.TimeOut =>
IF state = okay AND ~STPRubicon.EmptyString[stp.host] THEN
{state ← tryOpen; CONTINUE}];
IF state # tryOpen THEN EXIT;
BEGIN
savedPList: PList ← stp.plist;
stp.plist ← NIL;
SmashClosed[stp]; -- Call outside catch so Pup monitor unlocked
STPRubicon.FreeString[z, STP.Open[stp, stp.host]];
stp.plist ← DestroyPList[stp.plist];
stp.plist ← savedPList;
state ← triedOpen;
END;
ENDLOOP;
END;
-- Utility routines
CollectString: PUBLIC PROCEDURE [stp: STPOps.Handle, ps: LONG POINTER TO LONG STRING] =
BEGIN
char: CHARACTER;
IF ps↑ # NIL THEN ps↑.length ← 0 ELSE ps↑ ← z.NEW[StringBody[15]];
DO
char ← MyGetChar[stp ! MarkEncountered => {
mark: Stream.SubSequenceType ← LookAtMark[stp];
SELECT mark FROM
markHereIsPList, markEOC => EXIT;
ENDCASE => GenerateProtocolError[stp, eocExpected, mark]}];
STPRubicon.AppendCharAndGrow[ps, char, z];
ENDLOOP;
END;
CollectCode: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [code: CHARACTER] =
BEGIN
code ← 0C;
CheckConnection[stp];
code ← MyGetChar[stp ! MarkEncountered =>
GenerateProtocolError[stp, noCode, MyGetMark[stp]]];
END;
CheckConnection: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN
WHILE stp.byteStream = NIL DO
SIGNAL STP.Error[stp, noConnection, "Please open a connection!"L, 0C] ENDLOOP
END;
ErrorIfNextNotYes: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN
code: CHARACTER ← 0C;
mark: Stream.SubSequenceType;
[mark, code] ← GetCommand[stp, @stp.remoteString];
IF mark # markYes THEN GenerateErrorString[stp, requestRefused, stp.remoteString, code];
END;
ErrorIfNextNotEOC: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN
mark: Stream.SubSequenceType ← MyGetMark[stp];
IF mark # markEOC THEN GenerateProtocolError[stp, eocExpected, mark];
END;
GetServerType: PUBLIC PROCEDURE [server: LONG STRING]
RETURNS [serverType: ServerType] =
BEGIN OPEN LongString;
RETURN[SELECT TRUE FROM
EquivalentString[server, "MAXC"L],
EquivalentString[server, "MAXC1"L],
EquivalentString[server, "MAXC2"L] => tenex,
ENDCASE => ifs];
END;
LookAtMark: PUBLIC PROCEDURE [stp: STPOps.Handle]
RETURNS [Stream.SubSequenceType] =
BEGIN
IF ~stp.gotMark THEN
DO
[] ← MyGetChar[stp ! MarkEncountered => CONTINUE];
IF stp.gotMark THEN EXIT;
ENDLOOP;
RETURN[stp.mark]
END;
MyGetChar: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [char: CHARACTER] =
BEGIN
char ← Stream.GetChar[
stp.byteStream !
Stream.SSTChange =>
BEGIN stp.mark ← sst; stp.gotMark ← TRUE; ERROR MarkEncountered; END];
RETURN[char];
END;
MyGetMark: PUBLIC PROCEDURE [stp: STPOps.Handle]
RETURNS [mark: Stream.SubSequenceType] =
BEGIN mark ← LookAtMark[stp]; stp.gotMark ← FALSE; RETURN[mark] END;
MyPutString: PUBLIC PROCEDURE [byteStream: Stream.Handle, string: LONG STRING] =
BEGIN
block: Stream.Block ← [LOOPHOLE[@string.text], 0, string.length];
Stream.PutBlock[byteStream, block, FALSE];
END;
PropertyString: PUBLIC PROCEDURE [prop: STPOps.ValidProperties]
RETURNS [string: LONG STRING] = BEGIN RETURN[propertyStrings[prop]]; END;
SetCreateTime: PUBLIC PROCEDURE [stp: STPOps.Handle, creation: Time.Packed] =
BEGIN
cDate: STRING ← [24];
Time.Append[cDate, Time.Unpack[creation], TRUE];
STPRubicon.Replace[@stp.plist[createDate], cDate, z];
END;
SetFileType: PUBLIC PROCEDURE [
stp: STPOps.Handle, fileType: STP.Type] =
BEGIN
STPRubicon.Replace[@stp.plist[type],
SELECT fileType FROM text => "Text"L, binary => "Binary"L, ENDCASE => NIL, z];
END;
SetByteSize: PUBLIC PROCEDURE [stp: STPOps.Handle, fileType: STP.Type] =
BEGIN
STPRubicon.Replace[@stp.plist[byteSize],
SELECT stp.serverType FROM
ifs, unknown, tenex => IF fileType = text THEN NIL ELSE "8",
ENDCASE => ERROR STP.Error[stp, undefinedError, NIL], z];
END;
StringToFileType: PUBLIC PROCEDURE [string: LONG STRING]
RETURNS [type: STP.Type] =
BEGIN
type ←
SELECT TRUE FROM
string = NIL => unknown,
LongString.EquivalentString["Text"L, string] => text,
LongString.EquivalentString["Binary"L, string] => binary,
ENDCASE => unknown;
END;
-- PList and FileInfo Utilities
NameToPList: PUBLIC PROCEDURE [plist: PList, name: LONG STRING, type: FilenameType] =
BEGIN
i: CARDINAL;
versionSeen: BOOLEAN ← FALSE;
temp: LONG STRING;
IF STPRubicon.EmptyString[name] THEN RETURN;
temp ← z.NEW[StringBody[100]];
FOR i IN [0..name.length) DO
SELECT name[i] FROM
'[ => IF i # 0 THEN STPRubicon.AppendCharAndGrow[@temp, name[i], z];
'], ': =>
BEGIN STPRubicon.Replace[@plist[device], temp, z]; temp.length ← 0; END;
'< =>
BEGIN
IF temp.length # 0 THEN STPRubicon.Replace[@plist[device], temp, z];
temp.length ← 0;
STPRubicon.AppendCharAndGrow[@temp, name[i], z];
IF plist[directory] # NIL THEN plist[directory].length ← 0;
END;
'> =>
BEGIN
IF
(STPRubicon.EmptyString[plist[directory]] OR
plist[directory][plist[directory].length-1] # '>) AND
(temp.length = 0 OR temp[0] # '<)
THEN STPRubicon.AppendCharAndGrow[@plist[directory], name[i], z];
STPRubicon.AppendStringAndGrow[@plist[directory], temp, z];
temp.length ← 0;
END;
'!, '; =>
BEGIN
IF temp.length # 0 THEN STPRubicon.Replace[@plist[nameBody], temp, z];
versionSeen ← TRUE;
temp.length ← 0;
END;
ENDCASE => STPRubicon.AppendCharAndGrow[@temp, name[i], z];
ENDLOOP;
IF temp.length # 0 THEN {
IF versionSeen THEN STPRubicon.Replace[@plist[version], temp, z]
ELSE STPRubicon.Replace[@plist[nameBody], temp, z]};
z.FREE[@temp];
END;
PListToName: PUBLIC PROCEDURE [plist: PList, type: FilenameType]
RETURNS [name: LONG STRING] =
BEGIN
name ← z.NEW[StringBody[40]];
IF ~STPRubicon.EmptyString[plist[directory]] THEN
BEGIN
SELECT plist[directory][0] FROM
'> => FOR i: CARDINAL IN [1..plist[directory].length) DO
STPRubicon.AppendCharAndGrow[@name, plist[directory][i], z] ENDLOOP;
'< => STPRubicon.AppendStringAndGrow[@name, plist[directory], z];
ENDCASE => {
STPRubicon.AppendCharAndGrow[@name, '<, z];
STPRubicon.AppendStringAndGrow[@name, plist[directory], z]};
IF plist[directory][plist[directory].length - 1] # '> THEN
STPRubicon.AppendCharAndGrow[@name, '>, z];
END;
IF ~STPRubicon.EmptyString[plist[nameBody]] THEN
STPRubicon.AppendStringAndGrow[@name, plist[nameBody], z];
IF ~STPRubicon.EmptyString[plist[version]] THEN
BEGIN
IF type = alto THEN STPRubicon.AppendCharAndGrow[@name, '!, z]
ELSE STPRubicon.AppendCharAndGrow[@name, ';, z];
STPRubicon.AppendStringAndGrow[@name, plist[version], z];
END;
END;
MakeRemoteName: PUBLIC PROCEDURE [plist: PList, type: FilenameType] RETURNS [LONG STRING] = {
RETURN[IF STPRubicon.EmptyString[plist[serverName]] THEN PListToName[plist, type]
ELSE STPRubicon.CopyToNewString[plist[serverName], z]]};
PutPListItem: PUBLIC PROCEDURE [
byteStream: Stream.Handle, property: STPOps.ValidProperties, value: LONG STRING] =
BEGIN
Stream.PutChar[byteStream, '(];
MyPutString[byteStream, PropertyString[property]];
Stream.PutChar[byteStream, Ascii.SP];
MyPutString[byteStream, value];
Stream.PutChar[byteStream, ')];
END;
ResetPList: PUBLIC PROCEDURE [plist: PList] =
BEGIN
i: STPOps.ValidProperties;
IF plist = NIL THEN RETURN;
FOR i IN STPOps.ValidProperties DO
IF plist[i] # NIL THEN z.FREE[@plist[i]] ENDLOOP;
END;
GetFileInfo: PUBLIC PROCEDURE [stp: STPOps.Handle] RETURNS [STP.FileInfo] =
BEGIN
FOR i: STPOps.FileProperties IN STPOps.FileProperties DO
SELECT i FROM
directory => stp.info.directory ← stp.plist[i];
nameBody => stp.info.body ← stp.plist[i];
version => stp.info.version ← stp.plist[i];
author => stp.info.author ← stp.plist[i];
createDate => stp.info.create ← stp.plist[i];
readDate => stp.info.read ← stp.plist[i];
writeDate => stp.info.write ← stp.plist[i];
size =>
stp.info.size ←
IF STPRubicon.EmptyString[stp.plist[i]] THEN 0
ELSE LongString.StringToLongNumber[stp.plist[i], 10];
type => stp.info.type ← StringToFileType[stp.plist[i]];
ENDCASE;
ENDLOOP;
RETURN[stp.info];
END;
SetPListItem: PUBLIC PROCEDURE [plist: PList, property, value: LONG STRING] =
BEGIN
FOR i: STPOps.ValidProperties IN STPOps.ValidProperties DO
IF LongString.EquivalentString[PropertyString[i], property] THEN {
STPRubicon.Replace[@plist[i], value, z]; RETURN};
ENDLOOP;
ERROR BadProperty
END;
UserStateToPList: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN OPEN stp;
FOR i: STPOps.UserProperties IN STPOps.UserProperties DO
-- Solution 1
STPRubicon.Replace[@plist[i], userState[i], z]; -- Solution 2
-- IF plist[i] # NIL THEN ERROR Error[stp, undefinedError];
-- IF ~STPRubicon.EmptyString[userState[i] THEN
-- plist[i] ← STPRubicon.CopyToNewString[userState[i], z];
-- Solution 3
-- IF userState[i] # NIL THEN
-- STPRubicon.Replace[@plist[i], userState[i], z];
ENDLOOP;
END;
-- Desired Property stuff
SetDesiredProperties: PUBLIC PROC[stp: STPOps.Handle, props: STP.DesiredProperties] =
BEGIN
stp.desiredProps ← props
END;
GetDesiredProperties: PUBLIC PROC [stp: STPOps.Handle]
RETURNS [props: STP.DesiredProperties] =
BEGIN
RETURN[stp.desiredProps]
END;
PutDesiredProps: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN
FOR i: STPOps.FileProperties IN STPOps.FileProperties DO
IF stp.desiredProps[i] THEN
PutDesiredPropsItem[stp.byteStream, PropertyString[i]]
ENDLOOP;
END;
PutDesiredPropsItem: PUBLIC PROCEDURE [
byteStream: Stream.Handle, value: LONG STRING] =
BEGIN
Stream.PutChar[byteStream, '(];
MyPutString[byteStream, desiredPropString];
Stream.PutChar[byteStream, Ascii.SP];
MyPutString[byteStream, value];
Stream.PutChar[byteStream, ')];
END;
END. -- of STPsD