Merge branch 'master' of melanie@opensimulator.org:/var/git/opensim
commit
46a81b3527
|
@ -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>
|
|
@ -486,7 +486,9 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
{
|
||||
try
|
||||
{
|
||||
SendHTML500(response);
|
||||
byte[] buffer500 = SendHTML500(response);
|
||||
response.Body.Write(buffer500,0,buffer500.Length);
|
||||
response.Body.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -719,7 +721,15 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
catch (Exception e)
|
||||
{
|
||||
m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
|
||||
SendHTML500(response);
|
||||
try
|
||||
{
|
||||
byte[] buffer500 = SendHTML500(response);
|
||||
response.Body.Write(buffer500, 0, buffer500.Length);
|
||||
response.Body.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -1747,6 +1757,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
|||
response.ContentLength64 = buffer.Length;
|
||||
response.ContentEncoding = Encoding.UTF8;
|
||||
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
{
|
||||
enableAngularVerticalAttraction = true;
|
||||
enableAngularDeflection = false;
|
||||
enableAngularBanking = false;
|
||||
enableAngularBanking = true;
|
||||
if (BSParam.VehicleDebuggingEnabled)
|
||||
{
|
||||
enableAngularVerticalAttraction = true;
|
||||
|
@ -1280,11 +1280,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
// That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
|
||||
// TODO: This is here because this is where ODE put it but documentation says it
|
||||
// is a linear effect. Where should this check go?
|
||||
if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
|
||||
{
|
||||
angularMotorContributionV.X = 0f;
|
||||
angularMotorContributionV.Y = 0f;
|
||||
}
|
||||
//if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
|
||||
// {
|
||||
// angularMotorContributionV.X = 0f;
|
||||
// angularMotorContributionV.Y = 0f;
|
||||
// }
|
||||
|
||||
VehicleRotationalVelocity += angularMotorContributionV * VehicleOrientation;
|
||||
VDetailLog("{0}, MoveAngular,angularTurning,angularMotorContrib={1}", Prim.LocalID, angularMotorContributionV);
|
||||
|
@ -1439,22 +1439,23 @@ namespace OpenSim.Region.Physics.BulletSPlugin
|
|||
Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
|
||||
|
||||
// Figure out the yaw value for this much roll.
|
||||
// Squared because that seems to give a good value
|
||||
float yawAngle = (float)Math.Asin(rollComponents.Y * rollComponents.Y) * m_bankingEfficiency;
|
||||
|
||||
float yawAngle = m_angularMotorDirection.X * m_bankingEfficiency;
|
||||
// actual error = static turn error + dynamic turn error
|
||||
float mixedYawAngle = yawAngle * (1f - m_bankingMix) + yawAngle * m_bankingMix * VehicleForwardSpeed;
|
||||
float mixedYawAngle =(yawAngle * (1f - m_bankingMix)) + ((yawAngle * m_bankingMix) * VehicleForwardSpeed);
|
||||
|
||||
// TODO: the banking effect should not go to infinity but what to limit it to?
|
||||
mixedYawAngle = ClampInRange(-20f, mixedYawAngle, 20f);
|
||||
// And what should happen when this is being added to a user defined yaw that is already PI*4?
|
||||
mixedYawAngle = ClampInRange(-12, mixedYawAngle, 12);
|
||||
|
||||
// Build the force vector to change rotation from what it is to what it should be
|
||||
bankingContributionV.Z = -mixedYawAngle;
|
||||
|
||||
// Don't do it all at once.
|
||||
bankingContributionV /= m_bankingTimescale;
|
||||
// Don't do it all at once. Fudge because 1 second is too fast with most user defined roll as PI*4.
|
||||
bankingContributionV /= m_bankingTimescale * BSParam.VehicleAngularBankingTimescaleFudge;
|
||||
|
||||
//VehicleRotationalVelocity += bankingContributionV * VehicleOrientation;
|
||||
VehicleRotationalVelocity += bankingContributionV;
|
||||
|
||||
VehicleRotationalVelocity += bankingContributionV * VehicleOrientation;
|
||||
|
||||
VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}",
|
||||
Prim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV);
|
||||
|
|
|
@ -123,6 +123,7 @@ public static class BSParam
|
|||
public static Vector3 VehicleLinearFactor { get; private set; }
|
||||
public static Vector3 VehicleAngularFactor { get; private set; }
|
||||
public static float VehicleGroundGravityFudge { get; private set; }
|
||||
public static float VehicleAngularBankingTimescaleFudge { get; private set; }
|
||||
public static bool VehicleDebuggingEnabled { get; private set; }
|
||||
|
||||
// Linkset implementation parameters
|
||||
|
@ -543,10 +544,14 @@ public static class BSParam
|
|||
0.0f,
|
||||
(s) => { return VehicleRestitution; },
|
||||
(s,v) => { VehicleRestitution = v; } ),
|
||||
new ParameterDefn<float>("VehicleGroundGravityFudge", "Factor to multiple gravity if a ground vehicle is probably on the ground (0.0 - 1.0)",
|
||||
new ParameterDefn<float>("VehicleGroundGravityFudge", "Factor to multiply gravity if a ground vehicle is probably on the ground (0.0 - 1.0)",
|
||||
0.2f,
|
||||
(s) => { return VehicleGroundGravityFudge; },
|
||||
(s,v) => { VehicleGroundGravityFudge = v; } ),
|
||||
new ParameterDefn<float>("VehicleAngularBankingTimescaleFudge", "Factor to multiple angular banking timescale. Tune to increase realism.",
|
||||
60.0f,
|
||||
(s) => { return VehicleAngularBankingTimescaleFudge; },
|
||||
(s,v) => { VehicleAngularBankingTimescaleFudge = v; } ),
|
||||
new ParameterDefn<bool>("VehicleDebuggingEnable", "Turn on/off vehicle debugging",
|
||||
false,
|
||||
(s) => { return VehicleDebuggingEnabled; },
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1669,6 +1669,65 @@
|
|||
A header have been received.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:HttpServer.LocklessQueue`1">
|
||||
<summary>
|
||||
A thread-safe lockless queue that supports multiple readers and
|
||||
multiple writers
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:HttpServer.LocklessQueue`1.head">
|
||||
<summary>Queue head</summary>
|
||||
</member>
|
||||
<member name="F:HttpServer.LocklessQueue`1.tail">
|
||||
<summary>Queue tail</summary>
|
||||
</member>
|
||||
<member name="F:HttpServer.LocklessQueue`1.count">
|
||||
<summary>Queue item count</summary>
|
||||
</member>
|
||||
<member name="M:HttpServer.LocklessQueue`1.#ctor">
|
||||
<summary>
|
||||
Constructor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:HttpServer.LocklessQueue`1.Enqueue(`0)">
|
||||
<summary>
|
||||
Enqueue an item
|
||||
</summary>
|
||||
<param name="item">Item to enqeue</param>
|
||||
</member>
|
||||
<member name="M:HttpServer.LocklessQueue`1.TryDequeue(`0@)">
|
||||
<summary>
|
||||
Try to dequeue an item
|
||||
</summary>
|
||||
<param name="item">Dequeued item if the dequeue was successful</param>
|
||||
<returns>True if an item was successfully deqeued, otherwise false</returns>
|
||||
</member>
|
||||
<member name="P:HttpServer.LocklessQueue`1.Count">
|
||||
<summary>Gets the current number of items in the queue. Since this
|
||||
is a lockless collection this value should be treated as a close
|
||||
estimate</summary>
|
||||
</member>
|
||||
<member name="T:HttpServer.LocklessQueue`1.SingleLinkNode">
|
||||
<summary>
|
||||
Provides a node container for data in a singly linked list
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:HttpServer.LocklessQueue`1.SingleLinkNode.Next">
|
||||
<summary>Pointer to the next node in list</summary>
|
||||
</member>
|
||||
<member name="F:HttpServer.LocklessQueue`1.SingleLinkNode.Item">
|
||||
<summary>The data contained by the node</summary>
|
||||
</member>
|
||||
<member name="M:HttpServer.LocklessQueue`1.SingleLinkNode.#ctor">
|
||||
<summary>
|
||||
Constructor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:HttpServer.LocklessQueue`1.SingleLinkNode.#ctor(`0)">
|
||||
<summary>
|
||||
Constructor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:HttpServer.IHttpRequest">
|
||||
<summary>
|
||||
Contains server side HTTP request information.
|
||||
|
@ -2825,6 +2884,11 @@
|
|||
<param name="protocol">Kind of HTTPS protocol. Usually TLS or SSL.</param>
|
||||
<returns>A created <see cref="T:HttpServer.IHttpClientContext"/>.</returns>
|
||||
</member>
|
||||
<member name="M:HttpServer.IHttpContextFactory.Shutdown">
|
||||
<summary>
|
||||
Server is shutting down so shut down the factory
|
||||
</summary>
|
||||
</member>
|
||||
<member name="E:HttpServer.IHttpContextFactory.RequestReceived">
|
||||
<summary>
|
||||
A request have been received from one of the contexts.
|
||||
|
@ -2876,6 +2940,11 @@
|
|||
A creates <see cref="T:HttpServer.IHttpClientContext"/>.
|
||||
</returns>
|
||||
</member>
|
||||
<member name="M:HttpServer.HttpContextFactory.Shutdown">
|
||||
<summary>
|
||||
Server is shutting down so shut down the factory
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:HttpServer.HttpContextFactory.UseTraceLogs">
|
||||
<summary>
|
||||
True if detailed trace logs should be written.
|
||||
|
@ -4315,6 +4384,58 @@
|
|||
</summary>
|
||||
<param name="message">message describing the error</param>
|
||||
</member>
|
||||
<member name="T:HttpServer.ContextTimeoutManager">
|
||||
<summary>
|
||||
Timeout Manager. Checks for dead clients. Clients with open connections that are not doing anything. Closes sessions opened with keepalive.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:HttpServer.ContextTimeoutManager.ProcessContextTimeouts">
|
||||
<summary>
|
||||
Causes the watcher to immediately check the connections.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCount">
|
||||
<summary>
|
||||
Environment.TickCount is an int but it counts all 32 bits so it goes positive
|
||||
and negative every 24.9 days. This trims down TickCount so it doesn't wrap
|
||||
for the callers.
|
||||
This trims it to a 12 day interval so don't let your frame time get too long.
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCountSubtract(System.Int32,System.Int32)">
|
||||
<summary>
|
||||
Environment.TickCount is an int but it counts all 32 bits so it goes positive
|
||||
and negative every 24.9 days. Subtracts the passed value (previously fetched by
|
||||
'EnvironmentTickCount()') and accounts for any wrapping.
|
||||
</summary>
|
||||
<param name="newValue"></param>
|
||||
<param name="prevValue"></param>
|
||||
<returns>subtraction of passed prevValue from current Environment.TickCount</returns>
|
||||
</member>
|
||||
<member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCountAdd(System.Int32,System.Int32)">
|
||||
<summary>
|
||||
Environment.TickCount is an int but it counts all 32 bits so it goes positive
|
||||
and negative every 24.9 days. Subtracts the passed value (previously fetched by
|
||||
'EnvironmentTickCount()') and accounts for any wrapping.
|
||||
</summary>
|
||||
<param name="newValue"></param>
|
||||
<param name="prevValue"></param>
|
||||
<returns>subtraction of passed prevValue from current Environment.TickCount</returns>
|
||||
</member>
|
||||
<member name="M:HttpServer.ContextTimeoutManager.EnvironmentTickCountSubtract(System.Int32)">
|
||||
<summary>
|
||||
Environment.TickCount is an int but it counts all 32 bits so it goes positive
|
||||
and negative every 24.9 days. Subtracts the passed value (previously fetched by
|
||||
'EnvironmentTickCount()') and accounts for any wrapping.
|
||||
</summary>
|
||||
<returns>subtraction of passed prevValue from current Environment.TickCount</returns>
|
||||
</member>
|
||||
<member name="T:HttpServer.ContextTimeoutManager.MonitorType">
|
||||
<summary>
|
||||
Use a Thread or a Timer to monitor the ugly
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:HttpServer.Sessions.MemorySessionStore">
|
||||
<summary>
|
||||
Session store using memory for each session.
|
||||
|
|
115
prebuild.xml
115
prebuild.xml
|
@ -1914,121 +1914,6 @@
|
|||
</Files>
|
||||
</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 -->
|
||||
|
||||
<Project frameworkVersion="v3_5" name="OpenSim.Region.DataSnapshot" path="OpenSim/Region/DataSnapshot" type="Library">
|
||||
|
|
Loading…
Reference in New Issue