-- file: [Igor]<Emerson>STP>Private>UnsafeSTPsB.mesa - Simple/Stream Transfer Protocol
-- Traditional FTP-like interface stuff in here
-- Edited by:
-- Smokey, 17-Jul-81 7:48:31
-- JGS, 17-Aug-81 11:26:24
-- Karlton, 15-Mar-82 17:26:37
-- Loretta, 10-Nov-81 16:07:59
-- Davirro, 24-Sep-82 10:35:49
-- Daniels, 21-Sep-82 13:05:55
DIRECTORY
Environment USING [Byte, bytesPerPage],
FileStream USING[GetIndex, SetIndex],
Heap USING [systemZone],
Inline USING [BITAND],
PupStream USING [CloseReason, PupByteStreamAbort, StreamClosing],
UnsafeSTP USING [
Close, Completion, CompletionProcType, Confirmation, ConfirmProcType,
CredentialsErrors, Error, ErrorCode, FileErrors, NoteFileProcType, Type],
UnsafeSTPOps USING [
CheckConnection, CollectCode, CollectString, ErrorIfNextNotYes, ErrorIfNextNotEOC,
ErrorCodeToSTPErrorCode,
GenerateErrorString, GenerateProtocolError, GenerateStreamClosingError, GetCommand,
GetHereIsAndPList, GetPList, Handle, LookAtMark, MarkEncountered,
MakeRemoteName, markComment, markDelete,
markDirectory, markEOC, markHereIsFile, markHereIsPList, markNo, markNewDirectory,
markNewStore, markRename,
markRetrieve, markYes, MyGetMark, NameToPList, Object, Operation, PList,
PutCommand, PutPList, ResetPList, SelectError, SetByteSize, SetCreateTime,
SetFileType, UserStateToPList],
UnsafeSTPReplyCode USING[ReplyCode],
Stream USING [
Block, CompletionCode, EndOfStream, GetByte, Handle,
PutBlock, SetSST, SSTChange, SubSequenceType,
TimeOut],
Time USING [Packed];
UnsafeSTPsB: PROGRAM
IMPORTS FileStream, Heap, Inline, PupStream, STP: UnsafeSTP, STPOps: UnsafeSTPOps, Stream
EXPORTS UnsafeSTP, UnsafeSTPOps =
BEGIN OPEN PupStream, STPOps;
-- Data and types
BytesPerPage: CARDINAL = Environment.bytesPerPage;
PageOfStorage: TYPE = PACKED ARRAY [0..BytesPerPage) OF Environment.Byte;
Object: PUBLIC TYPE = STPOps.Object;
z: UNCOUNTED ZONE = Heap.systemZone;
-- Procedures for normal FTP-like interface
Delete: PUBLIC PROCEDURE [
stp: STPOps.Handle, name: LONG STRING, confirm: STP.ConfirmProcType,
complete: STP.CompletionProcType] =
BEGIN DoFiles[stp, name, confirm, complete, delete]; END;
Enumerate: PUBLIC PROCEDURE [
stp: STPOps.Handle, name: LONG STRING, proc: STP.NoteFileProcType] =
BEGIN
Foo1: STP.ConfirmProcType =
BEGIN
RETURN[answer: IF proc[file] = yes THEN do ELSE abort, localStream: NIL];
END;
Foo2: STP.CompletionProcType = BEGIN END;
DoFiles[stp, name, Foo1, Foo2, directory];
END;
Rename: PUBLIC PROCEDURE [
stp: STPOps.Handle, old, new: LONG STRING] =
BEGIN
mark, saveMark: Stream.SubSequenceType;
code: CHARACTER ← 0C;
CheckConnection[stp];
ResetPList[stp.plist];
UserStateToPList[stp];
NameToPList[stp.plist, old, alto];
PutPList[stp, markRename, FALSE];
ResetPList[stp.plist];
UserStateToPList[stp];
NameToPList[stp.plist, new, alto];
PutPList[stp, 0B];
[saveMark, code] ← GetCommand[stp, @stp.remoteString];
IF (mark ← MyGetMark[stp]) # markEOC THEN
GenerateProtocolError[stp, eocExpected, mark];
IF saveMark # markYes THEN
GenerateErrorString[stp, requestRefused, stp.remoteString, code];
END;
Retrieve: PUBLIC PROCEDURE [
stp: STPOps.Handle, file: LONG STRING, confirm: STP.ConfirmProcType ← NIL,
complete: STP.CompletionProcType ← NIL] =
BEGIN DoFiles[stp, file, confirm, complete, retrieve]; END;
Store: PUBLIC PROCEDURE [
stp: STPOps.Handle, file: LONG STRING, stream: Stream.Handle,
noteFile: STP.NoteFileProcType ← NIL,
fileType: STP.Type, creation: Time.Packed] =
BEGIN
Confirm: STP.ConfirmProcType =
{IF noteFile = NIL OR noteFile[file] = yes THEN
RETURN[do, stream] ELSE RETURN[skip, NIL]};
CheckConnection[stp];
ResetPList[stp.plist];
UserStateToPList[stp];
NameToPList[stp.plist, file, alto];
IF fileType = unknown THEN fileType ← FindFileType[stream];
SetFileType[stp, fileType];
STPOps.SetByteSize[stp, fileType];
STPOps.SetCreateTime[stp, creation];
DoFiles[stp, file, Confirm, NIL, store];
END;
-- common routine for Delete, Enumerate and Retrieve
DoFiles: PUBLIC PROCEDURE [
stp: STPOps.Handle, file: LONG STRING, confirm: STP.ConfirmProcType,
complete: STP.CompletionProcType, op: STPOps.Operation] =
BEGIN
reason: PupStream.CloseReason;
reply: STP.Confirmation;
string: LONG STRING ← NIL;
local: Stream.Handle ← NIL;
killConnection: BOOLEAN ← TRUE;
CleanUp: PROCEDURE =
BEGIN
IF killConnection THEN SmashClosed[stp];
IF string ~= NIL THEN z.FREE[@string]
END;
{ENABLE {
PupStream.StreamClosing => {reason ← why; GOTO streamClosing};
Stream.TimeOut => {reason ← transmissionTimeout; GOTO streamClosing};
UNWIND => CleanUp[]};
IF op # store THEN
BEGIN
CheckConnection[stp];
ResetPList[stp.plist];
UserStateToPList[stp];
NameToPList[stp.plist, file, alto];
END;
IF op = directory THEN
SELECT TryNewDirectory[stp, confirm ! STP.Error =>
IF code IN STP.CredentialsErrors OR code IN STP.FileErrors THEN
killConnection ← FALSE] FROM
finished => RETURN;
aborted => {CleanUp[]; RETURN};
tryOldDirectory => NULL; -- just falls thru
ENDCASE => ERROR;
PutPList[
stp,
SELECT op FROM
delete => markDelete,
directory => markDirectory,
retrieve => markRetrieve,
store => markNewStore,
ENDCASE => ERROR];
DO
IF LookAtMark[stp] = markEOC THEN {[] ← MyGetMark[stp]; EXIT};
GetHereIsAndPList[stp, op # directory ! STP.Error =>
IF code IN STP.CredentialsErrors OR code IN STP.FileErrors THEN
killConnection ← FALSE];
SELECT TRUE FROM
confirm = NIL => {reply ← do; local ← NIL};
ENDCASE => [reply, local] ← confirm[string ← MakeRemoteName[stp.plist, alto]];
IF string ~= NIL THEN z.FREE[@string];
SELECT reply FROM
do => {
completion: STP.Completion;
SELECT op FROM
delete => {
code: CHARACTER ← 0C;
mark: Stream.SubSequenceType;
PutCommand[stp, markYes, 0C, "Yes, please"L];
[mark, code] ← GetCommand[stp, @stp.remoteString];
SELECT mark FROM
markYes => completion ← ok;
markNo => completion ← error;
ENDCASE => GenerateErrorString[stp, protocolError, stp.remoteString, code]};
retrieve => {
PutCommand[stp, markYes, 0C, "Yes, please"L];
completion ← IF GetFile[stp, local, stp.plist[nameBody]] THEN ok ELSE error};
store => {
PutFile[stp, local, stp.plist[nameBody]];
ErrorIfNextNotYes[stp]};
ENDCASE;
IF complete # NIL THEN complete[completion, stp.remoteString];
IF LookAtMark[stp] = markEOC THEN {[] ← MyGetMark[stp]; EXIT}};
skip => {
IF op # directory THEN
PutCommand[stp, markNo, 106C, "No Thanks"L];
IF op = store THEN {
mark: Stream.SubSequenceType;
code: CHARACTER;
[mark, code] ← GetCommand[stp, @stp.remoteString];
IF mark # markNo THEN
GenerateErrorString[stp, protocolError, stp.remoteString, code]};
};
abort => {CleanUp[]; RETURN};
ENDCASE => ERROR;
ResetPList[stp.plist];
ENDLOOP;
EXITS
streamClosing => GenerateStreamClosingError[stp, reason]};
END;
TryNewDirectory: PROC[stp: STPOps.Handle, confirm: STP.ConfirmProcType]
RETURNS[{finished, aborted, tryOldDirectory}] = {
mark: Stream.SubSequenceType;
PutPList[stp, markNewDirectory];
DO
SELECT (mark ← MyGetMark[stp]) FROM
markComment => CollectString[stp, @stp.remoteString];
markHereIsPList => {
string: LONG STRING;
reply: STP.Confirmation;
DO
GetPList[stp: stp, gobbleEOC: FALSE, propertiesOk: TRUE
! MarkEncountered => {
mark: Stream.SubSequenceType ← LookAtMark[stp];
IF mark = markEOC THEN EXIT
ELSE GenerateProtocolError[stp, eocExpected, mark]
}];
string ← MakeRemoteName[stp.plist, alto];
[answer: reply] ← confirm[string];
IF string ~= NIL THEN z.FREE[@string];
IF reply = abort THEN RETURN[aborted];
ENDLOOP;
ErrorIfNextNotEOC[stp];
RETURN[finished];
};
markNo =>
BEGIN
code: CHARACTER = CollectCode[stp];
errorCode: STP.ErrorCode =
ErrorCodeToSTPErrorCode[requestRefused, code];
CollectString[stp, @stp.remoteString];
ErrorIfNextNotEOC[stp];
IF LOOPHOLE[code, UnsafeSTPReplyCode.ReplyCode] = badCommand THEN
RETURN[tryOldDirectory];
GenerateErrorString[stp, errorCode, stp.remoteString, code];
END;
ENDCASE => GenerateProtocolError[stp, badMark, mark, 0C];
ENDLOOP;
};
-- Procedures for doing FTP protocol operations
GetFile: PUBLIC PROCEDURE [
stp: STPOps.Handle, stream: Stream.Handle, file: LONG STRING]
RETURNS[gotIt: BOOLEAN] =
BEGIN
mark: Stream.SubSequenceType;
CheckConnection[stp];
SELECT (mark ← MyGetMark[stp]) FROM
markHereIsFile => NULL;
markNo => {
[] ← CollectCode[stp]; CollectString[stp, @stp.remoteString]; RETURN[FALSE]};
ENDCASE => SelectError[stp, "HereIsFile Expected"L, mark];
TransferTheFile[stp: stp, from: stp.byteStream, to: stream];
ErrorIfNextNotYes[stp]; -- should do something about file on local disk
RETURN[TRUE]
END;
PutFile: PUBLIC PROCEDURE [
stp: STPOps.Handle, stream: Stream.Handle, file: LONG STRING, sendEOC: BOOLEAN ← TRUE] =
BEGIN
CheckConnection[stp];
Stream.SetSST[stp.byteStream, markHereIsFile];
TransferTheFile[stp: stp, from: stream, to: stp.byteStream];
PutCommand[stp, markYes, 0C, "Transfer Completed"L, sendEOC];
END;
TransferTheFile: PUBLIC PROCEDURE [stp: STPOps.Handle, from, to: Stream.Handle] =
BEGIN
block: Stream.Block ← [NIL, 0, BytesPerPage];
BEGIN
ENABLE UNWIND => IF block.blockPointer ~= NIL THEN z.FREE[@block.blockPointer];
bytesTransferred: CARDINAL;
why: Stream.CompletionCode;
savedSST: Stream.SubSequenceType;
block.blockPointer ← LOOPHOLE[z.NEW[PageOfStorage]];
DO
block.startIndex ← 0;
block.stopIndexPlusOne ← BytesPerPage;
[bytesTransferred, why, savedSST] ← from.get[
from, block, from.options !
Stream.EndOfStream =>
BEGIN why ← endOfStream; bytesTransferred ← nextIndex; CONTINUE; END;
Stream.SSTChange =>
BEGIN
why ← sstChange;
savedSST ← sst;
bytesTransferred ← nextIndex;
CONTINUE;
END];
block.stopIndexPlusOne ← bytesTransferred;
Stream.PutBlock[to, block, FALSE];
SELECT why FROM
normal => NULL;
sstChange => {stp.mark ← savedSST; stp.gotMark ← TRUE; EXIT};
endOfStream => EXIT;
ENDCASE => ERROR;
ENDLOOP;
END;
IF block.blockPointer ~= NIL THEN z.FREE[@block.blockPointer];
END;
SmashClosed: PUBLIC PROCEDURE [stp: STPOps.Handle] =
BEGIN
IF stp = NIL OR stp.byteStream = NIL THEN RETURN;
-- First smash connection so it will not give us any grief, THEN close it
PupStream.PupByteStreamAbort[stp.byteStream, "Unwinding..."L];
STP.Close[stp ! STP.Error => IF code = noConnection THEN CONTINUE];
END;
FindFileType: PUBLIC PROCEDURE [stream: Stream.Handle]
RETURNS [fileType: STP.Type] =
BEGIN
currentIndex: LONG CARDINAL;
currentIndex ← FileStream.GetIndex[stream -- !--
-- Stream.InvalidOperation => {fileType ← unknown; GOTO return}-- ];
FileStream.SetIndex[stream, 0];
fileType ← text;
DO ENABLE Stream.EndOfStream => GOTO streamEND;
IF (Inline.BITAND[Stream.GetByte[stream], 200B]) # 0 THEN
{fileType ← binary; EXIT};
REPEAT streamEND => NULL;
ENDLOOP;
FileStream.SetIndex[stream, currentIndex];
-- EXITS return => RETURN;
END;
END. -- of STPsB