Adding
- NewAssetServer code - NewAssetServer addin manifest - example AssetServer.ini file0.6.3-post-fixes
parent
37a00427bc
commit
0e09b4a08b
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using ExtensionLoader.Config;
|
||||||
|
using HttpServer;
|
||||||
|
using log4net;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
|
||||||
|
namespace AssetServer
|
||||||
|
{
|
||||||
|
public class AssetServer : ServiceBase
|
||||||
|
{
|
||||||
|
public const string CONFIG_FILE = "AssetServer.ini";
|
||||||
|
|
||||||
|
public WebServer HttpServer;
|
||||||
|
public IniConfigSource ConfigFile;
|
||||||
|
|
||||||
|
public IAssetStorageProvider StorageProvider;
|
||||||
|
public IInventoryProvider InventoryProvider;
|
||||||
|
public IAuthenticationProvider AuthenticationProvider;
|
||||||
|
public IAuthorizationProvider AuthorizationProvider;
|
||||||
|
public IMetricsProvider MetricsProvider;
|
||||||
|
|
||||||
|
public AssetServer()
|
||||||
|
{
|
||||||
|
this.ServiceName = "OpenSimAssetServer";
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Start()
|
||||||
|
{
|
||||||
|
Logger.Log.Info("Starting Asset Server");
|
||||||
|
List<string> extensionList = null;
|
||||||
|
int port = 0;
|
||||||
|
X509Certificate2 serverCert = null;
|
||||||
|
|
||||||
|
try { ConfigFile = new IniConfigSource(CONFIG_FILE); }
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Failed to load the config file " + CONFIG_FILE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IConfig extensionConfig = ConfigFile.Configs["Config"];
|
||||||
|
|
||||||
|
// Load the port number to listen on
|
||||||
|
port = extensionConfig.GetInt("ListenPort");
|
||||||
|
|
||||||
|
// Load the server certificate file
|
||||||
|
string certFile = extensionConfig.GetString("SSLCertFile");
|
||||||
|
if (!String.IsNullOrEmpty(certFile))
|
||||||
|
serverCert = new X509Certificate2(certFile);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Failed to load [Config] section from " + CONFIG_FILE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load the extension list (and ordering) from our config file
|
||||||
|
IConfig extensionConfig = ConfigFile.Configs["Extensions"];
|
||||||
|
extensionList = new List<string>(extensionConfig.GetKeys());
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Failed to load [Extensions] section from " + CONFIG_FILE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//try
|
||||||
|
//{
|
||||||
|
// // Create a reference list for C# extensions compiled at runtime
|
||||||
|
// List<string> references = new List<string>();
|
||||||
|
// references.Add("OpenMetaverseTypes.dll");
|
||||||
|
// references.Add("OpenMetaverse.dll");
|
||||||
|
// references.Add("OpenMetaverse.StructuredData.dll");
|
||||||
|
// references.Add("OpenMetaverse.Http.dll");
|
||||||
|
// references.Add("ExtensionLoader.dll");
|
||||||
|
// references.Add("AssetServer.exe");
|
||||||
|
|
||||||
|
// // Get a list of all of the members of AssetServer that are interfaces
|
||||||
|
// List<FieldInfo> assignables = ExtensionLoader<AssetServer>.GetInterfaces(this);
|
||||||
|
|
||||||
|
// // Load all of the extensions
|
||||||
|
// ExtensionLoader<AssetServer>.LoadAllExtensions(
|
||||||
|
// Assembly.GetExecutingAssembly(),
|
||||||
|
// Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
|
||||||
|
// extensionList,
|
||||||
|
// references,
|
||||||
|
// "AssetServer.*.dll",
|
||||||
|
// "AssetServer.*.cs",
|
||||||
|
// this,
|
||||||
|
// assignables);
|
||||||
|
//}
|
||||||
|
//catch (ExtensionException ex)
|
||||||
|
//{
|
||||||
|
// Logger.Log.Error("Interface loading failed, shutting down: " + ex.Message);
|
||||||
|
// if (ex.InnerException != null)
|
||||||
|
// Logger.Log.Error(ex.InnerException.Message, ex.InnerException);
|
||||||
|
// Stop();
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
StorageProvider = LoadAssetServerPlugin() as IAssetStorageProvider;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InitHttpServer(port, serverCert);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Initializing the HTTP server failed, shutting down: " + ex.Message);
|
||||||
|
Stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start all of the extensions
|
||||||
|
//foreach (IExtension<AssetServer> extension in ExtensionLoader<AssetServer>.Extensions)
|
||||||
|
//{
|
||||||
|
// Logger.Log.Info("Starting extension " + extension.GetType().Name);
|
||||||
|
// extension.Start(this);
|
||||||
|
//}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
foreach (IExtension<AssetServer> extension in ExtensionLoader<AssetServer>.Extensions)
|
||||||
|
{
|
||||||
|
Logger.Log.Debug("Disposing extension " + extension.GetType().Name);
|
||||||
|
try { extension.Stop(); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{ Logger.Log.ErrorFormat("Failure shutting down extension {0}: {1}", extension.GetType().Name, ex.Message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HttpServer != null)
|
||||||
|
HttpServer.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitHttpServer(int port, X509Certificate serverCert)
|
||||||
|
{
|
||||||
|
if (serverCert != null)
|
||||||
|
HttpServer = new WebServer(IPAddress.Any, port, serverCert, null, false);
|
||||||
|
else
|
||||||
|
HttpServer = new WebServer(IPAddress.Any, port);
|
||||||
|
|
||||||
|
HttpServer.LogWriter = new log4netLogWriter(Logger.Log);
|
||||||
|
|
||||||
|
HttpServer.Set404Handler(
|
||||||
|
delegate(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Requested page was not found: " + request.Uri.PathAndQuery);
|
||||||
|
|
||||||
|
string notFoundString = "<html><head><title>Page Not Found</title></head><body>The requested page or method was not found</body></html>";
|
||||||
|
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(notFoundString);
|
||||||
|
response.Body.Write(buffer, 0, buffer.Length);
|
||||||
|
response.Status = HttpStatusCode.NotFound;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
HttpServer.Start();
|
||||||
|
|
||||||
|
Logger.Log.Info("Asset server is listening on port " + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region ServiceBase Overrides
|
||||||
|
|
||||||
|
protected override void OnStart(string[] args)
|
||||||
|
{
|
||||||
|
Start();
|
||||||
|
}
|
||||||
|
protected override void OnStop()
|
||||||
|
{
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private IAssetServerPlugin LoadAssetServerPlugin()
|
||||||
|
{
|
||||||
|
PluginLoader<IAssetServerPlugin> loader = new PluginLoader<IAssetServerPlugin>(new AssetServerPluginInitialiser(this));
|
||||||
|
|
||||||
|
//loader.Add ("/OpenSim/AssetServer/StorageProvider", new PluginProviderFilter (provider));
|
||||||
|
loader.Add("/OpenSim/AssetServer/StorageProvider", new PluginCountConstraint(1));
|
||||||
|
loader.Load();
|
||||||
|
|
||||||
|
return loader.Plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class log4netLogWriter : ILogWriter
|
||||||
|
{
|
||||||
|
ILog Log;
|
||||||
|
|
||||||
|
public log4netLogWriter(ILog log)
|
||||||
|
{
|
||||||
|
Log = log;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(object source, LogPrio prio, string message)
|
||||||
|
{
|
||||||
|
switch (prio)
|
||||||
|
{
|
||||||
|
case LogPrio.Trace:
|
||||||
|
case LogPrio.Debug:
|
||||||
|
Log.DebugFormat("{0}: {1}", source, message);
|
||||||
|
break;
|
||||||
|
case LogPrio.Info:
|
||||||
|
Log.InfoFormat("{0}: {1}", source, message);
|
||||||
|
break;
|
||||||
|
case LogPrio.Warning:
|
||||||
|
Log.WarnFormat("{0}: {1}", source, message);
|
||||||
|
break;
|
||||||
|
case LogPrio.Error:
|
||||||
|
Log.ErrorFormat("{0}: {1}", source, message);
|
||||||
|
break;
|
||||||
|
case LogPrio.Fatal:
|
||||||
|
Log.FatalFormat("{0}: {1}", source, message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class AuthorizeAll : IExtension<AssetServer>, IAuthorizationProvider
|
||||||
|
{
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public AuthorizeAll()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsMetadataAuthorized(UUID authToken, UUID assetID)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDataAuthorized(UUID authToken, UUID assetID)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCreateAuthorized(UUID authToken)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsInventoryReadAuthorized(UUID authToken, Uri owner)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsInventoryWriteAuthorized(UUID authToken, Uri owner)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Web;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using HttpServer;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class BrowseFrontend : IExtension<AssetServer>
|
||||||
|
{
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public BrowseFrontend()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
// Request for / or /?...
|
||||||
|
server.HttpServer.AddHandler("get", null, @"(^/$)|(^/\?.*)", BrowseRequestHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BrowseRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
const int ASSETS_PER_PAGE = 25;
|
||||||
|
const string HEADER = "<html><head><title>Asset Server</title></head><body>";
|
||||||
|
const string TABLE_HEADER =
|
||||||
|
"<table><tr><th>Name</th><th>Description</th><th>Type</th><th>ID</th><th>Temporary</th><th>SHA-1</th></tr>";
|
||||||
|
const string TABLE_FOOTER = "</table>";
|
||||||
|
const string FOOTER = "</body></html>";
|
||||||
|
|
||||||
|
UUID authToken = Utils.GetAuthToken(request);
|
||||||
|
|
||||||
|
StringBuilder html = new StringBuilder();
|
||||||
|
int start = 0;
|
||||||
|
uint page = 0;
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(request.Uri.Query))
|
||||||
|
{
|
||||||
|
NameValueCollection query = HttpUtility.ParseQueryString(request.Uri.Query);
|
||||||
|
if (!String.IsNullOrEmpty(query["page"]) && UInt32.TryParse(query["page"], out page))
|
||||||
|
start = (int)page * ASSETS_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.AppendLine(HEADER);
|
||||||
|
|
||||||
|
html.AppendLine("<p>");
|
||||||
|
if (page > 0)
|
||||||
|
html.AppendFormat("<a href=\"{0}?page={1}\">< Previous Page</a> | ", request.Uri.AbsolutePath, page - 1);
|
||||||
|
html.AppendFormat("<a href=\"{0}?page={1}\">Next Page ></a>", request.Uri.AbsolutePath, page + 1);
|
||||||
|
html.AppendLine("</p>");
|
||||||
|
|
||||||
|
html.AppendLine(TABLE_HEADER);
|
||||||
|
|
||||||
|
server.StorageProvider.ForEach(
|
||||||
|
delegate(Metadata data)
|
||||||
|
{
|
||||||
|
if (server.AuthorizationProvider.IsMetadataAuthorized(authToken, data.ID))
|
||||||
|
{
|
||||||
|
html.AppendLine(String.Format(
|
||||||
|
"<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td></tr>",
|
||||||
|
data.Name, data.Description, data.ContentType, data.ID, data.Temporary,
|
||||||
|
BitConverter.ToString(data.SHA1).Replace("-", String.Empty)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
html.AppendLine(String.Format(
|
||||||
|
"<tr><td>[Protected Asset]</td><td> </td><td> </td><td>{0}</td><td>{1}</td><td> </td></tr>",
|
||||||
|
data.ID, data.Temporary));
|
||||||
|
}
|
||||||
|
}, start, ASSETS_PER_PAGE
|
||||||
|
);
|
||||||
|
|
||||||
|
html.AppendLine(TABLE_FOOTER);
|
||||||
|
|
||||||
|
html.AppendLine(FOOTER);
|
||||||
|
|
||||||
|
byte[] responseData = System.Text.Encoding.UTF8.GetBytes(html.ToString());
|
||||||
|
|
||||||
|
response.Status = HttpStatusCode.OK;
|
||||||
|
response.Body.Write(responseData, 0, responseData.Length);
|
||||||
|
response.Body.Flush();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Xml;
|
||||||
|
using ExtensionLoader.Config;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public static class DBConnString
|
||||||
|
{
|
||||||
|
private static string connectionString;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the MySQL connection string out of either the asset server
|
||||||
|
/// .ini or a OpenSim-style .xml file and caches the result for future
|
||||||
|
/// requests
|
||||||
|
/// </summary>
|
||||||
|
public static string GetConnectionString(IniConfigSource configFile)
|
||||||
|
{
|
||||||
|
if (connectionString == null)
|
||||||
|
{
|
||||||
|
// Try parsing from the ini file
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Load the extension list (and ordering) from our config file
|
||||||
|
IConfig extensionConfig = configFile.Configs["MySQL"];
|
||||||
|
connectionString = extensionConfig.GetString("database_connect", null);
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
|
||||||
|
if (connectionString != null)
|
||||||
|
{
|
||||||
|
// Force MySQL's broken connection pooling off
|
||||||
|
MySqlConnectionStringBuilder builder = new MySqlConnectionStringBuilder(connectionString);
|
||||||
|
builder.Pooling = false;
|
||||||
|
if (String.IsNullOrEmpty(builder.Database))
|
||||||
|
Logger.Log.Error("No database selected in the connectionString: " + connectionString);
|
||||||
|
connectionString = builder.ToString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Database connection string is missing, check that the database_connect line is " +
|
||||||
|
"correct and uncommented in AssetServer.ini");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return connectionString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class NullAuthentication : IExtension<AssetServer>, IAuthenticationProvider
|
||||||
|
{
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public NullAuthentication()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddIdentifier(UUID authToken, Uri identifier)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveIdentifier(UUID authToken)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetIdentifier(UUID authToken, out Uri identifier)
|
||||||
|
{
|
||||||
|
identifier = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class NullMetrics : IExtension<AssetServer>, IMetricsProvider
|
||||||
|
{
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public NullMetrics()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogAssetMetadataFetch(string extension, BackendResponse response, UUID assetID, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] AssetMetadataFetch(): AssetID: {1}, Response: {2}", extension, assetID, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogAssetDataFetch(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] AssetDataFetch(): AssetID: {1}, DataSize: {2}, Response: {3}", extension, assetID,
|
||||||
|
dataSize, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogAssetCreate(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] AssetCreate(): AssetID: {1}, DataSize: {2}, Response: {3}", extension, assetID,
|
||||||
|
dataSize, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryFetch(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryFetch(): ObjID: {1}, Folder: {2}, OwnerID: {3}, Response: {4}", extension,
|
||||||
|
objID, folder, owner, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryFetchFolderContents(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryFetchFolderContents(): FolderID: {1}, OwnerID: {2}, Response: {3}", extension,
|
||||||
|
folderID, owner, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryFetchFolderList(string extension, BackendResponse response, Uri owner, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryFetchFolderList(): OwnerID: {1}, Response: {2}", extension,
|
||||||
|
owner, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryFetchInventory(string extension, BackendResponse response, Uri owner, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryFetchInventory(): OwnerID: {1}, Response: {2}", extension,
|
||||||
|
owner, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryFetchActiveGestures(string extension, BackendResponse response, Uri owner, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryFetchActiveGestures(): OwnerID: {1}, Response: {2}", extension,
|
||||||
|
owner, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryCreate(string extension, BackendResponse response, Uri owner, bool folder, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryCreate(): OwnerID: {1}, Response: {2}", extension,
|
||||||
|
owner, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryCreateInventory(string extension, BackendResponse response, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryCreateInventory(): Response: {1}", extension,
|
||||||
|
response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryDelete(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryDelete(): OwnerID: {1}, Folder: {2}, Response: {3}", extension,
|
||||||
|
owner, folder, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogInventoryPurgeFolder(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time)
|
||||||
|
{
|
||||||
|
Logger.Log.DebugFormat("[{0}] InventoryPurgeFolder(): OwnerID: {1}, FolderID: {2}, Response: {3}", extension,
|
||||||
|
owner, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using HttpServer;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class OpenSimFrontend : IExtension<AssetServer>
|
||||||
|
{
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public OpenSimFrontend()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
// Asset request
|
||||||
|
server.HttpServer.AddHandler("get", null, @"^/assets/", AssetRequestHandler);
|
||||||
|
|
||||||
|
// Asset creation
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/assets/", AssetPostHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID assetID;
|
||||||
|
// Split the URL up to get the asset ID out
|
||||||
|
string[] rawUrl = request.Uri.PathAndQuery.Split('/');
|
||||||
|
|
||||||
|
if (rawUrl.Length >= 3 && rawUrl[2].Length >= 36 && UUID.TryParse(rawUrl[2].Substring(0, 36), out assetID))
|
||||||
|
{
|
||||||
|
Metadata metadata;
|
||||||
|
byte[] assetData;
|
||||||
|
BackendResponse dataResponse;
|
||||||
|
|
||||||
|
if ((dataResponse = server.StorageProvider.TryFetchDataMetadata(assetID, out metadata, out assetData)) == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
MemoryStream stream = new MemoryStream();
|
||||||
|
|
||||||
|
XmlWriterSettings settings = new XmlWriterSettings();
|
||||||
|
settings.Indent = true;
|
||||||
|
XmlWriter writer = XmlWriter.Create(stream, settings);
|
||||||
|
|
||||||
|
writer.WriteStartDocument();
|
||||||
|
writer.WriteStartElement("AssetBase");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
|
||||||
|
writer.WriteStartElement("FullID");
|
||||||
|
writer.WriteStartElement("Guid");
|
||||||
|
writer.WriteString(assetID.ToString());
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteStartElement("ID");
|
||||||
|
writer.WriteString(assetID.ToString());
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteStartElement("Data");
|
||||||
|
writer.WriteBase64(assetData, 0, assetData.Length);
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteStartElement("Type");
|
||||||
|
writer.WriteValue(Utils.ContentTypeToSLAssetType(metadata.ContentType));
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteStartElement("Name");
|
||||||
|
writer.WriteString(metadata.Name);
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteStartElement("Description");
|
||||||
|
writer.WriteString(metadata.Description);
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteStartElement("Local");
|
||||||
|
writer.WriteValue(false);
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteStartElement("Temporary");
|
||||||
|
writer.WriteValue(metadata.Temporary);
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteEndDocument();
|
||||||
|
|
||||||
|
writer.Flush();
|
||||||
|
byte[] buffer = stream.GetBuffer();
|
||||||
|
|
||||||
|
response.Status = HttpStatusCode.OK;
|
||||||
|
response.ContentType = "application/xml";
|
||||||
|
response.ContentLength = stream.Length;
|
||||||
|
response.Body.Write(buffer, 0, (int)stream.Length);
|
||||||
|
response.Body.Flush();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.WarnFormat("Failed to fetch asset data or metadata for {0}: {1}", assetID, dataResponse);
|
||||||
|
response.Status = HttpStatusCode.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Unrecognized OpenSim asset request: " + request.Uri.PathAndQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
byte[] assetData = null;
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
|
||||||
|
Logger.Log.Debug("Handling OpenSim asset upload");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (XmlReader reader = XmlReader.Create(request.Body))
|
||||||
|
{
|
||||||
|
reader.MoveToContent();
|
||||||
|
reader.ReadStartElement("AssetBase");
|
||||||
|
|
||||||
|
reader.ReadStartElement("FullID");
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("Guid", String.Empty), out metadata.ID);
|
||||||
|
reader.ReadEndElement();
|
||||||
|
reader.ReadStartElement("ID");
|
||||||
|
reader.Skip();
|
||||||
|
reader.ReadEndElement();
|
||||||
|
|
||||||
|
// HACK: Broken on Mono. https://bugzilla.novell.com/show_bug.cgi?id=464229
|
||||||
|
//int readBytes = 0;
|
||||||
|
//byte[] buffer = new byte[1024];
|
||||||
|
//MemoryStream stream = new MemoryStream();
|
||||||
|
//BinaryWriter writer = new BinaryWriter(stream);
|
||||||
|
//while ((readBytes = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length)) > 0)
|
||||||
|
// writer.Write(buffer, 0, readBytes);
|
||||||
|
//writer.Flush();
|
||||||
|
//assetData = stream.GetBuffer();
|
||||||
|
//Array.Resize<byte>(ref assetData, (int)stream.Length);
|
||||||
|
|
||||||
|
assetData = Convert.FromBase64String(reader.ReadElementContentAsString());
|
||||||
|
|
||||||
|
int type;
|
||||||
|
Int32.TryParse(reader.ReadElementContentAsString("Type", String.Empty), out type);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(type);
|
||||||
|
metadata.Name = reader.ReadElementContentAsString("Name", String.Empty);
|
||||||
|
metadata.Description = reader.ReadElementContentAsString("Description", String.Empty);
|
||||||
|
Boolean.TryParse(reader.ReadElementContentAsString("Local", String.Empty), out metadata.Temporary);
|
||||||
|
Boolean.TryParse(reader.ReadElementContentAsString("Temporary", String.Empty), out metadata.Temporary);
|
||||||
|
|
||||||
|
reader.ReadEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (assetData != null && assetData.Length > 0)
|
||||||
|
{
|
||||||
|
metadata.SHA1 = OpenMetaverse.Utils.SHA1(assetData);
|
||||||
|
metadata.CreationDate = DateTime.Now;
|
||||||
|
|
||||||
|
BackendResponse storageResponse = server.StorageProvider.TryCreateAsset(metadata, assetData);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
response.Status = HttpStatusCode.Created;
|
||||||
|
else if (storageResponse == BackendResponse.NotFound)
|
||||||
|
response.Status = HttpStatusCode.NotFound;
|
||||||
|
else
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("AssetPostHandler called with no asset data");
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Failed to parse POST data (expecting AssetBase): " + ex.Message);
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Log.Debug("Finished handling OpenSim asset upload, Status: " + response.Status.ToString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,636 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.IO;
|
||||||
|
using System.Xml;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
using HttpServer;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class OpenSimInventoryFrontend : IExtension<AssetServer>
|
||||||
|
{
|
||||||
|
AssetServer server;
|
||||||
|
Utils.InventoryItemSerializer itemSerializer = new Utils.InventoryItemSerializer();
|
||||||
|
Utils.InventoryFolderSerializer folderSerializer = new Utils.InventoryFolderSerializer();
|
||||||
|
Utils.InventoryCollectionSerializer collectionSerializer = new Utils.InventoryCollectionSerializer();
|
||||||
|
|
||||||
|
public OpenSimInventoryFrontend()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/GetInventory/", GetInventoryHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/CreateInventory/", CreateInventoryHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/NewFolder/", NewFolderHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/UpdateFolder/", UpdateFolderHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/MoveFolder/", MoveFolderHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/PurgeFolder/", PurgeFolderHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/NewItem/", NewItemHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/DeleteItem/", DeleteItemHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/RootFolders/", RootFoldersHandler);
|
||||||
|
server.HttpServer.AddHandler("post", null, @"^/ActiveGestures/", ActiveGesturesHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetInventoryHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID sessionID, agentID;
|
||||||
|
UUID ownerID = DeserializeUUID(request.Body, out agentID, out sessionID);
|
||||||
|
|
||||||
|
if (ownerID != UUID.Zero)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("GetInventory is not scalable on some inventory backends, avoid calling it wherever possible");
|
||||||
|
|
||||||
|
Uri owner = Utils.GetOpenSimUri(ownerID);
|
||||||
|
InventoryCollection inventory;
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryFetchInventory(owner, out inventory);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
collectionSerializer.Serialize(response.Body, inventory);
|
||||||
|
response.Body.Flush();
|
||||||
|
}
|
||||||
|
else if (storageResponse == BackendResponse.NotFound)
|
||||||
|
{
|
||||||
|
// Return an empty inventory set to mimic OpenSim.Grid.InventoryServer.exe
|
||||||
|
inventory = new InventoryCollection();
|
||||||
|
inventory.UserID = ownerID;
|
||||||
|
inventory.Folders = new Dictionary<UUID, InventoryFolder>();
|
||||||
|
inventory.Items = new Dictionary<UUID, InventoryItem>();
|
||||||
|
collectionSerializer.Serialize(response.Body, inventory);
|
||||||
|
response.Body.Flush();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateInventoryHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID ownerID = DeserializeUUID(request.Body);
|
||||||
|
|
||||||
|
if (ownerID != UUID.Zero)
|
||||||
|
{
|
||||||
|
Uri owner = Utils.GetOpenSimUri(ownerID);
|
||||||
|
Logger.Log.DebugFormat("Created URI {0} for inventory creation", owner);
|
||||||
|
|
||||||
|
InventoryFolder rootFolder = new InventoryFolder("My Inventory", ownerID, UUID.Zero, (short)AssetType.Folder);
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryCreateInventory(owner, rootFolder);
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
CreateFolder("Animations", ownerID, rootFolder.ID, AssetType.Animation);
|
||||||
|
CreateFolder("Body Parts", ownerID, rootFolder.ID, AssetType.Bodypart);
|
||||||
|
CreateFolder("Calling Cards", ownerID, rootFolder.ID, AssetType.CallingCard);
|
||||||
|
CreateFolder("Clothing", ownerID, rootFolder.ID, AssetType.Clothing);
|
||||||
|
CreateFolder("Gestures", ownerID, rootFolder.ID, AssetType.Gesture);
|
||||||
|
CreateFolder("Landmarks", ownerID, rootFolder.ID, AssetType.Landmark);
|
||||||
|
CreateFolder("Lost and Found", ownerID, rootFolder.ID, AssetType.LostAndFoundFolder);
|
||||||
|
CreateFolder("Notecards", ownerID, rootFolder.ID, AssetType.Notecard);
|
||||||
|
CreateFolder("Objects", ownerID, rootFolder.ID, AssetType.Object);
|
||||||
|
CreateFolder("Photo Album", ownerID, rootFolder.ID, AssetType.SnapshotFolder);
|
||||||
|
CreateFolder("Scripts", ownerID, rootFolder.ID, AssetType.LSLText);
|
||||||
|
CreateFolder("Sounds", ownerID, rootFolder.ID, AssetType.Sound);
|
||||||
|
CreateFolder("Textures", ownerID, rootFolder.ID, AssetType.Texture);
|
||||||
|
CreateFolder("Trash", ownerID, rootFolder.ID, AssetType.TrashFolder);
|
||||||
|
|
||||||
|
SerializeBool(response.Body, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializeBool(response.Body, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID agentID, sessionID;
|
||||||
|
InventoryFolder folder = DeserializeFolder(request.Body, out agentID, out sessionID);
|
||||||
|
|
||||||
|
if (folder != null)
|
||||||
|
{
|
||||||
|
Uri owner = Utils.GetOpenSimUri(folder.Owner);
|
||||||
|
|
||||||
|
// Some calls that are moving or updating a folder instead of creating a new one
|
||||||
|
// will pass in an InventoryFolder without the name set. If this is the case we
|
||||||
|
// need to look up the name first
|
||||||
|
if (String.IsNullOrEmpty(folder.Name))
|
||||||
|
{
|
||||||
|
InventoryFolder oldFolder;
|
||||||
|
if (server.InventoryProvider.TryFetchFolder(owner, folder.ID, out oldFolder) == BackendResponse.Success)
|
||||||
|
folder.Name = oldFolder.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryCreateFolder(owner, folder);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
SerializeBool(response.Body, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializeBool(response.Body, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UpdateFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
return NewFolderHandler(client, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoveFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
return NewFolderHandler(client, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PurgeFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID agentID, sessionID;
|
||||||
|
InventoryFolder folder = DeserializeFolder(request.Body, out agentID, out sessionID);
|
||||||
|
|
||||||
|
if (folder != null)
|
||||||
|
{
|
||||||
|
Uri owner = Utils.GetOpenSimUri(folder.Owner);
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryPurgeFolder(owner, folder.ID);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
SerializeBool(response.Body, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializeBool(response.Body, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NewItemHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID agentID, sessionID;
|
||||||
|
InventoryItem item = DeserializeItem(request.Body, out agentID, out sessionID);
|
||||||
|
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
Uri owner = Utils.GetOpenSimUri(agentID);
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryCreateItem(owner, item);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
SerializeBool(response.Body, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializeBool(response.Body, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeleteItemHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID agentID, sessionID;
|
||||||
|
InventoryItem item = DeserializeItem(request.Body, out agentID, out sessionID);
|
||||||
|
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
Uri owner = Utils.GetOpenSimUri(item.Owner);
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryDeleteItem(owner, item.ID);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
SerializeBool(response.Body, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializeBool(response.Body, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RootFoldersHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID ownerID = DeserializeUUID(request.Body);
|
||||||
|
|
||||||
|
if (ownerID != UUID.Zero)
|
||||||
|
{
|
||||||
|
Uri owner = Utils.GetOpenSimUri(ownerID);
|
||||||
|
List<InventoryFolder> skeleton;
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryFetchFolderList(owner, out skeleton);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
SerializeFolderList(response.Body, skeleton);
|
||||||
|
}
|
||||||
|
else if (storageResponse == BackendResponse.NotFound)
|
||||||
|
{
|
||||||
|
// Return an empty set of inventory so the requester knows that
|
||||||
|
// an inventory needs to be created for this agent
|
||||||
|
SerializeFolderList(response.Body, new List<InventoryFolder>(0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActiveGesturesHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID ownerID = DeserializeUUID(request.Body);
|
||||||
|
|
||||||
|
if (ownerID != UUID.Zero)
|
||||||
|
{
|
||||||
|
Uri owner = Utils.GetOpenSimUri(ownerID);
|
||||||
|
List<InventoryItem> gestures;
|
||||||
|
BackendResponse storageResponse = server.InventoryProvider.TryFetchActiveGestures(owner, out gestures);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
SerializeItemList(response.Body, gestures);
|
||||||
|
}
|
||||||
|
else if (storageResponse == BackendResponse.NotFound)
|
||||||
|
{
|
||||||
|
// Return an empty set of gestures to match OpenSim.Grid.InventoryServer.exe behavior
|
||||||
|
SerializeItemList(response.Body, new List<InventoryItem>(0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackendResponse CreateFolder(string name, UUID ownerID, UUID parentID, AssetType assetType)
|
||||||
|
{
|
||||||
|
InventoryFolder folder = new InventoryFolder(name, ownerID, parentID, (short)assetType);
|
||||||
|
Uri owner = Utils.GetOpenSimUri(ownerID);
|
||||||
|
return server.InventoryProvider.TryCreateFolder(owner, folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID DeserializeUUID(Stream stream)
|
||||||
|
{
|
||||||
|
UUID id = UUID.Zero;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (XmlReader reader = XmlReader.Create(stream))
|
||||||
|
{
|
||||||
|
reader.MoveToContent();
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("guid", String.Empty), out id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Failed to parse POST data (expecting guid): " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID DeserializeUUID(Stream stream, out UUID agentID, out UUID sessionID)
|
||||||
|
{
|
||||||
|
UUID id;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (XmlReader reader = XmlReader.Create(stream))
|
||||||
|
{
|
||||||
|
reader.MoveToContent();
|
||||||
|
reader.ReadStartElement("RestSessionObjectOfGuid");
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("SessionID", String.Empty), out sessionID);
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("AvatarID", String.Empty), out agentID);
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("Body", String.Empty), out id);
|
||||||
|
reader.ReadEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Failed to parse GetInventory POST data: " + ex.Message);
|
||||||
|
agentID = UUID.Zero;
|
||||||
|
sessionID = UUID.Zero;
|
||||||
|
return UUID.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryFolder DeserializeFolder(Stream stream, out UUID agentID, out UUID sessionID)
|
||||||
|
{
|
||||||
|
InventoryFolder folder = new InventoryFolder();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (XmlReader reader = XmlReader.Create(stream))
|
||||||
|
{
|
||||||
|
reader.MoveToContent();
|
||||||
|
reader.ReadStartElement("RestSessionObjectOfInventoryFolderBase");
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("SessionID", String.Empty), out sessionID);
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("AvatarID", String.Empty), out agentID);
|
||||||
|
reader.ReadStartElement("Body");
|
||||||
|
if (reader.Name == "Name")
|
||||||
|
folder.Name = reader.ReadElementContentAsString("Name", String.Empty);
|
||||||
|
else
|
||||||
|
folder.Name = String.Empty;
|
||||||
|
ReadUUID(reader, "Owner", out folder.Owner);
|
||||||
|
ReadUUID(reader, "ParentID", out folder.ParentID);
|
||||||
|
ReadUUID(reader, "ID", out folder.ID);
|
||||||
|
Int16.TryParse(reader.ReadElementContentAsString("Type", String.Empty), out folder.Type);
|
||||||
|
UInt16.TryParse(reader.ReadElementContentAsString("Version", String.Empty), out folder.Version);
|
||||||
|
reader.ReadEndElement();
|
||||||
|
reader.ReadEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Failed to parse POST data (expecting InventoryFolderBase): " + ex.Message);
|
||||||
|
agentID = UUID.Zero;
|
||||||
|
sessionID = UUID.Zero;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
InventoryItem DeserializeItem(Stream stream, out UUID agentID, out UUID sessionID)
|
||||||
|
{
|
||||||
|
InventoryItem item = new InventoryItem();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (XmlReader reader = XmlReader.Create(stream))
|
||||||
|
{
|
||||||
|
reader.MoveToContent();
|
||||||
|
reader.ReadStartElement("RestSessionObjectOfInventoryItemBase");
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("SessionID", String.Empty), out sessionID);
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("AvatarID", String.Empty), out agentID);
|
||||||
|
reader.ReadStartElement("Body");
|
||||||
|
ReadUUID(reader, "ID", out item.ID);
|
||||||
|
Int32.TryParse(reader.ReadElementContentAsString("InvType", String.Empty), out item.InvType);
|
||||||
|
ReadUUID(reader, "Folder", out item.Folder);
|
||||||
|
ReadUUID(reader, "Owner", out item.Owner);
|
||||||
|
ReadUUID(reader, "Creator", out item.Creator);
|
||||||
|
item.Name = reader.ReadElementContentAsString("Name", String.Empty);
|
||||||
|
item.Description = reader.ReadElementContentAsString("Description", String.Empty);
|
||||||
|
UInt32.TryParse(reader.ReadElementContentAsString("NextPermissions", String.Empty), out item.NextPermissions);
|
||||||
|
UInt32.TryParse(reader.ReadElementContentAsString("CurrentPermissions", String.Empty), out item.CurrentPermissions);
|
||||||
|
UInt32.TryParse(reader.ReadElementContentAsString("BasePermissions", String.Empty), out item.BasePermissions);
|
||||||
|
UInt32.TryParse(reader.ReadElementContentAsString("EveryOnePermissions", String.Empty), out item.EveryOnePermissions);
|
||||||
|
UInt32.TryParse(reader.ReadElementContentAsString("GroupPermissions", String.Empty), out item.GroupPermissions);
|
||||||
|
Int32.TryParse(reader.ReadElementContentAsString("AssetType", String.Empty), out item.AssetType);
|
||||||
|
ReadUUID(reader, "AssetID", out item.AssetID);
|
||||||
|
ReadUUID(reader, "GroupID", out item.GroupID);
|
||||||
|
Boolean.TryParse(reader.ReadElementContentAsString("GroupOwned", String.Empty), out item.GroupOwned);
|
||||||
|
Int32.TryParse(reader.ReadElementContentAsString("SalePrice", String.Empty), out item.SalePrice);
|
||||||
|
Byte.TryParse(reader.ReadElementContentAsString("SaleType", String.Empty), out item.SaleType);
|
||||||
|
UInt32.TryParse(reader.ReadElementContentAsString("Flags", String.Empty), out item.Flags);
|
||||||
|
Int32.TryParse(reader.ReadElementContentAsString("CreationDate", String.Empty), out item.CreationDate);
|
||||||
|
reader.ReadEndElement();
|
||||||
|
reader.ReadEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Failed to parse POST data (expecting InventoryItemBase): " + ex.Message);
|
||||||
|
agentID = UUID.Zero;
|
||||||
|
sessionID = UUID.Zero;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerializeBool(Stream stream, bool value)
|
||||||
|
{
|
||||||
|
using (XmlWriter writer = XmlWriter.Create(stream))
|
||||||
|
{
|
||||||
|
writer.WriteStartDocument();
|
||||||
|
writer.WriteStartElement("boolean");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
|
||||||
|
writer.WriteString(value.ToString().ToLower());
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteEndDocument();
|
||||||
|
writer.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerializeFolderList(Stream stream, List<InventoryFolder> folders)
|
||||||
|
{
|
||||||
|
using (XmlWriter writer = XmlWriter.Create(stream))
|
||||||
|
{
|
||||||
|
writer.WriteStartDocument();
|
||||||
|
writer.WriteStartElement("ArrayOfInventoryFolderBase");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
|
||||||
|
|
||||||
|
if (folders != null)
|
||||||
|
{
|
||||||
|
foreach (InventoryFolder folder in folders)
|
||||||
|
{
|
||||||
|
writer.WriteStartElement("InventoryFolderBase");
|
||||||
|
writer.WriteElementString("Name", folder.Name);
|
||||||
|
WriteUUID(writer, "Owner", folder.Owner);
|
||||||
|
WriteUUID(writer, "ParentID", folder.ParentID);
|
||||||
|
WriteUUID(writer, "ID", folder.ID);
|
||||||
|
writer.WriteElementString("Type", XmlConvert.ToString(folder.Type));
|
||||||
|
writer.WriteElementString("Version", XmlConvert.ToString(folder.Version));
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteEndDocument();
|
||||||
|
|
||||||
|
writer.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerializeItemList(Stream stream, List<InventoryItem> items)
|
||||||
|
{
|
||||||
|
using (XmlWriter writer = XmlWriter.Create(stream))
|
||||||
|
{
|
||||||
|
writer.WriteStartDocument();
|
||||||
|
writer.WriteStartElement("ArrayOfInventoryItemBase");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
|
||||||
|
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
|
||||||
|
|
||||||
|
if (items != null)
|
||||||
|
{
|
||||||
|
foreach (InventoryItem item in items)
|
||||||
|
{
|
||||||
|
writer.WriteStartElement("InventoryItemBase");
|
||||||
|
WriteUUID(writer, "ID", item.ID);
|
||||||
|
writer.WriteElementString("InvType", XmlConvert.ToString(item.InvType));
|
||||||
|
WriteUUID(writer, "Folder", item.Folder);
|
||||||
|
WriteUUID(writer, "Owner", item.Owner);
|
||||||
|
WriteUUID(writer, "Creator", item.Creator);
|
||||||
|
writer.WriteElementString("Name", item.Name);
|
||||||
|
writer.WriteElementString("Description", item.Description);
|
||||||
|
writer.WriteElementString("NextPermissions", XmlConvert.ToString(item.NextPermissions));
|
||||||
|
writer.WriteElementString("CurrentPermissions", XmlConvert.ToString(item.CurrentPermissions));
|
||||||
|
writer.WriteElementString("BasePermissions", XmlConvert.ToString(item.BasePermissions));
|
||||||
|
writer.WriteElementString("EveryOnePermissions", XmlConvert.ToString(item.EveryOnePermissions));
|
||||||
|
writer.WriteElementString("GroupPermissions", XmlConvert.ToString(item.GroupPermissions));
|
||||||
|
writer.WriteElementString("AssetType", XmlConvert.ToString(item.AssetType));
|
||||||
|
WriteUUID(writer, "AssetID", item.AssetID);
|
||||||
|
WriteUUID(writer, "GroupID", item.GroupID);
|
||||||
|
writer.WriteElementString("GroupOwned", XmlConvert.ToString(item.GroupOwned));
|
||||||
|
writer.WriteElementString("SalePrice", XmlConvert.ToString(item.SalePrice));
|
||||||
|
writer.WriteElementString("SaleType", XmlConvert.ToString(item.SaleType));
|
||||||
|
writer.WriteElementString("Flags", XmlConvert.ToString(item.Flags));
|
||||||
|
writer.WriteElementString("CreationDate", XmlConvert.ToString(item.CreationDate));
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndElement();
|
||||||
|
writer.WriteEndDocument();
|
||||||
|
|
||||||
|
writer.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteUUID(XmlWriter writer, string name, UUID id)
|
||||||
|
{
|
||||||
|
writer.WriteStartElement(name);
|
||||||
|
writer.WriteElementString("Guid", XmlConvert.ToString(id.Guid));
|
||||||
|
writer.WriteEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadUUID(XmlReader reader, string name, out UUID id)
|
||||||
|
{
|
||||||
|
reader.ReadStartElement(name);
|
||||||
|
UUID.TryParse(reader.ReadElementContentAsString("Guid", String.Empty), out id);
|
||||||
|
reader.ReadEndElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region OpenSim AssetType
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The different types of grid assets
|
||||||
|
/// </summary>
|
||||||
|
public enum AssetType : sbyte
|
||||||
|
{
|
||||||
|
/// <summary>Unknown asset type</summary>
|
||||||
|
Unknown = -1,
|
||||||
|
/// <summary>Texture asset, stores in JPEG2000 J2C stream format</summary>
|
||||||
|
Texture = 0,
|
||||||
|
/// <summary>Sound asset</summary>
|
||||||
|
Sound = 1,
|
||||||
|
/// <summary>Calling card for another avatar</summary>
|
||||||
|
CallingCard = 2,
|
||||||
|
/// <summary>Link to a location in world</summary>
|
||||||
|
Landmark = 3,
|
||||||
|
// <summary>Legacy script asset, you should never see one of these</summary>
|
||||||
|
//[Obsolete]
|
||||||
|
//Script = 4,
|
||||||
|
/// <summary>Collection of textures and parameters that can be
|
||||||
|
/// worn by an avatar</summary>
|
||||||
|
Clothing = 5,
|
||||||
|
/// <summary>Primitive that can contain textures, sounds,
|
||||||
|
/// scripts and more</summary>
|
||||||
|
Object = 6,
|
||||||
|
/// <summary>Notecard asset</summary>
|
||||||
|
Notecard = 7,
|
||||||
|
/// <summary>Holds a collection of inventory items</summary>
|
||||||
|
Folder = 8,
|
||||||
|
/// <summary>Root inventory folder</summary>
|
||||||
|
RootFolder = 9,
|
||||||
|
/// <summary>Linden scripting language script</summary>
|
||||||
|
LSLText = 10,
|
||||||
|
/// <summary>LSO bytecode for a script</summary>
|
||||||
|
LSLBytecode = 11,
|
||||||
|
/// <summary>Uncompressed TGA texture</summary>
|
||||||
|
TextureTGA = 12,
|
||||||
|
/// <summary>Collection of textures and shape parameters that can
|
||||||
|
/// be worn</summary>
|
||||||
|
Bodypart = 13,
|
||||||
|
/// <summary>Trash folder</summary>
|
||||||
|
TrashFolder = 14,
|
||||||
|
/// <summary>Snapshot folder</summary>
|
||||||
|
SnapshotFolder = 15,
|
||||||
|
/// <summary>Lost and found folder</summary>
|
||||||
|
LostAndFoundFolder = 16,
|
||||||
|
/// <summary>Uncompressed sound</summary>
|
||||||
|
SoundWAV = 17,
|
||||||
|
/// <summary>Uncompressed TGA non-square image, not to be used as a
|
||||||
|
/// texture</summary>
|
||||||
|
ImageTGA = 18,
|
||||||
|
/// <summary>Compressed JPEG non-square image, not to be used as a
|
||||||
|
/// texture</summary>
|
||||||
|
ImageJPEG = 19,
|
||||||
|
/// <summary>Animation</summary>
|
||||||
|
Animation = 20,
|
||||||
|
/// <summary>Sequence of animations, sounds, chat, and pauses</summary>
|
||||||
|
Gesture = 21,
|
||||||
|
/// <summary>Simstate file</summary>
|
||||||
|
Simstate = 22,
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion OpenSim AssetType
|
||||||
|
}
|
|
@ -0,0 +1,804 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Data;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using ExtensionLoader.Config;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class OpenSimMySQLInventory : IExtension<AssetServer>, IInventoryProvider
|
||||||
|
{
|
||||||
|
const string EXTENSION_NAME = "OpenSimMySQLInventory"; // Used in metrics reporting
|
||||||
|
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public OpenSimMySQLInventory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Required Interfaces
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
Logger.Log.Info("Connected to MySQL inventory backend: " + dbConnection.ServerVersion);
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL inventory backend failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItem item)
|
||||||
|
{
|
||||||
|
item = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," +
|
||||||
|
"inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," +
|
||||||
|
"creationDate,groupID,groupOwned,flags,avatarID,parentFolderID,inventoryGroupPermissions FROM inventoryitems WHERE inventoryID='{0}'",
|
||||||
|
itemID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
item = new InventoryItem();
|
||||||
|
item.ID = itemID;
|
||||||
|
item.AssetID = UUID.Parse(reader.GetString(0));
|
||||||
|
item.AssetType = reader.GetInt32(1);
|
||||||
|
item.Name = reader.GetString(2);
|
||||||
|
item.Description = reader.GetString(3);
|
||||||
|
item.NextPermissions = (uint)reader.GetInt32(4);
|
||||||
|
item.CurrentPermissions = (uint)reader.GetInt32(5);
|
||||||
|
item.InvType = reader.GetInt32(6);
|
||||||
|
item.Creator = UUID.Parse(reader.GetString(7));
|
||||||
|
item.BasePermissions = (uint)reader.GetInt32(8);
|
||||||
|
item.EveryOnePermissions = (uint)reader.GetInt32(9);
|
||||||
|
item.SalePrice = reader.GetInt32(10);
|
||||||
|
item.SaleType = reader.GetByte(11);
|
||||||
|
item.CreationDate = reader.GetInt32(12);
|
||||||
|
item.GroupID = UUID.Parse(reader.GetString(13));
|
||||||
|
item.GroupOwned = reader.GetBoolean(14);
|
||||||
|
item.Flags = (uint)reader.GetInt32(15);
|
||||||
|
item.Owner = UUID.Parse(reader.GetString(16));
|
||||||
|
item.Folder = UUID.Parse(reader.GetString(17));
|
||||||
|
item.GroupPermissions = (uint)reader.GetInt32(18);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolder folder)
|
||||||
|
{
|
||||||
|
folder = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT folderName,type,version,agentID,parentFolderID FROM inventoryfolders WHERE folderID='{0}'",
|
||||||
|
folderID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
folder = new InventoryFolder();
|
||||||
|
folder.Children = null; // This call only returns data for the folder itself, no children data
|
||||||
|
folder.ID = folderID;
|
||||||
|
folder.Name = reader.GetString(0);
|
||||||
|
folder.Type = reader.GetInt16(1);
|
||||||
|
folder.Version = (ushort)reader.GetInt16(2);
|
||||||
|
folder.Owner = UUID.Parse(reader.GetString(3));
|
||||||
|
folder.ParentID = UUID.Parse(reader.GetString(4));
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents)
|
||||||
|
{
|
||||||
|
contents = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
contents = new InventoryCollection();
|
||||||
|
|
||||||
|
#region Folder retrieval
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT folderName,type,version,agentID,folderID FROM inventoryfolders WHERE parentFolderID='{0}'",
|
||||||
|
folderID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
contents.Folders = new Dictionary<UUID, InventoryFolder>();
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
InventoryFolder folder = new InventoryFolder();
|
||||||
|
folder.ParentID = folderID;
|
||||||
|
folder.Children = null; // This call doesn't do recursion
|
||||||
|
folder.Name = reader.GetString(0);
|
||||||
|
folder.Type = reader.GetInt16(1);
|
||||||
|
folder.Version = (ushort)reader.GetInt16(2);
|
||||||
|
folder.Owner = UUID.Parse(reader.GetString(3));
|
||||||
|
folder.ID = UUID.Parse(reader.GetString(4));
|
||||||
|
|
||||||
|
contents.Folders.Add(folder.ID, folder);
|
||||||
|
contents.UserID = folder.Owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Close();
|
||||||
|
|
||||||
|
#endregion Folder retrieval
|
||||||
|
|
||||||
|
#region Item retrieval
|
||||||
|
|
||||||
|
command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," +
|
||||||
|
"inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," +
|
||||||
|
"creationDate,groupID,groupOwned,flags,avatarID,inventoryID,inventoryGroupPermissions FROM inventoryitems WHERE parentFolderID='{0}'",
|
||||||
|
folderID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
contents.Items = new Dictionary<UUID, InventoryItem>();
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
InventoryItem item = new InventoryItem();
|
||||||
|
item.Folder = folderID;
|
||||||
|
item.AssetID = UUID.Parse(reader.GetString(0));
|
||||||
|
item.AssetType = reader.GetInt32(1);
|
||||||
|
item.Name = reader.GetString(2);
|
||||||
|
item.Description = reader.GetString(3);
|
||||||
|
item.NextPermissions = (uint)reader.GetInt32(4);
|
||||||
|
item.CurrentPermissions = (uint)reader.GetInt32(5);
|
||||||
|
item.InvType = reader.GetInt32(6);
|
||||||
|
item.Creator = UUID.Parse(reader.GetString(7));
|
||||||
|
item.BasePermissions = (uint)reader.GetInt32(8);
|
||||||
|
item.EveryOnePermissions = (uint)reader.GetInt32(9);
|
||||||
|
item.SalePrice = reader.GetInt32(10);
|
||||||
|
item.SaleType = reader.GetByte(11);
|
||||||
|
item.CreationDate = reader.GetInt32(12);
|
||||||
|
item.GroupID = UUID.Parse(reader.GetString(13));
|
||||||
|
item.GroupOwned = reader.GetBoolean(14);
|
||||||
|
item.Flags = (uint)reader.GetInt32(15);
|
||||||
|
item.Owner = UUID.Parse(reader.GetString(16));
|
||||||
|
item.ID = UUID.Parse(reader.GetString(17));
|
||||||
|
item.GroupPermissions = (uint)reader.GetInt32(18);
|
||||||
|
|
||||||
|
contents.Items.Add(item.ID, item);
|
||||||
|
contents.UserID = item.Owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Item retrieval
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchFolderContents(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolder> folders)
|
||||||
|
{
|
||||||
|
folders = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
UUID ownerID;
|
||||||
|
|
||||||
|
if (Utils.TryGetOpenSimUUID(owner, out ownerID))
|
||||||
|
{
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
folders = new List<InventoryFolder>();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT folderName,type,version,folderID,parentFolderID FROM inventoryfolders WHERE agentID='{0}'",
|
||||||
|
ownerID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
InventoryFolder folder = new InventoryFolder();
|
||||||
|
folder.Owner = ownerID;
|
||||||
|
folder.Children = null; // This call does not create a folder hierarchy
|
||||||
|
folder.Name = reader.GetString(0);
|
||||||
|
folder.Type = reader.GetInt16(1);
|
||||||
|
folder.Version = (ushort)reader.GetInt16(2);
|
||||||
|
folder.ID = UUID.Parse(reader.GetString(3));
|
||||||
|
folder.ParentID = UUID.Parse(reader.GetString(4));
|
||||||
|
|
||||||
|
folders.Add(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchFolderList(EXTENSION_NAME, ret, owner, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory)
|
||||||
|
{
|
||||||
|
inventory = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
List<InventoryFolder> folders;
|
||||||
|
UUID ownerID;
|
||||||
|
|
||||||
|
ret = TryFetchFolderList(owner, out folders);
|
||||||
|
|
||||||
|
if (ret == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
// Add the retrieved folders to the inventory collection
|
||||||
|
inventory = new InventoryCollection();
|
||||||
|
inventory.Folders = new Dictionary<UUID, InventoryFolder>(folders.Count);
|
||||||
|
foreach (InventoryFolder folder in folders)
|
||||||
|
inventory.Folders[folder.ID] = folder;
|
||||||
|
|
||||||
|
// Fetch inventory items
|
||||||
|
if (Utils.TryGetOpenSimUUID(owner, out ownerID))
|
||||||
|
{
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," +
|
||||||
|
"inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," +
|
||||||
|
"creationDate,groupID,groupOwned,flags,inventoryID,parentFolderID,inventoryGroupPermissions FROM inventoryitems WHERE " +
|
||||||
|
"avatarID='{0}'", ownerID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
inventory.UserID = ownerID;
|
||||||
|
inventory.Items = new Dictionary<UUID, InventoryItem>();
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
InventoryItem item = new InventoryItem();
|
||||||
|
item.Owner = ownerID;
|
||||||
|
item.AssetID = UUID.Parse(reader.GetString(0));
|
||||||
|
item.AssetType = reader.GetInt32(1);
|
||||||
|
item.Name = reader.GetString(2);
|
||||||
|
item.Description = reader.GetString(3);
|
||||||
|
item.NextPermissions = (uint)reader.GetInt32(4);
|
||||||
|
item.CurrentPermissions = (uint)reader.GetInt32(5);
|
||||||
|
item.InvType = reader.GetInt32(6);
|
||||||
|
item.Creator = UUID.Parse(reader.GetString(7));
|
||||||
|
item.BasePermissions = (uint)reader.GetInt32(8);
|
||||||
|
item.EveryOnePermissions = (uint)reader.GetInt32(9);
|
||||||
|
item.SalePrice = reader.GetInt32(10);
|
||||||
|
item.SaleType = reader.GetByte(11);
|
||||||
|
item.CreationDate = reader.GetInt32(12);
|
||||||
|
item.GroupID = UUID.Parse(reader.GetString(13));
|
||||||
|
item.GroupOwned = reader.GetBoolean(14);
|
||||||
|
item.Flags = (uint)reader.GetInt32(15);
|
||||||
|
item.ID = UUID.Parse(reader.GetString(16));
|
||||||
|
item.Folder = UUID.Parse(reader.GetString(17));
|
||||||
|
item.GroupPermissions = (uint)reader.GetInt32(18);
|
||||||
|
|
||||||
|
inventory.Items.Add(item.ID, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchInventory(EXTENSION_NAME, ret, owner, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItem> gestures)
|
||||||
|
{
|
||||||
|
gestures = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
UUID ownerID;
|
||||||
|
|
||||||
|
if (Utils.TryGetOpenSimUUID(owner, out ownerID))
|
||||||
|
{
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand("SELECT assetID,inventoryName,inventoryDescription,inventoryNextPermissions," +
|
||||||
|
"inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," +
|
||||||
|
"creationDate,groupID,groupOwned,inventoryID,parentFolderID,inventoryGroupPermissions FROM inventoryitems WHERE " +
|
||||||
|
"avatarId=?uuid AND assetType=?type AND flags=1", dbConnection);
|
||||||
|
command.Parameters.AddWithValue("?uuid", ownerID.ToString());
|
||||||
|
command.Parameters.AddWithValue("?type", (int)AssetType.Gesture);
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
InventoryItem item = new InventoryItem();
|
||||||
|
item.Owner = ownerID;
|
||||||
|
item.AssetType = (int)AssetType.Gesture;
|
||||||
|
item.Flags = (uint)1;
|
||||||
|
item.AssetID = UUID.Parse(reader.GetString(0));
|
||||||
|
item.Name = reader.GetString(1);
|
||||||
|
item.Description = reader.GetString(2);
|
||||||
|
item.NextPermissions = (uint)reader.GetInt32(3);
|
||||||
|
item.CurrentPermissions = (uint)reader.GetInt32(4);
|
||||||
|
item.InvType = reader.GetInt32(5);
|
||||||
|
item.Creator = UUID.Parse(reader.GetString(6));
|
||||||
|
item.BasePermissions = (uint)reader.GetInt32(7);
|
||||||
|
item.EveryOnePermissions = (uint)reader.GetInt32(8);
|
||||||
|
item.SalePrice = reader.GetInt32(9);
|
||||||
|
item.SaleType = reader.GetByte(10);
|
||||||
|
item.CreationDate = reader.GetInt32(11);
|
||||||
|
item.GroupID = UUID.Parse(reader.GetString(12));
|
||||||
|
item.GroupOwned = reader.GetBoolean(13);
|
||||||
|
item.ID = UUID.Parse(reader.GetString(14));
|
||||||
|
item.Folder = UUID.Parse(reader.GetString(15));
|
||||||
|
item.GroupPermissions = (uint)reader.GetInt32(16);
|
||||||
|
|
||||||
|
gestures.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchActiveGestures(EXTENSION_NAME, ret, owner, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateItem(Uri owner, InventoryItem item)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand(
|
||||||
|
"REPLACE INTO inventoryitems (assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," +
|
||||||
|
"inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," +
|
||||||
|
"creationDate,groupID,groupOwned,flags,inventoryID,avatarID,parentFolderID,inventoryGroupPermissions) VALUES " +
|
||||||
|
|
||||||
|
"(?assetID,?assetType,?inventoryName,?inventoryDescription,?inventoryNextPermissions,?inventoryCurrentPermissions,?invType," +
|
||||||
|
"?creatorID,?inventoryBasePermissions,?inventoryEveryOnePermissions,?salePrice,?saleType,?creationDate,?groupID,?groupOwned," +
|
||||||
|
"?flags,?inventoryID,?avatarID,?parentFolderID,?inventoryGroupPermissions)", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?assetID", item.AssetID.ToString());
|
||||||
|
command.Parameters.AddWithValue("?assetType", item.AssetType);
|
||||||
|
command.Parameters.AddWithValue("?inventoryName", item.Name);
|
||||||
|
command.Parameters.AddWithValue("?inventoryDescription", item.Description);
|
||||||
|
command.Parameters.AddWithValue("?inventoryNextPermissions", item.NextPermissions);
|
||||||
|
command.Parameters.AddWithValue("?inventoryCurrentPermissions", item.CurrentPermissions);
|
||||||
|
command.Parameters.AddWithValue("?invType", item.InvType);
|
||||||
|
command.Parameters.AddWithValue("?creatorID", item.Creator.ToString());
|
||||||
|
command.Parameters.AddWithValue("?inventoryBasePermissions", item.BasePermissions);
|
||||||
|
command.Parameters.AddWithValue("?inventoryEveryOnePermissions", item.EveryOnePermissions);
|
||||||
|
command.Parameters.AddWithValue("?salePrice", item.SalePrice);
|
||||||
|
command.Parameters.AddWithValue("?saleType", item.SaleType);
|
||||||
|
command.Parameters.AddWithValue("?creationDate", item.CreationDate);
|
||||||
|
command.Parameters.AddWithValue("?groupID", item.GroupID.ToString());
|
||||||
|
command.Parameters.AddWithValue("?groupOwned", item.GroupOwned);
|
||||||
|
command.Parameters.AddWithValue("?flags", item.Flags);
|
||||||
|
command.Parameters.AddWithValue("?inventoryID", item.ID);
|
||||||
|
command.Parameters.AddWithValue("?avatarID", item.Owner);
|
||||||
|
command.Parameters.AddWithValue("?parentFolderID", item.Folder);
|
||||||
|
command.Parameters.AddWithValue("?inventoryGroupPermissions", item.GroupPermissions);
|
||||||
|
|
||||||
|
int rowsAffected = command.ExecuteNonQuery();
|
||||||
|
if (rowsAffected == 1)
|
||||||
|
{
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else if (rowsAffected == 2)
|
||||||
|
{
|
||||||
|
Logger.Log.Info("Replaced inventory item " + item.ID.ToString());
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, false, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateFolder(Uri owner, InventoryFolder folder)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand(
|
||||||
|
"REPLACE INTO inventoryfolders (folderName,type,version,folderID,agentID,parentFolderID) VALUES " +
|
||||||
|
"(?folderName,?type,?version,?folderID,?agentID,?parentFolderID)", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?folderName", folder.Name);
|
||||||
|
command.Parameters.AddWithValue("?type", folder.Type);
|
||||||
|
command.Parameters.AddWithValue("?version", folder.Version);
|
||||||
|
command.Parameters.AddWithValue("?folderID", folder.ID);
|
||||||
|
command.Parameters.AddWithValue("?agentID", folder.Owner);
|
||||||
|
command.Parameters.AddWithValue("?parentFolderID", folder.ParentID);
|
||||||
|
|
||||||
|
int rowsAffected = command.ExecuteNonQuery();
|
||||||
|
if (rowsAffected == 1)
|
||||||
|
{
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else if (rowsAffected == 2)
|
||||||
|
{
|
||||||
|
Logger.Log.Info("Replaced inventory folder " + folder.ID.ToString());
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, true, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateInventory(Uri owner, InventoryFolder rootFolder)
|
||||||
|
{
|
||||||
|
return TryCreateFolder(owner, rootFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryDeleteItem(Uri owner, UUID itemID)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
UUID ownerID;
|
||||||
|
|
||||||
|
if (Utils.TryGetOpenSimUUID(owner, out ownerID))
|
||||||
|
{
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand(
|
||||||
|
"DELETE FROM inventoryitems WHERE inventoryID=?inventoryID AND avatarID=?avatarID", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?inventoryID", itemID.ToString());
|
||||||
|
command.Parameters.AddWithValue("?avatarID", ownerID.ToString());
|
||||||
|
|
||||||
|
int rowsAffected = command.ExecuteNonQuery();
|
||||||
|
if (rowsAffected == 1)
|
||||||
|
{
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("MySQL DELETE query affected {0} rows", rowsAffected);
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryDeleteFolder(Uri owner, UUID folderID)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
UUID ownerID;
|
||||||
|
|
||||||
|
if (Utils.TryGetOpenSimUUID(owner, out ownerID))
|
||||||
|
{
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand(
|
||||||
|
"DELETE FROM inventoryfolders WHERE folderID=?folderID AND agentID=?agentID", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?folderID", folderID.ToString());
|
||||||
|
command.Parameters.AddWithValue("?agentID", ownerID.ToString());
|
||||||
|
|
||||||
|
int rowsAffected = command.ExecuteNonQuery();
|
||||||
|
if (rowsAffected == 1)
|
||||||
|
{
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("MySQL DELETE query affected {0} rows", rowsAffected);
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryPurgeFolder(Uri owner, UUID folderID)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
UUID ownerID;
|
||||||
|
|
||||||
|
if (Utils.TryGetOpenSimUUID(owner, out ownerID))
|
||||||
|
{
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
#region Delete items
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand(
|
||||||
|
"DELETE FROM inventoryitems WHERE parentFolderID=?parentFolderID AND avatarID=?avatarID", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?parentFolderID", folderID.ToString());
|
||||||
|
command.Parameters.AddWithValue("?avatarID", ownerID.ToString());
|
||||||
|
|
||||||
|
int rowsAffected = command.ExecuteNonQuery();
|
||||||
|
|
||||||
|
#endregion Delete items
|
||||||
|
|
||||||
|
#region Delete folders
|
||||||
|
|
||||||
|
command = new MySqlCommand(
|
||||||
|
"DELETE FROM inventoryfolders WHERE parentFolderID=?parentFolderID AND agentID=?agentID", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?parentFolderID", folderID.ToString());
|
||||||
|
command.Parameters.AddWithValue("?agentID", ownerID.ToString());
|
||||||
|
|
||||||
|
rowsAffected += command.ExecuteNonQuery();
|
||||||
|
|
||||||
|
#endregion Delete folders
|
||||||
|
|
||||||
|
Logger.Log.DebugFormat("Deleted {0} inventory objects from MySQL in a folder purge", rowsAffected);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryPurgeFolder(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ForEach(Action<Metadata> action, int start, int count)
|
||||||
|
{
|
||||||
|
int rowCount = 0;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
MySqlDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT name,description,assetType,temporary,data,id FROM assets LIMIT {0}, {1}",
|
||||||
|
start, count);
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.CreationDate = OpenMetaverse.Utils.Epoch;
|
||||||
|
metadata.Description = reader.GetString(1);
|
||||||
|
metadata.ID = UUID.Parse(reader.GetString(5));
|
||||||
|
metadata.Name = reader.GetString(0);
|
||||||
|
metadata.SHA1 = OpenMetaverse.Utils.SHA1((byte[])reader.GetValue(4));
|
||||||
|
metadata.Temporary = reader.GetBoolean(3);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2));
|
||||||
|
|
||||||
|
action(metadata);
|
||||||
|
++rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Required Interfaces
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Data;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using ExtensionLoader.Config;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class OpenSimMySQLStorage : IExtension<AssetServer>, IStorageProvider
|
||||||
|
{
|
||||||
|
const string EXTENSION_NAME = "OpenSimMySQLStorage"; // Used in metrics reporting
|
||||||
|
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public OpenSimMySQLStorage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Required Interfaces
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
Logger.Log.Info("Connected to MySQL storage backend: " + dbConnection.ServerVersion);
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL storage backend failed: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata)
|
||||||
|
{
|
||||||
|
metadata = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT name,description,assetType,temporary FROM assets WHERE id='{0}'", assetID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
metadata = new Metadata();
|
||||||
|
metadata.CreationDate = OpenMetaverse.Utils.Epoch;
|
||||||
|
metadata.SHA1 = null;
|
||||||
|
metadata.ID = assetID;
|
||||||
|
metadata.Name = reader.GetString(0);
|
||||||
|
metadata.Description = reader.GetString(1);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2));
|
||||||
|
metadata.Temporary = reader.GetBoolean(3);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchData(UUID assetID, out byte[] assetData)
|
||||||
|
{
|
||||||
|
assetData = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT data FROM assets WHERE id='{0}'", assetID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
assetData = (byte[])reader.GetValue(0);
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData)
|
||||||
|
{
|
||||||
|
metadata = null;
|
||||||
|
assetData = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT name,description,assetType,temporary,data FROM assets WHERE id='{0}'", assetID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
metadata = new Metadata();
|
||||||
|
metadata.CreationDate = OpenMetaverse.Utils.Epoch;
|
||||||
|
metadata.SHA1 = null;
|
||||||
|
metadata.ID = assetID;
|
||||||
|
metadata.Name = reader.GetString(0);
|
||||||
|
metadata.Description = reader.GetString(1);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2));
|
||||||
|
metadata.Temporary = reader.GetBoolean(3);
|
||||||
|
|
||||||
|
assetData = (byte[])reader.GetValue(4);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now);
|
||||||
|
server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID)
|
||||||
|
{
|
||||||
|
assetID = metadata.ID = UUID.Random();
|
||||||
|
return TryCreateAsset(metadata, assetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand(
|
||||||
|
"REPLACE INTO assets (name,description,assetType,local,temporary,data,id) VALUES " +
|
||||||
|
"(?name,?description,?assetType,?local,?temporary,?data,?id)", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?name", metadata.Name);
|
||||||
|
command.Parameters.AddWithValue("?description", metadata.Description);
|
||||||
|
command.Parameters.AddWithValue("?assetType", Utils.ContentTypeToSLAssetType(metadata.ContentType));
|
||||||
|
command.Parameters.AddWithValue("?local", 0);
|
||||||
|
command.Parameters.AddWithValue("?temporary", metadata.Temporary);
|
||||||
|
command.Parameters.AddWithValue("?data", assetData);
|
||||||
|
command.Parameters.AddWithValue("?id", metadata.ID.ToString());
|
||||||
|
|
||||||
|
int rowsAffected = command.ExecuteNonQuery();
|
||||||
|
if (rowsAffected == 1)
|
||||||
|
{
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else if (rowsAffected == 2)
|
||||||
|
{
|
||||||
|
Logger.Log.Info("Replaced asset " + metadata.ID.ToString());
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetCreate(EXTENSION_NAME, ret, metadata.ID, assetData.Length, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ForEach(Action<Metadata> action, int start, int count)
|
||||||
|
{
|
||||||
|
int rowCount = 0;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
MySqlDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT name,description,assetType,temporary,data,id FROM assets LIMIT {0}, {1}",
|
||||||
|
start, count);
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.CreationDate = OpenMetaverse.Utils.Epoch;
|
||||||
|
metadata.Description = reader.GetString(1);
|
||||||
|
metadata.ID = UUID.Parse(reader.GetString(5));
|
||||||
|
metadata.Name = reader.GetString(0);
|
||||||
|
metadata.SHA1 = OpenMetaverse.Utils.SHA1((byte[])reader.GetValue(4));
|
||||||
|
metadata.Temporary = reader.GetBoolean(3);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2));
|
||||||
|
|
||||||
|
action(metadata);
|
||||||
|
++rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Required Interfaces
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,239 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Xml;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
using HttpServer;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class ReferenceFrontend : IExtension<AssetServer>
|
||||||
|
{
|
||||||
|
AssetServer server;
|
||||||
|
|
||||||
|
public ReferenceFrontend()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
// Asset metadata request
|
||||||
|
server.HttpServer.AddHandler("get", null, @"^/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/metadata",
|
||||||
|
MetadataRequestHandler);
|
||||||
|
|
||||||
|
// Asset data request
|
||||||
|
server.HttpServer.AddHandler("get", null, @"^/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/data",
|
||||||
|
DataRequestHandler);
|
||||||
|
|
||||||
|
// Asset creation
|
||||||
|
server.HttpServer.AddHandler("post", null, "^/createasset", CreateRequestHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetadataRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID assetID;
|
||||||
|
// Split the URL up into an AssetID and a method
|
||||||
|
string[] rawUrl = request.Uri.PathAndQuery.Split('/');
|
||||||
|
|
||||||
|
if (rawUrl.Length >= 3 && UUID.TryParse(rawUrl[1], out assetID))
|
||||||
|
{
|
||||||
|
UUID authToken = Utils.GetAuthToken(request);
|
||||||
|
|
||||||
|
if (server.AuthorizationProvider.IsMetadataAuthorized(authToken, assetID))
|
||||||
|
{
|
||||||
|
Metadata metadata;
|
||||||
|
BackendResponse storageResponse = server.StorageProvider.TryFetchMetadata(assetID, out metadata);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
// If the asset data location wasn't specified in the metadata, specify it
|
||||||
|
// manually here by pointing back to this asset server
|
||||||
|
if (!metadata.Methods.ContainsKey("data"))
|
||||||
|
{
|
||||||
|
metadata.Methods["data"] = new Uri(String.Format("{0}://{1}/{2}/data",
|
||||||
|
request.Uri.Scheme, request.Uri.Authority, assetID));
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] serializedData = metadata.SerializeToBytes();
|
||||||
|
|
||||||
|
response.Status = HttpStatusCode.OK;
|
||||||
|
response.ContentType = "application/json";
|
||||||
|
response.ContentLength = serializedData.Length;
|
||||||
|
response.Body.Write(serializedData, 0, serializedData.Length);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (storageResponse == BackendResponse.NotFound)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn("Could not find metadata for asset " + assetID.ToString());
|
||||||
|
response.Status = HttpStatusCode.NotFound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.Forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Status = HttpStatusCode.NotFound;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID assetID;
|
||||||
|
// Split the URL up into an AssetID and a method
|
||||||
|
string[] rawUrl = request.Uri.PathAndQuery.Split('/');
|
||||||
|
|
||||||
|
if (rawUrl.Length >= 3 && UUID.TryParse(rawUrl[1], out assetID))
|
||||||
|
{
|
||||||
|
UUID authToken = Utils.GetAuthToken(request);
|
||||||
|
|
||||||
|
if (server.AuthorizationProvider.IsDataAuthorized(authToken, assetID))
|
||||||
|
{
|
||||||
|
byte[] assetData;
|
||||||
|
BackendResponse storageResponse = server.StorageProvider.TryFetchData(assetID, out assetData);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.OK;
|
||||||
|
response.Status = HttpStatusCode.OK;
|
||||||
|
response.ContentType = "application/octet-stream";
|
||||||
|
response.AddHeader("Content-Disposition", "attachment; filename=" + assetID.ToString());
|
||||||
|
response.ContentLength = assetData.Length;
|
||||||
|
response.Body.Write(assetData, 0, assetData.Length);
|
||||||
|
}
|
||||||
|
else if (storageResponse == BackendResponse.NotFound)
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.NotFound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.Forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response)
|
||||||
|
{
|
||||||
|
UUID authToken = Utils.GetAuthToken(request);
|
||||||
|
|
||||||
|
if (server.AuthorizationProvider.IsCreateAuthorized(authToken))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OSD osdata = OSDParser.DeserializeJson(request.Body);
|
||||||
|
|
||||||
|
if (osdata.Type == OSDType.Map)
|
||||||
|
{
|
||||||
|
OSDMap map = (OSDMap)osdata;
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.Deserialize(map);
|
||||||
|
|
||||||
|
byte[] assetData = map["data"].AsBinary();
|
||||||
|
|
||||||
|
if (assetData != null && assetData.Length > 0)
|
||||||
|
{
|
||||||
|
BackendResponse storageResponse;
|
||||||
|
|
||||||
|
if (metadata.ID != UUID.Zero)
|
||||||
|
storageResponse = server.StorageProvider.TryCreateAsset(metadata, assetData);
|
||||||
|
else
|
||||||
|
storageResponse = server.StorageProvider.TryCreateAsset(metadata, assetData, out metadata.ID);
|
||||||
|
|
||||||
|
if (storageResponse == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.Created;
|
||||||
|
OSDMap responseMap = new OSDMap(1);
|
||||||
|
responseMap["id"] = OSD.FromUUID(metadata.ID);
|
||||||
|
LitJson.JsonData jsonData = OSDParser.SerializeJson(responseMap);
|
||||||
|
byte[] responseData = System.Text.Encoding.UTF8.GetBytes(jsonData.ToJson());
|
||||||
|
response.Body.Write(responseData, 0, responseData.Length);
|
||||||
|
response.Body.Flush();
|
||||||
|
}
|
||||||
|
else if (storageResponse == BackendResponse.NotFound)
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.NotFound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.BadRequest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.InternalServerError;
|
||||||
|
response.Reason = ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.Status = HttpStatusCode.Forbidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,602 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class SimpleInventory : IExtension<AssetServer>, IInventoryProvider
|
||||||
|
{
|
||||||
|
const string EXTENSION_NAME = "SimpleInventory"; // Used for metrics reporting
|
||||||
|
const string DEFAULT_INVENTORY_DIR = "SimpleInventory";
|
||||||
|
|
||||||
|
AssetServer server;
|
||||||
|
Dictionary<Uri, InventoryCollection> inventories = new Dictionary<Uri, InventoryCollection>();
|
||||||
|
Dictionary<Uri, List<InventoryItem>> activeGestures = new Dictionary<Uri, List<InventoryItem>>();
|
||||||
|
Utils.InventoryItemSerializer itemSerializer = new Utils.InventoryItemSerializer();
|
||||||
|
Utils.InventoryFolderSerializer folderSerializer = new Utils.InventoryFolderSerializer();
|
||||||
|
|
||||||
|
public SimpleInventory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Required Interfaces
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
LoadFiles(DEFAULT_INVENTORY_DIR);
|
||||||
|
|
||||||
|
Logger.Log.InfoFormat("Initialized the inventory index with data for {0} avatars",
|
||||||
|
inventories.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItem item)
|
||||||
|
{
|
||||||
|
item = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
else
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolder folder)
|
||||||
|
{
|
||||||
|
folder = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
else
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents)
|
||||||
|
{
|
||||||
|
contents = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
InventoryFolder folder;
|
||||||
|
|
||||||
|
if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
|
||||||
|
{
|
||||||
|
contents = new InventoryCollection();
|
||||||
|
contents.UserID = collection.UserID;
|
||||||
|
contents.Folders = new Dictionary<UUID, InventoryFolder>();
|
||||||
|
contents.Items = new Dictionary<UUID, InventoryItem>();
|
||||||
|
|
||||||
|
foreach (InventoryBase invBase in folder.Children.Values)
|
||||||
|
{
|
||||||
|
if (invBase is InventoryItem)
|
||||||
|
{
|
||||||
|
InventoryItem invItem = invBase as InventoryItem;
|
||||||
|
contents.Items.Add(invItem.ID, invItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InventoryFolder invFolder = invBase as InventoryFolder;
|
||||||
|
contents.Folders.Add(invFolder.ID, invFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchFolderContents(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolder> folders)
|
||||||
|
{
|
||||||
|
folders = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
if (inventories.TryGetValue(owner, out collection))
|
||||||
|
{
|
||||||
|
folders = new List<InventoryFolder>(collection.Folders.Values);
|
||||||
|
return BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchFolderList(EXTENSION_NAME, ret, owner, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory)
|
||||||
|
{
|
||||||
|
inventory = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
if (inventories.TryGetValue(owner, out inventory))
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
else
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchInventory(EXTENSION_NAME, ret, owner, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItem> gestures)
|
||||||
|
{
|
||||||
|
gestures = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
if (activeGestures.TryGetValue(owner, out gestures))
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
else
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryFetchActiveGestures(EXTENSION_NAME, ret, owner, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateItem(Uri owner, InventoryItem item)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
if (inventories.TryGetValue(owner, out collection))
|
||||||
|
{
|
||||||
|
// Delete this item first if it already exists
|
||||||
|
InventoryItem oldItem;
|
||||||
|
if (collection.Items.TryGetValue(item.ID, out oldItem))
|
||||||
|
TryDeleteItem(owner, item.ID);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create the file
|
||||||
|
SaveItem(item);
|
||||||
|
|
||||||
|
// Add the item to the collection
|
||||||
|
lock (collection) collection.Items[item.ID] = item;
|
||||||
|
|
||||||
|
// Add the item to its parent folder
|
||||||
|
InventoryFolder parent;
|
||||||
|
if (collection.Folders.TryGetValue(item.Folder, out parent))
|
||||||
|
lock (parent.Children) parent.Children.Add(item.ID, item);
|
||||||
|
|
||||||
|
// Add active gestures to our list
|
||||||
|
if (item.InvType == (int)InventoryType.Gesture && item.Flags == 1)
|
||||||
|
{
|
||||||
|
lock (activeGestures)
|
||||||
|
activeGestures[owner].Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error(ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, false, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateFolder(Uri owner, InventoryFolder folder)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
if (inventories.TryGetValue(owner, out collection))
|
||||||
|
{
|
||||||
|
// Delete this folder first if it already exists
|
||||||
|
InventoryFolder oldFolder;
|
||||||
|
if (collection.Folders.TryGetValue(folder.ID, out oldFolder))
|
||||||
|
TryDeleteFolder(owner, folder.ID);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create the file
|
||||||
|
SaveFolder(folder);
|
||||||
|
|
||||||
|
// Add the folder to the collection
|
||||||
|
lock (collection) collection.Folders[folder.ID] = folder;
|
||||||
|
|
||||||
|
// Add the folder to its parent folder
|
||||||
|
InventoryFolder parent;
|
||||||
|
if (collection.Folders.TryGetValue(folder.ParentID, out parent))
|
||||||
|
lock (parent.Children) parent.Children.Add(folder.ID, folder);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error(ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, true, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateInventory(Uri owner, InventoryFolder rootFolder)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
lock (inventories)
|
||||||
|
{
|
||||||
|
if (!inventories.ContainsKey(owner))
|
||||||
|
{
|
||||||
|
InventoryCollection collection = new InventoryCollection();
|
||||||
|
collection.UserID = rootFolder.Owner;
|
||||||
|
collection.Folders = new Dictionary<UUID, InventoryFolder>();
|
||||||
|
collection.Folders.Add(rootFolder.ID, rootFolder);
|
||||||
|
collection.Items = new Dictionary<UUID, InventoryItem>();
|
||||||
|
|
||||||
|
inventories.Add(owner, collection);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == BackendResponse.Success)
|
||||||
|
{
|
||||||
|
string path = Path.Combine(DEFAULT_INVENTORY_DIR, rootFolder.Owner.ToString());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create the directory for this agent
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
|
||||||
|
// Create an index.txt containing the UUID and URI for this agent
|
||||||
|
string[] index = new string[] { rootFolder.Owner.ToString(), owner.ToString() };
|
||||||
|
File.WriteAllLines(Path.Combine(path, "index.txt"), index);
|
||||||
|
|
||||||
|
// Create the root folder file
|
||||||
|
SaveFolder(rootFolder);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error(ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryCreateInventory(EXTENSION_NAME, ret, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryDeleteItem(Uri owner, UUID itemID)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
InventoryItem item;
|
||||||
|
if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
|
||||||
|
{
|
||||||
|
// Remove the item from its parent folder
|
||||||
|
InventoryFolder parent;
|
||||||
|
if (collection.Folders.TryGetValue(item.Folder, out parent))
|
||||||
|
lock (parent.Children) parent.Children.Remove(itemID);
|
||||||
|
|
||||||
|
// Remove the item from the collection
|
||||||
|
lock (collection) collection.Items.Remove(itemID);
|
||||||
|
|
||||||
|
// Remove from the active gestures list if applicable
|
||||||
|
if (item.InvType == (int)InventoryType.Gesture)
|
||||||
|
{
|
||||||
|
lock (activeGestures)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < activeGestures[owner].Count; i++)
|
||||||
|
{
|
||||||
|
if (activeGestures[owner][i].ID == itemID)
|
||||||
|
{
|
||||||
|
activeGestures[owner].RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the file. We don't know exactly what the file name is,
|
||||||
|
// so search for it
|
||||||
|
string path = PathFromURI(owner);
|
||||||
|
string[] matches = Directory.GetFiles(path, String.Format("*{0}.item", itemID), SearchOption.TopDirectoryOnly);
|
||||||
|
foreach (string match in matches)
|
||||||
|
{
|
||||||
|
try { File.Delete(match); }
|
||||||
|
catch (Exception ex) { Logger.Log.ErrorFormat("Failed to delete file {0}: {1}", match, ex.Message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryDeleteFolder(Uri owner, UUID folderID)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
InventoryFolder folder;
|
||||||
|
if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
|
||||||
|
{
|
||||||
|
// Remove the folder from its parent folder
|
||||||
|
InventoryFolder parent;
|
||||||
|
if (collection.Folders.TryGetValue(folder.ParentID, out parent))
|
||||||
|
lock (parent.Children) parent.Children.Remove(folderID);
|
||||||
|
|
||||||
|
// Remove the folder from the collection
|
||||||
|
lock (collection) collection.Items.Remove(folderID);
|
||||||
|
|
||||||
|
// Delete the folder file. We don't know exactly what the file name is,
|
||||||
|
// so search for it
|
||||||
|
string path = PathFromURI(owner);
|
||||||
|
string[] matches = Directory.GetFiles(path, String.Format("*{0}.folder", folderID), SearchOption.TopDirectoryOnly);
|
||||||
|
foreach (string match in matches)
|
||||||
|
{
|
||||||
|
try { File.Delete(match); }
|
||||||
|
catch (Exception ex) { Logger.Log.ErrorFormat("Failed to delete folder file {0}: {1}", match, ex.Message); }
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryPurgeFolder(Uri owner, UUID folderID)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
InventoryCollection collection;
|
||||||
|
InventoryFolder folder;
|
||||||
|
if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
|
||||||
|
{
|
||||||
|
// Delete all of the folder children
|
||||||
|
foreach (InventoryBase obj in new List<InventoryBase>(folder.Children.Values))
|
||||||
|
{
|
||||||
|
if (obj is InventoryItem)
|
||||||
|
{
|
||||||
|
TryDeleteItem(owner, (obj as InventoryItem).ID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InventoryFolder childFolder = obj as InventoryFolder;
|
||||||
|
TryPurgeFolder(owner, childFolder.ID);
|
||||||
|
TryDeleteFolder(owner, childFolder.ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogInventoryPurgeFolder(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Required Interfaces
|
||||||
|
|
||||||
|
void SaveItem(InventoryItem item)
|
||||||
|
{
|
||||||
|
string filename = String.Format("{0}-{1}.item", SanitizeFilename(item.Name), item.ID);
|
||||||
|
|
||||||
|
string path = Path.Combine(DEFAULT_INVENTORY_DIR, item.Owner.ToString());
|
||||||
|
path = Path.Combine(path, filename);
|
||||||
|
|
||||||
|
using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
itemSerializer.Serialize(stream, item);
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveFolder(InventoryFolder folder)
|
||||||
|
{
|
||||||
|
string filename = String.Format("{0}-{1}.folder", SanitizeFilename(folder.Name), folder.ID);
|
||||||
|
|
||||||
|
string path = Path.Combine(DEFAULT_INVENTORY_DIR, folder.Owner.ToString());
|
||||||
|
path = Path.Combine(path, filename);
|
||||||
|
|
||||||
|
using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
folderSerializer.Serialize(stream, folder);
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string SanitizeFilename(string filename)
|
||||||
|
{
|
||||||
|
string output = filename;
|
||||||
|
|
||||||
|
if (output.Length > 64)
|
||||||
|
output = output.Substring(0, 64);
|
||||||
|
|
||||||
|
foreach (char i in Path.GetInvalidFileNameChars())
|
||||||
|
output = output.Replace(i, '_');
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string PathFromURI(Uri uri)
|
||||||
|
{
|
||||||
|
byte[] hash = OpenMetaverse.Utils.SHA1(Encoding.UTF8.GetBytes(uri.ToString()));
|
||||||
|
StringBuilder digest = new StringBuilder(40);
|
||||||
|
|
||||||
|
// Convert the hash to a hex string
|
||||||
|
foreach (byte b in hash)
|
||||||
|
digest.AppendFormat(OpenMetaverse.Utils.EnUsCulture, "{0:x2}", b);
|
||||||
|
|
||||||
|
return Path.Combine(DEFAULT_INVENTORY_DIR, digest.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadFiles(string folder)
|
||||||
|
{
|
||||||
|
// Try to create the directory if it doesn't already exist
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
try { Directory.CreateDirectory(folder); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn(ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string[] agentFolders = Directory.GetDirectories(DEFAULT_INVENTORY_DIR);
|
||||||
|
|
||||||
|
for (int i = 0; i < agentFolders.Length; i++)
|
||||||
|
{
|
||||||
|
string foldername = agentFolders[i];
|
||||||
|
string indexPath = Path.Combine(foldername, "index.txt");
|
||||||
|
UUID ownerID = UUID.Zero;
|
||||||
|
Uri owner = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string[] index = File.ReadAllLines(indexPath);
|
||||||
|
ownerID = UUID.Parse(index[0]);
|
||||||
|
owner = new Uri(index[1]);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.WarnFormat("Failed loading the index file {0}: {1}", indexPath, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ownerID != UUID.Zero && owner != null)
|
||||||
|
{
|
||||||
|
// Initialize the active gestures list for this agent
|
||||||
|
activeGestures.Add(owner, new List<InventoryItem>());
|
||||||
|
|
||||||
|
InventoryCollection collection = new InventoryCollection();
|
||||||
|
collection.UserID = ownerID;
|
||||||
|
|
||||||
|
// Load all of the folders for this agent
|
||||||
|
string[] folders = Directory.GetFiles(foldername, "*.folder", SearchOption.TopDirectoryOnly);
|
||||||
|
collection.Folders = new Dictionary<UUID,InventoryFolder>(folders.Length);
|
||||||
|
|
||||||
|
for (int j = 0; j < folders.Length; j++)
|
||||||
|
{
|
||||||
|
InventoryFolder invFolder = (InventoryFolder)folderSerializer.Deserialize(
|
||||||
|
new FileStream(folders[j], FileMode.Open, FileAccess.Read));
|
||||||
|
collection.Folders[invFolder.ID] = invFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the folders collection, adding children to their parents
|
||||||
|
foreach (InventoryFolder invFolder in collection.Folders.Values)
|
||||||
|
{
|
||||||
|
InventoryFolder parent;
|
||||||
|
if (collection.Folders.TryGetValue(invFolder.ParentID, out parent))
|
||||||
|
parent.Children[invFolder.ID] = invFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load all of the items for this agent
|
||||||
|
string[] files = Directory.GetFiles(foldername, "*.item", SearchOption.TopDirectoryOnly);
|
||||||
|
collection.Items = new Dictionary<UUID, InventoryItem>(files.Length);
|
||||||
|
|
||||||
|
for (int j = 0; j < files.Length; j++)
|
||||||
|
{
|
||||||
|
InventoryItem invItem = (InventoryItem)itemSerializer.Deserialize(
|
||||||
|
new FileStream(files[j], FileMode.Open, FileAccess.Read));
|
||||||
|
collection.Items[invItem.ID] = invItem;
|
||||||
|
|
||||||
|
// Add items to their parent folders
|
||||||
|
InventoryFolder parent;
|
||||||
|
if (collection.Folders.TryGetValue(invItem.Folder, out parent))
|
||||||
|
parent.Children[invItem.ID] = invItem;
|
||||||
|
|
||||||
|
// Add active gestures to our list
|
||||||
|
if (invItem.InvType == (int)InventoryType.Gesture && invItem.Flags != 0)
|
||||||
|
activeGestures[owner].Add(invItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
inventories.Add(owner, collection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("Failed loading inventory from {0}: {1}", folder, ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.IO;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public class SimpleStorage : IExtension<AssetServer>, IStorageProvider
|
||||||
|
{
|
||||||
|
const string EXTENSION_NAME = ""; // Used in metrics reporting
|
||||||
|
const string DEFAULT_DATA_DIR = "SimpleAssets";
|
||||||
|
const string TEMP_DATA_DIR = "SimpleAssetsTemp";
|
||||||
|
|
||||||
|
AssetServer server;
|
||||||
|
Dictionary<UUID, Metadata> metadataStorage;
|
||||||
|
Dictionary<UUID, string> filenames;
|
||||||
|
|
||||||
|
public SimpleStorage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Required Interfaces
|
||||||
|
|
||||||
|
public void Start(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
metadataStorage = new Dictionary<UUID, Metadata>();
|
||||||
|
filenames = new Dictionary<UUID, string>();
|
||||||
|
|
||||||
|
LoadFiles(DEFAULT_DATA_DIR, false);
|
||||||
|
LoadFiles(TEMP_DATA_DIR, true);
|
||||||
|
|
||||||
|
Logger.Log.InfoFormat("Initialized the store index with metadata for {0} assets",
|
||||||
|
metadataStorage.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
WipeTemporary();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata)
|
||||||
|
{
|
||||||
|
metadata = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
if (metadataStorage.TryGetValue(assetID, out metadata))
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
else
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchData(UUID assetID, out byte[] assetData)
|
||||||
|
{
|
||||||
|
assetData = null;
|
||||||
|
string filename;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
if (filenames.TryGetValue(assetID, out filename))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
assetData = File.ReadAllBytes(filename);
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("Failed reading data for asset {0} from {1}: {2}", assetID, filename, ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData)
|
||||||
|
{
|
||||||
|
metadata = null;
|
||||||
|
assetData = null;
|
||||||
|
string filename;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
if (metadataStorage.TryGetValue(assetID, out metadata) &&
|
||||||
|
filenames.TryGetValue(assetID, out filename))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
assetData = File.ReadAllBytes(filename);
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("Failed reading data for asset {0} from {1}: {2}", assetID, filename, ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now);
|
||||||
|
server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID)
|
||||||
|
{
|
||||||
|
assetID = metadata.ID = UUID.Random();
|
||||||
|
return TryCreateAsset(metadata, assetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
string path;
|
||||||
|
string filename = String.Format("{0}.{1}", metadata.ID, Utils.ContentTypeToExtension(metadata.ContentType));
|
||||||
|
|
||||||
|
if (metadata.Temporary)
|
||||||
|
path = Path.Combine(TEMP_DATA_DIR, filename);
|
||||||
|
else
|
||||||
|
path = Path.Combine(DEFAULT_DATA_DIR, filename);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllBytes(path, assetData);
|
||||||
|
lock (filenames) filenames[metadata.ID] = path;
|
||||||
|
|
||||||
|
// Set the creation date to right now
|
||||||
|
metadata.CreationDate = DateTime.Now;
|
||||||
|
|
||||||
|
lock (metadataStorage)
|
||||||
|
metadataStorage[metadata.ID] = metadata;
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("Failed writing data for asset {0} to {1}: {2}", metadata.ID, filename, ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetCreate(EXTENSION_NAME, ret, metadata.ID, assetData.Length, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ForEach(Action<Metadata> action, int start, int count)
|
||||||
|
{
|
||||||
|
int rowCount = 0;
|
||||||
|
|
||||||
|
lock (metadataStorage)
|
||||||
|
{
|
||||||
|
foreach (Metadata metadata in metadataStorage.Values)
|
||||||
|
{
|
||||||
|
action(metadata);
|
||||||
|
++rowCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Required Interfaces
|
||||||
|
|
||||||
|
public void WipeTemporary()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(TEMP_DATA_DIR))
|
||||||
|
{
|
||||||
|
try { Directory.Delete(TEMP_DATA_DIR); }
|
||||||
|
catch (Exception ex) { Logger.Log.Error(ex.Message); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadFiles(string folder, bool temporary)
|
||||||
|
{
|
||||||
|
// Try to create the directory if it doesn't already exist
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
try { Directory.CreateDirectory(folder); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn(ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (metadataStorage)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string[] assets = Directory.GetFiles(folder);
|
||||||
|
|
||||||
|
for (int i = 0; i < assets.Length; i++)
|
||||||
|
{
|
||||||
|
string filename = assets[i];
|
||||||
|
byte[] data = File.ReadAllBytes(filename);
|
||||||
|
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.CreationDate = File.GetCreationTime(filename);
|
||||||
|
metadata.Description = String.Empty;
|
||||||
|
metadata.ID = SimpleUtils.ParseUUIDFromFilename(filename);
|
||||||
|
metadata.Name = SimpleUtils.ParseNameFromFilename(filename);
|
||||||
|
metadata.SHA1 = OpenMetaverse.Utils.SHA1(data);
|
||||||
|
metadata.Temporary = false;
|
||||||
|
metadata.ContentType = Utils.ExtensionToContentType(Path.GetExtension(filename).TrimStart('.'));
|
||||||
|
|
||||||
|
// Store the loaded data
|
||||||
|
metadataStorage[metadata.ID] = metadata;
|
||||||
|
filenames[metadata.ID] = filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Warn(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace AssetServer.Extensions
|
||||||
|
{
|
||||||
|
public static class SimpleUtils
|
||||||
|
{
|
||||||
|
public static string ParseNameFromFilename(string filename)
|
||||||
|
{
|
||||||
|
filename = Path.GetFileName(filename);
|
||||||
|
|
||||||
|
int dot = filename.LastIndexOf('.');
|
||||||
|
int firstDash = filename.IndexOf('-');
|
||||||
|
|
||||||
|
if (dot - 37 > 0 && firstDash > 0)
|
||||||
|
return filename.Substring(0, firstDash);
|
||||||
|
else
|
||||||
|
return String.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID ParseUUIDFromFilename(string filename)
|
||||||
|
{
|
||||||
|
int dot = filename.LastIndexOf('.');
|
||||||
|
|
||||||
|
if (dot > 35)
|
||||||
|
{
|
||||||
|
// Grab the last 36 characters of the filename
|
||||||
|
string uuidString = filename.Substring(dot - 36, 36);
|
||||||
|
UUID uuid;
|
||||||
|
UUID.TryParse(uuidString, out uuid);
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UUID uuid;
|
||||||
|
if (UUID.TryParse(Path.GetFileName(filename), out uuid))
|
||||||
|
return uuid;
|
||||||
|
else
|
||||||
|
return UUID.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
|
||||||
|
namespace AssetServer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Response from a call to a backend provider
|
||||||
|
/// </summary>
|
||||||
|
public enum BackendResponse
|
||||||
|
{
|
||||||
|
/// <summary>The call succeeded</summary>
|
||||||
|
Success,
|
||||||
|
/// <summary>The resource requested was not found</summary>
|
||||||
|
NotFound,
|
||||||
|
/// <summary>A server failure prevented the call from
|
||||||
|
/// completing</summary>
|
||||||
|
Failure
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AssetServerPluginInitialiser : PluginInitialiserBase
|
||||||
|
{
|
||||||
|
private AssetServer server;
|
||||||
|
|
||||||
|
public AssetServerPluginInitialiser (AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialise (IPlugin plugin)
|
||||||
|
{
|
||||||
|
IAssetServerPlugin p = plugin as IAssetServerPlugin;
|
||||||
|
p.Initialise (server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Interfaces
|
||||||
|
|
||||||
|
public interface IAssetServerPlugin : IPlugin
|
||||||
|
{
|
||||||
|
void Initialise(AssetServer server);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IStorageProvider
|
||||||
|
{
|
||||||
|
BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata);
|
||||||
|
BackendResponse TryFetchData(UUID assetID, out byte[] assetData);
|
||||||
|
BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData);
|
||||||
|
BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData);
|
||||||
|
BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID);
|
||||||
|
int ForEach(Action<Metadata> action, int start, int count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAssetStorageProvider : IAssetServerPlugin
|
||||||
|
{
|
||||||
|
BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata);
|
||||||
|
BackendResponse TryFetchData(UUID assetID, out byte[] assetData);
|
||||||
|
BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData);
|
||||||
|
BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData);
|
||||||
|
BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID);
|
||||||
|
int ForEach(Action<Metadata> action, int start, int count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IInventoryProvider
|
||||||
|
{
|
||||||
|
BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItem item);
|
||||||
|
BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolder folder);
|
||||||
|
BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents);
|
||||||
|
BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolder> folders);
|
||||||
|
BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory);
|
||||||
|
|
||||||
|
BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItem> gestures);
|
||||||
|
|
||||||
|
BackendResponse TryCreateItem(Uri owner, InventoryItem item);
|
||||||
|
BackendResponse TryCreateFolder(Uri owner, InventoryFolder folder);
|
||||||
|
BackendResponse TryCreateInventory(Uri owner, InventoryFolder rootFolder);
|
||||||
|
|
||||||
|
BackendResponse TryDeleteItem(Uri owner, UUID itemID);
|
||||||
|
BackendResponse TryDeleteFolder(Uri owner, UUID folderID);
|
||||||
|
BackendResponse TryPurgeFolder(Uri owner, UUID folderID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAuthenticationProvider
|
||||||
|
{
|
||||||
|
void AddIdentifier(UUID authToken, Uri identifier);
|
||||||
|
bool RemoveIdentifier(UUID authToken);
|
||||||
|
bool TryGetIdentifier(UUID authToken, out Uri identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAuthorizationProvider
|
||||||
|
{
|
||||||
|
bool IsMetadataAuthorized(UUID authToken, UUID assetID);
|
||||||
|
/// <summary>
|
||||||
|
/// Authorizes access to the data for an asset. Access to asset data
|
||||||
|
/// also implies access to the metadata for that asset
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="authToken">Authentication token to check for access</param>
|
||||||
|
/// <param name="assetID">ID of the requested asset</param>
|
||||||
|
/// <returns>True if access is granted, otherwise false</returns>
|
||||||
|
bool IsDataAuthorized(UUID authToken, UUID assetID);
|
||||||
|
bool IsCreateAuthorized(UUID authToken);
|
||||||
|
|
||||||
|
bool IsInventoryReadAuthorized(UUID authToken, Uri owner);
|
||||||
|
bool IsInventoryWriteAuthorized(UUID authToken, Uri owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IMetricsProvider
|
||||||
|
{
|
||||||
|
void LogAssetMetadataFetch(string extension, BackendResponse response, UUID assetID, DateTime time);
|
||||||
|
void LogAssetDataFetch(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time);
|
||||||
|
void LogAssetCreate(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time);
|
||||||
|
|
||||||
|
void LogInventoryFetch(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time);
|
||||||
|
void LogInventoryFetchFolderContents(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time);
|
||||||
|
void LogInventoryFetchFolderList(string extension, BackendResponse response, Uri owner, DateTime time);
|
||||||
|
void LogInventoryFetchInventory(string extension, BackendResponse response, Uri owner, DateTime time);
|
||||||
|
void LogInventoryFetchActiveGestures(string extension, BackendResponse response, Uri owner, DateTime time);
|
||||||
|
void LogInventoryCreate(string extension, BackendResponse response, Uri owner, bool folder, DateTime time);
|
||||||
|
void LogInventoryCreateInventory(string extension, BackendResponse response, DateTime time);
|
||||||
|
void LogInventoryDelete(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time);
|
||||||
|
void LogInventoryPurgeFolder(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Interfaces
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using OpenMetaverse;
|
||||||
|
|
||||||
|
namespace AssetServer
|
||||||
|
{
|
||||||
|
public class InventoryBase
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InventoryFolder : InventoryBase
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public UUID Owner;
|
||||||
|
public UUID ParentID;
|
||||||
|
public UUID ID;
|
||||||
|
public short Type;
|
||||||
|
public ushort Version;
|
||||||
|
|
||||||
|
[NonSerialized]
|
||||||
|
public Dictionary<UUID, InventoryBase> Children = new Dictionary<UUID, InventoryBase>();
|
||||||
|
|
||||||
|
public InventoryFolder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public InventoryFolder(string name, UUID ownerID, UUID parentID, short assetType)
|
||||||
|
{
|
||||||
|
ID = UUID.Random();
|
||||||
|
Name = name;
|
||||||
|
Owner = ownerID;
|
||||||
|
ParentID = parentID;
|
||||||
|
Type = assetType;
|
||||||
|
Version = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("{0} ({1})", Name, ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InventoryItem : InventoryBase
|
||||||
|
{
|
||||||
|
public UUID ID;
|
||||||
|
public int InvType;
|
||||||
|
public UUID Folder;
|
||||||
|
public UUID Owner;
|
||||||
|
public UUID Creator;
|
||||||
|
public string Name;
|
||||||
|
public string Description;
|
||||||
|
public uint NextPermissions;
|
||||||
|
public uint CurrentPermissions;
|
||||||
|
public uint BasePermissions;
|
||||||
|
public uint EveryOnePermissions;
|
||||||
|
public uint GroupPermissions;
|
||||||
|
public int AssetType;
|
||||||
|
public UUID AssetID;
|
||||||
|
public UUID GroupID;
|
||||||
|
public bool GroupOwned;
|
||||||
|
public int SalePrice;
|
||||||
|
public byte SaleType;
|
||||||
|
public uint Flags;
|
||||||
|
public int CreationDate;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("{0} ({1})", Name, ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InventoryCollection
|
||||||
|
{
|
||||||
|
public Dictionary<UUID, InventoryFolder> Folders;
|
||||||
|
public Dictionary<UUID, InventoryItem> Items;
|
||||||
|
public UUID UserID;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using log4net;
|
||||||
|
using log4net.Config;
|
||||||
|
|
||||||
|
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "log4net")]
|
||||||
|
|
||||||
|
namespace AssetServer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton logging class for the entire library
|
||||||
|
/// </summary>
|
||||||
|
public static class Logger
|
||||||
|
{
|
||||||
|
/// <summary>log4net logging engine</summary>
|
||||||
|
public static ILog Log;
|
||||||
|
|
||||||
|
static Logger()
|
||||||
|
{
|
||||||
|
Log = LogManager.GetLogger(System.Reflection.Assembly.GetExecutingAssembly().FullName);
|
||||||
|
|
||||||
|
// If error level reporting isn't enabled we assume no logger is configured and initialize a default
|
||||||
|
// ConsoleAppender
|
||||||
|
if (!Log.Logger.IsEnabledFor(log4net.Core.Level.Error))
|
||||||
|
{
|
||||||
|
log4net.Appender.ConsoleAppender appender = new log4net.Appender.ConsoleAppender();
|
||||||
|
appender.Layout = new log4net.Layout.PatternLayout("%timestamp [%thread] %-5level - %message%newline");
|
||||||
|
BasicConfigurator.Configure(appender);
|
||||||
|
|
||||||
|
Log.Info("No log configuration found, defaulting to console logging");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
|
||||||
|
namespace AssetServer
|
||||||
|
{
|
||||||
|
class MainEntry
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
AssetServer server = new AssetServer();
|
||||||
|
if (server.Start())
|
||||||
|
{
|
||||||
|
Console.WriteLine("Asset server is running. Press CTRL+C to quit");
|
||||||
|
|
||||||
|
Console.CancelKeyPress +=
|
||||||
|
delegate(object sender, ConsoleCancelEventArgs e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Asset server is shutting down...");
|
||||||
|
server.Shutdown();
|
||||||
|
Environment.Exit(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
Console.ReadLine();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ServiceBase[] servicesToRun = new ServiceBase[] { new AssetServer() };
|
||||||
|
ServiceBase.Run(servicesToRun);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Xml;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
|
namespace AssetServer
|
||||||
|
{
|
||||||
|
public class Metadata
|
||||||
|
{
|
||||||
|
public UUID ID;
|
||||||
|
public string Name;
|
||||||
|
public string Description;
|
||||||
|
public DateTime CreationDate;
|
||||||
|
public string ContentType;
|
||||||
|
public byte[] SHA1;
|
||||||
|
public bool Temporary;
|
||||||
|
public Dictionary<string, Uri> Methods = new Dictionary<string, Uri>();
|
||||||
|
public OSDMap ExtraData;
|
||||||
|
|
||||||
|
public OSDMap SerializeToOSD()
|
||||||
|
{
|
||||||
|
OSDMap osdata = new OSDMap();
|
||||||
|
|
||||||
|
if (ID != UUID.Zero) osdata["id"] = OSD.FromUUID(ID);
|
||||||
|
osdata["name"] = OSD.FromString(Name);
|
||||||
|
osdata["description"] = OSD.FromString(Description);
|
||||||
|
osdata["creation_date"] = OSD.FromDate(CreationDate);
|
||||||
|
osdata["type"] = OSD.FromString(ContentType);
|
||||||
|
osdata["sha1"] = OSD.FromBinary(SHA1);
|
||||||
|
osdata["temporary"] = OSD.FromBoolean(Temporary);
|
||||||
|
|
||||||
|
OSDMap methods = new OSDMap(Methods.Count);
|
||||||
|
foreach (KeyValuePair<string, Uri> kvp in Methods)
|
||||||
|
methods.Add(kvp.Key, OSD.FromUri(kvp.Value));
|
||||||
|
osdata["methods"] = methods;
|
||||||
|
|
||||||
|
if (ExtraData != null) osdata["extra_data"] = ExtraData;
|
||||||
|
|
||||||
|
return osdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] SerializeToBytes()
|
||||||
|
{
|
||||||
|
LitJson.JsonData jsonData = OSDParser.SerializeJson(SerializeToOSD());
|
||||||
|
return System.Text.Encoding.UTF8.GetBytes(jsonData.ToJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(byte[] data)
|
||||||
|
{
|
||||||
|
OSD osdata = OSDParser.DeserializeJson(System.Text.Encoding.UTF8.GetString(data));
|
||||||
|
Deserialize(osdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(string data)
|
||||||
|
{
|
||||||
|
OSD osdata = OSDParser.DeserializeJson(data);
|
||||||
|
Deserialize(osdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deserialize(OSD osdata)
|
||||||
|
{
|
||||||
|
if (osdata.Type == OSDType.Map)
|
||||||
|
{
|
||||||
|
OSDMap map = (OSDMap)osdata;
|
||||||
|
ID = map["id"].AsUUID();
|
||||||
|
Name = map["name"].AsString();
|
||||||
|
Description = map["description"].AsString();
|
||||||
|
CreationDate = map["creation_date"].AsDate();
|
||||||
|
ContentType = map["type"].AsString();
|
||||||
|
SHA1 = map["sha1"].AsBinary();
|
||||||
|
Temporary = map["temporary"].AsBoolean();
|
||||||
|
|
||||||
|
OSDMap methods = map["methods"] as OSDMap;
|
||||||
|
if (methods != null)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, OSD> kvp in methods)
|
||||||
|
Methods.Add(kvp.Key, kvp.Value.AsUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtraData = map["extra_data"] as OSDMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,352 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2008 Intel Corporation
|
||||||
|
* All rights reserved.
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* -- Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* -- Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* -- Neither the name of the Intel Corporation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Data;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using ExtensionLoader;
|
||||||
|
using ExtensionLoader.Config;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
using OpenSim.Framework;
|
||||||
|
using AssetServer.Extensions;
|
||||||
|
|
||||||
|
namespace AssetServer.Plugins
|
||||||
|
{
|
||||||
|
public class OpenSimAssetStoragePlugin : IAssetStorageProvider
|
||||||
|
{
|
||||||
|
const string EXTENSION_NAME = "OpenSimAssetStorage"; // Used in metrics reporting
|
||||||
|
|
||||||
|
private AssetServer server;
|
||||||
|
private IAssetProviderPlugin m_assetProvider;
|
||||||
|
|
||||||
|
public OpenSimAssetStoragePlugin()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IAssetStorageProvider implementation
|
||||||
|
|
||||||
|
public BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata)
|
||||||
|
{
|
||||||
|
metadata = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT name,description,assetType,temporary FROM assets WHERE id='{0}'", assetID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
metadata = new Metadata();
|
||||||
|
metadata.CreationDate = OpenMetaverse.Utils.Epoch;
|
||||||
|
metadata.SHA1 = null;
|
||||||
|
metadata.ID = assetID;
|
||||||
|
metadata.Name = reader.GetString(0);
|
||||||
|
metadata.Description = reader.GetString(1);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2));
|
||||||
|
metadata.Temporary = reader.GetBoolean(3);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchData(UUID assetID, out byte[] assetData)
|
||||||
|
{
|
||||||
|
assetData = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT data FROM assets WHERE id='{0}'", assetID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
assetData = (byte[])reader.GetValue(0);
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData)
|
||||||
|
{
|
||||||
|
metadata = null;
|
||||||
|
assetData = null;
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
IDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
IDbCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT name,description,assetType,temporary,data FROM assets WHERE id='{0}'", assetID.ToString());
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
|
||||||
|
if (reader.Read())
|
||||||
|
{
|
||||||
|
metadata = new Metadata();
|
||||||
|
metadata.CreationDate = OpenMetaverse.Utils.Epoch;
|
||||||
|
metadata.SHA1 = null;
|
||||||
|
metadata.ID = assetID;
|
||||||
|
metadata.Name = reader.GetString(0);
|
||||||
|
metadata.Description = reader.GetString(1);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2));
|
||||||
|
metadata.Temporary = reader.GetBoolean(3);
|
||||||
|
|
||||||
|
assetData = (byte[])reader.GetValue(4);
|
||||||
|
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = BackendResponse.NotFound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now);
|
||||||
|
server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID)
|
||||||
|
{
|
||||||
|
assetID = metadata.ID = UUID.Random();
|
||||||
|
return TryCreateAsset(metadata, assetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData)
|
||||||
|
{
|
||||||
|
BackendResponse ret;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = new MySqlCommand(
|
||||||
|
"REPLACE INTO assets (name,description,assetType,local,temporary,data,id) VALUES " +
|
||||||
|
"(?name,?description,?assetType,?local,?temporary,?data,?id)", dbConnection);
|
||||||
|
|
||||||
|
command.Parameters.AddWithValue("?name", metadata.Name);
|
||||||
|
command.Parameters.AddWithValue("?description", metadata.Description);
|
||||||
|
command.Parameters.AddWithValue("?assetType", Utils.ContentTypeToSLAssetType(metadata.ContentType));
|
||||||
|
command.Parameters.AddWithValue("?local", 0);
|
||||||
|
command.Parameters.AddWithValue("?temporary", metadata.Temporary);
|
||||||
|
command.Parameters.AddWithValue("?data", assetData);
|
||||||
|
command.Parameters.AddWithValue("?id", metadata.ID.ToString());
|
||||||
|
|
||||||
|
int rowsAffected = command.ExecuteNonQuery();
|
||||||
|
if (rowsAffected == 1)
|
||||||
|
{
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else if (rowsAffected == 2)
|
||||||
|
{
|
||||||
|
Logger.Log.Info("Replaced asset " + metadata.ID.ToString());
|
||||||
|
ret = BackendResponse.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
ret = BackendResponse.Failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.MetricsProvider.LogAssetCreate(EXTENSION_NAME, ret, metadata.ID, assetData.Length, DateTime.Now);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ForEach(Action<Metadata> action, int start, int count)
|
||||||
|
{
|
||||||
|
int rowCount = 0;
|
||||||
|
|
||||||
|
using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile)))
|
||||||
|
{
|
||||||
|
MySqlDataReader reader;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dbConnection.Open();
|
||||||
|
|
||||||
|
MySqlCommand command = dbConnection.CreateCommand();
|
||||||
|
command.CommandText = String.Format("SELECT name,description,assetType,temporary,data,id FROM assets LIMIT {0}, {1}",
|
||||||
|
start, count);
|
||||||
|
reader = command.ExecuteReader();
|
||||||
|
}
|
||||||
|
catch (MySqlException ex)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
Metadata metadata = new Metadata();
|
||||||
|
metadata.CreationDate = OpenMetaverse.Utils.Epoch;
|
||||||
|
metadata.Description = reader.GetString(1);
|
||||||
|
metadata.ID = UUID.Parse(reader.GetString(5));
|
||||||
|
metadata.Name = reader.GetString(0);
|
||||||
|
metadata.SHA1 = OpenMetaverse.Utils.SHA1((byte[])reader.GetValue(4));
|
||||||
|
metadata.Temporary = reader.GetBoolean(3);
|
||||||
|
metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2));
|
||||||
|
|
||||||
|
action(metadata);
|
||||||
|
++rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rowCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IAssetStorageProvider implementation
|
||||||
|
|
||||||
|
#region IPlugin implementation
|
||||||
|
|
||||||
|
public void Initialise(AssetServer server)
|
||||||
|
{
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_assetProvider = LoadDatabasePlugin("OpenSim.Data.MySQL.dll", server.ConfigFile.Configs["MySQL"].GetString("database_connect", null));
|
||||||
|
if (m_assetProvider == null)
|
||||||
|
{
|
||||||
|
Logger.Log.Error("[ASSET]: Failed to load a database plugin, server halting.");
|
||||||
|
Environment.Exit(-1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Logger.Log.InfoFormat("[ASSET]: Loaded storage backend: {0}", Version);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Log.WarnFormat("[ASSET]: Failure loading data plugin: {0}", e.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <para>Initialises asset interface</para>
|
||||||
|
/// </summary>
|
||||||
|
public void Initialise()
|
||||||
|
{
|
||||||
|
Logger.Log.InfoFormat("[ASSET]: {0} cannot be default-initialized!", Name);
|
||||||
|
throw new PluginNotInitialisedException(Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Version
|
||||||
|
{
|
||||||
|
get { return m_assetProvider.Version; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return "AssetServer storage provider"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IPlugin implementation
|
||||||
|
|
||||||
|
private IAssetProviderPlugin LoadDatabasePlugin(string provider, string connect)
|
||||||
|
{
|
||||||
|
PluginLoader<IAssetProviderPlugin> loader = new PluginLoader<IAssetProviderPlugin>(new AssetDataInitialiser(connect));
|
||||||
|
|
||||||
|
// Loader will try to load all providers (MySQL, MSSQL, etc)
|
||||||
|
// unless it is constrainted to the correct "Provider" entry in the addin.xml
|
||||||
|
loader.Add("/OpenSim/AssetData", new PluginProviderFilter (provider));
|
||||||
|
loader.Load();
|
||||||
|
|
||||||
|
return loader.Plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<Addin id="OpenSim.Grid.AssetServer.Plugins.OpenSim" version="0.1">
|
||||||
|
<Runtime>
|
||||||
|
<Import assembly="OpenSim.Grid.AssetServer.Plugins.OpenSim.dll"/>
|
||||||
|
</Runtime>
|
||||||
|
<Dependencies>
|
||||||
|
<Addin id="OpenSim.Grid.NewAssetServer" version="0.1" />
|
||||||
|
</Dependencies>
|
||||||
|
<ExtensionPoint path = "/OpenSim/AssetData">
|
||||||
|
<ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="OpenSim.Framework.IAssetProviderPlugin"/>
|
||||||
|
</ExtensionPoint>
|
||||||
|
<Extension path="/OpenSim/AssetServer/StorageProvider">
|
||||||
|
<Plugin id="OpenSimAssetStorage" provider="OpenSim.Grid.AssetServer.Plugins.OpenSim.dll" type="AssetServer.Plugins.OpenSimAssetStoragePlugin" />
|
||||||
|
</Extension>
|
||||||
|
</Addin>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,153 @@
|
||||||
|
[Config]
|
||||||
|
|
||||||
|
; The port number for the asset server to listen on. If a valid SSL certificate
|
||||||
|
; file is given for SSLCertFile, the HTTPS protocol will be used. Otherwise, the
|
||||||
|
; HTTP protocol is used.
|
||||||
|
ListenPort = 8003
|
||||||
|
|
||||||
|
; An SSL certificate file for the server. If a valid raw certificate or PKCS#12
|
||||||
|
; file is given the server will run in HTTPS mode.
|
||||||
|
;SSLCertFile = server.p12
|
||||||
|
|
||||||
|
[Extensions]
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
; Storage Providers
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Simple storage is a very basic storage system for the purposes of illustrating
|
||||||
|
; a storage backend example. The assets are stored in SimpleAssets/ and
|
||||||
|
; TempAssets/ (which is deleted when the server shuts down). Metadata is
|
||||||
|
; generated for all of the files at startup and when new assets are uploaded.
|
||||||
|
;SimpleStorage
|
||||||
|
|
||||||
|
; OpenSimMySQL storage connects to a MySQL server that has an assets table created
|
||||||
|
; by OpenSim. Open the AssetServer_Config.xml file from OpenSim and use the
|
||||||
|
; database connection string for the database_connect option in the MySQL section
|
||||||
|
; below. This backend combined with the OpenSimFrontend will allow the asset
|
||||||
|
; server to be used as a drop-in replacement for OpenSim.Grid.AssetServer.exe,
|
||||||
|
; while also allowing other frontends to run.
|
||||||
|
OpenSimMySQLStorage
|
||||||
|
|
||||||
|
; Uses Amazon.com's Simple Storage Service (http://aws.amazon.com/s3/) to store
|
||||||
|
; asset data and metadata. This backend does not handle any data requests, as the
|
||||||
|
; data is stored remotely and metadata replies will contain the amazon.com URL
|
||||||
|
; holding the actual asset data. Your Access Key ID and Secret Access Key must be
|
||||||
|
; set in the [Amazon] section below for this backend to function. If
|
||||||
|
; UseCloudFront is true and your Amazon account has CloudFront enabled,
|
||||||
|
; CloudFront URLs will be returned in metadata instead of normal S3 URLs.
|
||||||
|
;AmazonS3Storage
|
||||||
|
|
||||||
|
; Uses memcached (http://www.danga.com/memcached/) as a caching layer on top of
|
||||||
|
; another storage backend. If you use this, make sure you enable another storage
|
||||||
|
; provider as the actual backend, and that the MemcacheStorage line appears in
|
||||||
|
; this config file after the other storage provider.
|
||||||
|
;MemcachedStorage
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
; Inventory Providers
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Simple inventory is a very basic inventory storage system for the purposes of
|
||||||
|
; illustrating an inventory backend example. The inventory is stored in
|
||||||
|
; SimpleInventory/ by creating a folder for each agent that contains all of the
|
||||||
|
; inventory items and folders serialized as XML files.
|
||||||
|
;SimpleInventory
|
||||||
|
|
||||||
|
; OpenSimMySQL inventory connects to a MySQL server that has an inventory table
|
||||||
|
; created by OpenSim. If the OpenSimMySQLStorage backend is also being used, the
|
||||||
|
; inventory and asset tables must be stored in the same database. The
|
||||||
|
; database_connect string in the MySQL section below is used to connect to the
|
||||||
|
; database. This backend combined with the OpenSimInventoryFrontend will allow
|
||||||
|
; the server to be used as a drop-in replacement for
|
||||||
|
; OpenSim.Grid.InventoryServer.exe, while also allowing other frontends to run.
|
||||||
|
OpenSimMySQLInventory
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
; Authentication Providers
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; OpenID provides a direct method of authenticating with the asset server. Users
|
||||||
|
; can provide credentials and receive a session token directly from the asset
|
||||||
|
; server. The OpenIdAuth module provides a browser-based form login and an
|
||||||
|
; XML-based API, both accessible through the URL /authenticate.
|
||||||
|
;OpenIdAuth
|
||||||
|
NullAuthentication
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
; Authorization Providers
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; Authorize all is a dummy authorization module that allows all requests for
|
||||||
|
; metadata, data, and asset creation. Use this extension if your primary
|
||||||
|
; storage provider or front-end interface does not support authentication.
|
||||||
|
AuthorizeAll
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
; Metrics Providers
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; NullMetrics contains empty logging functions. Use this metrics provider if
|
||||||
|
; you want to disable metrics collection and reporting.
|
||||||
|
NullMetrics
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
; Frontends
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
; A simple frontend that provides three basic REST methods. /assetid/metadata
|
||||||
|
; will return the metadata for an asset (currently in LLSD format, that will
|
||||||
|
; change soon). /assetid/data will return the raw asset data with proper
|
||||||
|
; Content-Type and Content-Disposition headers to make downloading assets in a
|
||||||
|
; web browser easy.
|
||||||
|
ReferenceFrontend
|
||||||
|
|
||||||
|
; A frontend that matches the existing OpenSim XML for transferring grid
|
||||||
|
; assets. This will allow the asset server to function as a drop-in replacement
|
||||||
|
; for OpenSim.Grid.AssetServer.exe, and can be combined with OpenSimMySQLStorage
|
||||||
|
; to provide an identical replacement or any other storage backend.
|
||||||
|
OpenSimFrontend
|
||||||
|
|
||||||
|
; A frontend that matches the existing OpenSim XML for handling inventory
|
||||||
|
; transactions. This will allow the asset server to function as a drop-in
|
||||||
|
; replacement for OpenSim.Grid.InventoryServer.exe, and can be combined with
|
||||||
|
; OpenSimMySQLInventory to provide an identical replacement or any other
|
||||||
|
; inventory backend.
|
||||||
|
OpenSimInventoryFrontend
|
||||||
|
|
||||||
|
; An HTML interface for browsing through the asset store
|
||||||
|
BrowseFrontend
|
||||||
|
|
||||||
|
[MySQL]
|
||||||
|
|
||||||
|
; Database connection string used by the OpenSim MySQL backend. If this line is
|
||||||
|
; commented out or missing, the server will look for an AssetServer_Config.xml
|
||||||
|
; in the current working directory. This file is generated by
|
||||||
|
; OpenSim.Grid.AssetServer.exe and can be used without modification.
|
||||||
|
database_connect = "Server=localhost; Database=opensim; User=changeme; Password=changeme;"
|
||||||
|
|
||||||
|
[Amazon]
|
||||||
|
|
||||||
|
; Get these values by logging in to your Amazon S3 account and going to
|
||||||
|
; https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&action=access-key
|
||||||
|
AccessKeyID = xxxxxxxxxxxxxxxxxxxx
|
||||||
|
SecretAccessKey = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
|
|
||||||
|
; The bucket, or namespace, in your Amazon S3 account for storing assets in.
|
||||||
|
; Bucket names on S3 are global identifiers, and must be unique. Think up
|
||||||
|
; something clever or random.
|
||||||
|
BucketName = changeme
|
||||||
|
|
||||||
|
; Amazon CloudFront is a Content Distribution Network for S3 stores. If this is
|
||||||
|
; set to true, AmazonS3Storage will try to locate the first available CloudFront
|
||||||
|
; distribution tied to the active S3 bucket. If no usable distribution is found,
|
||||||
|
; a new one will be created.
|
||||||
|
UseCloudFront = true
|
||||||
|
|
||||||
|
[Memcached]
|
||||||
|
|
||||||
|
; A comma-separated list of the memcached servers that make up your caching
|
||||||
|
; pool. Each server is a hostname or IP address, optionally followed by a
|
||||||
|
; colon and port number if the server is not listening on the default 11211
|
||||||
|
; port.
|
||||||
|
Servers = localhost
|
|
@ -0,0 +1,21 @@
|
||||||
|
<Addin id="OpenSim.Grid.NewAssetServer" isroot="true" version="0.1">
|
||||||
|
<Runtime>
|
||||||
|
<Import assembly="OpenSim.Grid.NewAssetServer.exe"/>
|
||||||
|
<Import assembly="OpenSim.Framework.dll"/>
|
||||||
|
</Runtime>
|
||||||
|
<ExtensionPoint path="/OpenSim/AssetServer/StorageProvider">
|
||||||
|
<ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IAssetStorageProvider"/>
|
||||||
|
</ExtensionPoint>
|
||||||
|
<ExtensionPoint path="/OpenSim/AssetServer/InventoryProvider">
|
||||||
|
<ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IInventoryProvider"/>
|
||||||
|
</ExtensionPoint>
|
||||||
|
<ExtensionPoint path="/OpenSim/AssetServer/AuthenticationProvider">
|
||||||
|
<ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IAuthenticationProvider"/>
|
||||||
|
</ExtensionPoint>
|
||||||
|
<ExtensionPoint path="/OpenSim/AssetServer/AuthorizationProvider">
|
||||||
|
<ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IAuthorizationProvider"/>
|
||||||
|
</ExtensionPoint>
|
||||||
|
<ExtensionPoint path="/OpenSim/AssetServer/MetricsProvider">
|
||||||
|
<ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IMetricsProvider"/>
|
||||||
|
</ExtensionPoint>
|
||||||
|
</Addin>
|
Loading…
Reference in New Issue