FTPUser.mesa
Taft, October 17, 1983 4:11 pm
Bob Hagmann March 13, 1985 3:08:44 pm PST
Hal Murray, April 11, 1986 6:50:17 am PST
DIRECTORY
FTP,
FTPInternal,
IO,
Pup USING [Address],
PupName USING [Error, NameLookup],
PupStream USING [Create, waitForever],
PupWKS USING [ftp];
FTPUser: CEDAR PROGRAM
IMPORTS FTPInternal, IO, PupName, PupStream
EXPORTS FTP =
BEGIN OPEN FTP, FTPInternal;
FTP.
Object: PUBLIC TYPE = FTPInternal.Object;
CreateFromName: PUBLIC PROCEDURE [hostName: ROPE, localHerald: ROPENIL] RETURNS [h: Handle, remoteHerald: ROPE] =
BEGIN
hostAddress: Pup.Address = PupName.NameLookup[hostName, PupWKS.ftp !
PupName.Error => h.GenerateFailed[MapNameLookupError[code], text]];
[h, remoteHerald] ← CreateFromAddress[hostAddress, localHerald];
END;
CreateFromAddress: PUBLIC PROCEDURE [hostAddress: Pup.Address, localHerald: ROPENIL] RETURNS [h: Handle, remoteHerald: ROPE] =
BEGIN
mark: Mark;
code: FailureCode;
h ← NEW[Object ← []];
h.pList[local] ← NEW[PListObject];
h.byteStream ← PupStream.Create[
remote: hostAddress,
getTimeout: PupStream.waitForever,
putTimeout: PupStream.waitForever];
h.PutCommand[mark: version, code: LOOPHOLE[ftpVersion], text: localHerald, sendEOC: TRUE];
[mark, code] ← h.GetCommand[];
IF mark#version THEN h.GenerateFailed[protocolError];
IF code#LOOPHOLE[ftpVersion] THEN h.GenerateFailed[protocolError, "Incompatible protocol version"];
remoteHerald ← h.GetText[gobbleEOC: TRUE];
END;
Destroy: PUBLIC PROCEDURE [h: Handle] = {
h.byteStream.Close[];
};
Delete: PUBLIC PROCEDURE [h: Handle, confirm: ConfirmProc ← NIL] =
BEGIN
h.PutCommandAndPList[mark: delete, pList: h.pList[local], sendEOC: TRUE];
FOR firstTime: BOOLEANTRUE, FALSE DO
mark: Mark;
code: ReplyCode;
[mark, code] ← h.GetCommand[];
SELECT mark FROM
hereIsPList => {
h.pList[remote] ← h.GetPList[gobbleEOC: TRUE];
IF confirm#NIL AND confirm[h] THEN {
h.PutCommand[mark: yes, text: "Delete it!", sendEOC: TRUE];
[] ← h.GetYesNo[resumable: TRUE];
}
ELSE h.PutCommand[mark: no, code: skipThisFile, sendEOC: FALSE];
};
no => {
IF firstTime THEN h.GenerateFailed[code: code, text: h.GetText[gobbleEOC: TRUE], resumable: TRUE] ELSE h.GenerateFailed[protocolError];
};
endOfCommand =>
EXIT;
ENDCASE =>
h.GenerateFailed[protocolError];
ENDLOOP;
END;
Enumerate: PUBLIC PROCEDURE [h: Handle, noteFile: PROCEDURE [h: Handle]] =
BEGIN
NewEnumerate: PROCEDURE RETURNS [tryOld: BOOLEANFALSE] =
BEGIN
mark: Mark;
code: ReplyCode;
h.PutCommandAndPList[mark: newEnumerate, pList: h.pList[local], sendEOC: TRUE];
[mark, code] ← h.GetCommand[];
SELECT mark FROM
hereIsPList =>
DO -- process all property lists in (single) response to newEnumerate command
h.pList[remote] ← h.GetPList[endOfPropertiesOK: TRUE];
IF h.pList[remote]=NIL THEN EXIT;
noteFile[h];
ENDLOOP;
no =>
IF code=badCommand THEN RETURN [TRUE] -- newEnumerate unimplemented
ELSE h.GenerateFailed[code, h.GetText[gobbleEOC: TRUE]];
ENDCASE =>
h.GenerateFailed[protocolError];
END; -- NewEnumerate
OldEnumerate: PROCEDURE =
BEGIN
h.PutCommandAndPList[mark: enumerate, pList: h.pList[local], sendEOC: TRUE];
FOR firstTime: BOOLEANTRUE, FALSE DO
mark: Mark;
code: ReplyCode;
[mark, code] ← h.GetCommand[];
SELECT mark FROM
hereIsPList =>
BEGIN
h.pList[remote] ← h.GetPList[];
noteFile[h];
END;
no =>
IF firstTime THEN h.GenerateFailed[code, h.GetText[gobbleEOC: TRUE]] ELSE h.GenerateFailed[protocolError];
endOfCommand =>
IF firstTime THEN h.GenerateFailed[protocolError] ELSE EXIT;
ENDCASE =>
h.GenerateFailed[protocolError];
ENDLOOP;
END; -- OldEnumerate
IF NewEnumerate[] THEN OldEnumerate[];
END;
Rename: PUBLIC PROCEDURE [h: Handle, changeName: PROCEDURE [h: Handle]] =
BEGIN
oldPList: PList ← h.pList[local];
h.pList[local] ← NIL;
changeName[h];
h.PutCommandAndPList[mark: rename, pList: oldPList];
h.PutPList[pList: h.pList[local], sendEOC: TRUE];
[] ← h.GetYesNo[gobbleEOC: TRUE];
END;
Retrieve: PUBLIC PROCEDURE [h: Handle, confirm: ConfirmProc ← NIL, transfer: TransferProc, complete: CompleteProc ← NIL] =
BEGIN
h.PutCommandAndPList[mark: retrieve, pList: h.pList[local], sendEOC: TRUE];
FOR firstTime: BOOLEANTRUE, FALSE DO
mark: Mark;
code: ReplyCode;
[mark, code] ← h.GetCommand[];
SELECT mark FROM
hereIsPList =>
BEGIN
h.pList[remote] ← h.GetPList[gobbleEOC: TRUE];
IF confirm#NIL AND confirm[h] THEN
BEGIN
h.PutCommand[mark: yes, text: "Yes, please", sendEOC: TRUE];
[mark, code] ← h.GetCommand[];
SELECT mark FROM
hereIsFile =>
BEGIN
ok: BOOLEANFALSE;
transfer[h, h.byteStream];
ok ← h.GetYesNo[resumable: TRUE];
IF complete#NIL THEN complete[h, ok];
END;
no =>
h.GenerateFailed[code: code, text: h.GetText[gobbleEOC: TRUE], resumable: TRUE];
ENDCASE =>
h.GenerateFailed[protocolError];
END
ELSE h.PutCommand[mark: no, code: skipThisFile, sendEOC: FALSE];
END;
no =>
IF firstTime THEN h.GenerateFailed[code: code, text: h.GetText[gobbleEOC: TRUE], resumable: TRUE] ELSE h.GenerateFailed[protocolError];
endOfCommand =>
EXIT;
ENDCASE =>
h.GenerateFailed[protocolError];
ENDLOOP;
END;
Store: PUBLIC PROCEDURE [h: Handle, confirm: ConfirmProc ← NIL, transfer: TransferProc, complete: CompleteProc ← NIL] =
BEGIN
mark: Mark;
code: ReplyCode;
FOR protocol: {new, old} IN [new..old] DO
h.PutCommandAndPList[mark: IF protocol=new THEN newStore ELSE store, pList: h.pList[local], sendEOC: TRUE];
[mark, code] ← h.GetCommand[];
SELECT mark FROM
yes, hereIsPList =>
BEGIN
IF mark=hereIsPList THEN h.pList[remote] ← h.GetPList[gobbleEOC: TRUE];
IF confirm[h] THEN
BEGIN
ok: BOOLEANFALSE;
h.PutCommand[mark: hereIsFile];
transfer[h, h.byteStream];
h.PutCommand[mark: yes, text: "Sent ok", sendEOC: TRUE];
ok ← h.GetYesNo[resumable: TRUE];
IF complete#NIL THEN complete[h, ok];
EXIT;
END
ELSE h.PutCommand[mark: no, code: skipThisFile, sendEOC: FALSE];
END;
no =>
BEGIN
text: ROPE = h.GetText[gobbleEOC: TRUE];
IF protocol=new AND code=badCommand THEN NULL -- try again with old protocol
ELSE h.GenerateFailed[code, text];
END;
ENDCASE =>
h.GenerateFailed[protocolError];
ENDLOOP;
END;
END.
Bob Hagmann March 8, 1985 3:01:22 pm PST
changes to: Destroy, Delete