Finally remove the 'REST' ApplicationPlugins code which has been non-functional and largely commented out for many years.
parent
35843e8ec8
commit
03075359b5
|
@ -1,43 +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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This interface represents the boundary between the general purpose
|
|
||||||
/// REST plugin handling, and the functionally specific handlers. The
|
|
||||||
/// handler knows only to initialize and terminate all such handlers
|
|
||||||
/// that it finds. Implementing this interface identifies the class as
|
|
||||||
/// a REST handler implementation.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal interface IRest
|
|
||||||
{
|
|
||||||
void Initialize();
|
|
||||||
void Close();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +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 OpenSim.Framework.Servers.HttpServer;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <remarks>
|
|
||||||
/// The handler delegates are not noteworthy. The allocator allows
|
|
||||||
/// a given handler to optionally subclass the base RequestData
|
|
||||||
/// structure to carry any locally required per-request state
|
|
||||||
/// needed.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
public delegate void RestMethodHandler(RequestData rdata);
|
|
||||||
public delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response, string path);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This interface exports the generic plugin-handling services
|
|
||||||
/// available to each loaded REST services module (IRest implementation)
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal interface IRestHandler
|
|
||||||
{
|
|
||||||
|
|
||||||
string MsgId { get; }
|
|
||||||
string RequestId { get; }
|
|
||||||
|
|
||||||
void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ma);
|
|
||||||
void AddStreamHandler(string httpMethod, string path, RestMethod method);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +0,0 @@
|
||||||
<Addin id="OpenSim.ApplicationPlugins.Rest.Inventory" version="0.1">
|
|
||||||
<Runtime>
|
|
||||||
<Import assembly="OpenSim.ApplicationPlugins.Rest.Inventory.dll"/>
|
|
||||||
</Runtime>
|
|
||||||
<Dependencies>
|
|
||||||
<Addin id="OpenSim" version="0.5" />
|
|
||||||
</Dependencies>
|
|
||||||
<Extension path = "/OpenSim/Startup">
|
|
||||||
<Plugin id="RestInventory" type="OpenSim.ApplicationPlugins.Rest.Inventory.RestHandler" />
|
|
||||||
</Extension>
|
|
||||||
</Addin>
|
|
|
@ -1,551 +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 System.Text;
|
|
||||||
using log4net;
|
|
||||||
using Nini.Config;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Communications;
|
|
||||||
using OpenSim.Services.Interfaces;
|
|
||||||
using IAvatarService = OpenSim.Services.Interfaces.IAvatarService;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
public class Rest
|
|
||||||
{
|
|
||||||
internal static readonly ILog Log =
|
|
||||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
||||||
|
|
||||||
internal static bool DEBUG = Log.IsDebugEnabled;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Supported authentication schemes
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public const string AS_BASIC = "Basic"; // simple user/password verification
|
|
||||||
public const string AS_DIGEST = "Digest"; // password safe authentication
|
|
||||||
|
|
||||||
/// Supported Digest algorithms
|
|
||||||
|
|
||||||
public const string Digest_MD5 = "MD5"; // assumed default if omitted
|
|
||||||
public const string Digest_MD5Sess = "MD5-sess"; // session-span - not good for REST?
|
|
||||||
|
|
||||||
public const string Qop_Auth = "auth"; // authentication only
|
|
||||||
public const string Qop_Int = "auth-int"; // TODO
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These values have a single value for the whole
|
|
||||||
/// domain and lifetime of the plugin handler. We
|
|
||||||
/// make them static for ease of reference within
|
|
||||||
/// the assembly. These are initialized by the
|
|
||||||
/// RestHandler class during start-up.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal static IRestHandler Plugin = null;
|
|
||||||
internal static OpenSimBase main = null;
|
|
||||||
internal static string Prefix = null;
|
|
||||||
internal static IConfig Config = null;
|
|
||||||
internal static string GodKey = null;
|
|
||||||
internal static bool Authenticate = true;
|
|
||||||
internal static bool Secure = true;
|
|
||||||
internal static bool ExtendedEscape = true;
|
|
||||||
internal static bool DumpAsset = false;
|
|
||||||
internal static bool Fill = true;
|
|
||||||
internal static bool FlushEnabled = true;
|
|
||||||
internal static string Realm = "OpenSim REST";
|
|
||||||
internal static string Scheme = AS_BASIC;
|
|
||||||
internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These are all dependent upon the Comms manager
|
|
||||||
/// being initialized. So they have to be properties
|
|
||||||
/// because the comms manager is now a module and is
|
|
||||||
/// not guaranteed to be there when the rest handler
|
|
||||||
/// initializes.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal static IInventoryService InventoryServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.InventoryService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IUserAccountService UserServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.UserAccountService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IAuthenticationService AuthServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.AuthenticationService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IAvatarService AvatarServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.AvatarService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IAssetService AssetServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.AssetService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTTP requires that status information be generated for PUT
|
|
||||||
/// and POST opertaions. This is in support of that. The
|
|
||||||
/// operation verb gets substituted into the first string,
|
|
||||||
/// and the completion code is inserted into the tail. The
|
|
||||||
/// strings are put here to encourage consistency.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal static string statusHead = "<html><body><title>{0} status</title><break>";
|
|
||||||
internal static string statusTail = "</body></html>";
|
|
||||||
|
|
||||||
internal static Dictionary<int,string> HttpStatusDesc;
|
|
||||||
|
|
||||||
static Rest()
|
|
||||||
{
|
|
||||||
HttpStatusDesc = new Dictionary<int,string>();
|
|
||||||
if (HttpStatusCodeArray.Length != HttpStatusDescArray.Length)
|
|
||||||
{
|
|
||||||
Log.ErrorFormat("{0} HTTP Status Code and Description arrays do not match");
|
|
||||||
throw new Exception("HTTP Status array discrepancy");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repackage the data into something more tractable. The sparse
|
|
||||||
// nature of HTTP return codes makes an array a bad choice.
|
|
||||||
|
|
||||||
for (int i=0; i<HttpStatusCodeArray.Length; i++)
|
|
||||||
{
|
|
||||||
HttpStatusDesc.Add(HttpStatusCodeArray[i], HttpStatusDescArray[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static int CreationDate
|
|
||||||
{
|
|
||||||
get { return (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string MsgId
|
|
||||||
{
|
|
||||||
get { return Plugin.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string RequestId
|
|
||||||
{
|
|
||||||
get { return Plugin.RequestId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Encoding Encoding = Util.UTF8;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Version control for REST implementation. This
|
|
||||||
/// refers to the overall infrastructure represented
|
|
||||||
/// by the following classes
|
|
||||||
/// RequestData
|
|
||||||
/// RequestInventoryPlugin
|
|
||||||
/// Rest
|
|
||||||
/// It does no describe implementation classes such as
|
|
||||||
/// RestInventoryServices, which may morph much more
|
|
||||||
/// often. Such classes ARE dependent upon this however
|
|
||||||
/// and should check it in their Initialize method.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public static readonly float Version = 1.0F;
|
|
||||||
public const string Name = "REST 1.0";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Currently defined HTTP methods.
|
|
||||||
/// Only GET and HEAD are required to be
|
|
||||||
/// supported by all servers. See Respond
|
|
||||||
/// to see how these are handled.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
// REST AGENT 1.0 interpretations
|
|
||||||
public const string GET = "get"; // information retrieval - server state unchanged
|
|
||||||
public const string HEAD = "head"; // same as get except only the headers are returned.
|
|
||||||
public const string POST = "post"; // Replace the URI designated resource with the entity.
|
|
||||||
public const string PUT = "put"; // Add the entity to the context represented by the URI
|
|
||||||
public const string DELETE = "delete"; // Remove the URI designated resource from the server.
|
|
||||||
|
|
||||||
public const string OPTIONS = "options"; //
|
|
||||||
public const string TRACE = "trace"; //
|
|
||||||
public const string CONNECT = "connect"; //
|
|
||||||
|
|
||||||
// Define this in one place...
|
|
||||||
|
|
||||||
public const string UrlPathSeparator = "/";
|
|
||||||
public const string UrlMethodSeparator = ":";
|
|
||||||
|
|
||||||
// Redirection qualifications
|
|
||||||
|
|
||||||
public const bool PERMANENT = false;
|
|
||||||
public const bool TEMPORARY = true;
|
|
||||||
|
|
||||||
// Constant arrays used by String.Split
|
|
||||||
|
|
||||||
public static readonly char C_SPACE = ' ';
|
|
||||||
public static readonly char C_SLASH = '/';
|
|
||||||
public static readonly char C_PATHSEP = '/';
|
|
||||||
public static readonly char C_COLON = ':';
|
|
||||||
public static readonly char C_PLUS = '+';
|
|
||||||
public static readonly char C_PERIOD = '.';
|
|
||||||
public static readonly char C_COMMA = ',';
|
|
||||||
public static readonly char C_DQUOTE = '"';
|
|
||||||
|
|
||||||
public static readonly string CS_SPACE = " ";
|
|
||||||
public static readonly string CS_SLASH = "/";
|
|
||||||
public static readonly string CS_PATHSEP = "/";
|
|
||||||
public static readonly string CS_COLON = ":";
|
|
||||||
public static readonly string CS_PLUS = "+";
|
|
||||||
public static readonly string CS_PERIOD = ".";
|
|
||||||
public static readonly string CS_COMMA = ",";
|
|
||||||
public static readonly string CS_DQUOTE = "\"";
|
|
||||||
|
|
||||||
public static readonly char[] CA_SPACE = { C_SPACE };
|
|
||||||
public static readonly char[] CA_SLASH = { C_SLASH };
|
|
||||||
public static readonly char[] CA_PATHSEP = { C_PATHSEP };
|
|
||||||
public static readonly char[] CA_COLON = { C_COLON };
|
|
||||||
public static readonly char[] CA_PERIOD = { C_PERIOD };
|
|
||||||
public static readonly char[] CA_PLUS = { C_PLUS };
|
|
||||||
public static readonly char[] CA_COMMA = { C_COMMA };
|
|
||||||
public static readonly char[] CA_DQUOTE = { C_DQUOTE };
|
|
||||||
|
|
||||||
// HTTP Code Values (in value order)
|
|
||||||
|
|
||||||
public const int HttpStatusCodeContinue = 100;
|
|
||||||
public const int HttpStatusCodeSwitchingProtocols = 101;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeOK = 200;
|
|
||||||
public const int HttpStatusCodeCreated = 201;
|
|
||||||
public const int HttpStatusCodeAccepted = 202;
|
|
||||||
public const int HttpStatusCodeNonAuthoritative = 203;
|
|
||||||
public const int HttpStatusCodeNoContent = 204;
|
|
||||||
public const int HttpStatusCodeResetContent = 205;
|
|
||||||
public const int HttpStatusCodePartialContent = 206;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeMultipleChoices = 300;
|
|
||||||
public const int HttpStatusCodePermanentRedirect = 301;
|
|
||||||
public const int HttpStatusCodeFound = 302;
|
|
||||||
public const int HttpStatusCodeSeeOther = 303;
|
|
||||||
public const int HttpStatusCodeNotModified = 304;
|
|
||||||
public const int HttpStatusCodeUseProxy = 305;
|
|
||||||
public const int HttpStatusCodeReserved306 = 306;
|
|
||||||
public const int HttpStatusCodeTemporaryRedirect = 307;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeBadRequest = 400;
|
|
||||||
public const int HttpStatusCodeNotAuthorized = 401;
|
|
||||||
public const int HttpStatusCodePaymentRequired = 402;
|
|
||||||
public const int HttpStatusCodeForbidden = 403;
|
|
||||||
public const int HttpStatusCodeNotFound = 404;
|
|
||||||
public const int HttpStatusCodeMethodNotAllowed = 405;
|
|
||||||
public const int HttpStatusCodeNotAcceptable = 406;
|
|
||||||
public const int HttpStatusCodeProxyAuthenticate = 407;
|
|
||||||
public const int HttpStatusCodeTimeOut = 408;
|
|
||||||
public const int HttpStatusCodeConflict = 409;
|
|
||||||
public const int HttpStatusCodeGone = 410;
|
|
||||||
public const int HttpStatusCodeLengthRequired = 411;
|
|
||||||
public const int HttpStatusCodePreconditionFailed = 412;
|
|
||||||
public const int HttpStatusCodeEntityTooLarge = 413;
|
|
||||||
public const int HttpStatusCodeUriTooLarge = 414;
|
|
||||||
public const int HttpStatusCodeUnsupportedMedia = 415;
|
|
||||||
public const int HttpStatusCodeRangeNotSatsified = 416;
|
|
||||||
public const int HttpStatusCodeExpectationFailed = 417;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeServerError = 500;
|
|
||||||
public const int HttpStatusCodeNotImplemented = 501;
|
|
||||||
public const int HttpStatusCodeBadGateway = 502;
|
|
||||||
public const int HttpStatusCodeServiceUnavailable = 503;
|
|
||||||
public const int HttpStatusCodeGatewayTimeout = 504;
|
|
||||||
public const int HttpStatusCodeHttpVersionError = 505;
|
|
||||||
|
|
||||||
public static readonly int[] HttpStatusCodeArray = {
|
|
||||||
HttpStatusCodeContinue,
|
|
||||||
HttpStatusCodeSwitchingProtocols,
|
|
||||||
HttpStatusCodeOK,
|
|
||||||
HttpStatusCodeCreated,
|
|
||||||
HttpStatusCodeAccepted,
|
|
||||||
HttpStatusCodeNonAuthoritative,
|
|
||||||
HttpStatusCodeNoContent,
|
|
||||||
HttpStatusCodeResetContent,
|
|
||||||
HttpStatusCodePartialContent,
|
|
||||||
HttpStatusCodeMultipleChoices,
|
|
||||||
HttpStatusCodePermanentRedirect,
|
|
||||||
HttpStatusCodeFound,
|
|
||||||
HttpStatusCodeSeeOther,
|
|
||||||
HttpStatusCodeNotModified,
|
|
||||||
HttpStatusCodeUseProxy,
|
|
||||||
HttpStatusCodeReserved306,
|
|
||||||
HttpStatusCodeTemporaryRedirect,
|
|
||||||
HttpStatusCodeBadRequest,
|
|
||||||
HttpStatusCodeNotAuthorized,
|
|
||||||
HttpStatusCodePaymentRequired,
|
|
||||||
HttpStatusCodeForbidden,
|
|
||||||
HttpStatusCodeNotFound,
|
|
||||||
HttpStatusCodeMethodNotAllowed,
|
|
||||||
HttpStatusCodeNotAcceptable,
|
|
||||||
HttpStatusCodeProxyAuthenticate,
|
|
||||||
HttpStatusCodeTimeOut,
|
|
||||||
HttpStatusCodeConflict,
|
|
||||||
HttpStatusCodeGone,
|
|
||||||
HttpStatusCodeLengthRequired,
|
|
||||||
HttpStatusCodePreconditionFailed,
|
|
||||||
HttpStatusCodeEntityTooLarge,
|
|
||||||
HttpStatusCodeUriTooLarge,
|
|
||||||
HttpStatusCodeUnsupportedMedia,
|
|
||||||
HttpStatusCodeRangeNotSatsified,
|
|
||||||
HttpStatusCodeExpectationFailed,
|
|
||||||
HttpStatusCodeServerError,
|
|
||||||
HttpStatusCodeNotImplemented,
|
|
||||||
HttpStatusCodeBadGateway,
|
|
||||||
HttpStatusCodeServiceUnavailable,
|
|
||||||
HttpStatusCodeGatewayTimeout,
|
|
||||||
HttpStatusCodeHttpVersionError
|
|
||||||
};
|
|
||||||
|
|
||||||
// HTTP Status Descriptions (in status code order)
|
|
||||||
// This array must be kept strictly consistent with respect
|
|
||||||
// to the status code array above.
|
|
||||||
|
|
||||||
public static readonly string[] HttpStatusDescArray = {
|
|
||||||
"Continue Request",
|
|
||||||
"Switching Protocols",
|
|
||||||
"OK",
|
|
||||||
"CREATED",
|
|
||||||
"ACCEPTED",
|
|
||||||
"NON-AUTHORITATIVE INFORMATION",
|
|
||||||
"NO CONTENT",
|
|
||||||
"RESET CONTENT",
|
|
||||||
"PARTIAL CONTENT",
|
|
||||||
"MULTIPLE CHOICES",
|
|
||||||
"PERMANENT REDIRECT",
|
|
||||||
"FOUND",
|
|
||||||
"SEE OTHER",
|
|
||||||
"NOT MODIFIED",
|
|
||||||
"USE PROXY",
|
|
||||||
"RESERVED CODE 306",
|
|
||||||
"TEMPORARY REDIRECT",
|
|
||||||
"BAD REQUEST",
|
|
||||||
"NOT AUTHORIZED",
|
|
||||||
"PAYMENT REQUIRED",
|
|
||||||
"FORBIDDEN",
|
|
||||||
"NOT FOUND",
|
|
||||||
"METHOD NOT ALLOWED",
|
|
||||||
"NOT ACCEPTABLE",
|
|
||||||
"PROXY AUTHENTICATION REQUIRED",
|
|
||||||
"TIMEOUT",
|
|
||||||
"CONFLICT",
|
|
||||||
"GONE",
|
|
||||||
"LENGTH REQUIRED",
|
|
||||||
"PRECONDITION FAILED",
|
|
||||||
"ENTITY TOO LARGE",
|
|
||||||
"URI TOO LARGE",
|
|
||||||
"UNSUPPORTED MEDIA",
|
|
||||||
"RANGE NOT SATISFIED",
|
|
||||||
"EXPECTATION FAILED",
|
|
||||||
"SERVER ERROR",
|
|
||||||
"NOT IMPLEMENTED",
|
|
||||||
"BAD GATEWAY",
|
|
||||||
"SERVICE UNAVAILABLE",
|
|
||||||
"GATEWAY TIMEOUT",
|
|
||||||
"HTTP VERSION NOT SUPPORTED"
|
|
||||||
};
|
|
||||||
|
|
||||||
// HTTP Headers
|
|
||||||
|
|
||||||
public const string HttpHeaderAccept = "Accept";
|
|
||||||
public const string HttpHeaderAcceptCharset = "Accept-Charset";
|
|
||||||
public const string HttpHeaderAcceptEncoding = "Accept-Encoding";
|
|
||||||
public const string HttpHeaderAcceptLanguage = "Accept-Language";
|
|
||||||
public const string HttpHeaderAcceptRanges = "Accept-Ranges";
|
|
||||||
public const string HttpHeaderAge = "Age";
|
|
||||||
public const string HttpHeaderAllow = "Allow";
|
|
||||||
public const string HttpHeaderAuthorization = "Authorization";
|
|
||||||
public const string HttpHeaderCacheControl = "Cache-Control";
|
|
||||||
public const string HttpHeaderConnection = "Connection";
|
|
||||||
public const string HttpHeaderContentEncoding = "Content-Encoding";
|
|
||||||
public const string HttpHeaderContentLanguage = "Content-Language";
|
|
||||||
public const string HttpHeaderContentLength = "Content-Length";
|
|
||||||
public const string HttpHeaderContentLocation = "Content-Location";
|
|
||||||
public const string HttpHeaderContentMD5 = "Content-MD5";
|
|
||||||
public const string HttpHeaderContentRange = "Content-Range";
|
|
||||||
public const string HttpHeaderContentType = "Content-Type";
|
|
||||||
public const string HttpHeaderDate = "Date";
|
|
||||||
public const string HttpHeaderETag = "ETag";
|
|
||||||
public const string HttpHeaderExpect = "Expect";
|
|
||||||
public const string HttpHeaderExpires = "Expires";
|
|
||||||
public const string HttpHeaderFrom = "From";
|
|
||||||
public const string HttpHeaderHost = "Host";
|
|
||||||
public const string HttpHeaderIfMatch = "If-Match";
|
|
||||||
public const string HttpHeaderIfModifiedSince = "If-Modified-Since";
|
|
||||||
public const string HttpHeaderIfNoneMatch = "If-None-Match";
|
|
||||||
public const string HttpHeaderIfRange = "If-Range";
|
|
||||||
public const string HttpHeaderIfUnmodifiedSince = "If-Unmodified-Since";
|
|
||||||
public const string HttpHeaderLastModified = "Last-Modified";
|
|
||||||
public const string HttpHeaderLocation = "Location";
|
|
||||||
public const string HttpHeaderMaxForwards = "Max-Forwards";
|
|
||||||
public const string HttpHeaderPragma = "Pragma";
|
|
||||||
public const string HttpHeaderProxyAuthenticate = "Proxy-Authenticate";
|
|
||||||
public const string HttpHeaderProxyAuthorization = "Proxy-Authorization";
|
|
||||||
public const string HttpHeaderRange = "Range";
|
|
||||||
public const string HttpHeaderReferer = "Referer";
|
|
||||||
public const string HttpHeaderRetryAfter = "Retry-After";
|
|
||||||
public const string HttpHeaderServer = "Server";
|
|
||||||
public const string HttpHeaderTE = "TE";
|
|
||||||
public const string HttpHeaderTrailer = "Trailer";
|
|
||||||
public const string HttpHeaderTransferEncoding = "Transfer-Encoding";
|
|
||||||
public const string HttpHeaderUpgrade = "Upgrade";
|
|
||||||
public const string HttpHeaderUserAgent = "User-Agent";
|
|
||||||
public const string HttpHeaderVary = "Vary";
|
|
||||||
public const string HttpHeaderVia = "Via";
|
|
||||||
public const string HttpHeaderWarning = "Warning";
|
|
||||||
public const string HttpHeaderWWWAuthenticate = "WWW-Authenticate";
|
|
||||||
|
|
||||||
/// Utility routines
|
|
||||||
|
|
||||||
public static string StringToBase64(string str)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] encData_byte = new byte[str.Length];
|
|
||||||
encData_byte = Util.UTF8.GetBytes(str);
|
|
||||||
return Convert.ToBase64String(encData_byte);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return String.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Base64ToString(string str)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Util.Base64ToString(str);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return String.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string hvals = "0123456789abcdef";
|
|
||||||
|
|
||||||
public static int Hex2Int(string hex)
|
|
||||||
{
|
|
||||||
int val = 0;
|
|
||||||
int sum = 0;
|
|
||||||
string tmp = null;
|
|
||||||
|
|
||||||
if (hex != null)
|
|
||||||
{
|
|
||||||
tmp = hex.ToLower();
|
|
||||||
for (int i = 0; i < tmp.Length; i++)
|
|
||||||
{
|
|
||||||
val = hvals.IndexOf(tmp[i]);
|
|
||||||
if (val == -1)
|
|
||||||
break;
|
|
||||||
sum *= 16;
|
|
||||||
sum += val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nonce management
|
|
||||||
|
|
||||||
public static string NonceGenerator()
|
|
||||||
{
|
|
||||||
return StringToBase64(CreationDate + Guid.NewGuid().ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump the specified data stream
|
|
||||||
|
|
||||||
public static void Dump(byte[] data)
|
|
||||||
{
|
|
||||||
char[] buffer = new char[DumpLineSize];
|
|
||||||
int cc = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < data.Length; i++)
|
|
||||||
{
|
|
||||||
if (i % DumpLineSize == 0) Console.Write("\n{0}: ",i.ToString("d8"));
|
|
||||||
|
|
||||||
if (i % 4 == 0) Console.Write(" ");
|
|
||||||
|
|
||||||
Console.Write("{0}",data[i].ToString("x2"));
|
|
||||||
|
|
||||||
if (data[i] < 127 && data[i] > 31)
|
|
||||||
buffer[i % DumpLineSize] = (char) data[i];
|
|
||||||
else
|
|
||||||
buffer[i % DumpLineSize] = '.';
|
|
||||||
|
|
||||||
cc++;
|
|
||||||
|
|
||||||
if (i != 0 && (i + 1) % DumpLineSize == 0)
|
|
||||||
{
|
|
||||||
Console.Write(" |"+(new String(buffer))+"|");
|
|
||||||
cc = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish off any incomplete line
|
|
||||||
|
|
||||||
if (cc != 0)
|
|
||||||
{
|
|
||||||
for (int i = cc ; i < DumpLineSize; i++)
|
|
||||||
{
|
|
||||||
if (i % 4 == 0) Console.Write(" ");
|
|
||||||
Console.Write(" ");
|
|
||||||
buffer[i % DumpLineSize] = ' ';
|
|
||||||
}
|
|
||||||
Console.WriteLine(" |"+(new String(buffer))+"|");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.Write("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local exception type
|
|
||||||
|
|
||||||
public class RestException : Exception
|
|
||||||
{
|
|
||||||
internal int statusCode;
|
|
||||||
internal string statusDesc;
|
|
||||||
internal string httpmethod;
|
|
||||||
internal string httppath;
|
|
||||||
|
|
||||||
public RestException(string msg) : base(msg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,860 +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;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Xml;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Services.Interfaces;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
|
|
||||||
public class RestAppearanceServices : IRest
|
|
||||||
{
|
|
||||||
// private static readonly int PARM_USERID = 0;
|
|
||||||
|
|
||||||
// private static readonly int PARM_PATH = 1;
|
|
||||||
|
|
||||||
// private bool enabled = false;
|
|
||||||
private string qPrefix = "appearance";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The constructor makes sure that the service prefix is absolute
|
|
||||||
/// and the registers the service handler and the allocator.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public RestAppearanceServices()
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} User appearance 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);
|
|
||||||
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(DoAppearance,qPrefix,Allocate);
|
|
||||||
|
|
||||||
// Activate if everything went OK
|
|
||||||
|
|
||||||
// enabled = true;
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} User appearance services initialization complete", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Post-construction, pre-enabled initialization opportunity
|
|
||||||
/// Not currently exploited.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called by the plug-in to halt service processing. Local processing is
|
|
||||||
/// disabled.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
// enabled = false;
|
|
||||||
Rest.Log.InfoFormat("{0} User appearance services closing down", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This property is declared locally because it is used a lot and
|
|
||||||
/// brevity is nice.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal string MsgId
|
|
||||||
{
|
|
||||||
get { return Rest.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Interface
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name=request>Inbound HTTP request information</param>
|
|
||||||
/// <param name=response>Outbound HTTP request information</param>
|
|
||||||
/// <param name=qPrefix>REST service domain prefix</param>
|
|
||||||
/// <returns>A RequestData instance suitable for this service</returns>
|
|
||||||
|
|
||||||
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
{
|
|
||||||
return (RequestData) new AppearanceRequestData(request, response, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is registered with the handler when this service provider
|
|
||||||
/// is initialized. It is called whenever the plug-in identifies this service
|
|
||||||
/// provider as the best match for a given request.
|
|
||||||
/// It handles all aspects of inventory REST processing, i.e. /admin/inventory
|
|
||||||
/// </summary>
|
|
||||||
/// <param name=hdata>A consolidated HTTP request work area</param>
|
|
||||||
|
|
||||||
private void DoAppearance(RequestData hdata)
|
|
||||||
{
|
|
||||||
// !!! REFACTORIMG PROBLEM. This needs rewriting for 0.7
|
|
||||||
|
|
||||||
//AppearanceRequestData rdata = (AppearanceRequestData) hdata;
|
|
||||||
|
|
||||||
//Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId);
|
|
||||||
|
|
||||||
//// If we're disabled, do nothing.
|
|
||||||
|
|
||||||
//if (!enabled)
|
|
||||||
//{
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// Now that we know this is a serious attempt to
|
|
||||||
//// access inventory data, we should find out who
|
|
||||||
//// is asking, and make sure they are authorized
|
|
||||||
//// to do so. We need to validate the caller's
|
|
||||||
//// identity before revealing anything about the
|
|
||||||
//// status quo. Authenticate throws an exception
|
|
||||||
//// via Fail if no identity information is present.
|
|
||||||
////
|
|
||||||
//// With the present HTTP server we can't use the
|
|
||||||
//// builtin authentication mechanisms because they
|
|
||||||
//// would be enforced for all in-bound requests.
|
|
||||||
//// Instead we look at the headers ourselves and
|
|
||||||
//// handle authentication directly.
|
|
||||||
|
|
||||||
//try
|
|
||||||
//{
|
|
||||||
// if (!rdata.IsAuthenticated)
|
|
||||||
// {
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//catch (RestException e)
|
|
||||||
//{
|
|
||||||
// if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
|
|
||||||
// {
|
|
||||||
// Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
|
|
||||||
// Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
|
|
||||||
// Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
|
|
||||||
// }
|
|
||||||
// throw (e);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
|
|
||||||
|
|
||||||
//// We can only get here if we are authorized
|
|
||||||
////
|
|
||||||
//// The requestor may have specified an UUID or
|
|
||||||
//// a conjoined FirstName LastName string. We'll
|
|
||||||
//// try both. If we fail with the first, UUID,
|
|
||||||
//// attempt, we try the other. As an example, the
|
|
||||||
//// URI for a valid inventory request might be:
|
|
||||||
////
|
|
||||||
//// http://<host>:<port>/admin/inventory/Arthur Dent
|
|
||||||
////
|
|
||||||
//// Indicating that this is an inventory request for
|
|
||||||
//// an avatar named Arthur Dent. This is ALL that is
|
|
||||||
//// required to designate a GET for an entire
|
|
||||||
//// inventory.
|
|
||||||
////
|
|
||||||
|
|
||||||
//// Do we have at least a user agent name?
|
|
||||||
|
|
||||||
//if (rdata.Parameters.Length < 1)
|
|
||||||
//{
|
|
||||||
// Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// The first parameter MUST be the agent identification, either an UUID
|
|
||||||
//// or a space-separated First-name Last-Name specification. We check for
|
|
||||||
//// an UUID first, if anyone names their character using a valid UUID
|
|
||||||
//// that identifies another existing avatar will cause this a problem...
|
|
||||||
|
|
||||||
//try
|
|
||||||
//{
|
|
||||||
// rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
|
|
||||||
// Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
|
|
||||||
// rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
|
|
||||||
//}
|
|
||||||
//catch
|
|
||||||
//{
|
|
||||||
// string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
|
|
||||||
// if (names.Length == 2)
|
|
||||||
// {
|
|
||||||
// Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
|
|
||||||
// rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// If the user profile is null then either the server is broken, or the
|
|
||||||
//// user is not known. We always assume the latter case.
|
|
||||||
|
|
||||||
//if (rdata.userProfile != null)
|
|
||||||
//{
|
|
||||||
// Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}",
|
|
||||||
// MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// If we get to here, then we have effectively validated the user's
|
|
||||||
|
|
||||||
//switch (rdata.method)
|
|
||||||
//{
|
|
||||||
// case Rest.HEAD : // Do the processing, set the status code, suppress entity
|
|
||||||
// DoGet(rdata);
|
|
||||||
// rdata.buffer = null;
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.GET : // Do the processing, set the status code, return entity
|
|
||||||
// DoGet(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.PUT : // Update named element
|
|
||||||
// DoUpdate(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.POST : // Add new information to identified context.
|
|
||||||
// DoExtend(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.DELETE : // Delete information
|
|
||||||
// DoDelete(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// default :
|
|
||||||
// Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
|
|
||||||
// MsgId, rdata.method, rdata.path);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
|
|
||||||
// String.Format("{0} not supported", rdata.method));
|
|
||||||
// break;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Interface
|
|
||||||
|
|
||||||
#region method-specific processing
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method implements GET processing for user's appearance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name=rdata>HTTP service request work area</param>
|
|
||||||
|
|
||||||
// private void DoGet(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// if (adata == null)
|
|
||||||
// {
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeNoContent,
|
|
||||||
// String.Format("appearance data not found for user {0} {1}",
|
|
||||||
// rdata.userProfile.FirstName, rdata.userProfile.SurName));
|
|
||||||
// }
|
|
||||||
// rdata.userAppearance = adata.ToAvatarAppearance(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// rdata.initXmlWriter();
|
|
||||||
//
|
|
||||||
// FormatUserAppearance(rdata);
|
|
||||||
//
|
|
||||||
// // Indicate a successful request
|
|
||||||
//
|
|
||||||
// rdata.Complete();
|
|
||||||
//
|
|
||||||
// // Send the response to the user. The body will be implicitly
|
|
||||||
// // constructed from the result of the XML writer.
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// POST adds NEW information to the user profile database.
|
|
||||||
/// This effectively resets the appearance before applying those
|
|
||||||
/// characteristics supplied in the request.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
// private void DoExtend(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// bool created = false;
|
|
||||||
// bool modified = false;
|
|
||||||
// string newnode = String.Empty;
|
|
||||||
//
|
|
||||||
// Rest.Log.DebugFormat("{0} POST ENTRY", MsgId);
|
|
||||||
//
|
|
||||||
// //AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// rdata.userAppearance = new AvatarAppearance();
|
|
||||||
//
|
|
||||||
// // Although the following behavior is admitted by HTTP I am becoming
|
|
||||||
// // increasingly doubtful that it is appropriate for REST. If I attempt to
|
|
||||||
// // add a new record, and it already exists, then it seems to me that the
|
|
||||||
// // attempt should fail, rather than update the existing record.
|
|
||||||
// AvatarData adata = null;
|
|
||||||
// if (GetUserAppearance(rdata))
|
|
||||||
// {
|
|
||||||
// modified = rdata.userAppearance != null;
|
|
||||||
// created = !modified;
|
|
||||||
// adata = new AvatarData(rdata.userAppearance);
|
|
||||||
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
|
|
||||||
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// created = true;
|
|
||||||
// adata = new AvatarData(rdata.userAppearance);
|
|
||||||
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
|
|
||||||
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (created)
|
|
||||||
// {
|
|
||||||
// newnode = String.Format("{0} {1}", rdata.userProfile.FirstName,
|
|
||||||
// rdata.userProfile.SurName);
|
|
||||||
// // Must include a location header with a URI that identifies the new resource.
|
|
||||||
//
|
|
||||||
// rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}",
|
|
||||||
// rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode));
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (modified)
|
|
||||||
// {
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This updates the user's appearance. not all aspects need to be provided,
|
|
||||||
/// only those supplied will be changed.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
// private void DoUpdate(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// // REFACTORING PROBLEM This was commented out. It doesn't work for 0.7
|
|
||||||
//
|
|
||||||
// //bool created = false;
|
|
||||||
// //bool modified = false;
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// //rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// //// If the user exists then this is considered a modification regardless
|
|
||||||
// //// of what may, or may not be, specified in the payload.
|
|
||||||
//
|
|
||||||
// //if (rdata.userAppearance != null)
|
|
||||||
// //{
|
|
||||||
// // modified = true;
|
|
||||||
// // Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
|
|
||||||
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
|
|
||||||
// //}
|
|
||||||
//
|
|
||||||
// //if (created)
|
|
||||||
// //{
|
|
||||||
// // rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
// //}
|
|
||||||
// //else
|
|
||||||
// //{
|
|
||||||
// // if (modified)
|
|
||||||
// // {
|
|
||||||
// // rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
// // }
|
|
||||||
// // else
|
|
||||||
// // {
|
|
||||||
// // rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
// // }
|
|
||||||
// //}
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Delete the specified user's appearance. This actually performs a reset
|
|
||||||
/// to the default avatar appearance, if the info is already there.
|
|
||||||
/// Existing ownership is preserved. All prior updates are lost and can not
|
|
||||||
/// be recovered.
|
|
||||||
/// </summary>
|
|
||||||
// private void DoDelete(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// if (adata != null)
|
|
||||||
// {
|
|
||||||
// AvatarAppearance old = adata.ToAvatarAppearance(rdata.userProfile.ID);
|
|
||||||
// rdata.userAppearance = new AvatarAppearance();
|
|
||||||
// rdata.userAppearance.Owner = old.Owner;
|
|
||||||
// adata = new AvatarData(rdata.userAppearance);
|
|
||||||
//
|
|
||||||
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
|
|
||||||
//
|
|
||||||
// rdata.Complete();
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
|
|
||||||
// }
|
|
||||||
|
|
||||||
#endregion method-specific processing
|
|
||||||
|
|
||||||
private bool GetUserAppearance(AppearanceRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
XmlReader xml;
|
|
||||||
bool indata = false;
|
|
||||||
|
|
||||||
rdata.initXmlReader();
|
|
||||||
xml = rdata.reader;
|
|
||||||
|
|
||||||
while (xml.Read())
|
|
||||||
{
|
|
||||||
switch (xml.NodeType)
|
|
||||||
{
|
|
||||||
case XmlNodeType.Element :
|
|
||||||
switch (xml.Name)
|
|
||||||
{
|
|
||||||
case "Appearance" :
|
|
||||||
if (xml.MoveToAttribute("Height"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.AvatarHeight = (float) Convert.ToDouble(xml.Value);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
// if (xml.MoveToAttribute("Owner"))
|
|
||||||
// {
|
|
||||||
// rdata.userAppearance.Owner = (UUID)xml.Value;
|
|
||||||
// indata = true;
|
|
||||||
// }
|
|
||||||
if (xml.MoveToAttribute("Serial"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.Serial = Convert.ToInt32(xml.Value);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
/*
|
|
||||||
case "Body" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.BodyItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.BodyAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Skin" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkinItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkinAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Hair" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.HairItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.HairAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Eyes" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.EyesItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.EyesAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Shirt" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShirtItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShirtAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Pants" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.PantsItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.PantsAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Shoes" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShoesItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShoesAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Socks" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SocksItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SocksAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Jacket" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.JacketItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.JacketAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Gloves" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.GlovesItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.GlovesAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "UnderShirt" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderShirtItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderShirtAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "UnderPants" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderPantsItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderPantsAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Skirt" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkirtItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkirtAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
case "Attachment" :
|
|
||||||
{
|
|
||||||
|
|
||||||
int ap;
|
|
||||||
UUID asset;
|
|
||||||
UUID item;
|
|
||||||
|
|
||||||
if (xml.MoveToAttribute("AtPoint"))
|
|
||||||
{
|
|
||||||
ap = Convert.ToInt32(xml.Value);
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
asset = new UUID(xml.Value);
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
item = new UUID(xml.Value);
|
|
||||||
rdata.userAppearance.SetAttachment(ap, item, asset);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Texture" :
|
|
||||||
if (xml.MoveToAttribute("Default"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.Texture = new Primitive.TextureEntry(new UUID(xml.Value));
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Face" :
|
|
||||||
{
|
|
||||||
uint index;
|
|
||||||
if (xml.MoveToAttribute("Index"))
|
|
||||||
{
|
|
||||||
index = Convert.ToUInt32(xml.Value);
|
|
||||||
if (xml.MoveToAttribute("Id"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.Texture.CreateFace(index).TextureID = new UUID(xml.Value);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "VisualParameters" :
|
|
||||||
{
|
|
||||||
xml.ReadContentAsBase64(rdata.userAppearance.VisualParams,
|
|
||||||
0, rdata.userAppearance.VisualParams.Length);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return indata;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FormatPart(AppearanceRequestData rdata, string part, UUID item, UUID asset)
|
|
||||||
{
|
|
||||||
if (item != UUID.Zero || asset != UUID.Zero)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteStartElement(part);
|
|
||||||
if (item != UUID.Zero)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteAttributeString("Item",item.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset != UUID.Zero)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteAttributeString("Asset",asset.ToString());
|
|
||||||
}
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FormatUserAppearance(AppearanceRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance", MsgId);
|
|
||||||
|
|
||||||
if (rdata.userAppearance != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: appearance object exists", MsgId);
|
|
||||||
rdata.writer.WriteStartElement("Appearance");
|
|
||||||
|
|
||||||
rdata.writer.WriteAttributeString("Height", rdata.userAppearance.AvatarHeight.ToString());
|
|
||||||
// if (rdata.userAppearance.Owner != UUID.Zero)
|
|
||||||
// rdata.writer.WriteAttributeString("Owner", rdata.userAppearance.Owner.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Serial", rdata.userAppearance.Serial.ToString());
|
|
||||||
|
|
||||||
/*
|
|
||||||
FormatPart(rdata, "Body", rdata.userAppearance.BodyItem, rdata.userAppearance.BodyAsset);
|
|
||||||
FormatPart(rdata, "Skin", rdata.userAppearance.SkinItem, rdata.userAppearance.SkinAsset);
|
|
||||||
FormatPart(rdata, "Hair", rdata.userAppearance.HairItem, rdata.userAppearance.HairAsset);
|
|
||||||
FormatPart(rdata, "Eyes", rdata.userAppearance.EyesItem, rdata.userAppearance.EyesAsset);
|
|
||||||
|
|
||||||
FormatPart(rdata, "Shirt", rdata.userAppearance.ShirtItem, rdata.userAppearance.ShirtAsset);
|
|
||||||
FormatPart(rdata, "Pants", rdata.userAppearance.PantsItem, rdata.userAppearance.PantsAsset);
|
|
||||||
FormatPart(rdata, "Skirt", rdata.userAppearance.SkirtItem, rdata.userAppearance.SkirtAsset);
|
|
||||||
FormatPart(rdata, "Shoes", rdata.userAppearance.ShoesItem, rdata.userAppearance.ShoesAsset);
|
|
||||||
FormatPart(rdata, "Socks", rdata.userAppearance.SocksItem, rdata.userAppearance.SocksAsset);
|
|
||||||
|
|
||||||
FormatPart(rdata, "Jacket", rdata.userAppearance.JacketItem, rdata.userAppearance.JacketAsset);
|
|
||||||
FormatPart(rdata, "Gloves", rdata.userAppearance.GlovesItem, rdata.userAppearance.GlovesAsset);
|
|
||||||
|
|
||||||
FormatPart(rdata, "UnderShirt", rdata.userAppearance.UnderShirtItem, rdata.userAppearance.UnderShirtAsset);
|
|
||||||
FormatPart(rdata, "UnderPants", rdata.userAppearance.UnderPantsItem, rdata.userAppearance.UnderPantsAsset);
|
|
||||||
*/
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting attachments", MsgId);
|
|
||||||
|
|
||||||
rdata.writer.WriteStartElement("Attachments");
|
|
||||||
List<AvatarAttachment> attachments = rdata.userAppearance.GetAttachments();
|
|
||||||
foreach (AvatarAttachment attach in attachments)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteStartElement("Attachment");
|
|
||||||
rdata.writer.WriteAttributeString("AtPoint", attach.AttachPoint.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Item", attach.ItemID.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Asset", attach.AssetID.ToString());
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
|
|
||||||
Primitive.TextureEntry texture = rdata.userAppearance.Texture;
|
|
||||||
|
|
||||||
if (texture != null && (texture.DefaultTexture != null || texture.FaceTextures != null))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting textures", MsgId);
|
|
||||||
|
|
||||||
rdata.writer.WriteStartElement("Texture");
|
|
||||||
|
|
||||||
if (texture.DefaultTexture != null)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting default texture", MsgId);
|
|
||||||
rdata.writer.WriteAttributeString("Default",
|
|
||||||
texture.DefaultTexture.TextureID.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture.FaceTextures != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting face textures", MsgId);
|
|
||||||
|
|
||||||
for (int i=0; i<texture.FaceTextures.Length;i++)
|
|
||||||
{
|
|
||||||
if (texture.FaceTextures[i] != null)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteStartElement("Face");
|
|
||||||
rdata.writer.WriteAttributeString("Index", i.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Id",
|
|
||||||
texture.FaceTextures[i].TextureID.ToString());
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting visual parameters", MsgId);
|
|
||||||
|
|
||||||
rdata.writer.WriteStartElement("VisualParameters");
|
|
||||||
rdata.writer.WriteBase64(rdata.userAppearance.VisualParams,0,
|
|
||||||
rdata.userAppearance.VisualParams.Length);
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
rdata.writer.WriteFullEndElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: completed", MsgId);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region appearance RequestData extension
|
|
||||||
|
|
||||||
internal class AppearanceRequestData : RequestData
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These are the inventory specific request/response state
|
|
||||||
/// extensions.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
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("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
|
|
||||||
/// No parameters are required for POST, just thepayload.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asset processing has no special data area requirements.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal class AssetRequestData : RequestData
|
|
||||||
{
|
|
||||||
internal AssetRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
: base(request, response, prefix)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
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("<p> Created file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
|
|
||||||
/// No parameters are required for POST, just thepayload.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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("<p> Created file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
|
|
||||||
/// No parameters are required for POST, just thepayload.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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("<p> Created file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// File processing has no special data area requirements.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal class FileRequestData : RequestData
|
|
||||||
{
|
|
||||||
internal FileRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
: base(request, response, prefix)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <remarks>
|
|
||||||
/// 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.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
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<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>();
|
|
||||||
internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>();
|
|
||||||
internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>();
|
|
||||||
|
|
||||||
#region local static state
|
|
||||||
|
|
||||||
private static bool handlersLoaded = false;
|
|
||||||
private static List<Type> classes = new List<Type>();
|
|
||||||
private static List<IRest> handlers = new List<IRest>();
|
|
||||||
private static Type[] parms = new Type[0];
|
|
||||||
private static Object[] args = new Object[0];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is called by OpenSimMain immediately after loading the
|
|
||||||
/// plugin and after basic server setup, but before running any server commands.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that entries MUST be added to the active configuration files before
|
|
||||||
/// the plugin can be enabled.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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<Type> classes = new List<Type>();
|
|
||||||
private static List<ITest> tests = new List<ITest>();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal interface ITest
|
|
||||||
{
|
|
||||||
void Initialize();
|
|
||||||
void Execute(RequestData rdata);
|
|
||||||
void Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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 =
|
|
||||||
"<html>"
|
|
||||||
+ "<head><title>Remote Command Usage</title></head>"
|
|
||||||
+ "<body>"
|
|
||||||
+ "<p>Supported commands are:</p>"
|
|
||||||
+ "<dl>"
|
|
||||||
+ "<dt>move/avatar-name/x/y/z</dt>"
|
|
||||||
+ "<dd>moves the specified avatar to another location</dd>"
|
|
||||||
+ "</dl>"
|
|
||||||
+ "</body>"
|
|
||||||
+ "</html>"
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,228 +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.IO;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
#region GET methods
|
|
||||||
public string GetHandler(string request, string path, string param,
|
|
||||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
// foreach (string h in httpRequest.Headers.AllKeys)
|
|
||||||
// foreach (string v in httpRequest.Headers.GetValues(h))
|
|
||||||
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
|
|
||||||
|
|
||||||
MsgID = RequestID;
|
|
||||||
m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// param empty: regions list
|
|
||||||
if (String.IsNullOrEmpty(param)) return GetHandlerRegions(httpResponse);
|
|
||||||
|
|
||||||
// param not empty: specific region
|
|
||||||
return GetHandlerRegion(httpResponse, param);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetHandlerRegions(IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
|
|
||||||
foreach (Scene s in App.SceneManager.Scenes)
|
|
||||||
{
|
|
||||||
rxw.WriteStartElement(String.Empty, "uuid", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionID.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
}
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string ShortRegionInfo(string key, string value)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(value) ||
|
|
||||||
String.IsNullOrEmpty(key)) return null;
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "region", String.Empty);
|
|
||||||
rxw.WriteStartElement(String.Empty, key, String.Empty);
|
|
||||||
rxw.WriteString(value);
|
|
||||||
rxw.WriteEndDocument();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetHandlerRegion(IOSHttpResponse httpResponse, string param)
|
|
||||||
{
|
|
||||||
// be resilient and don't get confused by a terminating '/'
|
|
||||||
param = param.TrimEnd(new char[]{'/'});
|
|
||||||
string[] comps = param.Split('/');
|
|
||||||
UUID regionID = (UUID)comps[0];
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} GET region UUID {1}", MsgID, regionID.ToString());
|
|
||||||
|
|
||||||
if (UUID.Zero == regionID) throw new Exception("missing region ID");
|
|
||||||
|
|
||||||
Scene scene = null;
|
|
||||||
App.SceneManager.TryGetScene(regionID, out scene);
|
|
||||||
if (null == scene) return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
|
|
||||||
"GET", "cannot find region {0}", regionID.ToString());
|
|
||||||
|
|
||||||
RegionDetails details = new RegionDetails(scene.RegionInfo);
|
|
||||||
|
|
||||||
// m_log.DebugFormat("{0} GET comps {1}", MsgID, comps.Length);
|
|
||||||
// for (int i = 0; i < comps.Length; i++) m_log.DebugFormat("{0} GET comps[{1}] >{2}<", MsgID, i, comps[i]);
|
|
||||||
|
|
||||||
if (1 == comps.Length)
|
|
||||||
{
|
|
||||||
// complete region details requested
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
XmlSerializer xs = new XmlSerializer(typeof(RegionDetails));
|
|
||||||
xs.Serialize(rxw, details, _xmlNs);
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (2 == comps.Length)
|
|
||||||
{
|
|
||||||
string resp = ShortRegionInfo(comps[1], details[comps[1]]);
|
|
||||||
if (null != resp) return resp;
|
|
||||||
|
|
||||||
// m_log.DebugFormat("{0} GET comps advanced: >{1}<", MsgID, comps[1]);
|
|
||||||
|
|
||||||
// check for {terrain,stats,prims}
|
|
||||||
switch (comps[1].ToLower())
|
|
||||||
{
|
|
||||||
case "terrain":
|
|
||||||
return RegionTerrain(httpResponse, scene);
|
|
||||||
|
|
||||||
case "stats":
|
|
||||||
return RegionStats(httpResponse, scene);
|
|
||||||
|
|
||||||
case "prims":
|
|
||||||
return RegionPrims(httpResponse, scene, Vector3.Zero, Vector3.Zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (3 == comps.Length)
|
|
||||||
{
|
|
||||||
switch (comps[1].ToLower())
|
|
||||||
{
|
|
||||||
case "prims":
|
|
||||||
string[] subregion = comps[2].Split(',');
|
|
||||||
if (subregion.Length == 6)
|
|
||||||
{
|
|
||||||
Vector3 min, max;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
min = new Vector3((float)Double.Parse(subregion[0], Culture.NumberFormatInfo), (float)Double.Parse(subregion[1], Culture.NumberFormatInfo), (float)Double.Parse(subregion[2], Culture.NumberFormatInfo));
|
|
||||||
max = new Vector3((float)Double.Parse(subregion[3], Culture.NumberFormatInfo), (float)Double.Parse(subregion[4], Culture.NumberFormatInfo), (float)Double.Parse(subregion[5], Culture.NumberFormatInfo));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
|
|
||||||
"GET", "invalid subregion parameter");
|
|
||||||
}
|
|
||||||
return RegionPrims(httpResponse, scene, min, max);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
|
|
||||||
"GET", "invalid subregion parameter");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
|
|
||||||
"GET", "too many parameters {0}", param);
|
|
||||||
}
|
|
||||||
#endregion GET methods
|
|
||||||
|
|
||||||
protected string RegionTerrain(IOSHttpResponse httpResponse, Scene scene)
|
|
||||||
{
|
|
||||||
httpResponse.SendChunked = true;
|
|
||||||
httpResponse.ContentType = "text/xml";
|
|
||||||
|
|
||||||
return scene.Heightmap.SaveToXmlString();
|
|
||||||
//return Failure(httpResponse, OSHttpStatusCode.ServerErrorNotImplemented,
|
|
||||||
// "GET", "terrain not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string RegionStats(IOSHttpResponse httpResponse, Scene scene)
|
|
||||||
{
|
|
||||||
int users = scene.GetRootAgentCount();
|
|
||||||
int objects = scene.Entities.Count - users;
|
|
||||||
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "region", String.Empty);
|
|
||||||
rxw.WriteStartElement(String.Empty, "stats", String.Empty);
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "users", String.Empty);
|
|
||||||
rxw.WriteString(users.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "objects", String.Empty);
|
|
||||||
rxw.WriteString(objects.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
rxw.WriteEndDocument();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string RegionPrims(IOSHttpResponse httpResponse, Scene scene, Vector3 min, Vector3 max)
|
|
||||||
{
|
|
||||||
httpResponse.SendChunked = true;
|
|
||||||
httpResponse.ContentType = "text/xml";
|
|
||||||
|
|
||||||
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
|
|
||||||
if (serialiser != null)
|
|
||||||
serialiser.SavePrimsToXml2(scene, new StreamWriter(httpResponse.OutputStream), min, max);
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,136 +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.IO;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
#region GET methods
|
|
||||||
public string GetRegionInfoHandler(string request, string path, string param,
|
|
||||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
// foreach (string h in httpRequest.Headers.AllKeys)
|
|
||||||
// foreach (string v in httpRequest.Headers.GetValues(h))
|
|
||||||
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
|
|
||||||
|
|
||||||
MsgID = RequestID;
|
|
||||||
m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// param empty: regions list
|
|
||||||
// if (String.IsNullOrEmpty(param))
|
|
||||||
return GetRegionInfoHandlerRegions(httpResponse);
|
|
||||||
|
|
||||||
// // param not empty: specific region
|
|
||||||
// return GetRegionInfoHandlerRegion(httpResponse, param);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetRegionInfoHandlerRegions(IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
// regions info
|
|
||||||
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
|
|
||||||
{
|
|
||||||
// regions info: number of regions
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "number", String.Empty);
|
|
||||||
rxw.WriteValue(App.SceneManager.Scenes.Count);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
// regions info: max number of regions
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "max", String.Empty);
|
|
||||||
if (App.ConfigSource.Source.Configs["RemoteAdmin"] != null)
|
|
||||||
{
|
|
||||||
rxw.WriteValue(App.ConfigSource.Source.Configs["RemoteAdmin"].GetInt("region_limit", -1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rxw.WriteValue(-1);
|
|
||||||
}
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
// regions info: region
|
|
||||||
foreach (Scene s in App.SceneManager.Scenes)
|
|
||||||
{
|
|
||||||
rxw.WriteStartElement(String.Empty, "region", String.Empty);
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "uuid", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionID.ToString());
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "name", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionName);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "x", String.Empty);
|
|
||||||
rxw.WriteValue(s.RegionInfo.RegionLocX);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "y", String.Empty);
|
|
||||||
rxw.WriteValue(s.RegionInfo.RegionLocY);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "external_hostname", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.ExternalHostName);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "ip", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.InternalEndPoint.ToString());
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
int users = s.GetRootAgentCount();
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "avatars", String.Empty);
|
|
||||||
rxw.WriteValue(users);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "objects", String.Empty);
|
|
||||||
rxw.WriteValue(s.Entities.Count - users);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
#endregion GET methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +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.IO;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
#region POST methods
|
|
||||||
|
|
||||||
public string PostHandler(string request, string path, string param,
|
|
||||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
// foreach (string h in httpRequest.Headers.AllKeys)
|
|
||||||
// foreach (string v in httpRequest.Headers.GetValues(h))
|
|
||||||
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
|
|
||||||
|
|
||||||
MsgID = RequestID;
|
|
||||||
m_log.DebugFormat("{0} POST path {1} param {2}", MsgID, path, param);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// param empty: new region post
|
|
||||||
if (!IsGod(httpRequest))
|
|
||||||
// XXX: this needs to be turned into a FailureUnauthorized(...)
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorUnauthorized,
|
|
||||||
"GET", "you are not god");
|
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(param)) return CreateRegion(httpRequest, httpResponse);
|
|
||||||
|
|
||||||
// Parse region ID and other parameters
|
|
||||||
param = param.TrimEnd(new char[] {'/'});
|
|
||||||
string[] comps = param.Split('/');
|
|
||||||
UUID regionID = (UUID) comps[0];
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} POST region UUID {1}", MsgID, regionID.ToString());
|
|
||||||
if (UUID.Zero == regionID) throw new Exception("missing region ID");
|
|
||||||
|
|
||||||
Scene scene = null;
|
|
||||||
App.SceneManager.TryGetScene(regionID, out scene);
|
|
||||||
if (null == scene)
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
|
|
||||||
"POST", "cannot find region {0}", regionID.ToString());
|
|
||||||
|
|
||||||
if (2 == comps.Length)
|
|
||||||
{
|
|
||||||
// check for {prims}
|
|
||||||
switch (comps[1].ToLower())
|
|
||||||
{
|
|
||||||
case "prims":
|
|
||||||
return LoadPrims(request, httpRequest, httpResponse, scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
|
|
||||||
"POST", "url {0} not supported", param);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "POST", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CreateRegion(IOSHttpRequest request, IOSHttpResponse response)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
|
|
||||||
foreach (Scene s in App.SceneManager.Scenes)
|
|
||||||
{
|
|
||||||
rxw.WriteStartElement(String.Empty, "uuid", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionID.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
}
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LoadPrims(string requestBody, IOSHttpRequest request, IOSHttpResponse response, Scene scene)
|
|
||||||
{
|
|
||||||
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
|
|
||||||
if (serialiser != null)
|
|
||||||
serialiser.LoadPrimsFromXml2(scene, new StringReader(requestBody), true);
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion POST methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +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.Serialization;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
[XmlRoot(ElementName="region", IsNullable = false)]
|
|
||||||
public class RegionDetails
|
|
||||||
{
|
|
||||||
public string region_name;
|
|
||||||
public string region_id;
|
|
||||||
public uint region_x;
|
|
||||||
public uint region_y;
|
|
||||||
public string region_owner;
|
|
||||||
public string region_owner_id;
|
|
||||||
public uint region_http_port;
|
|
||||||
public uint region_port;
|
|
||||||
public string region_server_uri;
|
|
||||||
public string region_external_hostname;
|
|
||||||
|
|
||||||
public RegionDetails()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegionDetails(RegionInfo regInfo)
|
|
||||||
{
|
|
||||||
region_name = regInfo.RegionName;
|
|
||||||
region_id = regInfo.RegionID.ToString();
|
|
||||||
region_x = regInfo.RegionLocX;
|
|
||||||
region_y = regInfo.RegionLocY;
|
|
||||||
region_owner_id = regInfo.EstateSettings.EstateOwner.ToString();
|
|
||||||
region_http_port = regInfo.HttpPort;
|
|
||||||
region_server_uri = regInfo.ServerURI;
|
|
||||||
region_external_hostname = regInfo.ExternalHostName;
|
|
||||||
|
|
||||||
Uri uri = new Uri(region_server_uri);
|
|
||||||
region_port = (uint)uri.Port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string idx]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (idx.ToLower())
|
|
||||||
{
|
|
||||||
case "name":
|
|
||||||
return region_name;
|
|
||||||
case "id":
|
|
||||||
return region_id;
|
|
||||||
case "location":
|
|
||||||
return String.Format("<x>{0}</x><y>{1}</y>", region_x, region_y);
|
|
||||||
case "owner":
|
|
||||||
return region_owner;
|
|
||||||
case "owner_id":
|
|
||||||
return region_owner_id;
|
|
||||||
case "http_port":
|
|
||||||
return region_http_port.ToString();
|
|
||||||
case "server_uri":
|
|
||||||
return region_server_uri;
|
|
||||||
case "external_hostname":
|
|
||||||
case "hostname":
|
|
||||||
return region_external_hostname;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<Addin id="OpenSim.ApplicationPlugins.Rest.Regions" version="0.1">
|
|
||||||
<Runtime>
|
|
||||||
<Import assembly="OpenSim.ApplicationPlugins.Rest.Regions.dll"/>
|
|
||||||
</Runtime>
|
|
||||||
<Dependencies>
|
|
||||||
<Addin id="OpenSim" version="0.5" />
|
|
||||||
</Dependencies>
|
|
||||||
<Extension path = "/OpenSim/Startup">
|
|
||||||
<Plugin id="RestRegions" type="OpenSim.ApplicationPlugins.Rest.Regions.RestRegionPlugin" />
|
|
||||||
</Extension>
|
|
||||||
</Addin>
|
|
|
@ -1,94 +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.Serialization;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
private static XmlSerializerNamespaces _xmlNs;
|
|
||||||
|
|
||||||
static RestRegionPlugin()
|
|
||||||
{
|
|
||||||
_xmlNs = new XmlSerializerNamespaces();
|
|
||||||
_xmlNs.Add(String.Empty, String.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region overriding properties
|
|
||||||
public override string Name
|
|
||||||
{
|
|
||||||
get { return "REGION"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ConfigName
|
|
||||||
{
|
|
||||||
get { return "RestRegionPlugin"; }
|
|
||||||
}
|
|
||||||
#endregion overriding properties
|
|
||||||
|
|
||||||
#region overriding methods
|
|
||||||
/// <summary>
|
|
||||||
/// This method is called by OpenSimMain immediately after loading the
|
|
||||||
/// plugin and after basic server setup, but before running any server commands.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that entries MUST be added to the active configuration files before
|
|
||||||
/// the plugin can be enabled.
|
|
||||||
/// </remarks>
|
|
||||||
public override void Initialise(OpenSimBase openSim)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
base.Initialise(openSim);
|
|
||||||
if (!IsEnabled)
|
|
||||||
{
|
|
||||||
//m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_log.InfoFormat("{0} REST region plugin enabled", MsgID);
|
|
||||||
|
|
||||||
// add REST method handlers
|
|
||||||
AddRestStreamHandler("GET", "/regions/", GetHandler);
|
|
||||||
AddRestStreamHandler("POST", "/regions/", PostHandler);
|
|
||||||
AddRestStreamHandler("GET", "/regioninfo/", GetRegionInfoHandler);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message);
|
|
||||||
m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endregion overriding methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,417 +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.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Xml;
|
|
||||||
using log4net;
|
|
||||||
using Nini.Config;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest
|
|
||||||
{
|
|
||||||
public abstract class RestPlugin : IApplicationPlugin
|
|
||||||
{
|
|
||||||
#region properties
|
|
||||||
|
|
||||||
protected static readonly ILog m_log =
|
|
||||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
||||||
|
|
||||||
private IConfig _config; // Configuration source: Rest Plugins
|
|
||||||
private IConfig _pluginConfig; // Configuration source: Plugin specific
|
|
||||||
private OpenSimBase _app; // The 'server'
|
|
||||||
private BaseHttpServer _httpd; // The server's RPC interface
|
|
||||||
private string _prefix; // URL prefix below
|
|
||||||
// which all REST URLs
|
|
||||||
// are living
|
|
||||||
// private StringWriter _sw = null;
|
|
||||||
// private RestXmlWriter _xw = null;
|
|
||||||
|
|
||||||
private string _godkey;
|
|
||||||
private int _reqk;
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static string _threadRequestID = String.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return an ever increasing request ID for logging
|
|
||||||
/// </summary>
|
|
||||||
protected string RequestID
|
|
||||||
{
|
|
||||||
get { return _reqk++.ToString(); }
|
|
||||||
set { _reqk = Convert.ToInt32(value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Thread-constant message IDs for logging.
|
|
||||||
/// </summary>
|
|
||||||
protected string MsgID
|
|
||||||
{
|
|
||||||
get { return String.Format("[REST-{0}] #{1}", Name, _threadRequestID); }
|
|
||||||
set { _threadRequestID = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if Rest Plugins are enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool PluginsAreEnabled
|
|
||||||
{
|
|
||||||
get { return null != _config; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if specific Rest Plugin is enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsEnabled
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (null != _pluginConfig) && _pluginConfig.GetBoolean("enabled", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// OpenSimMain application
|
|
||||||
/// </summary>
|
|
||||||
public OpenSimBase App
|
|
||||||
{
|
|
||||||
get { return _app; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RPC server
|
|
||||||
/// </summary>
|
|
||||||
public BaseHttpServer HttpServer
|
|
||||||
{
|
|
||||||
get { return _httpd; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// URL prefix to use for all REST handlers
|
|
||||||
/// </summary>
|
|
||||||
public string Prefix
|
|
||||||
{
|
|
||||||
get { return _prefix; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Access to GOD password string
|
|
||||||
/// </summary>
|
|
||||||
protected string GodKey
|
|
||||||
{
|
|
||||||
get { return _godkey; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configuration of the plugin
|
|
||||||
/// </summary>
|
|
||||||
public IConfig Config
|
|
||||||
{
|
|
||||||
get { return _pluginConfig; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the plugin
|
|
||||||
/// </summary>
|
|
||||||
public abstract string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return the config section name
|
|
||||||
/// </summary>
|
|
||||||
public abstract string ConfigName { get; }
|
|
||||||
|
|
||||||
// public XmlTextWriter XmlWriter
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// if (null == _xw)
|
|
||||||
// {
|
|
||||||
// _sw = new StringWriter();
|
|
||||||
// _xw = new RestXmlWriter(_sw);
|
|
||||||
// _xw.Formatting = Formatting.Indented;
|
|
||||||
// }
|
|
||||||
// return _xw;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public string XmlWriterResult
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// _xw.Flush();
|
|
||||||
// _xw.Close();
|
|
||||||
// _xw = null;
|
|
||||||
|
|
||||||
// return _sw.ToString();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#endregion properties
|
|
||||||
|
|
||||||
#region methods
|
|
||||||
|
|
||||||
// TODO: required by IPlugin, but likely not at all right
|
|
||||||
private string m_version = "0.0";
|
|
||||||
|
|
||||||
public string Version
|
|
||||||
{
|
|
||||||
get { return m_version; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialise()
|
|
||||||
{
|
|
||||||
m_log.Info("[RESTPLUGIN]: " + Name + " cannot be default-initialized!");
|
|
||||||
throw new PluginNotInitialisedException(Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is called by OpenSimMain immediately after loading the
|
|
||||||
/// plugin and after basic server setup, but before running any server commands.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that entries MUST be added to the active configuration files before
|
|
||||||
/// the plugin can be enabled.
|
|
||||||
/// </remarks>
|
|
||||||
public virtual void Initialise(OpenSimBase openSim)
|
|
||||||
{
|
|
||||||
RequestID = "0";
|
|
||||||
MsgID = RequestID;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if ((_config = openSim.ConfigSource.Source.Configs["RestPlugins"]) == null)
|
|
||||||
{
|
|
||||||
m_log.WarnFormat("{0} Rest Plugins not configured", MsgID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_config.GetBoolean("enabled", false))
|
|
||||||
{
|
|
||||||
//m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_app = openSim;
|
|
||||||
_httpd = openSim.HttpServer;
|
|
||||||
|
|
||||||
// Retrieve GOD key value, if any.
|
|
||||||
_godkey = _config.GetString("god_key", String.Empty);
|
|
||||||
|
|
||||||
// Retrive prefix if any.
|
|
||||||
_prefix = _config.GetString("prefix", "/admin");
|
|
||||||
|
|
||||||
// Get plugin specific config
|
|
||||||
_pluginConfig = openSim.ConfigSource.Source.Configs[ConfigName];
|
|
||||||
|
|
||||||
m_log.InfoFormat("{0} Rest Plugins Enabled", MsgID);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// we can safely ignore this, as it just means that
|
|
||||||
// the key lookup in Configs failed, which signals to
|
|
||||||
// us that noone is interested in our services...they
|
|
||||||
// don't know what they are missing out on...
|
|
||||||
// NOTE: Under the present OpenSimulator implementation it is
|
|
||||||
// not possible for the openSimulator pointer to be null. However
|
|
||||||
// were the implementation to be changed, this could
|
|
||||||
// result in a silent initialization failure. Harmless
|
|
||||||
// except for lack of function and lack of any
|
|
||||||
// diagnostic indication as to why. The same is true if
|
|
||||||
// the HTTP server reference is bad.
|
|
||||||
// We should at least issue a message...
|
|
||||||
m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message);
|
|
||||||
m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void PostInitialise()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RestStreamHandler> _handlers = new List<RestStreamHandler>();
|
|
||||||
private Dictionary<string, IHttpAgentHandler> _agents = new Dictionary<string, IHttpAgentHandler>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a REST stream handler to the underlying HTTP server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="httpMethod">GET/PUT/POST/DELETE or
|
|
||||||
/// similar</param>
|
|
||||||
/// <param name="path">URL prefix</param>
|
|
||||||
/// <param name="method">RestMethod handler doing the actual work</param>
|
|
||||||
public virtual void AddRestStreamHandler(string httpMethod, string path, RestMethod method)
|
|
||||||
{
|
|
||||||
if (!IsEnabled) return;
|
|
||||||
|
|
||||||
if (!path.StartsWith(_prefix))
|
|
||||||
{
|
|
||||||
path = String.Format("{0}{1}", _prefix, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
RestStreamHandler h = new RestStreamHandler(httpMethod, path, method);
|
|
||||||
_httpd.AddStreamHandler(h);
|
|
||||||
_handlers.Add(h);
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} Added REST handler {1} {2}", MsgID, httpMethod, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a powerful Agent handler to the underlying HTTP
|
|
||||||
/// server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="agentName">name of agent handler</param>
|
|
||||||
/// <param name="handler">agent handler method</param>
|
|
||||||
/// <returns>false when the plugin is disabled or the agent
|
|
||||||
/// handler could not be added. Any generated exceptions are
|
|
||||||
/// allowed to drop through to the caller, i.e. ArgumentException.
|
|
||||||
/// </returns>
|
|
||||||
public bool AddAgentHandler(string agentName, IHttpAgentHandler handler)
|
|
||||||
{
|
|
||||||
if (!IsEnabled) return false;
|
|
||||||
_agents.Add(agentName, handler);
|
|
||||||
// return _httpd.AddAgentHandler(agentName, handler);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remove a powerful Agent handler from the underlying HTTP
|
|
||||||
/// server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="agentName">name of agent handler</param>
|
|
||||||
/// <param name="handler">agent handler method</param>
|
|
||||||
/// <returns>false when the plugin is disabled or the agent
|
|
||||||
/// handler could not be removed. Any generated exceptions are
|
|
||||||
/// allowed to drop through to the caller, i.e. KeyNotFound.
|
|
||||||
/// </returns>
|
|
||||||
public bool RemoveAgentHandler(string agentName, IHttpAgentHandler handler)
|
|
||||||
{
|
|
||||||
if (!IsEnabled) return false;
|
|
||||||
if (_agents[agentName] == handler)
|
|
||||||
{
|
|
||||||
_agents.Remove(agentName);
|
|
||||||
// return _httpd.RemoveAgentHandler(agentName, handler);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check whether the HTTP request came from god; that is, is
|
|
||||||
/// the god_key as configured in the config section supplied
|
|
||||||
/// via X-OpenSim-Godkey?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">HTTP request header</param>
|
|
||||||
/// <returns>true when the HTTP request came from god.</returns>
|
|
||||||
protected bool IsGod(IOSHttpRequest request)
|
|
||||||
{
|
|
||||||
string[] keys = request.Headers.GetValues("X-OpenSim-Godkey");
|
|
||||||
if (null == keys) return false;
|
|
||||||
|
|
||||||
// we take the last key supplied
|
|
||||||
return keys[keys.Length - 1] == _godkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks wether the X-OpenSim-Password value provided in the
|
|
||||||
/// HTTP header is indeed the password on file for the avatar
|
|
||||||
/// specified by the UUID
|
|
||||||
/// </summary>
|
|
||||||
protected bool IsVerifiedUser(IOSHttpRequest request, UUID uuid)
|
|
||||||
{
|
|
||||||
// XXX under construction
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clean up and remove all handlers that were added earlier.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Close()
|
|
||||||
{
|
|
||||||
foreach (RestStreamHandler h in _handlers)
|
|
||||||
{
|
|
||||||
_httpd.RemoveStreamHandler(h.HttpMethod, h.Path);
|
|
||||||
}
|
|
||||||
_handlers = null;
|
|
||||||
// foreach (KeyValuePair<string, IHttpAgentHandler> h in _agents)
|
|
||||||
// {
|
|
||||||
// _httpd.RemoveAgentHandler(h.Key, h.Value);
|
|
||||||
// }
|
|
||||||
_agents = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Dispose()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return a failure message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method">origin of the failure message</param>
|
|
||||||
/// <param name="message">failure message</param>
|
|
||||||
/// <remarks>This should probably set a return code as
|
|
||||||
/// well. (?)</remarks>
|
|
||||||
protected string Failure(IOSHttpResponse response, OSHttpStatusCode status,
|
|
||||||
string method, string format, params string[] msg)
|
|
||||||
{
|
|
||||||
string m = String.Format(format, msg);
|
|
||||||
|
|
||||||
response.StatusCode = (int) status;
|
|
||||||
response.StatusDescription = m;
|
|
||||||
|
|
||||||
m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, m);
|
|
||||||
return String.Format("<error>{0}</error>", m);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return a failure message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method">origin of the failure message</param>
|
|
||||||
/// <param name="e">exception causing the failure message</param>
|
|
||||||
/// <remarks>This should probably set a return code as
|
|
||||||
/// well. (?)</remarks>
|
|
||||||
public string Failure(IOSHttpResponse response, OSHttpStatusCode status,
|
|
||||||
string method, Exception e)
|
|
||||||
{
|
|
||||||
string m = String.Format("exception occurred: {0}", e.Message);
|
|
||||||
|
|
||||||
response.StatusCode = (int) status;
|
|
||||||
response.StatusDescription = m;
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} {1} failed: {2}", MsgID, method, e.ToString());
|
|
||||||
m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, e.Message);
|
|
||||||
|
|
||||||
return String.Format("<error>{0}</error>", e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +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.IO;
|
|
||||||
using System.Text;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest
|
|
||||||
{
|
|
||||||
public class RestXmlWriter: XmlTextWriter
|
|
||||||
{
|
|
||||||
private StringWriter m_sw = null;
|
|
||||||
|
|
||||||
public RestXmlWriter(StringWriter sw) : base(sw)
|
|
||||||
{
|
|
||||||
m_sw = sw;
|
|
||||||
Formatting = Formatting.Indented;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestXmlWriter(TextWriter textWriter) : base(textWriter)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestXmlWriter(Stream stream)
|
|
||||||
: this(stream, Encoding.UTF8)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RestXmlWriter(Stream stream, Encoding enc) : base(stream, enc)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteStartDocument()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteStartDocument(bool standalone)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
Flush();
|
|
||||||
Close();
|
|
||||||
return m_sw.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,276 +0,0 @@
|
||||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
|
||||||
|
|
||||||
<xsd:annotation>
|
|
||||||
<xsd:documentation xml:lang="en">
|
|
||||||
Open Simulator Export/Import XML schema
|
|
||||||
August 2008
|
|
||||||
</xsd:documentation>
|
|
||||||
</xsd:annotation>
|
|
||||||
|
|
||||||
<!-- WARNING!!!
|
|
||||||
This is currently a draft, it does not reflect
|
|
||||||
what is exported, nor what will be understood
|
|
||||||
on import. It is included as a working document
|
|
||||||
and this comment will be removed at such time as
|
|
||||||
the schema corresponds to reality.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
REST-related information
|
|
||||||
Inventory data is always framed by an
|
|
||||||
inventory element. Consists of zero or
|
|
||||||
more elements representing either folders
|
|
||||||
or items within those folders. The inventory
|
|
||||||
element represents the "real" root folder.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:element name="inventory" type="inventory_ct" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The inventory complex type is just an arbitrary
|
|
||||||
sequence of folders and items. In reality it is
|
|
||||||
typically just folders. Both item and folder
|
|
||||||
have corresponding complex types. It is distinct
|
|
||||||
from folders insofar as it has no other defining
|
|
||||||
attributes.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:complexType name="inventory_ct">
|
|
||||||
<xsd:element name="folder" type="folder_ct" maxOccurs="unbounded"/>
|
|
||||||
<xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<xsd:complexType name="folder_ct">
|
|
||||||
<xsd:attribute name="UUID" type="uuid_st" />
|
|
||||||
<xsd:attribute name="name" type="name_st" />
|
|
||||||
<xsd:attribute name="type" type="folder_type_st" />
|
|
||||||
<xsd:attribute name="description" type="xsd:string" /> <!-- added -->
|
|
||||||
<xsd:attribute name="version" type="unsignedShort" />
|
|
||||||
<xsd:attribute name="owner" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="creator" type="uuid_st" /> <!-- added -->
|
|
||||||
<xsd:attribute name="creationdate" type="date_st" /> <!-- added -->
|
|
||||||
|
|
||||||
<xsd:attribute name="parent" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" /> <!-- added -->
|
|
||||||
<xsd:element name="folder" type="folder_ct" maxOccurs="unbounded" />
|
|
||||||
<xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<xsd:complexType name="item_ct">
|
|
||||||
<xsd:attribute name="UUID" type="uuid_st" />
|
|
||||||
<xsd:attribute name="name" type="name_st" />
|
|
||||||
<xsd:attribute name="type" type="inventory_type_st" />
|
|
||||||
<xsd:attribute name="description" type="xsd:string" />
|
|
||||||
<xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
|
|
||||||
<xsd:attribute name="owner" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="creator" type="uuid_st" />
|
|
||||||
<xsd:attribute name="creationdate" type="date_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="folder" type="uuid_st" />
|
|
||||||
<xsd:attribute name="groupid" type="uuid_st" />
|
|
||||||
<xsd:attribute name="groupowned" type="xsd:boolean" />
|
|
||||||
<xsd:attribute name="saletype" type="sale_st" />
|
|
||||||
<xsd:attribute name="saleprice" type="xsd:decimal" />
|
|
||||||
|
|
||||||
<xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<xsd:complexType name="asset_ct">
|
|
||||||
<xsd:attribute name="UUID" type="uuid_st" />
|
|
||||||
<xsd:attribute name="name" type="name_st" />
|
|
||||||
<xsd:attribute name="type" type="asset_type_st" />
|
|
||||||
<xsd:attribute name="description" type="xsd:string" />
|
|
||||||
<xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
|
|
||||||
<xsd:attribute name="owner" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="creator" type="uuid_st" />
|
|
||||||
<xsd:attribute name="creationdate" type="date_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="temporary" type="xsd:boolean" />
|
|
||||||
<xsd:attribute name="local" type="xsd:boolean" />
|
|
||||||
<xsd:attribute name="inline" type="xsd:boolean" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<!-- Constrained Simple Data types -->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
We need to specify name as a simple type because on
|
|
||||||
some platforms it is constrained by a certain length
|
|
||||||
limitation. For completeness we indicate that whitespace
|
|
||||||
should be preserved exactly as specified.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="name_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<whiteSpace value="preserve" />
|
|
||||||
<minLength value="0" />
|
|
||||||
<maxLength value="64" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Type information in the folder is meant to indicate
|
|
||||||
the preferred asset type for this folder. As such, that
|
|
||||||
currently corresponds to the type values allowed for
|
|
||||||
assets, however that is not mandated, so for
|
|
||||||
now at least I'll represent this as a distinct
|
|
||||||
enumeration.
|
|
||||||
This seems inappropriate; it seems like the folder's
|
|
||||||
content should reflect the InventoryType classifications
|
|
||||||
rather than the asset types.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="folder_type_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:enumeration value="Texture" />
|
|
||||||
<xsd:enumeration value="Sound" />
|
|
||||||
<xsd:enumeration value="CallingCard" />
|
|
||||||
<xsd:enumeration value="Landmark" />
|
|
||||||
<xsd:enumeration value="Script" />
|
|
||||||
<xsd:enumeration value="Clothing" />
|
|
||||||
<xsd:enumeration value="Object" />
|
|
||||||
<xsd:enumeration value="Notecard" />
|
|
||||||
<xsd:enumeration value="LSLText" />
|
|
||||||
<xsd:enumeration value="LSLByteCode" />
|
|
||||||
<xsd:enumeration value="TextureTGA" />
|
|
||||||
<xsd:enumeration value="BodyPart" />
|
|
||||||
<xsd:enumeration value="SoundWAV" />
|
|
||||||
<xsd:enumeration value="ImageTGA" />
|
|
||||||
<xsd:enumeration value="ImageJPEG" />
|
|
||||||
<xsd:enumeration value="Animation" />
|
|
||||||
<xsd:enumeration value="Gesture" />
|
|
||||||
<xsd:enumeration value="Simstate" />
|
|
||||||
<xsd:enumeration value="Unknown" />
|
|
||||||
<xsd:enumeration value="LostAndFoundFolder" />
|
|
||||||
<xsd:enumeration value="SnapshotFolder" />
|
|
||||||
<xsd:enumeration value="TrashFolder" />
|
|
||||||
<xsd:enumeration value="Folder" />
|
|
||||||
<xsd:enumeration value="RootFolder" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Inventory item type designates an asset class, rather
|
|
||||||
than a specific asset type. For example, "SnapShot"
|
|
||||||
might include a number of asset types such as JPEG,
|
|
||||||
TGA, etc.. This is not a consistent interpretation,
|
|
||||||
classifications such as LostAndFound are meta-types
|
|
||||||
relative to asset classes.
|
|
||||||
|
|
||||||
These types should be abstract and not be tied to a
|
|
||||||
specific platform. A world's import facility should be
|
|
||||||
responsible for mapping these to meaningful internal
|
|
||||||
representations.
|
|
||||||
|
|
||||||
These types were based on information in:
|
|
||||||
libsecondlife/InventoryManager.cs
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="inventory_type_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:enumeration value="Texture" />
|
|
||||||
<xsd:enumeration value="Sound" />
|
|
||||||
<xsd:enumeration value="CallingCard" />
|
|
||||||
<xsd:enumeration value="Landmark" />
|
|
||||||
<xsd:enumeration value="Script" />
|
|
||||||
<xsd:enumeration value="Clothing" />
|
|
||||||
<xsd:enumeration value="Object" />
|
|
||||||
<xsd:enumeration value="Notecard" />
|
|
||||||
<xsd:enumeration value="LSL" />
|
|
||||||
<xsd:enumeration value="LSLBytecode" />
|
|
||||||
<xsd:enumeration value="TextureTGA" />
|
|
||||||
<xsd:enumeration value="BodyPart" />
|
|
||||||
<xsd:enumeration value="Snapshot" />
|
|
||||||
<xsd:enumeration value="Attachment" />
|
|
||||||
<xsd:enumeration value="Wearable" />
|
|
||||||
<xsd:enumeration value="Animation" />
|
|
||||||
<xsd:enumeration value="Gesture" />
|
|
||||||
<xsd:enumeration value="Folder" />
|
|
||||||
<xsd:enumeration value="Unknown" />
|
|
||||||
<xsd:enumeration value="LostAndFound" />
|
|
||||||
<xsd:enumeration value="Trash" />
|
|
||||||
<xsd:enumeration value="Root" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The asset types seem to be even more disarrayed than
|
|
||||||
the inventory types. It seems to be little more than
|
|
||||||
a reiteration of the inventory type information,
|
|
||||||
which adds little or nothing to the overall data
|
|
||||||
model.
|
|
||||||
|
|
||||||
Of course, given that these are drawn from the
|
|
||||||
libsecondlife definitions, we aren't at liberty to
|
|
||||||
simply redefine them in place. But the XML definitions
|
|
||||||
here could be made more useful.
|
|
||||||
|
|
||||||
These types were based on information in:
|
|
||||||
libsecondlife/AssetManager.cs
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="asset_type_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:enumeration value="Texture" />
|
|
||||||
<xsd:enumeration value="Sound" />
|
|
||||||
<xsd:enumeration value="CallingCard" />
|
|
||||||
<xsd:enumeration value="Landmark" />
|
|
||||||
<xsd:enumeration value="Script" />
|
|
||||||
<xsd:enumeration value="Clothing" />
|
|
||||||
<xsd:enumeration value="Object" />
|
|
||||||
<xsd:enumeration value="Notecard" />
|
|
||||||
<xsd:enumeration value="LSLText" />
|
|
||||||
<xsd:enumeration value="LSLByteCode" />
|
|
||||||
<xsd:enumeration value="TextureTGA" />
|
|
||||||
<xsd:enumeration value="BodyPart" />
|
|
||||||
<xsd:enumeration value="SoundWAV" />
|
|
||||||
<xsd:enumeration value="ImageTGA" />
|
|
||||||
<xsd:enumeration value="ImageJPEG" />
|
|
||||||
<xsd:enumeration value="Animation" />
|
|
||||||
<xsd:enumeration value="Gesture" />
|
|
||||||
<xsd:enumeration value="Simstate" />
|
|
||||||
<xsd:enumeration value="Unknown" />
|
|
||||||
<xsd:enumeration value="LostAndFoundFolder" />
|
|
||||||
<xsd:enumeration value="SnapshotFolder" />
|
|
||||||
<xsd:enumeration value="TrashFolder" />
|
|
||||||
<xsd:enumeration value="Folder" />
|
|
||||||
<xsd:enumeration value="RootFolder" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!-- This is describing the apparent form of a UUID. If
|
|
||||||
we ever want a more metaphysical definition we'll
|
|
||||||
need to add to it.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="uuid_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:pattern value="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"/>
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!-- This constrains the date representation. Currently
|
|
||||||
it is simply an integer representing the elapsed
|
|
||||||
?? since ??.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="date_st">
|
|
||||||
<xsd:restriction base="xsd:positiveInteger">
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!-- This constrains the representation of sale price.
|
|
||||||
Currently it is a simple decimal with no unit
|
|
||||||
specified.
|
|
||||||
Issues: interoperability.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="sale_st">
|
|
||||||
<xsd:restriction base="xsd:decimal">
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
</xsd:schema>
|
|
115
prebuild.xml
115
prebuild.xml
|
@ -1914,121 +1914,6 @@
|
||||||
</Files>
|
</Files>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
||||||
<!-- REST plugins -->
|
|
||||||
<Project frameworkVersion="v3_5" name="OpenSim.ApplicationPlugins.Rest" path="OpenSim/ApplicationPlugins/Rest" type="Library">
|
|
||||||
<Configuration name="Debug">
|
|
||||||
<Options>
|
|
||||||
<OutputPath>../../../bin/</OutputPath>
|
|
||||||
</Options>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration name="Release">
|
|
||||||
<Options>
|
|
||||||
<OutputPath>../../../bin/</OutputPath>
|
|
||||||
</Options>
|
|
||||||
</Configuration>
|
|
||||||
|
|
||||||
<ReferencePath>../../../bin/</ReferencePath>
|
|
||||||
<Reference name="Mono.Addins" path="../../../bin/"/>
|
|
||||||
<Reference name="System"/>
|
|
||||||
<Reference name="System.Xml"/>
|
|
||||||
<Reference name="OpenMetaverseTypes" path="../../../bin/"/>
|
|
||||||
<Reference name="Nini" path="../../../bin/"/>
|
|
||||||
<Reference name="XMLRPC" path="../../../bin/"/>
|
|
||||||
<Reference name="OpenSim"/>
|
|
||||||
<Reference name="OpenSim.Region.ClientStack"/>
|
|
||||||
<Reference name="OpenSim.Region.Framework"/>
|
|
||||||
<Reference name="OpenSim.Region.CoreModules"/>
|
|
||||||
<Reference name="OpenSim.Framework.Communications"/>
|
|
||||||
<Reference name="OpenSim.Framework"/>
|
|
||||||
<Reference name="OpenSim.Framework.Servers"/>
|
|
||||||
<Reference name="OpenSim.Framework.Servers.HttpServer"/>
|
|
||||||
<Reference name="OpenSim.Framework.Console"/>
|
|
||||||
<Reference name="log4net" path="../../../bin/"/>
|
|
||||||
|
|
||||||
<Files>
|
|
||||||
<Match pattern="*.cs" recurse="false"/>
|
|
||||||
</Files>
|
|
||||||
</Project>
|
|
||||||
|
|
||||||
<Project frameworkVersion="v3_5" name="OpenSim.ApplicationPlugins.Rest.Regions" path="OpenSim/ApplicationPlugins/Rest/Regions" type="Library">
|
|
||||||
<Configuration name="Debug">
|
|
||||||
<Options>
|
|
||||||
<OutputPath>../../../../bin/</OutputPath>
|
|
||||||
</Options>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration name="Release">
|
|
||||||
<Options>
|
|
||||||
<OutputPath>../../../../bin/</OutputPath>
|
|
||||||
</Options>
|
|
||||||
</Configuration>
|
|
||||||
|
|
||||||
<ReferencePath>../../../../bin/</ReferencePath>
|
|
||||||
<Reference name="Mono.Addins" path="../../../../bin/"/>
|
|
||||||
<Reference name="System"/>
|
|
||||||
<Reference name="System.Xml"/>
|
|
||||||
<Reference name="OpenMetaverseTypes" path="../../../../bin/"/>
|
|
||||||
<Reference name="Nini" path="../../../../bin/"/>
|
|
||||||
<Reference name="XMLRPC" path="../../../../bin/"/>
|
|
||||||
<Reference name="OpenSim"/>
|
|
||||||
<Reference name="OpenSim.Region.Framework"/>
|
|
||||||
<Reference name="OpenSim.Region.ClientStack"/>
|
|
||||||
<Reference name="OpenSim.Region.CoreModules"/>
|
|
||||||
<Reference name="OpenSim.Framework.Communications"/>
|
|
||||||
<Reference name="OpenSim.Framework"/>
|
|
||||||
<Reference name="OpenSim.Framework.Servers"/>
|
|
||||||
<Reference name="OpenSim.Framework.Servers.HttpServer"/>
|
|
||||||
<Reference name="OpenSim.Framework.Console"/>
|
|
||||||
<Reference name="OpenSim.ApplicationPlugins.Rest"/>
|
|
||||||
<Reference name="log4net" path="../../../../bin/"/>
|
|
||||||
|
|
||||||
<Files>
|
|
||||||
<Match pattern="*.cs" recurse="true"/>
|
|
||||||
<Match buildAction="EmbeddedResource" path="Resources" pattern="*.addin.xml" recurse="true"/>
|
|
||||||
</Files>
|
|
||||||
</Project>
|
|
||||||
|
|
||||||
<Project frameworkVersion="v3_5" name="OpenSim.ApplicationPlugins.Rest.Inventory" path="OpenSim/ApplicationPlugins/Rest/Inventory" type="Library">
|
|
||||||
<Configuration name="Debug">
|
|
||||||
<Options>
|
|
||||||
<OutputPath>../../../../bin/</OutputPath>
|
|
||||||
</Options>
|
|
||||||
</Configuration>
|
|
||||||
<Configuration name="Release">
|
|
||||||
<Options>
|
|
||||||
<OutputPath>../../../../bin/</OutputPath>
|
|
||||||
</Options>
|
|
||||||
</Configuration>
|
|
||||||
|
|
||||||
<ReferencePath>../../../../bin/</ReferencePath>
|
|
||||||
<Reference name="Mono.Addins" path="../../../../bin/"/>
|
|
||||||
<Reference name="System"/>
|
|
||||||
<Reference name="System.Xml"/>
|
|
||||||
<Reference name="System.Drawing"/>
|
|
||||||
<Reference name="OpenMetaverseTypes" path="../../../../bin/"/>
|
|
||||||
<Reference name="OpenMetaverse" path="../../../../bin/"/>
|
|
||||||
<Reference name="Nini" path="../../../../bin/"/>
|
|
||||||
<Reference name="XMLRPC" path="../../../../bin/"/>
|
|
||||||
<Reference name="OpenSim"/>
|
|
||||||
<Reference name="OpenSim.Region.Framework"/>
|
|
||||||
<Reference name="OpenSim.Region.ClientStack"/>
|
|
||||||
<Reference name="OpenSim.Region.CoreModules"/>
|
|
||||||
<Reference name="OpenSim.Framework.Communications"/>
|
|
||||||
<Reference name="OpenSim.Framework"/>
|
|
||||||
<Reference name="OpenSim.Framework.Servers"/>
|
|
||||||
<Reference name="OpenSim.Framework.Servers.HttpServer"/>
|
|
||||||
<Reference name="OpenSim.Framework.Console"/>
|
|
||||||
<Reference name="OpenSim.Services.Interfaces"/>
|
|
||||||
<Reference name="OpenSim.ApplicationPlugins.Rest"/>
|
|
||||||
<Reference name="log4net" path="../../../../bin/"/>
|
|
||||||
|
|
||||||
<Files>
|
|
||||||
<Match pattern="*.cs" recurse="true"/>
|
|
||||||
<Match buildAction="EmbeddedResource" path="Resources" pattern="*.addin.xml" recurse="true"/>
|
|
||||||
</Files>
|
|
||||||
</Project>
|
|
||||||
|
|
||||||
<!-- /REST plugins -->
|
|
||||||
|
|
||||||
<!-- Scene Server API Example Apps -->
|
<!-- Scene Server API Example Apps -->
|
||||||
|
|
||||||
<Project frameworkVersion="v3_5" name="OpenSim.Region.DataSnapshot" path="OpenSim/Region/DataSnapshot" type="Library">
|
<Project frameworkVersion="v3_5" name="OpenSim.Region.DataSnapshot" path="OpenSim/Region/DataSnapshot" type="Library">
|
||||||
|
|
Loading…
Reference in New Issue