aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Grid/NewAssetServer/Extensions/SimpleInventory.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Grid/NewAssetServer/Extensions/SimpleInventory.cs')
-rw-r--r--OpenSim/Grid/NewAssetServer/Extensions/SimpleInventory.cs602
1 files changed, 602 insertions, 0 deletions
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
30using System;
31using System.Collections.Generic;
32using System.Net;
33using System.IO;
34using System.Text;
35using ExtensionLoader;
36using OpenMetaverse;
37using OpenMetaverse.StructuredData;
38
39namespace 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}