aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs')
-rw-r--r--OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs625
1 files changed, 0 insertions, 625 deletions
diff --git a/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs b/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs
deleted file mode 100644
index 4010818..0000000
--- a/OpenSim/Grid/AssetInventoryServer/Plugins/Simple/SimpleInventoryStoragePlugin.cs
+++ /dev/null
@@ -1,625 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using System.Collections.Generic;
31using System.IO;
32using System.Text;
33using OpenMetaverse;
34using OpenSim.Framework;
35using log4net;
36
37namespace OpenSim.Grid.AssetInventoryServer.Plugins.Simple
38{
39 public class SimpleInventoryStoragePlugin : IInventoryStorageProvider
40 {
41 const string EXTENSION_NAME = "SimpleInventoryStorage"; // Used for metrics reporting
42 const string DEFAULT_INVENTORY_DIR = "SimpleInventory";
43
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 AssetInventoryServer server;
46 Dictionary<Uri, InventoryCollection> inventories = new Dictionary<Uri, InventoryCollection>();
47 Dictionary<Uri, List<InventoryItemBase>> activeGestures = new Dictionary<Uri, List<InventoryItemBase>>();
48 Utils.InventoryItemSerializer itemSerializer = new Utils.InventoryItemSerializer();
49 Utils.InventoryFolderSerializer folderSerializer = new Utils.InventoryFolderSerializer();
50
51 public SimpleInventoryStoragePlugin()
52 {
53 }
54
55 #region Required Interfaces
56
57 public BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItemBase item)
58 {
59 item = null;
60 BackendResponse ret;
61
62 InventoryCollection collection;
63 if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
64 ret = BackendResponse.Success;
65 else
66 ret = BackendResponse.NotFound;
67
68 server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
69 return ret;
70 }
71
72 public BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolderWithChildren folder)
73 {
74 folder = null;
75 BackendResponse ret;
76
77 InventoryCollection collection;
78 if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
79 ret = BackendResponse.Success;
80 else
81 ret = BackendResponse.NotFound;
82
83 server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
84 return ret;
85 }
86
87 public BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents)
88 {
89 contents = null;
90 BackendResponse ret;
91
92 InventoryCollection collection;
93 InventoryFolderWithChildren folder;
94
95 if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
96 {
97 contents = new InventoryCollection();
98 contents.UserID = collection.UserID;
99 contents.Folders = new Dictionary<UUID, InventoryFolderWithChildren>();
100 contents.Items = new Dictionary<UUID, InventoryItemBase>();
101
102 foreach (InventoryNodeBase invBase in folder.Children.Values)
103 {
104 if (invBase is InventoryItemBase)
105 {
106 InventoryItemBase invItem = invBase as InventoryItemBase;
107 contents.Items.Add(invItem.ID, invItem);
108 }
109 else
110 {
111 InventoryFolderWithChildren invFolder = invBase as InventoryFolderWithChildren;
112 contents.Folders.Add(invFolder.ID, invFolder);
113 }
114 }
115
116 ret = BackendResponse.Success;
117 }
118 else
119 {
120 ret = BackendResponse.NotFound;
121 }
122
123 server.MetricsProvider.LogInventoryFetchFolderContents(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
124 return ret;
125 }
126
127 public BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolderWithChildren> folders)
128 {
129 folders = null;
130 BackendResponse ret;
131
132 InventoryCollection collection;
133 if (inventories.TryGetValue(owner, out collection))
134 {
135 folders = new List<InventoryFolderWithChildren>(collection.Folders.Values);
136 return BackendResponse.Success;
137 }
138 else
139 {
140 ret = BackendResponse.NotFound;
141 }
142
143 server.MetricsProvider.LogInventoryFetchFolderList(EXTENSION_NAME, ret, owner, DateTime.Now);
144 return ret;
145 }
146
147 public BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory)
148 {
149 inventory = null;
150 BackendResponse ret;
151
152 if (inventories.TryGetValue(owner, out inventory))
153 ret = BackendResponse.Success;
154 else
155 ret = BackendResponse.NotFound;
156
157 server.MetricsProvider.LogInventoryFetchInventory(EXTENSION_NAME, ret, owner, DateTime.Now);
158 return ret;
159 }
160
161 public BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItemBase> gestures)
162 {
163 gestures = null;
164 BackendResponse ret;
165
166 if (activeGestures.TryGetValue(owner, out gestures))
167 ret = BackendResponse.Success;
168 else
169 ret = BackendResponse.NotFound;
170
171 server.MetricsProvider.LogInventoryFetchActiveGestures(EXTENSION_NAME, ret, owner, DateTime.Now);
172 return ret;
173 }
174
175 public BackendResponse TryCreateItem(Uri owner, InventoryItemBase item)
176 {
177 BackendResponse ret;
178
179 InventoryCollection collection;
180 if (inventories.TryGetValue(owner, out collection))
181 {
182 // Delete this item first if it already exists
183 InventoryItemBase oldItem;
184 if (collection.Items.TryGetValue(item.ID, out oldItem))
185 TryDeleteItem(owner, item.ID);
186
187 try
188 {
189 // Create the file
190 SaveItem(item);
191
192 // Add the item to the collection
193 lock (collection) collection.Items[item.ID] = item;
194
195 // Add the item to its parent folder
196 InventoryFolderWithChildren parent;
197 if (collection.Folders.TryGetValue(item.Folder, out parent))
198 lock (parent.Children) parent.Children.Add(item.ID, item);
199
200 // Add active gestures to our list
201 if (item.InvType == (int)InventoryType.Gesture && item.Flags == 1)
202 {
203 lock (activeGestures)
204 activeGestures[owner].Add(item);
205 }
206
207 ret = BackendResponse.Success;
208 }
209 catch (Exception ex)
210 {
211 m_log.Error("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
212 ret = BackendResponse.Failure;
213 }
214 }
215 else
216 {
217 return BackendResponse.NotFound;
218 }
219
220 server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, false, DateTime.Now);
221 return ret;
222 }
223
224 public BackendResponse TryCreateFolder(Uri owner, InventoryFolderWithChildren folder)
225 {
226 BackendResponse ret;
227
228 InventoryCollection collection;
229 if (inventories.TryGetValue(owner, out collection))
230 {
231 // Delete this folder first if it already exists
232 InventoryFolderWithChildren oldFolder;
233 if (collection.Folders.TryGetValue(folder.ID, out oldFolder))
234 TryDeleteFolder(owner, folder.ID);
235
236 try
237 {
238 // Create the file
239 SaveFolder(folder);
240
241 // Add the folder to the collection
242 lock (collection) collection.Folders[folder.ID] = folder;
243
244 // Add the folder to its parent folder
245 InventoryFolderWithChildren parent;
246 if (collection.Folders.TryGetValue(folder.ParentID, out parent))
247 lock (parent.Children) parent.Children.Add(folder.ID, folder);
248
249 ret = BackendResponse.Success;
250 }
251 catch (Exception ex)
252 {
253 m_log.Error("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
254 ret = BackendResponse.Failure;
255 }
256 }
257 else
258 {
259 ret = BackendResponse.NotFound;
260 }
261
262 server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, true, DateTime.Now);
263 return ret;
264 }
265
266 public BackendResponse TryCreateInventory(Uri owner, InventoryFolderWithChildren rootFolder)
267 {
268 BackendResponse ret;
269
270 lock (inventories)
271 {
272 if (!inventories.ContainsKey(owner))
273 {
274 InventoryCollection collection = new InventoryCollection();
275 collection.UserID = rootFolder.Owner;
276 collection.Folders = new Dictionary<UUID, InventoryFolderWithChildren>();
277 collection.Folders.Add(rootFolder.ID, rootFolder);
278 collection.Items = new Dictionary<UUID, InventoryItemBase>();
279
280 inventories.Add(owner, collection);
281
282 ret = BackendResponse.Success;
283 }
284 else
285 {
286 ret = BackendResponse.Failure;
287 }
288 }
289
290 if (ret == BackendResponse.Success)
291 {
292 string path = Path.Combine(DEFAULT_INVENTORY_DIR, rootFolder.Owner.ToString());
293 try
294 {
295 // Create the directory for this agent
296 Directory.CreateDirectory(path);
297
298 // Create an index.txt containing the UUID and URI for this agent
299 string[] index = new string[] { rootFolder.Owner.ToString(), owner.ToString() };
300 File.WriteAllLines(Path.Combine(path, "index.txt"), index);
301
302 // Create the root folder file
303 SaveFolder(rootFolder);
304 }
305 catch (Exception ex)
306 {
307 m_log.Error("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
308 ret = BackendResponse.Failure;
309 }
310 }
311
312 server.MetricsProvider.LogInventoryCreateInventory(EXTENSION_NAME, ret, DateTime.Now);
313 return ret;
314 }
315
316 public BackendResponse TryDeleteItem(Uri owner, UUID itemID)
317 {
318 BackendResponse ret;
319
320 InventoryCollection collection;
321 InventoryItemBase item;
322 if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
323 {
324 // Remove the item from its parent folder
325 InventoryFolderWithChildren parent;
326 if (collection.Folders.TryGetValue(item.Folder, out parent))
327 lock (parent.Children) parent.Children.Remove(itemID);
328
329 // Remove the item from the collection
330 lock (collection) collection.Items.Remove(itemID);
331
332 // Remove from the active gestures list if applicable
333 if (item.InvType == (int)InventoryType.Gesture)
334 {
335 lock (activeGestures)
336 {
337 for (int i = 0; i < activeGestures[owner].Count; i++)
338 {
339 if (activeGestures[owner][i].ID == itemID)
340 {
341 activeGestures[owner].RemoveAt(i);
342 break;
343 }
344 }
345 }
346 }
347
348 // Delete the file. We don't know exactly what the file name is,
349 // so search for it
350 string path = PathFromURI(owner);
351 string[] matches = Directory.GetFiles(path, String.Format("*{0}.item", itemID), SearchOption.TopDirectoryOnly);
352 foreach (string match in matches)
353 {
354 try { File.Delete(match); }
355 catch (Exception ex) { m_log.ErrorFormat("[SIMPLEINVENTORYSTORAGE]: Failed to delete file {0}: {1}", match, ex.Message); }
356 }
357
358 ret = BackendResponse.Success;
359 }
360 else
361 {
362 ret = BackendResponse.NotFound;
363 }
364
365 server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
366 return ret;
367 }
368
369 public BackendResponse TryDeleteFolder(Uri owner, UUID folderID)
370 {
371 BackendResponse ret;
372
373 InventoryCollection collection;
374 InventoryFolderWithChildren folder;
375 if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
376 {
377 // Remove the folder from its parent folder
378 InventoryFolderWithChildren parent;
379 if (collection.Folders.TryGetValue(folder.ParentID, out parent))
380 lock (parent.Children) parent.Children.Remove(folderID);
381
382 // Remove the folder from the collection
383 lock (collection) collection.Items.Remove(folderID);
384
385 // Delete the folder file. We don't know exactly what the file name is,
386 // so search for it
387 string path = PathFromURI(owner);
388 string[] matches = Directory.GetFiles(path, String.Format("*{0}.folder", folderID), SearchOption.TopDirectoryOnly);
389 foreach (string match in matches)
390 {
391 try { File.Delete(match); }
392 catch (Exception ex) { m_log.ErrorFormat("[SIMPLEINVENTORYSTORAGE]: Failed to delete folder file {0}: {1}", match, ex.Message); }
393 }
394
395 ret = BackendResponse.Success;
396 }
397 else
398 {
399 ret = BackendResponse.NotFound;
400 }
401
402 server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
403 return ret;
404 }
405
406 public BackendResponse TryPurgeFolder(Uri owner, UUID folderID)
407 {
408 BackendResponse ret;
409
410 InventoryCollection collection;
411 InventoryFolderWithChildren folder;
412 if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
413 {
414 // Delete all of the folder children
415 foreach (InventoryNodeBase obj in new List<InventoryNodeBase>(folder.Children.Values))
416 {
417 if (obj is InventoryItemBase)
418 {
419 TryDeleteItem(owner, (obj as InventoryItemBase).ID);
420 }
421 else
422 {
423 InventoryFolderWithChildren childFolder = obj as InventoryFolderWithChildren;
424 TryPurgeFolder(owner, childFolder.ID);
425 TryDeleteFolder(owner, childFolder.ID);
426 }
427 }
428
429 ret = BackendResponse.Success;
430 }
431 else
432 {
433 ret = BackendResponse.NotFound;
434 }
435
436 server.MetricsProvider.LogInventoryPurgeFolder(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
437 return ret;
438 }
439
440 #endregion Required Interfaces
441
442 void SaveItem(InventoryItemBase item)
443 {
444 string filename = String.Format("{0}-{1}.item", SanitizeFilename(item.Name), item.ID);
445
446 string path = Path.Combine(DEFAULT_INVENTORY_DIR, item.Owner.ToString());
447 path = Path.Combine(path, filename);
448
449 using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
450 {
451 itemSerializer.Serialize(stream, item);
452 stream.Flush();
453 }
454 }
455
456 void SaveFolder(InventoryFolderWithChildren folder)
457 {
458 string filename = String.Format("{0}-{1}.folder", SanitizeFilename(folder.Name), folder.ID);
459
460 string path = Path.Combine(DEFAULT_INVENTORY_DIR, folder.Owner.ToString());
461 path = Path.Combine(path, filename);
462
463 using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
464 {
465 folderSerializer.Serialize(stream, folder);
466 stream.Flush();
467 }
468 }
469
470 string SanitizeFilename(string filename)
471 {
472 string output = filename;
473
474 if (output.Length > 64)
475 output = output.Substring(0, 64);
476
477 foreach (char i in Path.GetInvalidFileNameChars())
478 output = output.Replace(i, '_');
479
480 return output;
481 }
482
483 static string PathFromURI(Uri uri)
484 {
485 byte[] hash = OpenMetaverse.Utils.SHA1(Encoding.UTF8.GetBytes(uri.ToString()));
486 StringBuilder digest = new StringBuilder(40);
487
488 // Convert the hash to a hex string
489 foreach (byte b in hash)
490 digest.AppendFormat(OpenMetaverse.Utils.EnUsCulture, "{0:x2}", b);
491
492 return Path.Combine(DEFAULT_INVENTORY_DIR, digest.ToString());
493 }
494
495 void LoadFiles(string folder)
496 {
497 // Try to create the directory if it doesn't already exist
498 if (!Directory.Exists(folder))
499 {
500 try { Directory.CreateDirectory(folder); }
501 catch (Exception ex)
502 {
503 m_log.Warn("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
504 return;
505 }
506 }
507
508 try
509 {
510 string[] agentFolders = Directory.GetDirectories(DEFAULT_INVENTORY_DIR);
511
512 for (int i = 0; i < agentFolders.Length; i++)
513 {
514 string foldername = agentFolders[i];
515 string indexPath = Path.Combine(foldername, "index.txt");
516 UUID ownerID = UUID.Zero;
517 Uri owner = null;
518
519 try
520 {
521 string[] index = File.ReadAllLines(indexPath);
522 ownerID = UUID.Parse(index[0]);
523 owner = new Uri(index[1]);
524 }
525 catch (Exception ex)
526 {
527 m_log.WarnFormat("[SIMPLEINVENTORYSTORAGE]: Failed loading the index file {0}: {1}", indexPath, ex.Message);
528 }
529
530 if (ownerID != UUID.Zero && owner != null)
531 {
532 // Initialize the active gestures list for this agent
533 activeGestures.Add(owner, new List<InventoryItemBase>());
534
535 InventoryCollection collection = new InventoryCollection();
536 collection.UserID = ownerID;
537
538 // Load all of the folders for this agent
539 string[] folders = Directory.GetFiles(foldername, "*.folder", SearchOption.TopDirectoryOnly);
540 collection.Folders = new Dictionary<UUID,InventoryFolderWithChildren>(folders.Length);
541
542 for (int j = 0; j < folders.Length; j++)
543 {
544 InventoryFolderWithChildren invFolder = (InventoryFolderWithChildren)folderSerializer.Deserialize(
545 new FileStream(folders[j], FileMode.Open, FileAccess.Read));
546 collection.Folders[invFolder.ID] = invFolder;
547 }
548
549 // Iterate over the folders collection, adding children to their parents
550 foreach (InventoryFolderWithChildren invFolder in collection.Folders.Values)
551 {
552 InventoryFolderWithChildren parent;
553 if (collection.Folders.TryGetValue(invFolder.ParentID, out parent))
554 parent.Children[invFolder.ID] = invFolder;
555 }
556
557 // Load all of the items for this agent
558 string[] files = Directory.GetFiles(foldername, "*.item", SearchOption.TopDirectoryOnly);
559 collection.Items = new Dictionary<UUID, InventoryItemBase>(files.Length);
560
561 for (int j = 0; j < files.Length; j++)
562 {
563 InventoryItemBase invItem = (InventoryItemBase)itemSerializer.Deserialize(
564 new FileStream(files[j], FileMode.Open, FileAccess.Read));
565 collection.Items[invItem.ID] = invItem;
566
567 // Add items to their parent folders
568 InventoryFolderWithChildren parent;
569 if (collection.Folders.TryGetValue(invItem.Folder, out parent))
570 parent.Children[invItem.ID] = invItem;
571
572 // Add active gestures to our list
573 if (invItem.InvType == (int)InventoryType.Gesture && invItem.Flags != 0)
574 activeGestures[owner].Add(invItem);
575 }
576
577 inventories.Add(owner, collection);
578 }
579 }
580 }
581 catch (Exception ex)
582 {
583 m_log.ErrorFormat("[SIMPLEINVENTORYSTORAGE]: Failed loading inventory from {0}: {1}", folder, ex.Message);
584 }
585 }
586
587 #region IPlugin implementation
588
589 public void Initialise(AssetInventoryServer server)
590 {
591 this.server = server;
592
593 LoadFiles(DEFAULT_INVENTORY_DIR);
594
595 m_log.InfoFormat("[SIMPLEINVENTORYSTORAGE]: Initialized the inventory index with data for {0} avatars",
596 inventories.Count);
597 }
598
599 /// <summary>
600 /// <para>Initialises asset interface</para>
601 /// </summary>
602 public void Initialise()
603 {
604 m_log.InfoFormat("[SIMPLEINVENTORYSTORAGE]: {0} cannot be default-initialized!", Name);
605 throw new PluginNotInitialisedException(Name);
606 }
607
608 public void Dispose()
609 {
610 }
611
612 public string Version
613 {
614 // TODO: this should be something meaningful and not hardcoded?
615 get { return "0.1"; }
616 }
617
618 public string Name
619 {
620 get { return "SimpleInventoryStorage"; }
621 }
622
623 #endregion IPlugin implementation
624 }
625}