DIRECTORY Atom USING [GetPName, GetPropFromList, MakeAtom, MakeAtomFromRefText, PropList, PutPropOnList], Basics USING [BYTE], BasicTime USING [GMT, MonthOfYear, minutesPerHour, nullGMT, Unpack, Unpacked], Convert USING [Error, IntFromRope, TimeFromRope], FTP, FTPInternal, IO, PupName USING [Code], PupStream USING [CloseReason, ConsumeMark, SendMark], RefText USING [TrustTextAsRope], Rope; FTPCommon: CEDAR PROGRAM IMPORTS Atom, BasicTime, Convert, FTPInternal, IO, PupStream, RefText, Rope EXPORTS FTP, FTPInternal = BEGIN ROPE: TYPE = Rope.ROPE; FailureCode: TYPE = FTP.FailureCode; Property: TYPE = FTP.Property; DateProperty: TYPE = FTP.DateProperty; EnumeratedProperty: TYPE = FTP.EnumeratedProperty; NumberProperty: TYPE = FTP.NumberProperty; LocalOrRemote: TYPE = FTP.LocalOrRemote; Object: PUBLIC TYPE = FTPInternal.Object; Handle: TYPE = FTPInternal.Handle; PList: TYPE = FTPInternal.PList; Mark: TYPE = FTPInternal.Mark; Failed: PUBLIC SIGNAL [h: Handle, code: FailureCode, text: ROPE _ NIL, resumable: BOOLEAN _ FALSE] = CODE; IsOpen: PUBLIC PROC [h: Handle] RETURNS [open: BOOLEAN] = {RETURN[h.byteStream # NIL]}; GetClientData: PUBLIC PROC [h: Handle] RETURNS [data: REF ANY] = {RETURN[h.clientData]}; SetClientData: PUBLIC PROC [h: Handle, data: REF ANY] = {h.clientData _ data}; GetDateProperty: PUBLIC PROC [h: Handle, prop: DateProperty, list: LocalOrRemote _ remote] RETURNS [value: BasicTime.GMT] = {RETURN[h.pList[list].date[prop]]}; GetEnumeratedProperty: PUBLIC PROC [h: Handle, prop: EnumeratedProperty, list: LocalOrRemote _ remote] RETURNS [propValue: FTP.EnumPropValue] = {RETURN[h.pList[list].enumerated[prop]]}; GetNumberProperty: PUBLIC PROC [h: Handle, prop: NumberProperty, list: LocalOrRemote _ remote] RETURNS [value: INT] = {RETURN[h.pList[list].number[prop]]}; GetTextProperty: PUBLIC PROC [h: Handle, prop: FTP.TextProperty, list: LocalOrRemote _ remote] RETURNS [value: ROPE] = {RETURN[h.pList[list].text[prop]]}; GetUserDefinedProperty: PUBLIC PROC [h: Handle, prop: ATOM, list: LocalOrRemote _ remote] RETURNS [value: ROPE] = { refAny: REF ANY _ h.pList[list].userDefined.GetPropFromList[prop]; value _ NARROW[refAny]; }; SetDateProperty: PUBLIC PROC [h: Handle, prop: DateProperty, value: BasicTime.GMT] = {h.pList[local].date[prop] _ value}; SetEnumeratedProperty: PUBLIC PROC [h: Handle, prop: EnumeratedProperty, value: FTP.EnumPropValue] = { SELECT prop FROM eolConvention => { h.pList[local].enumerated[eolConvention].eolConvention _ LOOPHOLE[value]; }; type => { h.pList[local].enumerated[type].type _ LOOPHOLE[value]; }; ENDCASE; }; SetNumberProperty: PUBLIC PROC [h: Handle, prop: NumberProperty, value: INT] = {h.pList[local].number[prop] _ value}; SetTextProperty: PUBLIC PROC [h: Handle, prop: FTP.TextProperty, value: ROPE] = {h.pList[local].text[prop] _ value}; SetUserDefinedProperty: PUBLIC PROC [h: Handle, prop: ATOM, value: ROPE] = {[] _ h.pList[local].userDefined.PutPropOnList[prop: prop, val: value]}; ResetProperties: PUBLIC PROC [h: Handle] = {h.pList[local]^ _ []}; GetDesiredProperties: PUBLIC PROC [h: Handle] RETURNS [props: FTP.PropertySet, userDefinedProps: LIST OF ATOM] = {RETURN [h.pList[remote].desiredProps, h.pList[remote].desiredUserDefinedProps]}; SetDesiredProperties: PUBLIC PROC [h: Handle, props: FTP.PropertySet, userDefinedProps: LIST OF ATOM _ NIL] = {h.pList[local].desiredProps _ props; h.pList[local].desiredUserDefinedProps _ userDefinedProps}; FileType: PUBLIC PROC [stream: IO.STREAM] RETURNS [type: FTP.Type _ text] = { char: CHAR; char _ stream.GetChar[! IO.EndOfStream => GOTO unknownReturn]; DO IF LOOPHOLE[char, Basics.BYTE] >= 200B THEN RETURN[binary]; char _ stream.GetChar[! IO.EndOfStream => GOTO textReturn]; ENDLOOP; EXITS unknownReturn => RETURN [unknown]; textReturn => RETURN [text]; }; DataType: PUBLIC PROC [block: IO.UnsafeBlock]RETURNS [type: FTP.Type _ text] = TRUSTED { count: INT _ block.count; IF count <= 0 THEN RETURN[unknown]; FOR index: INT _ block.startIndex, index+1 UNTIL count <= 0 DO byte: Basics.BYTE = block.base[index]; IF byte >= 200B THEN RETURN[binary]; count _ count - 1; ENDLOOP; }; GetCommand: PUBLIC PROC [h: Handle] RETURNS [mark: Mark, code: FTP.ReplyCode] = BEGIN DO FlushDataUntilMark[h]; mark _ LOOPHOLE[PupStream.ConsumeMark[h.byteStream]]; IF mark#comment THEN EXIT ENDLOOP; code _ IF HasReplyCode[mark] THEN LOOPHOLE[h.byteStream.GetChar[! IO.EndOfStream => GOTO error]] ELSE unspecified; EXITS error => h.GenerateFailed[protocolError]; END; GetText: PUBLIC PROC [h: Handle, gobbleEOC: BOOLEAN _ FALSE] RETURNS [text: ROPE] = BEGIN buffer: REF TEXT = h.GetBuffer[]; text _ NIL; DO buffer.length _ h.byteStream.GetBlock[buffer, 0, buffer.maxLength]; text _ text.Cat[Rope.FromRefText[buffer]]; IF h.byteStream.EndOf[] THEN EXIT; ENDLOOP; h.ReleaseBuffer[buffer]; IF gobbleEOC THEN BEGIN mark: Mark _ GetCommand[h].mark; IF mark#endOfCommand THEN h.GenerateFailed[protocolError]; END; END; GetPList: PUBLIC PROC [h: Handle, gobbleEOC: BOOLEAN _ FALSE, endOfPropertiesOK: BOOL _ FALSE] RETURNS [pList: PList] = BEGIN Append: PROC [c: CHAR] = BEGIN IF buffer.length = buffer.maxLength THEN h.GenerateFailed[badPList, "Property name or value too long"]; buffer[buffer.length] _ c; buffer.length _ buffer.length+1; END; buffer: REF TEXT = h.GetBuffer[]; char: CHARACTER; pList _ NEW [FTPInternal.PListObject _ []]; char _ h.byteStream.GetChar[ ! IO.EndOfStream => IF endOfPropertiesOK THEN GOTO empty ELSE GOTO badPList]; IF char#'( THEN GOTO badPList; DO ENABLE IO.EndOfStream => GOTO badPList; propName: ATOM; property: Property; found: BOOLEAN; char _ h.byteStream.GetChar[]; SELECT char FROM '( => NULL; ') => EXIT; ENDCASE => GOTO badPList; buffer.length _ 0; DO -- accumulate property name char _ h.byteStream.GetChar[]; SELECT char FROM '' => Append[h.byteStream.GetChar[]]; '(, ') => GOTO badPList; ' => EXIT; ENDCASE => Append[char]; ENDLOOP; propName _ MakeCanonicalAtom[buffer]; [found, property] _ PropertyLookup[propertyNames, propName]; buffer.length _ 0; DO -- accumulate property value char _ h.byteStream.GetChar[]; SELECT char FROM '' => Append[h.byteStream.GetChar[]]; '( => GOTO badPList; ') => EXIT; ENDCASE => Append[char]; ENDLOOP; IF found THEN BEGIN ENABLE Convert.Error => GOTO badProp; SELECT property FROM IN DateProperty => pList.date[property] _ Convert.TimeFromRope[RefText.TrustTextAsRope[buffer] ]; eolConvention => { eolConvention: FTP.EOLConvention; propName _ MakeCanonicalAtom[buffer]; [found, eolConvention] _ EOLConventionLookup[eolConventionNames, propName]; IF found THEN pList.enumerated[eolConvention].eolConvention _ eolConvention ELSE GOTO badProp; }; type => { myTypeIndex: FTP.Type; propName _ MakeCanonicalAtom[buffer]; [found, myTypeIndex] _ TypeLookup[typeNames, propName]; IF found THEN pList.enumerated[type].type _ myTypeIndex ELSE GOTO badProp; }; IN NumberProperty => pList.number[property] _ Convert.IntFromRope[RefText.TrustTextAsRope[buffer]]; IN FTP.TextProperty => pList.text[property] _ Rope.FromRefText[buffer]; desiredProperty => BEGIN propName _ MakeCanonicalAtom[buffer]; [found, property] _ PropertyLookup[propertyNames, propName]; IF found THEN pList.desiredProps[property] _ TRUE ELSE pList.desiredUserDefinedProps _ CONS[propName, pList.desiredUserDefinedProps]; END; ENDCASE; EXITS badProp => h.GenerateFailed[propertyError[property]]; END ELSE pList.userDefined _ Atom.PutPropOnList[propList: pList.userDefined, prop: propName, val: Rope.FromRefText[buffer]]; ENDLOOP; h.ReleaseBuffer[buffer]; IF gobbleEOC THEN BEGIN mark: Mark _ GetCommand[h].mark; IF mark#endOfCommand THEN h.GenerateFailed[protocolError]; END; EXITS empty => RETURN [NIL]; badPList => h.GenerateFailed[badPList]; END; GetYesNo: PUBLIC PROC [h: Handle, gobbleEOC: BOOLEAN _ FALSE, resumable: BOOLEAN _ FALSE] RETURNS [ok: BOOLEAN] = BEGIN mark: Mark; code: FTP.ReplyCode; [mark, code] _ h.GetCommand[]; SELECT mark FROM yes => [] _ h.GetText[gobbleEOC]; no => h.GenerateFailed[code, h.GetText[gobbleEOC: gobbleEOC], resumable]; ENDCASE => h.GenerateFailed[protocolError]; RETURN [mark=yes]; END; GetEOC: PUBLIC PROC [h: Handle] = BEGIN mark: Mark _ h.GetCommand[].mark; IF mark#endOfCommand THEN GenerateProtocolError[h, eocExpected, mark]; END; PutCommand: PUBLIC PROC [h: Handle, mark: Mark, code: FTP.ReplyCode _ unspecified, text: ROPE _ NIL, sendEOC: BOOLEAN _ TRUE] = BEGIN PupStream.SendMark[h.byteStream, LOOPHOLE[mark]]; IF HasReplyCode[mark] THEN h.byteStream.PutChar[LOOPHOLE[code]]; IF text.Length[]#0 THEN h.byteStream.PutRope[text]; IF sendEOC THEN PupStream.SendMark[h.byteStream, LOOPHOLE[Mark.endOfCommand]]; END; PutEOC: PUBLIC PROC [h: Handle] = { PupStream.SendMark[h.byteStream, LOOPHOLE[Mark.endOfCommand]]; }; PutPList: PUBLIC PROC [h: Handle, pList: PList, sendEOC: BOOLEAN _ TRUE] = BEGIN PutDateProp: PROC [date: BasicTime.GMT] = BEGIN unpack: BasicTime.Unpacked = BasicTime.Unpack[date]; dst: BOOL = unpack.dst = yes; absZone: INT _ ABS[IF dst THEN unpack.zone-60 ELSE unpack.zone]; zoneRope: ROPE _ NIL; h.byteStream.PutF["%2d-%g-%02d %2d", [integer[unpack.day]], [rope[monthName[unpack.month]]], [integer[unpack.year MOD 100]], [integer[unpack.hour]] ]; SELECT unpack.zone FROM 0 => IF ~dst THEN zoneRope _ "GMT"; NAT[5*BasicTime.minutesPerHour] => zoneRope _ IF dst THEN "EDT" ELSE "EST"; NAT[6*BasicTime.minutesPerHour] => zoneRope _ IF dst THEN "CDT" ELSE "CST"; NAT[7*BasicTime.minutesPerHour] => zoneRope _ IF dst THEN "MDT" ELSE "MST"; NAT[8*BasicTime.minutesPerHour] => zoneRope _ IF dst THEN "PDT" ELSE "PST"; ENDCASE; IF zoneRope = NIL THEN zoneRope _ IO.PutFR["%g%02d:%02d", IO.char[IF unpack.zone < 0 THEN '- ELSE '+], IO.card[absZone/BasicTime.minutesPerHour], IO.card[absZone MOD BasicTime.minutesPerHour] ]; h.byteStream.PutF[":%02d:%02d %g", [integer[unpack.minute]], [integer[unpack.second]], [rope[zoneRope]] ]; END; PutRopeProp: PROC [rope: ROPE] = BEGIN FOR i: INT IN [0..rope.Length[]) DO char: CHARACTER _ rope.Fetch[i]; SELECT char FROM '(, '), '' => h.byteStream.PutChar['']; ENDCASE; h.byteStream.PutChar[char]; ENDLOOP; END; h.byteStream.PutChar['(]; FOR property: Property IN Property DO nonNilProp: BOOLEAN _ SELECT property FROM IN DateProperty => pList.date[property]#BasicTime.nullGMT, IN EnumeratedProperty => pList.enumerated[property] # FTP.nullEnumPropValue, IN NumberProperty => pList.number[property] # 0, IN FTP.TextProperty => pList.text[property] # NIL, desiredProperty => FALSE, ENDCASE => ERROR; IF nonNilProp THEN BEGIN h.byteStream.PutChar['(]; h.byteStream.PutRope[Atom.GetPName[propertyNames[property.ORD]]]; h.byteStream.PutChar[' ]; SELECT property FROM IN DateProperty => PutDateProp[pList.date[property]]; eolConvention => PutRopeProp[Atom.GetPName[eolConventionNames[ pList.enumerated[property].eolConvention.ORD]]]; type => PutRopeProp[Atom.GetPName[typeNames[pList.enumerated[property].type.ORD]]]; IN NumberProperty => h.byteStream.Put[IO.int[pList.number[property]]]; IN FTP.TextProperty => PutRopeProp[pList.text[property]]; ENDCASE => ERROR; h.byteStream.PutChar[')]; END; ENDLOOP; FOR prop: Atom.PropList _ pList.userDefined, prop.rest UNTIL prop=NIL DO key: ATOM = NARROW[prop.first.key]; val: ROPE = NARROW[prop.first.val]; h.byteStream.PutChar['(]; PutRopeProp[Atom.GetPName[key]]; h.byteStream.PutChar[' ]; PutRopeProp[val]; h.byteStream.PutChar[')]; ENDLOOP; IF (pList.desiredProps#ALL[FALSE] AND pList.desiredProps#ALL[TRUE]) OR pList.desiredUserDefinedProps#NIL THEN { FOR property: Property IN FTP.BuiltInProperty DO IF pList.desiredProps[property] THEN { h.byteStream.PutRope["(Desired-property "]; h.byteStream.PutRope[Atom.GetPName[propertyNames[property.ORD]]]; h.byteStream.PutChar[')]; }; ENDLOOP; FOR propItem: LIST OF ATOM _ pList.desiredUserDefinedProps, propItem.rest UNTIL propItem=NIL DO h.byteStream.PutRope["(Desired-property "]; PutRopeProp[Atom.GetPName[propItem.first]]; h.byteStream.PutChar[')]; ENDLOOP; }; h.byteStream.PutChar[')]; IF sendEOC THEN PupStream.SendMark[h.byteStream, LOOPHOLE[Mark.endOfCommand]]; END; PutCommandAndPList: PUBLIC PROC [h: Handle, mark: Mark, pList: PList, sendEOC: BOOLEAN _ FALSE] = { h.PutCommand[mark: mark, sendEOC: FALSE]; h.PutPList[pList: pList, sendEOC: sendEOC]; }; NameToPList: PUBLIC PROC [plist: PList, name: Rope.ROPE, type: FTPInternal.FilenameType] = BEGIN pos: INT _ 0; length: INT = name.Length[]; IF pos >= length THEN RETURN; IF name.Fetch[pos] = '[ THEN BEGIN end: INT = name.Find["]"]; IF end > pos THEN { plist.text[device] _ name.Substr[pos+1, end-pos-1]; pos _ end+1 }; IF pos >= length THEN RETURN; END; IF name.Fetch[pos] = '< THEN BEGIN end: INT = name.Find[">"]; IF end > pos THEN { plist.text[directory] _ name.Substr[pos+1, end-pos-1]; pos _ end+1 }; IF pos >= length THEN RETURN; END; BEGIN end: INT _ name.Find["!"]; IF end >= 0 THEN plist.text[version] _ name.Substr[end+1, length-end-1] ELSE end _ length; plist.text[nameBody] _ name.Substr[pos, end-pos]; END; END; PListToName: PUBLIC PROC [plist: PList, type: FTPInternal.FilenameType] RETURNS [name: Rope.ROPE _ NIL] = BEGIN dir: Rope.ROPE = plist.text[directory]; dirLength: INT = dir.Length[]; IF dirLength # 0 THEN BEGIN SELECT dir.Fetch[0] FROM '> => name _ name.Cat[dir.Substr[1, dirLength-1]]; '< => name _ name.Cat[dir]; ENDCASE => name _ name.Cat["<", dir]; IF dir.Fetch[dirLength - 1] # '> THEN name _ name.Cat[">"]; END; IF plist.text[nameBody].Length[] # 0 THEN name _ name.Cat[plist.text[nameBody]]; IF plist.text[version].Length[] # 0 THEN name _ name.Cat[IF type = alto THEN "!" ELSE ";", plist.text[version]]; END; GetBuffer: PUBLIC PROC [h: Handle] RETURNS [buffer: REF TEXT] = { buffer _ NEW[TEXT[100]]; }; ReleaseBuffer: PUBLIC PROC [h: Handle, buffer: REF TEXT] = { }; HasReplyCode: PROC [mark: Mark] RETURNS [BOOLEAN] = BEGIN RETURN [SELECT mark FROM yes, no, version, mailboxException => TRUE, ENDCASE => FALSE]; END; MakeCanonicalAtom: PROC [text: REF TEXT] RETURNS [atom: ATOM] = BEGIN FOR i: CARDINAL IN [0..text.length) DO IF text[i] IN ['A..'Z] THEN text[i] _ text[i]+('a-'A); ENDLOOP; RETURN [Atom.MakeAtomFromRefText[text]]; END; AtomLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, ordinal: CARDINAL] = BEGIN found _ TRUE; FOR ordinal IN [0..table.length) DO IF table[ordinal]=name THEN RETURN; ENDLOOP; found _ FALSE; END; PropertyLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, property: Property] = LOOPHOLE [AtomLookup]; EOLConventionLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, eolConvention: FTP.EOLConvention] = LOOPHOLE [AtomLookup]; TypeLookup: PROC [table: REF NameTable, name: ATOM] RETURNS [found: BOOLEAN, type: FTP.Type] = LOOPHOLE [AtomLookup]; FlushDataUntilMark: PROC [h: Handle] = { buffer: REF TEXT = h.GetBuffer[]; DO buffer.length _ h.byteStream.GetBlock[buffer, 0, buffer.maxLength]; IF h.byteStream.EndOf[] THEN EXIT; ENDLOOP; h.ReleaseBuffer[buffer]; }; CollectCode: PROC [h: Handle] RETURNS [char: CHARACTER _ ' ] = { char _ h.byteStream.GetChar[]; -- may raise IO.EndOfStream }; CollectString: PROC [h: Handle] RETURNS [string: ROPE _ NIL] = { buffer: REF TEXT = h.GetBuffer[]; DO buffer.length _ h.byteStream.GetBlock[buffer, 0, buffer.maxLength]; string _ string.Cat[Rope.FromRefText[buffer]]; IF h.byteStream.EndOf[] THEN EXIT; ENDLOOP; h.ReleaseBuffer[buffer]; }; GenerateFailed: PUBLIC PROC [h: Handle, code: FailureCode, text: Rope.ROPE _ NIL, resumable: BOOLEAN _ FALSE] = BEGIN IF text=NIL THEN text _ GenerateErrorText[code]; IF resumable THEN SIGNAL Failed[h, code, text, TRUE] ELSE ERROR Failed[h, code, text, FALSE]; END; GenerateNo: PUBLIC PROC [h: Handle, code: FailureCode, text: Rope.ROPE _ NIL, sendEOC: BOOLEAN _ FALSE] = BEGIN IF text=NIL THEN text _ GenerateErrorText[code]; h.PutCommand[mark: no, code: code, text: text, sendEOC: sendEOC]; END; GenerateNoAndFailed: PUBLIC PROC [h: Handle, code: FailureCode, text: Rope.ROPE _ NIL, resumable: BOOLEAN _ FALSE] = BEGIN h.GenerateNo[code, text]; h.GenerateFailed[code, text, resumable]; END; GenerateErrorText: PROC [errorCode: FailureCode] RETURNS [text: Rope.ROPE]= { text _ SELECT errorCode FROM noSuchHost => "No such host!", noRouteToNetwork => "No route to network!", noNameLookupResponse => "Name lookup server is not responding", alreadyAConnection => "You already have a connection!", noConnection => "Please open a connection!", connectionClosed => "Connection closed (local or remote)!", connectionRejected => "Connection rejected by remote host!", connectionTimedOut => "Connection timed out!", accessDenied => "Access denied by remote server!", illegalUserName => "Invalid or illegal UserName!", illegalUserPassword => "Invalid or illegal UserPassword!", illegalUserAccount => "Invalid or illegal UserAccount!", illegalConnectName => "Invalid or illegal ConnectName!", illegalConnectPassword => "Invalid or illegal ConnectPassword!", credentailsMissing => "Name and/or Password not supplied!", protocolError => "Internal FTP protocol error!", illegalFileName => "Illegal filename!", noSuchFile => "File not found!", requestRefused => "Request refused by remote host!", accessError => "Illegal access attempt on remote stream!", undefinedError => "Undefined error!", ENDCASE => ERROR; }; GenerateStreamClosingError: PUBLIC PROC [h: Handle, why: PupStream.CloseReason] = BEGIN GenerateFailed[ h, SELECT why FROM localClose, remoteClose => connectionClosed, noRouteToNetwork => noRouteToNetwork, transmissionTimeout => connectionTimedOut, remoteReject => connectionRejected, ENDCASE => ERROR, NIL]; END; GenerateProtocolError: PUBLIC PROC [h: Handle, type: FTP.ProtocolError, mark: Mark, code: CHARACTER _ 0C, resumable: BOOLEAN _ FALSE] = BEGIN text: Rope.ROPE = IO.PutFR["%g, mark = %b, code = %b", [rope[SELECT type FROM badVersion => "Incompatible protocol version", badMark => "Invalid or undefined mark byte", badPList => "Invalid or malformed property list", eocExpected => "End-Of-Command mark byte expected", noCode => "error code is required after error mark byte", ENDCASE => ERROR]], [integer[mark.ORD]], [integer[code.ORD-0C.ORD]] ]; ERROR Failed[h, protocolError, text, resumable]; END; SelectError: PROC [h: Handle, s: Rope.ROPE , mark: Mark] = BEGIN code: CHARACTER _ 0C; remoteString: ROPE _ NIL; IF mark = no OR mark = comment THEN BEGIN IF mark # comment THEN code _ CollectCode[h]; remoteString _ CollectString[h]; GenerateFailed[h, requestRefused, IF remoteString.Length[] = 0 THEN s ELSE remoteString]; END ELSE GenerateProtocolError[h, badMark, mark, code]; END; MapNameLookupError: PUBLIC PROC [error: PupName.Code] RETURNS [FailureCode] = { RETURN [SELECT error FROM noRoute => noRouteToNetwork, noResponse => noNameLookupResponse, errorFromServer => noSuchHost, ENDCASE => undefinedError ]; }; MapStreamClosingError: PUBLIC PROC [error: PupStream.CloseReason] RETURNS [FailureCode] = { RETURN [SELECT error FROM localClose => connectionClosed, localAbort => aborted, remoteClose => connectionClosed, noRouteToNetwork => noRouteToNetwork, transmissionTimeout => connectionTimedOut, remoteReject => connectionRejected, ENDCASE => undefinedError ]; }; NameTable: TYPE = RECORD [ SEQUENCE length: CARDINAL OF ATOM]; propertyNames, typeNames, eolConventionNames: REF NameTable; nProperties: CARDINAL = Property.LAST.ORD+1; nTypes: CARDINAL = FTP.Type.LAST.ORD+1; nEOLConventions: CARDINAL = FTP.EOLConvention.LAST.ORD+1; monthName: ARRAY BasicTime.MonthOfYear OF Rope.ROPE = [January: "Jan", February: "Feb", March: "Mar", April: "Apr", May: "May", June: "Jun", July: "Jul", August: "Aug", September: "Sep", October: "Oct", November: "Nov", December: "Dec"]; propertyError: ARRAY Property OF FailureCode = [createDate: illegalCreationDate, readDate: illegalReadDate, writeDate: illegalWriteDate, eolConvention: illegalEOLConversion, type: illegalType, byteSize: illegalByteSize, checksum: badPList, size: badPList, author: illegalAuthor, connectName: illegalConnectName, connectPassword: illegalConnectPassword, device: illegalDevice, directory: illegalDirectory, nameBody: illegalNameBody, serverFileName: illegalServerFileName, userAccount: illegalUserAccount, userName: illegalUserName, userPassword: illegalUserPassword, version: illegalVersion, desiredProperty: badPList]; Init: PROC = BEGIN OPEN Atom; propertyNames _ NEW [NameTable[nProperties]]; propertyNames[Property.author.ORD] _ $author; propertyNames[Property.byteSize.ORD] _ MakeAtom["byte-size"]; propertyNames[Property.checksum.ORD] _ $checksum; propertyNames[Property.connectName.ORD] _ MakeAtom["connect-name"]; propertyNames[Property.connectPassword.ORD] _ MakeAtom["connect-password"]; propertyNames[Property.createDate.ORD] _ MakeAtom["creation-date"]; propertyNames[Property.desiredProperty.ORD] _ MakeAtom["desired-property"]; propertyNames[Property.device.ORD] _ $device; propertyNames[Property.directory.ORD] _ $directory; propertyNames[Property.eolConvention.ORD] _ MakeAtom["end-of-line-convention"]; propertyNames[Property.nameBody.ORD] _ MakeAtom["name-body"]; propertyNames[Property.readDate.ORD] _ MakeAtom["read-date"]; propertyNames[Property.serverFileName.ORD] _ MakeAtom["server-filename"]; propertyNames[Property.size.ORD] _ $size; propertyNames[Property.type.ORD] _ $type; propertyNames[Property.userAccount.ORD] _ MakeAtom["user-account"]; propertyNames[Property.userName.ORD] _ MakeAtom["user-name"]; propertyNames[Property.userPassword.ORD] _ MakeAtom["user-password"]; propertyNames[Property.version.ORD] _ $version; propertyNames[Property.writeDate.ORD] _ MakeAtom["write-date"]; typeNames _ NEW [NameTable[nTypes]]; typeNames[FTP.Type.binary.ORD] _ $binary; typeNames[FTP.Type.text.ORD] _ $text; typeNames[FTP.Type.unknown.ORD] _ $unknown; eolConventionNames _ NEW [NameTable[nEOLConventions]]; eolConventionNames[FTP.EOLConvention.cr.ORD] _ $cr; eolConventionNames[FTP.EOLConvention.crlf.ORD] _ $crlf; eolConventionNames[FTP.EOLConvention.transparent.ORD] _ $transparent; eolConventionNames[FTP.EOLConvention.unknown.ORD] _ $unknown; END; Init[]; END. :FTPCommon.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Common protocol and property lists Last edited by: Taft, October 7, 1983 2:23 pm Bob Hagmann July 11, 1985 9:25:08 am PDT Carl Hauser, May 1, 1986 1:26:17 pm PDT Hal Murray, April 10, 1986 1:22:44 pm PST FTP. This is supposed to be fast. It isn't. I don't know what trick was supposed to be used, except for GetBlock instead of GetChar. If you know, please fix -- rbh This is supposed to be fast. It isn't. I don't know what trick was supposed to be used. If you know, please fix -- rbh FTPInternal. Desired syntax is: "dd-mmm-yy hh:mm:ss +hh:mm" or "dd-mmm-yy hh:mm:ss zoneAbbrev" For now use REFs for buffers, but put a cache in here later Private Returns TRUE if the mark is among those that carry a ReplyCode in the immediately following data byte. Returns an ATOM for the canonical form of the specified text (property name or other keyword). May modify the text. Error handling Global data and initialization Bob Hagmann February 11, 1985 2:05:39 pm PST changes to: DIRECTORY Κ– "cedar" style˜šœ™Icodešœ Οmœ1™<—Jšœ"™"™J™K™(K™'K™)—unitšΟk ˜ KšœžœU˜_Kšœžœžœ˜Kšœ žœžœ:˜NKšœžœ$˜1Kšžœ˜K˜ Kšžœ˜Kšœžœ˜Kšœ žœ&˜5Kšœžœ˜ Kšœ˜—šœ žœž˜Kšžœ(žœ˜KKšžœžœž˜ —™Jšžœžœžœ˜J˜Jšœ žœžœ ˜$Jšœ žœžœ ˜Jšœžœžœ˜&Jšœžœžœ˜2Jšœžœžœ˜*Jšœžœžœ˜(J˜Jšœžœžœ˜)Jšœžœ˜"Jšœžœ˜ Jšœžœ˜L˜—šœ™L˜L˜LšΟbœžœžœ&žœžœ žœžœžœ˜jš Οnœžœžœ žœžœ˜9Kšœžœžœ˜—š   œžœžœ žœžœžœ˜@Kšœžœ˜—š   œžœžœžœžœ˜7Kšœ˜—š  œžœžœ?žœžœ˜{Kšœžœ˜#—š  œžœžœEžœ žœ˜Kšœžœ"˜)—š  œžœžœAžœ žœ˜uKšœžœ˜%—š  œžœžœžœ-žœ žœ˜vKšœžœ˜#—š  œžœžœžœ žœ žœ˜sKšœžœžœ3˜BKšœžœ ˜Kšœ˜—š œžœžœ2žœ˜TKšœ$˜$—š œžœžœ.žœ˜fšžœž˜šœ˜Kšœ9žœ˜IKšœ˜—šœ ˜ Kšœ'žœ˜7Kšœ˜—Kšžœ˜—Kšœ˜—š œžœžœ*žœ˜NKšœ&˜&—š  œžœžœžœžœ˜OKšœ$˜$—š  œžœžœžœ žœ˜JKšœH˜H—š œžœžœ˜*K˜—š œžœžœ žœ žœ žœžœžœ˜pKšœžœJ˜Q—š œžœžœžœ žœžœžœžœ˜mKšœa˜a—š œžœžœ žœžœžœžœ˜MKšœ‘™‘Kšœžœ˜ Kšœžœžœ˜>šž˜Kš žœžœžœ žœžœ ˜;Kšœžœžœ ˜;Kšžœ˜—šž˜Kšœžœ ˜"Kšœžœ˜—Kšœ˜—š œžœžœ žœ žœžœžœ˜XK™yKšœžœ˜Kšžœ žœžœ ˜#šžœžœžœ ž˜>Kšœ žœ˜&Kšžœžœžœ ˜$Kšœ˜Kšžœ˜—Kšœ˜—K˜—J™J™šœ™š   œžœžœ žœžœ ˜OKšž˜šž˜K˜Kšœžœ&˜5Kšžœžœž˜Kšžœ˜—Kš œžœžœžœžœžœ žœ ˜ršž˜Kšœ)˜)—Kšžœ˜—š œžœžœžœžœžœžœ˜SKšž˜Kšœžœžœ˜!Kšœžœ˜ šž˜KšœC˜CK˜*Kšžœžœžœ˜"Kšžœ˜—K˜šžœ ž˜Kšž˜K˜ Kšžœžœ!˜:Kšžœ˜—Kšžœ˜—š œžœžœžœžœžœžœžœ˜wKšž˜š œžœžœ˜Kšž˜Kšžœ"žœ?˜gK˜K˜ Kšžœ˜—Kšœžœžœ˜!Kšœž œ˜Kšœžœ ˜+šœ˜Kš žœžœžœžœžœžœ ˜K—Kšžœ žœžœ ˜šž˜Kšžœžœžœ ˜'Kšœ žœ˜K˜Kšœžœ˜Kšœ˜šžœž˜Kšœžœ˜ Kšœžœ˜ Kšžœžœ ˜—K˜šžœΟc˜Kšœ˜šžœž˜Kšœ%˜%Kšœ žœ ˜Kšœžœ˜ Kšžœ˜—Kšžœ˜—K˜%Kšœ<˜˜KKšžœžœ ˜Kšœ˜—šœ ˜ Kšœ žœ˜K˜%Kšœ7˜7Kšžœžœ*˜7Kšžœžœ ˜Kšœ˜—šžœ˜KšœN˜N—šžœžœ˜Kšœ0˜0—šœ˜Kšž˜K˜%Kšœ<˜Kšœ˜—š  œžœžœ$žœžœ˜JKšž˜š  œžœžœ˜)Kšž˜Kšœ4˜4Kšœžœ˜Kš œ žœžœžœžœžœ˜@Kšœ žœžœ˜JšœQ™QKšœržœ!˜–šžœ ž˜Jšœžœžœ˜#Jšžœ+žœžœžœ˜KJšžœ+žœžœžœ˜KJšžœ+žœžœžœ˜KJšžœ+žœžœžœ˜KJšžœ˜—šžœ žœžœ žœ˜9Jšžœžœžœžœ˜,Jšžœ(˜*Jšžœžœ˜-J˜—K˜Kšœj˜jKšžœ˜—š  œžœžœ˜ Kšž˜šžœžœžœž˜#Kšœž œ˜ šžœž˜K˜'Kšžœ˜—K˜Kšžœ˜—Kšžœ˜—K˜šžœžœ ž˜%šœ žœžœ ž˜*Kšžœ8˜:Kšžœ4žœ˜LKšžœ.˜0Kšžœžœ(žœ˜2Kšœžœ˜Kšžœžœ˜—šžœ ž˜Kšž˜K˜Kšœ:žœ˜AK˜šžœ ž˜Kšžœ3˜5Kšœhžœ˜oKšœLžœ˜SKšžœ$žœ˜FKšžœžœ3˜9Kšžœžœ˜—Kšœ˜Kšžœ˜—Kšžœ˜—šžœ4žœžœž˜HKšœžœžœ˜#Kšœžœžœ˜#K˜Kšœ ˜ K˜K˜K˜Kšžœ˜—šžœžœžœžœžœžœžœžœžœ˜ošžœžœžœž˜0šžœžœ˜&Kšœ+˜+Kšœ:žœ˜AK˜Kšœ˜—Kšžœ˜—š žœ žœžœžœ0žœ žœž˜_Kšœ+˜+Kšœ+˜+K˜Kšžœ˜—Kšœ˜—K˜Kšžœ žœ"žœ˜NKšžœ˜—š  œžœžœ0žœžœ˜cKšœ"žœ˜)Kšœ+˜+Kšœ˜—š  œžœžœžœ#˜ZKšž˜Kšœžœ˜ Kšœžœ˜Kšžœžœžœ˜Kšžœ˜šžœž˜ Kšœžœ˜Kšžœ žœE˜VKšžœžœžœ˜Kšžœ˜—Kšžœ˜šžœž˜ Kšœžœ˜Kšžœ žœH˜YKšžœžœžœ˜Kšžœ˜—šž˜Kšœžœ˜Kšžœ žœ8žœ˜ZKšœ1˜1—Kšžœ˜Kšžœ˜—š   œžœžœ0žœ žœžœ˜iKšž˜Kšœ žœ˜'Kšœ žœ˜šžœž˜Kšž˜šžœž˜Kšœ2˜2Kšœ˜Kšžœ˜%—Kšžœžœ˜;Kšžœ˜—Kšžœ#žœ'˜Pšžœ"ž˜(Kšœžœ žœžœ˜G—Kšžœ˜L™;—š   œžœžœ žœ žœžœ˜AJšœ žœžœ˜J˜—K˜š   œžœžœžœžœ˜