- /// These are the inventory specific request/response state
- /// extensions.
- ///
-
- internal UUID uuid = UUID.Zero;
- internal UserProfileData userProfile = null;
- internal AvatarAppearance userAppearance = null;
-
- internal AppearanceRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
- : base(request, response, prefix)
- {
- }
-
- }
-
- #endregion Appearance RequestData extension
-
- }
-}
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAssetServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestAssetServices.cs
deleted file mode 100644
index 4ba3d77..0000000
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestAssetServices.cs
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Xml;
-using OpenMetaverse;
-using OpenSim.Framework;
-using OpenSim.Framework.Servers;
-using OpenSim.Framework.Servers.HttpServer;
-
-namespace OpenSim.ApplicationPlugins.Rest.Inventory
-{
- public class RestAssetServices : IRest
- {
- private bool enabled = false;
- private string qPrefix = "assets";
-
- // A simple constructor is used to handle any once-only
- // initialization of working classes.
-
- public RestAssetServices()
- {
- Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
- Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
-
- // If the handler specifies a relative path for its domain
- // then we must add the standard absolute prefix, e.g. /admin
-
- if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
- {
- Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix);
- qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
- Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix);
- }
-
- // Register interface using the fully-qualified prefix
-
- Rest.Plugin.AddPathHandler(DoAsset, qPrefix, Allocate);
-
- // Activate if all went OK
-
- enabled = true;
-
- Rest.Log.InfoFormat("{0} Asset services initialization complete", MsgId);
- }
-
- // Post-construction, pre-enabled initialization opportunity
- // Not currently exploited.
-
- public void Initialize()
- {
- }
-
- // Called by the plug-in to halt REST processing. Local processing is
- // disabled, and control blocks until all current processing has
- // completed. No new processing will be started
-
- public void Close()
- {
- enabled = false;
- Rest.Log.InfoFormat("{0} Asset services ({1}) closing down", MsgId, qPrefix);
- }
-
- // Properties
-
- internal string MsgId
- {
- get { return Rest.MsgId; }
- }
-
- #region Interface
-
- private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
- {
- return (RequestData) new AssetRequestData(request, response, prefix);
- }
-
- // Asset Handler
-
- private void DoAsset(RequestData rparm)
- {
- if (!enabled) return;
-
- AssetRequestData rdata = (AssetRequestData) rparm;
-
- Rest.Log.DebugFormat("{0} REST Asset handler ({1}) ENTRY", MsgId, qPrefix);
-
- // Now that we know this is a serious attempt to
- // access inventory data, we should find out who
- // is asking, and make sure they are authorized
- // to do so. We need to validate the caller's
- // identity before revealing anything about the
- // status quo. Authenticate throws an exception
- // via Fail if no identity information is present.
- //
- // With the present HTTP server we can't use the
- // builtin authentication mechanisms because they
- // would be enforced for all in-bound requests.
- // Instead we look at the headers ourselves and
- // handle authentication directly.
-
- try
- {
- if (!rdata.IsAuthenticated)
- {
- rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
- }
- }
- catch (RestException e)
- {
- if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
- {
- Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
- Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
- rdata.request.Headers.Get("Authorization"));
- }
- else
- {
- Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
- Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
- rdata.request.Headers.Get("Authorization"));
- }
- throw (e);
- }
-
- // Remove the prefix and what's left are the parameters. If we don't have
- // the parameters we need, fail the request. Parameters do NOT include
- // any supplied query values.
-
- if (rdata.Parameters.Length > 0)
- {
- switch (rdata.method)
- {
- case "get" :
- DoGet(rdata);
- break;
- case "put" :
- DoPut(rdata);
- break;
- case "post" :
- DoPost(rdata);
- break;
- case "delete" :
- default :
- Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}",
- MsgId, rdata.method);
- rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
- break;
- }
- }
- else
- {
- Rest.Log.WarnFormat("{0} Asset: No agent information provided", MsgId);
- rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
- }
-
- Rest.Log.DebugFormat("{0} REST Asset handler EXIT", MsgId);
- }
-
- #endregion Interface
-
- ///
- /// The only parameter we recognize is a UUID.If an asset with this identification is
- /// found, it's content, base-64 encoded, is returned to the client.
- ///
-
- private void DoGet(AssetRequestData rdata)
- {
- Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
-
- if (rdata.Parameters.Length == 1)
- {
- UUID uuid = new UUID(rdata.Parameters[0]);
- AssetBase asset = Rest.AssetServices.Get(uuid.ToString());
-
- if (asset != null)
- {
- Rest.Log.DebugFormat("{0} Asset located <{1}>", MsgId, rdata.Parameters[0]);
-
- rdata.initXmlWriter();
-
- rdata.writer.WriteStartElement(String.Empty,"Asset",String.Empty);
-
- rdata.writer.WriteAttributeString("id", asset.ID);
- rdata.writer.WriteAttributeString("name", asset.Name);
- rdata.writer.WriteAttributeString("desc", asset.Description);
- rdata.writer.WriteAttributeString("type", asset.Type.ToString());
- rdata.writer.WriteAttributeString("local", asset.Local.ToString());
- rdata.writer.WriteAttributeString("temporary", asset.Temporary.ToString());
-
- rdata.writer.WriteBase64(asset.Data,0,asset.Data.Length);
-
- rdata.writer.WriteFullEndElement();
-
- }
- else
- {
- Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
- }
- }
-
- rdata.Complete();
- rdata.Respond(String.Format("Asset <{0}> : Normal completion", rdata.method));
-
- }
-
- ///
- /// UPDATE existing item, if it exists. URI identifies the item in question.
- /// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded)
- /// is decoded and stored in the database, identified by the supplied UUID.
- ///
- private void DoPut(AssetRequestData rdata)
- {
- bool modified = false;
- bool created = false;
-
- AssetBase asset = null;
-
- Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
-
- if (rdata.Parameters.Length == 1)
- {
-
- rdata.initXmlReader();
- XmlReader xml = rdata.reader;
-
- if (!xml.ReadToFollowing("Asset"))
- {
- Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
- }
-
- UUID uuid = new UUID(rdata.Parameters[0]);
- asset = Rest.AssetServices.Get(uuid.ToString());
-
- modified = (asset != null);
- created = !modified;
-
- asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString());
- asset.Description = xml.GetAttribute("desc");
- asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0;
- asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0;
- asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", ""));
-
- if (asset.ID != rdata.Parameters[0])
- {
- Rest.Log.WarnFormat("{0} URI and payload disagree on UUID U:{1} vs P:{2}",
- MsgId, rdata.Parameters[0], asset.ID);
- }
-
- Rest.AssetServices.Store(asset);
-
- }
- else
- {
- Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
- }
-
- if (created)
- {
- rdata.appendStatus(String.Format(" Created asset {0}, UUID {1}
", asset.Name, asset.FullID));
- rdata.Complete(Rest.HttpStatusCodeCreated);
- }
- else
- {
- if (modified)
- {
- rdata.appendStatus(String.Format("
Modified asset {0}, UUID {1}
", asset.Name, asset.FullID));
- rdata.Complete(Rest.HttpStatusCodeOK);
- }
- else
- {
- rdata.Complete(Rest.HttpStatusCodeNoContent);
- }
- }
-
- rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
-
- }
-
- ///
- /// CREATE new item, replace if it exists. URI identifies the context for the item in question.
- /// No parameters are required for POST, just thepayload.
- ///
-
- private void DoPost(AssetRequestData rdata)
- {
-
- bool modified = false;
- bool created = false;
-
- Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
-
- if (rdata.Parameters.Length != 0)
- {
- Rest.Log.WarnFormat("{0} Parameters ignored <{1}>", MsgId, rdata.path);
- Rest.Log.InfoFormat("{0} POST of an asset has no parameters", MsgId, rdata.path);
- }
-
- rdata.initXmlReader();
- XmlReader xml = rdata.reader;
-
- if (!xml.ReadToFollowing("Asset"))
- {
- Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
- }
-
- UUID uuid = new UUID(xml.GetAttribute("id"));
- AssetBase asset = Rest.AssetServices.Get(uuid.ToString());
-
- modified = (asset != null);
- created = !modified;
-
- asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString());
- asset.Description = xml.GetAttribute("desc");
- asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0;
- asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0;
- asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", ""));
-
- Rest.AssetServices.Store(asset);
-
- if (created)
- {
- rdata.appendStatus(String.Format("
Created asset {0}, UUID {1}
", asset.Name, asset.FullID));
- rdata.Complete(Rest.HttpStatusCodeCreated);
- }
- else
- {
- if (modified)
- {
- rdata.appendStatus(String.Format("
Modified asset {0}, UUID {1}
", asset.Name, asset.FullID));
- rdata.Complete(Rest.HttpStatusCodeOK);
- }
- else
- {
- rdata.Complete(Rest.HttpStatusCodeNoContent);
- }
- }
-
- rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
-
- }
-
- ///
- /// Asset processing has no special data area requirements.
- ///
-
- internal class AssetRequestData : RequestData
- {
- internal AssetRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
- : base(request, response, prefix)
- {
- }
- }
- }
-}
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs
deleted file mode 100644
index e79d2bd..0000000
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestFileServices.cs
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Xml;
-using System.IO;
-using OpenMetaverse;
-using OpenSim.Framework;
-using OpenSim.Framework.Servers;
-using OpenSim.Framework.Servers.HttpServer;
-
-namespace OpenSim.ApplicationPlugins.Rest.Inventory
-{
- public class RestFileServices : IRest
- {
- private bool enabled = false;
- private string qPrefix = "files";
-
- // A simple constructor is used to handle any once-only
- // initialization of working classes.
-
- public RestFileServices()
- {
- Rest.Log.InfoFormat("{0} File services initializing", MsgId);
- Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
-
- // If the handler specifies a relative path for its domain
- // then we must add the standard absolute prefix, e.g. /admin
-
- if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
- {
- Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix);
- qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
- Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix);
- }
-
- // Register interface using the fully-qualified prefix
-
- Rest.Plugin.AddPathHandler(DoFile, qPrefix, Allocate);
-
- // Activate if all went OK
-
- enabled = true;
-
- Rest.Log.InfoFormat("{0} File services initialization complete", MsgId);
- }
-
- // Post-construction, pre-enabled initialization opportunity
- // Not currently exploited.
-
- public void Initialize()
- {
- }
-
- // Called by the plug-in to halt REST processing. Local processing is
- // disabled, and control blocks until all current processing has
- // completed. No new processing will be started
-
- public void Close()
- {
- enabled = false;
- Rest.Log.InfoFormat("{0} File services ({1}) closing down", MsgId, qPrefix);
- }
-
- // Properties
-
- internal string MsgId
- {
- get { return Rest.MsgId; }
- }
-
- #region Interface
-
- private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
- {
- return (RequestData) new FileRequestData(request, response, prefix);
- }
-
- // Asset Handler
-
- private void DoFile(RequestData rparm)
- {
- if (!enabled) return;
-
- FileRequestData rdata = (FileRequestData) rparm;
-
- Rest.Log.DebugFormat("{0} REST File handler ({1}) ENTRY", MsgId, qPrefix);
-
- // Now that we know this is a serious attempt to
- // access file data, we should find out who
- // is asking, and make sure they are authorized
- // to do so. We need to validate the caller's
- // identity before revealing anything about the
- // status quo. Authenticate throws an exception
- // via Fail if no identity information is present.
- //
- // With the present HTTP server we can't use the
- // builtin authentication mechanisms because they
- // would be enforced for all in-bound requests.
- // Instead we look at the headers ourselves and
- // handle authentication directly.
-
- try
- {
- if (!rdata.IsAuthenticated)
- {
- rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
- }
- }
- catch (RestException e)
- {
- if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
- {
- Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
- Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
- rdata.request.Headers.Get("Authorization"));
- }
- else
- {
- Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
- Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
- rdata.request.Headers.Get("Authorization"));
- }
- throw (e);
- }
-
- // Remove the prefix and what's left are the parameters. If we don't have
- // the parameters we need, fail the request. Parameters do NOT include
- // any supplied query values.
-
- if (rdata.Parameters.Length > 0)
- {
- switch (rdata.method)
- {
- case "get" :
- DoGet(rdata);
- break;
- case "put" :
- DoPut(rdata);
- break;
- case "post" :
- DoPost(rdata);
- break;
- case "delete" :
- DoDelete(rdata);
- break;
- default :
- Rest.Log.WarnFormat("{0} File: Method not supported: {1}",
- MsgId, rdata.method);
- rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
- break;
- }
- }
- else
- {
- Rest.Log.WarnFormat("{0} File: No agent information provided", MsgId);
- rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
- }
-
- Rest.Log.DebugFormat("{0} REST File handler EXIT", MsgId);
-
- }
-
- #endregion Interface
-
- ///
- /// The only parameter we recognize is a UUID.If an asset with this identification is
- /// found, it's content, base-64 encoded, is returned to the client.
- ///
-
- private void DoGet(FileRequestData rdata)
- {
-
- string path = String.Empty;
-
- Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
-
- if (rdata.Parameters.Length > 1)
- {
- try
- {
- path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
- if (File.Exists(path))
- {
- Rest.Log.DebugFormat("{0} File located <{1}>", MsgId, path);
- Byte[] data = File.ReadAllBytes(path);
- rdata.initXmlWriter();
- rdata.writer.WriteStartElement(String.Empty,"File",String.Empty);
- rdata.writer.WriteAttributeString("name", path);
- rdata.writer.WriteBase64(data,0,data.Length);
- rdata.writer.WriteFullEndElement();
- }
- else
- {
- Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, path);
- rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0}", path));
- }
- }
- catch (Exception e)
- {
- Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, e.Message);
- rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
- path, e.Message));
- }
- }
-
- rdata.Complete();
- rdata.Respond(String.Format("File <{0}> : Normal completion", rdata.method));
-
- }
-
- ///
- /// UPDATE existing item, if it exists. URI identifies the item in question.
- /// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded)
- /// is decoded and stored in the database, identified by the supplied UUID.
- ///
- private void DoPut(FileRequestData rdata)
- {
- bool modified = false;
- bool created = false;
- string path = String.Empty;
-
- Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
-
- if (rdata.Parameters.Length > 1)
- {
- try
- {
- path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
- bool maymod = File.Exists(path);
-
- rdata.initXmlReader();
- XmlReader xml = rdata.reader;
-
- if (!xml.ReadToFollowing("File"))
- {
- Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
- }
-
- Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
-
- File.WriteAllBytes(path,data);
- modified = maymod;
- created = ! maymod;
- }
- catch (Exception e)
- {
- Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
- e.Message);
- }
- }
- else
- {
- Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
- }
-
- if (created)
- {
- rdata.appendStatus(String.Format("
Created file {0}
", path));
- rdata.Complete(Rest.HttpStatusCodeCreated);
- }
- else
- {
- if (modified)
- {
- rdata.appendStatus(String.Format("
Modified file {0}
", path));
- rdata.Complete(Rest.HttpStatusCodeOK);
- }
- else
- {
- rdata.Complete(Rest.HttpStatusCodeNoContent);
- }
- }
-
- rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
-
- }
-
- ///
- /// CREATE new item, replace if it exists. URI identifies the context for the item in question.
- /// No parameters are required for POST, just thepayload.
- ///
-
- private void DoPost(FileRequestData rdata)
- {
-
- bool modified = false;
- bool created = false;
- string path = String.Empty;
-
- Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
-
- if (rdata.Parameters.Length > 1)
- {
- try
- {
- path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
- bool maymod = File.Exists(path);
-
- rdata.initXmlReader();
- XmlReader xml = rdata.reader;
-
- if (!xml.ReadToFollowing("File"))
- {
- Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
- }
-
- Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
-
- File.WriteAllBytes(path,data);
- modified = maymod;
- created = ! maymod;
- }
- catch (Exception e)
- {
- Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
- e.Message);
- }
- }
- else
- {
- Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
- }
-
- if (created)
- {
- rdata.appendStatus(String.Format("
Created file {0}
", path));
- rdata.Complete(Rest.HttpStatusCodeCreated);
- }
- else
- {
- if (modified)
- {
- rdata.appendStatus(String.Format("
Modified file {0}
", path));
- rdata.Complete(Rest.HttpStatusCodeOK);
- }
- else
- {
- rdata.Complete(Rest.HttpStatusCodeNoContent);
- }
- }
-
- rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
-
- }
-
- ///
- /// CREATE new item, replace if it exists. URI identifies the context for the item in question.
- /// No parameters are required for POST, just thepayload.
- ///
-
- private void DoDelete(FileRequestData rdata)
- {
-
- bool modified = false;
- bool created = false;
- string path = String.Empty;
-
- Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
-
- if (rdata.Parameters.Length > 1)
- {
- try
- {
- path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
-
- if (File.Exists(path))
- {
- File.Delete(path);
- }
- }
- catch (Exception e)
- {
- Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
- e.Message);
- rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
- path, e.Message));
- }
- }
- else
- {
- Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
- rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
- }
-
- if (created)
- {
- rdata.appendStatus(String.Format("
Created file {0}
", path));
- rdata.Complete(Rest.HttpStatusCodeCreated);
- }
- else
- {
- if (modified)
- {
- rdata.appendStatus(String.Format("
Modified file {0}
", path));
- rdata.Complete(Rest.HttpStatusCodeOK);
- }
- else
- {
- rdata.Complete(Rest.HttpStatusCodeNoContent);
- }
- }
-
- rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
-
- }
-
- ///
- /// File processing has no special data area requirements.
- ///
-
- internal class FileRequestData : RequestData
- {
- internal FileRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
- : base(request, response, prefix)
- {
- }
- }
- }
-}
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs
deleted file mode 100644
index 072bd6f..0000000
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs
+++ /dev/null
@@ -1,662 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using OpenSim.Framework.Servers;
-using OpenSim.Framework.Servers.HttpServer;
-
-namespace OpenSim.ApplicationPlugins.Rest.Inventory
-{
- ///
- /// The class signature reveals the roles that RestHandler plays.
- ///
- /// [1] It is a sub-class of RestPlugin. It inherits and extends
- /// the functionality of this class, constraining it to the
- /// specific needs of this REST implementation. This relates
- /// to the plug-in mechanism supported by OpenSim, the specifics
- /// of which are mostly hidden by RestPlugin.
- /// [2] IRestHandler describes the interface that this class
- /// exports to service implementations. This is the services
- /// management interface.
- /// [3] IHttpAgentHandler describes the interface that is exported
- /// to the BaseHttpServer in support of this particular HTTP
- /// processing model. This is the request interface of the
- /// handler.
- ///
-
- public class RestHandler : RestPlugin, IRestHandler, IHttpAgentHandler
- {
- // Handler tables: both stream and REST are supported. The path handlers and their
- // respective allocators are stored in separate tables.
-
- internal Dictionary pathHandlers = new Dictionary();
- internal Dictionary pathAllocators = new Dictionary();
- internal Dictionary streamHandlers = new Dictionary();
-
- #region local static state
-
- private static bool handlersLoaded = false;
- private static List classes = new List();
- private static List handlers = new List();
- private static Type[] parms = new Type[0];
- private static Object[] args = new Object[0];
-
- ///
- /// This static initializer scans the ASSEMBLY for classes that
- /// export the IRest interface and builds a list of them. These
- /// are later activated by the handler. To add a new handler it
- /// is only necessary to create a new services class that implements
- /// the IRest interface, and recompile the handler. This gives
- /// all of the build-time flexibility of a modular approach
- /// while not introducing yet-another module loader. Note that
- /// multiple assembles can still be built, each with its own set
- /// of handlers. Examples of services classes are RestInventoryServices
- /// and RestSkeleton.
- ///
-
- static RestHandler()
- {
- Module[] mods = Assembly.GetExecutingAssembly().GetModules();
-
- foreach (Module m in mods)
- {
- Type[] types = m.GetTypes();
- foreach (Type t in types)
- {
- try
- {
- if (t.GetInterface("IRest") != null)
- {
- classes.Add(t);
- }
- }
- catch (Exception)
- {
- Rest.Log.WarnFormat("[STATIC-HANDLER]: #0 Error scanning {1}", t);
- Rest.Log.InfoFormat("[STATIC-HANDLER]: #0 {1} is not included", t);
- }
- }
- }
- }
-
- #endregion local static state
-
- #region local instance state
-
- ///
- /// This routine loads all of the handlers discovered during
- /// instance initialization.
- /// A table of all loaded and successfully constructed handlers
- /// is built, and this table is then used by the constructor to
- /// initialize each of the handlers in turn.
- /// NOTE: The loading process does not automatically imply that
- /// the handler has registered any kind of an interface, that
- /// may be (optionally) done by the handler either during
- /// construction, or during initialization.
- ///
- /// I was not able to make this code work within a constructor
- /// so it is isolated within this method.
- ///
-
- private void LoadHandlers()
- {
- lock (handlers)
- {
- if (!handlersLoaded)
- {
- ConstructorInfo ci;
- Object ht;
-
- foreach (Type t in classes)
- {
- try
- {
- ci = t.GetConstructor(parms);
- ht = ci.Invoke(args);
- handlers.Add((IRest)ht);
- }
- catch (Exception e)
- {
- Rest.Log.WarnFormat("{0} Unable to load {1} : {2}", MsgId, t, e.Message);
- }
- }
- handlersLoaded = true;
- }
- }
- }
-
- #endregion local instance state
-
- #region overriding properties
-
- // These properties override definitions
- // in the base class.
-
- // Name is used to differentiate the message header.
-
- public override string Name
- {
- get { return "HANDLER"; }
- }
-
- // Used to partition the .ini configuration space.
-
- public override string ConfigName
- {
- get { return "RestHandler"; }
- }
-
- // We have to rename these because we want
- // to be able to share the values with other
- // classes in our assembly and the base
- // names are protected.
-
- public string MsgId
- {
- get { return base.MsgID; }
- }
-
- public string RequestId
- {
- get { return base.RequestID; }
- }
-
- #endregion overriding properties
-
- #region overriding methods
-
- ///
- /// This method is called by OpenSimMain immediately after loading the
- /// plugin and after basic server setup, but before running any server commands.
- ///
- ///
- /// Note that entries MUST be added to the active configuration files before
- /// the plugin can be enabled.
- ///
-
- public override void Initialise(OpenSimBase openSim)
- {
- try
- {
- // This plugin will only be enabled if the broader
- // REST plugin mechanism is enabled.
-
- //Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId);
-
- base.Initialise(openSim);
-
- // IsEnabled is implemented by the base class and
- // reflects an overall RestPlugin status
-
- if (!IsEnabled)
- {
- //Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId);
- return;
- }
-
- Rest.Log.InfoFormat("{0} Rest <{1}> plugin will be enabled", MsgId, Name);
- Rest.Log.InfoFormat("{0} Configuration parameters read from <{1}>", MsgId, ConfigName);
-
- // These are stored in static variables to make
- // them easy to reach from anywhere in the assembly.
-
- Rest.main = openSim;
- if (Rest.main == null)
- throw new Exception("OpenSim base pointer is null");
-
- Rest.Plugin = this;
- Rest.Config = Config;
- Rest.Prefix = Prefix;
- Rest.GodKey = GodKey;
- Rest.Authenticate = Rest.Config.GetBoolean("authenticate", Rest.Authenticate);
- Rest.Scheme = Rest.Config.GetString("auth-scheme", Rest.Scheme);
- Rest.Secure = Rest.Config.GetBoolean("secured", Rest.Secure);
- Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape", Rest.ExtendedEscape);
- Rest.Realm = Rest.Config.GetString("realm", Rest.Realm);
- Rest.DumpAsset = Rest.Config.GetBoolean("dump-asset", Rest.DumpAsset);
- Rest.Fill = Rest.Config.GetBoolean("path-fill", Rest.Fill);
- Rest.DumpLineSize = Rest.Config.GetInt("dump-line-size", Rest.DumpLineSize);
- Rest.FlushEnabled = Rest.Config.GetBoolean("flush-on-error", Rest.FlushEnabled);
-
- // Note: Odd spacing is required in the following strings
-
- Rest.Log.InfoFormat("{0} Authentication is {1}required", MsgId,
- (Rest.Authenticate ? "" : "not "));
-
- Rest.Log.InfoFormat("{0} Security is {1}enabled", MsgId,
- (Rest.Secure ? "" : "not "));
-
- Rest.Log.InfoFormat("{0} Extended URI escape processing is {1}enabled", MsgId,
- (Rest.ExtendedEscape ? "" : "not "));
-
- Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
- (Rest.DumpAsset ? "" : "not "));
-
- // The supplied prefix MUST be absolute
-
- if (Rest.Prefix.Substring(0,1) != Rest.UrlPathSeparator)
- {
- Rest.Log.WarnFormat("{0} Prefix <{1}> is not absolute and must be", MsgId, Rest.Prefix);
- Rest.Log.InfoFormat("{0} Prefix changed to {1}>", MsgId, Rest.Prefix);
- Rest.Prefix = String.Format("{0}{1}", Rest.UrlPathSeparator, Rest.Prefix);
- }
-
- // If data dumping is requested, report on the chosen line
- // length.
-
- if (Rest.DumpAsset)
- {
- Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId, Rest.DumpLineSize);
- }
-
- // Load all of the handlers present in the
- // assembly
-
- // In principle, as we're an application plug-in,
- // most of what needs to be done could be done using
- // static resources, however the Open Sim plug-in
- // model makes this an instance, so that's what we
- // need to be.
- // There is only one Communications manager per
- // server, and by inference, only one each of the
- // user, asset, and inventory servers. So we can cache
- // those using a static initializer.
- // We move all of this processing off to another
- // services class to minimize overlap between function
- // and infrastructure.
-
- LoadHandlers();
-
- // The intention of a post construction initializer
- // is to allow for setup that is dependent upon other
- // activities outside of the agency.
-
- foreach (IRest handler in handlers)
- {
- try
- {
- handler.Initialize();
- }
- catch (Exception e)
- {
- Rest.Log.ErrorFormat("{0} initialization error: {1}", MsgId, e.Message);
- }
- }
-
- // Now that everything is setup we can proceed to
- // add THIS agent to the HTTP server's handler list
-
- // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will
- // have to be handled through the AddHttpHandler interface.
-// if (!AddAgentHandler(Rest.Name,this))
-// {
-// Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
-// foreach (IRest handler in handlers)
-// {
-// handler.Close();
-// }
-// }
-
- }
- catch (Exception e)
- {
- Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message);
- }
- }
-
- ///
- /// In the interests of efficiency, and because we cannot determine whether
- /// or not this instance will actually be harvested, we clobber the only
- /// anchoring reference to the working state for this plug-in. What the
- /// call to close does is irrelevant to this class beyond knowing that it
- /// can nullify the reference when it returns.
- /// To make sure everything is copacetic we make sure the primary interface
- /// is disabled by deleting the handler from the HTTP server tables.
- ///
-
- public override void Close()
- {
- Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
-
- // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will
- // have to be handled through the AddHttpHandler interface.
-// try
-// {
-// RemoveAgentHandler(Rest.Name, this);
-// }
-// catch (KeyNotFoundException){}
-
- foreach (IRest handler in handlers)
- {
- handler.Close();
- }
- }
-
- #endregion overriding methods
-
- #region interface methods
-
- ///
- /// This method is called by the HTTP server to match an incoming
- /// request. It scans all of the strings registered by the
- /// underlying handlers and looks for the best match. It returns
- /// true if a match is found.
- /// The matching process could be made arbitrarily complex.
- /// Note: The match is case-insensitive.
- ///
-
- public bool Match(OSHttpRequest request, OSHttpResponse response)
- {
-
- string path = request.RawUrl.ToLower();
-
- // Rest.Log.DebugFormat("{0} Match ENTRY", MsgId);
-
- try
- {
- foreach (string key in pathHandlers.Keys)
- {
- // Rest.Log.DebugFormat("{0} Match testing {1} against agent prefix <{2}>", MsgId, path, key);
-
- // Note that Match will not necessarily find the handler that will
- // actually be used - it does no test for the "closest" fit. It
- // simply reflects that at least one possible handler exists.
-
- if (path.StartsWith(key))
- {
- // Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
-
- // This apparently odd evaluation is needed to prevent a match
- // on anything other than a URI token boundary. Otherwise we
- // may match on URL's that were not intended for this handler.
-
- return (path.Length == key.Length ||
- path.Substring(key.Length, 1) == Rest.UrlPathSeparator);
- }
- }
-
- path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path);
-
- foreach (string key in streamHandlers.Keys)
- {
- // Rest.Log.DebugFormat("{0} Match testing {1} against stream prefix <{2}>", MsgId, path, key);
-
- // Note that Match will not necessarily find the handler that will
- // actually be used - it does no test for the "closest" fit. It
- // simply reflects that at least one possible handler exists.
-
- if (path.StartsWith(key))
- {
- // Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
-
- // This apparently odd evaluation is needed to prevent a match
- // on anything other than a URI token boundary. Otherwise we
- // may match on URL's that were not intended for this handler.
-
- return (path.Length == key.Length ||
- path.Substring(key.Length, 1) == Rest.UrlPathSeparator);
- }
- }
- }
- catch (Exception e)
- {
- Rest.Log.ErrorFormat("{0} matching exception for path <{1}> : {2}", MsgId, path, e.Message);
- }
-
- return false;
- }
-
- ///
- /// This is called by the HTTP server once the handler has indicated
- /// that it is able to handle the request.
- /// Preconditions:
- /// [1] request != null and is a valid request object
- /// [2] response != null and is a valid response object
- /// Behavior is undefined if preconditions are not satisfied.
- ///
-
- public bool Handle(OSHttpRequest request, OSHttpResponse response)
- {
- bool handled;
- base.MsgID = base.RequestID;
-
- // Debug only
-
- if (Rest.DEBUG)
- {
- Rest.Log.DebugFormat("{0} ENTRY", MsgId);
- Rest.Log.DebugFormat("{0} Agent: {1}", MsgId, request.UserAgent);
- Rest.Log.DebugFormat("{0} Method: {1}", MsgId, request.HttpMethod);
-
- for (int i = 0; i < request.Headers.Count; i++)
- {
- Rest.Log.DebugFormat("{0} Header [{1}] : <{2}> = <{3}>",
- MsgId, i, request.Headers.GetKey(i), request.Headers.Get(i));
- }
- Rest.Log.DebugFormat("{0} URI: {1}", MsgId, request.RawUrl);
- }
-
- // If a path handler worked we're done, otherwise try any
- // available stream handlers too.
-
- try
- {
- handled = (FindPathHandler(request, response) ||
- FindStreamHandler(request, response));
- }
- catch (Exception e)
- {
- // A raw exception indicates that something we weren't expecting has
- // happened. This should always reflect a shortcoming in the plugin,
- // or a failure to satisfy the preconditions. It should not reflect
- // an error in the request itself. Under such circumstances the state
- // of the request cannot be determined and we are obliged to mark it
- // as 'handled'.
-
- Rest.Log.ErrorFormat("{0} Plugin error: {1}", MsgId, e.Message);
- handled = true;
- }
-
- Rest.Log.DebugFormat("{0} EXIT", MsgId);
-
- return handled;
- }
-
- #endregion interface methods
-
- ///
- /// If there is a stream handler registered that can handle the
- /// request, then fine. If the request is not matched, do
- /// nothing.
- /// Note: The selection is case-insensitive
- ///
-
- private bool FindStreamHandler(OSHttpRequest request, OSHttpResponse response)
- {
- RequestData rdata = new RequestData(request, response, String.Empty);
-
- string bestMatch = String.Empty;
- string path = String.Format("{0}:{1}", rdata.method, rdata.path).ToLower();
-
- Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path);
-
- if (!IsEnabled)
- {
- return false;
- }
-
- foreach (string pattern in streamHandlers.Keys)
- {
- if (path.StartsWith(pattern))
- {
- if (pattern.Length > bestMatch.Length)
- {
- bestMatch = pattern;
- }
- }
- }
-
- // Handle using the best match available
-
- if (bestMatch.Length > 0)
- {
- Rest.Log.DebugFormat("{0} Stream-based handler matched with <{1}>", MsgId, bestMatch);
- RestStreamHandler handler = streamHandlers[bestMatch];
- rdata.buffer = handler.Handle(rdata.path, rdata.request.InputStream, rdata.request, rdata.response);
- rdata.AddHeader(rdata.response.ContentType,handler.ContentType);
- rdata.Respond("FindStreamHandler Completion");
- }
-
- return rdata.handled;
- }
-
- ///
- /// Add a stream handler for the designated HTTP method and path prefix.
- /// If the handler is not enabled, the request is ignored. If the path
- /// does not start with the REST prefix, it is added. If method-qualified
- /// path has not already been registered, the method is added to the active
- /// handler table.
- ///
- public void AddStreamHandler(string httpMethod, string path, RestMethod method)
- {
- if (!IsEnabled)
- {
- return;
- }
-
- if (!path.StartsWith(Rest.Prefix))
- {
- path = String.Format("{0}{1}", Rest.Prefix, path);
- }
-
- path = String.Format("{0}{1}{2}", httpMethod, Rest.UrlMethodSeparator, path);
-
- // Conditionally add to the list
-
- if (!streamHandlers.ContainsKey(path))
- {
- streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method));
- Rest.Log.DebugFormat("{0} Added handler for {1}", MsgId, path);
- }
- else
- {
- Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path);
- }
- }
-
- ///
- /// Given the supplied request/response, if the handler is enabled, the inbound
- /// information is used to match an entry in the active path handler tables, using
- /// the method-qualified path information. If a match is found, then the handler is
- /// invoked. The result is the boolean result of the handler, or false if no
- /// handler was located. The boolean indicates whether or not the request has been
- /// handled, not whether or not the request was successful - that information is in
- /// the response.
- /// Note: The selection process is case-insensitive
- ///
-
- internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
- {
- RequestData rdata = null;
- string bestMatch = null;
-
- if (!IsEnabled)
- {
- return false;
- }
-
- // Conditionally add to the list
-
- Rest.Log.DebugFormat("{0} Checking for path handler for <{1}>", MsgId, request.RawUrl);
-
- foreach (string pattern in pathHandlers.Keys)
- {
- if (request.RawUrl.ToLower().StartsWith(pattern))
- {
- if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
- {
- bestMatch = pattern;
- }
- }
- }
-
- if (!String.IsNullOrEmpty(bestMatch))
- {
- rdata = pathAllocators[bestMatch](request, response, bestMatch);
-
- Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch);
-
- try
- {
- pathHandlers[bestMatch](rdata);
- }
-
- // A plugin generated error indicates a request-related error
- // that has been handled by the plugin.
-
- catch (RestException r)
- {
- Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message);
- }
- }
-
- return (rdata == null) ? false : rdata.handled;
- }
-
- ///
- /// A method handler and a request allocator are stored using the designated
- /// path as a key. If an entry already exists, it is replaced by the new one.
- ///
-
- public void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
- {
- if (!IsEnabled)
- {
- return;
- }
-
- if (pathHandlers.ContainsKey(path))
- {
- Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path);
- pathHandlers.Remove(path);
- }
-
- if (pathAllocators.ContainsKey(path))
- {
- Rest.Log.DebugFormat("{0} Replacing allocator for <${1}>", MsgId, path);
- pathAllocators.Remove(path);
- }
-
- Rest.Log.DebugFormat("{0} Adding path handler for {1}", MsgId, path);
-
- pathHandlers.Add(path, mh);
- pathAllocators.Add(path, ra);
- }
- }
-}
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs
deleted file mode 100644
index 536f167..0000000
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestInventoryServices.cs
+++ /dev/null
@@ -1,2343 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Globalization;
-using System.IO;
-using System.Threading;
-using System.Timers;
-using System.Xml;
-using OpenMetaverse;
-using OpenMetaverse.Imaging;
-using OpenSim.Framework;
-
-using OpenSim.Framework.Servers;
-using OpenSim.Framework.Servers.HttpServer;
-using Timer=System.Timers.Timer;
-
-namespace OpenSim.ApplicationPlugins.Rest.Inventory
-{
- public class RestInventoryServices : IRest
- {
-// private static readonly int PARM_USERID = 0;
-// private static readonly int PARM_PATH = 1;
-
-// private bool enabled = false;
- private string qPrefix = "inventory";
-
-// private static readonly string PRIVATE_ROOT_NAME = "My Inventory";
-
- ///
- /// The constructor makes sure that the service prefix is absolute
- /// and the registers the service handler and the allocator.
- ///
-
- public RestInventoryServices()
- {
- Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId);
- Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
-
- // If a relative path was specified for the handler's domain,
- // add the standard prefix to make it absolute, e.g. /admin
-
- if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
- {
- Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
- qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
- Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
- }
-
- // Register interface using the absolute URI.
-
- Rest.Plugin.AddPathHandler(DoInventory,qPrefix,Allocate);
-
- // Activate if everything went OK
-
-// enabled = true;
-
- Rest.Log.InfoFormat("{0} Inventory services initialization complete", MsgId);
- }
-
- ///
- /// Post-construction, pre-enabled initialization opportunity
- /// Not currently exploited.
- ///
-
- public void Initialize()
- {
- }
-
- ///
- /// Called by the plug-in to halt service processing. Local processing is
- /// disabled.
- ///
-
- public void Close()
- {
-// enabled = false;
- Rest.Log.InfoFormat("{0} Inventory services closing down", MsgId);
- }
-
- ///
- /// This property is declared locally because it is used a lot and
- /// brevity is nice.
- ///
- internal string MsgId
- {
- get { return Rest.MsgId; }
- }
-
- #region Interface
-
- ///
- /// The plugin (RestHandler) calls this method to allocate the request
- /// state carrier for a new request. It is destroyed when the request
- /// completes. All request-instance specific state is kept here. This
- /// is registered when this service provider is registered.
- ///
- /// Inbound HTTP request information
- /// Outbound HTTP request information
- /// REST service domain prefix
- /// A RequestData instance suitable for this service
- private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
- {
- return (RequestData) new InventoryRequestData(request, response, prefix);
- }
-
- ///
- /// This method is registered with the handler when this service provider
- /// is initialized. It is called whenever the plug-in identifies this service
- /// provider as the best match for a given request.
- /// It handles all aspects of inventory REST processing, i.e. /admin/inventory
- ///
- /// A consolidated HTTP request work area
- private void DoInventory(RequestData hdata)
- {
-// InventoryRequestData rdata = (InventoryRequestData) hdata;
-
- Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId);
-
- // !!! REFACTORING PROBLEM
-
- //// If we're disabled, do nothing.
-
- //if (!enabled)
- //{
- // return;
- //}
-
- //// Now that we know this is a serious attempt to
- //// access inventory data, we should find out who
- //// is asking, and make sure they are authorized
- //// to do so. We need to validate the caller's
- //// identity before revealing anything about the
- //// status quo. Authenticate throws an exception
- //// via Fail if no identity information is present.
- ////
- //// With the present HTTP server we can't use the
- //// builtin authentication mechanisms because they
- //// would be enforced for all in-bound requests.
- //// Instead we look at the headers ourselves and
- //// handle authentication directly.
-
- //try
- //{
- // if (!rdata.IsAuthenticated)
- // {
- // rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
- // }
- //}
- //catch (RestException e)
- //{
- // if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
- // {
- // Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
- // Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
- // }
- // else
- // {
- // Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
- // Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
- // }
- // throw (e);
- //}
-
- //Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
-
- //// We can only get here if we are authorized
- ////
- //// The requestor may have specified an UUID or
- //// a conjoined FirstName LastName string. We'll
- //// try both. If we fail with the first, UUID,
- //// attempt, we try the other. As an example, the
- //// URI for a valid inventory request might be:
- ////
- //// http://:/admin/inventory/Arthur Dent
- ////
- //// Indicating that this is an inventory request for
- //// an avatar named Arthur Dent. This is ALL that is
- //// required to designate a GET for an entire
- //// inventory.
- ////
-
-
- //// Do we have at least a user agent name?
-
- //if (rdata.Parameters.Length < 1)
- //{
- // Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId);
- // rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
- //}
-
- //// The first parameter MUST be the agent identification, either an UUID
- //// or a space-separated First-name Last-Name specification. We check for
- //// an UUID first, if anyone names their character using a valid UUID
- //// that identifies another existing avatar will cause this a problem...
-
- //try
- //{
- // rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
- // Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
- // rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
- //}
- //catch
- //{
- // string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
- // if (names.Length == 2)
- // {
- // Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
- // rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]);
- // }
- // else
- // {
- // Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
- // rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
- // }
- //}
-
- //// If the user profile is null then either the server is broken, or the
- //// user is not known. We always assume the latter case.
-
- //if (rdata.userProfile != null)
- //{
- // Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}",
- // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
- //}
- //else
- //{
- // Rest.Log.WarnFormat("{0} No profile for {1}", MsgId, rdata.path);
- // rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
- //}
-
- //// If we get to here, then we have effectively validated the user's
- //// identity. Now we need to get the inventory. If the server does not
- //// have the inventory, we reject the request with an appropriate explanation.
- ////
- //// Note that inventory retrieval is an asynchronous event, we use the rdata
- //// class instance as the basis for our synchronization.
- ////
-
- //rdata.uuid = rdata.userProfile.ID;
-
- //if (Rest.InventoryServices.HasInventoryForUser(rdata.uuid))
- //{
- // rdata.root = Rest.InventoryServices.GetRootFolder(rdata.uuid);
-
- // Rest.Log.DebugFormat("{0} Inventory Root retrieved for {1} {2}",
- // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
-
- // Rest.InventoryServices.GetUserInventory(rdata.uuid, rdata.GetUserInventory);
-
- // Rest.Log.DebugFormat("{0} Inventory catalog requested for {1} {2}",
- // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
-
- // lock (rdata)
- // {
- // if (!rdata.HaveInventory)
- // {
- // rdata.startWD(1000);
- // rdata.timeout = false;
- // Monitor.Wait(rdata);
- // }
- // }
-
- // if (rdata.timeout)
- // {
- // Rest.Log.WarnFormat("{0} Inventory not available for {1} {2}. No response from service.",
- // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
- // rdata.Fail(Rest.HttpStatusCodeServerError, "inventory server not responding");
- // }
-
- // if (rdata.root == null)
- // {
- // Rest.Log.WarnFormat("{0} Inventory is not available [1] for agent {1} {2}",
- // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
- // rdata.Fail(Rest.HttpStatusCodeServerError, "inventory retrieval failed");
- // }
-
- //}
- //else
- //{
- // Rest.Log.WarnFormat("{0} Inventory is not locally available for agent {1} {2}",
- // MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
- // rdata.Fail(Rest.HttpStatusCodeNotFound, "no local inventory for user");
- //}
-
- //// If we get here, then we have successfully retrieved the user's information
- //// and inventory information is now available locally.
-
- //switch (rdata.method)
- //{
- // case Rest.HEAD : // Do the processing, set the status code, suppress entity
- // DoGet(rdata);
- // rdata.buffer = null;
- // break;
-
- // case Rest.GET : // Do the processing, set the status code, return entity
- // DoGet(rdata);
- // break;
-
- // case Rest.PUT : // Update named element
- // DoUpdate(rdata);
- // break;
-
- // case Rest.POST : // Add new information to identified context.
- // DoExtend(rdata);
- // break;
-
- // case Rest.DELETE : // Delete information
- // DoDelete(rdata);
- // break;
-
- // default :
- // Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
- // MsgId, rdata.method, rdata.path);
- // rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
- // String.Format("{0} not supported", rdata.method));
- // break;
- //}
- }
-
- #endregion Interface
-
- #region method-specific processing
-
- ///
- /// This method implements GET processing for inventory.
- /// Any remaining parameters are used to locate the
- /// corresponding subtree based upon node name.
- ///
- /// HTTP service request work area
-// 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));
-// }
-
- ///
- /// 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.
- ///
-
- ///
- /// POST adds new information to the inventory in the
- /// context identified by the URI.
- ///
- /// HTTP service request work area
-// 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(" Created asset {0}, UUID {1}
",
-// 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("
Created folder {0}, UUID {1}
",
-// folder.Name, folder.ID));
-// }
-// else
-// {
-// Rest.Log.DebugFormat("{0} Adding new folder", MsgId);
-// Rest.InventoryServices.AddFolder(folder);
-//
-// created = true;
-// rdata.appendStatus(String.Format("
Modified folder {0}, UUID {1}
",
-// 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("
Modified item {0}, UUID {1}
", 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("
Created item {0}, UUID {1}
", 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");
-// }
-// }
-
- ///
- /// 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.
- ///
- /// HTTP service request work area
-// 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("
Created asset {0}, UUID {1}
", 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("
Moving folder {0} UUID {1}
", f.Name, f.ID));
-// Rest.InventoryServices.MoveFolder(f);
-// }
-//
-// foreach (InventoryItemBase it in entity.Items)
-// {
-// rdata.appendStatus(String.Format("
Storing item {0} UUID {1}
", it.Name, it.ID));
-// Rest.InventoryServices.AddItem(it);
-// }
-// }
-//
-// ///
-// /// URI specifies an item to be updated
-// ///
-// ///
-// /// The entity must contain a single item node to be
-// /// updated. ID and Folder ID must be correct.
-// ///
-//
-// 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 uuids = new List();
-// 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("Storing item {0} UUID {1}
", 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));
-// }
-
- ///
- /// 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.
- ///
- /// HTTP service request work area
-// 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("
Deleted folder {0} UUID {1}
", 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 uuids = new List();
-// uuids.Add(item.ID);
-// Rest.InventoryServices.DeleteItems(item.Owner, uuids);
-// rdata.appendStatus(String.Format("Deleted item {0} UUID {1}
", item.Name, item.ID));
-// }
-//
-// rdata.Complete();
-// rdata.Respond(String.Format("Profile {0} : Normal completion", rdata.method));
-// }
-
-#endregion method-specific processing
-
- ///
- /// 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.
- ///
- ///
- /// HTTP service request work area
- /// The folder to be searched (parent)
- /// URI parameter index
- /// Should missing path members be created?
-
- 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 */
- }
-
- ///
- /// 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.
- ///
- /// HTTP service request work area
- /// The folder to be searched (parent)
- /// URI parameter index
-
- 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;
- }
- }
-
- ///
- /// 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.
- ///
-
- 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]));
-
- }
-
- ///
- /// 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.
- ///
- /// HTTP service request work area
- /// The folder to be searched (parent)
- /// pretty print indentation
- 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();
- }
-
- ///
- /// This method generates XML that describes an instance of InventoryItemBase.
- ///
- /// HTTP service request work area
- /// The item to be formatted
- /// Pretty print indentation
- 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("creatordata", String.Empty, i.CreatorData);
- 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();
- }
-
- ///
- /// 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.
- ///
- /// HTTP service request work area
- 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;
- }
-
- ///
- /// Make sure that an unchanged folder is not unnecessarily
- /// processed.
- ///
- /// Folder obtained from enclosed entity
- /// Folder obtained from the user's inventory
- private bool FolderHasChanged(InventoryFolderBase newf, InventoryFolderBase oldf)
- {
- return (newf.Name != oldf.Name
- || newf.ParentID != oldf.ParentID
- || newf.Owner != oldf.Owner
- || newf.Type != oldf.Type
- || newf.Version != oldf.Version
- );
- }
-
- ///
- /// Make sure that an unchanged item is not unnecessarily
- /// processed.
- ///
- /// Item obtained from enclosed entity
- /// Item obtained from the user's inventory
- private bool ItemHasChanged(InventoryItemBase newf, InventoryItemBase oldf)
- {
- return (newf.Name != oldf.Name
- || newf.Folder != oldf.Folder
- || newf.Description != oldf.Description
- || newf.Owner != oldf.Owner
- || newf.CreatorId != oldf.CreatorId
- || newf.AssetID != oldf.AssetID
- || newf.GroupID != oldf.GroupID
- || newf.GroupOwned != oldf.GroupOwned
- || newf.InvType != oldf.InvType
- || newf.AssetType != oldf.AssetType
- );
- }
-
- ///
- /// 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).
- ///
- /// HTTP service request work area
- 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;
- }
-
- ///
- /// This method creates an inventory Folder from the
- /// information supplied in the request's entity.
- /// A folder instance is created and initialized to reflect
- /// default values. These values are then overridden
- /// by information supplied in the entity.
- /// If context was not explicitly provided, then the
- /// appropriate ID values are determined.
- ///
-
- private void CollectFolder(XmlInventoryCollection ic)
- {
- Rest.Log.DebugFormat("{0} Interpret folder element", MsgId);
-
- InventoryFolderBase result = new InventoryFolderBase();
-
- // Default values
-
- result.Name = String.Empty;
- result.ID = UUID.Zero;
- result.Owner = ic.UserID;
- result.ParentID = UUID.Zero; // Context
- result.Type = (short) AssetType.Folder;
- result.Version = 1;
-
- if (ic.xml.HasAttributes)
- {
- for (int i = 0; i < ic.xml.AttributeCount; i++)
- {
- ic.xml.MoveToAttribute(i);
- switch (ic.xml.Name)
- {
- case "name":
- result.Name = ic.xml.Value;
- break;
- case "uuid":
- result.ID = new UUID(ic.xml.Value);
- break;
- case "parent":
- result.ParentID = new UUID(ic.xml.Value);
- break;
- case "owner":
- result.Owner = new UUID(ic.xml.Value);
- break;
- case "type":
- result.Type = Int16.Parse(ic.xml.Value);
- break;
- case "version":
- result.Version = UInt16.Parse(ic.xml.Value);
- break;
- default:
- Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}",
- MsgId, ic.xml.Name, ic.xml.Value);
- ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute <{0}>",
- ic.xml.Name));
- break;
- }
- }
- }
-
- ic.xml.MoveToElement();
-
- // The client is relying upon the reconstitution process
- // to determine the parent's UUID based upon context. This
- // is necessary where a new folder may have been
- // introduced.
-
- if (result.ParentID == UUID.Zero)
- {
- result.ParentID = ic.Parent();
- }
- else
- {
- bool found = false;
-
- foreach (InventoryFolderBase parent in ic.rdata.folders)
- {
- if (parent.ID == result.ParentID)
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}",
- MsgId, ic.Item.Folder, result.ID);
- ic.Fail(Rest.HttpStatusCodeBadRequest, "invalid parent");
- }
- }
-
- // This is a new folder, so no existing UUID is available
- // or appropriate
-
- if (result.ID == UUID.Zero)
- {
- result.ID = UUID.Random();
- }
-
- // Treat this as a new context. Any other information is
- // obsolete as a consequence.
-
- ic.Push(result);
- }
-
- ///
- /// This method is called to handle the construction of an Item
- /// instance from the supplied request entity. It is called
- /// whenever an Item start tag is detected.
- /// An instance of an Item is created and initialized to default
- /// values. These values are then overridden from values supplied
- /// as attributes to the Item element.
- /// This item is then stored in the XmlInventoryCollection and
- /// will be verified by Validate.
- /// All context is reset whenever the effective folder changes
- /// or an item is successfully validated.
- ///
- private void CollectItem(XmlInventoryCollection ic)
- {
- Rest.Log.DebugFormat("{0} Interpret item element", MsgId);
-
- InventoryItemBase result = new InventoryItemBase();
-
- result.Name = String.Empty;
- result.Description = String.Empty;
- result.ID = UUID.Zero;
- result.Folder = UUID.Zero;
- result.Owner = ic.UserID;
- result.CreatorId = ic.UserID.ToString();
- result.AssetID = UUID.Zero;
- result.GroupID = UUID.Zero;
- result.GroupOwned = false;
- result.InvType = (int) InventoryType.Unknown;
- result.AssetType = (int) AssetType.Unknown;
-
- if (ic.xml.HasAttributes)
- {
- for (int i = 0; i < ic.xml.AttributeCount; i++)
- {
- ic.xml.MoveToAttribute(i);
-
- switch (ic.xml.Name)
- {
- case "name":
- result.Name = ic.xml.Value;
- break;
- case "desc":
- result.Description = ic.xml.Value;
- break;
- case "uuid":
- result.ID = new UUID(ic.xml.Value);
- break;
- case "folder":
- result.Folder = new UUID(ic.xml.Value);
- break;
- case "owner":
- result.Owner = new UUID(ic.xml.Value);
- break;
- case "invtype":
- result.InvType = Int32.Parse(ic.xml.Value);
- break;
- case "creator":
- result.CreatorId = ic.xml.Value;
- break;
- case "assettype":
- result.AssetType = Int32.Parse(ic.xml.Value);
- break;
- case "groupowned":
- result.GroupOwned = Boolean.Parse(ic.xml.Value);
- break;
- case "groupid":
- result.GroupID = new UUID(ic.xml.Value);
- break;
- case "flags":
- result.Flags = UInt32.Parse(ic.xml.Value);
- break;
- case "creationdate":
- result.CreationDate = Int32.Parse(ic.xml.Value);
- break;
- case "saletype":
- result.SaleType = Byte.Parse(ic.xml.Value);
- break;
- case "saleprice":
- result.SalePrice = Int32.Parse(ic.xml.Value);
- break;
-
- default:
- Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}",
- MsgId, ic.xml.Name, ic.xml.Value);
- ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute",
- ic.xml.Name));
- break;
- }
- }
- }
-
- ic.xml.MoveToElement();
-
- ic.Push(result);
- }
-
- ///
- /// This method assembles an asset instance from the
- /// information supplied in the request's entity. It is
- /// called as a result of detecting a start tag for a
- /// type of Asset.
- /// The information is collected locally, and an asset
- /// instance is created only if the basic XML parsing
- /// completes successfully.
- /// Default values for all parts of the asset are
- /// established before overriding them from the supplied
- /// XML.
- /// If an asset has inline=true as an attribute, then
- /// the element contains the data representing the
- /// asset. This is saved as the data component.
- /// inline=false means that the element's payload is
- /// simply the UUID of the asset referenced by the
- /// item being constructed.
- /// An asset, if created is stored in the
- /// XmlInventoryCollection
- ///
- private void CollectAsset(XmlInventoryCollection ic)
- {
- Rest.Log.DebugFormat("{0} Interpret asset element", MsgId);
-
- string name = String.Empty;
- string desc = String.Empty;
- sbyte type = (sbyte) AssetType.Unknown;
- bool temp = false;
- bool local = false;
-
- // This is not a persistent attribute
- bool inline = false;
-
- UUID uuid = UUID.Zero;
-
- // Attribute is optional
- if (ic.xml.HasAttributes)
- {
- for (int i = 0; i < ic.xml.AttributeCount; i++)
- {
- ic.xml.MoveToAttribute(i);
- switch (ic.xml.Name)
- {
- case "name" :
- name = ic.xml.Value;
- break;
-
- case "type" :
- type = SByte.Parse(ic.xml.Value);
- break;
-
- case "description" :
- desc = ic.xml.Value;
- break;
-
- case "temporary" :
- temp = Boolean.Parse(ic.xml.Value);
- break;
-
- case "uuid" :
- uuid = new UUID(ic.xml.Value);
- break;
-
- case "inline" :
- inline = Boolean.Parse(ic.xml.Value);
- break;
-
- case "local" :
- local = Boolean.Parse(ic.xml.Value);
- break;
-
- default :
- Rest.Log.DebugFormat("{0} Asset: Unrecognized attribute: {1}:{2}",
- MsgId, ic.xml.Name, ic.xml.Value);
- ic.Fail(Rest.HttpStatusCodeBadRequest,
- String.Format("unrecognized attribute <{0}>", ic.xml.Name));
- break;
- }
- }
- }
-
- ic.xml.MoveToElement();
-
- // If this is a reference to an existing asset, just store the
- // asset ID into the item.
-
- if (!inline)
- {
- if (ic.Item != null)
- {
- ic.Item.AssetID = new UUID(ic.xml.ReadElementContentAsString());
- Rest.Log.DebugFormat("{0} Asset ID supplied: {1}", MsgId, ic.Item.AssetID);
- }
- else
- {
- Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId);
- ic.Fail(Rest.HttpStatusCodeBadRequest, "no context for asset");
- }
- }
-
- // Otherwise, generate an asset ID, store that into the item, and
- // create an entry in the asset list for the inlined asset. But
- // only if the size is non-zero.
-
- else
- {
- AssetBase asset = null;
- string b64string = null;
-
- // Generate a UUID if none were given, and generally none should
- // be. Ever.
-
- if (uuid == UUID.Zero)
- {
- uuid = UUID.Random();
- }
-
- // Create AssetBase entity to hold the inlined asset
-
- asset = new AssetBase(uuid, name, type, UUID.Zero.ToString());
-
- asset.Description = desc;
- asset.Local = local;
- asset.Temporary = temp;
-
- b64string = ic.xml.ReadElementContentAsString();
-
- Rest.Log.DebugFormat("{0} Data length is {1}", MsgId, b64string.Length);
- Rest.Log.DebugFormat("{0} Data content starts with: \n\t<{1}>", MsgId,
- b64string.Substring(0, b64string.Length > 132 ? 132 : b64string.Length));
-
- asset.Data = Convert.FromBase64String(b64string);
-
- // Ensure the asset always has some kind of data component
-
- if (asset.Data == null)
- {
- asset.Data = new byte[1];
- }
-
- // If this is in the context of an item, establish
- // a link with the item in context.
-
- if (ic.Item != null && ic.Item.AssetID == UUID.Zero)
- {
- ic.Item.AssetID = uuid;
- }
-
- ic.Push(asset);
- }
- }
-
- ///
- /// Store any permissions information provided by the request.
- /// This overrides the default permissions set when the
- /// XmlInventoryCollection object was created.
- ///
- private void CollectPermissions(XmlInventoryCollection ic)
- {
- if (ic.xml.HasAttributes)
- {
- for (int i = 0; i < ic.xml.AttributeCount; i++)
- {
- ic.xml.MoveToAttribute(i);
- switch (ic.xml.Name)
- {
- case "current":
- ic.CurrentPermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
- break;
- case "next":
- ic.NextPermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
- break;
- case "group":
- ic.GroupPermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
- break;
- case "everyone":
- ic.EveryOnePermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
- break;
- case "base":
- ic.BasePermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
- break;
- default:
- Rest.Log.DebugFormat("{0} Permissions: invalid attribute {1}:{2}",
- MsgId,ic.xml.Name, ic.xml.Value);
- ic.Fail(Rest.HttpStatusCodeBadRequest,
- String.Format("invalid attribute <{0}>", ic.xml.Name));
- break;
- }
- }
- }
-
- ic.xml.MoveToElement();
- }
-
- ///
- /// This method is called whenever an Item has been successfully
- /// reconstituted from the request's entity.
- /// It uses the information curren tin the XmlInventoryCollection
- /// to complete the item's specification, including any implied
- /// context and asset associations.
- /// It fails the request if any necessary item or asset information
- /// is missing.
- ///
-
- private void Validate(XmlInventoryCollection ic)
- {
- // There really should be an item present if we've
- // called validate. So fail if there is not.
-
- if (ic.Item == null)
- {
- Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId);
- ic.Fail(Rest.HttpStatusCodeBadRequest, "request parse error");
- }
-
- // Every item is required to have a name (via REST anyway)
-
- if (ic.Item.Name == String.Empty)
- {
- Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId);
- ic.Fail(Rest.HttpStatusCodeBadRequest, "item name required");
- }
-
- // An item MUST have an asset ID. AssetID should never be zero
- // here. It should always get set from the information stored
- // when the Asset element was processed.
-
- if (ic.Item.AssetID == UUID.Zero)
- {
- Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId);
- Rest.Log.InfoFormat("{0} Asset information is missing", MsgId);
- ic.Fail(Rest.HttpStatusCodeBadRequest, "asset information required");
- }
-
- // If the item is new, then assign it an ID
-
- if (ic.Item.ID == UUID.Zero)
- {
- ic.Item.ID = UUID.Random();
- }
-
- // If the context is being implied, obtain the current
- // folder item's ID. If it was specified explicitly, make
- // sure that theparent folder exists.
-
- if (ic.Item.Folder == UUID.Zero)
- {
- ic.Item.Folder = ic.Parent();
- }
- else
- {
- bool found = false;
-
- foreach (InventoryFolderBase parent in ic.rdata.folders)
- {
- if (parent.ID == ic.Item.Folder)
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}",
- MsgId, ic.Item.Folder, ic.Item.ID);
- ic.Fail(Rest.HttpStatusCodeBadRequest, "parent information required");
- }
- }
-
- // If this is an inline asset being constructed in the context
- // of a new Item, then use the itm's name here too.
-
- if (ic.Asset != null)
- {
- if (ic.Asset.Name == String.Empty)
- ic.Asset.Name = ic.Item.Name;
- if (ic.Asset.Description == String.Empty)
- ic.Asset.Description = ic.Item.Description;
- }
-
- // Assign permissions
-
- ic.Item.CurrentPermissions = ic.CurrentPermissions;
- ic.Item.EveryOnePermissions = ic.EveryOnePermissions;
- ic.Item.BasePermissions = ic.BasePermissions;
- ic.Item.GroupPermissions = ic.GroupPermissions;
- ic.Item.NextPermissions = ic.NextPermissions;
-
- // If no type was specified for this item, we can attempt to
- // infer something from the file type maybe. This is NOT as
- // good as having type be specified in the XML.
-
- if (ic.Item.AssetType == (int) AssetType.Unknown ||
- ic.Item.InvType == (int) InventoryType.Unknown)
- {
- Rest.Log.DebugFormat("{0} Attempting to infer item type", MsgId);
-
- string[] parts = ic.Item.Name.Split(Rest.CA_PERIOD);
-
- if (Rest.DEBUG)
- {
- for (int i = 0; i < parts.Length; i++)
- {
- Rest.Log.DebugFormat("{0} Name part {1} : {2}",
- MsgId, i, parts[i]);
- }
- }
-
- // If the associated item name is multi-part, then maybe
- // the last part will indicate the item type - if we're
- // lucky.
-
- if (parts.Length > 1)
- {
- Rest.Log.DebugFormat("{0} File type is {1}",
- MsgId, parts[parts.Length - 1]);
- switch (parts[parts.Length - 1])
- {
- case "jpeg2000" :
- case "jpeg-2000" :
- case "jpg2000" :
- case "jpg-2000" :
- Rest.Log.DebugFormat("{0} Type {1} inferred",
- MsgId, parts[parts.Length-1]);
- if (ic.Item.AssetType == (int) AssetType.Unknown)
- ic.Item.AssetType = (int) AssetType.ImageJPEG;
- if (ic.Item.InvType == (int) InventoryType.Unknown)
- ic.Item.InvType = (int) InventoryType.Texture;
- break;
- case "jpg" :
- case "jpeg" :
- Rest.Log.DebugFormat("{0} Type {1} inferred",
- MsgId, parts[parts.Length - 1]);
- if (ic.Item.AssetType == (int) AssetType.Unknown)
- ic.Item.AssetType = (int) AssetType.ImageJPEG;
- if (ic.Item.InvType == (int) InventoryType.Unknown)
- ic.Item.InvType = (int) InventoryType.Texture;
- break;
- case "tga" :
- if (parts[parts.Length - 2].IndexOf("_texture") != -1)
- {
- if (ic.Item.AssetType == (int) AssetType.Unknown)
- ic.Item.AssetType = (int) AssetType.TextureTGA;
- if (ic.Item.InvType == (int) AssetType.Unknown)
- ic.Item.InvType = (int) InventoryType.Texture;
- }
- else
- {
- if (ic.Item.AssetType == (int) AssetType.Unknown)
- ic.Item.AssetType = (int) AssetType.ImageTGA;
- if (ic.Item.InvType == (int) InventoryType.Unknown)
- ic.Item.InvType = (int) InventoryType.Snapshot;
- }
- break;
- default :
- Rest.Log.DebugFormat("{0} Asset/Inventory type could not be inferred for {1}",
- MsgId,ic.Item.Name);
- break;
- }
- }
- }
-
- /// If this is a TGA remember the fact
-
- if (ic.Item.AssetType == (int) AssetType.TextureTGA ||
- ic.Item.AssetType == (int) AssetType.ImageTGA)
- {
- Bitmap temp;
- Stream tgadata = new MemoryStream(ic.Asset.Data);
-
- temp = LoadTGAClass.LoadTGA(tgadata);
- try
- {
- ic.Asset.Data = OpenJPEG.EncodeFromImage(temp, true);
- }
- catch (DllNotFoundException)
- {
- Rest.Log.ErrorFormat("OpenJpeg is not installed correctly on this system. Asset Data is empty for {0}", ic.Item.Name);
- ic.Asset.Data = new Byte[0];
- }
- catch (IndexOutOfRangeException)
- {
- Rest.Log.ErrorFormat("OpenJpeg was unable to encode this. Asset Data is empty for {0}", ic.Item.Name);
- ic.Asset.Data = new Byte[0];
- }
- catch (Exception)
- {
- Rest.Log.ErrorFormat("OpenJpeg was unable to encode this. Asset Data is empty for {0}", ic.Item.Name);
- ic.Asset.Data = new Byte[0];
- }
- }
-
- ic.reset();
- }
-
- #region Inventory RequestData extension
-
- internal class InventoryRequestData : RequestData
- {
- ///
- /// These are the inventory specific request/response state
- /// extensions.
- ///
-
- internal UUID uuid = UUID.Zero;
- internal bool HaveInventory = false;
- internal ICollection folders = null;
- internal ICollection items = null;
- internal UserProfileData userProfile = null;
- internal InventoryFolderBase root = null;
- internal bool timeout = false;
- internal Timer watchDog = new Timer();
-
- internal InventoryRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
- : base(request, response, prefix)
- {
- }
-
- internal void startWD(double interval)
- {
- Rest.Log.DebugFormat("{0} Setting watchdog", MsgId);
- watchDog.Elapsed += new ElapsedEventHandler(OnTimeOut);
- watchDog.Interval = interval;
- watchDog.AutoReset = false;
- watchDog.Enabled = true;
- lock (watchDog)
- watchDog.Start();
-
- }
-
- internal void stopWD()
- {
- Rest.Log.DebugFormat("{0} Reset watchdog", MsgId);
- lock (watchDog)
- watchDog.Stop();
- }
-
- ///
- /// This is the callback method required by the inventory watchdog. The
- /// requestor issues an inventory request and then blocks until the
- /// request completes, or this method signals the monitor.
- ///
-
- private void OnTimeOut(object sender, ElapsedEventArgs args)
- {
- Rest.Log.DebugFormat("{0} Asynchronous inventory update timed-out", MsgId);
- // InventoryRequestData rdata = (InventoryRequestData) sender;
- lock (this)
- {
- this.folders = null;
- this.items = null;
- this.HaveInventory = false;
- this.timeout = true;
- Monitor.Pulse(this);
- }
- }
-
- ///
- /// This is the callback method required by inventory services. The
- /// requestor issues an inventory request and then blocks until this
- /// method signals the monitor.
- ///
-
- internal void GetUserInventory(ICollection folders, ICollection items)
- {
- Rest.Log.DebugFormat("{0} Asynchronously updating inventory data", MsgId);
- lock (this)
- {
- if (watchDog.Enabled)
- {
- this.stopWD();
- }
- this.folders = folders;
- this.items = items;
- this.HaveInventory = true;
- this.timeout = false;
- Monitor.Pulse(this);
- }
- }
- }
-
- #endregion Inventory RequestData extension
-
- ///
- /// This class is used to record and manage the hierarchy
- /// constructed from the entity supplied in the request for
- /// PUT and POST.
- ///
-
- internal class XmlInventoryCollection : InventoryCollection
- {
- internal InventoryRequestData rdata;
- private Stack stk;
-
- internal List Assets;
-
- internal InventoryItemBase Item;
- internal AssetBase Asset;
- internal XmlReader xml;
-
- internal /*static*/ const uint DefaultCurrent = 0x7FFFFFFF;
- internal /*static*/ const uint DefaultNext = 0x82000;
- internal /*static*/ const uint DefaultBase = 0x7FFFFFFF;
- internal /*static*/ const uint DefaultEveryOne = 0x0;
- internal /*static*/ const uint DefaultGroup = 0x0;
-
- internal uint CurrentPermissions = 0x00;
- internal uint NextPermissions = 0x00;
- internal uint BasePermissions = 0x00;
- internal uint EveryOnePermissions = 0x00;
- internal uint GroupPermissions = 0x00;
-
- internal XmlInventoryCollection()
- {
- Folders = new List();
- Items = new List();
- Assets = new List();
- }
-
- internal void init(InventoryRequestData p_rdata)
- {
- rdata = p_rdata;
- UserID = rdata.uuid;
- stk = new Stack();
- rdata.initXmlReader();
- xml = rdata.reader;
- initPermissions();
- }
-
- internal void initPermissions()
- {
- CurrentPermissions = DefaultCurrent;
- NextPermissions = DefaultNext;
- BasePermissions = DefaultBase;
- GroupPermissions = DefaultGroup;
- EveryOnePermissions = DefaultEveryOne;
- }
-
- internal UUID Parent()
- {
- if (stk.Count != 0)
- {
- return stk.Peek().ID;
- }
- else
- {
- return UUID.Zero;
- }
- }
-
- internal void Push(InventoryFolderBase folder)
- {
- stk.Push(folder);
- Folders.Add(folder);
- reset();
- }
-
- internal void Push(InventoryItemBase item)
- {
- Item = item;
- Items.Add(item);
- }
-
- internal void Push(AssetBase asset)
- {
- Asset = asset;
- Assets.Add(asset);
- }
-
- internal void Pop()
- {
- stk.Pop();
- reset();
- }
-
- internal void reset()
- {
- Item = null;
- Asset = null;
- initPermissions();
- }
-
- internal void Fail(int code, string addendum)
- {
- rdata.Fail(code, addendum);
- }
- }
- }
-}
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestTestServices.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestTestServices.cs
deleted file mode 100644
index 81596a3..0000000
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestTestServices.cs
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using OpenSim.Framework.Servers;
-using OpenSim.Framework.Servers.HttpServer;
-
-namespace OpenSim.ApplicationPlugins.Rest.Inventory
-{
- public class RestTestServices : IRest
- {
- private bool enabled = false;
- private string qPrefix = "test";
-
- // A simple constructor is used to handle any once-only
- // initialization of working classes.
-
- public RestTestServices()
- {
- Rest.Log.InfoFormat("{0} Test services initializing", MsgId);
- Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
-
- // If a relative path was specified, make it absolute by adding
- // the standard prefix, e.g. /admin
-
- if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
- {
- Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
- qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
- Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
- }
-
- // Load test cases
-
- loadTests();
- foreach (ITest test in tests)
- {
- test.Initialize();
- }
-
- // Register interface
-
- Rest.Plugin.AddPathHandler(DoTests,qPrefix,Allocate);
-
- // Activate
-
- enabled = true;
-
- Rest.Log.InfoFormat("{0} Test services initialization complete", MsgId);
- }
-
- // Post-construction, pre-enabled initialization opportunity
- // Not currently exploited.
-
- public void Initialize()
- {
- }
-
- // Called by the plug-in to halt REST processing. Local processing is
- // disabled, and control blocks until all current processing has
- // completed. No new processing will be started
-
- public void Close()
- {
- enabled = false;
- foreach (ITest test in tests)
- {
- test.Close();
- }
- Rest.Log.InfoFormat("{0} Test services closing down", MsgId);
- }
-
- // Properties
-
- internal string MsgId
- {
- get { return Rest.MsgId; }
- }
-
- #region Interface
-
- private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
- {
- return new RequestData(request, response, prefix);
- }
-
- // Inventory Handler
-
- private void DoTests(RequestData rdata)
- {
- if (!enabled)
- return;
-
- // Now that we know this is a serious attempt to
- // access inventory data, we should find out who
- // is asking, and make sure they are authorized
- // to do so. We need to validate the caller's
- // identity before revealing anything about the
- // status quo. Authenticate throws an exception
- // via Fail if no identity information is present.
- //
- // With the present HTTP server we can't use the
- // builtin authentication mechanisms because they
- // would be enforced for all in-bound requests.
- // Instead we look at the headers ourselves and
- // handle authentication directly.
-
- try
- {
- if (!rdata.IsAuthenticated)
- {
- rdata.Fail(Rest.HttpStatusCodeNotAuthorized,
- String.Format("user \"{0}\" could not be authenticated", rdata.userName));
- }
- }
- catch (RestException e)
- {
- if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
- {
- Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
- Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
- }
- else
- {
- Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
- Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
- }
- throw (e);
- }
-
- // Check that a test was specified
-
- if (rdata.Parameters.Length < 1)
- {
- Rest.Log.DebugFormat("{0} Insufficient parameters", MsgId);
- rdata.Fail(Rest.HttpStatusCodeBadRequest, "not enough parameters");
- }
-
- // Select the test
-
- foreach (ITest test in tests)
- {
- if (!rdata.handled)
- test.Execute(rdata);
- }
- }
-
- #endregion Interface
-
- private static bool testsLoaded = false;
- private static List classes = new List();
- private static List tests = new List();
- private static Type[] parms = new Type[0];
- private static Object[] args = new Object[0];
-
- static RestTestServices()
- {
- Module[] mods = Assembly.GetExecutingAssembly().GetModules();
- foreach (Module m in mods)
- {
- Type[] types = m.GetTypes();
- foreach (Type t in types)
- {
- try
- {
- if (t.GetInterface("ITest") != null)
- {
- classes.Add(t);
- }
- }
- catch (Exception e)
- {
- Rest.Log.WarnFormat("[STATIC-TEST] Unable to include test {0} : {1}", t, e.Message);
- }
- }
- }
- }
-
- ///
- /// This routine loads all of the handlers discovered during
- /// instance initialization. Each handler is responsible for
- /// registering itself with this handler.
- /// I was not able to make this code work in a constructor.
- ///
-
- private void loadTests()
- {
- lock (tests)
- {
- if (!testsLoaded)
- {
-
- ConstructorInfo ci;
- Object ht;
-
- foreach (Type t in classes)
- {
- try
- {
- if (t.GetInterface("ITest") != null)
- {
- ci = t.GetConstructor(parms);
- ht = ci.Invoke(args);
- tests.Add((ITest)ht);
- Rest.Log.InfoFormat("{0} Test {1} added", MsgId, t);
- }
- }
- catch (Exception e)
- {
- Rest.Log.WarnFormat("{0} Unable to load test {1} : {2}", MsgId, t, e.Message);
- }
- }
- testsLoaded = true;
- }
- }
- }
-
- }
-}
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/ITest.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/tests/ITest.cs
deleted file mode 100644
index eafc154..0000000
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/ITest.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-* Copyright (c) Contributors, http://opensimulator.org/
-* See CONTRIBUTORS.TXT for a full list of copyright holders.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are met:
-* * Redistributions of source code must retain the above copyright
-* notice, this list of conditions and the following disclaimer.
-* * Redistributions in binary form must reproduce the above copyright
-* notice, this list of conditions and the following disclaimer in the
-* documentation and/or other materials provided with the distribution.
-* * Neither the name of the OpenSimulator Project nor the
-* names of its contributors may be used to endorse or promote products
-* derived from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
-* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-*/
-
-namespace OpenSim.ApplicationPlugins.Rest.Inventory
-{
-
- ///
- /// This interface represents the boundary between the general purpose
- /// REST plugin handling, and the functionally specific handlers. The
- /// handler knows only to initialzie and terminate all such handlers
- /// that it finds.
- ///
-
- internal interface ITest
- {
- void Initialize();
- void Execute(RequestData rdata);
- void Close();
- }
-
-}
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/Remote.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/tests/Remote.cs
deleted file mode 100644
index 1c1afd0..0000000
--- a/OpenSim/ApplicationPlugins/Rest/Inventory/tests/Remote.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using OpenMetaverse;
-using OpenSim.Region.Framework.Scenes;
-
-namespace OpenSim.ApplicationPlugins.Rest.Inventory.Tests
-{
- public class Remote : ITest
- {
- private static readonly int PARM_TESTID = 0;
- private static readonly int PARM_COMMAND = 1;
-
- private static readonly int PARM_MOVE_AVATAR = 2;
- private static readonly int PARM_MOVE_X = 3;
- private static readonly int PARM_MOVE_Y = 4;
- private static readonly int PARM_MOVE_Z = 5;
-
- private bool enabled = false;
-
- // No constructor code is required.
-
- public Remote()
- {
- Rest.Log.InfoFormat("{0} Remote services constructor", MsgId);
- }
-
- // Post-construction, pre-enabled initialization opportunity
- // Not currently exploited.
-
- public void Initialize()
- {
- enabled = true;
- Rest.Log.InfoFormat("{0} Remote services initialized", MsgId);
- }
-
- // Called by the plug-in to halt REST processing. Local processing is
- // disabled, and control blocks until all current processing has
- // completed. No new processing will be started
-
- public void Close()
- {
- enabled = false;
- Rest.Log.InfoFormat("{0} Remote services closing down", MsgId);
- }
-
- // Properties
-
- internal string MsgId
- {
- get { return Rest.MsgId; }
- }
-
- // Remote Handler
- // Key information of interest here is the Parameters array, each
- // entry represents an element of the URI, with element zero being
- // the
-
- public void Execute(RequestData rdata)
- {
- if (!enabled) return;
-
- // If we can't relate to what's there, leave it for others.
-
- if (rdata.Parameters.Length == 0 || rdata.Parameters[PARM_TESTID] != "remote")
- return;
-
- Rest.Log.DebugFormat("{0} REST Remote handler ENTRY", MsgId);
-
- // Remove the prefix and what's left are the parameters. If we don't have
- // the parameters we need, fail the request. Parameters do NOT include
- // any supplied query values.
-
- if (rdata.Parameters.Length > 1)
- {
- switch (rdata.Parameters[PARM_COMMAND].ToLower())
- {
- case "move" :
- DoMove(rdata);
- break;
- default :
- DoHelp(rdata);
- break;
- }
- }
- else
- {
- DoHelp(rdata);
- }
- }
-
- private void DoHelp(RequestData rdata)
- {
- rdata.body = Help;
- rdata.Complete();
- rdata.Respond("Help");
- }
-
- private void DoMove(RequestData rdata)
- {
- if (rdata.Parameters.Length < 6)
- {
- Rest.Log.WarnFormat("{0} Move: No movement information provided", MsgId);
- rdata.Fail(Rest.HttpStatusCodeBadRequest, "no movement information provided");
- }
- else
- {
- string[] names = rdata.Parameters[PARM_MOVE_AVATAR].Split(Rest.CA_SPACE);
- ScenePresence presence = null;
- Scene scene = null;
-
- if (names.Length != 2)
- {
- rdata.Fail(Rest.HttpStatusCodeBadRequest,
- String.Format("invalid avatar name: <{0}>",rdata.Parameters[PARM_MOVE_AVATAR]));
- }
-
- Rest.Log.WarnFormat("{0} '{1}' command received for {2} {3}",
- MsgId, rdata.Parameters[0], names[0], names[1]);
-
- // The first parameter should be an avatar name, look for the
- // avatar in the known regions first.
-
- Rest.main.SceneManager.ForEachScene(delegate(Scene s)
- {
- s.ForEachRootScenePresence(delegate(ScenePresence sp)
- {
- if (sp.Firstname == names[0] && sp.Lastname == names[1])
- {
- scene = s;
- presence = sp;
- }
- });
- });
-
- if (presence != null)
- {
- Rest.Log.DebugFormat("{0} Move : Avatar {1} located in region {2}",
- MsgId, rdata.Parameters[PARM_MOVE_AVATAR], scene.RegionInfo.RegionName);
-
- try
- {
- float x = Convert.ToSingle(rdata.Parameters[PARM_MOVE_X]);
- float y = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Y]);
- float z = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Z]);
- Vector3 vector = new Vector3(x, y, z);
- presence.MoveToTarget(vector, false, false);
- }
- catch (Exception e)
- {
- rdata.Fail(Rest.HttpStatusCodeBadRequest,
- String.Format("invalid parameters: {0}", e.Message));
- }
- }
- else
- {
- rdata.Fail(Rest.HttpStatusCodeBadRequest,
- String.Format("avatar {0} not present", rdata.Parameters[PARM_MOVE_AVATAR]));
- }
-
- rdata.Complete();
- rdata.Respond("OK");
- }
- }
-
- private static readonly string Help =
- ""
- + "Remote Command Usage"
- + ""
- + "Supported commands are:
"
- + ""
- + "- move/avatar-name/x/y/z
"
- + "- moves the specified avatar to another location
"
- + "
"
- + ""
- + ""
- ;
- }
-}
--
cgit v1.1