<> <> <> <> 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; <> Object: PUBLIC TYPE = FTPInternal.Object; CreateFromName: PUBLIC PROCEDURE [hostName: ROPE, localHerald: ROPE _ NIL] 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: ROPE _ NIL] 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: BOOLEAN _ TRUE, 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: BOOLEAN _ FALSE] = 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: BOOLEAN _ TRUE, 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: BOOLEAN _ TRUE, 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: BOOLEAN _ FALSE; 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: BOOLEAN _ FALSE; 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. <> <> <<>>