Programming Internet EmailBy David Wood
1st Edition August 1999
1-56592-479-7, Order Number: 4797
378 pages, $34.95
The Application Configuration Access Protocol (ACAP) is a companion protocol to IMAP. The two share a commonality of design, as well as complementary features. These two protocols, working together, form the basis of a vision for mobile users' use of the Internet.
Whereas IMAP allows one to store and manipulate email on a remote server, so ACAP allows one to store and manipulate generic name-value pairs of information on a remote server. This data may be program configuration options (hence the name), address book data, or any other information that a user may wish to store.
Discussions of ACAP often include mention of the Lightweight Directory Access Protocol (LDAP). LDAP servers provide a directory service, meaning that they store name-value pairs of information and make those records available to authorized users. LDAP servers are generally used on corporate networks to provide a centralized repository for security information (such as usernames and passwords) and contact information (names, addresses, telephone numbers, etc.). Network services can then access the information in LDAP servers to avoid having to duplicate the information. Doesn't this sound a lot like ACAP?
Corporations have historically been reluctant to place their repositories of security and contact information on Internet-accessible servers, and for good reason. Even if a protocol provides adequate security, potential bugs in implementations give cause for concern. ACAP evolved to help address this concern. ACAP allows individuals to distribute a subset of corporate information via a server that is remotely accessible. Information is compartmentalized, and in case of a security hole, only this distributed subset is compromised.
ACAP clients, unlike LDAP clients, must be able to work offline by caching information locally. An ACAP server is updated when changes are made locally, either in real time or upon the next connection. ACAP is meant to be easier for clients to implement than IMAP; ACAP servers are more complex than the clients. This will hopefully encourage many application developers to choose to implement ACAP clients.
ACAP is increasingly being implemented by manufacturers of MUAs in order to provide access to a single point of storage for address book and configuration information. If an MUA implements both IMAP and ACAP, a user could connect to a single email account, use a single address book, and maintain consistent configuration details at home, at work, and while traveling. Of course, the price for these benefits is access to a server that provides IMAP and ACAP services.
ACAP is an Internet-proposed standard and is currently in version 1.0. An ACAP server is a TCP application daemon that listens for ACAP client requests on TCP port 674. MUAs, Web browsers, or any other type of network-oriented application may implement an ACAP client.
Some commercial products have implemented ACAP's predecessor, the Internet Messaging Support Protocol (IMSP), but IMSP never made it into the standards track. Commercial ACAP servers are still in the works as of this writing.
ACAP clients must be able to work offline. They may or may not connect to an ACAP server, depending on their needs. Typically, an ACAP client connects to a server when an application is started, and network access is available in order to determine if its configuration information is still current. This could (and probably should) be done in a thread to avoid long startup delays. Additional connections to a server are made when a user changes information that is held on the server; first the local cache is updated, and then the server is contacted at the next available opportunity.
Figure 12.1 shows an ACAP server and a possible relationship to an MUA, IMAP MRA, and IMAP client. In this scenario, an MUA implements both an IMAP and an ACAP client. The ACAP client is used to retrieve configuration information and address book data from an ACAP server. Since ACAP clients must be allowed to work offline, this communication does not necessarily have to occur upon MUA startup. An IMAP session can then be opened to an IMAP server to operate on email messages.
Such an arrangement allows for the creation of a very generic MUA. Consider an Internet cafe, for example. A user can provide authentication information (perhaps an X.509 certificate or some other acceptable credentials) and an ACAP server name when the MUA was started. This information is sufficient to provide the user with personalized configuration information (such as the location of the IMAP server and user interface preferences) and a personalized address book.
Information in an ACAP server is held in a slash (/) separated hierarchical structure of entries. Each entry has attributes. Each attribute consists of some metadata that describes what it holds and who may modify it.
Each level of the hierarchy is called a dataset. Datasets are used to hold information that should be interpreted together, such as an address book or the configuration information for a particular application. Figure 12.2 shows the relationship between entries, attributes, and datasets. A dataset is just a special type of entry: an analogy that won't hold up as we progress is that a dataset is like a directory on a filesystem and an entry is like a file.
A new dataset can be created by making an entry and placing a period (.) into its "subdataset" attribute. This is another way of saying that an entry is just an entry (with attributes), but if its "subdataset" attribute contains a period (.), then it becomes a dataset in its own right and can then hold other entries.
Each entry must have an attribute called (confusingly) entry. This attribute's metadata names the entry and holds its access control list and other information. Each attribute has metadata (which are just name-value pairs) like this.
Datasets are named in an interesting way. They have a long, hierarchical name but may also be accessed by "nicknames" to make searches shorter and easier for clients to understand. First, let's look at the long way. A unique dataset name consists of the following parts: a class name, a dataset type, and a unique data hierarchy. The class name is generally named after a vendor (e.g., Sun) and maybe a product name (e.g., Sun.HotJava). A dataset type is one of "site" (for per-location information), "group" (for per-group information), "host" (for per-server information), or "user" (for per-user information). Anything following the class name and dataset type is a hierarchical list of entries, as named by the application creating the dataset.
Since this book deals with email-related activities, the examples in this chapter will primarily show the use of user and group dataset types.
To make life easier, datasets may also be referred to by shortened names. Any dataset starting with the string "/byowner/" refers to an alternate view of the namespace sorted by the authorized users of the system. "/byowner/user/fred/", for example, shows the datasets owned by the user Fred. The special symbol "~", often used in Unix shells to indicate the current user's home directory, may also be used here: a dataset called "/byowner/~/" lists datasets owned by the currently authenticated user.
Dataset names can also be shortcut in another way. A dataset called "/" shows all datasets that the current user can view. In practice, this is the approach used by most clients. A user attempting to get information from his own address book, for example, might use a dataset name of "/addressbook/user/fred" or "/addressbook/group/engineering".
Datasets consist of entries. Each dataset has its own entry (called "", which holds its own naming and security information. Datasets can also inherit attributes from other datasets (such as read-write permissions).
Attributes within entries are collections of metadata, which in turn are name-value pairs. Attributes have either single or multiple values. Attribute names are stored in the UTF-8 character set.
Some predefined attributes exist for any entry. They are:
The name of the entry.
Contains the date and time of the last modification to any attribute in the entry.
If set, another entry exists under this one of the name(s) given.
Modtime attributes are in the format YYYYMMDDHHMMSSFF, meaning year, month, day, hour, minutes, seconds, and fractions of seconds in that order. The time zone for times in ACAP is always universal coordinated time, UTC.
Each attribute has some associated metadata that describes what the attribute holds and who can modify it. The metadata items for each attribute are listed in Table 12.1.
Table 12.1: ACAP Attribute Metadata Metadata Item Description Modification acl The access control list for the attribute. Maps usernames to a list of privileges (see the section Section 12.1.2, "Access Control," later in this chapter). read-write attribute The name of the attribute. read-only myrights The rights (permissions) that the client has for the attribute. read-only size The length of the value. If multiple values exist for the attribute, this will be a list of lengths. read-only value The value of the attribute. This will be a list of values if the attribute has more than one value. read-write
Each user of an ACAP system has specific rights to the data stored on it. These rights are also called permissions or privileges. They define whether the user is allowed to read or write the data and whether the user can change the rights of others. Rights can be finely controlled: datasets and even attributes within an entry can have rights attached to them. As with other ACAP features, rights may be inherited from parent datasets.
Rights are stored on an ACAP server in Access Control Lists (ACLs). ACLs are stored as an attribute in the particular entry for which data is being restricted.
Rights for attributes are given as a list of values. The following characters are used to identify the rights for an attribute:
Special search (see the description that follows)
Write (change existing entries)
Insert (write new entries)
Administer (change rights)
Search rights and read rights are complementary but a bit confusing. Search rights give a user the ability to compare the value of one attribute or dataset with another. Read rights allow a user to search datasets with the SEARCH command.
ACAP's SEARCH command is very powerful, as we shall see. The power of it becomes apparent when someone does a search like, "Give me contact information for all people who are in my address book who have email addresses in the netscape.com domain." That type of complex search, which includes a comparison, takes search rights. A simple search, such as "Give me contact information for all people who are in my address book," can be done with just read rights.
Write and insert rights are also complementary. Write rights allow a user to modify an attribute or dataset with the STORE command, and insert rights allow a STORE command to be used to create a new attribute or dataset.
In other words, write rights would allow one to issue a command that says, "Change Sue's email address in my address book to firstname.lastname@example.org." But to create a new address book entry for a new person requires insert rights.
Administer rights allow a user to modify access control lists or other attribute metadata.
By default, the owner of a dataset (the person who created it) has read and administer rights.
If we have a username "ralph", then we should have a "home directory" (in Unix terms) on an ACAP server called /byowner/user/ralph. In ACAP's weird and wonderful aliasing, this directory would also be accessible as /user/ralph or, if we logged in as ralph, /~.
If we wish to create a new dataset to hold an address book, we might call it /byowner/user/ralph/addressbook, which we could access as /addressbook/user/ralph. We will use this example throughout the rest of this chapter.
12.2 ACAP Commands
ACAP sessions, states, and commands are very similar to IMAP4rev1. This is intentional, since the protocol structure defined in IMAP is very flexible. ACAP, like IMAP and many other Internet protocols, is line oriented with lines ending in CRLF sequences.
ACAP sessions may be in one of three states at any one time. These states are:
Upon initial connection to an ACAP server, a client is in the Nonauthenticated State. A client enters the Authenticated State when a user logs into the server, providing the appropriate type of authentication credentials.
Once logged in, a client may search, add, or remove information in any dataset for which the authenticated user has been allowed access.
The Logout State is entered when a client logs out, a server refuses service, or a connection is otherwise interrupted. This state lasts just long enough for the client and server to close a connection.
Within each state, the client may issue the commands appropriate to that state.
Like IMAP, an ACAP client is required to be able to accept any server response at any time. This recognizes the multithreaded nature of modern MUAs, since they may very well issue multiple commands to a server simultaneously from different threads.
An ACAP client precedes each command with a pseudo-random alphanumeric string, or tag. This is done exactly as in IMAP commands. Server responses include a copy of the tag so that the client may determine to which command the server is responding. Clients should use a different tag for each command that they issue.
ACAP server responses are very similar to IMAP server responses. You may recall that an IMAP server response may contain either tagged or untagged lines. The tagged lines contain an explicit response to the client's request, and untagged lines may provide additional explanatory information. ACAP server's work the same way.
An ACAP server responds to requests with one of several status responses. The status responses for an IMAP server are OK, NO, BAD, PREAUTH, and BYE. The ACAP status responses are OK, NO, BAD, ALERT, and BYE. The only difference is ALERT, that informs the client of a condition that the client must display to the user. Each status response can contain an additional response code, which provides detailed information about a problem. Server response codes are described in Table 12.2.
The table contains references to ACAP commands. Although these commands have not yet been introduced, they are included here for later reference. ACAP commands have reasonably straightforward names, so reading them with prior knowledge of IMAP command names should not be difficult.
Table 12.2: ACAP Server Response Codes Response Code Command Where Valid Description AUTH-TOO-WEAK AUTHENTICATE The requested authentication mechanism cannot be used by the server. ENCRYPT-NEEDED AUTHENTICATE The requested authentication mechanism requires the use of strong encryption. INVALID STORE The server does not allow the data type given to be stored in the given location. MODIFIED UNCHANGEDSINCE STORE A conditional Store request failed because the entry has been changed in the interim by someone else. NOEXIST SEARCH or NOCREATE STORE The chosen dataset does not exist. PERMISSION Any The user is not allowed to take requested action, based on an ACL or implicit rights list. QUOTA STORE or SETACL The requested command would have used more disk space than the user is authorized. REFER Any command that takes a dataset as an argument The given command should be retried with one of the relative URLs given in the response. SASL AUTHENTICATE Gives an optional server response to a successful authentication. TOOMANY SEARCH with a LIMIT set Indicates that the search returned more entries than the given limit. TOOOLD DELETEDSINCE Deleted entry information is no longer available because too much time has passed. TRANSITION-NEEDED AUTHENTICATE The username is valid, but that user can't use the authentication mechanism requested until the authentication database is updated. TRYLATER Any The server has a temporary failure. The client should try again later. TRYFREECONTEXT SEARCH with MAKECONTEXT The search request failed because the server cannot create any more contexts. The client should try later or use a FREECONTEXT command. WAYTOOMANY SEARCH with HARDLIMITIndicates that the search returned more entries than the given hard lim�it.
When a client initiates an ACAP session, the server responds with a banner greeting consisting of an untagged ACAP response. Unlike other protocols, an ACAP server includes its capabilities in this banner greeting, which are provided as a list of parenthesized named values. The capabilities may include any of the following:
A number greater than 100 indicating the maximum number of search contexts (see the SEARCH command) that the server can support for this connection. If this number is 0, the server has no limit.
A string describing the server implementation (e.g., vendor, version, etc.).
A list of authentication mechanisms supported.
A banner greeting for an ACAP server might look like this:Client: <establishes connection> Server: * ACAP (IMPLEMENTATION "Plugged In ACAP Server v1.03") (CONTEXTLIMIT "150") (SASL "CRAM-MD5" "KERBEROS_V4")
From the banner, we can see that this ACAP server was created by
Plugged Inand has a version number of
1.03. It can support up to 150 contexts per connection, and it supports both CRAM-MD5 and KERBEROS Version 4 authentication. The CRAM-MD5 authentication mechanism is the default and is discussed in more detail in this section.
Three ACAP commands are valid in any state. They are: NOOP, LANG, and LOGOUT. We know about NOOP (no operation) from other protocols.
NOTE: An odd decision in the ACAP RFC forces servers to have at least a 30-minute inactivity timeout. As in other protocols, a NOOP command can be used to reset this timer.
LANG allows a client to indicate its language preferences. LOGOUT, naturally, is used to end a session.
The LANG command is used by a client that wishes the server to switch languages. The client provides a list of languages that it prefers, and the server switches to the first language in the client's list that it supports. The server also returns the language that it is now using and a list of comparison operators that may be used for searches in that language. Comparison operators are described in the section Section 12.2.2, "The Authenticated State," under the SEARCH command. For example:Client: AC23 LANG "en-au" "en-ca" "en-uk" "en-us" Server: AC23 LANG "en-au" "i;octet" "i;ascii-numeric" "en;primary" Server: AC23 OK "G'day, Mate"
The example shows a client asking a server to switch to one of several English variants. In this case, Australian English is requested, and the server switches to it. This can be seen from the greeting given in the response line. Comparison operators may be used with the SEARCH command and will be explained later.
The LOGOUT command simply informs the server that the client will not be issuing further commands. It looks like this:Client: AC24 LOGOUT Server: * BYE "See ya later" Server: AC24 OK "LOGOUT completed" (connection is closed by both parties)
Table 12.3 lists the ACAP commands that are valid in any state.
Table 12.3: ACAP Commands Valid in Any State Command Arguments Description Response NOOP None Takes no action. Can be used to reset server timeout. None. LANG Quoted list of language preferences Requests that the server send all further responses in the first preferred language supported. The language selected and a list of comparison operators LOGOUT None Informs the server that the client wishes to close the network connection. Required untagged response: BYE
All ACAP sessions begin in the Nonauthenticated State. The client cannot issue most commands until properly authenticating to the server. This is done by the client issuing the AUTHENTICATE command.
Like IMAP's AUTHENTICATE command and POP's AUTH command, ACAP's AUTHENTICATE command can be extended as necessary by implementing new authentication mechanisms. ACAP servers, as we have seen, advertise the mechanisms that they support. It is up to the client to choose a valid mechanism from the list.
 This ability of protocols to add new authentication mechanisms is accomplished by the Simple Authentication and Security Layer (SASL), described in RFC 2222.
ACAP authentication is generally done with the Challenge-Response Authentication Mechanism (CRAM) described in RFC 2195. Since this mechanism uses MD5 digesting, this is commonly called CRAM-MD5. This mechanism is also used by some IMAP and POP servers. It provides a better authentication capability than plain text username/password schemes since no unencrypted passwords need to be stored on a server or transmitted in the clear via the Internet.
There is generally no simple authentication mechanism in ACAP, although a server and client could implement one. This is due to the supposition that ACAP servers are to be used to store personal information in an Internet environment, so stronger authentication is preferred. All ACAP implementations are required by the proposed standard to implement at least CRAM-MD5 authentication.
A complete discussion of the CRAM-MD5 algorithms is beyond the scope of a book on email. However, take note that CRAM-MD5 requires that the client and server share a secret. That is, they must both be told to store the same piece of information before they can connect to one another. This is an attempt to make ACAP clients and servers more secure than most of today's POP and IMAP clients and servers, but it also means that the systems administration overhead of setting up ACAP accounts will be significantly more difficult. Too, if a traveler wishes to use an arbitrary MUA with ACAP, she will need to carry appropriate authentication credentials with her. This may be an interesting part of attempting to widely deploy ACAP. One way to solve this would be for client and server to use an X.509 certificate scheme, much like current SSL-enabled Web browsers and clients.
A sample authentication with CRAM-MD5 shows the server responding to a client's request with a string that includes a random number, a time/date stamp, and the full server name. The client uses that information along with the shared secret to compute its response. For example:Client: GH01 AUTHENTICATE "CRAM-MD5" Server: + "<email@example.com>" Client: "dwood 19d8a609da4da7a4904ce6e739d8a75d3890" Server: GH01 OK "CRAM-MD5 authentication successful"
Table 12.4 lists the ACAP commands that are valid in the Nonauthenticated State.
Table 12.4: ACAP Command Valid in the Nonauthenticated State Command Arguments Description Response AUTHENTICATE SASL authentication mechanism name The client uses this command to request authentication using the specified mechanism. If the server supports the mechanism, an authentication protocol exchange follows. Continuation data may be requested.
A client enters the Authenticated State upon successful login. It is from this state that the client is actually able to do something useful. ACAP allows most work to be done from two important commands: SEARCH and STORE. The SEARCH command allows a client to find existing information held on an ACAP server, and the STORE command allows a client to write new information to the server. These two commands have a number of optional modifiers that direct behavior and responses that return information based on the modifiers used.
Other commands available in this state support the SEARCH command (FREECONTEXT, UPDATECONTEXT, DELETEDSINCE) and allow the user to determine and potentially modify access restrictions (SETACL, DELETEACL, MYRIGHTS, LISTRIGHTS, GETQUOTA).
ACAP's monster command is SEARCH. Since the protocol is optimized for reading and it is expected that information will be read more often than written, most of ACAP's magic is encapsulated in this command.
The SEARCH command allows a user to perform searches across datasets. The searches may be very simple or very complex. Comparison operators (described later in this section) may be used to return very specific subsets of information from a server. Search results may even be saved by use of a "context". A context is a set of entries that were returned from a search. Contexts may be further searched by subsequent commands. So what, you say? With ACAP, changes to the information that makes up a context can even be reported automatically to the user! If someone else changes the data on the server while you are working with it, ACAP's context concept provides a mechanism for you to be informed of the changes.
The SEARCH command takes a number of complex-looking arguments. Don't worry about the amount of information; it is easy to construct a simple search and work up to complex searches as the command's syntax becomes more clear. The arguments are: a dataset or context name, a list of optional modifiers, and some search criteria.
For each successful search, a server will return a number of ENTRY intermediate responses, followed by a MODTIME response (which provides the time at which the search results were valid). If a search returns no information (the empty set), only the MODTIME response is given. Let's look at a very simple search:Client: GH38 SEARCH "/addressbook/user/ralph/Sue_Hoyle" RETURN ("Contact.Email") ALL Server: GH38 ENTRY "/addressbook/user/ralph/Sue_Hoyle" "firstname.lastname@example.org" Server: GH38 MODTIME "1999062412324812" Server: GH38 OK "SEARCH completed"
In this example, we searched Ralph's address book entry for
Sue_Hoyle. We wanted to return Sue's email address. So, first we named the entry,
/addressbook/user/ralph/Sue_Hoyle, then we gave a modifier stating that we just wanted the email address that was held in an attribute,
RETURN ("Contents.Email"), and then we gave some search criteria. In this case, the criteria
ALLwas used to return everything that matched. The server gave us a tagged ENTRY line that named the entry and the email address, followed by a MODTIME line that told us the time at which the data was known to be correct.
If Sue didn't have an email address, the preceding search would not have returned anything. The results would have looked like this:Client: GH38 SEARCH "/addressbook/user/ralph/Sue_Hoyle" RETURN ("Contact.Email") ALL Server: GH38 MODTIME "1999062412324812" Server: GH38 OK "SEARCH completed"
Naturally, there are many more search modifiers than RETURN and many more search criteria than ALL. That's where the fun begins. The infinite possible combinations of modifiers, criteria and stored data can create a specialized search command for any occasion. Table 12.5 describes the search modifiers defined in the current version of ACAP.
Table 12.5: ACAP Search Modifiers Modifier Arguments Description DEPTH <number> Searches the number of dataset levels given. A "0" indicates that all sublevels should be searched. HARDLIMIT <number> Forces the search to fail (with a WAYTOOMANY response) if more than the given number of entries would be returned. LIMIT <number> <number> Forces the search to limit the number of entries returned to the given number. A TOOMANY response would be generated if the limit is exceeded, but the search would not fail. MAKECONTEXT [ENUMERATE] [NOTIFY] <context name> Places the results of the search into a context. If the ENUMERATE option is given, the context is enumerated. If the NOTIFY option is given, the client will receive updates when the context changes. NOINHERIT Causes the search to ignore inherited values. RETURN (<list of attributes and optionally metadata>) Specifies the attributes that are listed in intermediate ENTRY responses. SORT <list of attributes and comparators> Specifies the order in which the intermediate ENTRY responses are to be returned.
Of particular note are the DEPTH, MAKECONTEXT, and RETURN modifiers. We have already had a look at RETURN, which informs the server what attributes to return for each matching entry. The DEPTH modifier allows us to search as many sublevels of the data hierarchy as we choose.
The MAKECONTEXT modifier is used to save the results of a search into a context. If the ENUMERATE option is used, the context entries are rated. That is, they each have a number within the context. This can be used as an aid when creating complex searches, as we shall see.
Comparison operators, or comparators, may be used to define exactly how two objects compare to one another. They are used as arguments to some modifiers. Comparators in ACAP are functions that perform ordering, equality, prefix, or substring matching on two objects. So one may pass two numbers to a comparator and have it return whether they were equal and whether one was bigger than the other. Two strings passed to a comparator tells whether they were equal and whether one was alphabetically before the other, and so forth. Prefix and substring matching information is also returned. Since many variations of the SEARCH command use comparators to order search results, comparators can be prefixed with a plus sign (+) (indicating that the order should be normal) or minus sign (-) (indicating that the order should be reversed).
ACAP servers must implement at least three comparators, called "i;octet", "i;ascii-casemap", and "i;ascii-numeric". The i;octet comparator treats its input as a series of unsigned octets and compares them to determine their equality, and so on. The i;ascii-casemap comparator translates all ASCII letters to uppercase and then does an i;octet comparison (so that "EMail" and "email" are equal). The i;ascii-numeric comparator interprets strings as ASCII digits.
Search criteria are the companions to search modifiers. Where modifiers affect the form of the search results, search criteria narrow the search so that only the desired data is returned. Typical boolean expressions such as AND, OR, and NOT are legal ACAP search criteria, as are the more esoteric PREFIX and SUBSTRING, which allow searches on entries where only part of the value is being matched.
Take note of the RANGE criterion. If a context is enumerated, RANGE allows one to pull out specific ranges from it. This means, for example, that one may order a context as one sees fit (with the SORT modifier) and then return the top (or bottom) few entries using the RANGE criterion.
Table 12.6 shows each of the ACAP search criteria.
So, knowing a bit more about searching, our friend Ralph could search his address book for any entries that contained email addresses. The result might look like this:
Table 12.6: ACAP Search Criteria Operator Arguments Description ALL None Causes every entry to be returned. AND <search key 1> <search key 2> Returns entries that match both search keys. COMPARE <attribute> <comparator> <value> Returns entries that contain the given attribute that compare to the given value (the same or later) in the manner specified by the comparator. COMPARESTRICT <attribute> <comparator> <value> Returns entries that contain the given attribute that compare to the given value (later only) in the manner specified by the comparator. EQUAL <attribute> <comparator> <value> Returns entries that contain the given attribute that are equal to the given value. NOT <search key> Returns entries that do not match the given search key. OR <search key 1> <search key 2> Returns entries that match either of the search keys. PREFIX <attribute> <comparator> <value> Returns entries that contain the given attribute that begins with the given value in the manner specified by the comparator. RANGE <start number> <end number> <modtime> Returns entries from an enumerated context that have numbers between the two given numbers, as the context existed at the time given. SUBSTRING <attribute> <comparator> <value> Returns entries that contain the given attribute that contains the given value in the manner specified by the comparator.We have added a DEPTH modifier to make sure that entries one level down (two total levels) are searched. Also, the search criteria is more interesting. Here, we searched for only those entries that had an attribute calledClient: GH39 SEARCH "/addressbook/user/ralph/" DEPTH 2 RETURN ("Contact.Email") NOT EQUAL "Email" "i;octet" NIL Server: GH39 ENTRY "/addressbook/user/ralph/Sue_Hoyle" "email@example.com" Server: GH39 ENTRY "/addressbook/user/ralph/Justin_Case" "firstname.lastname@example.org" Server: GH39 ENTRY "/addressbook/user/ralph/Zelda_O'Brien" "email@example.com" Server: GH39 MODTIME "1999062412382399" Server: GH39 OK "SEARCH completed"
The real power of ACAP searching takes place in the context of a context. How's that for polymorphism? Contexts can be created by using the MAKECONTEXT modifier. If we wish to save the results of a search so that we can search again on the results, this is the way to do it. This is especially handy for searches that return many, many entries.
Suppose, for example, that we repeat our last search, but this time we save the results to a context. We will have to name the context, so we'll call it "people_with_email":Client: GH40 SEARCH "/addressbook/user/ralph/" DEPTH 2 RETURN ("Contact.Email") MAKECONTEXT ENUMERATE "people_with_email" NOT EQUAL "Contact.Email" "i;octet" NIL Server: GH40 ENTRY "/addressbook/user/ralph/Sue_Hoyle" "firstname.lastname@example.org" Server: GH40 ENTRY "/addressbook/user/ralph/Justin_Case" "email@example.com" Server: GH40 ENTRY "/addressbook/user/ralph/Zelda_O'Brien" "firstname.lastname@example.org" Server: GH40 MODTIME "1999062412385473" Server: GH40 OK "Context 'people_with_email' created"
We now have a context, stored on the server, called "people_with_email". It contains the entries shown in the results of the last example. The context was enumerated, so each of the entries have numbers that can be used in a later search. We could have also asked the server to tell us when updates to entries in the context occur (with the NOTIFY option to MAKECONTEXT), but we'll leave that for now. Notifications of information of changing contexts are covered in the discussion of the UPDATECONTEXT command, later in this section.
If we now search our context, we can (for example) get just the first entry. For that, we use the RANGE modifier telling it to return from the first entry to the first entry:Client: GH41 SEARCH "people_with_email" RANGE 1 1 ALL Server: GH41 ENTRY "/addressbook/user/ralph/Sue_Hoyle" "email@example.com" Server: GH41 MODTIME "1999062412394812" Server: GH41 OK "SEARCH completed"
It was not necessary to give a RETURN modifier in this example because the context holds only the partial entries that were in the last results.
The FREECONTEXT command causes the server to remove context named as an argument. If the server is sending update notifications to a client (see UPDATECONTEXT, later in this section) for this context, they will cease. For example:Client: GH42 FREECONTEXT "people_without_email" Server: GH42 OK "FREECONTEXT completed"
The UPDATECONTEXT command may be used to cause a server to notify the client when any attributes within a context change. It takes a context name as an argument. This is a powerful concept: once a SEARCH command with a MAKECONTEXT modifier creates a context, a client can monitor the state of every entry in the context!
A client would request notifications for a context called "people_with_email" like this:Client: GH43 UPDATECONTEXT "people_with_email" Server: GH43 OK "UPDATECONTEXT completed"
From this point on, any changes to the context "people_with_email" will be sent to the client. The server can send these updates at any time. The only way to stop notifications from being sent is for the client to issue a FREECONTEXT command. Whenever an entry in the context changes, the server sends an untagged response to the client with the new information. The untagged server responses may be of these types:
Indicates that an entry has been added to the context
Indicates either that an entry's position in the context has changed or that a watched attribute has changed within the entry
Indicates that an entry has been deleted from the context
Indicates that the client has received all changes up to a given time
An ADDTO server response contains the context name, the entry name, the entry's position in the context, and a list of metadata. An ADDTO response that informs a client that an entry called "Mary Ryan" had been added might look like this:Server: * ADDTO "people_with_email" "Mary Ryan" 4 ("Contact.Telephone" "+61 7 3874 1583" "Contact.Email" "firstname.lastname@example.org")
In the preceding example, the entry is in the fourth position in the context. If the telephone number in the entry was now changed, the CHANGE response would be used. The CHANGE response lists the context name, the entry name, the entry's old position in the context, the entry's new position, and the metadata list.Server: * CHANGE "people_with_email" "Mary Ryan" 4 4 ("Contact.Telephone" "+61 7 3876 8457")
If the preceding entry was now deleted, the client would get a message that looks like this:Server: * REMOVEFROM "people_with_email" "Mary Ryan" 4
The REMOVEFROM response shows the context name, the entry name, and the entry's old position in the context.
At any time the server may send a MODTIME response, although this is generally only done when the client sends a new UPDATECONTEXT command. The MODTIME response is used to inform that client that it has all updates as of a certain time. Here is an example that shows the client issuing a new UPDATECONTEXT command:Client: GH43 UPDATECONTEXT "people_with_email" Server: * MODTIME "people_with_email" "19990624095600" Server: GH43 OK "UPDATECONTEXT completed"
Note that clients are not supposed to use the UPDATECONTEXT command to poll servers for updates; they should generally wait for the servers to send them in their own time. A client might issue a command like the preceding one on startup, however, or at similar times in order to ensure expected behavior.
The second of the two ACAP "magic" commands is STORE. It allows a client to write information to a server. This includes both creation or modification of datasets and their entries. Since the storing of a NIL value in an entry is the same as deleting it, STORE is also used to delete entries. Note that if any part of a STORE command fails, the entire request will fail; that is, nothing will be stored if an error occurs.
The arguments to the STORE command can look rather complex, but they are easily parsable. The proposed standard calls the arguments an entry store list. An entry store list begins with an entry path (a dataset or an attribute), followed by optional modifiers followed by attribute name-value pairs. Each of these is described in detail in this section.
STORE modifiers are either NOCREATE (which will generate an error if any of the attributes to be added do not already exist) and UNCHANGEDSINCE "<time>" (which will generate an error if any of the attributes to be added have been changed by someone else since the given time).
Attribute name-value pairs give a series of attribute names and the values to store in them. A value of NIL will cause the attribute to be deleted from the entry.
Setting the value of an "entry" attribute to NIL will cause the entry to be deleted from the dataset. Since a dataset is also an entry, setting the "entry" attribute of a dataset to NIL will cause the dataset to be deleted from its parent dataset. Setting an "entry" attribute value to DEFAULT effectively deletes all information held in the entry and then fills the entry with whatever it can inherit from its parent.
Let's look at the address book example again. If we wish to delete an entry from the address book, it can be done like this:Client: GH44 STORE ("/addressbook/user/ralph/Sue_Hoyle" "entry" NIL) Server: GH44 OK "STORE completed"
A subsequent SEARCH for that entry would show that it no longer existed. If, on the other hand, we wished to add a new entry to the address book that held a person's name and email address, we could do it like this:Client: GH45 STORE ("/addressbook/user/ralph/Phread_the_Terrorist" "Contact.CommonName" "Phread the Terrorist" "Contact.Email" "email@example.com") Server: GH45 OK "STORE completed"
The DELETEDSINCE command may be used to list entries that have been deleted from a dataset. It takes a dataset name and a time as arguments. The entries that have been deleted since the given time are listed in intermediate DELETED lines by the server.
The time given is in the same format as modtime (YYYYMMDDHHMMSSFF in UTC).
If we wanted to see all of the entries that have been deleted from an address book since midnight on June 24, 1999, a client might issue a command like this:Client: GH46 DELETEDSINCE "/addressbook/user/ralph" 19990624000000 Server: GH46 OK "DELETEDSINCE completed"
If the server doesn't have a list of deleted entries that goes back to the time given, it will report an error like this using the TOOOLD server response code:Client: GH47 DELETEDSINCE "/addressbook/user/ralph" 19970101000000 Server: GH47 NO (TOOOLD) "We trashed that data long ago"
The SETACL command may be used to change the ACL for a particular object. It takes an ACL object, an authentication identifier (e.g., username), and a list of access rights as arguments. The user issuing this command must have administer privileges on the given object.If a user with the appropriate rights wanted to set the access control list for an address book in the dataset /addressbook/user/ralph so that anyone could read or write information in it, the client would do something like this:Client: GH48 SETACL ("/addressbook/user/ralph") "anyone" "rw" Server: GH48 OK "SETACL completed"
If the user does not have administer privileges, the SETACL will fail. Suppose the user "dwood" tried to modify the address book owned by "ralph":Client: GH49 SETACL ("/addressbook/user/ralph") "anyone" "rw" Server: GH49 NO (PERMISSION ("/addressbook/user/ralph")) "'dwood' not permitted to modify access rights for '/addressbook/user/ralph'"
Note that this example shows the PERMISSION server response code.
The DELETEACL command may be used to either delete an access control list for an object or to delete a particular user's rights within an ACL. It takes an ACL object and (optionally) an authentication identifier. If an identifier is given, it deleted all rights for that user in the ACL. Otherwise, it deletes the entire ACL.
For example, if a user wanted to ensure that the user "ralph" had no rights at all on the address book /addressbook/user/margaret, a client might issue a command like this:Client: GH50 DELETEACL ("/addressbook/user/margaret") "ralph" Server: GH50 OK "DELETEACL completed"
You may recall from the discussion of ACLs that if an object doesn't have an ACL, it inherits user rights from parent objects farther up the hierarchy. For this reason, one is not allowed to delete a default ACL for a dataset. Therefore, if the preceding example didn't have the authentication identifier, it would have failed (because /addressbook/user/margaret is a dataset). If you tried to do that, it would look like this:Client: GH51 DELETEACL ("/addressbook/user/margaret") Server: GH51 BAD "Can't delete ACL from dataset"
The MYRIGHTS command can be used to show what rights a user has been granted for a particular dataset or entry. It takes an ACL object as an argument. That is, the argument names a dataset or entry, but the server reads that dataset's or entry's ACL when processing the command. The MYRIGHTS tagged intermediate response is returned by the server, which lists the client's rights for the given dataset. This command is related to the LISTRIGHTS command, described subsequently, which lists which rights the client may change for the given dataset.We would expect to have at least read and administer rights for entries in personally created datasets. Here is an example of a MYRIGHTS command on a personal address book that shows that we have read, write, insert, and administer rights on it:Client: GH52 MYRIGHTS ("/addressbook/user/dwood") Server: GH52 MYRIGHTS "rwia" Server: GH52 OK "MYRIGHTS completed"
A similar request on a public, shared dataset (say, a dataset holding system-wide configuration information) may return more restricted rights. In this example, the client may only read the information:Client: GH53 MYRIGHTS ("/options/global/") Server: GH53 MYRIGHTS "r" Server: GH53 OK "MYRIGHTS completed"
The LISTRIGHTS command can be used to determine what rights a user can change for a certain dataset or entry. It takes both an ACL object and an authentication identifier (e.g., a username) as arguments. The server returns a tagged LISTRIGHTS response that lists the rights that will always be granted to that user for that object and the rights that the user currently may change. Rights in the response are not duplicated, so if read permission is always granted, it will not also appear in the user's specific rights list.
For example, if a user "margaret" will always be granted implicit read and administer rights for personally created items and she also has permission to modify search and write rights on her personal address book, the command and response might look like this:Client: GH54 LISTRIGHTS ("/addressbook/user/margaret") "margaret" Server: GH54 LISTRIGHTS "ra" "x" "w" Server: GH54 OK "LISTRIGHTS completed"
This shows that Margaret is not allowed to give herself or anyone else insert rights. That is, she can change the data that is there but cannot create new entries. This is probably not realistic for a personal address book!
The GETQUOTA command can be used to determine how much storage space a user has available on a server. It takes a dataset as an argument and returns the client's quota limit (in bytes) and usage for that dataset. For example:Client: GH55 GETQUOTA "/addressbook/user/dwood" Server: * QUOTA "/addressbook/user/dwood" 2097152 1808728 Server: GH55 OK "GETQUOTA completed"
In this example, the client asked for quota information on a dataset representing a personal address book. The server responded with an untagged response that repeated the name of the dataset, then listed the client's quota (2 MB in this case) and the amount of storage space the client is currently using (1.8 MB or so). The client can only store about 200 KB before running out of room.
Table 12.7 lists the ACAP commands that are valid in the Authenticated State.
Table 12.7: ACAP Commands Valid in the Authenticated State Command Arguments Description Response SEARCH dataset or context name, optional modifiers and search criteria Returns a list of entries on a server that matches the search criteria. Intermediate responses: ENTRY, MODTIME, REFER; untagged responses: ADDTO, REMOVEFROM, CHANGE, MODTIME FREECONTEXT context name Requests that the server remove the named context. The context will no longer be searchable. None UPDATECONTEXT list of context names Requests that the server notify the client of any changes to the given contexts. Untagged responses: ADDTO, REMOVEFROM, CHANGE, MODTIME STORE entry store list Requests that the server create, modify or delete the given entries. Intermediate response: ENTRY DELETEDSINCE dataset name and time Requests that the server return a list of entries that have been deleted from the given dataset since the given time. Intermediate response: DELETED SETACL ACL object, authentication identifier and access rights. Requests a change in the access control list for the given object. None DELETEACL ACL object and optional authentication identifier Requests the removal of all or part of the access control list for the given object. None MYRIGHTS ACL object Requests the rights of the client on the given object. Intermediate response: MYRIGHTS LISTRIGHTS ACL object, authentication identifier Requests the rights that the client may change on the given object. Untagged response: LISTRIGHTS GETQUOTA dataset Requests the client's quota limit and usage for the given dataset. Untagged response: QUOTA
12.3 ACAP Sessions
An example ACAP session is shown in Figure 12.3. A client initiates an IMAP session to a server, which responds with a banner greeting. The client is now in the Nonauthenticated State since no authentication credentials have been presented. Following a successful login with CRAM-MD5 authentication, the session enters the Authenticated State.
Figure 12.3: A sample ACAP session
The first command issued in the Authenticated State is a SEARCH. The search looks for entries that have an email address in an attribute, much like the one shown in the SEARCH example, given previously. This time, however, a context is created with the NOTIFY modifier. Any changes to the context should be communicated to the client. This is tested by adding an entry (using a STORE command) incorporating another email address.
As soon as another entry is added that contains an email address, the server notifies the client (with the ADDTO response). We then free the context and log out.
Back to: Programming Internet Email
© 2001, O'Reilly & Associates, Inc.