/// <summary>
        /// This method implements GET processing for user's appearance.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>

        private void DoGet(AppearanceRequestData rdata)
        {
            rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);

            if (rdata.userAppearance == null)
            {
                rdata.Fail(Rest.HttpStatusCodeNoContent,
                           String.Format("appearance data not found for user {0} {1}",
                                         rdata.userProfile.FirstName, rdata.userProfile.SurName));
            }

            rdata.initXmlWriter();

            FormatUserAppearance(rdata);

            // Indicate a successful request

            rdata.Complete();

            // Send the response to the user. The body will be implicitly
            // constructed from the result of the XML writer.

            rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
        }
        /// <summary>
        /// This method implements GET processing for user's appearance.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>

        private void DoGet(AppearanceRequestData rdata)
        {

            rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);

            if (rdata.userAppearance == null)
            {
                rdata.Fail(Rest.HttpStatusCodeNoContent,
                    String.Format("appearance data not found for user {0} {1}", 
                      rdata.userProfile.FirstName, rdata.userProfile.SurName));
            }

            rdata.initXmlWriter();

            FormatUserAppearance(rdata);

            // Indicate a successful request

            rdata.Complete();

            // Send the response to the user. The body will be implicitly
            // constructed from the result of the XML writer.

            rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
        }
        /// <summary>
        /// This method is registered with the handler when this service provider
        /// is initialized. It is called whenever the plug-in identifies this service
        /// provider as the best match for a given request.
        /// It handles all aspects of inventory REST processing, i.e. /admin/inventory
        /// </summary>
        /// <param name=hdata>A consolidated HTTP request work area</param>

        private void DoAppearance(RequestData hdata)
        {
            AppearanceRequestData rdata = (AppearanceRequestData)hdata;

            Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId);

            // If we're disabled, do nothing.

            if (!enabled)
            {
                return;
            }

            // Now that we know this is a serious attempt to
            // access inventory data, we should find out who
            // is asking, and make sure they are authorized
            // to do so. We need to validate the caller's
            // identity before revealing anything about the
            // status quo. Authenticate throws an exception
            // via Fail if no identity information is present.
            //
            // With the present HTTP server we can't use the
            // builtin authentication mechanisms because they
            // would be enforced for all in-bound requests.
            // Instead we look at the headers ourselves and
            // handle authentication directly.

            try
            {
                if (!rdata.IsAuthenticated)
                {
                    rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated", rdata.userName));
                }
            }
            catch (RestException e)
            {
                if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
                {
                    Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
                    Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
                }
                else
                {
                    Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
                    Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
                }
                throw (e);
            }

            Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);

            // We can only get here if we are authorized
            //
            // The requestor may have specified an UUID or
            // a conjoined FirstName LastName string. We'll
            // try both. If we fail with the first, UUID,
            // attempt, we try the other. As an example, the
            // URI for a valid inventory request might be:
            //
            // http://<host>:<port>/admin/inventory/Arthur Dent
            //
            // Indicating that this is an inventory request for
            // an avatar named Arthur Dent. This is ALL that is
            // required to designate a GET for an entire
            // inventory.
            //

            // Do we have at least a user agent name?

            if (rdata.Parameters.Length < 1)
            {
                Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId);
                rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
            }

            // The first parameter MUST be the agent identification, either an UUID
            // or a space-separated First-name Last-Name specification. We check for
            // an UUID first, if anyone names their character using a valid UUID
            // that identifies another existing avatar will cause this a problem...

            try
            {
                rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
                Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
                rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
            }
            catch
            {
                string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
                if (names.Length == 2)
                {
                    Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
                    rdata.userProfile = Rest.UserServices.GetUserProfile(names[0], names[1]);
                }
                else
                {
                    Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
                    rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
                }
            }

            // If the user profile is null then either the server is broken, or the
            // user is not known. We always assume the latter case.

            if (rdata.userProfile != null)
            {
                Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}",
                                     MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
            }
            else
            {
                Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path);
                rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
            }

            // If we get to here, then we have effectively validated the user's

            switch (rdata.method)
            {
            case Rest.HEAD:        // Do the processing, set the status code, suppress entity
                DoGet(rdata);
                rdata.buffer = null;
                break;

            case Rest.GET:         // Do the processing, set the status code, return entity
                DoGet(rdata);
                break;

            case Rest.PUT:         // Update named element
                DoUpdate(rdata);
                break;

            case Rest.POST:        // Add new information to identified context.
                DoExtend(rdata);
                break;

            case Rest.DELETE:      // Delete information
                DoDelete(rdata);
                break;

            default:
                Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
                                    MsgId, rdata.method, rdata.path);
                rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
                           String.Format("{0} not supported", rdata.method));
                break;
            }
        }