
(1) Given a location, e.g. "pub.gale@ofb.net", construct the list of
candidate key names for this location:
	pub.gale@ofb.net
	pub.*@ofb.net
	*@ofb.net
There will always be N+1 candidate key names for a location with N local
parts (e.g., "pub" and "gale").

(2) In parallel, look up each of these candidate key names.  Each candidate
key will either return a positive reply, a negative reply, or no reply
(timeout, treated equivalently to negative reply).  If no positive replies
are received, the location does not exist and an error should be reported.
If more than one positive reply is received, one may be picked at random
(alternatively an error may be reported).

(3) In this positive key reply, look for the key.redirect field.  If it
exists, its value must be merged with the key name and the original
location to produce a new location.  First, count the number of components
in the local part of the key name, excluding any trailing '*'.  Remove that
many components from the beginning of the local part of the original
location.  Also remove the domain from the original location.  You will be
left with zero or more components from the end of the local part of the
original location.  Append this string to the local part of the location
stored in the key.redirect fragment.  The location thus produced is the
merged location.  Go to step (1) and repeat the process with the merged
location.  Use the merged location rather than the original location for
all further purposes when dealing with this message (such as category
generation, UI presentation, etc).

(4) If there is no key.redirect, then the key MUST contain either one or
more key.member fields; or the three fields rsa.modulus, rsa.bits, and
rsa.exponent; or both key.member and the rsa fields.  If there is no
key.member field present, then encrypt the puff using this public key. If
both types of fields are present, the puff must be encrypted to this public
key as well as the keys specified in the key.member field(s).  If
key.member is present but not the rsa fields, then encrypt only to the keys
listed in the key.member fragments.

(5) Each key.member field has a value whose contents are another key name,
or the empty string.  If the empty string is found, this means that the
puff should not be encrypted; processing may terminate here.  Otherwise,
construct the set of keys named in the key.member fields.  Look up each of
these keys in parallel, starting from step (2).  Each request must
terminate with either a valid public key, or the empty string.  When the
transitive closure is complete, the result will be either the empty string
or a set of public keys.  If it is the empty string, do not encrypt the
puff; otherwise, encrypt the puff to this list of keys.

This is a graph traversal; there may be cycles and shared structures.  If a
key contains a key.redirect fragment, the fragment is to be ignored during
this graph traversal.

Key fragments:
message/recipient - deprecated; used to set a friendly name for the
	recipient of the message
question.receipt - supercedes question/receipt; must be set to the location
	to which receipts should be sent
answer.receipt - supercedes answer/receipt; set to Gale ID of user sending
	receipt; used by clients to determine whether a puff is a return
	receipt or not
question.key - used in an AKD request; supercedes question/key; the local
	part of the key name must not be reversed
question/key - used in an AKD request; obsolete; the local part of the key
	name must be reversed
answer/key - response to old-style or new-style AKD request
answer.key - some key responses use this instead of answer/key
answer/key/error - error result for AKD request (old or new)

Special locations:
_gale.key.keyname - the location on which to answer AKD requests
_gale.query.keyname - the location in which to make AKD requests
_gale.notice.keyname - the location for presence notices for this user

Look up a public key named top.sub@dom in the disk cache in this order:
	~/auth/trusted/top.sub@dom.gpub
	~/auth/local/top.sub@dom.gpub
	~/auth/cache/top.sub@dom.gpub
	GALE_SYS_DIR/auth/trusted/top.sub@dom.gpub
	GALE_SYS_DIR/auth/local/top.sub@dom.gpub
	GALE_SYS_DIR/auth/cache/top.sub@dom.gpub
	~/auth/trusted/sub.top@dom
	~/auth/local/sub.top@dom
	~/auth/cache/sub.top@dom
	GALE_SYS_DIR/auth/trusted/sub.top@dom
	GALE_SYS_DIR/auth/local/sub.top@dom
	GALE_SYS_DIR/auth/cache/sub.top@dom
Look up a private key top.sub@dom in this order:
	~/auth/private/top.sub@dom.gpri
	GALE_SYS_DIR/auth/private/top.sub@dom.gpri
	~/auth/private/sub.top@dom
	GALE_SYS_DIR/auth/private/sub.top@dom

RLE encoding:
RLE data is a sequence of "chunks".  Each chunk is a single "control byte"
followed by one or more data bytes.  If the control byte has the high bit
set (0x80), then it is followed by N+1 data bytes, where N is the value of
the control byte with the high bit stripped.  Those N+1 data bytes should
be copied literally.  If the control byte does not have the high bit set,
then it is followed by a single data byte.  That data byte should be
repeated N+1 times in the output, where N is the value of the control byte.

UNAUTHORIZED LOCATIONS:
Resolve a location to a set of keys in the standard way.  If the set is
empty, then the location in INVALID.  If the set contains the empty string
(i.e. at least one key.member is "") or the user has access to at least one
of the private keys present in the set, then the location is VALID.
Otherwise, the user does not have one of the private keys listed in the
set, and the location is UNAUTHORIZED.

HEURISTICS FOR PRIVATE PUFFS:

If the set of encryption recipients does not include the sender's id,
then it is a "private" puff.  "Private" puffs request return receipts
and get logged to the private screen (see bug 93).  In addition,
replies to "private" puffs go to the sender by default, whereas
replies to non-private puffs go to the location by default.  Group
locations of which you are a member are thus treated as non-private
puffs, even though they are encrypted.

This was made obsolete by private group keys.  A better heuristic:
"puff is encrypted and I do not own the private key for any of
the keys it's encrypted to".

On puff receipt, here's how to choose to beep or not:
	if encrypted_recipients and signer and signer not in encrypted_recipients
