/// <summary>
        /// Arguably the most damaging REST interface. It deletes the inventory
        /// item or folder identified by the URI.
        ///
        /// We only process if the URI identified node appears to exist
        /// We do not test for success because we either get a context,
        /// or an exception is thrown.
        ///
        /// Folders are deleted by moving them to another folder and then
        /// purging that folder. We'll do that by creating a temporary
        /// sub-folder in the TrashCan and purging that folder's
        /// contents. If we can't can it, we don't delete it...
        /// So, if no trashcan is available, the request does nothing.
        /// Items are summarily deleted.
        ///
        /// In the interests of safety, a delete request should normally
        /// be performed using UUID, as a name might identify several
        /// elements.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>

        private void DoDelete(InventoryRequestData rdata)
        {
            Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, false);

            if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
                typeof(InventoryFolderImpl) == InventoryNode.GetType())
            {
                InventoryFolderBase TrashCan = GetTrashCan(rdata);

                InventoryFolderBase folder = (InventoryFolderBase) InventoryNode;
                Rest.Log.DebugFormat("{0} {1}: Folder {2} will be deleted",
                                     MsgId, rdata.method, rdata.path);
                folder.ParentID = TrashCan.ID;
                Rest.InventoryServices.MoveFolder(folder);
                Rest.InventoryServices.PurgeFolder(TrashCan);

                rdata.appendStatus(String.Format("<p>Deleted folder {0} UUID {1} <p>", folder.Name, folder.ID));
            }

            // Deleting items is much more straight forward.

            else
            {
                InventoryItemBase item = (InventoryItemBase) InventoryNode;
                Rest.Log.DebugFormat("{0} {1}: Item {2} will be deleted",
                                     MsgId, rdata.method, rdata.path);
                List<UUID> uuids = new List<UUID>();
                uuids.Add(item.ID);
                Rest.InventoryServices.DeleteItems(item.Owner, uuids);
                rdata.appendStatus(String.Format("<p>Deleted item {0} UUID {1} <p>", item.Name, item.ID));
            }

            rdata.Complete();
            rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method));
        }
        /// <summary>
        /// In the case of the inventory, and probably in general,
        /// the distinction between PUT and POST is not always
        /// easy to discern. The standard is badly worded in places,
        /// and adding a node to a hierarchy can be viewed as
        /// an addition, or as a modification to the inventory as
        /// a whole. This is exacerbated by an unjustified lack of
        /// consistency across different implementations.
        ///
        /// For OpenSim PUT is an update and POST is an addition. This
        /// is the behavior required by the HTTP specification and
        /// therefore as required by REST.
        ///
        /// The best way to explain the distinction is to
        /// consider the relationship between the URI and the
        /// enclosed entity. For PUT, the URI identifies the
        /// actual entity to be modified or replaced, i.e. the
        /// enclosed entity.
        ///
        /// If the operation is POST,then the URI describes the
        /// context into which the new entity will be added.
        ///
        /// As an example, suppose the URI contains:
        ///      /admin/inventory/Clothing
        ///
        /// A PUT request will normally result in some modification of
        /// the folder or item named "Clothing". Whereas a POST
        /// request will normally add some new information into the
        /// content identified by Clothing. It follows from this
        /// that for POST, the element identified by the URI MUST
        /// be a folder.
        /// </summary>

        /// <summary>
        /// POST adds new information to the inventory in the
        /// context identified by the URI.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        private void DoExtend(InventoryRequestData rdata)
        {
            bool  created  = false;
            bool  modified = false;
            string newnode = String.Empty;

            // Resolve the context node specified in the URI. Entity
            // data will be ADDED beneath this node. rdata already contains
            // information about the current content of the user's
            // inventory.

            Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill);

            // Processing depends upon the type of inventory node
            // identified in the URI. This is the CONTEXT for the
            // change. We either got a context or we threw an
            // exception.

            // It follows that we can only add information if the URI
            // has identified a folder. So only a type of folder is supported
            // in this case.

            if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
                typeof(InventoryFolderImpl) == InventoryNode.GetType())
            {
                // Cast the context node appropriately.

                InventoryFolderBase context    = (InventoryFolderBase) InventoryNode;

                Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}",
                                     MsgId, rdata.method, rdata.path);

                // Reconstitute the inventory sub-tree from the XML supplied in the entity.
                // The result is a stand-alone inventory subtree, not yet integrated into the
                // existing tree. An inventory collection consists of three components:
                // [1] A (possibly empty) set of folders.
                // [2] A (possibly empty) set of items.
                // [3] A (possibly empty) set of assets.
                // If all of these are empty, then the POST is a harmless no-operation.

                XmlInventoryCollection entity = ReconstituteEntity(rdata);

                // Inlined assets can be included in entity. These must be incorporated into
                // the asset database before we attempt to update the inventory. If anything
                // fails, return a failure to requestor.

                if (entity.Assets.Count > 0)
                {
                    Rest.Log.DebugFormat("{0} Adding {1} assets to server",
                                         MsgId, entity.Assets.Count);

                    foreach (AssetBase asset in entity.Assets)
                    {
                        Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}",
                                             MsgId, asset.ID, asset.Type, asset.Name);
                        Rest.AssetServices.Store(asset);

                        created = true;
                        rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>",
                                        asset.Name, asset.ID));

                        if (Rest.DEBUG && Rest.DumpAsset)
                        {
                            Rest.Dump(asset.Data);
                        }
                    }
                }

                // Modify the context using the collection of folders and items
                // returned in the XmlInventoryCollection.

                foreach (InventoryFolderBase folder in entity.Folders)
                {
                    InventoryFolderBase found;

                    // If the parentID is zero, then this folder is going
                    // into the root folder identified by the URI. The requestor
                    // may have already set the parent ID explicitly, in which
                    // case we don't have to do it here.

                    if (folder.ParentID == UUID.Zero || folder.ParentID == context.ID)
                    {
                        if (newnode != String.Empty)
                        {
                            Rest.Log.DebugFormat("{0} Too many resources", MsgId);
                            rdata.Fail(Rest.HttpStatusCodeBadRequest, "only one root entity is allowed");
                        }
                        folder.ParentID = context.ID;
                        newnode = folder.Name;
                    }

                    // Search the existing inventory for an existing entry. If
                    // we have one, we need to decide if it has really changed.
                    // It could just be present as (unnecessary) context, and we
                    // don't want to waste time updating the database in that
                    // case, OR, it could be being moved from another location
                    // in which case an update is most certainly necessary.

                    found = null;

                    foreach (InventoryFolderBase xf in rdata.folders)
                    {
                        // Compare identifying attribute
                        if (xf.ID == folder.ID)
                        {
                            found = xf;
                            break;
                        }
                    }

                    if (found != null && FolderHasChanged(folder,found))
                    {
                        Rest.Log.DebugFormat("{0} Updating existing folder", MsgId);
                        Rest.InventoryServices.MoveFolder(folder);

                        modified = true;
                        rdata.appendStatus(String.Format("<p> Created folder {0}, UUID {1} <p>",
                                                         folder.Name, folder.ID));
                    }
                    else
                    {
                        Rest.Log.DebugFormat("{0} Adding new folder", MsgId);
                        Rest.InventoryServices.AddFolder(folder);

                        created = true;
                        rdata.appendStatus(String.Format("<p> Modified folder {0}, UUID {1} <p>",
                                                         folder.Name, folder.ID));
                    }
                }

                // Now we repeat a similar process for the items included
                // in the entity.

                foreach (InventoryItemBase item in entity.Items)
                {
                    InventoryItemBase found = null;

                    // If the parentID is zero, then this is going
                    // directly into the root identified by the URI.

                    if (item.Folder == UUID.Zero)
                    {
                        item.Folder = context.ID;
                    }

                    // Determine whether this is a new item or a
                    // replacement definition.

                    foreach (InventoryItemBase xi in rdata.items)
                    {
                        // Compare identifying attribute
                        if (xi.ID == item.ID)
                        {
                            found = xi;
                            break;
                        }
                    }

                    if (found != null && ItemHasChanged(item, found))
                    {
                        Rest.Log.DebugFormat("{0} Updating item {1} {2} {3} {4} {5}",
                                             MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name);
                        Rest.InventoryServices.UpdateItem(item);
                        modified = true;
                        rdata.appendStatus(String.Format("<p> Modified item {0}, UUID {1} <p>", item.Name, item.ID));
                    }
                    else
                    {
                        Rest.Log.DebugFormat("{0} Adding item {1} {2} {3} {4} {5}",
                                             MsgId, item.ID, item.AssetID, item.InvType, item.AssetType, item.Name);
                        Rest.InventoryServices.AddItem(item);
                        created = true;
                        rdata.appendStatus(String.Format("<p> Created item {0}, UUID {1} <p>", item.Name, item.ID));
                    }
                }

                if (created)
                {
                    // Must include a location header with a URI that identifies the new resource.
                    rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}/{3}",
                             rdata.hostname, rdata.port,rdata.path,newnode));
                    rdata.Complete(Rest.HttpStatusCodeCreated);
                }
                else
                {
                    if (modified)
                    {
                        rdata.Complete(Rest.HttpStatusCodeOK);
                    }
                    else
                    {
                        rdata.Complete(Rest.HttpStatusCodeNoContent);
                    }
                }

                rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method));
            }
            else
            {
                Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}",
                                     MsgId, rdata.method, rdata.path, InventoryNode.GetType());
                rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid resource context");
            }
        }
        /// <summary>
        /// PUT updates the URI-identified element in the inventory. This
        /// is actually far more flexible than it might at first sound. For
        /// PUT the URI serves two purposes:
        ///     [1] It identifies the user whose inventory is to be
        ///         processed.
        ///     [2] It optionally specifies a subtree of the inventory
        ///         that is to be used to resolve any relative subtree
        ///         specifications in the entity. If nothing is specified
        ///         then the whole of the private inventory is implied.
        /// Please note that the subtree specified by the URI is only relevant
        /// to an entity containing a URI relative specification, i.e. one or
        /// more elements do not specify parent folder information. These
        /// elements will be implicitly referenced within the context identified
        /// by the URI.
        /// If an element in the entity specifies an explicit parent folder, then
        /// that parent is effective, regardless of any value specified in the
        /// URI. If the parent does not exist, then the element, and any dependent
        /// elements, are ignored. This case is actually detected and handled
        /// during the reconstitution process.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        private void DoUpdate(InventoryRequestData rdata)
        {
            int     count  = 0;
            bool  created  = false;
            bool  modified = false;

            // Resolve the inventory node that is to be modified.
            // rdata already contains information about the current
            // content of the user's inventory.

            Object InventoryNode = getInventoryNode(rdata, rdata.root, PARM_PATH, Rest.Fill);

            // As long as we have a node, then we have something
            // meaningful to do, unlike POST. So we reconstitute the
            // subtree before doing anything else. Note that we
            // etiher got a valid node or we threw an exception.

            XmlInventoryCollection entity = ReconstituteEntity(rdata);

            // Incorporate any inlined assets first. Any failures
            // will terminate the request.

            if (entity.Assets.Count > 0)
            {
                Rest.Log.DebugFormat("{0} Adding {1} assets to server",
                                     MsgId, entity.Assets.Count);

                foreach (AssetBase asset in entity.Assets)
                {
                    Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}",
                                         MsgId, asset.ID, asset.Type, asset.Name);

                    // The asset was validated during the collection process

                    Rest.AssetServices.Store(asset);

                    created = true;
                    rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.ID));

                    if (Rest.DEBUG && Rest.DumpAsset)
                    {
                        Rest.Dump(asset.Data);
                    }
                }
            }

            // The URI specifies either a folder or an item to be updated.
            //
            // The root node in the entity will replace the node identified
            // by the URI. This means the parent will remain the same, but
            // any or all attributes associated with the named element
            // will change.
            //
            // If the inventory collection contains an element with a zero
            // parent ID, then this is taken to be the replacement for the
            // named node. The collection MAY also specify an explicit
            // parent ID, in this case it MAY identify the same parent as
            // the current node, or it MAY specify a different parent,
            // indicating that the folder is being moved in addition to any
            // other modifications being made.

            if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
                typeof(InventoryFolderImpl) == InventoryNode.GetType())
            {
                bool rfound = false;
                InventoryFolderBase uri = (InventoryFolderBase) InventoryNode;
                InventoryFolderBase xml = null;

                // If the entity to be replaced resolved to be the root
                // directory itself (My Inventory), then make sure that
                // the supplied data include as appropriately typed and
                // named folder. Note that we can;t rule out the possibility
                // of a sub-directory being called "My Inventory", so that
                // is anticipated.

                if (uri == rdata.root)
                {
                    foreach (InventoryFolderBase folder in entity.Folders)
                    {
                        if ((rfound = (folder.Name == PRIVATE_ROOT_NAME)))
                        {
                            if ((rfound = (folder.ParentID == UUID.Zero)))
                                break;
                        }
                    }

                    if (!rfound)
                    {
                        Rest.Log.DebugFormat("{0} {1}: Path <{2}> will result in loss of inventory",
                                             MsgId, rdata.method, rdata.path);
                        rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid inventory structure");
                    }
                }

                // Scan the set of folders in the entity collection for an
                // entry that matches the context folder. It is assumed that
                // the only reliable indicator of this is a zero UUID (using
                // implicit context), or the parent's UUID matches that of the
                // URI designated node (explicit context). We don't allow
                // ambiguity in this case because this is POST and we are
                // supposed to be modifying a specific node.
                // We assign any element IDs required as an economy; we don't
                // want to iterate over the fodler set again if it can be
                // helped.

                foreach (InventoryFolderBase folder in entity.Folders)
                {
                    if (folder.ParentID == uri.ParentID ||
                        folder.ParentID == UUID.Zero)
                    {
                        folder.ParentID = uri.ParentID;
                        xml = folder;
                        count++;
                    }
                }

                // More than one entry is ambiguous. Other folders should be
                // added using the POST verb.

                if (count > 1)
                {
                    Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous",
                                         MsgId, rdata.method, rdata.path);
                    rdata.Fail(Rest.HttpStatusCodeConflict, "context is ambiguous");
                }

                // Exactly one entry means we ARE replacing the node
                // identified by the URI. So we delete the old folder
                // by moving it to the trash and then purging it.
                // We then add all of the folders and items we
                // included in the entity. The subtree has been
                // modified.

                if (count == 1)
                {
                    InventoryFolderBase TrashCan = GetTrashCan(rdata);

                    // All went well, so we generate a UUID is one is
                    // needed.

                    if (xml.ID == UUID.Zero)
                    {
                        xml.ID = UUID.Random();
                    }

                    uri.ParentID = TrashCan.ID;
                    Rest.InventoryServices.MoveFolder(uri);
                    Rest.InventoryServices.PurgeFolder(TrashCan);
                    modified = true;
                }

                // Now, regardelss of what they represent, we
                // integrate all of the elements in the entity.

                foreach (InventoryFolderBase f in entity.Folders)
                {
                    rdata.appendStatus(String.Format("<p>Moving folder {0} UUID {1} <p>", f.Name, f.ID));
                    Rest.InventoryServices.MoveFolder(f);
                }

                foreach (InventoryItemBase it in entity.Items)
                {
                    rdata.appendStatus(String.Format("<p>Storing item {0} UUID {1} <p>", it.Name, it.ID));
                    Rest.InventoryServices.AddItem(it);
                }
            }

            /// <summary>
            /// URI specifies an item to be updated
            /// </summary>
            /// <remarks>
            /// The entity must contain a single item node to be
            /// updated. ID and Folder ID must be correct.
            /// </remarks>

            else
            {
                InventoryItemBase uri = (InventoryItemBase) InventoryNode;
                InventoryItemBase xml = null;

                if (entity.Folders.Count != 0)
                {
                    Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>",
                                         MsgId, rdata.method, rdata.path);
                    rdata.Fail(Rest.HttpStatusCodeBadRequest, "folder is not allowed");
                }

                if (entity.Items.Count > 1)
                {
                    Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>",
                                         MsgId, rdata.method, rdata.path);
                    rdata.Fail(Rest.HttpStatusCodeBadRequest, "too may items");
                }

                xml = entity.Items[0];

                if (xml.ID == UUID.Zero)
                {
                    xml.ID = UUID.Random();
                }

                // If the folder reference has changed, then this item is
                // being moved. Otherwise we'll just delete the old, and
                // add in the new.

                // Delete the old item

                List<UUID> uuids = new List<UUID>();
                uuids.Add(uri.ID);
                Rest.InventoryServices.DeleteItems(uri.Owner, uuids);

                // Add the new item to the inventory

                Rest.InventoryServices.AddItem(xml);

                rdata.appendStatus(String.Format("<p>Storing item {0} UUID {1} <p>", xml.Name, xml.ID));
            }

            if (created)
            {
                rdata.Complete(Rest.HttpStatusCodeCreated);
            }
            else
            {
                if (modified)
                {
                    rdata.Complete(Rest.HttpStatusCodeOK);
                }
                else
                {
                    rdata.Complete(Rest.HttpStatusCodeNoContent);
                }
            }

            rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method));

        }
        /// <summary>
        /// This method implements GET processing for inventory.
        /// Any remaining parameters are used to locate the
        /// corresponding subtree based upon node name.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        private void DoGet(InventoryRequestData rdata)
        {
            rdata.initXmlWriter();

            rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty);

            // If there are additional parameters, then these represent
            // a path relative to the root of the inventory. This path
            // must be traversed before we format the sub-tree thus
            // identified.

            traverse(rdata, rdata.root, PARM_PATH);

            // Close all open elements

            rdata.writer.WriteFullEndElement();

            // 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("Inventory {0} Normal completion", rdata.method));
        }