/// <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));
//        }

        /// <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>
        /// 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));
//        }

#endregion method-specific processing

        /// <summary>
        /// This method is called to obtain the OpenSim inventory object identified
        /// by the supplied URI. This may be either an Item or a Folder, so a suitably
        /// ambiguous return type is employed (Object). This method recurses as
        /// necessary to process the designated hierarchy.
        ///
        /// If we reach the end of the URI then we return the contextual folder to
        /// our caller.
        ///
        /// If we are not yet at the end of the URI we attempt to find a child folder
        /// and if we succeed we recurse.
        ///
        /// If this is the last node, then we look to see if this is an item. If it is,
        /// we return that item.
        ///
        /// If we reach the end of an inventory path and the URI si not yet exhausted,
        /// then if 'fill' is specified, we create the intermediate nodes.
        ///
        /// Otherwise we fail the request on the ground of an invalid URI.
        ///
        /// An ambiguous request causes the request to fail.
        ///
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        /// <param name=folder>The folder to be searched (parent)</param>
        /// <param name=pi>URI parameter index</param>
        /// <param name=fill>Should missing path members be created?</param>

        private Object getInventoryNode(InventoryRequestData rdata,
                                        InventoryFolderBase folder,
                                        int pi, bool fill)
        {
            InventoryFolderBase foundf = null;
            int fk = 0;

            Rest.Log.DebugFormat("{0} Searching folder {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi);

            // We have just run off the end of the parameter sequence

            if (pi >= rdata.Parameters.Length)
            {
                return folder;
            }

            // There are more names in the parameter sequence,
            // look for the folder named by param[pi] as a
            // child of the folder supplied as an argument.
            // Note that a UUID may have been supplied as the
            // identifier (it is the ONLY guaranteed unambiguous
            // option.

            if (rdata.folders != null)
            {
                foreach (InventoryFolderBase f in rdata.folders)
                {
                    // Look for the present node in the directory list
                    if (f.ParentID == folder.ID &&
                        (f.Name == rdata.Parameters[pi] ||
                         f.ID.ToString() == rdata.Parameters[pi]))
                    {
                        foundf = f;
                        fk++;
                    }
                }
            }

            // If more than one node matched, then the path, as specified
            // is ambiguous.

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

            // If we find a match, then the method
            // increment the parameter index, and calls itself
            // passing the found folder as the new context.

            if (foundf != null)
            {
                return getInventoryNode(rdata, foundf, pi+1, fill);
            }

            // No folders that match. Perhaps this parameter identifies an item? If
            // it does, then it MUST also be the last name in the sequence.

            if (pi == rdata.Parameters.Length-1)
            {
                if (rdata.items != null)
                {
                    int k = 0;
                    InventoryItemBase li = null;
                    foreach (InventoryItemBase i in rdata.items)
                    {
                        if (i.Folder == folder.ID &&
                            (i.Name == rdata.Parameters[pi] ||
                             i.ID.ToString() == rdata.Parameters[pi]))
                        {
                            li = i;
                            k++;
                        }
                    }
                    if (k == 1)
                    {
                        return li;
                    }
                    else if (k > 1)
                    {
                        Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous",
                                             MsgId, rdata.method, rdata.path);
                        rdata.Fail(Rest.HttpStatusCodeConflict, "request is ambiguous");
                    }
                }
            }

            // If fill is enabled, then we must create the missing intermediate nodes.
            // And of course, even this is not straightforward. All intermediate nodes
            // are obviously folders, but the last node may be a folder or an item.

            if (fill)
            {
            }

            // No fill, so abandon the request

            Rest.Log.DebugFormat("{0} {1}: Resource {2} not found",
                                 MsgId, rdata.method, rdata.path);
            rdata.Fail(Rest.HttpStatusCodeNotFound,
                        String.Format("resource {0}:{1} not found", rdata.method, rdata.path));

            return null; /* Never reached */
        }
        /// <summary>
        /// This method is called by PUT and POST to create an XmlInventoryCollection
        /// instance that reflects the content of the entity supplied on the request.
        /// Any elements in the completed collection whose UUID is zero, are
        /// considered to be located relative to the end-point identified int he
        /// URI. In this way, an entire sub-tree can be conveyed in a single REST
        /// PUT or POST request.
        ///
        /// A new instance of XmlInventoryCollection is created and, if the request
        /// has an entity, it is more completely initialized. thus, if no entity was
        /// provided the collection is valid, but empty.
        ///
        /// The entity is then scanned and each tag is processed to produce the
        /// appropriate inventory elements. At the end f the scan, teh XmlInventoryCollection
        /// will reflect the subtree described by the entity.
        ///
        /// This is a very flexible mechanism, the entity may contain arbitrary,
        /// discontiguous tree fragments, or may contain single element. The caller is
        /// responsible for integrating this collection (and ensuring that any
        /// missing parent IDs are resolved).
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        internal XmlInventoryCollection ReconstituteEntity(InventoryRequestData rdata)
        {
            Rest.Log.DebugFormat("{0} Reconstituting entity", MsgId);

            XmlInventoryCollection ic = new XmlInventoryCollection();

            if (rdata.request.HasEntityBody)
            {
                Rest.Log.DebugFormat("{0} Entity present", MsgId);

                ic.init(rdata);

                try
                {
                    while (ic.xml.Read())
                    {
                        switch (ic.xml.NodeType)
                        {
                            case XmlNodeType.Element:
                                Rest.Log.DebugFormat("{0} StartElement: <{1}>",
                                                     MsgId, ic.xml.Name);

                                switch (ic.xml.Name)
                                {
                                    case "Folder":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectFolder(ic);
                                        break;
                                    case "Item":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectItem(ic);
                                        break;
                                    case "Asset":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectAsset(ic);
                                        break;
                                    case "Permissions":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectPermissions(ic);
                                        break;
                                    default:
                                        Rest.Log.DebugFormat("{0} Ignoring {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                }

                                // This stinks, but the ReadElement call above not only reads
                                // the imbedded data, but also consumes the end tag for Asset
                                // and moves the element pointer on to the containing Item's
                                // element-end, however, if there was a permissions element
                                // following, it would get us to the start of that..
                                if (ic.xml.NodeType == XmlNodeType.EndElement &&
                                    ic.xml.Name     == "Item")
                                {
                                    Validate(ic);
                                }
                                break;

                            case XmlNodeType.EndElement :
                                switch (ic.xml.Name)
                                {
                                    case "Folder":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        ic.Pop();
                                        break;
                                    case "Item":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        Validate(ic);
                                        break;
                                    case "Asset":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                    case "Permissions":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                    default:
                                        Rest.Log.DebugFormat("{0} Ignoring {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                    }
                                break;

                            default:
                                Rest.Log.DebugFormat("{0} Ignoring: <{1}>:<{2}>",
                                                     MsgId, ic.xml.NodeType, ic.xml.Value);
                                break;
                        }
                    }
                }
                catch (XmlException e)
                {
                    Rest.Log.WarnFormat("{0} XML parsing error: {1}", MsgId, e.Message);
                    throw e;
                }
                catch (Exception e)
                {
                    Rest.Log.WarnFormat("{0} Unexpected XML parsing error: {1}", MsgId, e.Message);
                    throw e;
                }
            }
            else
            {
                Rest.Log.DebugFormat("{0} Entity absent", MsgId);
            }

            if (Rest.DEBUG)
            {
                Rest.Log.DebugFormat("{0} Reconstituted entity", MsgId);
                Rest.Log.DebugFormat("{0} {1} assets", MsgId, ic.Assets.Count);
                Rest.Log.DebugFormat("{0} {1} folder", MsgId, ic.Folders.Count);
                Rest.Log.DebugFormat("{0} {1} items", MsgId, ic.Items.Count);
            }

            return ic;
        }
 internal void init(InventoryRequestData p_rdata)
 {
     rdata   = p_rdata;
     UserID  = rdata.uuid;
     stk     = new Stack<InventoryFolderBase>();
     rdata.initXmlReader();
     xml     = rdata.reader;
     initPermissions();
 }
        /// <summary>
        /// This method generates XML that describes an instance of InventoryItemBase.
        /// </summary>
        /// <param name="rdata">HTTP service request work area</param>
        /// <param name="i">The item to be formatted</param>
        /// <param name="indent">Pretty print indentation</param>
        private void formatItem(InventoryRequestData rdata, InventoryItemBase i, string indent)
        {
            Rest.Log.DebugFormat("{0}   Item : {1} {2} {3} Type = {4}, AssetType = {5}",
                                 MsgId, i.ID, indent, i.Name, i.InvType, i.AssetType);

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

            rdata.writer.WriteAttributeString("name", String.Empty, i.Name);
            rdata.writer.WriteAttributeString("desc", String.Empty, i.Description);
            rdata.writer.WriteAttributeString("uuid", String.Empty, i.ID.ToString());
            rdata.writer.WriteAttributeString("folder", String.Empty, i.Folder.ToString());
            rdata.writer.WriteAttributeString("owner", String.Empty, i.Owner.ToString());
            rdata.writer.WriteAttributeString("creator", String.Empty, i.CreatorId);
            rdata.writer.WriteAttributeString("creationdate", String.Empty, i.CreationDate.ToString());
            rdata.writer.WriteAttributeString("invtype", String.Empty, i.InvType.ToString());
            rdata.writer.WriteAttributeString("assettype", String.Empty, i.AssetType.ToString());
            rdata.writer.WriteAttributeString("groupowned", String.Empty, i.GroupOwned.ToString());
            rdata.writer.WriteAttributeString("groupid", String.Empty, i.GroupID.ToString());
            rdata.writer.WriteAttributeString("saletype", String.Empty, i.SaleType.ToString());
            rdata.writer.WriteAttributeString("saleprice", String.Empty, i.SalePrice.ToString());
            rdata.writer.WriteAttributeString("flags", String.Empty, i.Flags.ToString());

            rdata.writer.WriteStartElement(String.Empty, "Permissions", String.Empty);
            rdata.writer.WriteAttributeString("current", String.Empty, i.CurrentPermissions.ToString("X"));
            rdata.writer.WriteAttributeString("next", String.Empty, i.NextPermissions.ToString("X"));
            rdata.writer.WriteAttributeString("group", String.Empty, i.GroupPermissions.ToString("X"));
            rdata.writer.WriteAttributeString("everyone", String.Empty, i.EveryOnePermissions.ToString("X"));
            rdata.writer.WriteAttributeString("base", String.Empty, i.BasePermissions.ToString("X"));
            rdata.writer.WriteEndElement();

            rdata.writer.WriteElementString("Asset", i.AssetID.ToString());

            rdata.writer.WriteEndElement();
        }
        /// <summary>
        /// This method creates a "trashcan" folder to support folder and item
        /// deletions by this interface. The xisting trash folder is found and
        /// this folder is created within it. It is called "tmp" to indicate to
        /// the client that it is OK to delete this folder. The REST interface
        /// will recreate the folder on an as-required basis.
        /// If the trash can cannot be created, then by implication the request
        /// that required it cannot be completed, and it fails accordingly.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        private InventoryFolderBase GetTrashCan(InventoryRequestData rdata)
        {
            InventoryFolderBase TrashCan = null;

            foreach (InventoryFolderBase f in rdata.folders)
            {
                if (f.Name == "Trash")
                {
                    foreach (InventoryFolderBase t in rdata.folders)
                    {
                        if (t.Name == "tmp")
                        {
                            TrashCan = t;
                        }
                    }
                    if (TrashCan == null)
                    {
                        TrashCan = new InventoryFolderBase();
                        TrashCan.Name = "tmp";
                        TrashCan.ID   = UUID.Random();
                        TrashCan.Version = 1;
                        TrashCan.Type = (short) AssetType.TrashFolder;
                        TrashCan.ParentID = f.ID;
                        TrashCan.Owner = f.Owner;
                        Rest.InventoryServices.AddFolder(TrashCan);
                    }
                }
            }

            if (TrashCan == null)
            {
                Rest.Log.DebugFormat("{0} No Trash Can available", MsgId);
                rdata.Fail(Rest.HttpStatusCodeServerError, "unable to create trash can");
            }

            return TrashCan;
        }
        /// <summary>
        /// This is the recursive method. I've separated them in this way so that
        /// we do not have to waste cycles on any first-case-only processing.
        /// </summary>

        private void traverseInventory(InventoryRequestData rdata, InventoryFolderBase folder, int pi)
        {
            int fk = 0;
            InventoryFolderBase ffound = null;
            InventoryItemBase   ifound = null;

            Rest.Log.DebugFormat("{0} Traverse Folder : {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi);

            foreach (InventoryFolderBase f in rdata.folders)
            {
                if (f.ParentID == folder.ID &&
                    (f.Name == rdata.Parameters[pi] ||
                     f.ID.ToString() == rdata.Parameters[pi]))
                {
                    fk++;
                    ffound = f;
                }
            }

            // If this is the last element in the parameter sequence, then
            // it is reasonable to check for an item. All intermediate nodes
            // MUST be folders.

            if (pi == rdata.Parameters.Length-1)
            {
                // Only if there are any items, and there pretty much always are.

                if (rdata.items != null)
                {
                    foreach (InventoryItemBase i in rdata.items)
                    {
                        if (i.Folder == folder.ID &&
                            (i.Name == rdata.Parameters[pi] ||
                             i.ID.ToString() == rdata.Parameters[pi]))
                        {
                            fk++;
                            ifound = i;
                        }
                    }
                }
            }

            if (fk == 1)
            {
                if (ffound != null)
                {
                    if (pi < rdata.Parameters.Length-1)
                    {
                        traverseInventory(rdata, ffound, pi+1);
                    }
                    else
                    {
                        formatInventory(rdata, ffound, String.Empty);
                    }
                    return;
                }
                else
                {
                    // Fetching an Item has a special significance. In this
                    // case we also want to fetch the associated asset.
                    // To make it interesting, we'll do this via redirection.
                    string asseturl = String.Format("http://{0}:{1}/{2}{3}{4}", rdata.hostname, rdata.port,
                        "admin/assets",Rest.UrlPathSeparator,ifound.AssetID.ToString());
                    rdata.Redirect(asseturl,Rest.PERMANENT);
                    Rest.Log.DebugFormat("{0} Never Reached", MsgId);
                }
            }
            else if (fk > 1)
            {
                rdata.Fail(Rest.HttpStatusCodeConflict,
                           String.Format("ambiguous element ({0}) in path specified: <{1}>",
                                         pi, rdata.path));
            }

            Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>",
                                 MsgId, rdata.path);
            rdata.Fail(Rest.HttpStatusCodeNotFound,String.Format("no such item/folder : {0}",
                                                                 rdata.Parameters[pi]));

        }
        /// <summary>
        /// This method generates XML that describes an instance of InventoryFolderBase.
        /// It recurses as necessary to reflect a folder hierarchy, and calls formatItem
        /// to generate XML for any items encountered along the way.
        /// The indentation parameter is solely for the benefit of trace record
        /// formatting.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        /// <param name=folder>The folder to be searched (parent)</param>
        /// <param name=indent>pretty print indentation</param>
        private void formatInventory(InventoryRequestData rdata, InventoryFolderBase folder, string indent)
        {
            if (Rest.DEBUG)
            {
                Rest.Log.DebugFormat("{0} Folder : {1} {2} {3} type = {4}",
                        MsgId, folder.ID, indent, folder.Name, folder.Type);
                indent += "\t";
            }

            // Start folder item

            rdata.writer.WriteStartElement(String.Empty,"Folder",String.Empty);
            rdata.writer.WriteAttributeString("name",String.Empty,folder.Name);
            rdata.writer.WriteAttributeString("uuid",String.Empty,folder.ID.ToString());
            rdata.writer.WriteAttributeString("parent",String.Empty,folder.ParentID.ToString());
            rdata.writer.WriteAttributeString("owner",String.Empty,folder.Owner.ToString());
            rdata.writer.WriteAttributeString("type",String.Empty,folder.Type.ToString());
            rdata.writer.WriteAttributeString("version",String.Empty,folder.Version.ToString());

            if (rdata.folders != null)
            {
                foreach (InventoryFolderBase f in rdata.folders)
                {
                    if (f.ParentID == folder.ID)
                    {
                        formatInventory(rdata, f, indent);
                    }
                }
            }

            if (rdata.items != null)
            {
                foreach (InventoryItemBase i in rdata.items)
                {
                    if (i.Folder == folder.ID)
                    {
                        formatItem(rdata, i, indent);
                    }
                }
            }

            // End folder item

            rdata.writer.WriteEndElement();
        }
        /// <summary>
        /// This routine traverse the inventory's structure until the end-point identified
        /// in the URI is reached, the remainder of the inventory (if any) is then formatted
        /// and returned to the requestor.
        ///
        /// Note that this method is only interested in those folder that match elements of
        /// the URI supplied by the requestor, so once a match is fund, the processing does
        /// not need to consider any further elements.
        ///
        /// Only the last element in the URI should identify an item.
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        /// <param name=folder>The folder to be searched (parent)</param>
        /// <param name=pi>URI parameter index</param>

        private void traverse(InventoryRequestData rdata, InventoryFolderBase folder, int pi)
        {
            Rest.Log.DebugFormat("{0} Traverse[initial] : {1} {2} [{3}]", MsgId, folder.ID, folder.Name, pi);

            if (rdata.folders != null)
            {
                // If there was only one parameter (avatar name), then the entire
                // inventory is being requested.

                if (rdata.Parameters.Length == 1)
                {
                    formatInventory(rdata, rdata.root, String.Empty);
                }

                // Has the client specified the root directory name explicitly?
                // if yes, then we just absorb the reference, because the folder
                // we start looking in for a match *is* the root directory. If there
                // are more parameters remaining we tarverse, otehrwise it's time
                // to format. Otherwise,we consider the "My Inventory" to be implied
                // and we just traverse normally.

                else if (folder.ID.ToString() == rdata.Parameters[pi] ||
                         folder.Name          == rdata.Parameters[pi])
                {
                    // Length is -1 because the avatar name is a parameter
                    if (pi<(rdata.Parameters.Length-1))
                    {
                        traverseInventory(rdata, folder, pi+1);
                    }
                    else
                    {
                        formatInventory(rdata, folder, String.Empty);
                    }
                }
                else
                {
                    traverseInventory(rdata, folder, pi);
                }

                return;
            }
        }
        /// <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>
        /// 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>
        /// 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>
        /// 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));
        }