Heading:
Mesa FTP Functional Specification
Page Numbers: Yes X: 527 Y: 10.5" First Page: 40
Appendix E: Client File Primitives
E.1. Description of the Option
An FTP User or Server manipulates its local file system by means of a family of procedures called file primitives. This family includes, for example, procedures for enumerating the members of a local file group, for reading and writing the contents of local files, and for deleting and renaming local files. The FTP implementation includes an implementation manipulating the standard Alto file system.
Rather than the file system interface offered by FTP, the client may, if it wishes, provide its own file primitives to a particular FTP User or Listener. By so doing, a client can use FTP, for example to:
1. interface to another local file system (for example, Juniper).
2. interface to a pseudo file system (for example, a printer).
3. produce or consume files on the fly (that is, files that never exist on secondary storage).
4. transform filenames (for example, convert abstract filenames to concrete ones).
5. control access to particular functions on a per-user or per-host basis.
6. maintain a log of file system activity.
The client can also implement certain file primitives while relying on FTP for others, or use the FTP implementations as building blocks for its own implementations. For example, a client could log file access attempts by supplying an implementation of the OpenFile procedure (described in Section E.8) that records the event and then calls FTP’s OpenFile procedure to actually open the file.
E.2. Exercising the Option
The client exercises the option described above by means of the filePrimitives parameter required by both the FTPCreateUser and FTPCreateListener procedures. This parameter is a POINTER to a RECORD containing the PROCEDUREs by which the newly created FTP User, or any FTP Servers created by the newly created FTP Listener, are to access the local file system. The client may supply its own version of this record, rather than rely upon the standard version offered by FTP. In constructing the record and/or implementing the procedures it contains, the client may draw upon any of the file primitives it finds in the FTP-provided record. Since FTP will not copy the record presented to it, the client must preserve it intact until FTPDestroyUser or FTPDestroyListener is called:
FilePrimitives: TYPE = POINTER TO FilePrimitivesObject;
FilePrimitivesObject: TYPE = RECORD [
-- program management primitives
CreateFileSystem: PROCEDURE [bufferSize: CARDINAL] RETURNS [fileSystem: FileSystem],
DestroyFileSystem: PROCEDURE [fileSystem: FileSystem],
-- filename manipulation primitives
DecomposeFilename,
ComposeFilename: PROCEDURE [fileSystem: FileSystem, absoluteFilename: STRING, virtualFilename: VirtualFilename],
-- access control primitives
InspectCredentials: PROCEDURE [fileSystem: FileSystem, status: Status, user, password: STRING],
-- file enumeration primitives
EnumerateFiles: PROCEDURE [fileSystem: FileSystem, files: STRING, intent: EnumerateFilesIntent,
processFile:
PROCEDURE [UNSPECIFIED, STRING, FileInfo],
processFileData:
UNSPECIFIED],
-- file transfer primitives
OpenFile: PROCEDURE [fileSystem: FileSystem, file: STRING, mode: Mode, fileTypePlease: BOOLEAN, info: FileInfo] RETURNS [fileHandle: FileHandle, fileType: FileType],
ReadFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle,
sendBlock:
PROCEDURE [UNSPECIFIED, POINTER, CARDINAL],
sendBlockData:
UNSPECIFIED],
WriteFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle, receiveBlock: PROCEDURE [UNSPECIFIED, POINTER, CARDINAL] RETURNS [CARDINAL], receiveBlockData: UNSPECIFIED],
CloseFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle, aborted: BOOLEAN],
-- file manipulation primitives
DeleteFile: PROCEDURE [fileSystem: FileSystem, file: STRING],
RenameFile: PROCEDURE [fileSystem: FileSystem, currentFile, newFile: STRING]];
FileSystem: TYPE = POINTER TO FileSystemObject;
FileSystemObject: TYPE = RECORD [dummy: UNSPECIFIED];
VirtualFilename: TYPE = POINTER TO VirtualFilenameObject;
VirtualFilenameObject: TYPE = RECORD [device, directory, name, version: STRING];
Status: TYPE = {primary, secondary};
EnumerateFilesIntent: TYPE = Intent[enumeration..deletion];
Intent: TYPE = {enumeration, retrieval, deletion, renaming, unspecified};
FileInfo: TYPE = POINTER TO FileInfoObject;
FileInfoObject: TYPE = RECORD [
fileType: FileType, byteSize:
CARDINAL, byteCount: LONG CARDINAL, creationDate, writeDate, readDate, author: STRING];
FileType: TYPE = {text, binary, unknown};
Mode: TYPE = {read, write, append, writeThenRead, readThenWrite};
FileHandle: TYPE = POINTER TO FileHandleObject;
FileHandleObject: TYPE = RECORD [dummy: UNSPECIFIED];
E.3. General Characteristics
The file primitives employed by FTP and suppliable by the client are described below. Statements that apply to all valid implementations of a primitive (that is, the FTP implementation for the Alto and any a client might supply) are rendered in the standard font. Statements that apply only to the implementation supplied by FTP are rendered in a smaller font. Except where stated to the contrary, filenames supplied as arguments to file primitives are absolute filenames and have thus already been processed by the ComposeFilename primitive described in Section E.5.
In accordance with standard Mesa exception handling conventions, file primitives report errors by signalling. FTP catches any signal that reaches it and aborts the current transaction (with the help of the remote FTP User or Server, as necessary). Wherever possible, client file primitives should use the standard FTP signal, FTPError (described in Section 1.4), to report errors. Doing so enables the FTP User or Server to communicate the error to the remote FTP Server or User in a meaningful way. The description of each procedure below includes a list of the FtpError values that seem, to the author, most appropriate for that primitive. Requests by file primitive implementors for new FtpError values will be gladly entertained.
E.4. Program Management Primitives
FTP or its client provides two procedures for creating and destroying instances of the local file system. The first, CreateFileSystem, used by both FTP User and Server, creates a new instance of the local file system and specifies the size in pages, bufferSize, of the buffers to be used by its ReadFile and WriteFile primitives (see Section E.8). The procedure returns a handle, fileSystem, to the newly created file system instance, which the caller must retain and later present to any of the other file primitives it invokes. The fileSystem is a pointer to a private record containing all of the state information the file system instance requires to function properly. The Alto implementation of this procedure simply records the buffer size:
CreateFileSystem: PROCEDURE [bufferSize: CARDINAL] RETURNS [fileSystem: FileSystem];
FileSystem: TYPE = POINTER TO FileSystemObject;
FileSystemObject: TYPE = RECORD[dummy: UNSPECIFIED];
The second procedure, DestroyFileSystem, used by both FTP User and Server, destroys a previously created instance of the local file system, reclaiming any local resources allocated to it. Before invoking this procedure, the caller must close all open files. The Alto implementation of this procedure is essentially a no operations:
DestroyFileSystem: PROCEDURE [fileSystem: FileSystem];
FileSystem: TYPE = POINTER TO FileSystemObject;
FileSystemObject: TYPE = RECORD[dummy: UNSPECIFIED];
E.5. Filename Manipulation Primitives
FTP or its client provides two procedures that serve to encapsulate FTP’s knowledge of local file naming conventions. The first, DecomposeFilename, used by both FTP User and Server, constructs a virtualFilename from an absoluteFilename, verifying the syntax of the absolute filename as a side effect. The caller provides the STRINGs into which the components of the virtual filename are to be placed. Components that are without meaning to the local file system are rendered as zero-length (rather than NIL) STRINGs. The Alto implementation of this procedure sets the length of the device and directory components to zero, since these components are without meaning to the Alto file system; returns the name component always; and returns a version component if an exclamation point in the absolute filename signals its presence:
DecomposeFilename: PROCEDURE [fileSystem: FileSystem, absoluteFilename: STRING, virtualFilename: VirtualFilename];
VirtualFilename: TYPE = POINTER TO VirtualFilenameObject;
VirtualFilenameObject: TYPE = RECORD [device, directory, name, version: STRING];
Exceptions: illegalFilename.
The second procedure, ComposeFilename, used by both FTP User and Server, constructs a fully qualified absoluteFilename from a virtualFilename and an unqualified or partially specified absoluteFilename (which is overwritten in the process). If the virtual filename is unspecified (that is, if all of its components are of zero length), the absolute filename to be returned by the procedure is completely specified by the absolute filename supplied to it. If the absolute filename supplied to the procedure is unspecified (that is, if it is of zero length), the absolute filename to be returned is completely specified by the virtual filename. If both absolute and virtual filenames are specified, the procedure optionally uses the virtual filename to default unspecified fields in the absolute filename. An implementation of this procedure could, for example, construct the fully qualified absolute filename "<Mesa>FTP>FTPDefs.Mesa" from a virtual filename the directory component of which is "Mesa" and an absolute filename the value of which is "FTP>FTPDefs.Mesa". Components of the virtual filename that are present but without meaning to the local file system should be ignored. The Alto implementation of this procedure ignores the device and directory components, since they are without meaning to the Alto file system, but uses the name and version components to default components that may be missing from the absolute filename. Having applied the available defaults, it then insists upon a name component and accepts a version component if it is present:
ComposeFilename: PROCEDURE [fileSystem: FileSystem, absoluteFilename: STRING, virtualFilename: VirtualFilename];
VirtualFilename: TYPE = POINTER TO VirtualFilenameObject;
VirtualFilenameObject: TYPE = RECORD [device, directory, name, version: STRING];
Exceptions: illegalFilename.
E.6. Access Control Primitives
FTP or its client provides one procedure, InspectCredentials, for inspecting credentials presented by the remote client. Used only by FTP Server, this procedure verifies and records the primary or secondary credentials--user and password--with which the next file system access will be implicitly attempted. The procedure verifies the user’s existence and the password’s correctness and records the fact that they were (correctly) supplied; the file primitive through which access to a particular file is subsequently attempted then determines whether those credentials entitle the remote FTP User to manipulate the file in the manner requested (for example, the DeleteFile primitive described in Section E.9 verifies that the client has delete access to the target file before honoring the delete request). Primary credentials typically identify the user upon whose behalf the access is attempted (a la the Tenex Login command) while secondary credentials, when necessary, usually identify another area of the file system--in addition to the user’s own workspace--to which the user claims access (a la the Tenex Connect command). Primary and secondary credentials (if any) are presented for inspection immediately before the call to the file primitive--EnumerateFiles, OpenFile, DeleteFile, or RenameFile--which attempts the access, and implicitly are discarded by the file system instance immediately after that operation. The Alto implementation of this procedure is a no operation:
InspectCredentials: PROCEDURE [fileSystem: FileSystem, status: Status, user, password: STRING];
Status: TYPE = {primary, secondary};
Exceptions: noSuchPrimaryUser, noSuchSecondaryUser, incorrectPrimaryPassword, incorrectSecondaryPassword.
E.7. File Enumeration Primitives
FTP or its client provides one procedure, EnumerateFiles, for enumerating the members of a local file group. For each file in the group whose file group designator, files, is specified, the procedure, used by both FTP User and Server, supplies to a caller-provided procedure, processFile, the caller’s processFileData, the absolute filename of the file, and a variety of other file information (FileInfo). The order in which filenames are presented to the caller is file-system-dependent, but alphabetical order is typical. Unknown or unspecified file information is rendered as unknown, zero, or NIL, as appropriate. The Alto implementation of this procedure recognizes in the file group designator the two special characters, asterisk (’*), denoting zero or more arbitrary characters, and pound sign (’#), denoting exactly one arbitrary character. The procedure returns to its caller all those files in the local file system that satisfy this mask. The files are presented in the order determined by DirectoryDefs.EnumerateDirectory and no use is made of the intent parameter:
EnumerateFiles: PROCEDURE [fileSystem: FileSystem, files: STRING, intent: EnumerateFilesIntent,
processFile:
PROCEDURE [UNSPECIFIED, STRING, FileInfo],
processFileData:
UNSPECIFIED];
EnumerateFilesIntent: TYPE = Intent[enumeration..deletion];
Intent: TYPE = {enumeration, retrieval, deletion, renaming, unspecified};
FileInfo: TYPE = POINTER TO FileInfoObject;
FileInfoObject: TYPE = RECORD [
fileType: FileType, byteSize:
CARDINAL, byteCount: LONG CARDINAL, creationDate, writeDate, readDate, author: STRING];
FileType: TYPE = {text, binary, unknown};
Exceptions: credentialsMissing, requestedAccessDenied, illegalFilename, noSuchFile, fileDataError.
The intent parameter supplied by the caller declares the manner in which the caller expects to manipulate the files whose names are presented to it. This information enables the file system, for example, to determine the version(s) of a file it should present to the caller (for instance, the procedure might return the most recent versions of files being retrieved and the oldest versions of files being deleted). The caller may specify any of the following intents:
1. An intent of enumeration declares that the caller simply seeks the names of (and file information for) the members of the designated file group (for presentation to a human user, for example) and intends to manipulate the files in no other way during the course of the enumeration.
2. An intent of retrieval declares that the caller seeks to retrieve some or all (but possibly none) of the designated files and to manipulate them in no other way during the course of the enumeration. The caller’s processFile procedure may retrieve the file whose name is presented to it by opening that file for read via OpenFile, obtaining the contents of the file via ReadFile, and then closing the file via CloseFile (see Section E.8).
3. An intent of deletion declares that the caller seeks to delete some or all (but possibly none) of the designated files and to manipulate them in no other way during the course of the enumeration. The client’s processFile procedure may delete the file whose name is presented to it by supplying that name to the DeleteFile procedure described in Section E.9.
E.8. File Transfer Primitives
FTP or its client provides four procedures for transferring files to and from the local file system. The client must supply its own version of all or none of these procedures, since they interact with one another by means of the file handle returned by the first. The first procedure, OpenFile, used by both FTP User and Server, verifies either the existence of an old file (if mode is read, append, or readThenWrite) or the availability of space for a new one (if mode is write or writeThenRead), establishes the remote client’s access to that file (in conjunction with the InspectCredentials procedure described in Section E.6), prepares the file to be read or written, and returns a handle, fileHandle, to the newly opened file. If fileTypePlease is TRUE (in which case mode will be read), the procedure also returns the fileType--text or binary--of the file being opened. If mode is write, and info is not NIL, and info.creationDate is not NIL, then info.creationDate contains the creation date and time of the file which should be saved as the creation date and time of the local file so that various automatic updating heuristics will operate correctly. If mode is read or readThenWrite, and info is not NIL, and info.creationDate is not NIL, then the creation date and time of the local file should be appended to info.creationDate so that it can be passed to the remote file system where it will be saved as the creation date and time of the new file. The Alto implementation of this procedure attaches a byte stream to the file and returns its handle. If the file’s type is requested, it scans the file until it encounters a byte with the high-order bit set to one (in which case the file is classified as binary) or reaches the end of the file (in which case the file is classified as text):
OpenFile: PROCEDURE [fileSystem: FileSystem, file: STRING, mode: Mode, fileTypePlease: BOOLEAN, info: FileInfo] RETURNS [fileHandle: FileHandle, fileType: FileType];
Mode: TYPE = {read, write, append, writeThenRead, readThenWrite};
FileHandle: TYPE = POINTER TO FileHandleObject;
FileHandleObject: TYPE = RECORD[dummy: UNSPECIFIED];
FileType: TYPE = {text, binary, unknown};
Exceptions: credentialsMissing, requestedAccessDenied, illegalFilename, noSuchFile, fileAlreadyExists, fileBusy, noRoomForFile, fileDataError.
The mode writeThenRead enables calls to both WriteFile and ReadFile, in that order; the mode readThenWrite enables calls to the same procedures in the other order. If mode is write or writeThenRead and the length of the file STRING is zero, the procedure opens a scratch file and return its name in the STRING. The reader is referred to Section E.10 for information about the manner in which this and other file primitives are used by FTP.
The second procedure, ReadFile, used by both FTP User and Server, presents to its caller the contents of the file (previously opened for read) the handle of which is specified by fileHandle. The procedure supplies to a caller-provided procedure, sendBlock, the sendBlockData supplied by ReadFile’s caller and the location and length in bytes of successive segments of the file. After the entire file has been output in this manner, ReadFile signals end of file by calling sendBlock a final time with a byte count of zero. The Alto implementation of this procedure simply allocates a buffer, reads successive blocks of the file from the disk stream into the buffer and presents them to sendBlock, and then releases the buffer:
ReadFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle,
sendBlock:
PROCEDURE [UNSPECIFIED, POINTER, CARDINAL],
sendBlockData:
UNSPECIFIED];
FileHandle: TYPE = POINTER TO FileHandleObject;
FileHandleObject: TYPE = RECORD[dummy: UNSPECIFIED];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork, fileDataError.
The third procedure, WriteFile, used by both FTP User and Server, accepts from its caller the contents of the file (previously opened for write) the handle of which is specified by fileHandle. The procedure supplies in zero or more calls to a caller-provided procedure, receiveBlock, the receiveBlockData supplied by WriteFile’s caller and the location and length in words of a buffer into which the next segment of the file is to be placed. In response, receiveBlock returns the segment left-adjusted in the buffer, along with its length in bytes. ReceiveBlock eventually signals end of file by returning a byte count of zero. The Alto implementation of this procedure simply allocates a buffer, reads successive blocks of the file into the buffer and appends them to the disk stream, and then releases the buffer:
WriteFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle, receiveBlock: PROCEDURE [UNSPECIFIED, POINTER, CARDINAL] RETURNS [CARDINAL], receiveBlockData: UNSPECIFIED];
FileHandle: TYPE = POINTER TO FileHandleObject;
FileHandleObject: TYPE = RECORD[dummy: UNSPECIFIED];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork, noRoomForFile, fileDataError.
The fourth procedure, CloseFile, used by both FTP User and Server, closes the previously opened file the handle of which is specified by fileHandle. If the caller’s attempt to read or write the file (via ReadFile or WriteFile) had to be aborted for some reason, the caller so indicates. If the file whose transfer was aborted was opened for write, writeThenRead, or readThenWrite, the procedure discards (that is, deletes) the partial file. The Alto implementation of this procedure simply destroys the disk stream and then deletes the file if the transfer was aborted and the stream was being written:
CloseFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle, aborted: BOOLEAN];
FileHandle: TYPE = POINTER TO FileHandleObject;
FileHandleObject: TYPE = RECORD[dummy: UNSPECIFIED];
Exceptions: noRoomForFile, fileDataError.
E.9. File Manipulation Primitives
FTP or its client provides two procedures for manipulating existing local files. The first, DeleteFile, used only by FTP Server, deletes the specified local file, reclaiming the space it occupied on secondary storage. The Alto implementation of this procedure simply deletes the file:
DeleteFile: PROCEDURE [fileSystem: FileSystem, file: STRING];
Exceptions: credentialsMissing, requestedAccessDenied, illegalFilename, noSuchFile, fileBusy, fileDataError.
The second procedure, RenameFile, used only by FTP Server, renames the local file the current name for which is specified by currentFile, assigning it the new name, newFile. The Alto implementation of this procedure creates a new file (with the appropriate name), copys the contents of the file to it, and then deletes the original file:
RenameFile: PROCEDURE [fileSystem: FileSystem, currentFile, newFile: STRING];
Exceptions: credentialsMissing, requestedAccessDenied, illegalFilename, noSuchFile, fileAlreadyExists, fileBusy, noRoomForFile, fileDataError.
E.10. Usage of File Primitives by FTP
In principle, the programmer who elects to implement his own file primitives should provide FTP with a complete and coherent set which meets the above specifications. In practice, however, only a subset of the primitives may actually be required in any particular application (for example, an FTP File Listener). In such cases, the required implementation effort can be reduced by knowledge of which primitives FTP uses to implement its various features.
What follows, therefore, is an exhaustive specification of the use currently made by FTP of the file primitives supplied by the client. The programmer may wish to exploit this information and implement only those primitives required by his particular configuration and application. Doing so, however, obliges him to keep careful tabs on the corresponding usage data for subsequent FTP releases, the file primitive usage for which may change or expand.
The twelve file primitives are currently used as follows:
CreateFileSystem
1. invoked by FTPOpenConnection unless purpose is mail.
2. invoked as part of the initialization of each new FTP File (but not Mail) Server.
3. invoked by the FTP-provided CreateMailSystem mail primitive (see Appendix F).
DestroyFileSystem
1. invoked by FTPCloseConnection unless FTPOpenConnection’s purpose was mail.
2. invoked as part of the finalization of each retiring FTP File (but not Mail) Server.
3. invoked by the FTP-provided DestroyMailSystem mail primitive (see Appendix F).
DecomposeFilename
1. invoked by FTP File Servers asked to enumerate, store, retrieve, or delete files.
2. invoked by the Alto FTP-provided ComposeFilename primitive.
ComposeFilename
1. invoked by FTP File Servers asked to enumerate, store, retrieve, delete, or rename files.
InspectCredentials
1. invoked by FTP File Servers asked to enumerate, store, retrieve, delete, or rename files.
EnumerateFiles
1. invoked by FTP File Servers asked to enumerate, retrieve, or delete files.
OpenFile
1. invoked by FTPEnumerateFiles if intent is renaming or unspecified to open a scratch file for filenames writeThenRead.
2. invoked by FTPStoreFile to open the source file read.
3. invoked by FTPRetrieveFile to open the destination file write.
4. invoked by FTP File Servers to open the destination file for a store write.
5. invoked by FTP File Servers to open the source file for a retrieve read.
6. invoked by the Alto FTP-provided RenameFile primitive to open the current file read and to open the new file write.
7. invoked by the FTP-provided CreateMailSystem mail primitive (see Appendix F) to open its mailbox directory file (that is, FTPSysMail-Directory.Bravo) read and to open a scratch file for incoming messages writeThenRead.
8. invoked by the FTP-provided RetrieveMessages mail primitive (see Appendix F) to open the mailbox file (named in FTPSysMail-Directory.Bravo) readThenWrite.
9. invoked by the FTP-provided DeliverMessage mail primitive (see Appendix F) to open the mailbox file (named in FTPSysMail-Directory.Bravo) append.
ReadFile
1. invoked by FTPEnumerateFiles if intent is renaming or unspecified to read a scratch file for filenames.
2. invoked by FTPStoreFile to read the source file.
3. invoked by FTP File Servers to read the source file for a retrieve.
4. invoked by the Alto FTP-provided RenameFile primitive to read the current file.
5. invoked by the FTP-provided LocateMailboxes mail primitive (see Appendix F) to read its mailbox directory file (that is, FTPSysMail-Directory.Bravo).
6. invoked by the FTP-provided RetrieveMessages mail primitive (see Appendix F) to read the mailbox file (named in FTPSysMail-Directory.Bravo).
7. invoked by the FTP-provided DeliverMessage mail primitive (see Appendix F) to read the scratch file for incoming messages.
WriteFile
1. invoked by FTPEnumerateFiles if intent is renaming or unspecified to write a scratch file for filenames.
2. invoked by FTPRetrieveFile to write the destination file.
3. invoked by FTP File Servers to write the destination file for a store.
4. invoked by the Alto FTP-provided RenameFile primitive to write the new file.
5. invoked by the FTP-provided StageMessage mail primitive (see Appendix F) to write a scratch file for incoming messages.
6. invoked by the FTP-provided DeliverMessage mail primitive (see Appendix F) to write (that is, append to) the mailbox file (named in FTPSysMail-Directory.Bravo).
CloseFile
1. invoked by FTPEnumerateFiles if intent is renaming or unspecified to close a scratch file for filenames.
2. invoked by FTPStoreFile to close the source file.
3. invoked by FTPRetrieveFile to close the destination file.
4. invoked by FTP File Servers to close the destination file for a store.
5. invoked by FTP File Servers to close the source file for a retrieve.
6. invoked by the Alto FTP-provided RenameFile primitive to close the current file and to close the new file.
7. invoked by the FTP-provided DestroyMailSystem mail primitive (see Appendix F) to close its mailbox directory file (that is, FTPSysMail-Directory.Bravo) and to close a scratch file for incoming messages.
8. invoked by the FTP-provided RetrieveMessages mail primitive (see Appendix F) to close the mailbox file (named in FTPSysMail-Directory.Bravo).
9. invoked by the FTP-provided DeliverMessage mail primitive (see Appendix F) to close the mailbox file (named in FTPSysMail-Directory.Bravo).
DeleteFile
1. invoked by FTP File Servers when asked to delete a file.
2. invoked by the Alto FTP-provided RenameFile primitive to delete the current file.
RenameFile
1. invoked by FTP File Servers when asked to rename a file.
Appendix F: Client Mail Primitives
F.1. Description of the Option
An FTP Server manipulates its local mail system by means of a family of procedures called mail primitives. This family includes, for example, procedures for appending a message to one or more local mailboxes, for emptying a local mailbox of its contents, and for forwarding a message to one or more remote mailboxes. The FTP implementation includes one set of primitives, which implements a simple-minded mail system in terms of the file primitives described in Appendix E.
Rather than use the mail system interfaces offered by FTP, the client may, if it wishes, provide its own mail primitives to a particular FTP Listener. By so doing, a client can use FTP, for example to:
1. interface to another local mail system.
2. interface to a pseudo mail system (for example, a printer).
3. transform mailbox names (for example, convert abstract mailbox names to concrete ones).
4. control access to particular functions on a per-user or per-host basis.
5. maintain a log of mail system activity.
The client can also implement certain mail primitives while relying on FTP for others, or use the FTP implementations as building blocks for its own implementations. For example, a client could log mail deliveries by supplying an implementation of the DeliverMessage procedure (described in Section F.7) that records the event and then calls FTP’s DeliverMessage procedure to actually deliver the message.
F.2. Exercising the Option
The client exercises the option described above by means of the mailPrimitives parameter required by the FTPCreateListener procedure. This parameter is a POINTER to a RECORD containing the PROCEDUREs by which any FTP Servers created by the newly created FTP Listener are to access the local mail system. The client may supply its own version of this record rather than rely upon the standard version offered by FTP. In constructing the record and/or implementing the procedures it contains, the client may draw upon any of the mail primitives it finds in the FTP-provided record. Since FTP will not copy the record presented to it, the client must preserve it intact until FTPDestroyListener is called:
MailPrimitives: TYPE = POINTER TO MailPrimitivesObject;
MailPrimitivesObject: TYPE = RECORD [
-- program management primitives
CreateMailSystem: PROCEDURE [filePrimitives: FilePrimitives, bufferSize: CARDINAL] RETURNS [mailSystem: MailSystem, forwardingProvided: BOOLEAN],
DestroyMailSystem: PROCEDURE [mailSystem: MailSystem],
-- access control primitives
InspectCredentials: PROCEDURE [mailSystem: MailSystem, status: Status, user, password: STRING],
-- identification primitives
LocateMailboxes: PROCEDURE [mailSystem: MailSystem, localMailboxList: Mailbox],
-- delivery primitives
StageMessage: PROCEDURE [mailSystem: MailSystem,
receiveBlock:
PROCEDURE [UNSPECIFIED, POINTER, CARDINAL] RETURNS [CARDINAL],
receiveBlockData:
UNSPECIFIED],
DeliverMessage: PROCEDURE [mailSystem: MailSystem, localMailboxList: Mailbox],
ForwardMessage: PROCEDURE [mailSystem: MailSystem, remoteMailboxList: Mailbox],
-- retrieval primitives
RetrieveMessages: PROCEDURE [mailSystem: MailSystem, localMailbox: Mailbox, processMessage: PROCEDURE [MessageInfo],
sendBlock:
PROCEDURE [UNSPECIFIED, POINTER, CARDINAL],
sendBlockData:
UNSPECIFIED]];
FilePrimitives: TYPE = POINTER TO FilePrimitivesObject;
FilePrimitivesObject: TYPE = RECORD [...];
MailSystem: TYPE = POINTER TO MailSystemObject;
MailSystemObject: TYPE = RECORD [dummy: UNSPECIFIED];
Status: TYPE = {primary, secondary};
Mailbox: TYPE = POINTER TO MailboxObject;
MailboxObject: TYPE = RECORD [
number:
CARDINAL, mailbox, location: STRING, located, delivered: BOOLEAN, nextMailbox: Mailbox];
MessageInfo: TYPE = POINTER TO MessageInfoObject;
MessageInfoObject: TYPE = RECORD [
byteCount:
LONG CARDINAL, deliveryDate: STRING, opened, deleted: BOOLEAN];
F.3. General Characteristics
The mail primitives employed by FTP and suppliable by the client are described below. Statements that apply to all valid implementations of a primitive (that is, FTP’s implementation and any a client might supply) are rendered in the standard font. Statements that apply only to the standard implementation supplied by FTP are rendered in a smaller font.
In accordance with standard Mesa exception handling conventions, mail primitives report errors by signalling. FTP catches any signal that reaches it and aborts the current transaction (with the help of the remote FTP User, as necessary). Wherever possible, client mail primitives should use the standard FTP signal, FTPError (described in Section 1.4), to report errors. Doing so enables the FTP Server to communicate the error to the remote FTP User in a meaningful way. The description of each procedure below includes a list of the FtpError values that seem, to the author, most appropriate for that primitive. Requests by mail primitive implementors for new FtpError values will be gladly entertained.
F.4. Program Management Primitives
FTP or its client provides two procedures for creating and destroying instances of the local mail system. The first, CreateMailSystem, creates a new instance of the local mail system founded upon the specified file system; the mail system will use only the file system whose filePrimitives and bufferSize (passed to CreateFileSystem) are specified. The procedure returns a handle, mailSystem, to the newly created mail system instance, which the caller must retain and later present to any of the other mail primitives it invokes, along with an indication, forwardingProvided, of whether the mail system implements the ForwardMessage primitive described in Section F.7. The mailSystem is a pointer to a private record containing all of the state information the mail system instance requires to function properly. The FTP implementation of this procedure records the specified file primitives, creates an instance of the corresponding file system, opens for read the mailbox directory file named FTPSysMail-Directory.Bravo (which is assumed to contain zero or more lines of the form: mailboxname@filename), opens for writeThenRead a scratch file in which incoming messages will be staged for delivery, and then returns with forwardingProvided set to FALSE:
CreateMailSystem: PROCEDURE [filePrimitives: FilePrimitives, bufferSize: CARDINAL] RETURNS [mailSystem: MailSystem, forwardingProvided: BOOLEAN];
FilePrimitives: TYPE = POINTER TO FilePrimitivesObject;
FilePrimitivesObject: TYPE = RECORD [...];
MailSystem: TYPE = POINTER TO MailSystemObject;
MailSystemObject: TYPE = RECORD [dummy: UNSPECIFIED];
Exceptions: credentialsMissing, requestedAccessDenied, illegalFilename, noSuchFile, fileAlreadyExists, fileBusy, noRoomForFile, fileDataError, filePrimitivesNotSpecified.
The second procedure, DestroyMailSystem, destroys a previously created instance of the local mail system, reclaiming any local resources allocated to it. The FTP implementation of this procedure closes the staging and directory files and then destroys the previously created file system instance:
DestroyMailSystem: PROCEDURE [mailSystem: MailSystem];
MailSystem: TYPE = POINTER TO MailSystemObject;
MailSystemObject: TYPE = RECORD [dummy: UNSPECIFIED];
Exceptions: noRoomForFile, fileDataError.
F.5. Access Control Primitives
FTP or its client provides one procedure, InspectCredentials, for inspecting credentials presented by the remote client. This procedure verifies and records the primary or secondary credentials--user and password--with which the next mail system access will be implicitly attempted. It is customary to require credentials when an attempt is made to empty (that is, read) a mailbox, but not when an attempt is made to deliver a message (that is, append) to a mailbox. The procedure verifies the existence of the user and the correctness of the password’s and records the fact that they were (correctly) supplied; the mail primitive through which access to a particular mailbox is subsequently attempted then determines whether those credentials entitle the remote FTP User to manipulate the mailbox in the manner requested (for example, the RetrieveMessages primitive described in Section F.8 verifies that the client has read access to the target mailbox before honoring the retrieve request). Primary credentials typically identify the user upon whose behalf the access is attempted (a la the Tenex Login command) while secondary credentials, when necessary, usually identify another area of the mail system--in addition to the mailbox of the user--to which the user claims access (a la the Tenex Connect command). (The distinction between primary and secondary credentials is really more germane to file systems than to mail systems.) Primary and secondary credentials (if any) are presented for inspection immediately before the call to the mail primitive--DeliverMessage, ForwardMessage, or RetrieveMessages--which attempts the access, and implicitly are discarded by the file system instance immediately after that operation. The FTP implementation of this procedure is a no operation:
InspectCredentials: PROCEDURE [mailSystem: MailSystem, status: Status, user, password: STRING];
Status: TYPE = {primary, secondary};
Exceptions: noSuchPrimaryUser, noSuchSecondaryUser, incorrectPrimaryPassword, incorrectSecondaryPassword.
F.6. Mailbox Identification Primitives
FTP or its client provides one procedure, LocateMailboxes, for verifying the existence of (and optionally locating) one or more local mailboxes. The procedure receives among its parameters a linked list, localMailboxList, of one or more mailbox objects. The value NIL in the nextMailbox field signals the end of the list. For every mailbox object whose located field is FALSE, the procedure sets located to TRUE if the mailbox named by the object exists. In addition, it optionally records in the object’s location field, information (for example, a filename) that might assist a subsequently called primitive deliver mail to or empty the mailbox. If it makes use of the location field at all (FTP initializes it to NIL), the procedure must store in it a STRING allocated from the heap, which FTP will return to the heap when the subsequent delivery or retrieval operation is complete. The FTP implementation of this procedure searches the directory file for each mailbox name and records the corresponding filename as the location of the mailbox’s:
LocateMailboxes: PROCEDURE [mailSystem: MailSystem, localMailboxList: Mailbox];
Mailbox: TYPE = POINTER TO MailboxObject;
MailboxObject: TYPE = RECORD [
number:
CARDINAL, mailbox, location: STRING, located, delivered: BOOLEAN, nextMailbox: Mailbox];
F.7. Mail Delivery Primitives
FTP or its client provides three procedures for delivering messages to local and remote mailboxes. The first procedure, StageMessage, accepts from the remote FTP User the text of a message that subsequently is to be delivered to one or more local or remote mailboxes. The procedure supplies in zero or more calls to a caller-provided procedure, receiveBlock, the receiveBlockData supplied by StageMessage’s caller, and the location and length in words of a buffer into which the next segment of the message is to be placed. In response, receiveBlock returns the segment left-adjusted in the buffer, along with its length in bytes. ReceiveBlock eventually signals end of message by returning a byte count of zero. The FTP implementation of this procedure writes the text of the message onto the previously opened scratch file by means of the WriteFile file primitive:
StageMessage: PROCEDURE [mailSystem: MailSystem,
receiveBlock:
PROCEDURE [UNSPECIFIED, POINTER, CARDINAL] RETURNS [CARDINAL],
receiveBlockData:
UNSPECIFIED];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork, noRoomForFile, fileDataError.
The second procedure, DeliverMessage, appends a previously staged message to one or more previously verified local mailboxes. The procedure receives among its parameters a linked list, localMailboxList, of one or more mailbox objects. The value NIL in the nextMailbox field signals the end of the list. For every mailbox object whose located field is TRUE and delivered field is FALSE, the procedure attempts to deliver the message to the named mailbox and sets delivered to TRUE if successful. The procedure uses the contents of the location field, as set by LocateMailboxes, to help it deliver the mail. The FTP implementation of this procedure appends the contents of the scratch file in which the message was staged to the file named by location, preceding it with a header containing the current date and time and the length of the message:
DeliverMessage: PROCEDURE [mailSystem: MailSystem, localMailboxList: Mailbox];
Mailbox: TYPE = POINTER TO MailboxObject;
MailboxObject: TYPE = RECORD [
number:
CARDINAL, mailbox, location,: STRING, located, delivered: BOOLEAN, nextMailbox: Mailbox];
Exceptions: credentialsMissing, requestedAccessDenied, illegalFilename, noSuchFile, fileAlreadyExists, fileBusy, noRoomForFile, fileDataError.
The third procedure, ForwardMessage, enqueues the previously staged message for delivery to one or more remote mailboxes. The procedure receives among its parameters a linked list, remoteMailboxList, of one or more mailbox objects. The value NIL in the nextMailbox field signals the end of the list. For each mailbox object, the procedure attempts to enqueue the message for later delivery to the named mailbox at the remote host the name of which is stored in the location field, and sets located to TRUE if successful. The FTP implementation of this procedure, which will never be called because CreateMailSystem returns with forwardingProvided set to FALSE, is a no operation:
ForwardMessage: PROCEDURE [mailSystem: MailSystem, remoteMailboxList: Mailbox];
Mailbox: TYPE = POINTER TO MailboxObject;
MailboxObject: TYPE = RECORD [
number:
CARDINAL, mailbox, location: STRING, located, delivered: BOOLEAN, nextMailbox: Mailbox];
F.8. Mail Retrieval Primitives
FTP or its client provides one procedure, RetrieveMessages, for retrieving the contents of (and then resetting to empty) a local mailbox. For each of the zero or more messages in the specified localMailbox, the procedure first invokes the caller-supplied processMessage procedure with information about the message: its size in bytes, byteCount; the date and time, deliveryDate, at which the message was deposited in the local mailbox; and whether or not the message has been opened (that is, examined) or deleted while in the mailbox (Maxc mailboxes, for example, can be manipulated directly via the Tenex MSG subsystem). Following each invocation of processMessage, RetrieveMessages supplies in zero or more calls to a second FTP-provided procedure, sendBlock, the sendBlockData supplied by RetrieveMessages’ caller and the location and length in bytes of successive segments of the message. After the entire message has been output in this manner, RetrieveMessages signals end of message by calling sendBlock a final time with a byte count of zero. After all messages have been returned, the procedure resets the contents of the mailbox to empty. RetrieveMessages is implemented in such a way that no new messages are lost during the retrieval process. The FTP implementation of this procedure retrieves each message’s byteCount and deliveryDate from the message header and forces opened and deleted to FALSE:
RetrieveMessages: PROCEDURE [mailSystem: MailSystem, localMailbox: Mailbox, processMessage: PROCEDURE [MessageInfo],
sendBlock:
PROCEDURE [UNSPECIFIED, POINTER, CARDINAL],
sendBlockData:
UNSPECIFIED];
Mailbox: TYPE = POINTER TO MailboxObject;
MailboxObject: TYPE = RECORD [
number:
CARDINAL, mailbox, location: STRING, located, delivered: BOOLEAN, nextMailbox: Mailbox];
MessageInfo: TYPE = POINTER TO MessageInfoObject;
MessageInfoObject: TYPE = RECORD [
byteCount:
LONG CARDINAL, deliveryDate: STRING, opened, deleted: BOOLEAN];
Exceptions: connectionTimedOut, connectionClosed, noRouteToNetwork, credentialsMissing, requestedAccessDenied, illegalFilename, noSuchFile, fileAlreadyExists, fileBusy, noRoomForFile, fileDataError.