diff options
Diffstat (limited to '')
24 files changed, 5893 insertions, 0 deletions
diff --git a/OpenSim/Grid/NewAssetServer/AssetServer.cs b/OpenSim/Grid/NewAssetServer/AssetServer.cs new file mode 100644 index 0000000..c6864e7 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/AssetServer.cs | |||
@@ -0,0 +1,262 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.IO; | ||
33 | using System.Net; | ||
34 | using System.Reflection; | ||
35 | using System.Security.Cryptography.X509Certificates; | ||
36 | using System.ServiceProcess; | ||
37 | using ExtensionLoader; | ||
38 | using ExtensionLoader.Config; | ||
39 | using HttpServer; | ||
40 | using log4net; | ||
41 | using OpenSim.Framework; | ||
42 | |||
43 | namespace AssetServer | ||
44 | { | ||
45 | public class AssetServer : ServiceBase | ||
46 | { | ||
47 | public const string CONFIG_FILE = "AssetServer.ini"; | ||
48 | |||
49 | public WebServer HttpServer; | ||
50 | public IniConfigSource ConfigFile; | ||
51 | |||
52 | public IAssetStorageProvider StorageProvider; | ||
53 | public IInventoryProvider InventoryProvider; | ||
54 | public IAuthenticationProvider AuthenticationProvider; | ||
55 | public IAuthorizationProvider AuthorizationProvider; | ||
56 | public IMetricsProvider MetricsProvider; | ||
57 | |||
58 | public AssetServer() | ||
59 | { | ||
60 | this.ServiceName = "OpenSimAssetServer"; | ||
61 | } | ||
62 | |||
63 | public bool Start() | ||
64 | { | ||
65 | Logger.Log.Info("Starting Asset Server"); | ||
66 | List<string> extensionList = null; | ||
67 | int port = 0; | ||
68 | X509Certificate2 serverCert = null; | ||
69 | |||
70 | try { ConfigFile = new IniConfigSource(CONFIG_FILE); } | ||
71 | catch (Exception) | ||
72 | { | ||
73 | Logger.Log.Error("Failed to load the config file " + CONFIG_FILE); | ||
74 | return false; | ||
75 | } | ||
76 | |||
77 | try | ||
78 | { | ||
79 | IConfig extensionConfig = ConfigFile.Configs["Config"]; | ||
80 | |||
81 | // Load the port number to listen on | ||
82 | port = extensionConfig.GetInt("ListenPort"); | ||
83 | |||
84 | // Load the server certificate file | ||
85 | string certFile = extensionConfig.GetString("SSLCertFile"); | ||
86 | if (!String.IsNullOrEmpty(certFile)) | ||
87 | serverCert = new X509Certificate2(certFile); | ||
88 | } | ||
89 | catch (Exception) | ||
90 | { | ||
91 | Logger.Log.Error("Failed to load [Config] section from " + CONFIG_FILE); | ||
92 | return false; | ||
93 | } | ||
94 | |||
95 | try | ||
96 | { | ||
97 | // Load the extension list (and ordering) from our config file | ||
98 | IConfig extensionConfig = ConfigFile.Configs["Extensions"]; | ||
99 | extensionList = new List<string>(extensionConfig.GetKeys()); | ||
100 | } | ||
101 | catch (Exception) | ||
102 | { | ||
103 | Logger.Log.Error("Failed to load [Extensions] section from " + CONFIG_FILE); | ||
104 | return false; | ||
105 | } | ||
106 | |||
107 | //try | ||
108 | //{ | ||
109 | // // Create a reference list for C# extensions compiled at runtime | ||
110 | // List<string> references = new List<string>(); | ||
111 | // references.Add("OpenMetaverseTypes.dll"); | ||
112 | // references.Add("OpenMetaverse.dll"); | ||
113 | // references.Add("OpenMetaverse.StructuredData.dll"); | ||
114 | // references.Add("OpenMetaverse.Http.dll"); | ||
115 | // references.Add("ExtensionLoader.dll"); | ||
116 | // references.Add("AssetServer.exe"); | ||
117 | |||
118 | // // Get a list of all of the members of AssetServer that are interfaces | ||
119 | // List<FieldInfo> assignables = ExtensionLoader<AssetServer>.GetInterfaces(this); | ||
120 | |||
121 | // // Load all of the extensions | ||
122 | // ExtensionLoader<AssetServer>.LoadAllExtensions( | ||
123 | // Assembly.GetExecutingAssembly(), | ||
124 | // Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), | ||
125 | // extensionList, | ||
126 | // references, | ||
127 | // "AssetServer.*.dll", | ||
128 | // "AssetServer.*.cs", | ||
129 | // this, | ||
130 | // assignables); | ||
131 | //} | ||
132 | //catch (ExtensionException ex) | ||
133 | //{ | ||
134 | // Logger.Log.Error("Interface loading failed, shutting down: " + ex.Message); | ||
135 | // if (ex.InnerException != null) | ||
136 | // Logger.Log.Error(ex.InnerException.Message, ex.InnerException); | ||
137 | // Stop(); | ||
138 | // return false; | ||
139 | //} | ||
140 | |||
141 | StorageProvider = LoadAssetServerPlugin() as IAssetStorageProvider; | ||
142 | |||
143 | try | ||
144 | { | ||
145 | InitHttpServer(port, serverCert); | ||
146 | } | ||
147 | catch (Exception ex) | ||
148 | { | ||
149 | Logger.Log.Error("Initializing the HTTP server failed, shutting down: " + ex.Message); | ||
150 | Stop(); | ||
151 | return false; | ||
152 | } | ||
153 | |||
154 | // Start all of the extensions | ||
155 | //foreach (IExtension<AssetServer> extension in ExtensionLoader<AssetServer>.Extensions) | ||
156 | //{ | ||
157 | // Logger.Log.Info("Starting extension " + extension.GetType().Name); | ||
158 | // extension.Start(this); | ||
159 | //} | ||
160 | |||
161 | return true; | ||
162 | } | ||
163 | |||
164 | public void Shutdown() | ||
165 | { | ||
166 | foreach (IExtension<AssetServer> extension in ExtensionLoader<AssetServer>.Extensions) | ||
167 | { | ||
168 | Logger.Log.Debug("Disposing extension " + extension.GetType().Name); | ||
169 | try { extension.Stop(); } | ||
170 | catch (Exception ex) | ||
171 | { Logger.Log.ErrorFormat("Failure shutting down extension {0}: {1}", extension.GetType().Name, ex.Message); } | ||
172 | } | ||
173 | |||
174 | if (HttpServer != null) | ||
175 | HttpServer.Stop(); | ||
176 | } | ||
177 | |||
178 | void InitHttpServer(int port, X509Certificate serverCert) | ||
179 | { | ||
180 | if (serverCert != null) | ||
181 | HttpServer = new WebServer(IPAddress.Any, port, serverCert, null, false); | ||
182 | else | ||
183 | HttpServer = new WebServer(IPAddress.Any, port); | ||
184 | |||
185 | HttpServer.LogWriter = new log4netLogWriter(Logger.Log); | ||
186 | |||
187 | HttpServer.Set404Handler( | ||
188 | delegate(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
189 | { | ||
190 | Logger.Log.Warn("Requested page was not found: " + request.Uri.PathAndQuery); | ||
191 | |||
192 | string notFoundString = "<html><head><title>Page Not Found</title></head><body>The requested page or method was not found</body></html>"; | ||
193 | byte[] buffer = System.Text.Encoding.UTF8.GetBytes(notFoundString); | ||
194 | response.Body.Write(buffer, 0, buffer.Length); | ||
195 | response.Status = HttpStatusCode.NotFound; | ||
196 | return true; | ||
197 | } | ||
198 | ); | ||
199 | |||
200 | HttpServer.Start(); | ||
201 | |||
202 | Logger.Log.Info("Asset server is listening on port " + port); | ||
203 | } | ||
204 | |||
205 | #region ServiceBase Overrides | ||
206 | |||
207 | protected override void OnStart(string[] args) | ||
208 | { | ||
209 | Start(); | ||
210 | } | ||
211 | protected override void OnStop() | ||
212 | { | ||
213 | Shutdown(); | ||
214 | } | ||
215 | |||
216 | #endregion | ||
217 | |||
218 | private IAssetServerPlugin LoadAssetServerPlugin() | ||
219 | { | ||
220 | PluginLoader<IAssetServerPlugin> loader = new PluginLoader<IAssetServerPlugin>(new AssetServerPluginInitialiser(this)); | ||
221 | |||
222 | //loader.Add ("/OpenSim/AssetServer/StorageProvider", new PluginProviderFilter (provider)); | ||
223 | loader.Add("/OpenSim/AssetServer/StorageProvider", new PluginCountConstraint(1)); | ||
224 | loader.Load(); | ||
225 | |||
226 | return loader.Plugin; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | public class log4netLogWriter : ILogWriter | ||
231 | { | ||
232 | ILog Log; | ||
233 | |||
234 | public log4netLogWriter(ILog log) | ||
235 | { | ||
236 | Log = log; | ||
237 | } | ||
238 | |||
239 | public void Write(object source, LogPrio prio, string message) | ||
240 | { | ||
241 | switch (prio) | ||
242 | { | ||
243 | case LogPrio.Trace: | ||
244 | case LogPrio.Debug: | ||
245 | Log.DebugFormat("{0}: {1}", source, message); | ||
246 | break; | ||
247 | case LogPrio.Info: | ||
248 | Log.InfoFormat("{0}: {1}", source, message); | ||
249 | break; | ||
250 | case LogPrio.Warning: | ||
251 | Log.WarnFormat("{0}: {1}", source, message); | ||
252 | break; | ||
253 | case LogPrio.Error: | ||
254 | Log.ErrorFormat("{0}: {1}", source, message); | ||
255 | break; | ||
256 | case LogPrio.Fatal: | ||
257 | Log.FatalFormat("{0}: {1}", source, message); | ||
258 | break; | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/AuthorizeAll.cs b/OpenSim/Grid/NewAssetServer/Extensions/AuthorizeAll.cs new file mode 100644 index 0000000..f112c5e --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/AuthorizeAll.cs | |||
@@ -0,0 +1,78 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using ExtensionLoader; | ||
32 | using OpenMetaverse; | ||
33 | |||
34 | namespace AssetServer.Extensions | ||
35 | { | ||
36 | public class AuthorizeAll : IExtension<AssetServer>, IAuthorizationProvider | ||
37 | { | ||
38 | AssetServer server; | ||
39 | |||
40 | public AuthorizeAll() | ||
41 | { | ||
42 | } | ||
43 | |||
44 | public void Start(AssetServer server) | ||
45 | { | ||
46 | this.server = server; | ||
47 | } | ||
48 | |||
49 | public void Stop() | ||
50 | { | ||
51 | } | ||
52 | |||
53 | public bool IsMetadataAuthorized(UUID authToken, UUID assetID) | ||
54 | { | ||
55 | return true; | ||
56 | } | ||
57 | |||
58 | public bool IsDataAuthorized(UUID authToken, UUID assetID) | ||
59 | { | ||
60 | return true; | ||
61 | } | ||
62 | |||
63 | public bool IsCreateAuthorized(UUID authToken) | ||
64 | { | ||
65 | return true; | ||
66 | } | ||
67 | |||
68 | public bool IsInventoryReadAuthorized(UUID authToken, Uri owner) | ||
69 | { | ||
70 | return true; | ||
71 | } | ||
72 | |||
73 | public bool IsInventoryWriteAuthorized(UUID authToken, Uri owner) | ||
74 | { | ||
75 | return true; | ||
76 | } | ||
77 | } | ||
78 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/BrowseFrontend.cs b/OpenSim/Grid/NewAssetServer/Extensions/BrowseFrontend.cs new file mode 100644 index 0000000..9f42722 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/BrowseFrontend.cs | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Collections.Specialized; | ||
33 | using System.Net; | ||
34 | using System.Text; | ||
35 | using System.Web; | ||
36 | using ExtensionLoader; | ||
37 | using OpenMetaverse; | ||
38 | using HttpServer; | ||
39 | |||
40 | namespace AssetServer.Extensions | ||
41 | { | ||
42 | public class BrowseFrontend : IExtension<AssetServer> | ||
43 | { | ||
44 | AssetServer server; | ||
45 | |||
46 | public BrowseFrontend() | ||
47 | { | ||
48 | } | ||
49 | |||
50 | public void Start(AssetServer server) | ||
51 | { | ||
52 | this.server = server; | ||
53 | |||
54 | // Request for / or /?... | ||
55 | server.HttpServer.AddHandler("get", null, @"(^/$)|(^/\?.*)", BrowseRequestHandler); | ||
56 | } | ||
57 | |||
58 | public void Stop() | ||
59 | { | ||
60 | } | ||
61 | |||
62 | bool BrowseRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
63 | { | ||
64 | const int ASSETS_PER_PAGE = 25; | ||
65 | const string HEADER = "<html><head><title>Asset Server</title></head><body>"; | ||
66 | const string TABLE_HEADER = | ||
67 | "<table><tr><th>Name</th><th>Description</th><th>Type</th><th>ID</th><th>Temporary</th><th>SHA-1</th></tr>"; | ||
68 | const string TABLE_FOOTER = "</table>"; | ||
69 | const string FOOTER = "</body></html>"; | ||
70 | |||
71 | UUID authToken = Utils.GetAuthToken(request); | ||
72 | |||
73 | StringBuilder html = new StringBuilder(); | ||
74 | int start = 0; | ||
75 | uint page = 0; | ||
76 | |||
77 | if (!String.IsNullOrEmpty(request.Uri.Query)) | ||
78 | { | ||
79 | NameValueCollection query = HttpUtility.ParseQueryString(request.Uri.Query); | ||
80 | if (!String.IsNullOrEmpty(query["page"]) && UInt32.TryParse(query["page"], out page)) | ||
81 | start = (int)page * ASSETS_PER_PAGE; | ||
82 | } | ||
83 | |||
84 | html.AppendLine(HEADER); | ||
85 | |||
86 | html.AppendLine("<p>"); | ||
87 | if (page > 0) | ||
88 | html.AppendFormat("<a href=\"{0}?page={1}\">< Previous Page</a> | ", request.Uri.AbsolutePath, page - 1); | ||
89 | html.AppendFormat("<a href=\"{0}?page={1}\">Next Page ></a>", request.Uri.AbsolutePath, page + 1); | ||
90 | html.AppendLine("</p>"); | ||
91 | |||
92 | html.AppendLine(TABLE_HEADER); | ||
93 | |||
94 | server.StorageProvider.ForEach( | ||
95 | delegate(Metadata data) | ||
96 | { | ||
97 | if (server.AuthorizationProvider.IsMetadataAuthorized(authToken, data.ID)) | ||
98 | { | ||
99 | html.AppendLine(String.Format( | ||
100 | "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td></tr>", | ||
101 | data.Name, data.Description, data.ContentType, data.ID, data.Temporary, | ||
102 | BitConverter.ToString(data.SHA1).Replace("-", String.Empty))); | ||
103 | } | ||
104 | else | ||
105 | { | ||
106 | html.AppendLine(String.Format( | ||
107 | "<tr><td>[Protected Asset]</td><td> </td><td> </td><td>{0}</td><td>{1}</td><td> </td></tr>", | ||
108 | data.ID, data.Temporary)); | ||
109 | } | ||
110 | }, start, ASSETS_PER_PAGE | ||
111 | ); | ||
112 | |||
113 | html.AppendLine(TABLE_FOOTER); | ||
114 | |||
115 | html.AppendLine(FOOTER); | ||
116 | |||
117 | byte[] responseData = System.Text.Encoding.UTF8.GetBytes(html.ToString()); | ||
118 | |||
119 | response.Status = HttpStatusCode.OK; | ||
120 | response.Body.Write(responseData, 0, responseData.Length); | ||
121 | response.Body.Flush(); | ||
122 | return true; | ||
123 | } | ||
124 | } | ||
125 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/DBConnString.cs b/OpenSim/Grid/NewAssetServer/Extensions/DBConnString.cs new file mode 100644 index 0000000..3c5f971 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/DBConnString.cs | |||
@@ -0,0 +1,78 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Xml; | ||
32 | using ExtensionLoader.Config; | ||
33 | using MySql.Data.MySqlClient; | ||
34 | |||
35 | namespace AssetServer.Extensions | ||
36 | { | ||
37 | public static class DBConnString | ||
38 | { | ||
39 | private static string connectionString; | ||
40 | |||
41 | /// <summary> | ||
42 | /// Parses the MySQL connection string out of either the asset server | ||
43 | /// .ini or a OpenSim-style .xml file and caches the result for future | ||
44 | /// requests | ||
45 | /// </summary> | ||
46 | public static string GetConnectionString(IniConfigSource configFile) | ||
47 | { | ||
48 | if (connectionString == null) | ||
49 | { | ||
50 | // Try parsing from the ini file | ||
51 | try | ||
52 | { | ||
53 | // Load the extension list (and ordering) from our config file | ||
54 | IConfig extensionConfig = configFile.Configs["MySQL"]; | ||
55 | connectionString = extensionConfig.GetString("database_connect", null); | ||
56 | } | ||
57 | catch (Exception) { } | ||
58 | |||
59 | if (connectionString != null) | ||
60 | { | ||
61 | // Force MySQL's broken connection pooling off | ||
62 | MySqlConnectionStringBuilder builder = new MySqlConnectionStringBuilder(connectionString); | ||
63 | builder.Pooling = false; | ||
64 | if (String.IsNullOrEmpty(builder.Database)) | ||
65 | Logger.Log.Error("No database selected in the connectionString: " + connectionString); | ||
66 | connectionString = builder.ToString(); | ||
67 | } | ||
68 | else | ||
69 | { | ||
70 | Logger.Log.Error("Database connection string is missing, check that the database_connect line is " + | ||
71 | "correct and uncommented in AssetServer.ini"); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | return connectionString; | ||
76 | } | ||
77 | } | ||
78 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/NullAuthentication.cs b/OpenSim/Grid/NewAssetServer/Extensions/NullAuthentication.cs new file mode 100644 index 0000000..9d38bf4 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/NullAuthentication.cs | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using ExtensionLoader; | ||
32 | using OpenMetaverse; | ||
33 | |||
34 | namespace AssetServer.Extensions | ||
35 | { | ||
36 | public class NullAuthentication : IExtension<AssetServer>, IAuthenticationProvider | ||
37 | { | ||
38 | AssetServer server; | ||
39 | |||
40 | public NullAuthentication() | ||
41 | { | ||
42 | } | ||
43 | |||
44 | public void Start(AssetServer server) | ||
45 | { | ||
46 | this.server = server; | ||
47 | } | ||
48 | |||
49 | public void Stop() | ||
50 | { | ||
51 | } | ||
52 | |||
53 | public void AddIdentifier(UUID authToken, Uri identifier) | ||
54 | { | ||
55 | } | ||
56 | |||
57 | public bool RemoveIdentifier(UUID authToken) | ||
58 | { | ||
59 | return true; | ||
60 | } | ||
61 | |||
62 | public bool TryGetIdentifier(UUID authToken, out Uri identifier) | ||
63 | { | ||
64 | identifier = null; | ||
65 | return true; | ||
66 | } | ||
67 | } | ||
68 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/NullMetrics.cs b/OpenSim/Grid/NewAssetServer/Extensions/NullMetrics.cs new file mode 100644 index 0000000..84657c4 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/NullMetrics.cs | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using ExtensionLoader; | ||
32 | using OpenMetaverse; | ||
33 | |||
34 | namespace AssetServer.Extensions | ||
35 | { | ||
36 | public class NullMetrics : IExtension<AssetServer>, IMetricsProvider | ||
37 | { | ||
38 | AssetServer server; | ||
39 | |||
40 | public NullMetrics() | ||
41 | { | ||
42 | } | ||
43 | |||
44 | public void Start(AssetServer server) | ||
45 | { | ||
46 | this.server = server; | ||
47 | } | ||
48 | |||
49 | public void Stop() | ||
50 | { | ||
51 | } | ||
52 | |||
53 | public void LogAssetMetadataFetch(string extension, BackendResponse response, UUID assetID, DateTime time) | ||
54 | { | ||
55 | Logger.Log.DebugFormat("[{0}] AssetMetadataFetch(): AssetID: {1}, Response: {2}", extension, assetID, response); | ||
56 | } | ||
57 | |||
58 | public void LogAssetDataFetch(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time) | ||
59 | { | ||
60 | Logger.Log.DebugFormat("[{0}] AssetDataFetch(): AssetID: {1}, DataSize: {2}, Response: {3}", extension, assetID, | ||
61 | dataSize, response); | ||
62 | } | ||
63 | |||
64 | public void LogAssetCreate(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time) | ||
65 | { | ||
66 | Logger.Log.DebugFormat("[{0}] AssetCreate(): AssetID: {1}, DataSize: {2}, Response: {3}", extension, assetID, | ||
67 | dataSize, response); | ||
68 | } | ||
69 | |||
70 | public void LogInventoryFetch(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time) | ||
71 | { | ||
72 | Logger.Log.DebugFormat("[{0}] InventoryFetch(): ObjID: {1}, Folder: {2}, OwnerID: {3}, Response: {4}", extension, | ||
73 | objID, folder, owner, response); | ||
74 | } | ||
75 | |||
76 | public void LogInventoryFetchFolderContents(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time) | ||
77 | { | ||
78 | Logger.Log.DebugFormat("[{0}] InventoryFetchFolderContents(): FolderID: {1}, OwnerID: {2}, Response: {3}", extension, | ||
79 | folderID, owner, response); | ||
80 | } | ||
81 | |||
82 | public void LogInventoryFetchFolderList(string extension, BackendResponse response, Uri owner, DateTime time) | ||
83 | { | ||
84 | Logger.Log.DebugFormat("[{0}] InventoryFetchFolderList(): OwnerID: {1}, Response: {2}", extension, | ||
85 | owner, response); | ||
86 | } | ||
87 | |||
88 | public void LogInventoryFetchInventory(string extension, BackendResponse response, Uri owner, DateTime time) | ||
89 | { | ||
90 | Logger.Log.DebugFormat("[{0}] InventoryFetchInventory(): OwnerID: {1}, Response: {2}", extension, | ||
91 | owner, response); | ||
92 | } | ||
93 | |||
94 | public void LogInventoryFetchActiveGestures(string extension, BackendResponse response, Uri owner, DateTime time) | ||
95 | { | ||
96 | Logger.Log.DebugFormat("[{0}] InventoryFetchActiveGestures(): OwnerID: {1}, Response: {2}", extension, | ||
97 | owner, response); | ||
98 | } | ||
99 | |||
100 | public void LogInventoryCreate(string extension, BackendResponse response, Uri owner, bool folder, DateTime time) | ||
101 | { | ||
102 | Logger.Log.DebugFormat("[{0}] InventoryCreate(): OwnerID: {1}, Response: {2}", extension, | ||
103 | owner, response); | ||
104 | } | ||
105 | |||
106 | public void LogInventoryCreateInventory(string extension, BackendResponse response, DateTime time) | ||
107 | { | ||
108 | Logger.Log.DebugFormat("[{0}] InventoryCreateInventory(): Response: {1}", extension, | ||
109 | response); | ||
110 | } | ||
111 | |||
112 | public void LogInventoryDelete(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time) | ||
113 | { | ||
114 | Logger.Log.DebugFormat("[{0}] InventoryDelete(): OwnerID: {1}, Folder: {2}, Response: {3}", extension, | ||
115 | owner, folder, response); | ||
116 | } | ||
117 | |||
118 | public void LogInventoryPurgeFolder(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time) | ||
119 | { | ||
120 | Logger.Log.DebugFormat("[{0}] InventoryPurgeFolder(): OwnerID: {1}, FolderID: {2}, Response: {3}", extension, | ||
121 | owner, response); | ||
122 | } | ||
123 | } | ||
124 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/OpenSimFrontend.cs b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimFrontend.cs new file mode 100644 index 0000000..7a645b3 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimFrontend.cs | |||
@@ -0,0 +1,215 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.IO; | ||
34 | using System.Xml; | ||
35 | using ExtensionLoader; | ||
36 | using OpenMetaverse; | ||
37 | using HttpServer; | ||
38 | |||
39 | namespace AssetServer.Extensions | ||
40 | { | ||
41 | public class OpenSimFrontend : IExtension<AssetServer> | ||
42 | { | ||
43 | AssetServer server; | ||
44 | |||
45 | public OpenSimFrontend() | ||
46 | { | ||
47 | } | ||
48 | |||
49 | public void Start(AssetServer server) | ||
50 | { | ||
51 | this.server = server; | ||
52 | |||
53 | // Asset request | ||
54 | server.HttpServer.AddHandler("get", null, @"^/assets/", AssetRequestHandler); | ||
55 | |||
56 | // Asset creation | ||
57 | server.HttpServer.AddHandler("post", null, @"^/assets/", AssetPostHandler); | ||
58 | } | ||
59 | |||
60 | public void Stop() | ||
61 | { | ||
62 | } | ||
63 | |||
64 | bool AssetRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
65 | { | ||
66 | UUID assetID; | ||
67 | // Split the URL up to get the asset ID out | ||
68 | string[] rawUrl = request.Uri.PathAndQuery.Split('/'); | ||
69 | |||
70 | if (rawUrl.Length >= 3 && rawUrl[2].Length >= 36 && UUID.TryParse(rawUrl[2].Substring(0, 36), out assetID)) | ||
71 | { | ||
72 | Metadata metadata; | ||
73 | byte[] assetData; | ||
74 | BackendResponse dataResponse; | ||
75 | |||
76 | if ((dataResponse = server.StorageProvider.TryFetchDataMetadata(assetID, out metadata, out assetData)) == BackendResponse.Success) | ||
77 | { | ||
78 | MemoryStream stream = new MemoryStream(); | ||
79 | |||
80 | XmlWriterSettings settings = new XmlWriterSettings(); | ||
81 | settings.Indent = true; | ||
82 | XmlWriter writer = XmlWriter.Create(stream, settings); | ||
83 | |||
84 | writer.WriteStartDocument(); | ||
85 | writer.WriteStartElement("AssetBase"); | ||
86 | writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance"); | ||
87 | writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema"); | ||
88 | writer.WriteStartElement("FullID"); | ||
89 | writer.WriteStartElement("Guid"); | ||
90 | writer.WriteString(assetID.ToString()); | ||
91 | writer.WriteEndElement(); | ||
92 | writer.WriteEndElement(); | ||
93 | writer.WriteStartElement("ID"); | ||
94 | writer.WriteString(assetID.ToString()); | ||
95 | writer.WriteEndElement(); | ||
96 | writer.WriteStartElement("Data"); | ||
97 | writer.WriteBase64(assetData, 0, assetData.Length); | ||
98 | writer.WriteEndElement(); | ||
99 | writer.WriteStartElement("Type"); | ||
100 | writer.WriteValue(Utils.ContentTypeToSLAssetType(metadata.ContentType)); | ||
101 | writer.WriteEndElement(); | ||
102 | writer.WriteStartElement("Name"); | ||
103 | writer.WriteString(metadata.Name); | ||
104 | writer.WriteEndElement(); | ||
105 | writer.WriteStartElement("Description"); | ||
106 | writer.WriteString(metadata.Description); | ||
107 | writer.WriteEndElement(); | ||
108 | writer.WriteStartElement("Local"); | ||
109 | writer.WriteValue(false); | ||
110 | writer.WriteEndElement(); | ||
111 | writer.WriteStartElement("Temporary"); | ||
112 | writer.WriteValue(metadata.Temporary); | ||
113 | writer.WriteEndElement(); | ||
114 | writer.WriteEndElement(); | ||
115 | writer.WriteEndDocument(); | ||
116 | |||
117 | writer.Flush(); | ||
118 | byte[] buffer = stream.GetBuffer(); | ||
119 | |||
120 | response.Status = HttpStatusCode.OK; | ||
121 | response.ContentType = "application/xml"; | ||
122 | response.ContentLength = stream.Length; | ||
123 | response.Body.Write(buffer, 0, (int)stream.Length); | ||
124 | response.Body.Flush(); | ||
125 | } | ||
126 | else | ||
127 | { | ||
128 | Logger.Log.WarnFormat("Failed to fetch asset data or metadata for {0}: {1}", assetID, dataResponse); | ||
129 | response.Status = HttpStatusCode.NotFound; | ||
130 | } | ||
131 | } | ||
132 | else | ||
133 | { | ||
134 | Logger.Log.Warn("Unrecognized OpenSim asset request: " + request.Uri.PathAndQuery); | ||
135 | } | ||
136 | |||
137 | return true; | ||
138 | } | ||
139 | |||
140 | bool AssetPostHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
141 | { | ||
142 | byte[] assetData = null; | ||
143 | Metadata metadata = new Metadata(); | ||
144 | |||
145 | Logger.Log.Debug("Handling OpenSim asset upload"); | ||
146 | |||
147 | try | ||
148 | { | ||
149 | using (XmlReader reader = XmlReader.Create(request.Body)) | ||
150 | { | ||
151 | reader.MoveToContent(); | ||
152 | reader.ReadStartElement("AssetBase"); | ||
153 | |||
154 | reader.ReadStartElement("FullID"); | ||
155 | UUID.TryParse(reader.ReadElementContentAsString("Guid", String.Empty), out metadata.ID); | ||
156 | reader.ReadEndElement(); | ||
157 | reader.ReadStartElement("ID"); | ||
158 | reader.Skip(); | ||
159 | reader.ReadEndElement(); | ||
160 | |||
161 | // HACK: Broken on Mono. https://bugzilla.novell.com/show_bug.cgi?id=464229 | ||
162 | //int readBytes = 0; | ||
163 | //byte[] buffer = new byte[1024]; | ||
164 | //MemoryStream stream = new MemoryStream(); | ||
165 | //BinaryWriter writer = new BinaryWriter(stream); | ||
166 | //while ((readBytes = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length)) > 0) | ||
167 | // writer.Write(buffer, 0, readBytes); | ||
168 | //writer.Flush(); | ||
169 | //assetData = stream.GetBuffer(); | ||
170 | //Array.Resize<byte>(ref assetData, (int)stream.Length); | ||
171 | |||
172 | assetData = Convert.FromBase64String(reader.ReadElementContentAsString()); | ||
173 | |||
174 | int type; | ||
175 | Int32.TryParse(reader.ReadElementContentAsString("Type", String.Empty), out type); | ||
176 | metadata.ContentType = Utils.SLAssetTypeToContentType(type); | ||
177 | metadata.Name = reader.ReadElementContentAsString("Name", String.Empty); | ||
178 | metadata.Description = reader.ReadElementContentAsString("Description", String.Empty); | ||
179 | Boolean.TryParse(reader.ReadElementContentAsString("Local", String.Empty), out metadata.Temporary); | ||
180 | Boolean.TryParse(reader.ReadElementContentAsString("Temporary", String.Empty), out metadata.Temporary); | ||
181 | |||
182 | reader.ReadEndElement(); | ||
183 | } | ||
184 | |||
185 | if (assetData != null && assetData.Length > 0) | ||
186 | { | ||
187 | metadata.SHA1 = OpenMetaverse.Utils.SHA1(assetData); | ||
188 | metadata.CreationDate = DateTime.Now; | ||
189 | |||
190 | BackendResponse storageResponse = server.StorageProvider.TryCreateAsset(metadata, assetData); | ||
191 | |||
192 | if (storageResponse == BackendResponse.Success) | ||
193 | response.Status = HttpStatusCode.Created; | ||
194 | else if (storageResponse == BackendResponse.NotFound) | ||
195 | response.Status = HttpStatusCode.NotFound; | ||
196 | else | ||
197 | response.Status = HttpStatusCode.InternalServerError; | ||
198 | } | ||
199 | else | ||
200 | { | ||
201 | Logger.Log.Warn("AssetPostHandler called with no asset data"); | ||
202 | response.Status = HttpStatusCode.BadRequest; | ||
203 | } | ||
204 | } | ||
205 | catch (Exception ex) | ||
206 | { | ||
207 | Logger.Log.Warn("Failed to parse POST data (expecting AssetBase): " + ex.Message); | ||
208 | response.Status = HttpStatusCode.BadRequest; | ||
209 | } | ||
210 | |||
211 | Logger.Log.Debug("Finished handling OpenSim asset upload, Status: " + response.Status.ToString()); | ||
212 | return true; | ||
213 | } | ||
214 | } | ||
215 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/OpenSimInventoryFrontend.cs b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimInventoryFrontend.cs new file mode 100644 index 0000000..a559f19 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimInventoryFrontend.cs | |||
@@ -0,0 +1,636 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.IO; | ||
34 | using System.Xml; | ||
35 | using ExtensionLoader; | ||
36 | using OpenMetaverse; | ||
37 | using OpenMetaverse.StructuredData; | ||
38 | using HttpServer; | ||
39 | |||
40 | namespace AssetServer.Extensions | ||
41 | { | ||
42 | public class OpenSimInventoryFrontend : IExtension<AssetServer> | ||
43 | { | ||
44 | AssetServer server; | ||
45 | Utils.InventoryItemSerializer itemSerializer = new Utils.InventoryItemSerializer(); | ||
46 | Utils.InventoryFolderSerializer folderSerializer = new Utils.InventoryFolderSerializer(); | ||
47 | Utils.InventoryCollectionSerializer collectionSerializer = new Utils.InventoryCollectionSerializer(); | ||
48 | |||
49 | public OpenSimInventoryFrontend() | ||
50 | { | ||
51 | } | ||
52 | |||
53 | public void Start(AssetServer server) | ||
54 | { | ||
55 | this.server = server; | ||
56 | |||
57 | server.HttpServer.AddHandler("post", null, @"^/GetInventory/", GetInventoryHandler); | ||
58 | server.HttpServer.AddHandler("post", null, @"^/CreateInventory/", CreateInventoryHandler); | ||
59 | server.HttpServer.AddHandler("post", null, @"^/NewFolder/", NewFolderHandler); | ||
60 | server.HttpServer.AddHandler("post", null, @"^/UpdateFolder/", UpdateFolderHandler); | ||
61 | server.HttpServer.AddHandler("post", null, @"^/MoveFolder/", MoveFolderHandler); | ||
62 | server.HttpServer.AddHandler("post", null, @"^/PurgeFolder/", PurgeFolderHandler); | ||
63 | server.HttpServer.AddHandler("post", null, @"^/NewItem/", NewItemHandler); | ||
64 | server.HttpServer.AddHandler("post", null, @"^/DeleteItem/", DeleteItemHandler); | ||
65 | server.HttpServer.AddHandler("post", null, @"^/RootFolders/", RootFoldersHandler); | ||
66 | server.HttpServer.AddHandler("post", null, @"^/ActiveGestures/", ActiveGesturesHandler); | ||
67 | } | ||
68 | |||
69 | public void Stop() | ||
70 | { | ||
71 | } | ||
72 | |||
73 | bool GetInventoryHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
74 | { | ||
75 | UUID sessionID, agentID; | ||
76 | UUID ownerID = DeserializeUUID(request.Body, out agentID, out sessionID); | ||
77 | |||
78 | if (ownerID != UUID.Zero) | ||
79 | { | ||
80 | Logger.Log.Warn("GetInventory is not scalable on some inventory backends, avoid calling it wherever possible"); | ||
81 | |||
82 | Uri owner = Utils.GetOpenSimUri(ownerID); | ||
83 | InventoryCollection inventory; | ||
84 | BackendResponse storageResponse = server.InventoryProvider.TryFetchInventory(owner, out inventory); | ||
85 | |||
86 | if (storageResponse == BackendResponse.Success) | ||
87 | { | ||
88 | collectionSerializer.Serialize(response.Body, inventory); | ||
89 | response.Body.Flush(); | ||
90 | } | ||
91 | else if (storageResponse == BackendResponse.NotFound) | ||
92 | { | ||
93 | // Return an empty inventory set to mimic OpenSim.Grid.InventoryServer.exe | ||
94 | inventory = new InventoryCollection(); | ||
95 | inventory.UserID = ownerID; | ||
96 | inventory.Folders = new Dictionary<UUID, InventoryFolder>(); | ||
97 | inventory.Items = new Dictionary<UUID, InventoryItem>(); | ||
98 | collectionSerializer.Serialize(response.Body, inventory); | ||
99 | response.Body.Flush(); | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | response.Status = HttpStatusCode.InternalServerError; | ||
104 | } | ||
105 | } | ||
106 | else | ||
107 | { | ||
108 | response.Status = HttpStatusCode.BadRequest; | ||
109 | } | ||
110 | |||
111 | return true; | ||
112 | } | ||
113 | |||
114 | bool CreateInventoryHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
115 | { | ||
116 | UUID ownerID = DeserializeUUID(request.Body); | ||
117 | |||
118 | if (ownerID != UUID.Zero) | ||
119 | { | ||
120 | Uri owner = Utils.GetOpenSimUri(ownerID); | ||
121 | Logger.Log.DebugFormat("Created URI {0} for inventory creation", owner); | ||
122 | |||
123 | InventoryFolder rootFolder = new InventoryFolder("My Inventory", ownerID, UUID.Zero, (short)AssetType.Folder); | ||
124 | BackendResponse storageResponse = server.InventoryProvider.TryCreateInventory(owner, rootFolder); | ||
125 | if (storageResponse == BackendResponse.Success) | ||
126 | { | ||
127 | CreateFolder("Animations", ownerID, rootFolder.ID, AssetType.Animation); | ||
128 | CreateFolder("Body Parts", ownerID, rootFolder.ID, AssetType.Bodypart); | ||
129 | CreateFolder("Calling Cards", ownerID, rootFolder.ID, AssetType.CallingCard); | ||
130 | CreateFolder("Clothing", ownerID, rootFolder.ID, AssetType.Clothing); | ||
131 | CreateFolder("Gestures", ownerID, rootFolder.ID, AssetType.Gesture); | ||
132 | CreateFolder("Landmarks", ownerID, rootFolder.ID, AssetType.Landmark); | ||
133 | CreateFolder("Lost and Found", ownerID, rootFolder.ID, AssetType.LostAndFoundFolder); | ||
134 | CreateFolder("Notecards", ownerID, rootFolder.ID, AssetType.Notecard); | ||
135 | CreateFolder("Objects", ownerID, rootFolder.ID, AssetType.Object); | ||
136 | CreateFolder("Photo Album", ownerID, rootFolder.ID, AssetType.SnapshotFolder); | ||
137 | CreateFolder("Scripts", ownerID, rootFolder.ID, AssetType.LSLText); | ||
138 | CreateFolder("Sounds", ownerID, rootFolder.ID, AssetType.Sound); | ||
139 | CreateFolder("Textures", ownerID, rootFolder.ID, AssetType.Texture); | ||
140 | CreateFolder("Trash", ownerID, rootFolder.ID, AssetType.TrashFolder); | ||
141 | |||
142 | SerializeBool(response.Body, true); | ||
143 | return true; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | SerializeBool(response.Body, false); | ||
148 | return true; | ||
149 | } | ||
150 | |||
151 | bool NewFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
152 | { | ||
153 | UUID agentID, sessionID; | ||
154 | InventoryFolder folder = DeserializeFolder(request.Body, out agentID, out sessionID); | ||
155 | |||
156 | if (folder != null) | ||
157 | { | ||
158 | Uri owner = Utils.GetOpenSimUri(folder.Owner); | ||
159 | |||
160 | // Some calls that are moving or updating a folder instead of creating a new one | ||
161 | // will pass in an InventoryFolder without the name set. If this is the case we | ||
162 | // need to look up the name first | ||
163 | if (String.IsNullOrEmpty(folder.Name)) | ||
164 | { | ||
165 | InventoryFolder oldFolder; | ||
166 | if (server.InventoryProvider.TryFetchFolder(owner, folder.ID, out oldFolder) == BackendResponse.Success) | ||
167 | folder.Name = oldFolder.Name; | ||
168 | } | ||
169 | |||
170 | BackendResponse storageResponse = server.InventoryProvider.TryCreateFolder(owner, folder); | ||
171 | |||
172 | if (storageResponse == BackendResponse.Success) | ||
173 | { | ||
174 | SerializeBool(response.Body, true); | ||
175 | return true; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | SerializeBool(response.Body, false); | ||
180 | return true; | ||
181 | } | ||
182 | |||
183 | bool UpdateFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
184 | { | ||
185 | return NewFolderHandler(client, request, response); | ||
186 | } | ||
187 | |||
188 | bool MoveFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
189 | { | ||
190 | return NewFolderHandler(client, request, response); | ||
191 | } | ||
192 | |||
193 | bool PurgeFolderHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
194 | { | ||
195 | UUID agentID, sessionID; | ||
196 | InventoryFolder folder = DeserializeFolder(request.Body, out agentID, out sessionID); | ||
197 | |||
198 | if (folder != null) | ||
199 | { | ||
200 | Uri owner = Utils.GetOpenSimUri(folder.Owner); | ||
201 | BackendResponse storageResponse = server.InventoryProvider.TryPurgeFolder(owner, folder.ID); | ||
202 | |||
203 | if (storageResponse == BackendResponse.Success) | ||
204 | { | ||
205 | SerializeBool(response.Body, true); | ||
206 | return true; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | SerializeBool(response.Body, false); | ||
211 | return true; | ||
212 | } | ||
213 | |||
214 | bool NewItemHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
215 | { | ||
216 | UUID agentID, sessionID; | ||
217 | InventoryItem item = DeserializeItem(request.Body, out agentID, out sessionID); | ||
218 | |||
219 | if (item != null) | ||
220 | { | ||
221 | Uri owner = Utils.GetOpenSimUri(agentID); | ||
222 | BackendResponse storageResponse = server.InventoryProvider.TryCreateItem(owner, item); | ||
223 | |||
224 | if (storageResponse == BackendResponse.Success) | ||
225 | { | ||
226 | SerializeBool(response.Body, true); | ||
227 | return true; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | SerializeBool(response.Body, false); | ||
232 | return true; | ||
233 | } | ||
234 | |||
235 | bool DeleteItemHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
236 | { | ||
237 | UUID agentID, sessionID; | ||
238 | InventoryItem item = DeserializeItem(request.Body, out agentID, out sessionID); | ||
239 | |||
240 | if (item != null) | ||
241 | { | ||
242 | Uri owner = Utils.GetOpenSimUri(item.Owner); | ||
243 | BackendResponse storageResponse = server.InventoryProvider.TryDeleteItem(owner, item.ID); | ||
244 | |||
245 | if (storageResponse == BackendResponse.Success) | ||
246 | { | ||
247 | SerializeBool(response.Body, true); | ||
248 | return true; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | SerializeBool(response.Body, false); | ||
253 | return true; | ||
254 | } | ||
255 | |||
256 | bool RootFoldersHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
257 | { | ||
258 | UUID ownerID = DeserializeUUID(request.Body); | ||
259 | |||
260 | if (ownerID != UUID.Zero) | ||
261 | { | ||
262 | Uri owner = Utils.GetOpenSimUri(ownerID); | ||
263 | List<InventoryFolder> skeleton; | ||
264 | BackendResponse storageResponse = server.InventoryProvider.TryFetchFolderList(owner, out skeleton); | ||
265 | |||
266 | if (storageResponse == BackendResponse.Success) | ||
267 | { | ||
268 | SerializeFolderList(response.Body, skeleton); | ||
269 | } | ||
270 | else if (storageResponse == BackendResponse.NotFound) | ||
271 | { | ||
272 | // Return an empty set of inventory so the requester knows that | ||
273 | // an inventory needs to be created for this agent | ||
274 | SerializeFolderList(response.Body, new List<InventoryFolder>(0)); | ||
275 | } | ||
276 | else | ||
277 | { | ||
278 | response.Status = HttpStatusCode.InternalServerError; | ||
279 | } | ||
280 | } | ||
281 | else | ||
282 | { | ||
283 | response.Status = HttpStatusCode.BadRequest; | ||
284 | } | ||
285 | |||
286 | return true; | ||
287 | } | ||
288 | |||
289 | bool ActiveGesturesHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
290 | { | ||
291 | UUID ownerID = DeserializeUUID(request.Body); | ||
292 | |||
293 | if (ownerID != UUID.Zero) | ||
294 | { | ||
295 | Uri owner = Utils.GetOpenSimUri(ownerID); | ||
296 | List<InventoryItem> gestures; | ||
297 | BackendResponse storageResponse = server.InventoryProvider.TryFetchActiveGestures(owner, out gestures); | ||
298 | |||
299 | if (storageResponse == BackendResponse.Success) | ||
300 | { | ||
301 | SerializeItemList(response.Body, gestures); | ||
302 | } | ||
303 | else if (storageResponse == BackendResponse.NotFound) | ||
304 | { | ||
305 | // Return an empty set of gestures to match OpenSim.Grid.InventoryServer.exe behavior | ||
306 | SerializeItemList(response.Body, new List<InventoryItem>(0)); | ||
307 | } | ||
308 | else | ||
309 | { | ||
310 | response.Status = HttpStatusCode.InternalServerError; | ||
311 | } | ||
312 | } | ||
313 | else | ||
314 | { | ||
315 | response.Status = HttpStatusCode.BadRequest; | ||
316 | } | ||
317 | |||
318 | return true; | ||
319 | } | ||
320 | |||
321 | BackendResponse CreateFolder(string name, UUID ownerID, UUID parentID, AssetType assetType) | ||
322 | { | ||
323 | InventoryFolder folder = new InventoryFolder(name, ownerID, parentID, (short)assetType); | ||
324 | Uri owner = Utils.GetOpenSimUri(ownerID); | ||
325 | return server.InventoryProvider.TryCreateFolder(owner, folder); | ||
326 | } | ||
327 | |||
328 | UUID DeserializeUUID(Stream stream) | ||
329 | { | ||
330 | UUID id = UUID.Zero; | ||
331 | |||
332 | try | ||
333 | { | ||
334 | using (XmlReader reader = XmlReader.Create(stream)) | ||
335 | { | ||
336 | reader.MoveToContent(); | ||
337 | UUID.TryParse(reader.ReadElementContentAsString("guid", String.Empty), out id); | ||
338 | } | ||
339 | } | ||
340 | catch (Exception ex) | ||
341 | { | ||
342 | Logger.Log.Warn("Failed to parse POST data (expecting guid): " + ex.Message); | ||
343 | } | ||
344 | |||
345 | return id; | ||
346 | } | ||
347 | |||
348 | UUID DeserializeUUID(Stream stream, out UUID agentID, out UUID sessionID) | ||
349 | { | ||
350 | UUID id; | ||
351 | |||
352 | try | ||
353 | { | ||
354 | using (XmlReader reader = XmlReader.Create(stream)) | ||
355 | { | ||
356 | reader.MoveToContent(); | ||
357 | reader.ReadStartElement("RestSessionObjectOfGuid"); | ||
358 | UUID.TryParse(reader.ReadElementContentAsString("SessionID", String.Empty), out sessionID); | ||
359 | UUID.TryParse(reader.ReadElementContentAsString("AvatarID", String.Empty), out agentID); | ||
360 | UUID.TryParse(reader.ReadElementContentAsString("Body", String.Empty), out id); | ||
361 | reader.ReadEndElement(); | ||
362 | } | ||
363 | } | ||
364 | catch (Exception ex) | ||
365 | { | ||
366 | Logger.Log.Warn("Failed to parse GetInventory POST data: " + ex.Message); | ||
367 | agentID = UUID.Zero; | ||
368 | sessionID = UUID.Zero; | ||
369 | return UUID.Zero; | ||
370 | } | ||
371 | |||
372 | return id; | ||
373 | } | ||
374 | |||
375 | InventoryFolder DeserializeFolder(Stream stream, out UUID agentID, out UUID sessionID) | ||
376 | { | ||
377 | InventoryFolder folder = new InventoryFolder(); | ||
378 | |||
379 | try | ||
380 | { | ||
381 | using (XmlReader reader = XmlReader.Create(stream)) | ||
382 | { | ||
383 | reader.MoveToContent(); | ||
384 | reader.ReadStartElement("RestSessionObjectOfInventoryFolderBase"); | ||
385 | UUID.TryParse(reader.ReadElementContentAsString("SessionID", String.Empty), out sessionID); | ||
386 | UUID.TryParse(reader.ReadElementContentAsString("AvatarID", String.Empty), out agentID); | ||
387 | reader.ReadStartElement("Body"); | ||
388 | if (reader.Name == "Name") | ||
389 | folder.Name = reader.ReadElementContentAsString("Name", String.Empty); | ||
390 | else | ||
391 | folder.Name = String.Empty; | ||
392 | ReadUUID(reader, "Owner", out folder.Owner); | ||
393 | ReadUUID(reader, "ParentID", out folder.ParentID); | ||
394 | ReadUUID(reader, "ID", out folder.ID); | ||
395 | Int16.TryParse(reader.ReadElementContentAsString("Type", String.Empty), out folder.Type); | ||
396 | UInt16.TryParse(reader.ReadElementContentAsString("Version", String.Empty), out folder.Version); | ||
397 | reader.ReadEndElement(); | ||
398 | reader.ReadEndElement(); | ||
399 | } | ||
400 | } | ||
401 | catch (Exception ex) | ||
402 | { | ||
403 | Logger.Log.Warn("Failed to parse POST data (expecting InventoryFolderBase): " + ex.Message); | ||
404 | agentID = UUID.Zero; | ||
405 | sessionID = UUID.Zero; | ||
406 | return null; | ||
407 | } | ||
408 | |||
409 | return folder; | ||
410 | } | ||
411 | |||
412 | InventoryItem DeserializeItem(Stream stream, out UUID agentID, out UUID sessionID) | ||
413 | { | ||
414 | InventoryItem item = new InventoryItem(); | ||
415 | |||
416 | try | ||
417 | { | ||
418 | using (XmlReader reader = XmlReader.Create(stream)) | ||
419 | { | ||
420 | reader.MoveToContent(); | ||
421 | reader.ReadStartElement("RestSessionObjectOfInventoryItemBase"); | ||
422 | UUID.TryParse(reader.ReadElementContentAsString("SessionID", String.Empty), out sessionID); | ||
423 | UUID.TryParse(reader.ReadElementContentAsString("AvatarID", String.Empty), out agentID); | ||
424 | reader.ReadStartElement("Body"); | ||
425 | ReadUUID(reader, "ID", out item.ID); | ||
426 | Int32.TryParse(reader.ReadElementContentAsString("InvType", String.Empty), out item.InvType); | ||
427 | ReadUUID(reader, "Folder", out item.Folder); | ||
428 | ReadUUID(reader, "Owner", out item.Owner); | ||
429 | ReadUUID(reader, "Creator", out item.Creator); | ||
430 | item.Name = reader.ReadElementContentAsString("Name", String.Empty); | ||
431 | item.Description = reader.ReadElementContentAsString("Description", String.Empty); | ||
432 | UInt32.TryParse(reader.ReadElementContentAsString("NextPermissions", String.Empty), out item.NextPermissions); | ||
433 | UInt32.TryParse(reader.ReadElementContentAsString("CurrentPermissions", String.Empty), out item.CurrentPermissions); | ||
434 | UInt32.TryParse(reader.ReadElementContentAsString("BasePermissions", String.Empty), out item.BasePermissions); | ||
435 | UInt32.TryParse(reader.ReadElementContentAsString("EveryOnePermissions", String.Empty), out item.EveryOnePermissions); | ||
436 | UInt32.TryParse(reader.ReadElementContentAsString("GroupPermissions", String.Empty), out item.GroupPermissions); | ||
437 | Int32.TryParse(reader.ReadElementContentAsString("AssetType", String.Empty), out item.AssetType); | ||
438 | ReadUUID(reader, "AssetID", out item.AssetID); | ||
439 | ReadUUID(reader, "GroupID", out item.GroupID); | ||
440 | Boolean.TryParse(reader.ReadElementContentAsString("GroupOwned", String.Empty), out item.GroupOwned); | ||
441 | Int32.TryParse(reader.ReadElementContentAsString("SalePrice", String.Empty), out item.SalePrice); | ||
442 | Byte.TryParse(reader.ReadElementContentAsString("SaleType", String.Empty), out item.SaleType); | ||
443 | UInt32.TryParse(reader.ReadElementContentAsString("Flags", String.Empty), out item.Flags); | ||
444 | Int32.TryParse(reader.ReadElementContentAsString("CreationDate", String.Empty), out item.CreationDate); | ||
445 | reader.ReadEndElement(); | ||
446 | reader.ReadEndElement(); | ||
447 | } | ||
448 | } | ||
449 | catch (Exception ex) | ||
450 | { | ||
451 | Logger.Log.Warn("Failed to parse POST data (expecting InventoryItemBase): " + ex.Message); | ||
452 | agentID = UUID.Zero; | ||
453 | sessionID = UUID.Zero; | ||
454 | return null; | ||
455 | } | ||
456 | |||
457 | return item; | ||
458 | } | ||
459 | |||
460 | void SerializeBool(Stream stream, bool value) | ||
461 | { | ||
462 | using (XmlWriter writer = XmlWriter.Create(stream)) | ||
463 | { | ||
464 | writer.WriteStartDocument(); | ||
465 | writer.WriteStartElement("boolean"); | ||
466 | writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance"); | ||
467 | writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema"); | ||
468 | writer.WriteString(value.ToString().ToLower()); | ||
469 | writer.WriteEndElement(); | ||
470 | writer.WriteEndDocument(); | ||
471 | writer.Flush(); | ||
472 | } | ||
473 | |||
474 | stream.Flush(); | ||
475 | } | ||
476 | |||
477 | void SerializeFolderList(Stream stream, List<InventoryFolder> folders) | ||
478 | { | ||
479 | using (XmlWriter writer = XmlWriter.Create(stream)) | ||
480 | { | ||
481 | writer.WriteStartDocument(); | ||
482 | writer.WriteStartElement("ArrayOfInventoryFolderBase"); | ||
483 | writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance"); | ||
484 | writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema"); | ||
485 | |||
486 | if (folders != null) | ||
487 | { | ||
488 | foreach (InventoryFolder folder in folders) | ||
489 | { | ||
490 | writer.WriteStartElement("InventoryFolderBase"); | ||
491 | writer.WriteElementString("Name", folder.Name); | ||
492 | WriteUUID(writer, "Owner", folder.Owner); | ||
493 | WriteUUID(writer, "ParentID", folder.ParentID); | ||
494 | WriteUUID(writer, "ID", folder.ID); | ||
495 | writer.WriteElementString("Type", XmlConvert.ToString(folder.Type)); | ||
496 | writer.WriteElementString("Version", XmlConvert.ToString(folder.Version)); | ||
497 | writer.WriteEndElement(); | ||
498 | } | ||
499 | } | ||
500 | |||
501 | writer.WriteEndElement(); | ||
502 | writer.WriteEndDocument(); | ||
503 | |||
504 | writer.Flush(); | ||
505 | } | ||
506 | |||
507 | stream.Flush(); | ||
508 | } | ||
509 | |||
510 | void SerializeItemList(Stream stream, List<InventoryItem> items) | ||
511 | { | ||
512 | using (XmlWriter writer = XmlWriter.Create(stream)) | ||
513 | { | ||
514 | writer.WriteStartDocument(); | ||
515 | writer.WriteStartElement("ArrayOfInventoryItemBase"); | ||
516 | writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance"); | ||
517 | writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema"); | ||
518 | |||
519 | if (items != null) | ||
520 | { | ||
521 | foreach (InventoryItem item in items) | ||
522 | { | ||
523 | writer.WriteStartElement("InventoryItemBase"); | ||
524 | WriteUUID(writer, "ID", item.ID); | ||
525 | writer.WriteElementString("InvType", XmlConvert.ToString(item.InvType)); | ||
526 | WriteUUID(writer, "Folder", item.Folder); | ||
527 | WriteUUID(writer, "Owner", item.Owner); | ||
528 | WriteUUID(writer, "Creator", item.Creator); | ||
529 | writer.WriteElementString("Name", item.Name); | ||
530 | writer.WriteElementString("Description", item.Description); | ||
531 | writer.WriteElementString("NextPermissions", XmlConvert.ToString(item.NextPermissions)); | ||
532 | writer.WriteElementString("CurrentPermissions", XmlConvert.ToString(item.CurrentPermissions)); | ||
533 | writer.WriteElementString("BasePermissions", XmlConvert.ToString(item.BasePermissions)); | ||
534 | writer.WriteElementString("EveryOnePermissions", XmlConvert.ToString(item.EveryOnePermissions)); | ||
535 | writer.WriteElementString("GroupPermissions", XmlConvert.ToString(item.GroupPermissions)); | ||
536 | writer.WriteElementString("AssetType", XmlConvert.ToString(item.AssetType)); | ||
537 | WriteUUID(writer, "AssetID", item.AssetID); | ||
538 | WriteUUID(writer, "GroupID", item.GroupID); | ||
539 | writer.WriteElementString("GroupOwned", XmlConvert.ToString(item.GroupOwned)); | ||
540 | writer.WriteElementString("SalePrice", XmlConvert.ToString(item.SalePrice)); | ||
541 | writer.WriteElementString("SaleType", XmlConvert.ToString(item.SaleType)); | ||
542 | writer.WriteElementString("Flags", XmlConvert.ToString(item.Flags)); | ||
543 | writer.WriteElementString("CreationDate", XmlConvert.ToString(item.CreationDate)); | ||
544 | writer.WriteEndElement(); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | writer.WriteEndElement(); | ||
549 | writer.WriteEndDocument(); | ||
550 | |||
551 | writer.Flush(); | ||
552 | } | ||
553 | |||
554 | stream.Flush(); | ||
555 | } | ||
556 | |||
557 | void WriteUUID(XmlWriter writer, string name, UUID id) | ||
558 | { | ||
559 | writer.WriteStartElement(name); | ||
560 | writer.WriteElementString("Guid", XmlConvert.ToString(id.Guid)); | ||
561 | writer.WriteEndElement(); | ||
562 | } | ||
563 | |||
564 | void ReadUUID(XmlReader reader, string name, out UUID id) | ||
565 | { | ||
566 | reader.ReadStartElement(name); | ||
567 | UUID.TryParse(reader.ReadElementContentAsString("Guid", String.Empty), out id); | ||
568 | reader.ReadEndElement(); | ||
569 | } | ||
570 | } | ||
571 | |||
572 | #region OpenSim AssetType | ||
573 | |||
574 | /// <summary> | ||
575 | /// The different types of grid assets | ||
576 | /// </summary> | ||
577 | public enum AssetType : sbyte | ||
578 | { | ||
579 | /// <summary>Unknown asset type</summary> | ||
580 | Unknown = -1, | ||
581 | /// <summary>Texture asset, stores in JPEG2000 J2C stream format</summary> | ||
582 | Texture = 0, | ||
583 | /// <summary>Sound asset</summary> | ||
584 | Sound = 1, | ||
585 | /// <summary>Calling card for another avatar</summary> | ||
586 | CallingCard = 2, | ||
587 | /// <summary>Link to a location in world</summary> | ||
588 | Landmark = 3, | ||
589 | // <summary>Legacy script asset, you should never see one of these</summary> | ||
590 | //[Obsolete] | ||
591 | //Script = 4, | ||
592 | /// <summary>Collection of textures and parameters that can be | ||
593 | /// worn by an avatar</summary> | ||
594 | Clothing = 5, | ||
595 | /// <summary>Primitive that can contain textures, sounds, | ||
596 | /// scripts and more</summary> | ||
597 | Object = 6, | ||
598 | /// <summary>Notecard asset</summary> | ||
599 | Notecard = 7, | ||
600 | /// <summary>Holds a collection of inventory items</summary> | ||
601 | Folder = 8, | ||
602 | /// <summary>Root inventory folder</summary> | ||
603 | RootFolder = 9, | ||
604 | /// <summary>Linden scripting language script</summary> | ||
605 | LSLText = 10, | ||
606 | /// <summary>LSO bytecode for a script</summary> | ||
607 | LSLBytecode = 11, | ||
608 | /// <summary>Uncompressed TGA texture</summary> | ||
609 | TextureTGA = 12, | ||
610 | /// <summary>Collection of textures and shape parameters that can | ||
611 | /// be worn</summary> | ||
612 | Bodypart = 13, | ||
613 | /// <summary>Trash folder</summary> | ||
614 | TrashFolder = 14, | ||
615 | /// <summary>Snapshot folder</summary> | ||
616 | SnapshotFolder = 15, | ||
617 | /// <summary>Lost and found folder</summary> | ||
618 | LostAndFoundFolder = 16, | ||
619 | /// <summary>Uncompressed sound</summary> | ||
620 | SoundWAV = 17, | ||
621 | /// <summary>Uncompressed TGA non-square image, not to be used as a | ||
622 | /// texture</summary> | ||
623 | ImageTGA = 18, | ||
624 | /// <summary>Compressed JPEG non-square image, not to be used as a | ||
625 | /// texture</summary> | ||
626 | ImageJPEG = 19, | ||
627 | /// <summary>Animation</summary> | ||
628 | Animation = 20, | ||
629 | /// <summary>Sequence of animations, sounds, chat, and pauses</summary> | ||
630 | Gesture = 21, | ||
631 | /// <summary>Simstate file</summary> | ||
632 | Simstate = 22, | ||
633 | } | ||
634 | |||
635 | #endregion OpenSim AssetType | ||
636 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/OpenSimMySQLInventory.cs b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimMySQLInventory.cs new file mode 100644 index 0000000..1b5facd --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimMySQLInventory.cs | |||
@@ -0,0 +1,804 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.Data; | ||
34 | using MySql.Data.MySqlClient; | ||
35 | using ExtensionLoader; | ||
36 | using ExtensionLoader.Config; | ||
37 | using OpenMetaverse; | ||
38 | using OpenMetaverse.StructuredData; | ||
39 | |||
40 | namespace AssetServer.Extensions | ||
41 | { | ||
42 | public class OpenSimMySQLInventory : IExtension<AssetServer>, IInventoryProvider | ||
43 | { | ||
44 | const string EXTENSION_NAME = "OpenSimMySQLInventory"; // Used in metrics reporting | ||
45 | |||
46 | AssetServer server; | ||
47 | |||
48 | public OpenSimMySQLInventory() | ||
49 | { | ||
50 | } | ||
51 | |||
52 | #region Required Interfaces | ||
53 | |||
54 | public void Start(AssetServer server) | ||
55 | { | ||
56 | this.server = server; | ||
57 | |||
58 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
59 | { | ||
60 | try | ||
61 | { | ||
62 | dbConnection.Open(); | ||
63 | Logger.Log.Info("Connected to MySQL inventory backend: " + dbConnection.ServerVersion); | ||
64 | } | ||
65 | catch (MySqlException ex) | ||
66 | { | ||
67 | Logger.Log.Error("Connection to MySQL inventory backend failed: " + ex.Message); | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | public void Stop() | ||
73 | { | ||
74 | } | ||
75 | |||
76 | public BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItem item) | ||
77 | { | ||
78 | item = null; | ||
79 | BackendResponse ret; | ||
80 | |||
81 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
82 | { | ||
83 | IDataReader reader; | ||
84 | |||
85 | try | ||
86 | { | ||
87 | dbConnection.Open(); | ||
88 | |||
89 | IDbCommand command = dbConnection.CreateCommand(); | ||
90 | command.CommandText = String.Format("SELECT assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," + | ||
91 | "inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," + | ||
92 | "creationDate,groupID,groupOwned,flags,avatarID,parentFolderID,inventoryGroupPermissions FROM inventoryitems WHERE inventoryID='{0}'", | ||
93 | itemID.ToString()); | ||
94 | reader = command.ExecuteReader(); | ||
95 | |||
96 | if (reader.Read()) | ||
97 | { | ||
98 | item = new InventoryItem(); | ||
99 | item.ID = itemID; | ||
100 | item.AssetID = UUID.Parse(reader.GetString(0)); | ||
101 | item.AssetType = reader.GetInt32(1); | ||
102 | item.Name = reader.GetString(2); | ||
103 | item.Description = reader.GetString(3); | ||
104 | item.NextPermissions = (uint)reader.GetInt32(4); | ||
105 | item.CurrentPermissions = (uint)reader.GetInt32(5); | ||
106 | item.InvType = reader.GetInt32(6); | ||
107 | item.Creator = UUID.Parse(reader.GetString(7)); | ||
108 | item.BasePermissions = (uint)reader.GetInt32(8); | ||
109 | item.EveryOnePermissions = (uint)reader.GetInt32(9); | ||
110 | item.SalePrice = reader.GetInt32(10); | ||
111 | item.SaleType = reader.GetByte(11); | ||
112 | item.CreationDate = reader.GetInt32(12); | ||
113 | item.GroupID = UUID.Parse(reader.GetString(13)); | ||
114 | item.GroupOwned = reader.GetBoolean(14); | ||
115 | item.Flags = (uint)reader.GetInt32(15); | ||
116 | item.Owner = UUID.Parse(reader.GetString(16)); | ||
117 | item.Folder = UUID.Parse(reader.GetString(17)); | ||
118 | item.GroupPermissions = (uint)reader.GetInt32(18); | ||
119 | |||
120 | ret = BackendResponse.Success; | ||
121 | } | ||
122 | else | ||
123 | { | ||
124 | ret = BackendResponse.NotFound; | ||
125 | } | ||
126 | } | ||
127 | catch (MySqlException ex) | ||
128 | { | ||
129 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
130 | ret = BackendResponse.Failure; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now); | ||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | public BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolder folder) | ||
139 | { | ||
140 | folder = null; | ||
141 | BackendResponse ret; | ||
142 | |||
143 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
144 | { | ||
145 | IDataReader reader; | ||
146 | |||
147 | try | ||
148 | { | ||
149 | dbConnection.Open(); | ||
150 | |||
151 | IDbCommand command = dbConnection.CreateCommand(); | ||
152 | command.CommandText = String.Format("SELECT folderName,type,version,agentID,parentFolderID FROM inventoryfolders WHERE folderID='{0}'", | ||
153 | folderID.ToString()); | ||
154 | reader = command.ExecuteReader(); | ||
155 | |||
156 | if (reader.Read()) | ||
157 | { | ||
158 | folder = new InventoryFolder(); | ||
159 | folder.Children = null; // This call only returns data for the folder itself, no children data | ||
160 | folder.ID = folderID; | ||
161 | folder.Name = reader.GetString(0); | ||
162 | folder.Type = reader.GetInt16(1); | ||
163 | folder.Version = (ushort)reader.GetInt16(2); | ||
164 | folder.Owner = UUID.Parse(reader.GetString(3)); | ||
165 | folder.ParentID = UUID.Parse(reader.GetString(4)); | ||
166 | |||
167 | ret = BackendResponse.Success; | ||
168 | } | ||
169 | else | ||
170 | { | ||
171 | ret = BackendResponse.NotFound; | ||
172 | } | ||
173 | } | ||
174 | catch (MySqlException ex) | ||
175 | { | ||
176 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
177 | ret = BackendResponse.Failure; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now); | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | public BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents) | ||
186 | { | ||
187 | contents = null; | ||
188 | BackendResponse ret; | ||
189 | |||
190 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
191 | { | ||
192 | IDataReader reader; | ||
193 | |||
194 | try | ||
195 | { | ||
196 | dbConnection.Open(); | ||
197 | |||
198 | contents = new InventoryCollection(); | ||
199 | |||
200 | #region Folder retrieval | ||
201 | |||
202 | IDbCommand command = dbConnection.CreateCommand(); | ||
203 | command.CommandText = String.Format("SELECT folderName,type,version,agentID,folderID FROM inventoryfolders WHERE parentFolderID='{0}'", | ||
204 | folderID.ToString()); | ||
205 | reader = command.ExecuteReader(); | ||
206 | |||
207 | contents.Folders = new Dictionary<UUID, InventoryFolder>(); | ||
208 | |||
209 | while (reader.Read()) | ||
210 | { | ||
211 | InventoryFolder folder = new InventoryFolder(); | ||
212 | folder.ParentID = folderID; | ||
213 | folder.Children = null; // This call doesn't do recursion | ||
214 | folder.Name = reader.GetString(0); | ||
215 | folder.Type = reader.GetInt16(1); | ||
216 | folder.Version = (ushort)reader.GetInt16(2); | ||
217 | folder.Owner = UUID.Parse(reader.GetString(3)); | ||
218 | folder.ID = UUID.Parse(reader.GetString(4)); | ||
219 | |||
220 | contents.Folders.Add(folder.ID, folder); | ||
221 | contents.UserID = folder.Owner; | ||
222 | } | ||
223 | |||
224 | reader.Close(); | ||
225 | |||
226 | #endregion Folder retrieval | ||
227 | |||
228 | #region Item retrieval | ||
229 | |||
230 | command = dbConnection.CreateCommand(); | ||
231 | command.CommandText = String.Format("SELECT assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," + | ||
232 | "inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," + | ||
233 | "creationDate,groupID,groupOwned,flags,avatarID,inventoryID,inventoryGroupPermissions FROM inventoryitems WHERE parentFolderID='{0}'", | ||
234 | folderID.ToString()); | ||
235 | reader = command.ExecuteReader(); | ||
236 | |||
237 | contents.Items = new Dictionary<UUID, InventoryItem>(); | ||
238 | |||
239 | while (reader.Read()) | ||
240 | { | ||
241 | InventoryItem item = new InventoryItem(); | ||
242 | item.Folder = folderID; | ||
243 | item.AssetID = UUID.Parse(reader.GetString(0)); | ||
244 | item.AssetType = reader.GetInt32(1); | ||
245 | item.Name = reader.GetString(2); | ||
246 | item.Description = reader.GetString(3); | ||
247 | item.NextPermissions = (uint)reader.GetInt32(4); | ||
248 | item.CurrentPermissions = (uint)reader.GetInt32(5); | ||
249 | item.InvType = reader.GetInt32(6); | ||
250 | item.Creator = UUID.Parse(reader.GetString(7)); | ||
251 | item.BasePermissions = (uint)reader.GetInt32(8); | ||
252 | item.EveryOnePermissions = (uint)reader.GetInt32(9); | ||
253 | item.SalePrice = reader.GetInt32(10); | ||
254 | item.SaleType = reader.GetByte(11); | ||
255 | item.CreationDate = reader.GetInt32(12); | ||
256 | item.GroupID = UUID.Parse(reader.GetString(13)); | ||
257 | item.GroupOwned = reader.GetBoolean(14); | ||
258 | item.Flags = (uint)reader.GetInt32(15); | ||
259 | item.Owner = UUID.Parse(reader.GetString(16)); | ||
260 | item.ID = UUID.Parse(reader.GetString(17)); | ||
261 | item.GroupPermissions = (uint)reader.GetInt32(18); | ||
262 | |||
263 | contents.Items.Add(item.ID, item); | ||
264 | contents.UserID = item.Owner; | ||
265 | } | ||
266 | |||
267 | #endregion Item retrieval | ||
268 | |||
269 | ret = BackendResponse.Success; | ||
270 | } | ||
271 | catch (MySqlException ex) | ||
272 | { | ||
273 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
274 | ret = BackendResponse.Failure; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | server.MetricsProvider.LogInventoryFetchFolderContents(EXTENSION_NAME, ret, owner, folderID, DateTime.Now); | ||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | public BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolder> folders) | ||
283 | { | ||
284 | folders = null; | ||
285 | BackendResponse ret; | ||
286 | UUID ownerID; | ||
287 | |||
288 | if (Utils.TryGetOpenSimUUID(owner, out ownerID)) | ||
289 | { | ||
290 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
291 | { | ||
292 | IDataReader reader; | ||
293 | |||
294 | try | ||
295 | { | ||
296 | dbConnection.Open(); | ||
297 | folders = new List<InventoryFolder>(); | ||
298 | |||
299 | IDbCommand command = dbConnection.CreateCommand(); | ||
300 | command.CommandText = String.Format("SELECT folderName,type,version,folderID,parentFolderID FROM inventoryfolders WHERE agentID='{0}'", | ||
301 | ownerID.ToString()); | ||
302 | reader = command.ExecuteReader(); | ||
303 | |||
304 | while (reader.Read()) | ||
305 | { | ||
306 | InventoryFolder folder = new InventoryFolder(); | ||
307 | folder.Owner = ownerID; | ||
308 | folder.Children = null; // This call does not create a folder hierarchy | ||
309 | folder.Name = reader.GetString(0); | ||
310 | folder.Type = reader.GetInt16(1); | ||
311 | folder.Version = (ushort)reader.GetInt16(2); | ||
312 | folder.ID = UUID.Parse(reader.GetString(3)); | ||
313 | folder.ParentID = UUID.Parse(reader.GetString(4)); | ||
314 | |||
315 | folders.Add(folder); | ||
316 | } | ||
317 | |||
318 | ret = BackendResponse.Success; | ||
319 | } | ||
320 | catch (MySqlException ex) | ||
321 | { | ||
322 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
323 | ret = BackendResponse.Failure; | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | else | ||
328 | { | ||
329 | ret = BackendResponse.NotFound; | ||
330 | } | ||
331 | |||
332 | server.MetricsProvider.LogInventoryFetchFolderList(EXTENSION_NAME, ret, owner, DateTime.Now); | ||
333 | return ret; | ||
334 | } | ||
335 | |||
336 | public BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory) | ||
337 | { | ||
338 | inventory = null; | ||
339 | BackendResponse ret; | ||
340 | List<InventoryFolder> folders; | ||
341 | UUID ownerID; | ||
342 | |||
343 | ret = TryFetchFolderList(owner, out folders); | ||
344 | |||
345 | if (ret == BackendResponse.Success) | ||
346 | { | ||
347 | // Add the retrieved folders to the inventory collection | ||
348 | inventory = new InventoryCollection(); | ||
349 | inventory.Folders = new Dictionary<UUID, InventoryFolder>(folders.Count); | ||
350 | foreach (InventoryFolder folder in folders) | ||
351 | inventory.Folders[folder.ID] = folder; | ||
352 | |||
353 | // Fetch inventory items | ||
354 | if (Utils.TryGetOpenSimUUID(owner, out ownerID)) | ||
355 | { | ||
356 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
357 | { | ||
358 | IDataReader reader; | ||
359 | |||
360 | try | ||
361 | { | ||
362 | dbConnection.Open(); | ||
363 | |||
364 | IDbCommand command = dbConnection.CreateCommand(); | ||
365 | command.CommandText = String.Format("SELECT assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," + | ||
366 | "inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," + | ||
367 | "creationDate,groupID,groupOwned,flags,inventoryID,parentFolderID,inventoryGroupPermissions FROM inventoryitems WHERE " + | ||
368 | "avatarID='{0}'", ownerID.ToString()); | ||
369 | reader = command.ExecuteReader(); | ||
370 | |||
371 | inventory.UserID = ownerID; | ||
372 | inventory.Items = new Dictionary<UUID, InventoryItem>(); | ||
373 | |||
374 | while (reader.Read()) | ||
375 | { | ||
376 | InventoryItem item = new InventoryItem(); | ||
377 | item.Owner = ownerID; | ||
378 | item.AssetID = UUID.Parse(reader.GetString(0)); | ||
379 | item.AssetType = reader.GetInt32(1); | ||
380 | item.Name = reader.GetString(2); | ||
381 | item.Description = reader.GetString(3); | ||
382 | item.NextPermissions = (uint)reader.GetInt32(4); | ||
383 | item.CurrentPermissions = (uint)reader.GetInt32(5); | ||
384 | item.InvType = reader.GetInt32(6); | ||
385 | item.Creator = UUID.Parse(reader.GetString(7)); | ||
386 | item.BasePermissions = (uint)reader.GetInt32(8); | ||
387 | item.EveryOnePermissions = (uint)reader.GetInt32(9); | ||
388 | item.SalePrice = reader.GetInt32(10); | ||
389 | item.SaleType = reader.GetByte(11); | ||
390 | item.CreationDate = reader.GetInt32(12); | ||
391 | item.GroupID = UUID.Parse(reader.GetString(13)); | ||
392 | item.GroupOwned = reader.GetBoolean(14); | ||
393 | item.Flags = (uint)reader.GetInt32(15); | ||
394 | item.ID = UUID.Parse(reader.GetString(16)); | ||
395 | item.Folder = UUID.Parse(reader.GetString(17)); | ||
396 | item.GroupPermissions = (uint)reader.GetInt32(18); | ||
397 | |||
398 | inventory.Items.Add(item.ID, item); | ||
399 | } | ||
400 | |||
401 | ret = BackendResponse.Success; | ||
402 | } | ||
403 | catch (MySqlException ex) | ||
404 | { | ||
405 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
406 | ret = BackendResponse.Failure; | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | else | ||
411 | { | ||
412 | ret = BackendResponse.NotFound; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | server.MetricsProvider.LogInventoryFetchInventory(EXTENSION_NAME, ret, owner, DateTime.Now); | ||
417 | return ret; | ||
418 | } | ||
419 | |||
420 | public BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItem> gestures) | ||
421 | { | ||
422 | gestures = null; | ||
423 | BackendResponse ret; | ||
424 | UUID ownerID; | ||
425 | |||
426 | if (Utils.TryGetOpenSimUUID(owner, out ownerID)) | ||
427 | { | ||
428 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
429 | { | ||
430 | IDataReader reader; | ||
431 | |||
432 | try | ||
433 | { | ||
434 | dbConnection.Open(); | ||
435 | |||
436 | MySqlCommand command = new MySqlCommand("SELECT assetID,inventoryName,inventoryDescription,inventoryNextPermissions," + | ||
437 | "inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," + | ||
438 | "creationDate,groupID,groupOwned,inventoryID,parentFolderID,inventoryGroupPermissions FROM inventoryitems WHERE " + | ||
439 | "avatarId=?uuid AND assetType=?type AND flags=1", dbConnection); | ||
440 | command.Parameters.AddWithValue("?uuid", ownerID.ToString()); | ||
441 | command.Parameters.AddWithValue("?type", (int)AssetType.Gesture); | ||
442 | reader = command.ExecuteReader(); | ||
443 | |||
444 | while (reader.Read()) | ||
445 | { | ||
446 | InventoryItem item = new InventoryItem(); | ||
447 | item.Owner = ownerID; | ||
448 | item.AssetType = (int)AssetType.Gesture; | ||
449 | item.Flags = (uint)1; | ||
450 | item.AssetID = UUID.Parse(reader.GetString(0)); | ||
451 | item.Name = reader.GetString(1); | ||
452 | item.Description = reader.GetString(2); | ||
453 | item.NextPermissions = (uint)reader.GetInt32(3); | ||
454 | item.CurrentPermissions = (uint)reader.GetInt32(4); | ||
455 | item.InvType = reader.GetInt32(5); | ||
456 | item.Creator = UUID.Parse(reader.GetString(6)); | ||
457 | item.BasePermissions = (uint)reader.GetInt32(7); | ||
458 | item.EveryOnePermissions = (uint)reader.GetInt32(8); | ||
459 | item.SalePrice = reader.GetInt32(9); | ||
460 | item.SaleType = reader.GetByte(10); | ||
461 | item.CreationDate = reader.GetInt32(11); | ||
462 | item.GroupID = UUID.Parse(reader.GetString(12)); | ||
463 | item.GroupOwned = reader.GetBoolean(13); | ||
464 | item.ID = UUID.Parse(reader.GetString(14)); | ||
465 | item.Folder = UUID.Parse(reader.GetString(15)); | ||
466 | item.GroupPermissions = (uint)reader.GetInt32(16); | ||
467 | |||
468 | gestures.Add(item); | ||
469 | } | ||
470 | |||
471 | ret = BackendResponse.Success; | ||
472 | } | ||
473 | catch (MySqlException ex) | ||
474 | { | ||
475 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
476 | ret = BackendResponse.Failure; | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | else | ||
481 | { | ||
482 | ret = BackendResponse.NotFound; | ||
483 | } | ||
484 | |||
485 | server.MetricsProvider.LogInventoryFetchActiveGestures(EXTENSION_NAME, ret, owner, DateTime.Now); | ||
486 | return ret; | ||
487 | } | ||
488 | |||
489 | public BackendResponse TryCreateItem(Uri owner, InventoryItem item) | ||
490 | { | ||
491 | BackendResponse ret; | ||
492 | |||
493 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
494 | { | ||
495 | try | ||
496 | { | ||
497 | dbConnection.Open(); | ||
498 | |||
499 | MySqlCommand command = new MySqlCommand( | ||
500 | "REPLACE INTO inventoryitems (assetID,assetType,inventoryName,inventoryDescription,inventoryNextPermissions," + | ||
501 | "inventoryCurrentPermissions,invType,creatorID,inventoryBasePermissions,inventoryEveryOnePermissions,salePrice,saleType," + | ||
502 | "creationDate,groupID,groupOwned,flags,inventoryID,avatarID,parentFolderID,inventoryGroupPermissions) VALUES " + | ||
503 | |||
504 | "(?assetID,?assetType,?inventoryName,?inventoryDescription,?inventoryNextPermissions,?inventoryCurrentPermissions,?invType," + | ||
505 | "?creatorID,?inventoryBasePermissions,?inventoryEveryOnePermissions,?salePrice,?saleType,?creationDate,?groupID,?groupOwned," + | ||
506 | "?flags,?inventoryID,?avatarID,?parentFolderID,?inventoryGroupPermissions)", dbConnection); | ||
507 | |||
508 | command.Parameters.AddWithValue("?assetID", item.AssetID.ToString()); | ||
509 | command.Parameters.AddWithValue("?assetType", item.AssetType); | ||
510 | command.Parameters.AddWithValue("?inventoryName", item.Name); | ||
511 | command.Parameters.AddWithValue("?inventoryDescription", item.Description); | ||
512 | command.Parameters.AddWithValue("?inventoryNextPermissions", item.NextPermissions); | ||
513 | command.Parameters.AddWithValue("?inventoryCurrentPermissions", item.CurrentPermissions); | ||
514 | command.Parameters.AddWithValue("?invType", item.InvType); | ||
515 | command.Parameters.AddWithValue("?creatorID", item.Creator.ToString()); | ||
516 | command.Parameters.AddWithValue("?inventoryBasePermissions", item.BasePermissions); | ||
517 | command.Parameters.AddWithValue("?inventoryEveryOnePermissions", item.EveryOnePermissions); | ||
518 | command.Parameters.AddWithValue("?salePrice", item.SalePrice); | ||
519 | command.Parameters.AddWithValue("?saleType", item.SaleType); | ||
520 | command.Parameters.AddWithValue("?creationDate", item.CreationDate); | ||
521 | command.Parameters.AddWithValue("?groupID", item.GroupID.ToString()); | ||
522 | command.Parameters.AddWithValue("?groupOwned", item.GroupOwned); | ||
523 | command.Parameters.AddWithValue("?flags", item.Flags); | ||
524 | command.Parameters.AddWithValue("?inventoryID", item.ID); | ||
525 | command.Parameters.AddWithValue("?avatarID", item.Owner); | ||
526 | command.Parameters.AddWithValue("?parentFolderID", item.Folder); | ||
527 | command.Parameters.AddWithValue("?inventoryGroupPermissions", item.GroupPermissions); | ||
528 | |||
529 | int rowsAffected = command.ExecuteNonQuery(); | ||
530 | if (rowsAffected == 1) | ||
531 | { | ||
532 | ret = BackendResponse.Success; | ||
533 | } | ||
534 | else if (rowsAffected == 2) | ||
535 | { | ||
536 | Logger.Log.Info("Replaced inventory item " + item.ID.ToString()); | ||
537 | ret = BackendResponse.Success; | ||
538 | } | ||
539 | else | ||
540 | { | ||
541 | Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected); | ||
542 | ret = BackendResponse.Failure; | ||
543 | } | ||
544 | } | ||
545 | catch (MySqlException ex) | ||
546 | { | ||
547 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
548 | ret = BackendResponse.Failure; | ||
549 | } | ||
550 | } | ||
551 | |||
552 | server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, false, DateTime.Now); | ||
553 | return ret; | ||
554 | } | ||
555 | |||
556 | public BackendResponse TryCreateFolder(Uri owner, InventoryFolder folder) | ||
557 | { | ||
558 | BackendResponse ret; | ||
559 | |||
560 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
561 | { | ||
562 | try | ||
563 | { | ||
564 | dbConnection.Open(); | ||
565 | |||
566 | MySqlCommand command = new MySqlCommand( | ||
567 | "REPLACE INTO inventoryfolders (folderName,type,version,folderID,agentID,parentFolderID) VALUES " + | ||
568 | "(?folderName,?type,?version,?folderID,?agentID,?parentFolderID)", dbConnection); | ||
569 | |||
570 | command.Parameters.AddWithValue("?folderName", folder.Name); | ||
571 | command.Parameters.AddWithValue("?type", folder.Type); | ||
572 | command.Parameters.AddWithValue("?version", folder.Version); | ||
573 | command.Parameters.AddWithValue("?folderID", folder.ID); | ||
574 | command.Parameters.AddWithValue("?agentID", folder.Owner); | ||
575 | command.Parameters.AddWithValue("?parentFolderID", folder.ParentID); | ||
576 | |||
577 | int rowsAffected = command.ExecuteNonQuery(); | ||
578 | if (rowsAffected == 1) | ||
579 | { | ||
580 | ret = BackendResponse.Success; | ||
581 | } | ||
582 | else if (rowsAffected == 2) | ||
583 | { | ||
584 | Logger.Log.Info("Replaced inventory folder " + folder.ID.ToString()); | ||
585 | ret = BackendResponse.Success; | ||
586 | } | ||
587 | else | ||
588 | { | ||
589 | Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected); | ||
590 | ret = BackendResponse.Failure; | ||
591 | } | ||
592 | } | ||
593 | catch (MySqlException ex) | ||
594 | { | ||
595 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
596 | ret = BackendResponse.Failure; | ||
597 | } | ||
598 | } | ||
599 | |||
600 | server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, true, DateTime.Now); | ||
601 | return ret; | ||
602 | } | ||
603 | |||
604 | public BackendResponse TryCreateInventory(Uri owner, InventoryFolder rootFolder) | ||
605 | { | ||
606 | return TryCreateFolder(owner, rootFolder); | ||
607 | } | ||
608 | |||
609 | public BackendResponse TryDeleteItem(Uri owner, UUID itemID) | ||
610 | { | ||
611 | BackendResponse ret; | ||
612 | UUID ownerID; | ||
613 | |||
614 | if (Utils.TryGetOpenSimUUID(owner, out ownerID)) | ||
615 | { | ||
616 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
617 | { | ||
618 | try | ||
619 | { | ||
620 | dbConnection.Open(); | ||
621 | |||
622 | MySqlCommand command = new MySqlCommand( | ||
623 | "DELETE FROM inventoryitems WHERE inventoryID=?inventoryID AND avatarID=?avatarID", dbConnection); | ||
624 | |||
625 | command.Parameters.AddWithValue("?inventoryID", itemID.ToString()); | ||
626 | command.Parameters.AddWithValue("?avatarID", ownerID.ToString()); | ||
627 | |||
628 | int rowsAffected = command.ExecuteNonQuery(); | ||
629 | if (rowsAffected == 1) | ||
630 | { | ||
631 | ret = BackendResponse.Success; | ||
632 | } | ||
633 | else | ||
634 | { | ||
635 | Logger.Log.ErrorFormat("MySQL DELETE query affected {0} rows", rowsAffected); | ||
636 | ret = BackendResponse.NotFound; | ||
637 | } | ||
638 | } | ||
639 | catch (MySqlException ex) | ||
640 | { | ||
641 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
642 | ret = BackendResponse.Failure; | ||
643 | } | ||
644 | } | ||
645 | } | ||
646 | else | ||
647 | { | ||
648 | ret = BackendResponse.NotFound; | ||
649 | } | ||
650 | |||
651 | server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now); | ||
652 | return ret; | ||
653 | } | ||
654 | |||
655 | public BackendResponse TryDeleteFolder(Uri owner, UUID folderID) | ||
656 | { | ||
657 | BackendResponse ret; | ||
658 | UUID ownerID; | ||
659 | |||
660 | if (Utils.TryGetOpenSimUUID(owner, out ownerID)) | ||
661 | { | ||
662 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
663 | { | ||
664 | try | ||
665 | { | ||
666 | dbConnection.Open(); | ||
667 | |||
668 | MySqlCommand command = new MySqlCommand( | ||
669 | "DELETE FROM inventoryfolders WHERE folderID=?folderID AND agentID=?agentID", dbConnection); | ||
670 | |||
671 | command.Parameters.AddWithValue("?folderID", folderID.ToString()); | ||
672 | command.Parameters.AddWithValue("?agentID", ownerID.ToString()); | ||
673 | |||
674 | int rowsAffected = command.ExecuteNonQuery(); | ||
675 | if (rowsAffected == 1) | ||
676 | { | ||
677 | ret = BackendResponse.Success; | ||
678 | } | ||
679 | else | ||
680 | { | ||
681 | Logger.Log.ErrorFormat("MySQL DELETE query affected {0} rows", rowsAffected); | ||
682 | ret = BackendResponse.NotFound; | ||
683 | } | ||
684 | } | ||
685 | catch (MySqlException ex) | ||
686 | { | ||
687 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
688 | ret = BackendResponse.Failure; | ||
689 | } | ||
690 | } | ||
691 | } | ||
692 | else | ||
693 | { | ||
694 | ret = BackendResponse.NotFound; | ||
695 | } | ||
696 | |||
697 | server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now); | ||
698 | return ret; | ||
699 | } | ||
700 | |||
701 | public BackendResponse TryPurgeFolder(Uri owner, UUID folderID) | ||
702 | { | ||
703 | BackendResponse ret; | ||
704 | UUID ownerID; | ||
705 | |||
706 | if (Utils.TryGetOpenSimUUID(owner, out ownerID)) | ||
707 | { | ||
708 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
709 | { | ||
710 | try | ||
711 | { | ||
712 | dbConnection.Open(); | ||
713 | |||
714 | #region Delete items | ||
715 | |||
716 | MySqlCommand command = new MySqlCommand( | ||
717 | "DELETE FROM inventoryitems WHERE parentFolderID=?parentFolderID AND avatarID=?avatarID", dbConnection); | ||
718 | |||
719 | command.Parameters.AddWithValue("?parentFolderID", folderID.ToString()); | ||
720 | command.Parameters.AddWithValue("?avatarID", ownerID.ToString()); | ||
721 | |||
722 | int rowsAffected = command.ExecuteNonQuery(); | ||
723 | |||
724 | #endregion Delete items | ||
725 | |||
726 | #region Delete folders | ||
727 | |||
728 | command = new MySqlCommand( | ||
729 | "DELETE FROM inventoryfolders WHERE parentFolderID=?parentFolderID AND agentID=?agentID", dbConnection); | ||
730 | |||
731 | command.Parameters.AddWithValue("?parentFolderID", folderID.ToString()); | ||
732 | command.Parameters.AddWithValue("?agentID", ownerID.ToString()); | ||
733 | |||
734 | rowsAffected += command.ExecuteNonQuery(); | ||
735 | |||
736 | #endregion Delete folders | ||
737 | |||
738 | Logger.Log.DebugFormat("Deleted {0} inventory objects from MySQL in a folder purge", rowsAffected); | ||
739 | |||
740 | ret = BackendResponse.Success; | ||
741 | } | ||
742 | catch (MySqlException ex) | ||
743 | { | ||
744 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
745 | ret = BackendResponse.Failure; | ||
746 | } | ||
747 | } | ||
748 | } | ||
749 | else | ||
750 | { | ||
751 | ret = BackendResponse.NotFound; | ||
752 | } | ||
753 | |||
754 | server.MetricsProvider.LogInventoryPurgeFolder(EXTENSION_NAME, ret, owner, folderID, DateTime.Now); | ||
755 | return ret; | ||
756 | } | ||
757 | |||
758 | public int ForEach(Action<Metadata> action, int start, int count) | ||
759 | { | ||
760 | int rowCount = 0; | ||
761 | |||
762 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
763 | { | ||
764 | MySqlDataReader reader; | ||
765 | |||
766 | try | ||
767 | { | ||
768 | dbConnection.Open(); | ||
769 | |||
770 | MySqlCommand command = dbConnection.CreateCommand(); | ||
771 | command.CommandText = String.Format("SELECT name,description,assetType,temporary,data,id FROM assets LIMIT {0}, {1}", | ||
772 | start, count); | ||
773 | reader = command.ExecuteReader(); | ||
774 | } | ||
775 | catch (MySqlException ex) | ||
776 | { | ||
777 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | while (reader.Read()) | ||
782 | { | ||
783 | Metadata metadata = new Metadata(); | ||
784 | metadata.CreationDate = OpenMetaverse.Utils.Epoch; | ||
785 | metadata.Description = reader.GetString(1); | ||
786 | metadata.ID = UUID.Parse(reader.GetString(5)); | ||
787 | metadata.Name = reader.GetString(0); | ||
788 | metadata.SHA1 = OpenMetaverse.Utils.SHA1((byte[])reader.GetValue(4)); | ||
789 | metadata.Temporary = reader.GetBoolean(3); | ||
790 | metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2)); | ||
791 | |||
792 | action(metadata); | ||
793 | ++rowCount; | ||
794 | } | ||
795 | |||
796 | reader.Close(); | ||
797 | } | ||
798 | |||
799 | return rowCount; | ||
800 | } | ||
801 | |||
802 | #endregion Required Interfaces | ||
803 | } | ||
804 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/OpenSimMySQLStorage.cs b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimMySQLStorage.cs new file mode 100644 index 0000000..34f1ef0 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/OpenSimMySQLStorage.cs | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.Data; | ||
34 | using MySql.Data.MySqlClient; | ||
35 | using ExtensionLoader; | ||
36 | using ExtensionLoader.Config; | ||
37 | using OpenMetaverse; | ||
38 | using OpenMetaverse.StructuredData; | ||
39 | |||
40 | namespace AssetServer.Extensions | ||
41 | { | ||
42 | public class OpenSimMySQLStorage : IExtension<AssetServer>, IStorageProvider | ||
43 | { | ||
44 | const string EXTENSION_NAME = "OpenSimMySQLStorage"; // Used in metrics reporting | ||
45 | |||
46 | AssetServer server; | ||
47 | |||
48 | public OpenSimMySQLStorage() | ||
49 | { | ||
50 | } | ||
51 | |||
52 | #region Required Interfaces | ||
53 | |||
54 | public void Start(AssetServer server) | ||
55 | { | ||
56 | this.server = server; | ||
57 | |||
58 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
59 | { | ||
60 | try | ||
61 | { | ||
62 | dbConnection.Open(); | ||
63 | Logger.Log.Info("Connected to MySQL storage backend: " + dbConnection.ServerVersion); | ||
64 | } | ||
65 | catch (MySqlException ex) | ||
66 | { | ||
67 | Logger.Log.Error("Connection to MySQL storage backend failed: " + ex.Message); | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | public void Stop() | ||
73 | { | ||
74 | } | ||
75 | |||
76 | public BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata) | ||
77 | { | ||
78 | metadata = null; | ||
79 | BackendResponse ret; | ||
80 | |||
81 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
82 | { | ||
83 | IDataReader reader; | ||
84 | |||
85 | try | ||
86 | { | ||
87 | dbConnection.Open(); | ||
88 | |||
89 | IDbCommand command = dbConnection.CreateCommand(); | ||
90 | command.CommandText = String.Format("SELECT name,description,assetType,temporary FROM assets WHERE id='{0}'", assetID.ToString()); | ||
91 | reader = command.ExecuteReader(); | ||
92 | |||
93 | if (reader.Read()) | ||
94 | { | ||
95 | metadata = new Metadata(); | ||
96 | metadata.CreationDate = OpenMetaverse.Utils.Epoch; | ||
97 | metadata.SHA1 = null; | ||
98 | metadata.ID = assetID; | ||
99 | metadata.Name = reader.GetString(0); | ||
100 | metadata.Description = reader.GetString(1); | ||
101 | metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2)); | ||
102 | metadata.Temporary = reader.GetBoolean(3); | ||
103 | |||
104 | ret = BackendResponse.Success; | ||
105 | } | ||
106 | else | ||
107 | { | ||
108 | ret = BackendResponse.NotFound; | ||
109 | } | ||
110 | } | ||
111 | catch (MySqlException ex) | ||
112 | { | ||
113 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
114 | ret = BackendResponse.Failure; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now); | ||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | public BackendResponse TryFetchData(UUID assetID, out byte[] assetData) | ||
123 | { | ||
124 | assetData = null; | ||
125 | BackendResponse ret; | ||
126 | |||
127 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
128 | { | ||
129 | IDataReader reader; | ||
130 | |||
131 | try | ||
132 | { | ||
133 | dbConnection.Open(); | ||
134 | |||
135 | IDbCommand command = dbConnection.CreateCommand(); | ||
136 | command.CommandText = String.Format("SELECT data FROM assets WHERE id='{0}'", assetID.ToString()); | ||
137 | reader = command.ExecuteReader(); | ||
138 | |||
139 | if (reader.Read()) | ||
140 | { | ||
141 | assetData = (byte[])reader.GetValue(0); | ||
142 | ret = BackendResponse.Success; | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | ret = BackendResponse.NotFound; | ||
147 | } | ||
148 | } | ||
149 | catch (MySqlException ex) | ||
150 | { | ||
151 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
152 | ret = BackendResponse.Failure; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now); | ||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | public BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData) | ||
161 | { | ||
162 | metadata = null; | ||
163 | assetData = null; | ||
164 | BackendResponse ret; | ||
165 | |||
166 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
167 | { | ||
168 | IDataReader reader; | ||
169 | |||
170 | try | ||
171 | { | ||
172 | dbConnection.Open(); | ||
173 | |||
174 | IDbCommand command = dbConnection.CreateCommand(); | ||
175 | command.CommandText = String.Format("SELECT name,description,assetType,temporary,data FROM assets WHERE id='{0}'", assetID.ToString()); | ||
176 | reader = command.ExecuteReader(); | ||
177 | |||
178 | if (reader.Read()) | ||
179 | { | ||
180 | metadata = new Metadata(); | ||
181 | metadata.CreationDate = OpenMetaverse.Utils.Epoch; | ||
182 | metadata.SHA1 = null; | ||
183 | metadata.ID = assetID; | ||
184 | metadata.Name = reader.GetString(0); | ||
185 | metadata.Description = reader.GetString(1); | ||
186 | metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2)); | ||
187 | metadata.Temporary = reader.GetBoolean(3); | ||
188 | |||
189 | assetData = (byte[])reader.GetValue(4); | ||
190 | |||
191 | ret = BackendResponse.Success; | ||
192 | } | ||
193 | else | ||
194 | { | ||
195 | ret = BackendResponse.NotFound; | ||
196 | } | ||
197 | } | ||
198 | catch (MySqlException ex) | ||
199 | { | ||
200 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
201 | ret = BackendResponse.Failure; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now); | ||
206 | server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now); | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID) | ||
211 | { | ||
212 | assetID = metadata.ID = UUID.Random(); | ||
213 | return TryCreateAsset(metadata, assetData); | ||
214 | } | ||
215 | |||
216 | public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData) | ||
217 | { | ||
218 | BackendResponse ret; | ||
219 | |||
220 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
221 | { | ||
222 | try | ||
223 | { | ||
224 | dbConnection.Open(); | ||
225 | |||
226 | MySqlCommand command = new MySqlCommand( | ||
227 | "REPLACE INTO assets (name,description,assetType,local,temporary,data,id) VALUES " + | ||
228 | "(?name,?description,?assetType,?local,?temporary,?data,?id)", dbConnection); | ||
229 | |||
230 | command.Parameters.AddWithValue("?name", metadata.Name); | ||
231 | command.Parameters.AddWithValue("?description", metadata.Description); | ||
232 | command.Parameters.AddWithValue("?assetType", Utils.ContentTypeToSLAssetType(metadata.ContentType)); | ||
233 | command.Parameters.AddWithValue("?local", 0); | ||
234 | command.Parameters.AddWithValue("?temporary", metadata.Temporary); | ||
235 | command.Parameters.AddWithValue("?data", assetData); | ||
236 | command.Parameters.AddWithValue("?id", metadata.ID.ToString()); | ||
237 | |||
238 | int rowsAffected = command.ExecuteNonQuery(); | ||
239 | if (rowsAffected == 1) | ||
240 | { | ||
241 | ret = BackendResponse.Success; | ||
242 | } | ||
243 | else if (rowsAffected == 2) | ||
244 | { | ||
245 | Logger.Log.Info("Replaced asset " + metadata.ID.ToString()); | ||
246 | ret = BackendResponse.Success; | ||
247 | } | ||
248 | else | ||
249 | { | ||
250 | Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected); | ||
251 | ret = BackendResponse.Failure; | ||
252 | } | ||
253 | } | ||
254 | catch (MySqlException ex) | ||
255 | { | ||
256 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
257 | ret = BackendResponse.Failure; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | server.MetricsProvider.LogAssetCreate(EXTENSION_NAME, ret, metadata.ID, assetData.Length, DateTime.Now); | ||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | public int ForEach(Action<Metadata> action, int start, int count) | ||
266 | { | ||
267 | int rowCount = 0; | ||
268 | |||
269 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
270 | { | ||
271 | MySqlDataReader reader; | ||
272 | |||
273 | try | ||
274 | { | ||
275 | dbConnection.Open(); | ||
276 | |||
277 | MySqlCommand command = dbConnection.CreateCommand(); | ||
278 | command.CommandText = String.Format("SELECT name,description,assetType,temporary,data,id FROM assets LIMIT {0}, {1}", | ||
279 | start, count); | ||
280 | reader = command.ExecuteReader(); | ||
281 | } | ||
282 | catch (MySqlException ex) | ||
283 | { | ||
284 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | while (reader.Read()) | ||
289 | { | ||
290 | Metadata metadata = new Metadata(); | ||
291 | metadata.CreationDate = OpenMetaverse.Utils.Epoch; | ||
292 | metadata.Description = reader.GetString(1); | ||
293 | metadata.ID = UUID.Parse(reader.GetString(5)); | ||
294 | metadata.Name = reader.GetString(0); | ||
295 | metadata.SHA1 = OpenMetaverse.Utils.SHA1((byte[])reader.GetValue(4)); | ||
296 | metadata.Temporary = reader.GetBoolean(3); | ||
297 | metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2)); | ||
298 | |||
299 | action(metadata); | ||
300 | ++rowCount; | ||
301 | } | ||
302 | |||
303 | reader.Close(); | ||
304 | } | ||
305 | |||
306 | return rowCount; | ||
307 | } | ||
308 | |||
309 | #endregion Required Interfaces | ||
310 | } | ||
311 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/ReferenceFrontend.cs b/OpenSim/Grid/NewAssetServer/Extensions/ReferenceFrontend.cs new file mode 100644 index 0000000..133f87c --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/ReferenceFrontend.cs | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.Xml; | ||
34 | using ExtensionLoader; | ||
35 | using OpenMetaverse; | ||
36 | using OpenMetaverse.StructuredData; | ||
37 | using HttpServer; | ||
38 | |||
39 | namespace AssetServer.Extensions | ||
40 | { | ||
41 | public class ReferenceFrontend : IExtension<AssetServer> | ||
42 | { | ||
43 | AssetServer server; | ||
44 | |||
45 | public ReferenceFrontend() | ||
46 | { | ||
47 | } | ||
48 | |||
49 | public void Start(AssetServer server) | ||
50 | { | ||
51 | this.server = server; | ||
52 | |||
53 | // Asset metadata request | ||
54 | 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", | ||
55 | MetadataRequestHandler); | ||
56 | |||
57 | // Asset data request | ||
58 | 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", | ||
59 | DataRequestHandler); | ||
60 | |||
61 | // Asset creation | ||
62 | server.HttpServer.AddHandler("post", null, "^/createasset", CreateRequestHandler); | ||
63 | } | ||
64 | |||
65 | public void Stop() | ||
66 | { | ||
67 | } | ||
68 | |||
69 | bool MetadataRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
70 | { | ||
71 | UUID assetID; | ||
72 | // Split the URL up into an AssetID and a method | ||
73 | string[] rawUrl = request.Uri.PathAndQuery.Split('/'); | ||
74 | |||
75 | if (rawUrl.Length >= 3 && UUID.TryParse(rawUrl[1], out assetID)) | ||
76 | { | ||
77 | UUID authToken = Utils.GetAuthToken(request); | ||
78 | |||
79 | if (server.AuthorizationProvider.IsMetadataAuthorized(authToken, assetID)) | ||
80 | { | ||
81 | Metadata metadata; | ||
82 | BackendResponse storageResponse = server.StorageProvider.TryFetchMetadata(assetID, out metadata); | ||
83 | |||
84 | if (storageResponse == BackendResponse.Success) | ||
85 | { | ||
86 | // If the asset data location wasn't specified in the metadata, specify it | ||
87 | // manually here by pointing back to this asset server | ||
88 | if (!metadata.Methods.ContainsKey("data")) | ||
89 | { | ||
90 | metadata.Methods["data"] = new Uri(String.Format("{0}://{1}/{2}/data", | ||
91 | request.Uri.Scheme, request.Uri.Authority, assetID)); | ||
92 | } | ||
93 | |||
94 | byte[] serializedData = metadata.SerializeToBytes(); | ||
95 | |||
96 | response.Status = HttpStatusCode.OK; | ||
97 | response.ContentType = "application/json"; | ||
98 | response.ContentLength = serializedData.Length; | ||
99 | response.Body.Write(serializedData, 0, serializedData.Length); | ||
100 | |||
101 | } | ||
102 | else if (storageResponse == BackendResponse.NotFound) | ||
103 | { | ||
104 | Logger.Log.Warn("Could not find metadata for asset " + assetID.ToString()); | ||
105 | response.Status = HttpStatusCode.NotFound; | ||
106 | } | ||
107 | else | ||
108 | { | ||
109 | response.Status = HttpStatusCode.InternalServerError; | ||
110 | } | ||
111 | } | ||
112 | else | ||
113 | { | ||
114 | response.Status = HttpStatusCode.Forbidden; | ||
115 | } | ||
116 | |||
117 | return true; | ||
118 | } | ||
119 | |||
120 | response.Status = HttpStatusCode.NotFound; | ||
121 | return true; | ||
122 | } | ||
123 | |||
124 | bool DataRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
125 | { | ||
126 | UUID assetID; | ||
127 | // Split the URL up into an AssetID and a method | ||
128 | string[] rawUrl = request.Uri.PathAndQuery.Split('/'); | ||
129 | |||
130 | if (rawUrl.Length >= 3 && UUID.TryParse(rawUrl[1], out assetID)) | ||
131 | { | ||
132 | UUID authToken = Utils.GetAuthToken(request); | ||
133 | |||
134 | if (server.AuthorizationProvider.IsDataAuthorized(authToken, assetID)) | ||
135 | { | ||
136 | byte[] assetData; | ||
137 | BackendResponse storageResponse = server.StorageProvider.TryFetchData(assetID, out assetData); | ||
138 | |||
139 | if (storageResponse == BackendResponse.Success) | ||
140 | { | ||
141 | response.Status = HttpStatusCode.OK; | ||
142 | response.Status = HttpStatusCode.OK; | ||
143 | response.ContentType = "application/octet-stream"; | ||
144 | response.AddHeader("Content-Disposition", "attachment; filename=" + assetID.ToString()); | ||
145 | response.ContentLength = assetData.Length; | ||
146 | response.Body.Write(assetData, 0, assetData.Length); | ||
147 | } | ||
148 | else if (storageResponse == BackendResponse.NotFound) | ||
149 | { | ||
150 | response.Status = HttpStatusCode.NotFound; | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | response.Status = HttpStatusCode.InternalServerError; | ||
155 | } | ||
156 | } | ||
157 | else | ||
158 | { | ||
159 | response.Status = HttpStatusCode.Forbidden; | ||
160 | } | ||
161 | |||
162 | return true; | ||
163 | } | ||
164 | |||
165 | response.Status = HttpStatusCode.BadRequest; | ||
166 | return true; | ||
167 | } | ||
168 | |||
169 | bool CreateRequestHandler(IHttpClientContext client, IHttpRequest request, IHttpResponse response) | ||
170 | { | ||
171 | UUID authToken = Utils.GetAuthToken(request); | ||
172 | |||
173 | if (server.AuthorizationProvider.IsCreateAuthorized(authToken)) | ||
174 | { | ||
175 | try | ||
176 | { | ||
177 | OSD osdata = OSDParser.DeserializeJson(request.Body); | ||
178 | |||
179 | if (osdata.Type == OSDType.Map) | ||
180 | { | ||
181 | OSDMap map = (OSDMap)osdata; | ||
182 | Metadata metadata = new Metadata(); | ||
183 | metadata.Deserialize(map); | ||
184 | |||
185 | byte[] assetData = map["data"].AsBinary(); | ||
186 | |||
187 | if (assetData != null && assetData.Length > 0) | ||
188 | { | ||
189 | BackendResponse storageResponse; | ||
190 | |||
191 | if (metadata.ID != UUID.Zero) | ||
192 | storageResponse = server.StorageProvider.TryCreateAsset(metadata, assetData); | ||
193 | else | ||
194 | storageResponse = server.StorageProvider.TryCreateAsset(metadata, assetData, out metadata.ID); | ||
195 | |||
196 | if (storageResponse == BackendResponse.Success) | ||
197 | { | ||
198 | response.Status = HttpStatusCode.Created; | ||
199 | OSDMap responseMap = new OSDMap(1); | ||
200 | responseMap["id"] = OSD.FromUUID(metadata.ID); | ||
201 | LitJson.JsonData jsonData = OSDParser.SerializeJson(responseMap); | ||
202 | byte[] responseData = System.Text.Encoding.UTF8.GetBytes(jsonData.ToJson()); | ||
203 | response.Body.Write(responseData, 0, responseData.Length); | ||
204 | response.Body.Flush(); | ||
205 | } | ||
206 | else if (storageResponse == BackendResponse.NotFound) | ||
207 | { | ||
208 | response.Status = HttpStatusCode.NotFound; | ||
209 | } | ||
210 | else | ||
211 | { | ||
212 | response.Status = HttpStatusCode.InternalServerError; | ||
213 | } | ||
214 | } | ||
215 | else | ||
216 | { | ||
217 | response.Status = HttpStatusCode.BadRequest; | ||
218 | } | ||
219 | } | ||
220 | else | ||
221 | { | ||
222 | response.Status = HttpStatusCode.BadRequest; | ||
223 | } | ||
224 | } | ||
225 | catch (Exception ex) | ||
226 | { | ||
227 | response.Status = HttpStatusCode.InternalServerError; | ||
228 | response.Reason = ex.Message; | ||
229 | } | ||
230 | } | ||
231 | else | ||
232 | { | ||
233 | response.Status = HttpStatusCode.Forbidden; | ||
234 | } | ||
235 | |||
236 | return true; | ||
237 | } | ||
238 | } | ||
239 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/SimpleInventory.cs b/OpenSim/Grid/NewAssetServer/Extensions/SimpleInventory.cs new file mode 100644 index 0000000..782b6b4 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/SimpleInventory.cs | |||
@@ -0,0 +1,602 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.IO; | ||
34 | using System.Text; | ||
35 | using ExtensionLoader; | ||
36 | using OpenMetaverse; | ||
37 | using OpenMetaverse.StructuredData; | ||
38 | |||
39 | namespace AssetServer.Extensions | ||
40 | { | ||
41 | public class SimpleInventory : IExtension<AssetServer>, IInventoryProvider | ||
42 | { | ||
43 | const string EXTENSION_NAME = "SimpleInventory"; // Used for metrics reporting | ||
44 | const string DEFAULT_INVENTORY_DIR = "SimpleInventory"; | ||
45 | |||
46 | AssetServer server; | ||
47 | Dictionary<Uri, InventoryCollection> inventories = new Dictionary<Uri, InventoryCollection>(); | ||
48 | Dictionary<Uri, List<InventoryItem>> activeGestures = new Dictionary<Uri, List<InventoryItem>>(); | ||
49 | Utils.InventoryItemSerializer itemSerializer = new Utils.InventoryItemSerializer(); | ||
50 | Utils.InventoryFolderSerializer folderSerializer = new Utils.InventoryFolderSerializer(); | ||
51 | |||
52 | public SimpleInventory() | ||
53 | { | ||
54 | } | ||
55 | |||
56 | #region Required Interfaces | ||
57 | |||
58 | public void Start(AssetServer server) | ||
59 | { | ||
60 | this.server = server; | ||
61 | |||
62 | LoadFiles(DEFAULT_INVENTORY_DIR); | ||
63 | |||
64 | Logger.Log.InfoFormat("Initialized the inventory index with data for {0} avatars", | ||
65 | inventories.Count); | ||
66 | } | ||
67 | |||
68 | public void Stop() | ||
69 | { | ||
70 | } | ||
71 | |||
72 | public BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItem item) | ||
73 | { | ||
74 | item = null; | ||
75 | BackendResponse ret; | ||
76 | |||
77 | InventoryCollection collection; | ||
78 | if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item)) | ||
79 | ret = BackendResponse.Success; | ||
80 | else | ||
81 | ret = BackendResponse.NotFound; | ||
82 | |||
83 | server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now); | ||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | public BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolder folder) | ||
88 | { | ||
89 | folder = null; | ||
90 | BackendResponse ret; | ||
91 | |||
92 | InventoryCollection collection; | ||
93 | if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder)) | ||
94 | ret = BackendResponse.Success; | ||
95 | else | ||
96 | ret = BackendResponse.NotFound; | ||
97 | |||
98 | server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now); | ||
99 | return ret; | ||
100 | } | ||
101 | |||
102 | public BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents) | ||
103 | { | ||
104 | contents = null; | ||
105 | BackendResponse ret; | ||
106 | |||
107 | InventoryCollection collection; | ||
108 | InventoryFolder folder; | ||
109 | |||
110 | if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder)) | ||
111 | { | ||
112 | contents = new InventoryCollection(); | ||
113 | contents.UserID = collection.UserID; | ||
114 | contents.Folders = new Dictionary<UUID, InventoryFolder>(); | ||
115 | contents.Items = new Dictionary<UUID, InventoryItem>(); | ||
116 | |||
117 | foreach (InventoryBase invBase in folder.Children.Values) | ||
118 | { | ||
119 | if (invBase is InventoryItem) | ||
120 | { | ||
121 | InventoryItem invItem = invBase as InventoryItem; | ||
122 | contents.Items.Add(invItem.ID, invItem); | ||
123 | } | ||
124 | else | ||
125 | { | ||
126 | InventoryFolder invFolder = invBase as InventoryFolder; | ||
127 | contents.Folders.Add(invFolder.ID, invFolder); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | ret = BackendResponse.Success; | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | ret = BackendResponse.NotFound; | ||
136 | } | ||
137 | |||
138 | server.MetricsProvider.LogInventoryFetchFolderContents(EXTENSION_NAME, ret, owner, folderID, DateTime.Now); | ||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | public BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolder> folders) | ||
143 | { | ||
144 | folders = null; | ||
145 | BackendResponse ret; | ||
146 | |||
147 | InventoryCollection collection; | ||
148 | if (inventories.TryGetValue(owner, out collection)) | ||
149 | { | ||
150 | folders = new List<InventoryFolder>(collection.Folders.Values); | ||
151 | return BackendResponse.Success; | ||
152 | } | ||
153 | else | ||
154 | { | ||
155 | ret = BackendResponse.NotFound; | ||
156 | } | ||
157 | |||
158 | server.MetricsProvider.LogInventoryFetchFolderList(EXTENSION_NAME, ret, owner, DateTime.Now); | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | public BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory) | ||
163 | { | ||
164 | inventory = null; | ||
165 | BackendResponse ret; | ||
166 | |||
167 | if (inventories.TryGetValue(owner, out inventory)) | ||
168 | ret = BackendResponse.Success; | ||
169 | else | ||
170 | ret = BackendResponse.NotFound; | ||
171 | |||
172 | server.MetricsProvider.LogInventoryFetchInventory(EXTENSION_NAME, ret, owner, DateTime.Now); | ||
173 | return ret; | ||
174 | } | ||
175 | |||
176 | public BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItem> gestures) | ||
177 | { | ||
178 | gestures = null; | ||
179 | BackendResponse ret; | ||
180 | |||
181 | if (activeGestures.TryGetValue(owner, out gestures)) | ||
182 | ret = BackendResponse.Success; | ||
183 | else | ||
184 | ret = BackendResponse.NotFound; | ||
185 | |||
186 | server.MetricsProvider.LogInventoryFetchActiveGestures(EXTENSION_NAME, ret, owner, DateTime.Now); | ||
187 | return ret; | ||
188 | } | ||
189 | |||
190 | public BackendResponse TryCreateItem(Uri owner, InventoryItem item) | ||
191 | { | ||
192 | BackendResponse ret; | ||
193 | |||
194 | InventoryCollection collection; | ||
195 | if (inventories.TryGetValue(owner, out collection)) | ||
196 | { | ||
197 | // Delete this item first if it already exists | ||
198 | InventoryItem oldItem; | ||
199 | if (collection.Items.TryGetValue(item.ID, out oldItem)) | ||
200 | TryDeleteItem(owner, item.ID); | ||
201 | |||
202 | try | ||
203 | { | ||
204 | // Create the file | ||
205 | SaveItem(item); | ||
206 | |||
207 | // Add the item to the collection | ||
208 | lock (collection) collection.Items[item.ID] = item; | ||
209 | |||
210 | // Add the item to its parent folder | ||
211 | InventoryFolder parent; | ||
212 | if (collection.Folders.TryGetValue(item.Folder, out parent)) | ||
213 | lock (parent.Children) parent.Children.Add(item.ID, item); | ||
214 | |||
215 | // Add active gestures to our list | ||
216 | if (item.InvType == (int)InventoryType.Gesture && item.Flags == 1) | ||
217 | { | ||
218 | lock (activeGestures) | ||
219 | activeGestures[owner].Add(item); | ||
220 | } | ||
221 | |||
222 | ret = BackendResponse.Success; | ||
223 | } | ||
224 | catch (Exception ex) | ||
225 | { | ||
226 | Logger.Log.Error(ex.Message); | ||
227 | ret = BackendResponse.Failure; | ||
228 | } | ||
229 | } | ||
230 | else | ||
231 | { | ||
232 | return BackendResponse.NotFound; | ||
233 | } | ||
234 | |||
235 | server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, false, DateTime.Now); | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | public BackendResponse TryCreateFolder(Uri owner, InventoryFolder folder) | ||
240 | { | ||
241 | BackendResponse ret; | ||
242 | |||
243 | InventoryCollection collection; | ||
244 | if (inventories.TryGetValue(owner, out collection)) | ||
245 | { | ||
246 | // Delete this folder first if it already exists | ||
247 | InventoryFolder oldFolder; | ||
248 | if (collection.Folders.TryGetValue(folder.ID, out oldFolder)) | ||
249 | TryDeleteFolder(owner, folder.ID); | ||
250 | |||
251 | try | ||
252 | { | ||
253 | // Create the file | ||
254 | SaveFolder(folder); | ||
255 | |||
256 | // Add the folder to the collection | ||
257 | lock (collection) collection.Folders[folder.ID] = folder; | ||
258 | |||
259 | // Add the folder to its parent folder | ||
260 | InventoryFolder parent; | ||
261 | if (collection.Folders.TryGetValue(folder.ParentID, out parent)) | ||
262 | lock (parent.Children) parent.Children.Add(folder.ID, folder); | ||
263 | |||
264 | ret = BackendResponse.Success; | ||
265 | } | ||
266 | catch (Exception ex) | ||
267 | { | ||
268 | Logger.Log.Error(ex.Message); | ||
269 | ret = BackendResponse.Failure; | ||
270 | } | ||
271 | } | ||
272 | else | ||
273 | { | ||
274 | ret = BackendResponse.NotFound; | ||
275 | } | ||
276 | |||
277 | server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, true, DateTime.Now); | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | public BackendResponse TryCreateInventory(Uri owner, InventoryFolder rootFolder) | ||
282 | { | ||
283 | BackendResponse ret; | ||
284 | |||
285 | lock (inventories) | ||
286 | { | ||
287 | if (!inventories.ContainsKey(owner)) | ||
288 | { | ||
289 | InventoryCollection collection = new InventoryCollection(); | ||
290 | collection.UserID = rootFolder.Owner; | ||
291 | collection.Folders = new Dictionary<UUID, InventoryFolder>(); | ||
292 | collection.Folders.Add(rootFolder.ID, rootFolder); | ||
293 | collection.Items = new Dictionary<UUID, InventoryItem>(); | ||
294 | |||
295 | inventories.Add(owner, collection); | ||
296 | |||
297 | ret = BackendResponse.Success; | ||
298 | } | ||
299 | else | ||
300 | { | ||
301 | ret = BackendResponse.Failure; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | if (ret == BackendResponse.Success) | ||
306 | { | ||
307 | string path = Path.Combine(DEFAULT_INVENTORY_DIR, rootFolder.Owner.ToString()); | ||
308 | try | ||
309 | { | ||
310 | // Create the directory for this agent | ||
311 | Directory.CreateDirectory(path); | ||
312 | |||
313 | // Create an index.txt containing the UUID and URI for this agent | ||
314 | string[] index = new string[] { rootFolder.Owner.ToString(), owner.ToString() }; | ||
315 | File.WriteAllLines(Path.Combine(path, "index.txt"), index); | ||
316 | |||
317 | // Create the root folder file | ||
318 | SaveFolder(rootFolder); | ||
319 | } | ||
320 | catch (Exception ex) | ||
321 | { | ||
322 | Logger.Log.Error(ex.Message); | ||
323 | ret = BackendResponse.Failure; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | server.MetricsProvider.LogInventoryCreateInventory(EXTENSION_NAME, ret, DateTime.Now); | ||
328 | return ret; | ||
329 | } | ||
330 | |||
331 | public BackendResponse TryDeleteItem(Uri owner, UUID itemID) | ||
332 | { | ||
333 | BackendResponse ret; | ||
334 | |||
335 | InventoryCollection collection; | ||
336 | InventoryItem item; | ||
337 | if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item)) | ||
338 | { | ||
339 | // Remove the item from its parent folder | ||
340 | InventoryFolder parent; | ||
341 | if (collection.Folders.TryGetValue(item.Folder, out parent)) | ||
342 | lock (parent.Children) parent.Children.Remove(itemID); | ||
343 | |||
344 | // Remove the item from the collection | ||
345 | lock (collection) collection.Items.Remove(itemID); | ||
346 | |||
347 | // Remove from the active gestures list if applicable | ||
348 | if (item.InvType == (int)InventoryType.Gesture) | ||
349 | { | ||
350 | lock (activeGestures) | ||
351 | { | ||
352 | for (int i = 0; i < activeGestures[owner].Count; i++) | ||
353 | { | ||
354 | if (activeGestures[owner][i].ID == itemID) | ||
355 | { | ||
356 | activeGestures[owner].RemoveAt(i); | ||
357 | break; | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | |||
363 | // Delete the file. We don't know exactly what the file name is, | ||
364 | // so search for it | ||
365 | string path = PathFromURI(owner); | ||
366 | string[] matches = Directory.GetFiles(path, String.Format("*{0}.item", itemID), SearchOption.TopDirectoryOnly); | ||
367 | foreach (string match in matches) | ||
368 | { | ||
369 | try { File.Delete(match); } | ||
370 | catch (Exception ex) { Logger.Log.ErrorFormat("Failed to delete file {0}: {1}", match, ex.Message); } | ||
371 | } | ||
372 | |||
373 | ret = BackendResponse.Success; | ||
374 | } | ||
375 | else | ||
376 | { | ||
377 | ret = BackendResponse.NotFound; | ||
378 | } | ||
379 | |||
380 | server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now); | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | public BackendResponse TryDeleteFolder(Uri owner, UUID folderID) | ||
385 | { | ||
386 | BackendResponse ret; | ||
387 | |||
388 | InventoryCollection collection; | ||
389 | InventoryFolder folder; | ||
390 | if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder)) | ||
391 | { | ||
392 | // Remove the folder from its parent folder | ||
393 | InventoryFolder parent; | ||
394 | if (collection.Folders.TryGetValue(folder.ParentID, out parent)) | ||
395 | lock (parent.Children) parent.Children.Remove(folderID); | ||
396 | |||
397 | // Remove the folder from the collection | ||
398 | lock (collection) collection.Items.Remove(folderID); | ||
399 | |||
400 | // Delete the folder file. We don't know exactly what the file name is, | ||
401 | // so search for it | ||
402 | string path = PathFromURI(owner); | ||
403 | string[] matches = Directory.GetFiles(path, String.Format("*{0}.folder", folderID), SearchOption.TopDirectoryOnly); | ||
404 | foreach (string match in matches) | ||
405 | { | ||
406 | try { File.Delete(match); } | ||
407 | catch (Exception ex) { Logger.Log.ErrorFormat("Failed to delete folder file {0}: {1}", match, ex.Message); } | ||
408 | } | ||
409 | |||
410 | ret = BackendResponse.Success; | ||
411 | } | ||
412 | else | ||
413 | { | ||
414 | ret = BackendResponse.NotFound; | ||
415 | } | ||
416 | |||
417 | server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now); | ||
418 | return ret; | ||
419 | } | ||
420 | |||
421 | public BackendResponse TryPurgeFolder(Uri owner, UUID folderID) | ||
422 | { | ||
423 | BackendResponse ret; | ||
424 | |||
425 | InventoryCollection collection; | ||
426 | InventoryFolder folder; | ||
427 | if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder)) | ||
428 | { | ||
429 | // Delete all of the folder children | ||
430 | foreach (InventoryBase obj in new List<InventoryBase>(folder.Children.Values)) | ||
431 | { | ||
432 | if (obj is InventoryItem) | ||
433 | { | ||
434 | TryDeleteItem(owner, (obj as InventoryItem).ID); | ||
435 | } | ||
436 | else | ||
437 | { | ||
438 | InventoryFolder childFolder = obj as InventoryFolder; | ||
439 | TryPurgeFolder(owner, childFolder.ID); | ||
440 | TryDeleteFolder(owner, childFolder.ID); | ||
441 | } | ||
442 | } | ||
443 | |||
444 | ret = BackendResponse.Success; | ||
445 | } | ||
446 | else | ||
447 | { | ||
448 | ret = BackendResponse.NotFound; | ||
449 | } | ||
450 | |||
451 | server.MetricsProvider.LogInventoryPurgeFolder(EXTENSION_NAME, ret, owner, folderID, DateTime.Now); | ||
452 | return ret; | ||
453 | } | ||
454 | |||
455 | #endregion Required Interfaces | ||
456 | |||
457 | void SaveItem(InventoryItem item) | ||
458 | { | ||
459 | string filename = String.Format("{0}-{1}.item", SanitizeFilename(item.Name), item.ID); | ||
460 | |||
461 | string path = Path.Combine(DEFAULT_INVENTORY_DIR, item.Owner.ToString()); | ||
462 | path = Path.Combine(path, filename); | ||
463 | |||
464 | using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write)) | ||
465 | { | ||
466 | itemSerializer.Serialize(stream, item); | ||
467 | stream.Flush(); | ||
468 | } | ||
469 | } | ||
470 | |||
471 | void SaveFolder(InventoryFolder folder) | ||
472 | { | ||
473 | string filename = String.Format("{0}-{1}.folder", SanitizeFilename(folder.Name), folder.ID); | ||
474 | |||
475 | string path = Path.Combine(DEFAULT_INVENTORY_DIR, folder.Owner.ToString()); | ||
476 | path = Path.Combine(path, filename); | ||
477 | |||
478 | using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write)) | ||
479 | { | ||
480 | folderSerializer.Serialize(stream, folder); | ||
481 | stream.Flush(); | ||
482 | } | ||
483 | } | ||
484 | |||
485 | string SanitizeFilename(string filename) | ||
486 | { | ||
487 | string output = filename; | ||
488 | |||
489 | if (output.Length > 64) | ||
490 | output = output.Substring(0, 64); | ||
491 | |||
492 | foreach (char i in Path.GetInvalidFileNameChars()) | ||
493 | output = output.Replace(i, '_'); | ||
494 | |||
495 | return output; | ||
496 | } | ||
497 | |||
498 | static string PathFromURI(Uri uri) | ||
499 | { | ||
500 | byte[] hash = OpenMetaverse.Utils.SHA1(Encoding.UTF8.GetBytes(uri.ToString())); | ||
501 | StringBuilder digest = new StringBuilder(40); | ||
502 | |||
503 | // Convert the hash to a hex string | ||
504 | foreach (byte b in hash) | ||
505 | digest.AppendFormat(OpenMetaverse.Utils.EnUsCulture, "{0:x2}", b); | ||
506 | |||
507 | return Path.Combine(DEFAULT_INVENTORY_DIR, digest.ToString()); | ||
508 | } | ||
509 | |||
510 | void LoadFiles(string folder) | ||
511 | { | ||
512 | // Try to create the directory if it doesn't already exist | ||
513 | if (!Directory.Exists(folder)) | ||
514 | { | ||
515 | try { Directory.CreateDirectory(folder); } | ||
516 | catch (Exception ex) | ||
517 | { | ||
518 | Logger.Log.Warn(ex.Message); | ||
519 | return; | ||
520 | } | ||
521 | } | ||
522 | |||
523 | try | ||
524 | { | ||
525 | string[] agentFolders = Directory.GetDirectories(DEFAULT_INVENTORY_DIR); | ||
526 | |||
527 | for (int i = 0; i < agentFolders.Length; i++) | ||
528 | { | ||
529 | string foldername = agentFolders[i]; | ||
530 | string indexPath = Path.Combine(foldername, "index.txt"); | ||
531 | UUID ownerID = UUID.Zero; | ||
532 | Uri owner = null; | ||
533 | |||
534 | try | ||
535 | { | ||
536 | string[] index = File.ReadAllLines(indexPath); | ||
537 | ownerID = UUID.Parse(index[0]); | ||
538 | owner = new Uri(index[1]); | ||
539 | } | ||
540 | catch (Exception ex) | ||
541 | { | ||
542 | Logger.Log.WarnFormat("Failed loading the index file {0}: {1}", indexPath, ex.Message); | ||
543 | } | ||
544 | |||
545 | if (ownerID != UUID.Zero && owner != null) | ||
546 | { | ||
547 | // Initialize the active gestures list for this agent | ||
548 | activeGestures.Add(owner, new List<InventoryItem>()); | ||
549 | |||
550 | InventoryCollection collection = new InventoryCollection(); | ||
551 | collection.UserID = ownerID; | ||
552 | |||
553 | // Load all of the folders for this agent | ||
554 | string[] folders = Directory.GetFiles(foldername, "*.folder", SearchOption.TopDirectoryOnly); | ||
555 | collection.Folders = new Dictionary<UUID,InventoryFolder>(folders.Length); | ||
556 | |||
557 | for (int j = 0; j < folders.Length; j++) | ||
558 | { | ||
559 | InventoryFolder invFolder = (InventoryFolder)folderSerializer.Deserialize( | ||
560 | new FileStream(folders[j], FileMode.Open, FileAccess.Read)); | ||
561 | collection.Folders[invFolder.ID] = invFolder; | ||
562 | } | ||
563 | |||
564 | // Iterate over the folders collection, adding children to their parents | ||
565 | foreach (InventoryFolder invFolder in collection.Folders.Values) | ||
566 | { | ||
567 | InventoryFolder parent; | ||
568 | if (collection.Folders.TryGetValue(invFolder.ParentID, out parent)) | ||
569 | parent.Children[invFolder.ID] = invFolder; | ||
570 | } | ||
571 | |||
572 | // Load all of the items for this agent | ||
573 | string[] files = Directory.GetFiles(foldername, "*.item", SearchOption.TopDirectoryOnly); | ||
574 | collection.Items = new Dictionary<UUID, InventoryItem>(files.Length); | ||
575 | |||
576 | for (int j = 0; j < files.Length; j++) | ||
577 | { | ||
578 | InventoryItem invItem = (InventoryItem)itemSerializer.Deserialize( | ||
579 | new FileStream(files[j], FileMode.Open, FileAccess.Read)); | ||
580 | collection.Items[invItem.ID] = invItem; | ||
581 | |||
582 | // Add items to their parent folders | ||
583 | InventoryFolder parent; | ||
584 | if (collection.Folders.TryGetValue(invItem.Folder, out parent)) | ||
585 | parent.Children[invItem.ID] = invItem; | ||
586 | |||
587 | // Add active gestures to our list | ||
588 | if (invItem.InvType == (int)InventoryType.Gesture && invItem.Flags != 0) | ||
589 | activeGestures[owner].Add(invItem); | ||
590 | } | ||
591 | |||
592 | inventories.Add(owner, collection); | ||
593 | } | ||
594 | } | ||
595 | } | ||
596 | catch (Exception ex) | ||
597 | { | ||
598 | Logger.Log.ErrorFormat("Failed loading inventory from {0}: {1}", folder, ex.Message); | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/SimpleStorage.cs b/OpenSim/Grid/NewAssetServer/Extensions/SimpleStorage.cs new file mode 100644 index 0000000..1c0fe33 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/SimpleStorage.cs | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.IO; | ||
34 | using ExtensionLoader; | ||
35 | using OpenMetaverse; | ||
36 | using OpenMetaverse.StructuredData; | ||
37 | |||
38 | namespace AssetServer.Extensions | ||
39 | { | ||
40 | public class SimpleStorage : IExtension<AssetServer>, IStorageProvider | ||
41 | { | ||
42 | const string EXTENSION_NAME = ""; // Used in metrics reporting | ||
43 | const string DEFAULT_DATA_DIR = "SimpleAssets"; | ||
44 | const string TEMP_DATA_DIR = "SimpleAssetsTemp"; | ||
45 | |||
46 | AssetServer server; | ||
47 | Dictionary<UUID, Metadata> metadataStorage; | ||
48 | Dictionary<UUID, string> filenames; | ||
49 | |||
50 | public SimpleStorage() | ||
51 | { | ||
52 | } | ||
53 | |||
54 | #region Required Interfaces | ||
55 | |||
56 | public void Start(AssetServer server) | ||
57 | { | ||
58 | this.server = server; | ||
59 | metadataStorage = new Dictionary<UUID, Metadata>(); | ||
60 | filenames = new Dictionary<UUID, string>(); | ||
61 | |||
62 | LoadFiles(DEFAULT_DATA_DIR, false); | ||
63 | LoadFiles(TEMP_DATA_DIR, true); | ||
64 | |||
65 | Logger.Log.InfoFormat("Initialized the store index with metadata for {0} assets", | ||
66 | metadataStorage.Count); | ||
67 | } | ||
68 | |||
69 | public void Stop() | ||
70 | { | ||
71 | WipeTemporary(); | ||
72 | } | ||
73 | |||
74 | public BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata) | ||
75 | { | ||
76 | metadata = null; | ||
77 | BackendResponse ret; | ||
78 | |||
79 | if (metadataStorage.TryGetValue(assetID, out metadata)) | ||
80 | ret = BackendResponse.Success; | ||
81 | else | ||
82 | ret = BackendResponse.NotFound; | ||
83 | |||
84 | server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now); | ||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | public BackendResponse TryFetchData(UUID assetID, out byte[] assetData) | ||
89 | { | ||
90 | assetData = null; | ||
91 | string filename; | ||
92 | BackendResponse ret; | ||
93 | |||
94 | if (filenames.TryGetValue(assetID, out filename)) | ||
95 | { | ||
96 | try | ||
97 | { | ||
98 | assetData = File.ReadAllBytes(filename); | ||
99 | ret = BackendResponse.Success; | ||
100 | } | ||
101 | catch (Exception ex) | ||
102 | { | ||
103 | Logger.Log.ErrorFormat("Failed reading data for asset {0} from {1}: {2}", assetID, filename, ex.Message); | ||
104 | ret = BackendResponse.Failure; | ||
105 | } | ||
106 | } | ||
107 | else | ||
108 | { | ||
109 | ret = BackendResponse.NotFound; | ||
110 | } | ||
111 | |||
112 | server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now); | ||
113 | return ret; | ||
114 | } | ||
115 | |||
116 | public BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData) | ||
117 | { | ||
118 | metadata = null; | ||
119 | assetData = null; | ||
120 | string filename; | ||
121 | BackendResponse ret; | ||
122 | |||
123 | if (metadataStorage.TryGetValue(assetID, out metadata) && | ||
124 | filenames.TryGetValue(assetID, out filename)) | ||
125 | { | ||
126 | try | ||
127 | { | ||
128 | assetData = File.ReadAllBytes(filename); | ||
129 | ret = BackendResponse.Success; | ||
130 | } | ||
131 | catch (Exception ex) | ||
132 | { | ||
133 | Logger.Log.ErrorFormat("Failed reading data for asset {0} from {1}: {2}", assetID, filename, ex.Message); | ||
134 | ret = BackendResponse.Failure; | ||
135 | } | ||
136 | } | ||
137 | else | ||
138 | { | ||
139 | ret = BackendResponse.NotFound; | ||
140 | } | ||
141 | |||
142 | server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now); | ||
143 | server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now); | ||
144 | return ret; | ||
145 | } | ||
146 | |||
147 | public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID) | ||
148 | { | ||
149 | assetID = metadata.ID = UUID.Random(); | ||
150 | return TryCreateAsset(metadata, assetData); | ||
151 | } | ||
152 | |||
153 | public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData) | ||
154 | { | ||
155 | BackendResponse ret; | ||
156 | |||
157 | string path; | ||
158 | string filename = String.Format("{0}.{1}", metadata.ID, Utils.ContentTypeToExtension(metadata.ContentType)); | ||
159 | |||
160 | if (metadata.Temporary) | ||
161 | path = Path.Combine(TEMP_DATA_DIR, filename); | ||
162 | else | ||
163 | path = Path.Combine(DEFAULT_DATA_DIR, filename); | ||
164 | |||
165 | try | ||
166 | { | ||
167 | File.WriteAllBytes(path, assetData); | ||
168 | lock (filenames) filenames[metadata.ID] = path; | ||
169 | |||
170 | // Set the creation date to right now | ||
171 | metadata.CreationDate = DateTime.Now; | ||
172 | |||
173 | lock (metadataStorage) | ||
174 | metadataStorage[metadata.ID] = metadata; | ||
175 | |||
176 | ret = BackendResponse.Success; | ||
177 | } | ||
178 | catch (Exception ex) | ||
179 | { | ||
180 | Logger.Log.ErrorFormat("Failed writing data for asset {0} to {1}: {2}", metadata.ID, filename, ex.Message); | ||
181 | ret = BackendResponse.Failure; | ||
182 | } | ||
183 | |||
184 | server.MetricsProvider.LogAssetCreate(EXTENSION_NAME, ret, metadata.ID, assetData.Length, DateTime.Now); | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | public int ForEach(Action<Metadata> action, int start, int count) | ||
189 | { | ||
190 | int rowCount = 0; | ||
191 | |||
192 | lock (metadataStorage) | ||
193 | { | ||
194 | foreach (Metadata metadata in metadataStorage.Values) | ||
195 | { | ||
196 | action(metadata); | ||
197 | ++rowCount; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | return rowCount; | ||
202 | } | ||
203 | |||
204 | #endregion Required Interfaces | ||
205 | |||
206 | public void WipeTemporary() | ||
207 | { | ||
208 | if (Directory.Exists(TEMP_DATA_DIR)) | ||
209 | { | ||
210 | try { Directory.Delete(TEMP_DATA_DIR); } | ||
211 | catch (Exception ex) { Logger.Log.Error(ex.Message); } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | void LoadFiles(string folder, bool temporary) | ||
216 | { | ||
217 | // Try to create the directory if it doesn't already exist | ||
218 | if (!Directory.Exists(folder)) | ||
219 | { | ||
220 | try { Directory.CreateDirectory(folder); } | ||
221 | catch (Exception ex) | ||
222 | { | ||
223 | Logger.Log.Warn(ex.Message); | ||
224 | return; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | lock (metadataStorage) | ||
229 | { | ||
230 | try | ||
231 | { | ||
232 | string[] assets = Directory.GetFiles(folder); | ||
233 | |||
234 | for (int i = 0; i < assets.Length; i++) | ||
235 | { | ||
236 | string filename = assets[i]; | ||
237 | byte[] data = File.ReadAllBytes(filename); | ||
238 | |||
239 | Metadata metadata = new Metadata(); | ||
240 | metadata.CreationDate = File.GetCreationTime(filename); | ||
241 | metadata.Description = String.Empty; | ||
242 | metadata.ID = SimpleUtils.ParseUUIDFromFilename(filename); | ||
243 | metadata.Name = SimpleUtils.ParseNameFromFilename(filename); | ||
244 | metadata.SHA1 = OpenMetaverse.Utils.SHA1(data); | ||
245 | metadata.Temporary = false; | ||
246 | metadata.ContentType = Utils.ExtensionToContentType(Path.GetExtension(filename).TrimStart('.')); | ||
247 | |||
248 | // Store the loaded data | ||
249 | metadataStorage[metadata.ID] = metadata; | ||
250 | filenames[metadata.ID] = filename; | ||
251 | } | ||
252 | } | ||
253 | catch (Exception ex) | ||
254 | { | ||
255 | Logger.Log.Warn(ex.Message); | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Extensions/SimpleUtils.cs b/OpenSim/Grid/NewAssetServer/Extensions/SimpleUtils.cs new file mode 100644 index 0000000..6642f90 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Extensions/SimpleUtils.cs | |||
@@ -0,0 +1,44 @@ | |||
1 | using System; | ||
2 | using System.IO; | ||
3 | using OpenMetaverse; | ||
4 | |||
5 | namespace AssetServer.Extensions | ||
6 | { | ||
7 | public static class SimpleUtils | ||
8 | { | ||
9 | public static string ParseNameFromFilename(string filename) | ||
10 | { | ||
11 | filename = Path.GetFileName(filename); | ||
12 | |||
13 | int dot = filename.LastIndexOf('.'); | ||
14 | int firstDash = filename.IndexOf('-'); | ||
15 | |||
16 | if (dot - 37 > 0 && firstDash > 0) | ||
17 | return filename.Substring(0, firstDash); | ||
18 | else | ||
19 | return String.Empty; | ||
20 | } | ||
21 | |||
22 | public static UUID ParseUUIDFromFilename(string filename) | ||
23 | { | ||
24 | int dot = filename.LastIndexOf('.'); | ||
25 | |||
26 | if (dot > 35) | ||
27 | { | ||
28 | // Grab the last 36 characters of the filename | ||
29 | string uuidString = filename.Substring(dot - 36, 36); | ||
30 | UUID uuid; | ||
31 | UUID.TryParse(uuidString, out uuid); | ||
32 | return uuid; | ||
33 | } | ||
34 | else | ||
35 | { | ||
36 | UUID uuid; | ||
37 | if (UUID.TryParse(Path.GetFileName(filename), out uuid)) | ||
38 | return uuid; | ||
39 | else | ||
40 | return UUID.Zero; | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Interfaces.cs b/OpenSim/Grid/NewAssetServer/Interfaces.cs new file mode 100644 index 0000000..8368922 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Interfaces.cs | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using OpenMetaverse; | ||
34 | using OpenMetaverse.StructuredData; | ||
35 | using OpenSim.Framework; | ||
36 | |||
37 | namespace AssetServer | ||
38 | { | ||
39 | /// <summary> | ||
40 | /// Response from a call to a backend provider | ||
41 | /// </summary> | ||
42 | public enum BackendResponse | ||
43 | { | ||
44 | /// <summary>The call succeeded</summary> | ||
45 | Success, | ||
46 | /// <summary>The resource requested was not found</summary> | ||
47 | NotFound, | ||
48 | /// <summary>A server failure prevented the call from | ||
49 | /// completing</summary> | ||
50 | Failure | ||
51 | } | ||
52 | |||
53 | public class AssetServerPluginInitialiser : PluginInitialiserBase | ||
54 | { | ||
55 | private AssetServer server; | ||
56 | |||
57 | public AssetServerPluginInitialiser (AssetServer server) | ||
58 | { | ||
59 | this.server = server; | ||
60 | } | ||
61 | |||
62 | public override void Initialise (IPlugin plugin) | ||
63 | { | ||
64 | IAssetServerPlugin p = plugin as IAssetServerPlugin; | ||
65 | p.Initialise (server); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | #region Interfaces | ||
70 | |||
71 | public interface IAssetServerPlugin : IPlugin | ||
72 | { | ||
73 | void Initialise(AssetServer server); | ||
74 | } | ||
75 | |||
76 | public interface IStorageProvider | ||
77 | { | ||
78 | BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata); | ||
79 | BackendResponse TryFetchData(UUID assetID, out byte[] assetData); | ||
80 | BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData); | ||
81 | BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData); | ||
82 | BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID); | ||
83 | int ForEach(Action<Metadata> action, int start, int count); | ||
84 | } | ||
85 | |||
86 | public interface IAssetStorageProvider : IAssetServerPlugin | ||
87 | { | ||
88 | BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata); | ||
89 | BackendResponse TryFetchData(UUID assetID, out byte[] assetData); | ||
90 | BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData); | ||
91 | BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData); | ||
92 | BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID); | ||
93 | int ForEach(Action<Metadata> action, int start, int count); | ||
94 | } | ||
95 | |||
96 | public interface IInventoryProvider | ||
97 | { | ||
98 | BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItem item); | ||
99 | BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolder folder); | ||
100 | BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents); | ||
101 | BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolder> folders); | ||
102 | BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory); | ||
103 | |||
104 | BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItem> gestures); | ||
105 | |||
106 | BackendResponse TryCreateItem(Uri owner, InventoryItem item); | ||
107 | BackendResponse TryCreateFolder(Uri owner, InventoryFolder folder); | ||
108 | BackendResponse TryCreateInventory(Uri owner, InventoryFolder rootFolder); | ||
109 | |||
110 | BackendResponse TryDeleteItem(Uri owner, UUID itemID); | ||
111 | BackendResponse TryDeleteFolder(Uri owner, UUID folderID); | ||
112 | BackendResponse TryPurgeFolder(Uri owner, UUID folderID); | ||
113 | } | ||
114 | |||
115 | public interface IAuthenticationProvider | ||
116 | { | ||
117 | void AddIdentifier(UUID authToken, Uri identifier); | ||
118 | bool RemoveIdentifier(UUID authToken); | ||
119 | bool TryGetIdentifier(UUID authToken, out Uri identifier); | ||
120 | } | ||
121 | |||
122 | public interface IAuthorizationProvider | ||
123 | { | ||
124 | bool IsMetadataAuthorized(UUID authToken, UUID assetID); | ||
125 | /// <summary> | ||
126 | /// Authorizes access to the data for an asset. Access to asset data | ||
127 | /// also implies access to the metadata for that asset | ||
128 | /// </summary> | ||
129 | /// <param name="authToken">Authentication token to check for access</param> | ||
130 | /// <param name="assetID">ID of the requested asset</param> | ||
131 | /// <returns>True if access is granted, otherwise false</returns> | ||
132 | bool IsDataAuthorized(UUID authToken, UUID assetID); | ||
133 | bool IsCreateAuthorized(UUID authToken); | ||
134 | |||
135 | bool IsInventoryReadAuthorized(UUID authToken, Uri owner); | ||
136 | bool IsInventoryWriteAuthorized(UUID authToken, Uri owner); | ||
137 | } | ||
138 | |||
139 | public interface IMetricsProvider | ||
140 | { | ||
141 | void LogAssetMetadataFetch(string extension, BackendResponse response, UUID assetID, DateTime time); | ||
142 | void LogAssetDataFetch(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time); | ||
143 | void LogAssetCreate(string extension, BackendResponse response, UUID assetID, int dataSize, DateTime time); | ||
144 | |||
145 | void LogInventoryFetch(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time); | ||
146 | void LogInventoryFetchFolderContents(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time); | ||
147 | void LogInventoryFetchFolderList(string extension, BackendResponse response, Uri owner, DateTime time); | ||
148 | void LogInventoryFetchInventory(string extension, BackendResponse response, Uri owner, DateTime time); | ||
149 | void LogInventoryFetchActiveGestures(string extension, BackendResponse response, Uri owner, DateTime time); | ||
150 | void LogInventoryCreate(string extension, BackendResponse response, Uri owner, bool folder, DateTime time); | ||
151 | void LogInventoryCreateInventory(string extension, BackendResponse response, DateTime time); | ||
152 | void LogInventoryDelete(string extension, BackendResponse response, Uri owner, UUID objID, bool folder, DateTime time); | ||
153 | void LogInventoryPurgeFolder(string extension, BackendResponse response, Uri owner, UUID folderID, DateTime time); | ||
154 | } | ||
155 | |||
156 | #endregion Interfaces | ||
157 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/InventoryObjects.cs b/OpenSim/Grid/NewAssetServer/InventoryObjects.cs new file mode 100644 index 0000000..cffa643 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/InventoryObjects.cs | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using OpenMetaverse; | ||
33 | |||
34 | namespace AssetServer | ||
35 | { | ||
36 | public class InventoryBase | ||
37 | { | ||
38 | } | ||
39 | |||
40 | public class InventoryFolder : InventoryBase | ||
41 | { | ||
42 | public string Name; | ||
43 | public UUID Owner; | ||
44 | public UUID ParentID; | ||
45 | public UUID ID; | ||
46 | public short Type; | ||
47 | public ushort Version; | ||
48 | |||
49 | [NonSerialized] | ||
50 | public Dictionary<UUID, InventoryBase> Children = new Dictionary<UUID, InventoryBase>(); | ||
51 | |||
52 | public InventoryFolder() | ||
53 | { | ||
54 | } | ||
55 | |||
56 | public InventoryFolder(string name, UUID ownerID, UUID parentID, short assetType) | ||
57 | { | ||
58 | ID = UUID.Random(); | ||
59 | Name = name; | ||
60 | Owner = ownerID; | ||
61 | ParentID = parentID; | ||
62 | Type = assetType; | ||
63 | Version = 1; | ||
64 | } | ||
65 | |||
66 | public override string ToString() | ||
67 | { | ||
68 | return String.Format("{0} ({1})", Name, ID); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | public class InventoryItem : InventoryBase | ||
73 | { | ||
74 | public UUID ID; | ||
75 | public int InvType; | ||
76 | public UUID Folder; | ||
77 | public UUID Owner; | ||
78 | public UUID Creator; | ||
79 | public string Name; | ||
80 | public string Description; | ||
81 | public uint NextPermissions; | ||
82 | public uint CurrentPermissions; | ||
83 | public uint BasePermissions; | ||
84 | public uint EveryOnePermissions; | ||
85 | public uint GroupPermissions; | ||
86 | public int AssetType; | ||
87 | public UUID AssetID; | ||
88 | public UUID GroupID; | ||
89 | public bool GroupOwned; | ||
90 | public int SalePrice; | ||
91 | public byte SaleType; | ||
92 | public uint Flags; | ||
93 | public int CreationDate; | ||
94 | |||
95 | public override string ToString() | ||
96 | { | ||
97 | return String.Format("{0} ({1})", Name, ID); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | public class InventoryCollection | ||
102 | { | ||
103 | public Dictionary<UUID, InventoryFolder> Folders; | ||
104 | public Dictionary<UUID, InventoryItem> Items; | ||
105 | public UUID UserID; | ||
106 | } | ||
107 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Logger.cs b/OpenSim/Grid/NewAssetServer/Logger.cs new file mode 100644 index 0000000..dd40115 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Logger.cs | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using log4net; | ||
32 | using log4net.Config; | ||
33 | |||
34 | [assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "log4net")] | ||
35 | |||
36 | namespace AssetServer | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// Singleton logging class for the entire library | ||
40 | /// </summary> | ||
41 | public static class Logger | ||
42 | { | ||
43 | /// <summary>log4net logging engine</summary> | ||
44 | public static ILog Log; | ||
45 | |||
46 | static Logger() | ||
47 | { | ||
48 | Log = LogManager.GetLogger(System.Reflection.Assembly.GetExecutingAssembly().FullName); | ||
49 | |||
50 | // If error level reporting isn't enabled we assume no logger is configured and initialize a default | ||
51 | // ConsoleAppender | ||
52 | if (!Log.Logger.IsEnabledFor(log4net.Core.Level.Error)) | ||
53 | { | ||
54 | log4net.Appender.ConsoleAppender appender = new log4net.Appender.ConsoleAppender(); | ||
55 | appender.Layout = new log4net.Layout.PatternLayout("%timestamp [%thread] %-5level - %message%newline"); | ||
56 | BasicConfigurator.Configure(appender); | ||
57 | |||
58 | Log.Info("No log configuration found, defaulting to console logging"); | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Main.cs b/OpenSim/Grid/NewAssetServer/Main.cs new file mode 100644 index 0000000..9f7dd3e --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Main.cs | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.ServiceProcess; | ||
32 | |||
33 | namespace AssetServer | ||
34 | { | ||
35 | class MainEntry | ||
36 | { | ||
37 | static void Main(string[] args) | ||
38 | { | ||
39 | #if DEBUG | ||
40 | AssetServer server = new AssetServer(); | ||
41 | if (server.Start()) | ||
42 | { | ||
43 | Console.WriteLine("Asset server is running. Press CTRL+C to quit"); | ||
44 | |||
45 | Console.CancelKeyPress += | ||
46 | delegate(object sender, ConsoleCancelEventArgs e) | ||
47 | { | ||
48 | Console.WriteLine("Asset server is shutting down..."); | ||
49 | server.Shutdown(); | ||
50 | Environment.Exit(0); | ||
51 | }; | ||
52 | |||
53 | while (true) | ||
54 | Console.ReadLine(); | ||
55 | } | ||
56 | #else | ||
57 | ServiceBase[] servicesToRun = new ServiceBase[] { new AssetServer() }; | ||
58 | ServiceBase.Run(servicesToRun); | ||
59 | #endif | ||
60 | } | ||
61 | } | ||
62 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Metadata.cs b/OpenSim/Grid/NewAssetServer/Metadata.cs new file mode 100644 index 0000000..247a3e8 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Metadata.cs | |||
@@ -0,0 +1,85 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.Xml; | ||
4 | using OpenMetaverse; | ||
5 | using OpenMetaverse.StructuredData; | ||
6 | |||
7 | namespace AssetServer | ||
8 | { | ||
9 | public class Metadata | ||
10 | { | ||
11 | public UUID ID; | ||
12 | public string Name; | ||
13 | public string Description; | ||
14 | public DateTime CreationDate; | ||
15 | public string ContentType; | ||
16 | public byte[] SHA1; | ||
17 | public bool Temporary; | ||
18 | public Dictionary<string, Uri> Methods = new Dictionary<string, Uri>(); | ||
19 | public OSDMap ExtraData; | ||
20 | |||
21 | public OSDMap SerializeToOSD() | ||
22 | { | ||
23 | OSDMap osdata = new OSDMap(); | ||
24 | |||
25 | if (ID != UUID.Zero) osdata["id"] = OSD.FromUUID(ID); | ||
26 | osdata["name"] = OSD.FromString(Name); | ||
27 | osdata["description"] = OSD.FromString(Description); | ||
28 | osdata["creation_date"] = OSD.FromDate(CreationDate); | ||
29 | osdata["type"] = OSD.FromString(ContentType); | ||
30 | osdata["sha1"] = OSD.FromBinary(SHA1); | ||
31 | osdata["temporary"] = OSD.FromBoolean(Temporary); | ||
32 | |||
33 | OSDMap methods = new OSDMap(Methods.Count); | ||
34 | foreach (KeyValuePair<string, Uri> kvp in Methods) | ||
35 | methods.Add(kvp.Key, OSD.FromUri(kvp.Value)); | ||
36 | osdata["methods"] = methods; | ||
37 | |||
38 | if (ExtraData != null) osdata["extra_data"] = ExtraData; | ||
39 | |||
40 | return osdata; | ||
41 | } | ||
42 | |||
43 | public byte[] SerializeToBytes() | ||
44 | { | ||
45 | LitJson.JsonData jsonData = OSDParser.SerializeJson(SerializeToOSD()); | ||
46 | return System.Text.Encoding.UTF8.GetBytes(jsonData.ToJson()); | ||
47 | } | ||
48 | |||
49 | public void Deserialize(byte[] data) | ||
50 | { | ||
51 | OSD osdata = OSDParser.DeserializeJson(System.Text.Encoding.UTF8.GetString(data)); | ||
52 | Deserialize(osdata); | ||
53 | } | ||
54 | |||
55 | public void Deserialize(string data) | ||
56 | { | ||
57 | OSD osdata = OSDParser.DeserializeJson(data); | ||
58 | Deserialize(osdata); | ||
59 | } | ||
60 | |||
61 | public void Deserialize(OSD osdata) | ||
62 | { | ||
63 | if (osdata.Type == OSDType.Map) | ||
64 | { | ||
65 | OSDMap map = (OSDMap)osdata; | ||
66 | ID = map["id"].AsUUID(); | ||
67 | Name = map["name"].AsString(); | ||
68 | Description = map["description"].AsString(); | ||
69 | CreationDate = map["creation_date"].AsDate(); | ||
70 | ContentType = map["type"].AsString(); | ||
71 | SHA1 = map["sha1"].AsBinary(); | ||
72 | Temporary = map["temporary"].AsBoolean(); | ||
73 | |||
74 | OSDMap methods = map["methods"] as OSDMap; | ||
75 | if (methods != null) | ||
76 | { | ||
77 | foreach (KeyValuePair<string, OSD> kvp in methods) | ||
78 | Methods.Add(kvp.Key, kvp.Value.AsUri()); | ||
79 | } | ||
80 | |||
81 | ExtraData = map["extra_data"] as OSDMap; | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Plugins/OpenSim/OpenSimAssetStoragePlugin.cs b/OpenSim/Grid/NewAssetServer/Plugins/OpenSim/OpenSimAssetStoragePlugin.cs new file mode 100644 index 0000000..dd05e5d --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Plugins/OpenSim/OpenSimAssetStoragePlugin.cs | |||
@@ -0,0 +1,352 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Generic; | ||
32 | using System.Net; | ||
33 | using System.Data; | ||
34 | using MySql.Data.MySqlClient; | ||
35 | using ExtensionLoader; | ||
36 | using ExtensionLoader.Config; | ||
37 | using OpenMetaverse; | ||
38 | using OpenMetaverse.StructuredData; | ||
39 | using OpenSim.Framework; | ||
40 | using AssetServer.Extensions; | ||
41 | |||
42 | namespace AssetServer.Plugins | ||
43 | { | ||
44 | public class OpenSimAssetStoragePlugin : IAssetStorageProvider | ||
45 | { | ||
46 | const string EXTENSION_NAME = "OpenSimAssetStorage"; // Used in metrics reporting | ||
47 | |||
48 | private AssetServer server; | ||
49 | private IAssetProviderPlugin m_assetProvider; | ||
50 | |||
51 | public OpenSimAssetStoragePlugin() | ||
52 | { | ||
53 | } | ||
54 | |||
55 | #region IAssetStorageProvider implementation | ||
56 | |||
57 | public BackendResponse TryFetchMetadata(UUID assetID, out Metadata metadata) | ||
58 | { | ||
59 | metadata = null; | ||
60 | BackendResponse ret; | ||
61 | |||
62 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
63 | { | ||
64 | IDataReader reader; | ||
65 | |||
66 | try | ||
67 | { | ||
68 | dbConnection.Open(); | ||
69 | |||
70 | IDbCommand command = dbConnection.CreateCommand(); | ||
71 | command.CommandText = String.Format("SELECT name,description,assetType,temporary FROM assets WHERE id='{0}'", assetID.ToString()); | ||
72 | reader = command.ExecuteReader(); | ||
73 | |||
74 | if (reader.Read()) | ||
75 | { | ||
76 | metadata = new Metadata(); | ||
77 | metadata.CreationDate = OpenMetaverse.Utils.Epoch; | ||
78 | metadata.SHA1 = null; | ||
79 | metadata.ID = assetID; | ||
80 | metadata.Name = reader.GetString(0); | ||
81 | metadata.Description = reader.GetString(1); | ||
82 | metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2)); | ||
83 | metadata.Temporary = reader.GetBoolean(3); | ||
84 | |||
85 | ret = BackendResponse.Success; | ||
86 | } | ||
87 | else | ||
88 | { | ||
89 | ret = BackendResponse.NotFound; | ||
90 | } | ||
91 | } | ||
92 | catch (MySqlException ex) | ||
93 | { | ||
94 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
95 | ret = BackendResponse.Failure; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now); | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | public BackendResponse TryFetchData(UUID assetID, out byte[] assetData) | ||
104 | { | ||
105 | assetData = null; | ||
106 | BackendResponse ret; | ||
107 | |||
108 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
109 | { | ||
110 | IDataReader reader; | ||
111 | |||
112 | try | ||
113 | { | ||
114 | dbConnection.Open(); | ||
115 | |||
116 | IDbCommand command = dbConnection.CreateCommand(); | ||
117 | command.CommandText = String.Format("SELECT data FROM assets WHERE id='{0}'", assetID.ToString()); | ||
118 | reader = command.ExecuteReader(); | ||
119 | |||
120 | if (reader.Read()) | ||
121 | { | ||
122 | assetData = (byte[])reader.GetValue(0); | ||
123 | ret = BackendResponse.Success; | ||
124 | } | ||
125 | else | ||
126 | { | ||
127 | ret = BackendResponse.NotFound; | ||
128 | } | ||
129 | } | ||
130 | catch (MySqlException ex) | ||
131 | { | ||
132 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
133 | ret = BackendResponse.Failure; | ||
134 | } | ||
135 | } | ||
136 | |||
137 | server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now); | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | public BackendResponse TryFetchDataMetadata(UUID assetID, out Metadata metadata, out byte[] assetData) | ||
142 | { | ||
143 | metadata = null; | ||
144 | assetData = null; | ||
145 | BackendResponse ret; | ||
146 | |||
147 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
148 | { | ||
149 | IDataReader reader; | ||
150 | |||
151 | try | ||
152 | { | ||
153 | dbConnection.Open(); | ||
154 | |||
155 | IDbCommand command = dbConnection.CreateCommand(); | ||
156 | command.CommandText = String.Format("SELECT name,description,assetType,temporary,data FROM assets WHERE id='{0}'", assetID.ToString()); | ||
157 | reader = command.ExecuteReader(); | ||
158 | |||
159 | if (reader.Read()) | ||
160 | { | ||
161 | metadata = new Metadata(); | ||
162 | metadata.CreationDate = OpenMetaverse.Utils.Epoch; | ||
163 | metadata.SHA1 = null; | ||
164 | metadata.ID = assetID; | ||
165 | metadata.Name = reader.GetString(0); | ||
166 | metadata.Description = reader.GetString(1); | ||
167 | metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2)); | ||
168 | metadata.Temporary = reader.GetBoolean(3); | ||
169 | |||
170 | assetData = (byte[])reader.GetValue(4); | ||
171 | |||
172 | ret = BackendResponse.Success; | ||
173 | } | ||
174 | else | ||
175 | { | ||
176 | ret = BackendResponse.NotFound; | ||
177 | } | ||
178 | } | ||
179 | catch (MySqlException ex) | ||
180 | { | ||
181 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
182 | ret = BackendResponse.Failure; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | server.MetricsProvider.LogAssetMetadataFetch(EXTENSION_NAME, ret, assetID, DateTime.Now); | ||
187 | server.MetricsProvider.LogAssetDataFetch(EXTENSION_NAME, ret, assetID, (assetData != null ? assetData.Length : 0), DateTime.Now); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData, out UUID assetID) | ||
192 | { | ||
193 | assetID = metadata.ID = UUID.Random(); | ||
194 | return TryCreateAsset(metadata, assetData); | ||
195 | } | ||
196 | |||
197 | public BackendResponse TryCreateAsset(Metadata metadata, byte[] assetData) | ||
198 | { | ||
199 | BackendResponse ret; | ||
200 | |||
201 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
202 | { | ||
203 | try | ||
204 | { | ||
205 | dbConnection.Open(); | ||
206 | |||
207 | MySqlCommand command = new MySqlCommand( | ||
208 | "REPLACE INTO assets (name,description,assetType,local,temporary,data,id) VALUES " + | ||
209 | "(?name,?description,?assetType,?local,?temporary,?data,?id)", dbConnection); | ||
210 | |||
211 | command.Parameters.AddWithValue("?name", metadata.Name); | ||
212 | command.Parameters.AddWithValue("?description", metadata.Description); | ||
213 | command.Parameters.AddWithValue("?assetType", Utils.ContentTypeToSLAssetType(metadata.ContentType)); | ||
214 | command.Parameters.AddWithValue("?local", 0); | ||
215 | command.Parameters.AddWithValue("?temporary", metadata.Temporary); | ||
216 | command.Parameters.AddWithValue("?data", assetData); | ||
217 | command.Parameters.AddWithValue("?id", metadata.ID.ToString()); | ||
218 | |||
219 | int rowsAffected = command.ExecuteNonQuery(); | ||
220 | if (rowsAffected == 1) | ||
221 | { | ||
222 | ret = BackendResponse.Success; | ||
223 | } | ||
224 | else if (rowsAffected == 2) | ||
225 | { | ||
226 | Logger.Log.Info("Replaced asset " + metadata.ID.ToString()); | ||
227 | ret = BackendResponse.Success; | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | Logger.Log.ErrorFormat("MySQL REPLACE query affected {0} rows", rowsAffected); | ||
232 | ret = BackendResponse.Failure; | ||
233 | } | ||
234 | } | ||
235 | catch (MySqlException ex) | ||
236 | { | ||
237 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
238 | ret = BackendResponse.Failure; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | server.MetricsProvider.LogAssetCreate(EXTENSION_NAME, ret, metadata.ID, assetData.Length, DateTime.Now); | ||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | public int ForEach(Action<Metadata> action, int start, int count) | ||
247 | { | ||
248 | int rowCount = 0; | ||
249 | |||
250 | using (MySqlConnection dbConnection = new MySqlConnection(DBConnString.GetConnectionString(server.ConfigFile))) | ||
251 | { | ||
252 | MySqlDataReader reader; | ||
253 | |||
254 | try | ||
255 | { | ||
256 | dbConnection.Open(); | ||
257 | |||
258 | MySqlCommand command = dbConnection.CreateCommand(); | ||
259 | command.CommandText = String.Format("SELECT name,description,assetType,temporary,data,id FROM assets LIMIT {0}, {1}", | ||
260 | start, count); | ||
261 | reader = command.ExecuteReader(); | ||
262 | } | ||
263 | catch (MySqlException ex) | ||
264 | { | ||
265 | Logger.Log.Error("Connection to MySQL backend failed: " + ex.Message); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | while (reader.Read()) | ||
270 | { | ||
271 | Metadata metadata = new Metadata(); | ||
272 | metadata.CreationDate = OpenMetaverse.Utils.Epoch; | ||
273 | metadata.Description = reader.GetString(1); | ||
274 | metadata.ID = UUID.Parse(reader.GetString(5)); | ||
275 | metadata.Name = reader.GetString(0); | ||
276 | metadata.SHA1 = OpenMetaverse.Utils.SHA1((byte[])reader.GetValue(4)); | ||
277 | metadata.Temporary = reader.GetBoolean(3); | ||
278 | metadata.ContentType = Utils.SLAssetTypeToContentType(reader.GetInt32(2)); | ||
279 | |||
280 | action(metadata); | ||
281 | ++rowCount; | ||
282 | } | ||
283 | |||
284 | reader.Close(); | ||
285 | } | ||
286 | |||
287 | return rowCount; | ||
288 | } | ||
289 | |||
290 | #endregion IAssetStorageProvider implementation | ||
291 | |||
292 | #region IPlugin implementation | ||
293 | |||
294 | public void Initialise(AssetServer server) | ||
295 | { | ||
296 | this.server = server; | ||
297 | |||
298 | try | ||
299 | { | ||
300 | m_assetProvider = LoadDatabasePlugin("OpenSim.Data.MySQL.dll", server.ConfigFile.Configs["MySQL"].GetString("database_connect", null)); | ||
301 | if (m_assetProvider == null) | ||
302 | { | ||
303 | Logger.Log.Error("[ASSET]: Failed to load a database plugin, server halting."); | ||
304 | Environment.Exit(-1); | ||
305 | } | ||
306 | else | ||
307 | Logger.Log.InfoFormat("[ASSET]: Loaded storage backend: {0}", Version); | ||
308 | } | ||
309 | catch (Exception e) | ||
310 | { | ||
311 | Logger.Log.WarnFormat("[ASSET]: Failure loading data plugin: {0}", e.ToString()); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | /// <summary> | ||
316 | /// <para>Initialises asset interface</para> | ||
317 | /// </summary> | ||
318 | public void Initialise() | ||
319 | { | ||
320 | Logger.Log.InfoFormat("[ASSET]: {0} cannot be default-initialized!", Name); | ||
321 | throw new PluginNotInitialisedException(Name); | ||
322 | } | ||
323 | |||
324 | public void Dispose() | ||
325 | { | ||
326 | } | ||
327 | |||
328 | public string Version | ||
329 | { | ||
330 | get { return m_assetProvider.Version; } | ||
331 | } | ||
332 | |||
333 | public string Name | ||
334 | { | ||
335 | get { return "AssetServer storage provider"; } | ||
336 | } | ||
337 | |||
338 | #endregion IPlugin implementation | ||
339 | |||
340 | private IAssetProviderPlugin LoadDatabasePlugin(string provider, string connect) | ||
341 | { | ||
342 | PluginLoader<IAssetProviderPlugin> loader = new PluginLoader<IAssetProviderPlugin>(new AssetDataInitialiser(connect)); | ||
343 | |||
344 | // Loader will try to load all providers (MySQL, MSSQL, etc) | ||
345 | // unless it is constrainted to the correct "Provider" entry in the addin.xml | ||
346 | loader.Add("/OpenSim/AssetData", new PluginProviderFilter (provider)); | ||
347 | loader.Load(); | ||
348 | |||
349 | return loader.Plugin; | ||
350 | } | ||
351 | } | ||
352 | } | ||
diff --git a/OpenSim/Grid/NewAssetServer/Plugins/OpenSim/Resources/AssetServerOpenSimPlugins.addin.xml b/OpenSim/Grid/NewAssetServer/Plugins/OpenSim/Resources/AssetServerOpenSimPlugins.addin.xml new file mode 100644 index 0000000..0e473ad --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Plugins/OpenSim/Resources/AssetServerOpenSimPlugins.addin.xml | |||
@@ -0,0 +1,14 @@ | |||
1 | <Addin id="OpenSim.Grid.AssetServer.Plugins.OpenSim" version="0.1"> | ||
2 | <Runtime> | ||
3 | <Import assembly="OpenSim.Grid.AssetServer.Plugins.OpenSim.dll"/> | ||
4 | </Runtime> | ||
5 | <Dependencies> | ||
6 | <Addin id="OpenSim.Grid.NewAssetServer" version="0.1" /> | ||
7 | </Dependencies> | ||
8 | <ExtensionPoint path = "/OpenSim/AssetData"> | ||
9 | <ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="OpenSim.Framework.IAssetProviderPlugin"/> | ||
10 | </ExtensionPoint> | ||
11 | <Extension path="/OpenSim/AssetServer/StorageProvider"> | ||
12 | <Plugin id="OpenSimAssetStorage" provider="OpenSim.Grid.AssetServer.Plugins.OpenSim.dll" type="AssetServer.Plugins.OpenSimAssetStoragePlugin" /> | ||
13 | </Extension> | ||
14 | </Addin> | ||
diff --git a/OpenSim/Grid/NewAssetServer/Utils.cs b/OpenSim/Grid/NewAssetServer/Utils.cs new file mode 100644 index 0000000..5499933 --- /dev/null +++ b/OpenSim/Grid/NewAssetServer/Utils.cs | |||
@@ -0,0 +1,1034 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008 Intel Corporation | ||
3 | * All rights reserved. | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * | ||
8 | * -- Redistributions of source code must retain the above copyright | ||
9 | * notice, this list of conditions and the following disclaimer. | ||
10 | * -- Redistributions in binary form must reproduce the above copyright | ||
11 | * notice, this list of conditions and the following disclaimer in the | ||
12 | * documentation and/or other materials provided with the distribution. | ||
13 | * -- Neither the name of the Intel Corporation nor the names of its | ||
14 | * contributors may be used to endorse or promote products derived from | ||
15 | * this software without specific prior written permission. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
20 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR ITS | ||
21 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
22 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
24 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
25 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
26 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | using System; | ||
31 | using System.Collections.Specialized; | ||
32 | using System.Globalization; | ||
33 | using System.Net; | ||
34 | using System.Xml; | ||
35 | using System.Xml.Serialization; | ||
36 | using OpenMetaverse; | ||
37 | using OpenMetaverse.StructuredData; | ||
38 | using HttpServer; | ||
39 | |||
40 | namespace AssetServer | ||
41 | { | ||
42 | public static class Utils | ||
43 | { | ||
44 | public static UUID GetAuthToken(IHttpRequest request) | ||
45 | { | ||
46 | UUID authToken = UUID.Zero; | ||
47 | |||
48 | string[] authHeader = request.Headers.GetValues("Authorization"); | ||
49 | if (authHeader != null && authHeader.Length == 1) | ||
50 | { | ||
51 | // Example header: | ||
52 | // Authorization: OpenGrid 65fda0b5-4446-42f5-b828-aaf644293646 | ||
53 | string[] authHeaderParts = authHeader[0].Split(' '); | ||
54 | if (authHeaderParts.Length == 2 && authHeaderParts[0] == "OpenGrid") | ||
55 | UUID.TryParse(authHeaderParts[1], out authToken); | ||
56 | } | ||
57 | |||
58 | if (authToken == UUID.Zero && request.Cookies != null) | ||
59 | { | ||
60 | // Check for an authToken cookie to make logins browser-compatible | ||
61 | RequestCookie authCookie = request.Cookies["authToken"]; | ||
62 | if (authCookie != null) | ||
63 | UUID.TryParse(authCookie.Value, out authToken); | ||
64 | } | ||
65 | |||
66 | return authToken; | ||
67 | } | ||
68 | |||
69 | public static Uri GetOpenSimUri(UUID avatarID) | ||
70 | { | ||
71 | return new Uri("http://opensim/" + avatarID.ToString()); | ||
72 | } | ||
73 | |||
74 | public static bool TryGetOpenSimUUID(Uri avatarUri, out UUID avatarID) | ||
75 | { | ||
76 | string[] parts = avatarUri.Segments; | ||
77 | return UUID.TryParse(parts[parts.Length - 1], out avatarID); | ||
78 | } | ||
79 | |||
80 | #region SL / file extension / content-type conversions | ||
81 | |||
82 | public static string SLAssetTypeToContentType(int assetType) | ||
83 | { | ||
84 | switch (assetType) | ||
85 | { | ||
86 | case 0: | ||
87 | return "image/jp2"; | ||
88 | case 1: | ||
89 | return "application/ogg"; | ||
90 | case 2: | ||
91 | return "application/x-metaverse-callingcard"; | ||
92 | case 3: | ||
93 | return "application/x-metaverse-landmark"; | ||
94 | case 5: | ||
95 | return "application/x-metaverse-clothing"; | ||
96 | case 6: | ||
97 | return "application/x-metaverse-primitive"; | ||
98 | case 7: | ||
99 | return "application/x-metaverse-notecard"; | ||
100 | case 8: | ||
101 | return "application/x-metaverse-folder"; | ||
102 | case 10: | ||
103 | return "application/x-metaverse-lsl"; | ||
104 | case 11: | ||
105 | return "application/x-metaverse-lso"; | ||
106 | case 12: | ||
107 | return "image/tga"; | ||
108 | case 13: | ||
109 | return "application/x-metaverse-bodypart"; | ||
110 | case 17: | ||
111 | return "audio/x-wav"; | ||
112 | case 19: | ||
113 | return "image/jpeg"; | ||
114 | case 20: | ||
115 | return "application/x-metaverse-animation"; | ||
116 | case 21: | ||
117 | return "application/x-metaverse-gesture"; | ||
118 | case 22: | ||
119 | return "application/x-metaverse-simstate"; | ||
120 | default: | ||
121 | return "application/octet-stream"; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | public static int ContentTypeToSLAssetType(string contentType) | ||
126 | { | ||
127 | switch (contentType) | ||
128 | { | ||
129 | case "image/jp2": | ||
130 | return 0; | ||
131 | case "application/ogg": | ||
132 | return 1; | ||
133 | case "application/x-metaverse-callingcard": | ||
134 | return 2; | ||
135 | case "application/x-metaverse-landmark": | ||
136 | return 3; | ||
137 | case "application/x-metaverse-clothing": | ||
138 | return 5; | ||
139 | case "application/x-metaverse-primitive": | ||
140 | return 6; | ||
141 | case "application/x-metaverse-notecard": | ||
142 | return 7; | ||
143 | case "application/x-metaverse-lsl": | ||
144 | return 10; | ||
145 | case "application/x-metaverse-lso": | ||
146 | return 11; | ||
147 | case "image/tga": | ||
148 | return 12; | ||
149 | case "application/x-metaverse-bodypart": | ||
150 | return 13; | ||
151 | case "audio/x-wav": | ||
152 | return 17; | ||
153 | case "image/jpeg": | ||
154 | return 19; | ||
155 | case "application/x-metaverse-animation": | ||
156 | return 20; | ||
157 | case "application/x-metaverse-gesture": | ||
158 | return 21; | ||
159 | case "application/x-metaverse-simstate": | ||
160 | return 22; | ||
161 | default: | ||
162 | return -1; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | public static string ContentTypeToExtension(string contentType) | ||
167 | { | ||
168 | switch (contentType) | ||
169 | { | ||
170 | case "image/jp2": | ||
171 | return "texture"; | ||
172 | case "application/ogg": | ||
173 | return "ogg"; | ||
174 | case "application/x-metaverse-callingcard": | ||
175 | return "callingcard"; | ||
176 | case "application/x-metaverse-landmark": | ||
177 | return "landmark"; | ||
178 | case "application/x-metaverse-clothing": | ||
179 | return "clothing"; | ||
180 | case "application/x-metaverse-primitive": | ||
181 | return "primitive"; | ||
182 | case "application/x-metaverse-notecard": | ||
183 | return "notecard"; | ||
184 | case "application/x-metaverse-lsl": | ||
185 | return "lsl"; | ||
186 | case "application/x-metaverse-lso": | ||
187 | return "lso"; | ||
188 | case "image/tga": | ||
189 | return "tga"; | ||
190 | case "application/x-metaverse-bodypart": | ||
191 | return "bodypart"; | ||
192 | case "audio/x-wav": | ||
193 | return "wav"; | ||
194 | case "image/jpeg": | ||
195 | return "jpg"; | ||
196 | case "application/x-metaverse-animation": | ||
197 | return "animation"; | ||
198 | case "application/x-metaverse-gesture": | ||
199 | return "gesture"; | ||
200 | case "application/x-metaverse-simstate": | ||
201 | return "simstate"; | ||
202 | default: | ||
203 | return "bin"; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | public static string ExtensionToContentType(string extension) | ||
208 | { | ||
209 | switch (extension) | ||
210 | { | ||
211 | case "texture": | ||
212 | case "jp2": | ||
213 | case "j2c": | ||
214 | return "image/jp2"; | ||
215 | case "sound": | ||
216 | case "ogg": | ||
217 | return "application/ogg"; | ||
218 | case "callingcard": | ||
219 | return "application/x-metaverse-callingcard"; | ||
220 | case "landmark": | ||
221 | return "application/x-metaverse-landmark"; | ||
222 | case "clothing": | ||
223 | return "application/x-metaverse-clothing"; | ||
224 | case "primitive": | ||
225 | return "application/x-metaverse-primitive"; | ||
226 | case "notecard": | ||
227 | return "application/x-metaverse-notecard"; | ||
228 | case "lsl": | ||
229 | return "application/x-metaverse-lsl"; | ||
230 | case "lso": | ||
231 | return "application/x-metaverse-lso"; | ||
232 | case "tga": | ||
233 | return "image/tga"; | ||
234 | case "bodypart": | ||
235 | return "application/x-metaverse-bodypart"; | ||
236 | case "wav": | ||
237 | return "audio/x-wav"; | ||
238 | case "jpg": | ||
239 | case "jpeg": | ||
240 | return "image/jpeg"; | ||
241 | case "animation": | ||
242 | return "application/x-metaverse-animation"; | ||
243 | case "gesture": | ||
244 | return "application/x-metaverse-gesture"; | ||
245 | case "simstate": | ||
246 | return "application/x-metaverse-simstate"; | ||
247 | case "txt": | ||
248 | return "text/plain"; | ||
249 | case "xml": | ||
250 | return "application/xml"; | ||
251 | default: | ||
252 | return "application/octet-stream"; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | #endregion SL / file extension / content-type conversions | ||
257 | |||
258 | #region XML Serialization | ||
259 | |||
260 | public class GeneratedReader : XmlSerializationReader | ||
261 | { | ||
262 | public object ReadRoot_InventoryFolderBase() | ||
263 | { | ||
264 | Reader.MoveToContent(); | ||
265 | if (Reader.LocalName != "InventoryFolderBase" || Reader.NamespaceURI != "") | ||
266 | throw CreateUnknownNodeException(); | ||
267 | return ReadObject_InventoryFolder(true, true); | ||
268 | } | ||
269 | |||
270 | public object ReadRoot_InventoryItemBase() | ||
271 | { | ||
272 | Reader.MoveToContent(); | ||
273 | if (Reader.LocalName != "InventoryItemBase" || Reader.NamespaceURI != "") | ||
274 | throw CreateUnknownNodeException(); | ||
275 | return ReadObject_InventoryItem(true, true); | ||
276 | } | ||
277 | |||
278 | public object ReadRoot_InventoryCollection() | ||
279 | { | ||
280 | Reader.MoveToContent(); | ||
281 | if (Reader.LocalName != "InventoryCollection" || Reader.NamespaceURI != "") | ||
282 | throw CreateUnknownNodeException(); | ||
283 | return ReadObject_InventoryCollection(true, true); | ||
284 | } | ||
285 | |||
286 | public InventoryFolder ReadObject_InventoryFolder(bool isNullable, bool checkType) | ||
287 | { | ||
288 | InventoryFolder ob = null; | ||
289 | if (isNullable && ReadNull()) return null; | ||
290 | |||
291 | if (checkType) | ||
292 | { | ||
293 | System.Xml.XmlQualifiedName t = GetXsiType(); | ||
294 | if (t == null) | ||
295 | { } | ||
296 | else if (t.Name != "InventoryFolderBase" || t.Namespace != "") | ||
297 | throw CreateUnknownTypeException(t); | ||
298 | } | ||
299 | |||
300 | ob = (InventoryFolder)Activator.CreateInstance(typeof(InventoryFolder), true); | ||
301 | |||
302 | Reader.MoveToElement(); | ||
303 | |||
304 | while (Reader.MoveToNextAttribute()) | ||
305 | { | ||
306 | if (IsXmlnsAttribute(Reader.Name)) | ||
307 | { | ||
308 | } | ||
309 | else | ||
310 | { | ||
311 | UnknownNode(ob); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | Reader.MoveToElement(); | ||
316 | Reader.MoveToElement(); | ||
317 | if (Reader.IsEmptyElement) | ||
318 | { | ||
319 | Reader.Skip(); | ||
320 | return ob; | ||
321 | } | ||
322 | |||
323 | Reader.ReadStartElement(); | ||
324 | Reader.MoveToContent(); | ||
325 | |||
326 | bool b0 = false, b1 = false, b2 = false, b3 = false, b4 = false, b5 = false; | ||
327 | |||
328 | while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) | ||
329 | { | ||
330 | if (Reader.NodeType == System.Xml.XmlNodeType.Element) | ||
331 | { | ||
332 | if (Reader.LocalName == "Owner" && Reader.NamespaceURI == "" && !b1) | ||
333 | { | ||
334 | b1 = true; | ||
335 | ob.@Owner = ReadObject_UUID(false, true); | ||
336 | } | ||
337 | else if (Reader.LocalName == "Version" && Reader.NamespaceURI == "" && !b5) | ||
338 | { | ||
339 | b5 = true; | ||
340 | string s6 = Reader.ReadElementString(); | ||
341 | ob.@Version = UInt16.Parse(s6, CultureInfo.InvariantCulture); | ||
342 | } | ||
343 | else if (Reader.LocalName == "ID" && Reader.NamespaceURI == "" && !b3) | ||
344 | { | ||
345 | b3 = true; | ||
346 | ob.@ID = ReadObject_UUID(false, true); | ||
347 | } | ||
348 | else if (Reader.LocalName == "Type" && Reader.NamespaceURI == "" && !b4) | ||
349 | { | ||
350 | b4 = true; | ||
351 | string s7 = Reader.ReadElementString(); | ||
352 | ob.@Type = Int16.Parse(s7, CultureInfo.InvariantCulture); | ||
353 | } | ||
354 | else if (Reader.LocalName == "Name" && Reader.NamespaceURI == "" && !b0) | ||
355 | { | ||
356 | b0 = true; | ||
357 | string s8 = Reader.ReadElementString(); | ||
358 | ob.@Name = s8; | ||
359 | } | ||
360 | else if (Reader.LocalName == "ParentID" && Reader.NamespaceURI == "" && !b2) | ||
361 | { | ||
362 | b2 = true; | ||
363 | ob.@ParentID = ReadObject_UUID(false, true); | ||
364 | } | ||
365 | else | ||
366 | { | ||
367 | UnknownNode(ob); | ||
368 | } | ||
369 | } | ||
370 | else | ||
371 | UnknownNode(ob); | ||
372 | |||
373 | Reader.MoveToContent(); | ||
374 | } | ||
375 | |||
376 | ReadEndElement(); | ||
377 | |||
378 | return ob; | ||
379 | } | ||
380 | |||
381 | public InventoryItem ReadObject_InventoryItem(bool isNullable, bool checkType) | ||
382 | { | ||
383 | InventoryItem ob = null; | ||
384 | if (isNullable && ReadNull()) return null; | ||
385 | |||
386 | if (checkType) | ||
387 | { | ||
388 | System.Xml.XmlQualifiedName t = GetXsiType(); | ||
389 | if (t == null) | ||
390 | { } | ||
391 | else if (t.Name != "InventoryItemBase" || t.Namespace != "") | ||
392 | throw CreateUnknownTypeException(t); | ||
393 | } | ||
394 | |||
395 | ob = (InventoryItem)Activator.CreateInstance(typeof(InventoryItem), true); | ||
396 | |||
397 | Reader.MoveToElement(); | ||
398 | |||
399 | while (Reader.MoveToNextAttribute()) | ||
400 | { | ||
401 | if (IsXmlnsAttribute(Reader.Name)) | ||
402 | { | ||
403 | } | ||
404 | else | ||
405 | { | ||
406 | UnknownNode(ob); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | Reader.MoveToElement(); | ||
411 | Reader.MoveToElement(); | ||
412 | if (Reader.IsEmptyElement) | ||
413 | { | ||
414 | Reader.Skip(); | ||
415 | return ob; | ||
416 | } | ||
417 | |||
418 | Reader.ReadStartElement(); | ||
419 | Reader.MoveToContent(); | ||
420 | |||
421 | bool b9 = false, b10 = false, b11 = false, b12 = false, b13 = false, b14 = false, b15 = false, b16 = false, b17 = false, b18 = false, b19 = false, b20 = false, b21 = false, b22 = false, b23 = false, b24 = false, b25 = false, b26 = false, b27 = false, b28 = false; | ||
422 | |||
423 | while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) | ||
424 | { | ||
425 | if (Reader.NodeType == System.Xml.XmlNodeType.Element) | ||
426 | { | ||
427 | if (Reader.LocalName == "GroupPermissions" && Reader.NamespaceURI == "" && !b20) | ||
428 | { | ||
429 | b20 = true; | ||
430 | string s29 = Reader.ReadElementString(); | ||
431 | ob.@GroupPermissions = UInt32.Parse(s29, CultureInfo.InvariantCulture); | ||
432 | } | ||
433 | else if (Reader.LocalName == "AssetType" && Reader.NamespaceURI == "" && !b21) | ||
434 | { | ||
435 | b21 = true; | ||
436 | string s30 = Reader.ReadElementString(); | ||
437 | ob.@AssetType = Int32.Parse(s30, CultureInfo.InvariantCulture); | ||
438 | } | ||
439 | else if (Reader.LocalName == "SalePrice" && Reader.NamespaceURI == "" && !b25) | ||
440 | { | ||
441 | b25 = true; | ||
442 | string s31 = Reader.ReadElementString(); | ||
443 | ob.@SalePrice = Int32.Parse(s31, CultureInfo.InvariantCulture); | ||
444 | } | ||
445 | else if (Reader.LocalName == "AssetID" && Reader.NamespaceURI == "" && !b22) | ||
446 | { | ||
447 | b22 = true; | ||
448 | ob.@AssetID = ReadObject_UUID(false, true); | ||
449 | } | ||
450 | else if (Reader.LocalName == "Folder" && Reader.NamespaceURI == "" && !b11) | ||
451 | { | ||
452 | b11 = true; | ||
453 | ob.@Folder = ReadObject_UUID(false, true); | ||
454 | } | ||
455 | else if (Reader.LocalName == "Name" && Reader.NamespaceURI == "" && !b14) | ||
456 | { | ||
457 | b14 = true; | ||
458 | string s32 = Reader.ReadElementString(); | ||
459 | ob.@Name = s32; | ||
460 | } | ||
461 | else if (Reader.LocalName == "NextPermissions" && Reader.NamespaceURI == "" && !b16) | ||
462 | { | ||
463 | b16 = true; | ||
464 | string s33 = Reader.ReadElementString(); | ||
465 | ob.@NextPermissions = UInt32.Parse(s33, CultureInfo.InvariantCulture); | ||
466 | } | ||
467 | else if (Reader.LocalName == "BasePermissions" && Reader.NamespaceURI == "" && !b18) | ||
468 | { | ||
469 | b18 = true; | ||
470 | string s34 = Reader.ReadElementString(); | ||
471 | ob.@BasePermissions = UInt32.Parse(s34, CultureInfo.InvariantCulture); | ||
472 | } | ||
473 | else if (Reader.LocalName == "ID" && Reader.NamespaceURI == "" && !b9) | ||
474 | { | ||
475 | b9 = true; | ||
476 | ob.@ID = ReadObject_UUID(false, true); | ||
477 | } | ||
478 | else if (Reader.LocalName == "Flags" && Reader.NamespaceURI == "" && !b27) | ||
479 | { | ||
480 | b27 = true; | ||
481 | string s35 = Reader.ReadElementString(); | ||
482 | ob.@Flags = UInt32.Parse(s35, CultureInfo.InvariantCulture); | ||
483 | } | ||
484 | else if (Reader.LocalName == "GroupOwned" && Reader.NamespaceURI == "" && !b24) | ||
485 | { | ||
486 | b24 = true; | ||
487 | string s36 = Reader.ReadElementString(); | ||
488 | ob.@GroupOwned = XmlConvert.ToBoolean(s36); | ||
489 | } | ||
490 | else if (Reader.LocalName == "InvType" && Reader.NamespaceURI == "" && !b10) | ||
491 | { | ||
492 | b10 = true; | ||
493 | string s37 = Reader.ReadElementString(); | ||
494 | ob.@InvType = Int32.Parse(s37, CultureInfo.InvariantCulture); | ||
495 | } | ||
496 | else if (Reader.LocalName == "GroupID" && Reader.NamespaceURI == "" && !b23) | ||
497 | { | ||
498 | b23 = true; | ||
499 | ob.@GroupID = ReadObject_UUID(false, true); | ||
500 | } | ||
501 | else if (Reader.LocalName == "Description" && Reader.NamespaceURI == "" && !b15) | ||
502 | { | ||
503 | b15 = true; | ||
504 | string s38 = Reader.ReadElementString(); | ||
505 | ob.@Description = s38; | ||
506 | } | ||
507 | else if (Reader.LocalName == "CreationDate" && Reader.NamespaceURI == "" && !b28) | ||
508 | { | ||
509 | b28 = true; | ||
510 | string s39 = Reader.ReadElementString(); | ||
511 | ob.@CreationDate = Int32.Parse(s39, CultureInfo.InvariantCulture); | ||
512 | } | ||
513 | else if (Reader.LocalName == "EveryOnePermissions" && Reader.NamespaceURI == "" && !b19) | ||
514 | { | ||
515 | b19 = true; | ||
516 | string s40 = Reader.ReadElementString(); | ||
517 | ob.@EveryOnePermissions = UInt32.Parse(s40, CultureInfo.InvariantCulture); | ||
518 | } | ||
519 | else if (Reader.LocalName == "Creator" && Reader.NamespaceURI == "" && !b13) | ||
520 | { | ||
521 | b13 = true; | ||
522 | ob.@Creator = ReadObject_UUID(false, true); | ||
523 | } | ||
524 | else if (Reader.LocalName == "Owner" && Reader.NamespaceURI == "" && !b12) | ||
525 | { | ||
526 | b12 = true; | ||
527 | ob.@Owner = ReadObject_UUID(false, true); | ||
528 | } | ||
529 | else if (Reader.LocalName == "SaleType" && Reader.NamespaceURI == "" && !b26) | ||
530 | { | ||
531 | b26 = true; | ||
532 | string s41 = Reader.ReadElementString(); | ||
533 | ob.@SaleType = byte.Parse(s41, CultureInfo.InvariantCulture); | ||
534 | } | ||
535 | else if (Reader.LocalName == "CurrentPermissions" && Reader.NamespaceURI == "" && !b17) | ||
536 | { | ||
537 | b17 = true; | ||
538 | string s42 = Reader.ReadElementString(); | ||
539 | ob.@CurrentPermissions = UInt32.Parse(s42, CultureInfo.InvariantCulture); | ||
540 | } | ||
541 | else | ||
542 | { | ||
543 | UnknownNode(ob); | ||
544 | } | ||
545 | } | ||
546 | else | ||
547 | UnknownNode(ob); | ||
548 | |||
549 | Reader.MoveToContent(); | ||
550 | } | ||
551 | |||
552 | ReadEndElement(); | ||
553 | |||
554 | return ob; | ||
555 | } | ||
556 | |||
557 | public InventoryCollection ReadObject_InventoryCollection(bool isNullable, bool checkType) | ||
558 | { | ||
559 | InventoryCollection ob = null; | ||
560 | if (isNullable && ReadNull()) return null; | ||
561 | |||
562 | if (checkType) | ||
563 | { | ||
564 | System.Xml.XmlQualifiedName t = GetXsiType(); | ||
565 | if (t == null) | ||
566 | { } | ||
567 | else if (t.Name != "InventoryCollection" || t.Namespace != "") | ||
568 | throw CreateUnknownTypeException(t); | ||
569 | } | ||
570 | |||
571 | ob = (InventoryCollection)Activator.CreateInstance(typeof(InventoryCollection), true); | ||
572 | |||
573 | Reader.MoveToElement(); | ||
574 | |||
575 | while (Reader.MoveToNextAttribute()) | ||
576 | { | ||
577 | if (IsXmlnsAttribute(Reader.Name)) | ||
578 | { | ||
579 | } | ||
580 | else | ||
581 | { | ||
582 | UnknownNode(ob); | ||
583 | } | ||
584 | } | ||
585 | |||
586 | Reader.MoveToElement(); | ||
587 | Reader.MoveToElement(); | ||
588 | if (Reader.IsEmptyElement) | ||
589 | { | ||
590 | Reader.Skip(); | ||
591 | if (ob.@Folders == null) | ||
592 | { | ||
593 | ob.@Folders = new System.Collections.Generic.Dictionary<UUID, InventoryFolder>(); | ||
594 | } | ||
595 | if (ob.@Items == null) | ||
596 | { | ||
597 | ob.@Items = new System.Collections.Generic.Dictionary<UUID, InventoryItem>(); | ||
598 | } | ||
599 | return ob; | ||
600 | } | ||
601 | |||
602 | Reader.ReadStartElement(); | ||
603 | Reader.MoveToContent(); | ||
604 | |||
605 | bool b43 = false, b44 = false, b45 = false; | ||
606 | |||
607 | while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) | ||
608 | { | ||
609 | if (Reader.NodeType == System.Xml.XmlNodeType.Element) | ||
610 | { | ||
611 | if (Reader.LocalName == "UserID" && Reader.NamespaceURI == "" && !b45) | ||
612 | { | ||
613 | b45 = true; | ||
614 | ob.@UserID = ReadObject_UUID(false, true); | ||
615 | } | ||
616 | else if (Reader.LocalName == "Items" && Reader.NamespaceURI == "" && !b44) | ||
617 | { | ||
618 | System.Collections.Generic.Dictionary<UUID, InventoryItem> o46 = ob.@Items; | ||
619 | if (((object)o46) == null) | ||
620 | { | ||
621 | o46 = new System.Collections.Generic.Dictionary<UUID, InventoryItem>(); | ||
622 | ob.@Items = o46; | ||
623 | } | ||
624 | if (Reader.IsEmptyElement) | ||
625 | { | ||
626 | Reader.Skip(); | ||
627 | } | ||
628 | else | ||
629 | { | ||
630 | int n47 = 0; | ||
631 | Reader.ReadStartElement(); | ||
632 | Reader.MoveToContent(); | ||
633 | |||
634 | while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) | ||
635 | { | ||
636 | if (Reader.NodeType == System.Xml.XmlNodeType.Element) | ||
637 | { | ||
638 | if (Reader.LocalName == "InventoryItemBase" && Reader.NamespaceURI == "") | ||
639 | { | ||
640 | if (((object)o46) == null) | ||
641 | throw CreateReadOnlyCollectionException("System.Collections.Generic.List<InventoryItemBase>"); | ||
642 | InventoryItem item = ReadObject_InventoryItem(true, true); | ||
643 | o46.Add(item.ID, item); | ||
644 | n47++; | ||
645 | } | ||
646 | else UnknownNode(null); | ||
647 | } | ||
648 | else UnknownNode(null); | ||
649 | |||
650 | Reader.MoveToContent(); | ||
651 | } | ||
652 | ReadEndElement(); | ||
653 | } | ||
654 | b44 = true; | ||
655 | } | ||
656 | else if (Reader.LocalName == "Folders" && Reader.NamespaceURI == "" && !b43) | ||
657 | { | ||
658 | System.Collections.Generic.Dictionary<UUID, InventoryFolder> o48 = ob.@Folders; | ||
659 | if (((object)o48) == null) | ||
660 | { | ||
661 | o48 = new System.Collections.Generic.Dictionary<UUID, InventoryFolder>(); | ||
662 | ob.@Folders = o48; | ||
663 | } | ||
664 | if (Reader.IsEmptyElement) | ||
665 | { | ||
666 | Reader.Skip(); | ||
667 | } | ||
668 | else | ||
669 | { | ||
670 | int n49 = 0; | ||
671 | Reader.ReadStartElement(); | ||
672 | Reader.MoveToContent(); | ||
673 | |||
674 | while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) | ||
675 | { | ||
676 | if (Reader.NodeType == System.Xml.XmlNodeType.Element) | ||
677 | { | ||
678 | if (Reader.LocalName == "InventoryFolderBase" && Reader.NamespaceURI == "") | ||
679 | { | ||
680 | if (((object)o48) == null) | ||
681 | throw CreateReadOnlyCollectionException("System.Collections.Generic.List<InventoryFolderBase>"); | ||
682 | InventoryFolder folder = ReadObject_InventoryFolder(true, true); | ||
683 | o48.Add(folder.ID, folder); | ||
684 | n49++; | ||
685 | } | ||
686 | else UnknownNode(null); | ||
687 | } | ||
688 | else UnknownNode(null); | ||
689 | |||
690 | Reader.MoveToContent(); | ||
691 | } | ||
692 | ReadEndElement(); | ||
693 | } | ||
694 | b43 = true; | ||
695 | } | ||
696 | else | ||
697 | { | ||
698 | UnknownNode(ob); | ||
699 | } | ||
700 | } | ||
701 | else | ||
702 | UnknownNode(ob); | ||
703 | |||
704 | Reader.MoveToContent(); | ||
705 | } | ||
706 | if (ob.@Folders == null) | ||
707 | { | ||
708 | ob.@Folders = new System.Collections.Generic.Dictionary<UUID, InventoryFolder>(); | ||
709 | } | ||
710 | if (ob.@Items == null) | ||
711 | { | ||
712 | ob.@Items = new System.Collections.Generic.Dictionary<UUID, InventoryItem>(); | ||
713 | } | ||
714 | |||
715 | ReadEndElement(); | ||
716 | |||
717 | return ob; | ||
718 | } | ||
719 | |||
720 | public OpenMetaverse.UUID ReadObject_UUID(bool isNullable, bool checkType) | ||
721 | { | ||
722 | OpenMetaverse.UUID ob = (OpenMetaverse.UUID)Activator.CreateInstance(typeof(OpenMetaverse.UUID), true); | ||
723 | System.Xml.XmlQualifiedName t = GetXsiType(); | ||
724 | if (t == null) | ||
725 | { } | ||
726 | else if (t.Name != "UUID" || t.Namespace != "") | ||
727 | throw CreateUnknownTypeException(t); | ||
728 | |||
729 | Reader.MoveToElement(); | ||
730 | |||
731 | while (Reader.MoveToNextAttribute()) | ||
732 | { | ||
733 | if (IsXmlnsAttribute(Reader.Name)) | ||
734 | { | ||
735 | } | ||
736 | else | ||
737 | { | ||
738 | UnknownNode(ob); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | Reader.MoveToElement(); | ||
743 | Reader.MoveToElement(); | ||
744 | if (Reader.IsEmptyElement) | ||
745 | { | ||
746 | Reader.Skip(); | ||
747 | return ob; | ||
748 | } | ||
749 | |||
750 | Reader.ReadStartElement(); | ||
751 | Reader.MoveToContent(); | ||
752 | |||
753 | bool b52 = false; | ||
754 | |||
755 | while (Reader.NodeType != System.Xml.XmlNodeType.EndElement) | ||
756 | { | ||
757 | if (Reader.NodeType == System.Xml.XmlNodeType.Element) | ||
758 | { | ||
759 | if (Reader.LocalName == "Guid" && Reader.NamespaceURI == "" && !b52) | ||
760 | { | ||
761 | b52 = true; | ||
762 | string s53 = Reader.ReadElementString(); | ||
763 | ob.@Guid = XmlConvert.ToGuid(s53); | ||
764 | } | ||
765 | else | ||
766 | { | ||
767 | UnknownNode(ob); | ||
768 | } | ||
769 | } | ||
770 | else | ||
771 | UnknownNode(ob); | ||
772 | |||
773 | Reader.MoveToContent(); | ||
774 | } | ||
775 | |||
776 | ReadEndElement(); | ||
777 | |||
778 | return ob; | ||
779 | } | ||
780 | |||
781 | protected override void InitCallbacks() | ||
782 | { | ||
783 | } | ||
784 | |||
785 | protected override void InitIDs() | ||
786 | { | ||
787 | } | ||
788 | } | ||
789 | |||
790 | public class GeneratedWriter : XmlSerializationWriter | ||
791 | { | ||
792 | const string xmlNamespace = "http://www.w3.org/2000/xmlns/"; | ||
793 | static readonly System.Reflection.MethodInfo toBinHexStringMethod = typeof(XmlConvert).GetMethod("ToBinHexString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic, null, new Type[] { typeof(byte[]) }, null); | ||
794 | static string ToBinHexString(byte[] input) | ||
795 | { | ||
796 | return input == null ? null : (string)toBinHexStringMethod.Invoke(null, new object[] { input }); | ||
797 | } | ||
798 | public void WriteRoot_InventoryFolder(object o) | ||
799 | { | ||
800 | WriteStartDocument(); | ||
801 | InventoryFolder ob = (InventoryFolder)o; | ||
802 | TopLevelElement(); | ||
803 | WriteObject_InventoryFolder(ob, "InventoryFolderBase", "", true, false, true); | ||
804 | } | ||
805 | |||
806 | public void WriteRoot_InventoryItem(object o) | ||
807 | { | ||
808 | WriteStartDocument(); | ||
809 | InventoryItem ob = (InventoryItem)o; | ||
810 | TopLevelElement(); | ||
811 | WriteObject_InventoryItem(ob, "InventoryItemBase", "", true, false, true); | ||
812 | } | ||
813 | |||
814 | public void WriteRoot_InventoryCollection(object o) | ||
815 | { | ||
816 | WriteStartDocument(); | ||
817 | InventoryCollection ob = (InventoryCollection)o; | ||
818 | TopLevelElement(); | ||
819 | WriteObject_InventoryCollection(ob, "InventoryCollection", "", true, false, true); | ||
820 | } | ||
821 | |||
822 | void WriteObject_InventoryFolder(InventoryFolder ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) | ||
823 | { | ||
824 | if (((object)ob) == null) | ||
825 | { | ||
826 | if (isNullable) | ||
827 | WriteNullTagLiteral(element, namesp); | ||
828 | return; | ||
829 | } | ||
830 | |||
831 | System.Type type = ob.GetType(); | ||
832 | if (type == typeof(InventoryFolder)) | ||
833 | { } | ||
834 | else | ||
835 | { | ||
836 | throw CreateUnknownTypeException(ob); | ||
837 | } | ||
838 | |||
839 | if (writeWrappingElem) | ||
840 | { | ||
841 | WriteStartElement(element, namesp, ob); | ||
842 | } | ||
843 | |||
844 | if (needType) WriteXsiType("InventoryFolderBase", ""); | ||
845 | |||
846 | WriteElementString("Name", "", ob.@Name); | ||
847 | WriteObject_UUID(ob.@Owner, "Owner", "", false, false, true); | ||
848 | WriteObject_UUID(ob.@ParentID, "ParentID", "", false, false, true); | ||
849 | WriteObject_UUID(ob.@ID, "ID", "", false, false, true); | ||
850 | WriteElementString("Type", "", ob.@Type.ToString(CultureInfo.InvariantCulture)); | ||
851 | WriteElementString("Version", "", ob.@Version.ToString(CultureInfo.InvariantCulture)); | ||
852 | if (writeWrappingElem) WriteEndElement(ob); | ||
853 | } | ||
854 | |||
855 | void WriteObject_InventoryItem(InventoryItem ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) | ||
856 | { | ||
857 | if (((object)ob) == null) | ||
858 | { | ||
859 | if (isNullable) | ||
860 | WriteNullTagLiteral(element, namesp); | ||
861 | return; | ||
862 | } | ||
863 | |||
864 | System.Type type = ob.GetType(); | ||
865 | if (type == typeof(InventoryItem)) | ||
866 | { } | ||
867 | else | ||
868 | { | ||
869 | throw CreateUnknownTypeException(ob); | ||
870 | } | ||
871 | |||
872 | if (writeWrappingElem) | ||
873 | { | ||
874 | WriteStartElement(element, namesp, ob); | ||
875 | } | ||
876 | |||
877 | if (needType) WriteXsiType("InventoryItemBase", ""); | ||
878 | |||
879 | WriteObject_UUID(ob.@ID, "ID", "", false, false, true); | ||
880 | WriteElementString("InvType", "", ob.@InvType.ToString(CultureInfo.InvariantCulture)); | ||
881 | WriteObject_UUID(ob.@Folder, "Folder", "", false, false, true); | ||
882 | WriteObject_UUID(ob.@Owner, "Owner", "", false, false, true); | ||
883 | WriteObject_UUID(ob.@Creator, "Creator", "", false, false, true); | ||
884 | WriteElementString("Name", "", ob.@Name); | ||
885 | WriteElementString("Description", "", ob.@Description); | ||
886 | WriteElementString("NextPermissions", "", ob.@NextPermissions.ToString(CultureInfo.InvariantCulture)); | ||
887 | WriteElementString("CurrentPermissions", "", ob.@CurrentPermissions.ToString(CultureInfo.InvariantCulture)); | ||
888 | WriteElementString("BasePermissions", "", ob.@BasePermissions.ToString(CultureInfo.InvariantCulture)); | ||
889 | WriteElementString("EveryOnePermissions", "", ob.@EveryOnePermissions.ToString(CultureInfo.InvariantCulture)); | ||
890 | WriteElementString("GroupPermissions", "", ob.@GroupPermissions.ToString(CultureInfo.InvariantCulture)); | ||
891 | WriteElementString("AssetType", "", ob.@AssetType.ToString(CultureInfo.InvariantCulture)); | ||
892 | WriteObject_UUID(ob.@AssetID, "AssetID", "", false, false, true); | ||
893 | WriteObject_UUID(ob.@GroupID, "GroupID", "", false, false, true); | ||
894 | WriteElementString("GroupOwned", "", (ob.@GroupOwned ? "true" : "false")); | ||
895 | WriteElementString("SalePrice", "", ob.@SalePrice.ToString(CultureInfo.InvariantCulture)); | ||
896 | WriteElementString("SaleType", "", ob.@SaleType.ToString(CultureInfo.InvariantCulture)); | ||
897 | WriteElementString("Flags", "", ob.@Flags.ToString(CultureInfo.InvariantCulture)); | ||
898 | WriteElementString("CreationDate", "", ob.@CreationDate.ToString(CultureInfo.InvariantCulture)); | ||
899 | if (writeWrappingElem) WriteEndElement(ob); | ||
900 | } | ||
901 | |||
902 | void WriteObject_InventoryCollection(InventoryCollection ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) | ||
903 | { | ||
904 | if (((object)ob) == null) | ||
905 | { | ||
906 | if (isNullable) | ||
907 | WriteNullTagLiteral(element, namesp); | ||
908 | return; | ||
909 | } | ||
910 | |||
911 | System.Type type = ob.GetType(); | ||
912 | if (type == typeof(InventoryCollection)) | ||
913 | { } | ||
914 | else | ||
915 | { | ||
916 | throw CreateUnknownTypeException(ob); | ||
917 | } | ||
918 | |||
919 | if (writeWrappingElem) | ||
920 | { | ||
921 | WriteStartElement(element, namesp, ob); | ||
922 | } | ||
923 | |||
924 | if (needType) WriteXsiType("InventoryCollection", ""); | ||
925 | |||
926 | if (ob.@Folders != null) | ||
927 | { | ||
928 | WriteStartElement("Folders", "", ob.@Folders); | ||
929 | foreach (InventoryFolder folder in ob.Folders.Values) | ||
930 | { | ||
931 | WriteObject_InventoryFolder(folder, "InventoryFolderBase", "", true, false, true); | ||
932 | } | ||
933 | WriteEndElement(ob.@Folders); | ||
934 | } | ||
935 | if (ob.@Items != null) | ||
936 | { | ||
937 | WriteStartElement("Items", "", ob.@Items); | ||
938 | foreach (InventoryItem item in ob.Items.Values) | ||
939 | { | ||
940 | WriteObject_InventoryItem(item, "InventoryItemBase", "", true, false, true); | ||
941 | } | ||
942 | WriteEndElement(ob.@Items); | ||
943 | } | ||
944 | WriteObject_UUID(ob.@UserID, "UserID", "", false, false, true); | ||
945 | if (writeWrappingElem) WriteEndElement(ob); | ||
946 | } | ||
947 | |||
948 | void WriteObject_UUID(OpenMetaverse.UUID ob, string element, string namesp, bool isNullable, bool needType, bool writeWrappingElem) | ||
949 | { | ||
950 | System.Type type = ob.GetType(); | ||
951 | if (type == typeof(OpenMetaverse.UUID)) | ||
952 | { } | ||
953 | else | ||
954 | { | ||
955 | throw CreateUnknownTypeException(ob); | ||
956 | } | ||
957 | |||
958 | if (writeWrappingElem) | ||
959 | { | ||
960 | WriteStartElement(element, namesp, ob); | ||
961 | } | ||
962 | |||
963 | if (needType) WriteXsiType("UUID", ""); | ||
964 | |||
965 | WriteElementString("Guid", "", XmlConvert.ToString(ob.@Guid)); | ||
966 | if (writeWrappingElem) WriteEndElement(ob); | ||
967 | } | ||
968 | |||
969 | protected override void InitCallbacks() | ||
970 | { | ||
971 | } | ||
972 | |||
973 | } | ||
974 | |||
975 | public class BaseXmlSerializer : System.Xml.Serialization.XmlSerializer | ||
976 | { | ||
977 | protected override System.Xml.Serialization.XmlSerializationReader CreateReader() | ||
978 | { | ||
979 | return new GeneratedReader(); | ||
980 | } | ||
981 | |||
982 | protected override System.Xml.Serialization.XmlSerializationWriter CreateWriter() | ||
983 | { | ||
984 | return new GeneratedWriter(); | ||
985 | } | ||
986 | |||
987 | public override bool CanDeserialize(System.Xml.XmlReader xmlReader) | ||
988 | { | ||
989 | return true; | ||
990 | } | ||
991 | } | ||
992 | |||
993 | public sealed class InventoryFolderSerializer : BaseXmlSerializer | ||
994 | { | ||
995 | protected override void Serialize(object obj, System.Xml.Serialization.XmlSerializationWriter writer) | ||
996 | { | ||
997 | ((GeneratedWriter)writer).WriteRoot_InventoryFolder(obj); | ||
998 | } | ||
999 | |||
1000 | protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader) | ||
1001 | { | ||
1002 | return ((GeneratedReader)reader).ReadRoot_InventoryFolderBase(); | ||
1003 | } | ||
1004 | } | ||
1005 | |||
1006 | public sealed class InventoryItemSerializer : BaseXmlSerializer | ||
1007 | { | ||
1008 | protected override void Serialize(object obj, System.Xml.Serialization.XmlSerializationWriter writer) | ||
1009 | { | ||
1010 | ((GeneratedWriter)writer).WriteRoot_InventoryItem(obj); | ||
1011 | } | ||
1012 | |||
1013 | protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader) | ||
1014 | { | ||
1015 | return ((GeneratedReader)reader).ReadRoot_InventoryItemBase(); | ||
1016 | } | ||
1017 | } | ||
1018 | |||
1019 | public sealed class InventoryCollectionSerializer : BaseXmlSerializer | ||
1020 | { | ||
1021 | protected override void Serialize(object obj, System.Xml.Serialization.XmlSerializationWriter writer) | ||
1022 | { | ||
1023 | ((GeneratedWriter)writer).WriteRoot_InventoryCollection(obj); | ||
1024 | } | ||
1025 | |||
1026 | protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader) | ||
1027 | { | ||
1028 | return ((GeneratedReader)reader).ReadRoot_InventoryCollection(); | ||
1029 | } | ||
1030 | } | ||
1031 | |||
1032 | #endregion XML Serialization | ||
1033 | } | ||
1034 | } | ||
diff --git a/bin/AssetServer.ini.example b/bin/AssetServer.ini.example new file mode 100644 index 0000000..f346624 --- /dev/null +++ b/bin/AssetServer.ini.example | |||
@@ -0,0 +1,153 @@ | |||
1 | [Config] | ||
2 | |||
3 | ; The port number for the asset server to listen on. If a valid SSL certificate | ||
4 | ; file is given for SSLCertFile, the HTTPS protocol will be used. Otherwise, the | ||
5 | ; HTTP protocol is used. | ||
6 | ListenPort = 8003 | ||
7 | |||
8 | ; An SSL certificate file for the server. If a valid raw certificate or PKCS#12 | ||
9 | ; file is given the server will run in HTTPS mode. | ||
10 | ;SSLCertFile = server.p12 | ||
11 | |||
12 | [Extensions] | ||
13 | |||
14 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
15 | ; Storage Providers | ||
16 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
17 | |||
18 | ; Simple storage is a very basic storage system for the purposes of illustrating | ||
19 | ; a storage backend example. The assets are stored in SimpleAssets/ and | ||
20 | ; TempAssets/ (which is deleted when the server shuts down). Metadata is | ||
21 | ; generated for all of the files at startup and when new assets are uploaded. | ||
22 | ;SimpleStorage | ||
23 | |||
24 | ; OpenSimMySQL storage connects to a MySQL server that has an assets table created | ||
25 | ; by OpenSim. Open the AssetServer_Config.xml file from OpenSim and use the | ||
26 | ; database connection string for the database_connect option in the MySQL section | ||
27 | ; below. This backend combined with the OpenSimFrontend will allow the asset | ||
28 | ; server to be used as a drop-in replacement for OpenSim.Grid.AssetServer.exe, | ||
29 | ; while also allowing other frontends to run. | ||
30 | OpenSimMySQLStorage | ||
31 | |||
32 | ; Uses Amazon.com's Simple Storage Service (http://aws.amazon.com/s3/) to store | ||
33 | ; asset data and metadata. This backend does not handle any data requests, as the | ||
34 | ; data is stored remotely and metadata replies will contain the amazon.com URL | ||
35 | ; holding the actual asset data. Your Access Key ID and Secret Access Key must be | ||
36 | ; set in the [Amazon] section below for this backend to function. If | ||
37 | ; UseCloudFront is true and your Amazon account has CloudFront enabled, | ||
38 | ; CloudFront URLs will be returned in metadata instead of normal S3 URLs. | ||
39 | ;AmazonS3Storage | ||
40 | |||
41 | ; Uses memcached (http://www.danga.com/memcached/) as a caching layer on top of | ||
42 | ; another storage backend. If you use this, make sure you enable another storage | ||
43 | ; provider as the actual backend, and that the MemcacheStorage line appears in | ||
44 | ; this config file after the other storage provider. | ||
45 | ;MemcachedStorage | ||
46 | |||
47 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
48 | ; Inventory Providers | ||
49 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
50 | |||
51 | ; Simple inventory is a very basic inventory storage system for the purposes of | ||
52 | ; illustrating an inventory backend example. The inventory is stored in | ||
53 | ; SimpleInventory/ by creating a folder for each agent that contains all of the | ||
54 | ; inventory items and folders serialized as XML files. | ||
55 | ;SimpleInventory | ||
56 | |||
57 | ; OpenSimMySQL inventory connects to a MySQL server that has an inventory table | ||
58 | ; created by OpenSim. If the OpenSimMySQLStorage backend is also being used, the | ||
59 | ; inventory and asset tables must be stored in the same database. The | ||
60 | ; database_connect string in the MySQL section below is used to connect to the | ||
61 | ; database. This backend combined with the OpenSimInventoryFrontend will allow | ||
62 | ; the server to be used as a drop-in replacement for | ||
63 | ; OpenSim.Grid.InventoryServer.exe, while also allowing other frontends to run. | ||
64 | OpenSimMySQLInventory | ||
65 | |||
66 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
67 | ; Authentication Providers | ||
68 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
69 | |||
70 | ; OpenID provides a direct method of authenticating with the asset server. Users | ||
71 | ; can provide credentials and receive a session token directly from the asset | ||
72 | ; server. The OpenIdAuth module provides a browser-based form login and an | ||
73 | ; XML-based API, both accessible through the URL /authenticate. | ||
74 | ;OpenIdAuth | ||
75 | NullAuthentication | ||
76 | |||
77 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
78 | ; Authorization Providers | ||
79 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
80 | |||
81 | ; Authorize all is a dummy authorization module that allows all requests for | ||
82 | ; metadata, data, and asset creation. Use this extension if your primary | ||
83 | ; storage provider or front-end interface does not support authentication. | ||
84 | AuthorizeAll | ||
85 | |||
86 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
87 | ; Metrics Providers | ||
88 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
89 | |||
90 | ; NullMetrics contains empty logging functions. Use this metrics provider if | ||
91 | ; you want to disable metrics collection and reporting. | ||
92 | NullMetrics | ||
93 | |||
94 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
95 | ; Frontends | ||
96 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
97 | |||
98 | ; A simple frontend that provides three basic REST methods. /assetid/metadata | ||
99 | ; will return the metadata for an asset (currently in LLSD format, that will | ||
100 | ; change soon). /assetid/data will return the raw asset data with proper | ||
101 | ; Content-Type and Content-Disposition headers to make downloading assets in a | ||
102 | ; web browser easy. | ||
103 | ReferenceFrontend | ||
104 | |||
105 | ; A frontend that matches the existing OpenSim XML for transferring grid | ||
106 | ; assets. This will allow the asset server to function as a drop-in replacement | ||
107 | ; for OpenSim.Grid.AssetServer.exe, and can be combined with OpenSimMySQLStorage | ||
108 | ; to provide an identical replacement or any other storage backend. | ||
109 | OpenSimFrontend | ||
110 | |||
111 | ; A frontend that matches the existing OpenSim XML for handling inventory | ||
112 | ; transactions. This will allow the asset server to function as a drop-in | ||
113 | ; replacement for OpenSim.Grid.InventoryServer.exe, and can be combined with | ||
114 | ; OpenSimMySQLInventory to provide an identical replacement or any other | ||
115 | ; inventory backend. | ||
116 | OpenSimInventoryFrontend | ||
117 | |||
118 | ; An HTML interface for browsing through the asset store | ||
119 | BrowseFrontend | ||
120 | |||
121 | [MySQL] | ||
122 | |||
123 | ; Database connection string used by the OpenSim MySQL backend. If this line is | ||
124 | ; commented out or missing, the server will look for an AssetServer_Config.xml | ||
125 | ; in the current working directory. This file is generated by | ||
126 | ; OpenSim.Grid.AssetServer.exe and can be used without modification. | ||
127 | database_connect = "Server=localhost; Database=opensim; User=changeme; Password=changeme;" | ||
128 | |||
129 | [Amazon] | ||
130 | |||
131 | ; Get these values by logging in to your Amazon S3 account and going to | ||
132 | ; https://aws-portal.amazon.com/gp/aws/developer/account/index.html?ie=UTF8&action=access-key | ||
133 | AccessKeyID = xxxxxxxxxxxxxxxxxxxx | ||
134 | SecretAccessKey = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | ||
135 | |||
136 | ; The bucket, or namespace, in your Amazon S3 account for storing assets in. | ||
137 | ; Bucket names on S3 are global identifiers, and must be unique. Think up | ||
138 | ; something clever or random. | ||
139 | BucketName = changeme | ||
140 | |||
141 | ; Amazon CloudFront is a Content Distribution Network for S3 stores. If this is | ||
142 | ; set to true, AmazonS3Storage will try to locate the first available CloudFront | ||
143 | ; distribution tied to the active S3 bucket. If no usable distribution is found, | ||
144 | ; a new one will be created. | ||
145 | UseCloudFront = true | ||
146 | |||
147 | [Memcached] | ||
148 | |||
149 | ; A comma-separated list of the memcached servers that make up your caching | ||
150 | ; pool. Each server is a hostname or IP address, optionally followed by a | ||
151 | ; colon and port number if the server is not listening on the default 11211 | ||
152 | ; port. | ||
153 | Servers = localhost | ||
diff --git a/bin/OpenSim.Grid.NewAssetServer.addin.xml b/bin/OpenSim.Grid.NewAssetServer.addin.xml new file mode 100644 index 0000000..8d8a863 --- /dev/null +++ b/bin/OpenSim.Grid.NewAssetServer.addin.xml | |||
@@ -0,0 +1,21 @@ | |||
1 | <Addin id="OpenSim.Grid.NewAssetServer" isroot="true" version="0.1"> | ||
2 | <Runtime> | ||
3 | <Import assembly="OpenSim.Grid.NewAssetServer.exe"/> | ||
4 | <Import assembly="OpenSim.Framework.dll"/> | ||
5 | </Runtime> | ||
6 | <ExtensionPoint path="/OpenSim/AssetServer/StorageProvider"> | ||
7 | <ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IAssetStorageProvider"/> | ||
8 | </ExtensionPoint> | ||
9 | <ExtensionPoint path="/OpenSim/AssetServer/InventoryProvider"> | ||
10 | <ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IInventoryProvider"/> | ||
11 | </ExtensionPoint> | ||
12 | <ExtensionPoint path="/OpenSim/AssetServer/AuthenticationProvider"> | ||
13 | <ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IAuthenticationProvider"/> | ||
14 | </ExtensionPoint> | ||
15 | <ExtensionPoint path="/OpenSim/AssetServer/AuthorizationProvider"> | ||
16 | <ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IAuthorizationProvider"/> | ||
17 | </ExtensionPoint> | ||
18 | <ExtensionPoint path="/OpenSim/AssetServer/MetricsProvider"> | ||
19 | <ExtensionNode name="Plugin" type="OpenSim.Framework.PluginExtensionNode" objectType="AssetServer.IMetricsProvider"/> | ||
20 | </ExtensionPoint> | ||
21 | </Addin> | ||