diff options
author | Mike Mazur | 2009-02-16 02:24:57 +0000 |
---|---|---|
committer | Mike Mazur | 2009-02-16 02:24:57 +0000 |
commit | 0e09b4a08beddb3b0239a3f088ab9a230b8f3979 (patch) | |
tree | 481d1cb9a60062dea6e646e0e6c5d9d87ce40b6e /OpenSim/Grid/NewAssetServer/Extensions | |
parent | Thank you, cmickeyb, for a patch to ass two string functions (diff) | |
download | opensim-SC_OLD-0e09b4a08beddb3b0239a3f088ab9a230b8f3979.zip opensim-SC_OLD-0e09b4a08beddb3b0239a3f088ab9a230b8f3979.tar.gz opensim-SC_OLD-0e09b4a08beddb3b0239a3f088ab9a230b8f3979.tar.bz2 opensim-SC_OLD-0e09b4a08beddb3b0239a3f088ab9a230b8f3979.tar.xz |
Adding
- NewAssetServer code
- NewAssetServer addin manifest
- example AssetServer.ini file
Diffstat (limited to 'OpenSim/Grid/NewAssetServer/Extensions')
13 files changed, 3584 insertions, 0 deletions
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 | } | ||