parent
19ad7db5e1
commit
7025a8040e
|
@ -34,8 +34,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
/// <summary>
|
||||
/// This interface represents the boundary between the general purpose
|
||||
/// REST plugin handling, and the functionally specific handlers. The
|
||||
/// handler knows only to initialzie and terminate all such handlers
|
||||
/// that it finds.
|
||||
/// handler knows only to initialize and terminate all such handlers
|
||||
/// that it finds. Implementing this interface identifies the class as
|
||||
/// a REST handler implementation.
|
||||
/// </summary>
|
||||
|
||||
internal interface IRest
|
||||
|
|
|
@ -34,6 +34,7 @@ using System.Security.Cryptography;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using libsecondlife;
|
||||
using System.Xml;
|
||||
|
@ -50,7 +51,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
/// This structure is created on entry to the Handler
|
||||
/// method and is disposed of upon return. It is part of
|
||||
/// 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
|
||||
/// object is instantiated, and may be extended by, any
|
||||
/// given handler. See the inventory handler for an example
|
||||
|
@ -71,11 +72,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
internal OSHttpRequest request = null;
|
||||
internal OSHttpResponse response = null;
|
||||
internal string qprefix = null;
|
||||
|
||||
// Request lifetime values
|
||||
|
||||
internal NameValueCollection headers = null;
|
||||
internal List<string> removed_headers = null;
|
||||
internal byte[] buffer = null;
|
||||
internal string body = null;
|
||||
internal string html = null;
|
||||
|
@ -96,11 +96,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
internal string hostname = "localhost";
|
||||
internal int port = 80;
|
||||
internal string prefix = Rest.UrlPathSeparator;
|
||||
internal bool keepAlive = false;
|
||||
internal bool chunked = false;
|
||||
|
||||
// Authentication related state
|
||||
|
||||
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 domain = 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*",
|
||||
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);
|
||||
|
||||
private static Regex digestParm2 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*(?<pval>[^\\p{P}\\s]+)",
|
||||
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);
|
||||
|
||||
// For efficiency, we create static instances of these objects
|
||||
|
@ -165,11 +169,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
// 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;
|
||||
response = p_response;
|
||||
qprefix = p_qprefix;
|
||||
|
||||
sbuilder.Length = 0;
|
||||
|
||||
|
@ -182,7 +187,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
method = request.HttpMethod.ToLower();
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Challenge reason: Required scheme not accepted", MsgId);
|
||||
Rest.Log.DebugFormat("{0} Challenge reason: Requested scheme not acceptable", MsgId);
|
||||
DoChallenge();
|
||||
}
|
||||
|
||||
|
@ -268,15 +274,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
switch (reqscheme)
|
||||
{
|
||||
case "digest" :
|
||||
Rest.Log.DebugFormat("{0} Digest authentication offered", MsgId);
|
||||
DoDigest(authdata);
|
||||
break;
|
||||
case "digest" :
|
||||
Rest.Log.DebugFormat("{0} Digest authentication offered", MsgId);
|
||||
DoDigest(authdata);
|
||||
break;
|
||||
|
||||
case "basic" :
|
||||
Rest.Log.DebugFormat("{0} Basic authentication offered", MsgId);
|
||||
DoBasic(authdata);
|
||||
break;
|
||||
case "basic" :
|
||||
Rest.Log.DebugFormat("{0} Basic authentication offered", MsgId);
|
||||
DoBasic(authdata);
|
||||
break;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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.
|
||||
|
||||
if (!authparms.TryGetValue("nonce", out nonce))
|
||||
if (!authparms.TryGetValue("nonce", out nonce) || nonce == null)
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Authentication failed: nonce missing", MsgId);
|
||||
break;
|
||||
|
@ -457,26 +463,28 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
cntable[cnonce] = nck;
|
||||
cntable[nonce] = nck;
|
||||
}
|
||||
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;
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
|
@ -583,57 +607,135 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
sbuilder.Append(Rest.CS_COMMA);
|
||||
}
|
||||
|
||||
if (Rest.Domains.Count != 0)
|
||||
{
|
||||
sbuilder.Append("domain=");
|
||||
sbuilder.Append(Rest.CS_DQUOTE);
|
||||
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);
|
||||
}
|
||||
// We don;t know the userid that will be used
|
||||
// so we cannot make any authentication domain
|
||||
// assumptions. So the prefix will determine
|
||||
// this.
|
||||
|
||||
if (sbuilder[sbuilder.Length-1] == Rest.C_COMMA)
|
||||
{
|
||||
sbuilder.Length = sbuilder.Length-1;
|
||||
}
|
||||
sbuilder.Append("domain=");
|
||||
sbuilder.Append(Rest.CS_DQUOTE);
|
||||
sbuilder.Append(qprefix);
|
||||
sbuilder.Append(Rest.CS_DQUOTE);
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
@ -773,11 +875,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
if (reset)
|
||||
{
|
||||
buffer = null;
|
||||
body = null;
|
||||
SendHtml(message);
|
||||
body = html;
|
||||
}
|
||||
|
||||
if (Rest.DEBUG)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Request Failure State Dump", MsgId);
|
||||
Rest.Log.DebugFormat("{0} Scheme = {1}", MsgId, scheme);
|
||||
Rest.Log.DebugFormat("{0} Realm = {1}", MsgId, realm);
|
||||
Rest.Log.DebugFormat("{0} Domain = {1}", MsgId, domain);
|
||||
|
@ -828,10 +932,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
{
|
||||
|
||||
Rest.Log.DebugFormat("{0} Generating Response", MsgId);
|
||||
|
||||
// Process any arbitrary headers collected
|
||||
|
||||
BuildHeaders();
|
||||
Rest.Log.DebugFormat("{0} Method is {1}", MsgId, method);
|
||||
|
||||
// A Head request can NOT have a body!
|
||||
if (method != Rest.HEAD)
|
||||
|
@ -839,6 +940,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Buffer-based entity", MsgId);
|
||||
if (response.Headers.Get("Content-Encoding") == null)
|
||||
response.ContentEncoding = encoding;
|
||||
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
|
||||
// has been stored, we consider that a success
|
||||
// Set the status code & description. If nothing has been stored,
|
||||
// we consider that a success.
|
||||
|
||||
if (statusCode == 0)
|
||||
{
|
||||
Complete();
|
||||
}
|
||||
|
||||
// Set the response code in the actual carrier
|
||||
|
||||
response.StatusCode = statusCode;
|
||||
|
||||
if (response.StatusCode == (int)OSHttpStatusCode.RedirectMovedTemporarily ||
|
||||
response.StatusCode == (int)OSHttpStatusCode.RedirectMovedPermanently)
|
||||
// For a redirect we need to set the relocation header accordingly
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// And include the status description if provided.
|
||||
|
||||
if (statusDescription != null)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Status description is {1}", MsgId, statusDescription);
|
||||
response.StatusDescription = statusDescription;
|
||||
}
|
||||
|
||||
// Finally we send back our response, consuming
|
||||
// any exceptions that doing so might produce.
|
||||
// Finally we send back our response.
|
||||
|
||||
// We've left the setting of handled' until the
|
||||
// last minute because the header settings included
|
||||
|
@ -913,6 +1036,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
handled = true;
|
||||
|
||||
DumpHeaders();
|
||||
|
||||
// if (request.InputStream != null)
|
||||
// {
|
||||
// Rest.Log.DebugFormat("{0} Closing input stream", MsgId);
|
||||
// request.InputStream.Close();
|
||||
// }
|
||||
|
||||
if (buffer != null && buffer.Length != 0)
|
||||
{
|
||||
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.Close();
|
||||
// Closing the outputstream should complete the transmission process
|
||||
|
||||
if (request.InputStream != null)
|
||||
{
|
||||
request.InputStream.Close();
|
||||
}
|
||||
Rest.Log.DebugFormat("{0} Closing output stream", MsgId);
|
||||
response.OutputStream.Close();
|
||||
|
||||
}
|
||||
|
||||
|
@ -935,19 +1064,23 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
}
|
||||
|
||||
// Add a header to the table. If the header
|
||||
// already exists, it is replaced.
|
||||
// Add a header to the table. We need to allow
|
||||
// multiple instances of many of the headers.
|
||||
// If the
|
||||
|
||||
internal void AddHeader(string hdr, string data)
|
||||
{
|
||||
|
||||
if (headers == null)
|
||||
if (Rest.DEBUG)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
headers[hdr] = data;
|
||||
|
||||
response.Headers.Add(hdr, data);
|
||||
}
|
||||
|
||||
// Keep explicit track of any headers which
|
||||
|
@ -955,43 +1088,30 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
internal void RemoveHeader(string hdr)
|
||||
{
|
||||
|
||||
if (removed_headers == null)
|
||||
if (Rest.DEBUG)
|
||||
{
|
||||
removed_headers = new List<string>();
|
||||
}
|
||||
|
||||
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, hdr);
|
||||
if (response.Headers.Get(hdr) == null)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Removing header: <{1}>", MsgId, h);
|
||||
response.Headers.Remove(h);
|
||||
Rest.Log.DebugFormat("{0} No such header existed",
|
||||
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}>",
|
||||
MsgId, headers.GetKey(i), headers.Get(i));
|
||||
response.Headers.Add(headers.GetKey(i), headers.Get(i));
|
||||
Rest.Log.DebugFormat("{0} Header[{1}] : {2}", MsgId, i,
|
||||
response.Headers.Get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1019,7 +1139,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
path = uri.AbsolutePath;
|
||||
if (path.EndsWith(Rest.UrlPathSeparator))
|
||||
path = path.Substring(0,path.Length-1);
|
||||
path = Uri.UnescapeDataString(path);
|
||||
}
|
||||
|
||||
// If we succeeded in getting a path, perform any
|
||||
|
@ -1040,6 +1159,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
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
|
||||
|
||||
hostname = uri.Host;
|
||||
|
@ -1149,14 +1273,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
internal void initXmlReader()
|
||||
{
|
||||
|
||||
XmlReaderSettings settings = new XmlReaderSettings();
|
||||
|
||||
settings.ConformanceLevel = ConformanceLevel.Fragment;
|
||||
settings.IgnoreComments = true;
|
||||
settings.IgnoreWhitespace = true;
|
||||
settings.IgnoreProcessingInstructions = true;
|
||||
settings.ValidationType = ValidationType.None;
|
||||
// reader = XmlReader.Create(new StringReader(entity),settings);
|
||||
|
||||
reader = XmlReader.Create(request.InputStream,settings);
|
||||
|
||||
}
|
||||
|
||||
private void Flush()
|
||||
|
|
|
@ -69,7 +69,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
internal static bool ExtendedEscape = true;
|
||||
internal static bool DumpAsset = false;
|
||||
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 DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
|
||||
|
||||
|
|
|
@ -44,35 +44,31 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
public class RestAssetServices : IRest
|
||||
{
|
||||
|
||||
private string key = "assets";
|
||||
private bool enabled = false;
|
||||
private string qPrefix = "assets";
|
||||
|
||||
// A simple constructor is used to handle any once-only
|
||||
// initialization of working classes.
|
||||
|
||||
public RestAssetServices(RestHandler p_rest)
|
||||
public RestAssetServices()
|
||||
{
|
||||
|
||||
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
|
||||
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))
|
||||
{
|
||||
qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix;
|
||||
}
|
||||
|
||||
// Authentication domain
|
||||
|
||||
Rest.Domains.Add(key,Rest.Config.GetString("asset-domain",qPrefix));
|
||||
|
||||
// Register interface
|
||||
// Register interface using the fully-qualified prefix
|
||||
|
||||
Rest.Plugin.AddPathHandler(DoAsset, qPrefix, Allocate);
|
||||
|
||||
// Activate
|
||||
// Activate if all went OK
|
||||
|
||||
enabled = true;
|
||||
|
||||
|
|
|
@ -38,10 +38,33 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
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
|
||||
|
||||
private static bool handlersLoaded = false;
|
||||
private static List<Type> classes = new List<Type>();
|
||||
private static List<IRest> handlers = new List<IRest>();
|
||||
private static Type[] parms = new Type[0];
|
||||
private static Object[] args = new Object[0];
|
||||
|
||||
/// <summary>
|
||||
/// This static initializer scans the assembly for classes that
|
||||
/// This static initializer scans the ASSEMBLY for classes that
|
||||
/// export the IRest interface and builds a list of them. These
|
||||
/// are later activated by the handler. To add a new handler it
|
||||
/// is only necessary to create a new services class that implements
|
||||
|
@ -49,73 +72,78 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
/// all of the build-time flexibility of a modular approach
|
||||
/// while not introducing yet-another module loader. Note that
|
||||
/// multiple assembles can still be built, each with its own set
|
||||
/// of handlers.
|
||||
/// of handlers. Examples of services classes are RestInventoryServices
|
||||
/// and RestSkeleton.
|
||||
/// </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()
|
||||
{
|
||||
|
||||
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
|
||||
|
||||
foreach (Module m in mods)
|
||||
{
|
||||
Type[] types = m.GetTypes();
|
||||
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
|
||||
|
||||
#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>
|
||||
/// This routine loads all of the handlers discovered during
|
||||
/// instance initialization. Each handler is responsible for
|
||||
/// registering itself with this handler.
|
||||
/// I was not able to make this code work in a constructor.
|
||||
/// instance initialization.
|
||||
/// A table of all loaded and successfully constructed handlers
|
||||
/// is built, and this table is then used by the constructor to
|
||||
/// initialize each of the handlers in turn.
|
||||
/// NOTE: The loading process does not automatically imply that
|
||||
/// the handler has registered any kind of an interface, that
|
||||
/// may be (optionally) done by the handler either during
|
||||
/// construction, or during initialization.
|
||||
///
|
||||
/// I was not able to make this code work within a constructor
|
||||
/// so it is islated within this method.
|
||||
/// </summary>
|
||||
|
||||
private void LoadHandlers()
|
||||
{
|
||||
lock (handlers)
|
||||
{
|
||||
if (!handlersLoaded)
|
||||
{
|
||||
parms[0] = this.GetType();
|
||||
args[0] = this;
|
||||
|
||||
ConstructorInfo ci;
|
||||
Object ht;
|
||||
|
||||
foreach (Type t in classes)
|
||||
{
|
||||
ci = t.GetConstructor(parms);
|
||||
ht = ci.Invoke(args);
|
||||
handlers.Add((IRest)ht);
|
||||
try
|
||||
{
|
||||
ci = t.GetConstructor(parms);
|
||||
ht = ci.Invoke(args);
|
||||
handlers.Add((IRest)ht);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Unable to load {1} : {2}", MsgId, t, e.Message);
|
||||
}
|
||||
}
|
||||
handlersLoaded = true;
|
||||
}
|
||||
|
@ -126,14 +154,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
#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
|
||||
{
|
||||
get { return "HANDLER"; }
|
||||
}
|
||||
|
||||
// Used to partition the configuration space.
|
||||
// Used to partition the .ini configuration space.
|
||||
|
||||
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
|
||||
/// the plugin can be enabled.
|
||||
/// </remarks>
|
||||
|
||||
public override void Initialise(OpenSimBase openSim)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
/// <remarks>
|
||||
/// This plugin will only be enabled if the broader
|
||||
/// REST plugin mechanism is enabled.
|
||||
/// </remarks>
|
||||
// This plugin will only be enabled if the broader
|
||||
// REST plugin mechanism is enabled.
|
||||
|
||||
Rest.Log.InfoFormat("{0} Plugin is initializing", MsgID);
|
||||
Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId);
|
||||
|
||||
base.Initialise(openSim);
|
||||
|
||||
// IsEnabled is implemented by the base class and
|
||||
// reflects an overall RestPlugin status
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Plugins are disabled", MsgID);
|
||||
Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId);
|
||||
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
|
||||
/// them easy to reach from anywhere in the assembly.
|
||||
/// </remarks>
|
||||
// These are stored in static variables to make
|
||||
// them easy to reach from anywhere in the assembly.
|
||||
|
||||
Rest.main = openSim;
|
||||
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.DumpAsset ? "" : "not "));
|
||||
|
||||
// If data dumping is requested, report on the chosen line
|
||||
// length.
|
||||
|
||||
if (Rest.DumpAsset)
|
||||
{
|
||||
Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId,
|
||||
|
@ -247,22 +281,24 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
LoadHandlers();
|
||||
|
||||
/// <remarks>
|
||||
/// The intention of a post construction initializer
|
||||
/// is to allow for setup that is dependent upon other
|
||||
/// activities outside of the agency. We don't currently
|
||||
/// have any, but the design allows for it.
|
||||
/// </remarks>
|
||||
// The intention of a post construction initializer
|
||||
// is to allow for setup that is dependent upon other
|
||||
// activities outside of the agency.
|
||||
|
||||
foreach (IRest handler in handlers)
|
||||
{
|
||||
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 and
|
||||
/// add this agent to the HTTP server's handler list
|
||||
/// </remarks>
|
||||
// Now that everything is setup we can proceed to
|
||||
// add THIS agent to the HTTP server's handler list
|
||||
|
||||
if (!AddAgentHandler(Rest.Name,this))
|
||||
{
|
||||
|
@ -276,7 +312,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
}
|
||||
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
|
||||
/// is disabled by deleting the handler from the HTTP server tables.
|
||||
/// </summary>
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
|
||||
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgID);
|
||||
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -313,45 +350,62 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
#region interface methods
|
||||
|
||||
/// <summary>
|
||||
/// This method is called by the server to match the client, it could
|
||||
/// just return true if we only want one such handler. For now we
|
||||
/// match any explicitly specified client.
|
||||
/// This method is called by the HTTP server to match an incoming
|
||||
/// request. It scans all of the strings registered by the
|
||||
/// underlying handlers and looks for the best match. It returns
|
||||
/// true if a match is found.
|
||||
/// The matching process could be made arbitrarily complex.
|
||||
/// </summary>
|
||||
|
||||
public bool Match(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
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);
|
||||
foreach (string key in streamHandlers.Keys)
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is called by the HTTP server once the handler has indicated
|
||||
/// that t is able to handle the request.
|
||||
/// Preconditions:
|
||||
/// [1] request != null and is a valid request object
|
||||
/// [2] response != null and is a valid response object
|
||||
/// Behavior is undefined if preconditions are not satisfied.
|
||||
/// </summary>
|
||||
|
||||
public bool Handle(OSHttpRequest request, OSHttpResponse response)
|
||||
{
|
||||
bool handled;
|
||||
base.MsgID = base.RequestID;
|
||||
|
||||
// Debug only
|
||||
|
||||
if (Rest.DEBUG)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} ENTRY", MsgId);
|
||||
|
@ -371,8 +425,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
try
|
||||
{
|
||||
handled = FindPathHandler(request, response) ||
|
||||
FindStreamHandler(request, response);
|
||||
handled = ( FindPathHandler(request, response) ||
|
||||
FindStreamHandler(request, response) );
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -406,6 +460,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path);
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (string pattern in streamHandlers.Keys)
|
||||
{
|
||||
if (path.StartsWith(pattern))
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -454,17 +519,26 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
if (!streamHandlers.ContainsKey(path))
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathHandlers.ContainsKey(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.Threading;
|
||||
using System.Xml;
|
||||
using OpenJPEGNet;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using OpenSim.Framework.Communications;
|
||||
|
@ -44,35 +45,33 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
public class RestInventoryServices : IRest
|
||||
{
|
||||
|
||||
private string key = "inventory";
|
||||
private bool enabled = false;
|
||||
private string qPrefix = "inventory";
|
||||
|
||||
// A simple constructor is used to handle any once-only
|
||||
// initialization of working classes.
|
||||
/// <summary>
|
||||
/// 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} 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))
|
||||
{
|
||||
qPrefix = Rest.Prefix + Rest.UrlPathSeparator + qPrefix;
|
||||
}
|
||||
|
||||
// Authentication domain
|
||||
|
||||
Rest.Domains.Add(key, Rest.Config.GetString("inventory-domain",qPrefix));
|
||||
|
||||
// Register interface
|
||||
// Register interface using the absolute URI.
|
||||
|
||||
Rest.Plugin.AddPathHandler(DoInventory,qPrefix,Allocate);
|
||||
|
||||
// Activate
|
||||
// Activate if everything went OK
|
||||
|
||||
enabled = true;
|
||||
|
||||
|
@ -80,16 +79,20 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
}
|
||||
|
||||
// Post-construction, pre-enabled initialization opportunity
|
||||
// Not currently exploited.
|
||||
/// <summary>
|
||||
/// Post-construction, pre-enabled initialization opportunity
|
||||
/// Not currently exploited.
|
||||
/// </summary>
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
// Called by the plug-in to halt REST processing. Local processing is
|
||||
// disabled, and control blocks until all current processing has
|
||||
// completed. No new processing will be started
|
||||
/// <summary>
|
||||
/// Called by the plug-in to halt REST processing. Local processing is
|
||||
/// disabled, and control blocks until all current processing has
|
||||
/// completed. No new processing will be started
|
||||
/// </summary>
|
||||
|
||||
public void Close()
|
||||
{
|
||||
|
@ -97,7 +100,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
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
|
||||
{
|
||||
|
@ -106,15 +112,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
#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)
|
||||
{
|
||||
return (RequestData) new InventoryRequestData(request, response, qPrefix);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is registered with the handler when this class is
|
||||
/// initialized. It is called whenever the URI includes this handler's
|
||||
/// prefix string.
|
||||
/// This method is registered with the handler when this service provider
|
||||
/// is initialized. It is called whenever the plug-in identifies this service
|
||||
/// provider as the best match.
|
||||
/// It handles all aspects of inventory REST processing.
|
||||
/// </summary>
|
||||
|
||||
|
@ -125,7 +138,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
Rest.Log.DebugFormat("{0} DoInventory ENTRY", MsgId);
|
||||
|
||||
// We're disabled
|
||||
// If we're disabled, do nothing.
|
||||
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
|
@ -169,23 +183,32 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
|
||||
|
||||
// We can only get here if we're authorized
|
||||
//
|
||||
// The requestor may have specified an LLUUID or
|
||||
// a conjoined FirstNameLastName string. We'll
|
||||
// try both. If we fail with the first, UUID,
|
||||
// attempt, then we need two nodes to construct
|
||||
// a valid avatar name.
|
||||
/// <remarks>
|
||||
/// We can only get here if we are authorized
|
||||
///
|
||||
/// The requestor may have specified an LLUUID or
|
||||
/// a conjoined FirstName LastName string. We'll
|
||||
/// try both. If we fail with the first, UUID,
|
||||
/// attempt, we try the other. As an example, the
|
||||
/// URI for a valid inventory request might be:
|
||||
///
|
||||
/// http://<host>:<port>/admin/inventory/Arthur Dent
|
||||
///
|
||||
/// Indicating that this is an inventory request for
|
||||
/// an avatar named Arthur Dent. This is ALl that is
|
||||
/// required to designate a GET for an entire
|
||||
/// inventory.
|
||||
/// </remarks>
|
||||
|
||||
// Do we have at least a user agent name?
|
||||
|
||||
if (rdata.parameters.Length < 1)
|
||||
{
|
||||
Rest.Log.WarnFormat("{0} Inventory: No user agent identifier specified", MsgId);
|
||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, 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.
|
||||
|
||||
try
|
||||
|
@ -205,10 +228,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
else
|
||||
{
|
||||
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)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} Profile obtained for agent {1} {2}",
|
||||
|
@ -217,11 +243,20 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
else
|
||||
{
|
||||
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
|
||||
// for the specified user.
|
||||
// If we get to here, then we have effectively validated the user's
|
||||
// identity. Now we need to get the inventory. If the server does not
|
||||
// have the inventory, we reject the request with an appropriate explanation.
|
||||
//
|
||||
// Note that inventory retrieval is an asynchronous event, we use the rdata
|
||||
// class instance as the basis for our synchronization.
|
||||
//
|
||||
// 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;
|
||||
|
||||
|
@ -250,7 +285,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
{
|
||||
Rest.Log.DebugFormat("{0} Inventory is not available [1] for agent {1} {2}",
|
||||
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}",
|
||||
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
|
||||
|
@ -292,7 +327,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
Rest.Log.DebugFormat("{0} Method {1} not supported for {2}",
|
||||
MsgId, rdata.method, rdata.path);
|
||||
rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
|
||||
Rest.HttpStatusDescMethodNotAllowed);
|
||||
Rest.HttpStatusDescMethodNotAllowed+": "+rdata.method+" not supported");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -315,10 +350,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
traverseInventory(rdata, rdata.root, 1);
|
||||
|
@ -332,33 +376,35 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
}
|
||||
|
||||
/// <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
|
||||
/// easy to discern. Adding a directory can be viewed 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
|
||||
/// the entity and the URI. If we view POST as an update,
|
||||
/// then the enity represents a replacement for the
|
||||
/// element named by the URI. If the operation is PUT,
|
||||
/// then the URI describes the context into which the
|
||||
/// entity will be added.
|
||||
/// For OpenSim POST is an update and PUT is an addition.
|
||||
///
|
||||
/// The best way to exaplain the distinction is to
|
||||
/// consider the relationship between the URI and the
|
||||
/// entity in question. For POST, the URI identifies the
|
||||
/// 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:
|
||||
/// /admin/inventory/Clothing
|
||||
/// Suppose the entity represents a Folder, called
|
||||
/// "Clothes".
|
||||
///
|
||||
/// A POST request will result in the replacement of
|
||||
/// "Clothing" by "Clothes". Whereas a PUT request
|
||||
/// would add Clothes as a sub-directory of Clothing.
|
||||
///
|
||||
/// This is the model followed by this implementation.
|
||||
/// A POST request will result in some modification of
|
||||
/// the folder or item named "Clothing". Whereas a PUT
|
||||
/// request will add some new information into the
|
||||
/// content identified by Clothing. It follows from this
|
||||
/// that for PUT, the element identified by the URI must
|
||||
/// be a folder.
|
||||
/// </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.
|
||||
/// </summary>
|
||||
|
||||
|
@ -376,7 +422,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
// exception.
|
||||
|
||||
// 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.
|
||||
|
||||
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}",
|
||||
MsgId, rdata.method, rdata.path);
|
||||
|
||||
// Reconstitute inventory sub-tree from the XML supplied in the entity.
|
||||
// This is a stand-alone inventory subtree, not yet integrated into the
|
||||
// existing tree.
|
||||
// Reconstitute the inventory sub-tree from the XML supplied in the entity.
|
||||
// The result is a stand-alone inventory subtree, not yet integrated into the
|
||||
// existing tree. An inventory collection consists of three components:
|
||||
// [1] A (possibly empty) set of folders.
|
||||
// [2] A (possibly empty) set of items.
|
||||
// [3] A (possibly empty) set of assets.
|
||||
// If all of these are empty, then the PUT is a harmless no-operation.
|
||||
|
||||
XmlInventoryCollection entity = ReconstituteEntity(rdata);
|
||||
|
||||
// Inlined assest included in entity. If anything fails,
|
||||
// return failure to requestor.
|
||||
// Inlined assets can be included in entity. These must be incorporated into
|
||||
// the asset database before we attempt to update the inventory. If anything
|
||||
// fails, return a failure to requestor.
|
||||
|
||||
if (entity.Assets.Count > 0)
|
||||
{
|
||||
|
@ -410,10 +461,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}",
|
||||
MsgId, asset.ID, asset.Type, asset.Name);
|
||||
Rest.AssetServices.AddAsset(asset);
|
||||
|
||||
if (Rest.DumpAsset)
|
||||
{
|
||||
Rest.Dump(asset.Data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -424,11 +477,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
foreach (InventoryFolderBase folder in entity.Folders)
|
||||
{
|
||||
|
||||
InventoryFolderBase found = null;
|
||||
InventoryFolderBase found;
|
||||
|
||||
// If the parentID is zero, then this is going
|
||||
// into the root identified by the URI. The requestor
|
||||
// may have already set the parent ID correctly, in which
|
||||
// If the parentID is zero, then this folder is going
|
||||
// into the root folder identified by the URI. The requestor
|
||||
// may have already set the parent ID explicitly, in which
|
||||
// case we don't have to do it here.
|
||||
|
||||
if (folder.ParentID == LLUUID.Zero)
|
||||
|
@ -437,7 +490,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
}
|
||||
|
||||
// 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
|
||||
// don't want to waste time updating the database in that
|
||||
// case, OR, it could be being moved from another location
|
||||
|
@ -451,6 +504,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
if (xf.ID == folder.ID)
|
||||
{
|
||||
found = xf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,6 +546,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
if (xi.ID == item.ID)
|
||||
{
|
||||
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}",
|
||||
MsgId, rdata.method, rdata.path, InventoryNode.GetType());
|
||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": invalid resource context");
|
||||
}
|
||||
|
||||
rdata.Complete();
|
||||
|
@ -531,7 +586,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
/// [1] It identifies the user whose inventory is to be
|
||||
/// processed.
|
||||
/// [2] It optionally specifies a subtree of the inventory
|
||||
/// that is to be used to resolve an relative subtree
|
||||
/// that is to be used to resolve any relative subtree
|
||||
/// specifications in the entity. If nothing is specified
|
||||
/// then the whole inventory is implied.
|
||||
/// 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
|
||||
/// by the URI.
|
||||
/// 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
|
||||
/// elements, are ignored. This case is actually detected and handled
|
||||
/// during the reconstitution process.
|
||||
|
@ -555,33 +610,54 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
Object InventoryNode = getInventoryNode(rdata, rdata.root, 1);
|
||||
|
||||
// As long as we have a context, then we have something
|
||||
// meaningful to do, unlike PUT. So reconstitute the
|
||||
// As long as we have a node, then we have something
|
||||
// meaningful to do, unlike PUT. So we reconstitute the
|
||||
// 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);
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Asset was validated during the collection
|
||||
// process
|
||||
Rest.Log.DebugFormat("{0} Rest asset: {1} {2} {3}",
|
||||
MsgId, asset.ID, asset.Type, asset.Name);
|
||||
|
||||
// The asset was validated during the collection process
|
||||
|
||||
Rest.AssetServices.AddAsset(asset);
|
||||
|
||||
if (Rest.DumpAsset)
|
||||
{
|
||||
Rest.Dump(asset.Data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URI specifies a folder to be updated.
|
||||
/// The URI specifies either a folder or an item to be updated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The root node in the entity must have the same
|
||||
/// UUID as the node identified by the URI. The
|
||||
/// parentID if different indicates that the updated
|
||||
/// folder is actually being moved too.
|
||||
/// The root node in the entity will replace the node identified
|
||||
/// by the URI. This means the parent will remain the same, but
|
||||
/// any or all attributes associated with the named element
|
||||
/// will change.
|
||||
///
|
||||
/// If the inventory collection contains an element with a zero
|
||||
/// parent ID, then this is taken to be the replacement for the
|
||||
/// named node. The collection MAY also specify an explicit
|
||||
/// parent ID, in this case it MAY identify the same parent as
|
||||
/// the current node, or it MAY specify a different parent,
|
||||
/// indicating that the folder is being moved in addition to any
|
||||
/// other modifications being made.
|
||||
/// </remarks>
|
||||
|
||||
if (typeof(InventoryFolderBase) == InventoryNode.GetType() ||
|
||||
|
@ -592,7 +668,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
InventoryFolderBase xml = null;
|
||||
|
||||
// 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
|
||||
// implicit context), or the parent's UUID matches that of the
|
||||
// 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)
|
||||
{
|
||||
Rest.Log.DebugFormat("{0} {1}: Request for <{2}> is ambiguous",
|
||||
MsgId, rdata.method, rdata.path);
|
||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": context is ambiguous");
|
||||
}
|
||||
|
||||
// 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}>",
|
||||
MsgId, rdata.method, rdata.path);
|
||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": folder is not allowed");
|
||||
}
|
||||
|
||||
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}>",
|
||||
MsgId, rdata.method, rdata.path);
|
||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": too may items");
|
||||
}
|
||||
|
||||
xml = entity.Items[0];
|
||||
|
@ -854,7 +931,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
{
|
||||
Rest.Log.DebugFormat("{0} {1}: Request for {2} is ambiguous",
|
||||
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",
|
||||
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 */
|
||||
|
||||
|
@ -931,7 +1008,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
|
||||
Rest.Log.DebugFormat("{0} Inventory does not contain item/folder: <{1}>",
|
||||
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.Type = (short) AssetType.TrashFolder;
|
||||
TrashCan.ParentID = f.ID;
|
||||
TrashCan.Owner = f.Owner;
|
||||
Rest.InventoryServices.AddFolder(TrashCan);
|
||||
}
|
||||
}
|
||||
|
@ -1070,7 +1148,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
{
|
||||
Rest.Log.DebugFormat("{0} No Trash Can available", MsgId);
|
||||
rdata.Fail(Rest.HttpStatusCodeServerError,
|
||||
Rest.HttpStatusDescServerError);
|
||||
Rest.HttpStatusDescServerError+": unable to create trash can");
|
||||
}
|
||||
|
||||
return TrashCan;
|
||||
|
@ -1313,7 +1391,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}",
|
||||
MsgId, ic.xml.Name, ic.xml.Value);
|
||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": unrecognized attribute");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1349,7 +1427,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}",
|
||||
MsgId, ic.Item.Folder, result.ID);
|
||||
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}",
|
||||
MsgId, ic.xml.Name, ic.xml.Value);
|
||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": unrecognized attribute");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1570,7 +1648,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|||
{
|
||||
Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId);
|
||||
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);
|
||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": request parse error");
|
||||
}
|
||||
|
||||
// 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);
|
||||
ic.Fail(Rest.HttpStatusCodeBadRequest,
|
||||
Rest.HttpStatusDescBadRequest);
|
||||
Rest.HttpStatusDescBadRequest+": item name required");
|
||||
}
|
||||
|
||||
// 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.InfoFormat("{0} Asset information is missing", MsgId);
|
||||
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}",
|
||||
MsgId, ic.Item.Folder, ic.Item.ID);
|
||||
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)
|
||||
ic.Item.InvType = (int) AssetType.ImageJPEG;
|
||||
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 :
|
||||
Rest.Log.DebugFormat("{0} Type was not inferred", MsgId);
|
||||
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();
|
||||
|
||||
}
|
||||
|
|
|
@ -171,18 +171,20 @@ namespace OpenSim.Framework.Servers
|
|||
OSHttpRequest request = new OSHttpRequest(context.Request);
|
||||
OSHttpResponse response = new OSHttpResponse(context.Response);
|
||||
|
||||
// user agent based requests? not sure where this actually gets used from
|
||||
if (request.UserAgent != null)
|
||||
{
|
||||
IHttpAgentHandler agentHandler;
|
||||
// This is the REST agent interface. We require an agent to properly identify
|
||||
// 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
|
||||
// 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))
|
||||
{
|
||||
m_log.DebugFormat("[HTTP-AGENT] Handler located for {0}", request.UserAgent);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,7 +510,7 @@ namespace OpenSim.Framework.Servers
|
|||
catch (Exception e)
|
||||
{
|
||||
// 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
|
||||
// client.
|
||||
try
|
||||
|
@ -524,7 +526,10 @@ namespace OpenSim.Framework.Servers
|
|||
}
|
||||
}
|
||||
|
||||
// Indicate that the request has been "handled"
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public void HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
|
||||
|
|
Loading…
Reference in New Issue