parent
19ad7db5e1
commit
7025a8040e
|
@ -34,8 +34,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This interface represents the boundary between the general purpose
|
/// This interface represents the boundary between the general purpose
|
||||||
/// REST plugin handling, and the functionally specific handlers. The
|
/// REST plugin handling, and the functionally specific handlers. The
|
||||||
/// handler knows only to initialzie and terminate all such handlers
|
/// handler knows only to initialize and terminate all such handlers
|
||||||
/// that it finds.
|
/// that it finds. Implementing this interface identifies the class as
|
||||||
|
/// a REST handler implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
internal interface IRest
|
internal interface IRest
|
||||||
|
|
|
@ -34,6 +34,7 @@ using System.Security.Cryptography;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Servers;
|
using OpenSim.Framework.Servers;
|
||||||
using libsecondlife;
|
using libsecondlife;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
@ -50,7 +51,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
/// This structure is created on entry to the Handler
|
/// This structure is created on entry to the Handler
|
||||||
/// method and is disposed of upon return. It is part of
|
/// method and is disposed of upon return. It is part of
|
||||||
/// the plug-in infrastructure, rather than the functionally
|
/// the plug-in infrastructure, rather than the functionally
|
||||||
/// specifici REST handler, and fundamental changes to
|
/// specific REST handler, and fundamental changes to
|
||||||
/// this should be reflected in the Rest HandlerVersion. The
|
/// this should be reflected in the Rest HandlerVersion. The
|
||||||
/// object is instantiated, and may be extended by, any
|
/// object is instantiated, and may be extended by, any
|
||||||
/// given handler. See the inventory handler for an example
|
/// given handler. See the inventory handler for an example
|
||||||
|
@ -71,11 +72,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
internal OSHttpRequest request = null;
|
internal OSHttpRequest request = null;
|
||||||
internal OSHttpResponse response = null;
|
internal OSHttpResponse response = null;
|
||||||
|
internal string qprefix = null;
|
||||||
|
|
||||||
// Request lifetime values
|
// Request lifetime values
|
||||||
|
|
||||||
internal NameValueCollection headers = null;
|
|
||||||
internal List<string> removed_headers = null;
|
|
||||||
internal byte[] buffer = null;
|
internal byte[] buffer = null;
|
||||||
internal string body = null;
|
internal string body = null;
|
||||||
internal string html = null;
|
internal string html = null;
|
||||||
|
@ -96,11 +96,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
internal string hostname = "localhost";
|
internal string hostname = "localhost";
|
||||||
internal int port = 80;
|
internal int port = 80;
|
||||||
internal string prefix = Rest.UrlPathSeparator;
|
internal string prefix = Rest.UrlPathSeparator;
|
||||||
|
internal bool keepAlive = false;
|
||||||
|
internal bool chunked = false;
|
||||||
|
|
||||||
// Authentication related state
|
// Authentication related state
|
||||||
|
|
||||||
internal bool authenticated = false;
|
internal bool authenticated = false;
|
||||||
internal string scheme = Rest.AS_DIGEST;
|
// internal string scheme = Rest.AS_DIGEST;
|
||||||
|
// internal string scheme = Rest.AS_BASIC;
|
||||||
|
internal string scheme = null;
|
||||||
internal string realm = Rest.Realm;
|
internal string realm = Rest.Realm;
|
||||||
internal string domain = null;
|
internal string domain = null;
|
||||||
internal string nonce = null;
|
internal string nonce = null;
|
||||||
|
@ -148,13 +152,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
private static Regex basicParms = new Regex("^\\s*(?:\\w+)\\s+(?<pval>\\S+)\\s*",
|
private static Regex basicParms = new Regex("^\\s*(?:\\w+)\\s+(?<pval>\\S+)\\s*",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private static Regex digestParm1 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*\"(?<pval>\\S+)\"",
|
private static Regex digestParm1 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*\"(?<pval>[^\"]+)\"",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private static Regex digestParm2 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*(?<pval>[^\\p{P}\\s]+)",
|
private static Regex digestParm2 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*(?<pval>[^\\p{P}\\s]+)",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private static Regex reuserPass = new Regex("\\s*(?<user>\\w+)\\s*:\\s*(?<pass>\\S*)",
|
private static Regex reuserPass = new Regex("\\s*(?<user>[^:]+)\\s*:\\s*(?<pass>\\S*)",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
// For efficiency, we create static instances of these objects
|
// For efficiency, we create static instances of these objects
|
||||||
|
@ -165,11 +169,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
|
|
||||||
internal RequestData(OSHttpRequest p_request, OSHttpResponse p_response, string qprefix)
|
internal RequestData(OSHttpRequest p_request, OSHttpResponse p_response, string p_qprefix)
|
||||||
{
|
{
|
||||||
|
|
||||||
request = p_request;
|
request = p_request;
|
||||||
response = p_response;
|
response = p_response;
|
||||||
|
qprefix = p_qprefix;
|
||||||
|
|
||||||
sbuilder.Length = 0;
|
sbuilder.Length = 0;
|
||||||
|
|
||||||
|
@ -182,7 +187,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
method = request.HttpMethod.ToLower();
|
method = request.HttpMethod.ToLower();
|
||||||
initUrl();
|
initUrl();
|
||||||
|
|
||||||
initParameters(qprefix.Length);
|
initParameters(p_qprefix.Length);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,11 +259,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we want a specific authentication mechanism, make sure
|
// If we want a specific authentication mechanism, make sure
|
||||||
// we get it.
|
// we get it. null indicates we don't care. non-null indicates
|
||||||
|
// a specific scheme requirement.
|
||||||
|
|
||||||
if (scheme != null && scheme.ToLower() != reqscheme)
|
if (scheme != null && scheme.ToLower() != reqscheme)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Challenge reason: Required scheme not accepted", MsgId);
|
Rest.Log.DebugFormat("{0} Challenge reason: Requested scheme not acceptable", MsgId);
|
||||||
DoChallenge();
|
DoChallenge();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,15 +274,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
switch (reqscheme)
|
switch (reqscheme)
|
||||||
{
|
{
|
||||||
case "digest" :
|
case "digest" :
|
||||||
Rest.Log.DebugFormat("{0} Digest authentication offered", MsgId);
|
Rest.Log.DebugFormat("{0} Digest authentication offered", MsgId);
|
||||||
DoDigest(authdata);
|
DoDigest(authdata);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "basic" :
|
case "basic" :
|
||||||
Rest.Log.DebugFormat("{0} Basic authentication offered", MsgId);
|
Rest.Log.DebugFormat("{0} Basic authentication offered", MsgId);
|
||||||
DoBasic(authdata);
|
DoBasic(authdata);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the current header is invalid, then a challenge is still needed.
|
// If the current header is invalid, then a challenge is still needed.
|
||||||
|
@ -406,10 +412,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
authparms.TryGetValue("uri", out authPrefix);
|
authparms.TryGetValue("uri", out authPrefix);
|
||||||
|
|
||||||
// There MUST be a nonce string present. We're not preserving any server
|
// There MUST be a nonce string present. We're not preserving any server
|
||||||
// side state and we can;t validate the MD5 unless the lcient returns it
|
// side state and we can't validate the MD5 unless the client returns it
|
||||||
// to us, as it should.
|
// to us, as it should.
|
||||||
|
|
||||||
if (!authparms.TryGetValue("nonce", out nonce))
|
if (!authparms.TryGetValue("nonce", out nonce) || nonce == null)
|
||||||
{
|
{
|
||||||
Rest.Log.WarnFormat("{0} Authentication failed: nonce missing", MsgId);
|
Rest.Log.WarnFormat("{0} Authentication failed: nonce missing", MsgId);
|
||||||
break;
|
break;
|
||||||
|
@ -457,26 +463,28 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
cnonce = authparms["cnonce"];
|
cnonce = authparms["cnonce"];
|
||||||
|
|
||||||
if (!authparms.ContainsKey("nc"))
|
if (!authparms.TryGetValue("nc", out nck) || nck == null)
|
||||||
{
|
{
|
||||||
Rest.Log.WarnFormat("{0} Authentication failed: cnonce counter missing", MsgId);
|
Rest.Log.WarnFormat("{0} Authentication failed: cnonce counter missing", MsgId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
nck = authparms["nc"];
|
Rest.Log.DebugFormat("{0} Comparing nonce indices", MsgId);
|
||||||
|
|
||||||
if (cntable.TryGetValue(cnonce, out ncl))
|
if (cntable.TryGetValue(nonce, out ncl))
|
||||||
{
|
{
|
||||||
if (Rest.Hex2Int(ncl) <= Rest.Hex2Int(nck))
|
Rest.Log.DebugFormat("{0} nonce values: Verify that request({1}) > Reference({2})", MsgId, nck, ncl);
|
||||||
|
|
||||||
|
if (Rest.Hex2Int(ncl) >= Rest.Hex2Int(nck))
|
||||||
{
|
{
|
||||||
Rest.Log.WarnFormat("{0} Authentication failed: bad cnonce counter", MsgId);
|
Rest.Log.WarnFormat("{0} Authentication failed: bad cnonce counter", MsgId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cntable[cnonce] = nck;
|
cntable[nonce] = nck;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lock (cntable) cntable.Add(cnonce, nck);
|
lock (cntable) cntable.Add(nonce, nck);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -519,6 +527,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
sbuilder.Length = 0;
|
sbuilder.Length = 0;
|
||||||
|
|
||||||
|
if (scheme == null || scheme == Rest.AS_BASIC)
|
||||||
|
{
|
||||||
|
|
||||||
|
sbuilder.Append(Rest.AS_BASIC);
|
||||||
|
|
||||||
|
if (realm != null)
|
||||||
|
{
|
||||||
|
sbuilder.Append(" realm=\"");
|
||||||
|
sbuilder.Append(realm);
|
||||||
|
sbuilder.Append("\"");
|
||||||
|
}
|
||||||
|
AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
sbuilder.Length = 0;
|
||||||
|
|
||||||
if (scheme == null || scheme == Rest.AS_DIGEST)
|
if (scheme == null || scheme == Rest.AS_DIGEST)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -583,57 +607,135 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
sbuilder.Append(Rest.CS_COMMA);
|
sbuilder.Append(Rest.CS_COMMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Rest.Domains.Count != 0)
|
// We don;t know the userid that will be used
|
||||||
{
|
// so we cannot make any authentication domain
|
||||||
sbuilder.Append("domain=");
|
// assumptions. So the prefix will determine
|
||||||
sbuilder.Append(Rest.CS_DQUOTE);
|
// this.
|
||||||
foreach (string dom in Rest.Domains.Values)
|
|
||||||
{
|
|
||||||
sbuilder.Append(dom);
|
|
||||||
sbuilder.Append(Rest.CS_SPACE);
|
|
||||||
}
|
|
||||||
if (sbuilder[sbuilder.Length-1] == Rest.C_SPACE)
|
|
||||||
{
|
|
||||||
sbuilder.Length = sbuilder.Length-1;
|
|
||||||
}
|
|
||||||
sbuilder.Append(Rest.CS_DQUOTE);
|
|
||||||
sbuilder.Append(Rest.CS_COMMA);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sbuilder[sbuilder.Length-1] == Rest.C_COMMA)
|
sbuilder.Append("domain=");
|
||||||
{
|
sbuilder.Append(Rest.CS_DQUOTE);
|
||||||
sbuilder.Length = sbuilder.Length-1;
|
sbuilder.Append(qprefix);
|
||||||
}
|
sbuilder.Append(Rest.CS_DQUOTE);
|
||||||
|
|
||||||
AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString());
|
AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scheme == null || scheme == Rest.AS_BASIC)
|
|
||||||
{
|
|
||||||
|
|
||||||
sbuilder.Append(Rest.AS_BASIC);
|
|
||||||
|
|
||||||
if (realm != null)
|
|
||||||
{
|
|
||||||
sbuilder.Append(" realm=\"");
|
|
||||||
sbuilder.Append(realm);
|
|
||||||
sbuilder.Append("\"");
|
|
||||||
}
|
|
||||||
AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method provides validation in support of the BASIC
|
||||||
|
/// authentication method. This is not normaly expected to be
|
||||||
|
/// used, but is included for completeness (and because I tried
|
||||||
|
/// it first).
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
private bool Validate(string user, string pass)
|
private bool Validate(string user, string pass)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Validating {1}:{2}", MsgId, user, pass);
|
|
||||||
return user == "awebb" && pass == getPassword(user);
|
Rest.Log.DebugFormat("{0} Simple User Validation", MsgId);
|
||||||
|
|
||||||
|
// Both values are required
|
||||||
|
|
||||||
|
if (user == null || pass == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Eliminate any leading or trailing spaces
|
||||||
|
user = user.Trim();
|
||||||
|
|
||||||
|
return vetPassword(user, pass);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This mechanism is used by the digest authetnication mechanism
|
||||||
|
/// to return the user's password. In fact, because the OpenSim
|
||||||
|
/// user's passwords are already hashed, and the HTTP mechanism
|
||||||
|
/// does not supply an open password, the hashed passwords cannot
|
||||||
|
/// be used unless the cliemt has used the same salting mechanism
|
||||||
|
/// to has the password before using it in the authentication
|
||||||
|
/// algorithn. This is not inconceivable...
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
private string getPassword(string user)
|
private string getPassword(string user)
|
||||||
{
|
{
|
||||||
return Rest.GodKey;
|
|
||||||
|
int x;
|
||||||
|
string first;
|
||||||
|
string last;
|
||||||
|
|
||||||
|
// Distinguish the parts, if necessary
|
||||||
|
|
||||||
|
if ((x=user.IndexOf(Rest.C_SPACE)) != -1)
|
||||||
|
{
|
||||||
|
first = user.Substring(0,x);
|
||||||
|
last = user.Substring(x+1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
first = user;
|
||||||
|
last = String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProfileData udata = Rest.UserServices.GetUserProfile(first, last);
|
||||||
|
|
||||||
|
// If we don;t recognize the user id, perhaps it is god?
|
||||||
|
|
||||||
|
if (udata == null)
|
||||||
|
{
|
||||||
|
Rest.Log.DebugFormat("{0} Administrator", MsgId);
|
||||||
|
return Rest.GodKey;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Rest.Log.DebugFormat("{0} Normal User {1}", MsgId, user);
|
||||||
|
return udata.PasswordHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is used by the BASIC authentication scheme to calculate
|
||||||
|
/// the double hash used by OpenSim to encode user's passwords.
|
||||||
|
/// It returns true, if the supplied password is actually correct.
|
||||||
|
/// If the specified user-id is not recognized, but the password
|
||||||
|
/// matches the God password, then this is accepted as an admin
|
||||||
|
/// session.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
private bool vetPassword(string user, string pass)
|
||||||
|
{
|
||||||
|
|
||||||
|
int x;
|
||||||
|
string HA1;
|
||||||
|
string first;
|
||||||
|
string last;
|
||||||
|
|
||||||
|
// Distinguish the parts, if necessary
|
||||||
|
|
||||||
|
if ((x=user.IndexOf(Rest.C_SPACE)) != -1)
|
||||||
|
{
|
||||||
|
first = user.Substring(0,x);
|
||||||
|
last = user.Substring(x+1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
first = user;
|
||||||
|
last = String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
UserProfileData udata = Rest.UserServices.GetUserProfile(first, last);
|
||||||
|
|
||||||
|
// If we don;t recognize the user id, perhaps it is god?
|
||||||
|
|
||||||
|
if (udata == null)
|
||||||
|
return pass == Rest.GodKey;
|
||||||
|
|
||||||
|
HA1 = HashToString(pass);
|
||||||
|
HA1 = HashToString(String.Format("{0}:{1}",HA1,udata.PasswordSalt));
|
||||||
|
|
||||||
|
return (0 == sc.Compare(HA1, udata.PasswordHash));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the request-digest
|
// Validate the request-digest
|
||||||
|
@ -773,11 +875,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
if (reset)
|
if (reset)
|
||||||
{
|
{
|
||||||
buffer = null;
|
buffer = null;
|
||||||
body = null;
|
SendHtml(message);
|
||||||
|
body = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Rest.DEBUG)
|
if (Rest.DEBUG)
|
||||||
{
|
{
|
||||||
|
Rest.Log.DebugFormat("{0} Request Failure State Dump", MsgId);
|
||||||
Rest.Log.DebugFormat("{0} Scheme = {1}", MsgId, scheme);
|
Rest.Log.DebugFormat("{0} Scheme = {1}", MsgId, scheme);
|
||||||
Rest.Log.DebugFormat("{0} Realm = {1}", MsgId, realm);
|
Rest.Log.DebugFormat("{0} Realm = {1}", MsgId, realm);
|
||||||
Rest.Log.DebugFormat("{0} Domain = {1}", MsgId, domain);
|
Rest.Log.DebugFormat("{0} Domain = {1}", MsgId, domain);
|
||||||
|
@ -828,10 +932,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Generating Response", MsgId);
|
Rest.Log.DebugFormat("{0} Generating Response", MsgId);
|
||||||
|
Rest.Log.DebugFormat("{0} Method is {1}", MsgId, method);
|
||||||
// Process any arbitrary headers collected
|
|
||||||
|
|
||||||
BuildHeaders();
|
|
||||||
|
|
||||||
// A Head request can NOT have a body!
|
// A Head request can NOT have a body!
|
||||||
if (method != Rest.HEAD)
|
if (method != Rest.HEAD)
|
||||||
|
@ -839,6 +940,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Response is not abbreviated", MsgId);
|
Rest.Log.DebugFormat("{0} Response is not abbreviated", MsgId);
|
||||||
|
|
||||||
|
// If the writer is non-null then we know that an XML
|
||||||
|
// data component exists. Flush and close the writer and
|
||||||
|
// then convert the result to the expected buffer format
|
||||||
|
// unless the request has already been failed for some
|
||||||
|
// reason.
|
||||||
|
|
||||||
if (writer != null)
|
if (writer != null)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} XML Response handler extension ENTRY", MsgId);
|
Rest.Log.DebugFormat("{0} XML Response handler extension ENTRY", MsgId);
|
||||||
|
@ -869,41 +976,57 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OK, if the buffer contains something, regardless of how
|
||||||
|
// it got there, set various response headers accordingly.
|
||||||
|
|
||||||
if (buffer != null)
|
if (buffer != null)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Buffer-based entity", MsgId);
|
Rest.Log.DebugFormat("{0} Buffer-based entity", MsgId);
|
||||||
if (response.Headers.Get("Content-Encoding") == null)
|
|
||||||
response.ContentEncoding = encoding;
|
|
||||||
response.ContentLength64 = buffer.Length;
|
response.ContentLength64 = buffer.Length;
|
||||||
response.SendChunked = false;
|
|
||||||
response.KeepAlive = false;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.ContentLength64 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.Headers.Get("Content-Encoding") == null)
|
||||||
|
response.ContentEncoding = encoding;
|
||||||
|
|
||||||
|
response.SendChunked = chunked;
|
||||||
|
response.KeepAlive = keepAlive;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the status code & description. If nothing
|
// Set the status code & description. If nothing has been stored,
|
||||||
// has been stored, we consider that a success
|
// we consider that a success.
|
||||||
|
|
||||||
if (statusCode == 0)
|
if (statusCode == 0)
|
||||||
{
|
{
|
||||||
Complete();
|
Complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the response code in the actual carrier
|
||||||
|
|
||||||
response.StatusCode = statusCode;
|
response.StatusCode = statusCode;
|
||||||
|
|
||||||
if (response.StatusCode == (int)OSHttpStatusCode.RedirectMovedTemporarily ||
|
// For a redirect we need to set the relocation header accordingly
|
||||||
response.StatusCode == (int)OSHttpStatusCode.RedirectMovedPermanently)
|
|
||||||
|
if (response.StatusCode == (int) Rest.HttpStatusCodeTemporaryRedirect ||
|
||||||
|
response.StatusCode == (int) Rest.HttpStatusCodePermanentRedirect)
|
||||||
{
|
{
|
||||||
|
Rest.Log.DebugFormat("{0} Re-direct location is {1}", MsgId, redirectLocation);
|
||||||
response.RedirectLocation = redirectLocation;
|
response.RedirectLocation = redirectLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And include the status description if provided.
|
||||||
|
|
||||||
if (statusDescription != null)
|
if (statusDescription != null)
|
||||||
{
|
{
|
||||||
|
Rest.Log.DebugFormat("{0} Status description is {1}", MsgId, statusDescription);
|
||||||
response.StatusDescription = statusDescription;
|
response.StatusDescription = statusDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally we send back our response, consuming
|
// Finally we send back our response.
|
||||||
// any exceptions that doing so might produce.
|
|
||||||
|
|
||||||
// We've left the setting of handled' until the
|
// We've left the setting of handled' until the
|
||||||
// last minute because the header settings included
|
// last minute because the header settings included
|
||||||
|
@ -913,6 +1036,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
handled = true;
|
handled = true;
|
||||||
|
|
||||||
|
DumpHeaders();
|
||||||
|
|
||||||
|
// if (request.InputStream != null)
|
||||||
|
// {
|
||||||
|
// Rest.Log.DebugFormat("{0} Closing input stream", MsgId);
|
||||||
|
// request.InputStream.Close();
|
||||||
|
// }
|
||||||
|
|
||||||
if (buffer != null && buffer.Length != 0)
|
if (buffer != null && buffer.Length != 0)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Entity buffer, length = {1} : <{2}>",
|
Rest.Log.DebugFormat("{0} Entity buffer, length = {1} : <{2}>",
|
||||||
|
@ -920,12 +1051,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.OutputStream.Close();
|
// Closing the outputstream should complete the transmission process
|
||||||
|
|
||||||
if (request.InputStream != null)
|
Rest.Log.DebugFormat("{0} Closing output stream", MsgId);
|
||||||
{
|
response.OutputStream.Close();
|
||||||
request.InputStream.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,19 +1064,23 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a header to the table. If the header
|
// Add a header to the table. We need to allow
|
||||||
// already exists, it is replaced.
|
// multiple instances of many of the headers.
|
||||||
|
// If the
|
||||||
|
|
||||||
internal void AddHeader(string hdr, string data)
|
internal void AddHeader(string hdr, string data)
|
||||||
{
|
{
|
||||||
|
if (Rest.DEBUG)
|
||||||
if (headers == null)
|
|
||||||
{
|
{
|
||||||
headers = new NameValueCollection();
|
Rest.Log.DebugFormat("{0} Adding header: <{1}: {2}>",
|
||||||
|
MsgId, hdr, data);
|
||||||
|
if (response.Headers.Get(hdr) != null)
|
||||||
|
{
|
||||||
|
Rest.Log.DebugFormat("{0} Multipe {1} headers will be generated>",
|
||||||
|
MsgId, hdr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
response.Headers.Add(hdr, data);
|
||||||
headers[hdr] = data;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep explicit track of any headers which
|
// Keep explicit track of any headers which
|
||||||
|
@ -955,43 +1088,30 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
internal void RemoveHeader(string hdr)
|
internal void RemoveHeader(string hdr)
|
||||||
{
|
{
|
||||||
|
if (Rest.DEBUG)
|
||||||
if (removed_headers == null)
|
|
||||||
{
|
{
|
||||||
removed_headers = new List<string>();
|
Rest.Log.DebugFormat("{0} Removing header: <{1}>", MsgId, hdr);
|
||||||
}
|
if (response.Headers.Get(hdr) == null)
|
||||||
|
|
||||||
removed_headers.Add(hdr);
|
|
||||||
|
|
||||||
if (headers != null)
|
|
||||||
{
|
|
||||||
headers.Remove(hdr);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should it prove necessary, we could always
|
|
||||||
// restore the header collection from a cloned
|
|
||||||
// copy, but for now we'll assume that that is
|
|
||||||
// not necessary.
|
|
||||||
|
|
||||||
private void BuildHeaders()
|
|
||||||
{
|
|
||||||
if (removed_headers != null)
|
|
||||||
{
|
|
||||||
foreach (string h in removed_headers)
|
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Removing header: <{1}>", MsgId, h);
|
Rest.Log.DebugFormat("{0} No such header existed",
|
||||||
response.Headers.Remove(h);
|
MsgId, hdr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (headers!= null)
|
response.Headers.Remove(hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dump headers that will be generated in the response
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
internal void DumpHeaders()
|
||||||
|
{
|
||||||
|
if (Rest.DEBUG)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < headers.Count; i++)
|
for (int i=0;i<response.Headers.Count;i++)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Adding header: <{1}: {2}>",
|
Rest.Log.DebugFormat("{0} Header[{1}] : {2}", MsgId, i,
|
||||||
MsgId, headers.GetKey(i), headers.Get(i));
|
response.Headers.Get(i));
|
||||||
response.Headers.Add(headers.GetKey(i), headers.Get(i));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1019,7 +1139,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
path = uri.AbsolutePath;
|
path = uri.AbsolutePath;
|
||||||
if (path.EndsWith(Rest.UrlPathSeparator))
|
if (path.EndsWith(Rest.UrlPathSeparator))
|
||||||
path = path.Substring(0,path.Length-1);
|
path = path.Substring(0,path.Length-1);
|
||||||
path = Uri.UnescapeDataString(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we succeeded in getting a path, perform any
|
// If we succeeded in getting a path, perform any
|
||||||
|
@ -1040,6 +1159,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
pathNodes = EmptyPath;
|
pathNodes = EmptyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Elimiate any %-escaped values. This is left until here
|
||||||
|
// so that escaped "+' are not mistakenly replaced.
|
||||||
|
|
||||||
|
path = Uri.UnescapeDataString(path);
|
||||||
|
|
||||||
// Request server context info
|
// Request server context info
|
||||||
|
|
||||||
hostname = uri.Host;
|
hostname = uri.Host;
|
||||||
|
@ -1149,14 +1273,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
internal void initXmlReader()
|
internal void initXmlReader()
|
||||||
{
|
{
|
||||||
|
|
||||||
XmlReaderSettings settings = new XmlReaderSettings();
|
XmlReaderSettings settings = new XmlReaderSettings();
|
||||||
|
|
||||||
settings.ConformanceLevel = ConformanceLevel.Fragment;
|
settings.ConformanceLevel = ConformanceLevel.Fragment;
|
||||||
settings.IgnoreComments = true;
|
settings.IgnoreComments = true;
|
||||||
settings.IgnoreWhitespace = true;
|
settings.IgnoreWhitespace = true;
|
||||||
settings.IgnoreProcessingInstructions = true;
|
settings.IgnoreProcessingInstructions = true;
|
||||||
settings.ValidationType = ValidationType.None;
|
settings.ValidationType = ValidationType.None;
|
||||||
// reader = XmlReader.Create(new StringReader(entity),settings);
|
|
||||||
reader = XmlReader.Create(request.InputStream,settings);
|
reader = XmlReader.Create(request.InputStream,settings);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Flush()
|
private void Flush()
|
||||||
|
|
|
@ -69,7 +69,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
internal static bool ExtendedEscape = true;
|
internal static bool ExtendedEscape = true;
|
||||||
internal static bool DumpAsset = false;
|
internal static bool DumpAsset = false;
|
||||||
internal static string Realm = "REST";
|
internal static string Realm = "REST";
|
||||||
internal static Dictionary<string,string> Domains = new Dictionary<string,string>();
|
|
||||||
internal static int CreationDate = (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
|
internal static int CreationDate = (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||||
internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
|
internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
|
||||||
|
|
||||||
|
|
|
@ -44,35 +44,31 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
public class RestAssetServices : IRest
|
public class RestAssetServices : IRest
|
||||||
{
|
{
|
||||||
|
|
||||||
private string key = "assets";
|
|
||||||
private bool enabled = false;
|
private bool enabled = false;
|
||||||
private string qPrefix = "assets";
|
private string qPrefix = "assets";
|
||||||
|
|
||||||
// A simple constructor is used to handle any once-only
|
// A simple constructor is used to handle any once-only
|
||||||
// initialization of working classes.
|
// initialization of working classes.
|
||||||
|
|
||||||
public RestAssetServices(RestHandler p_rest)
|
public RestAssetServices()
|
||||||
{
|
{
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
|
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
|
||||||
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
||||||
|
|
||||||
// Integrate domain
|
// 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))
|
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
|
||||||
{
|
{
|
||||||
qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix;
|
qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication domain
|
// Register interface using the fully-qualified prefix
|
||||||
|
|
||||||
Rest.Domains.Add(key,Rest.Config.GetString("asset-domain",qPrefix));
|
|
||||||
|
|
||||||
// Register interface
|
|
||||||
|
|
||||||
Rest.Plugin.AddPathHandler(DoAsset, qPrefix, Allocate);
|
Rest.Plugin.AddPathHandler(DoAsset, qPrefix, Allocate);
|
||||||
|
|
||||||
// Activate
|
// Activate if all went OK
|
||||||
|
|
||||||
enabled = true;
|
enabled = true;
|
||||||
|
|
||||||
|
|
|
@ -38,10 +38,33 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
public class RestHandler : RestPlugin, IHttpAgentHandler
|
public class RestHandler : RestPlugin, IHttpAgentHandler
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
|
||||||
|
internal delegate void RestMethodHandler(RequestData rdata);
|
||||||
|
internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response);
|
||||||
|
|
||||||
|
// 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
|
#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>
|
/// <summary>
|
||||||
/// This static initializer scans the assembly for classes that
|
/// This static initializer scans the ASSEMBLY for classes that
|
||||||
/// export the IRest interface and builds a list of them. These
|
/// export the IRest interface and builds a list of them. These
|
||||||
/// are later activated by the handler. To add a new handler it
|
/// are later activated by the handler. To add a new handler it
|
||||||
/// is only necessary to create a new services class that implements
|
/// is only necessary to create a new services class that implements
|
||||||
|
@ -49,73 +72,78 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
/// all of the build-time flexibility of a modular approach
|
/// all of the build-time flexibility of a modular approach
|
||||||
/// while not introducing yet-another module loader. Note that
|
/// while not introducing yet-another module loader. Note that
|
||||||
/// multiple assembles can still be built, each with its own set
|
/// multiple assembles can still be built, each with its own set
|
||||||
/// of handlers.
|
/// of handlers. Examples of services classes are RestInventoryServices
|
||||||
|
/// and RestSkeleton.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
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[1];
|
|
||||||
private static Object[] args = new Object[1];
|
|
||||||
|
|
||||||
static RestHandler()
|
static RestHandler()
|
||||||
{
|
{
|
||||||
|
|
||||||
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
|
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
|
||||||
|
|
||||||
foreach (Module m in mods)
|
foreach (Module m in mods)
|
||||||
{
|
{
|
||||||
Type[] types = m.GetTypes();
|
Type[] types = m.GetTypes();
|
||||||
foreach (Type t in types)
|
foreach (Type t in types)
|
||||||
{
|
{
|
||||||
if (t.GetInterface("IRest") != null)
|
try
|
||||||
{
|
{
|
||||||
classes.Add(t);
|
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
|
#endregion local static state
|
||||||
|
|
||||||
#region local instance state
|
#region local instance state
|
||||||
|
|
||||||
/// <remarks>
|
|
||||||
/// The handler delegate is 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>
|
|
||||||
internal delegate void RestMethodHandler(RequestData rdata);
|
|
||||||
internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response);
|
|
||||||
|
|
||||||
// Handler tables: both stream and REST are supported
|
|
||||||
|
|
||||||
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>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This routine loads all of the handlers discovered during
|
/// This routine loads all of the handlers discovered during
|
||||||
/// instance initialization. Each handler is responsible for
|
/// instance initialization.
|
||||||
/// registering itself with this handler.
|
/// A table of all loaded and successfully constructed handlers
|
||||||
/// I was not able to make this code work in a constructor.
|
/// 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 islated within this method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
private void LoadHandlers()
|
private void LoadHandlers()
|
||||||
{
|
{
|
||||||
lock (handlers)
|
lock (handlers)
|
||||||
{
|
{
|
||||||
if (!handlersLoaded)
|
if (!handlersLoaded)
|
||||||
{
|
{
|
||||||
parms[0] = this.GetType();
|
|
||||||
args[0] = this;
|
|
||||||
|
|
||||||
ConstructorInfo ci;
|
ConstructorInfo ci;
|
||||||
Object ht;
|
Object ht;
|
||||||
|
|
||||||
foreach (Type t in classes)
|
foreach (Type t in classes)
|
||||||
{
|
{
|
||||||
ci = t.GetConstructor(parms);
|
try
|
||||||
ht = ci.Invoke(args);
|
{
|
||||||
handlers.Add((IRest)ht);
|
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;
|
handlersLoaded = true;
|
||||||
}
|
}
|
||||||
|
@ -126,14 +154,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
#region overriding properties
|
#region overriding properties
|
||||||
|
|
||||||
// Used to differentiate the message header.
|
// These properties override definitions
|
||||||
|
// in the base class.
|
||||||
|
|
||||||
|
// Name is used to differentiate the message header.
|
||||||
|
|
||||||
public override string Name
|
public override string Name
|
||||||
{
|
{
|
||||||
get { return "HANDLER"; }
|
get { return "HANDLER"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used to partition the configuration space.
|
// Used to partition the .ini configuration space.
|
||||||
|
|
||||||
public override string ConfigName
|
public override string ConfigName
|
||||||
{
|
{
|
||||||
|
@ -167,32 +198,32 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
/// Note that entries MUST be added to the active configuration files before
|
/// Note that entries MUST be added to the active configuration files before
|
||||||
/// the plugin can be enabled.
|
/// the plugin can be enabled.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
|
||||||
public override void Initialise(OpenSimBase openSim)
|
public override void Initialise(OpenSimBase openSim)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <remarks>
|
// This plugin will only be enabled if the broader
|
||||||
/// This plugin will only be enabled if the broader
|
// REST plugin mechanism is enabled.
|
||||||
/// REST plugin mechanism is enabled.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Plugin is initializing", MsgID);
|
Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId);
|
||||||
|
|
||||||
base.Initialise(openSim);
|
base.Initialise(openSim);
|
||||||
|
|
||||||
|
// IsEnabled is implemented by the base class and
|
||||||
|
// reflects an overall RestPlugin status
|
||||||
|
|
||||||
if (!IsEnabled)
|
if (!IsEnabled)
|
||||||
{
|
{
|
||||||
Rest.Log.WarnFormat("{0} Plugins are disabled", MsgID);
|
Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Plugin will be enabled", MsgID);
|
Rest.Log.InfoFormat("{0} Plugin will be enabled", MsgId);
|
||||||
|
|
||||||
/// <remarks>
|
// These are stored in static variables to make
|
||||||
/// These are stored in static variables to make
|
// them easy to reach from anywhere in the assembly.
|
||||||
/// them easy to reach from anywhere in the assembly.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
Rest.main = openSim;
|
Rest.main = openSim;
|
||||||
Rest.Plugin = this;
|
Rest.Plugin = this;
|
||||||
|
@ -223,6 +254,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
|
Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
|
||||||
(Rest.DumpAsset ? "" : "not "));
|
(Rest.DumpAsset ? "" : "not "));
|
||||||
|
|
||||||
|
// If data dumping is requested, report on the chosen line
|
||||||
|
// length.
|
||||||
|
|
||||||
if (Rest.DumpAsset)
|
if (Rest.DumpAsset)
|
||||||
{
|
{
|
||||||
Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId,
|
Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId,
|
||||||
|
@ -247,22 +281,24 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
LoadHandlers();
|
LoadHandlers();
|
||||||
|
|
||||||
/// <remarks>
|
// The intention of a post construction initializer
|
||||||
/// The intention of a post construction initializer
|
// is to allow for setup that is dependent upon other
|
||||||
/// is to allow for setup that is dependent upon other
|
// activities outside of the agency.
|
||||||
/// activities outside of the agency. We don't currently
|
|
||||||
/// have any, but the design allows for it.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
foreach (IRest handler in handlers)
|
foreach (IRest handler in handlers)
|
||||||
{
|
{
|
||||||
handler.Initialize();
|
try
|
||||||
|
{
|
||||||
|
handler.Initialize();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Rest.Log.ErrorFormat("{0} initialization error: {1}", MsgId, e.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
// Now that everything is setup we can proceed to
|
||||||
/// Now that everything is setup we can proceed and
|
// add THIS agent to the HTTP server's handler list
|
||||||
/// add this agent to the HTTP server's handler list
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
if (!AddAgentHandler(Rest.Name,this))
|
if (!AddAgentHandler(Rest.Name,this))
|
||||||
{
|
{
|
||||||
|
@ -276,7 +312,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgID, e.Message);
|
Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -290,10 +326,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
/// To make sure everything is copacetic we make sure the primary interface
|
/// To make sure everything is copacetic we make sure the primary interface
|
||||||
/// is disabled by deleting the handler from the HTTP server tables.
|
/// is disabled by deleting the handler from the HTTP server tables.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public override void Close()
|
public override void Close()
|
||||||
{
|
{
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgID);
|
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -313,45 +350,62 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
#region interface methods
|
#region interface methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is called by the server to match the client, it could
|
/// This method is called by the HTTP server to match an incoming
|
||||||
/// just return true if we only want one such handler. For now we
|
/// request. It scans all of the strings registered by the
|
||||||
/// match any explicitly specified client.
|
/// underlying handlers and looks for the best match. It returns
|
||||||
|
/// true if a match is found.
|
||||||
|
/// The matching process could be made arbitrarily complex.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public bool Match(OSHttpRequest request, OSHttpResponse response)
|
public bool Match(OSHttpRequest request, OSHttpResponse response)
|
||||||
{
|
{
|
||||||
string path = request.RawUrl;
|
string path = request.RawUrl;
|
||||||
foreach (string key in pathHandlers.Keys)
|
|
||||||
{
|
|
||||||
if (path.StartsWith(key))
|
|
||||||
{
|
|
||||||
return ( path.Length == key.Length ||
|
|
||||||
path.Substring(key.Length,1) == Rest.UrlPathSeparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path);
|
try
|
||||||
foreach (string key in streamHandlers.Keys)
|
|
||||||
{
|
{
|
||||||
if (path.StartsWith(key))
|
foreach (string key in pathHandlers.Keys)
|
||||||
{
|
{
|
||||||
return true;
|
if (path.StartsWith(key))
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (path.StartsWith(key))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Rest.Log.ErrorFormat("{0} matching exception for path <{1}> : {2}", MsgId, path, e.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// This is called by the HTTP server once the handler has indicated
|
||||||
|
/// that t is able to handle the request.
|
||||||
/// Preconditions:
|
/// Preconditions:
|
||||||
/// [1] request != null and is a valid request object
|
/// [1] request != null and is a valid request object
|
||||||
/// [2] response != null and is a valid response object
|
/// [2] response != null and is a valid response object
|
||||||
/// Behavior is undefined if preconditions are not satisfied.
|
/// Behavior is undefined if preconditions are not satisfied.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
public bool Handle(OSHttpRequest request, OSHttpResponse response)
|
public bool Handle(OSHttpRequest request, OSHttpResponse response)
|
||||||
{
|
{
|
||||||
bool handled;
|
bool handled;
|
||||||
base.MsgID = base.RequestID;
|
base.MsgID = base.RequestID;
|
||||||
|
|
||||||
|
// Debug only
|
||||||
|
|
||||||
if (Rest.DEBUG)
|
if (Rest.DEBUG)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} ENTRY", MsgId);
|
Rest.Log.DebugFormat("{0} ENTRY", MsgId);
|
||||||
|
@ -371,8 +425,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
handled = FindPathHandler(request, response) ||
|
handled = ( FindPathHandler(request, response) ||
|
||||||
FindStreamHandler(request, response);
|
FindStreamHandler(request, response) );
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -406,6 +460,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path);
|
Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path);
|
||||||
|
|
||||||
|
if (!IsEnabled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (string pattern in streamHandlers.Keys)
|
foreach (string pattern in streamHandlers.Keys)
|
||||||
{
|
{
|
||||||
if (path.StartsWith(pattern))
|
if (path.StartsWith(pattern))
|
||||||
|
@ -432,7 +491,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preserves the original handler's semantics
|
/// <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)
|
public void AddStreamHandler(string httpMethod, string path, RestMethod method)
|
||||||
{
|
{
|
||||||
|
@ -454,17 +519,26 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
if (!streamHandlers.ContainsKey(path))
|
if (!streamHandlers.ContainsKey(path))
|
||||||
{
|
{
|
||||||
streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method));
|
streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method));
|
||||||
Rest.Log.DebugFormat("{0} Added handler for {1}", MsgID, path);
|
Rest.Log.DebugFormat("{0} Added handler for {1}", MsgId, path);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgID, path);
|
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.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
|
internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
|
||||||
{
|
{
|
||||||
|
|
||||||
RequestData rdata = null;
|
RequestData rdata = null;
|
||||||
|
@ -516,8 +590,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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>
|
||||||
|
|
||||||
internal void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
|
internal void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!IsEnabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (pathHandlers.ContainsKey(path))
|
if (pathHandlers.ContainsKey(path))
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path);
|
Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path);
|
||||||
|
@ -537,4 +622,5 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using OpenJPEGNet;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Servers;
|
using OpenSim.Framework.Servers;
|
||||||
using OpenSim.Framework.Communications;
|
using OpenSim.Framework.Communications;
|
||||||
|
@ -44,35 +45,33 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
public class RestInventoryServices : IRest
|
public class RestInventoryServices : IRest
|
||||||
{
|
{
|
||||||
|
|
||||||
private string key = "inventory";
|
|
||||||
private bool enabled = false;
|
private bool enabled = false;
|
||||||
private string qPrefix = "inventory";
|
private string qPrefix = "inventory";
|
||||||
|
|
||||||
// A simple constructor is used to handle any once-only
|
/// <summary>
|
||||||
// initialization of working classes.
|
/// A simple constructor is used to handle any once-only
|
||||||
|
/// initialization of working classes.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
public RestInventoryServices(RestHandler p_rest)
|
public RestInventoryServices()
|
||||||
{
|
{
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId);
|
Rest.Log.InfoFormat("{0} Inventory services initializing", MsgId);
|
||||||
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
||||||
|
|
||||||
// Update to reflect the full prefix if not absolute
|
// 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))
|
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
|
||||||
{
|
{
|
||||||
qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix;
|
qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication domain
|
// Register interface using the absolute URI.
|
||||||
|
|
||||||
Rest.Domains.Add(key, Rest.Config.GetString("inventory-domain",qPrefix));
|
|
||||||
|
|
||||||
// Register interface
|
|
||||||
|
|
||||||
Rest.Plugin.AddPathHandler(DoInventory,qPrefix,Allocate);
|
Rest.Plugin.AddPathHandler(DoInventory,qPrefix,Allocate);
|
||||||
|
|
||||||
// Activate
|
// Activate if everything went OK
|
||||||
|
|
||||||
enabled = true;
|
enabled = true;
|
||||||
|
|
||||||
|
@ -80,16 +79,20 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post-construction, pre-enabled initialization opportunity
|
/// <summary>
|
||||||
// Not currently exploited.
|
/// Post-construction, pre-enabled initialization opportunity
|
||||||
|
/// Not currently exploited.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by the plug-in to halt REST processing. Local processing is
|
/// <summary>
|
||||||
// disabled, and control blocks until all current processing has
|
/// Called by the plug-in to halt REST processing. Local processing is
|
||||||
// completed. No new processing will be started
|
/// disabled, and control blocks until all current processing has
|
||||||
|
/// completed. No new processing will be started
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
|
@ -97,7 +100,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.InfoFormat("{0} Inventory services closing down", MsgId);
|
Rest.Log.InfoFormat("{0} Inventory services closing down", MsgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenient properties
|
/// <summary>
|
||||||
|
/// This property is declared locally because it is used a lot and
|
||||||
|
/// brevity is nice.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
internal string MsgId
|
internal string MsgId
|
||||||
{
|
{
|
||||||
|
@ -106,15 +112,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
#region Interface
|
#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>
|
||||||
|
|
||||||
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response)
|
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response)
|
||||||
{
|
{
|
||||||
return (RequestData) new InventoryRequestData(request, response, qPrefix);
|
return (RequestData) new InventoryRequestData(request, response, qPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is registered with the handler when this class is
|
/// This method is registered with the handler when this service provider
|
||||||
/// initialized. It is called whenever the URI includes this handler's
|
/// is initialized. It is called whenever the plug-in identifies this service
|
||||||
/// prefix string.
|
/// provider as the best match.
|
||||||
/// It handles all aspects of inventory REST processing.
|
/// It handles all aspects of inventory REST processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
|
@ -125,7 +138,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId);
|
Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId);
|
||||||
|
|
||||||
// We're disabled
|
// If we're disabled, do nothing.
|
||||||
|
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -169,23 +183,32 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
|
Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
|
||||||
|
|
||||||
// We can only get here if we're authorized
|
/// <remarks>
|
||||||
//
|
/// We can only get here if we are authorized
|
||||||
// The requestor may have specified an LLUUID or
|
///
|
||||||
// a conjoined FirstNameLastName string. We'll
|
/// The requestor may have specified an LLUUID or
|
||||||
// try both. If we fail with the first, UUID,
|
/// a conjoined FirstName LastName string. We'll
|
||||||
// attempt, then we need two nodes to construct
|
/// try both. If we fail with the first, UUID,
|
||||||
// a valid avatar name.
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
|
||||||
// Do we have at least a user agent name?
|
// Do we have at least a user agent name?
|
||||||
|
|
||||||
if (rdata.parameters.Length < 1)
|
if (rdata.parameters.Length < 1)
|
||||||
{
|
{
|
||||||
Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId);
|
Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId);
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest);
|
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest+": No user identity specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The next parameter MUST be the agent identification, either an LLUUID
|
// The first parameter MUST be the agent identification, either an LLUUID
|
||||||
// or a space-separated First-name Last-Name specification.
|
// or a space-separated First-name Last-Name specification.
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -205,10 +228,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
|
Rest.Log.DebugFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest);
|
rdata.Fail(Rest.HttpStatusCodeBadRequest, Rest.HttpStatusDescBadRequest+": invalid user identity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the user rpofile is null then either the server is broken, or the
|
||||||
|
// user is not known. We always assume the latter case.
|
||||||
|
|
||||||
if (rdata.userProfile != null)
|
if (rdata.userProfile != null)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}",
|
Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}",
|
||||||
|
@ -217,11 +243,20 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} No profile for {1}", MsgId, rdata.path);
|
Rest.Log.DebugFormat("{0} No profile for {1}", MsgId, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound);
|
rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": unrecognized user identity");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get to here, then we have successfully obtained an inventory
|
// If we get to here, then we have effectively validated the user's
|
||||||
// for the specified user.
|
// identity. Now we need to get the inventory. If the server does not
|
||||||
|
// have the inventory, we reject the request with an appropriate explanation.
|
||||||
|
//
|
||||||
|
// Note that inventory retrieval is an asynchronous event, we use the rdata
|
||||||
|
// class instance as the basis for our synchronization.
|
||||||
|
//
|
||||||
|
// TODO
|
||||||
|
// If something went wrong in inventory processing the thread could stall here
|
||||||
|
// indefinitely. There should be a watchdog timer to fail the request if the
|
||||||
|
// response is not recieved in a timely fashion.
|
||||||
|
|
||||||
rdata.uuid = rdata.userProfile.ID;
|
rdata.uuid = rdata.userProfile.ID;
|
||||||
|
|
||||||
|
@ -250,7 +285,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Inventory is not available [1] for agent {1} {2}",
|
Rest.Log.DebugFormat("{0} Inventory is not available [1] for agent {1} {2}",
|
||||||
MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
|
MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
|
||||||
rdata.Fail(Rest.HttpStatusCodeServerError,Rest.HttpStatusDescServerError);
|
rdata.Fail(Rest.HttpStatusCodeServerError,Rest.HttpStatusDescServerError+": inventory retrieval failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -258,7 +293,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} Inventory is not available for agent [3] {1} {2}",
|
Rest.Log.DebugFormat("{0} Inventory is not available for agent [3] {1} {2}",
|
||||||
MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
|
MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound);
|
rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": no inventory for user");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, then we have successfully retrieved the user's information
|
// If we get here, then we have successfully retrieved the user's information
|
||||||
|
@ -292,7 +327,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} Method {1} not supported for {2}",
|
Rest.Log.DebugFormat("{0} Method {1} not supported for {2}",
|
||||||
MsgId, rdata.method, rdata.path);
|
MsgId, rdata.method, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
|
rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
|
||||||
Rest.HttpStatusDescMethodNotAllowed);
|
Rest.HttpStatusDescMethodNotAllowed+": "+rdata.method+" not supported");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,10 +350,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty);
|
rdata.writer.WriteStartElement(String.Empty,"Inventory",String.Empty);
|
||||||
|
|
||||||
|
// If there was only one parameter, then the entire
|
||||||
|
// inventory is being requested.
|
||||||
|
|
||||||
if (rdata.parameters.Length == 1)
|
if (rdata.parameters.Length == 1)
|
||||||
{
|
{
|
||||||
formatInventory(rdata, rdata.root, String.Empty);
|
formatInventory(rdata, rdata.root, String.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are additional parameters, then these represent
|
||||||
|
// a path relative to the root of the inventory. This path
|
||||||
|
// must be traversed before we format the sub-tree thus
|
||||||
|
// identified.
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
traverseInventory(rdata, rdata.root, 1);
|
traverseInventory(rdata, rdata.root, 1);
|
||||||
|
@ -332,33 +376,35 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// In the case of the inventory, and probably much else
|
/// In the case of the inventory, and probably in general,
|
||||||
/// the distinction between PUT and POST is not always
|
/// the distinction between PUT and POST is not always
|
||||||
/// easy to discern. Adding a directory can be viewed as
|
/// easy to discern. Adding a directory can be viewed as
|
||||||
/// an addition, or as a modification to the inventory as
|
/// an addition, or as a modification to the inventory as
|
||||||
/// a whole.
|
/// a whole. This is exacerbated by a lack of consistency
|
||||||
|
/// across different implementations.
|
||||||
///
|
///
|
||||||
/// The best distinction may be the relationship between
|
/// For OpenSim POST is an update and PUT is an addition.
|
||||||
/// the entity and the URI. If we view POST as an update,
|
///
|
||||||
/// then the enity represents a replacement for the
|
/// The best way to exaplain the distinction is to
|
||||||
/// element named by the URI. If the operation is PUT,
|
/// consider the relationship between the URI and the
|
||||||
/// then the URI describes the context into which the
|
/// entity in question. For POST, the URI identifies the
|
||||||
/// entity will be added.
|
/// entity to be modified or replaced.
|
||||||
|
/// If the operation is PUT,then the URI describes the
|
||||||
|
/// context into which the new entity will be added.
|
||||||
///
|
///
|
||||||
/// As an example, suppose the URI contains:
|
/// As an example, suppose the URI contains:
|
||||||
/// /admin/inventory/Clothing
|
/// /admin/inventory/Clothing
|
||||||
/// Suppose the entity represents a Folder, called
|
|
||||||
/// "Clothes".
|
|
||||||
///
|
///
|
||||||
/// A POST request will result in the replacement of
|
/// A POST request will result in some modification of
|
||||||
/// "Clothing" by "Clothes". Whereas a PUT request
|
/// the folder or item named "Clothing". Whereas a PUT
|
||||||
/// would add Clothes as a sub-directory of Clothing.
|
/// request will add some new information into the
|
||||||
///
|
/// content identified by Clothing. It follows from this
|
||||||
/// This is the model followed by this implementation.
|
/// that for PUT, the element identified by the URI must
|
||||||
|
/// be a folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PUT adds new information to the inventory at the
|
/// PUT adds new information to the inventory in the
|
||||||
/// context identified by the URI.
|
/// context identified by the URI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
||||||
|
@ -376,7 +422,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
// exception.
|
// exception.
|
||||||
|
|
||||||
// It follows that we can only add information if the URI
|
// It follows that we can only add information if the URI
|
||||||
// has identified a folder. So only folder is supported
|
// has identified a folder. So only a type of folder is supported
|
||||||
// in this case.
|
// in this case.
|
||||||
|
|
||||||
if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
|
if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
|
||||||
|
@ -390,14 +436,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}",
|
Rest.Log.DebugFormat("{0} {1}: Resource(s) will be added to folder {2}",
|
||||||
MsgId, rdata.method, rdata.path);
|
MsgId, rdata.method, rdata.path);
|
||||||
|
|
||||||
// Reconstitute inventory sub-tree from the XML supplied in the entity.
|
// Reconstitute the inventory sub-tree from the XML supplied in the entity.
|
||||||
// This is a stand-alone inventory subtree, not yet integrated into the
|
// The result is a stand-alone inventory subtree, not yet integrated into the
|
||||||
// existing tree.
|
// existing tree. An inventory collection consists of three components:
|
||||||
|
// [1] A (possibly empty) set of folders.
|
||||||
|
// [2] A (possibly empty) set of items.
|
||||||
|
// [3] A (possibly empty) set of assets.
|
||||||
|
// If all of these are empty, then the PUT is a harmless no-operation.
|
||||||
|
|
||||||
XmlInventoryCollection entity = ReconstituteEntity(rdata);
|
XmlInventoryCollection entity = ReconstituteEntity(rdata);
|
||||||
|
|
||||||
// Inlined assest included in entity. If anything fails,
|
// Inlined assets can be included in entity. These must be incorporated into
|
||||||
// return failure to requestor.
|
// the asset database before we attempt to update the inventory. If anything
|
||||||
|
// fails, return a failure to requestor.
|
||||||
|
|
||||||
if (entity.Assets.Count > 0)
|
if (entity.Assets.Count > 0)
|
||||||
{
|
{
|
||||||
|
@ -410,10 +461,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}",
|
Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}",
|
||||||
MsgId, asset.ID, asset.Type, asset.Name);
|
MsgId, asset.ID, asset.Type, asset.Name);
|
||||||
Rest.AssetServices.AddAsset(asset);
|
Rest.AssetServices.AddAsset(asset);
|
||||||
|
|
||||||
if (Rest.DumpAsset)
|
if (Rest.DumpAsset)
|
||||||
{
|
{
|
||||||
Rest.Dump(asset.Data);
|
Rest.Dump(asset.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -424,11 +477,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
foreach (InventoryFolderBase folder in entity.Folders)
|
foreach (InventoryFolderBase folder in entity.Folders)
|
||||||
{
|
{
|
||||||
|
|
||||||
InventoryFolderBase found = null;
|
InventoryFolderBase found;
|
||||||
|
|
||||||
// If the parentID is zero, then this is going
|
// If the parentID is zero, then this folder is going
|
||||||
// into the root identified by the URI. The requestor
|
// into the root folder identified by the URI. The requestor
|
||||||
// may have already set the parent ID correctly, in which
|
// may have already set the parent ID explicitly, in which
|
||||||
// case we don't have to do it here.
|
// case we don't have to do it here.
|
||||||
|
|
||||||
if (folder.ParentID == LLUUID.Zero)
|
if (folder.ParentID == LLUUID.Zero)
|
||||||
|
@ -437,7 +490,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search the existing inventory for an existing entry. If
|
// Search the existing inventory for an existing entry. If
|
||||||
// we have once, we need to decide if it has really changed.
|
// we have one, we need to decide if it has really changed.
|
||||||
// It could just be present as (unnecessary) context, and we
|
// It could just be present as (unnecessary) context, and we
|
||||||
// don't want to waste time updating the database in that
|
// don't want to waste time updating the database in that
|
||||||
// case, OR, it could be being moved from another location
|
// case, OR, it could be being moved from another location
|
||||||
|
@ -451,6 +504,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
if (xf.ID == folder.ID)
|
if (xf.ID == folder.ID)
|
||||||
{
|
{
|
||||||
found = xf;
|
found = xf;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,6 +546,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
if (xi.ID == item.ID)
|
if (xi.ID == item.ID)
|
||||||
{
|
{
|
||||||
found = xi;
|
found = xi;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,7 +571,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}",
|
Rest.Log.DebugFormat("{0} {1}: Resource {2} is not a valid context: {3}",
|
||||||
MsgId, rdata.method, rdata.path, InventoryNode.GetType());
|
MsgId, rdata.method, rdata.path, InventoryNode.GetType());
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": invalid resource context");
|
||||||
}
|
}
|
||||||
|
|
||||||
rdata.Complete();
|
rdata.Complete();
|
||||||
|
@ -531,7 +586,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
/// [1] It identifies the user whose inventory is to be
|
/// [1] It identifies the user whose inventory is to be
|
||||||
/// processed.
|
/// processed.
|
||||||
/// [2] It optionally specifies a subtree of the inventory
|
/// [2] It optionally specifies a subtree of the inventory
|
||||||
/// that is to be used to resolve an relative subtree
|
/// that is to be used to resolve any relative subtree
|
||||||
/// specifications in the entity. If nothing is specified
|
/// specifications in the entity. If nothing is specified
|
||||||
/// then the whole inventory is implied.
|
/// then the whole inventory is implied.
|
||||||
/// Please note that the subtree specified by the URI is only relevant
|
/// Please note that the subtree specified by the URI is only relevant
|
||||||
|
@ -540,7 +595,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
/// elements will be implicitly referenced within the context identified
|
/// elements will be implicitly referenced within the context identified
|
||||||
/// by the URI.
|
/// by the URI.
|
||||||
/// If an element in the entity specifies an explicit parent folder, then
|
/// If an element in the entity specifies an explicit parent folder, then
|
||||||
/// that parent is effective, regardless of nay value specified in the
|
/// that parent is effective, regardless of any value specified in the
|
||||||
/// URI. If the parent does not exist, then the element, and any dependent
|
/// URI. If the parent does not exist, then the element, and any dependent
|
||||||
/// elements, are ignored. This case is actually detected and handled
|
/// elements, are ignored. This case is actually detected and handled
|
||||||
/// during the reconstitution process.
|
/// during the reconstitution process.
|
||||||
|
@ -555,33 +610,54 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
Object InventoryNode = getInventoryNode(rdata, rdata.root, 1);
|
Object InventoryNode = getInventoryNode(rdata, rdata.root, 1);
|
||||||
|
|
||||||
// As long as we have a context, then we have something
|
// As long as we have a node, then we have something
|
||||||
// meaningful to do, unlike PUT. So reconstitute the
|
// meaningful to do, unlike PUT. So we reconstitute the
|
||||||
// subtree before doing anything else. Note that we
|
// subtree before doing anything else. Note that we
|
||||||
// etiher got a context or we threw an exception.
|
// etiher got a valid node or we threw an exception.
|
||||||
|
|
||||||
XmlInventoryCollection entity = ReconstituteEntity(rdata);
|
XmlInventoryCollection entity = ReconstituteEntity(rdata);
|
||||||
|
|
||||||
// Incorporate any inlined assets first
|
// Incorporate any inlined assets first. Any failures
|
||||||
|
// will terminate the request.
|
||||||
|
|
||||||
if (entity.Assets.Count != 0)
|
if (entity.Assets.Count > 0)
|
||||||
{
|
{
|
||||||
|
Rest.Log.DebugFormat("{0} Adding {1} assets to server",
|
||||||
|
MsgId, entity.Assets.Count);
|
||||||
|
|
||||||
foreach (AssetBase asset in entity.Assets)
|
foreach (AssetBase asset in entity.Assets)
|
||||||
{
|
{
|
||||||
// Asset was validated during the collection
|
Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}",
|
||||||
// process
|
MsgId, asset.ID, asset.Type, asset.Name);
|
||||||
|
|
||||||
|
// The asset was validated during the collection process
|
||||||
|
|
||||||
Rest.AssetServices.AddAsset(asset);
|
Rest.AssetServices.AddAsset(asset);
|
||||||
|
|
||||||
|
if (Rest.DumpAsset)
|
||||||
|
{
|
||||||
|
Rest.Dump(asset.Data);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// URI specifies a folder to be updated.
|
/// The URI specifies either a folder or an item to be updated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The root node in the entity must have the same
|
/// The root node in the entity will replace the node identified
|
||||||
/// UUID as the node identified by the URI. The
|
/// by the URI. This means the parent will remain the same, but
|
||||||
/// parentID if different indicates that the updated
|
/// any or all attributes associated with the named element
|
||||||
/// folder is actually being moved too.
|
/// will change.
|
||||||
|
///
|
||||||
|
/// If the inventory collection contains an element with a zero
|
||||||
|
/// parent ID, then this is taken to be the replacement for the
|
||||||
|
/// named node. The collection MAY also specify an explicit
|
||||||
|
/// parent ID, in this case it MAY identify the same parent as
|
||||||
|
/// the current node, or it MAY specify a different parent,
|
||||||
|
/// indicating that the folder is being moved in addition to any
|
||||||
|
/// other modifications being made.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
|
||||||
if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
|
if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
|
||||||
|
@ -592,7 +668,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
InventoryFolderBase xml = null;
|
InventoryFolderBase xml = null;
|
||||||
|
|
||||||
// Scan the set of folders in the entity collection for an
|
// Scan the set of folders in the entity collection for an
|
||||||
// entry that macthes the context folder. It is assumed that
|
// entry that matches the context folder. It is assumed that
|
||||||
// the only reliable indicator of this is a zero UUID ( using
|
// the only reliable indicator of this is a zero UUID ( using
|
||||||
// implicit context), or the parent's UUID matches that of the
|
// implicit context), or the parent's UUID matches that of the
|
||||||
// URI designated node (explicit context). We don't allow
|
// URI designated node (explicit context). We don't allow
|
||||||
|
@ -617,14 +693,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// More than one entry is ambiguous
|
// More than one entry is ambiguous. Other folders should be
|
||||||
|
// added using the PUT verb.
|
||||||
|
|
||||||
if (count > 1)
|
if (count > 1)
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous",
|
Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous",
|
||||||
MsgId, rdata.method, rdata.path);
|
MsgId, rdata.method, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": context is ambiguous");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exactly one entry means we ARE replacing the node
|
// Exactly one entry means we ARE replacing the node
|
||||||
|
@ -679,7 +756,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>",
|
Rest.Log.DebugFormat("{0} {1}: Request should not contain any folders <{2}>",
|
||||||
MsgId, rdata.method, rdata.path);
|
MsgId, rdata.method, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": folder is not allowed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.Items.Count > 1)
|
if (entity.Items.Count > 1)
|
||||||
|
@ -687,7 +764,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>",
|
Rest.Log.DebugFormat("{0} {1}: Entity contains too many items <{2}>",
|
||||||
MsgId, rdata.method, rdata.path);
|
MsgId, rdata.method, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": too may items");
|
||||||
}
|
}
|
||||||
|
|
||||||
xml = entity.Items[0];
|
xml = entity.Items[0];
|
||||||
|
@ -854,7 +931,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous",
|
Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous",
|
||||||
MsgId, rdata.method, rdata.path);
|
MsgId, rdata.method, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound);
|
rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound+": request is ambiguous");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -863,7 +940,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} {1}: Resource {2} not found",
|
Rest.Log.DebugFormat("{0} {1}: Resource {2} not found",
|
||||||
MsgId, rdata.method, rdata.path);
|
MsgId, rdata.method, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound);
|
rdata.Fail(Rest.HttpStatusCodeNotFound, Rest.HttpStatusDescNotFound+": resource "+rdata.path+" not found");
|
||||||
|
|
||||||
return null; /* Never reached */
|
return null; /* Never reached */
|
||||||
|
|
||||||
|
@ -931,7 +1008,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>",
|
Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>",
|
||||||
MsgId, rdata.path);
|
MsgId, rdata.path);
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound);
|
rdata.Fail(Rest.HttpStatusCodeNotFound,Rest.HttpStatusDescNotFound+": no such item/folder");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,6 +1138,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
TrashCan.Version = 1;
|
TrashCan.Version = 1;
|
||||||
TrashCan.Type = (short) AssetType.TrashFolder;
|
TrashCan.Type = (short) AssetType.TrashFolder;
|
||||||
TrashCan.ParentID = f.ID;
|
TrashCan.ParentID = f.ID;
|
||||||
|
TrashCan.Owner = f.Owner;
|
||||||
Rest.InventoryServices.AddFolder(TrashCan);
|
Rest.InventoryServices.AddFolder(TrashCan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1070,7 +1148,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} No Trash Can available", MsgId);
|
Rest.Log.DebugFormat("{0} No Trash Can available", MsgId);
|
||||||
rdata.Fail(Rest.HttpStatusCodeServerError,
|
rdata.Fail(Rest.HttpStatusCodeServerError,
|
||||||
Rest.HttpStatusDescServerError);
|
Rest.HttpStatusDescServerError+": unable to create trash can");
|
||||||
}
|
}
|
||||||
|
|
||||||
return TrashCan;
|
return TrashCan;
|
||||||
|
@ -1313,7 +1391,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}",
|
Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}",
|
||||||
MsgId, ic.xml.Name, ic.xml.Value);
|
MsgId, ic.xml.Name, ic.xml.Value);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": unrecognized attribute");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1349,7 +1427,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}",
|
Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}",
|
||||||
MsgId, ic.Item.Folder, result.ID);
|
MsgId, ic.Item.Folder, result.ID);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": invalid parent");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1457,7 +1535,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}",
|
Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}",
|
||||||
MsgId, ic.xml.Name, ic.xml.Value);
|
MsgId, ic.xml.Name, ic.xml.Value);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": unrecognized attribute");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1570,7 +1648,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId);
|
Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": no context for asset");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1691,7 +1769,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId);
|
Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": request parse error");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every item is required to have a name (via REST anyway)
|
// Every item is required to have a name (via REST anyway)
|
||||||
|
@ -1700,7 +1778,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
{
|
{
|
||||||
Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId);
|
Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": item name required");
|
||||||
}
|
}
|
||||||
|
|
||||||
// An item MUST have an asset ID. AssetID should never be zero
|
// An item MUST have an asset ID. AssetID should never be zero
|
||||||
|
@ -1713,7 +1791,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId);
|
Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId);
|
||||||
Rest.Log.InfoFormat("{0} Asset information is missing", MsgId);
|
Rest.Log.InfoFormat("{0} Asset information is missing", MsgId);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": asset information required");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1751,7 +1829,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}",
|
Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}",
|
||||||
MsgId, ic.Item.Folder, ic.Item.ID);
|
MsgId, ic.Item.Folder, ic.Item.ID);
|
||||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||||
Rest.HttpStatusDescBadRequest);
|
Rest.HttpStatusDescBadRequest+": parent information required");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1825,6 +1903,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
if (ic.Item.InvType == (int) AssetType.Unknown)
|
if (ic.Item.InvType == (int) AssetType.Unknown)
|
||||||
ic.Item.InvType = (int) AssetType.ImageJPEG;
|
ic.Item.InvType = (int) AssetType.ImageJPEG;
|
||||||
break;
|
break;
|
||||||
|
case "tga" :
|
||||||
|
if (parts[parts.Length - 2].IndexOf("_texture") != -1)
|
||||||
|
{
|
||||||
|
if (ic.Item.AssetType == (int) AssetType.Unknown)
|
||||||
|
ic.Item.AssetType = (int) AssetType.TextureTGA;
|
||||||
|
if (ic.Item.InvType == (int) AssetType.Unknown)
|
||||||
|
ic.Item.InvType = (int) AssetType.TextureTGA;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ic.Item.AssetType == (int) AssetType.Unknown)
|
||||||
|
ic.Item.AssetType = (int) AssetType.ImageTGA;
|
||||||
|
if (ic.Item.InvType == (int) AssetType.Unknown)
|
||||||
|
ic.Item.InvType = (int) AssetType.ImageTGA;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default :
|
default :
|
||||||
Rest.Log.DebugFormat("{0} Type was not inferred", MsgId);
|
Rest.Log.DebugFormat("{0} Type was not inferred", MsgId);
|
||||||
break;
|
break;
|
||||||
|
@ -1832,6 +1926,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this is a TGA remember the fact
|
||||||
|
|
||||||
|
if (ic.Item.AssetType == (int) AssetType.TextureTGA ||
|
||||||
|
ic.Item.AssetType == (int) AssetType.ImageTGA)
|
||||||
|
{
|
||||||
|
// TODO: DO we need to convert it? Or is it enough to flag
|
||||||
|
// it appropriately?
|
||||||
|
}
|
||||||
|
|
||||||
ic.reset();
|
ic.reset();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,18 +171,20 @@ namespace OpenSim.Framework.Servers
|
||||||
OSHttpRequest request = new OSHttpRequest(context.Request);
|
OSHttpRequest request = new OSHttpRequest(context.Request);
|
||||||
OSHttpResponse response = new OSHttpResponse(context.Response);
|
OSHttpResponse response = new OSHttpResponse(context.Response);
|
||||||
|
|
||||||
// user agent based requests? not sure where this actually gets used from
|
// This is the REST agent interface. We require an agent to properly identify
|
||||||
if (request.UserAgent != null)
|
// itself. If the REST handler recognizes the prefix it will attempt to
|
||||||
{
|
// satisfy the request. If it is not recognizable, and no damage has occurred
|
||||||
IHttpAgentHandler agentHandler;
|
// the request can be passed through to the other handlers. This is a low
|
||||||
|
// probability event; if a request is matched it is normally expected to be
|
||||||
|
// handled
|
||||||
|
|
||||||
if (TryGetAgentHandler(request, response, out agentHandler))
|
IHttpAgentHandler agentHandler;
|
||||||
|
|
||||||
|
if (TryGetAgentHandler(request, response, out agentHandler))
|
||||||
|
{
|
||||||
|
if (HandleAgentRequest(agentHandler, request, response))
|
||||||
{
|
{
|
||||||
if (HandleAgentRequest(agentHandler, request, response))
|
return;
|
||||||
{
|
|
||||||
m_log.DebugFormat("[HTTP-AGENT] Handler located for {0}", request.UserAgent);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,7 +510,7 @@ namespace OpenSim.Framework.Servers
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// If the handler did in fact close the stream, then this will blow
|
// If the handler did in fact close the stream, then this will blow
|
||||||
// chunks, so that that doesn;t disturb anybody we throw away any
|
// chunks. So that that doesn't disturb anybody we throw away any
|
||||||
// and all exceptions raised. We've done our best to release the
|
// and all exceptions raised. We've done our best to release the
|
||||||
// client.
|
// client.
|
||||||
try
|
try
|
||||||
|
@ -524,7 +526,10 @@ namespace OpenSim.Framework.Servers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indicate that the request has been "handled"
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
|
public void HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
|
||||||
|
|
Loading…
Reference in New Issue