123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625 |
- /*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- using System;
- using System.Reflection;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using OpenMetaverse;
- using OpenSim.Framework;
- using log4net;
- namespace OpenSim.Grid.AssetInventoryServer.Plugins.Simple
- {
- public class SimpleInventoryStoragePlugin : IInventoryStorageProvider
- {
- const string EXTENSION_NAME = "SimpleInventoryStorage"; // Used for metrics reporting
- const string DEFAULT_INVENTORY_DIR = "SimpleInventory";
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- AssetInventoryServer server;
- Dictionary<Uri, InventoryCollection> inventories = new Dictionary<Uri, InventoryCollection>();
- Dictionary<Uri, List<InventoryItemBase>> activeGestures = new Dictionary<Uri, List<InventoryItemBase>>();
- Utils.InventoryItemSerializer itemSerializer = new Utils.InventoryItemSerializer();
- Utils.InventoryFolderSerializer folderSerializer = new Utils.InventoryFolderSerializer();
- public SimpleInventoryStoragePlugin()
- {
- }
- #region Required Interfaces
- public BackendResponse TryFetchItem(Uri owner, UUID itemID, out InventoryItemBase item)
- {
- item = null;
- BackendResponse ret;
- InventoryCollection collection;
- if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
- ret = BackendResponse.Success;
- else
- ret = BackendResponse.NotFound;
- server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
- return ret;
- }
- public BackendResponse TryFetchFolder(Uri owner, UUID folderID, out InventoryFolderWithChildren folder)
- {
- folder = null;
- BackendResponse ret;
- InventoryCollection collection;
- if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
- ret = BackendResponse.Success;
- else
- ret = BackendResponse.NotFound;
- server.MetricsProvider.LogInventoryFetch(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
- return ret;
- }
- public BackendResponse TryFetchFolderContents(Uri owner, UUID folderID, out InventoryCollection contents)
- {
- contents = null;
- BackendResponse ret;
- InventoryCollection collection;
- InventoryFolderWithChildren folder;
- if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
- {
- contents = new InventoryCollection();
- contents.UserID = collection.UserID;
- contents.Folders = new Dictionary<UUID, InventoryFolderWithChildren>();
- contents.Items = new Dictionary<UUID, InventoryItemBase>();
- foreach (InventoryNodeBase invBase in folder.Children.Values)
- {
- if (invBase is InventoryItemBase)
- {
- InventoryItemBase invItem = invBase as InventoryItemBase;
- contents.Items.Add(invItem.ID, invItem);
- }
- else
- {
- InventoryFolderWithChildren invFolder = invBase as InventoryFolderWithChildren;
- contents.Folders.Add(invFolder.ID, invFolder);
- }
- }
- ret = BackendResponse.Success;
- }
- else
- {
- ret = BackendResponse.NotFound;
- }
- server.MetricsProvider.LogInventoryFetchFolderContents(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
- return ret;
- }
- public BackendResponse TryFetchFolderList(Uri owner, out List<InventoryFolderWithChildren> folders)
- {
- folders = null;
- BackendResponse ret;
- InventoryCollection collection;
- if (inventories.TryGetValue(owner, out collection))
- {
- folders = new List<InventoryFolderWithChildren>(collection.Folders.Values);
- return BackendResponse.Success;
- }
- else
- {
- ret = BackendResponse.NotFound;
- }
- server.MetricsProvider.LogInventoryFetchFolderList(EXTENSION_NAME, ret, owner, DateTime.Now);
- return ret;
- }
- public BackendResponse TryFetchInventory(Uri owner, out InventoryCollection inventory)
- {
- inventory = null;
- BackendResponse ret;
- if (inventories.TryGetValue(owner, out inventory))
- ret = BackendResponse.Success;
- else
- ret = BackendResponse.NotFound;
- server.MetricsProvider.LogInventoryFetchInventory(EXTENSION_NAME, ret, owner, DateTime.Now);
- return ret;
- }
- public BackendResponse TryFetchActiveGestures(Uri owner, out List<InventoryItemBase> gestures)
- {
- gestures = null;
- BackendResponse ret;
- if (activeGestures.TryGetValue(owner, out gestures))
- ret = BackendResponse.Success;
- else
- ret = BackendResponse.NotFound;
- server.MetricsProvider.LogInventoryFetchActiveGestures(EXTENSION_NAME, ret, owner, DateTime.Now);
- return ret;
- }
- public BackendResponse TryCreateItem(Uri owner, InventoryItemBase item)
- {
- BackendResponse ret;
- InventoryCollection collection;
- if (inventories.TryGetValue(owner, out collection))
- {
- // Delete this item first if it already exists
- InventoryItemBase oldItem;
- if (collection.Items.TryGetValue(item.ID, out oldItem))
- TryDeleteItem(owner, item.ID);
- try
- {
- // Create the file
- SaveItem(item);
- // Add the item to the collection
- lock (collection) collection.Items[item.ID] = item;
- // Add the item to its parent folder
- InventoryFolderWithChildren parent;
- if (collection.Folders.TryGetValue(item.Folder, out parent))
- lock (parent.Children) parent.Children.Add(item.ID, item);
- // Add active gestures to our list
- if (item.InvType == (int)InventoryType.Gesture && item.Flags == 1)
- {
- lock (activeGestures)
- activeGestures[owner].Add(item);
- }
- ret = BackendResponse.Success;
- }
- catch (Exception ex)
- {
- m_log.Error("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
- ret = BackendResponse.Failure;
- }
- }
- else
- {
- return BackendResponse.NotFound;
- }
- server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, false, DateTime.Now);
- return ret;
- }
- public BackendResponse TryCreateFolder(Uri owner, InventoryFolderWithChildren folder)
- {
- BackendResponse ret;
- InventoryCollection collection;
- if (inventories.TryGetValue(owner, out collection))
- {
- // Delete this folder first if it already exists
- InventoryFolderWithChildren oldFolder;
- if (collection.Folders.TryGetValue(folder.ID, out oldFolder))
- TryDeleteFolder(owner, folder.ID);
- try
- {
- // Create the file
- SaveFolder(folder);
- // Add the folder to the collection
- lock (collection) collection.Folders[folder.ID] = folder;
- // Add the folder to its parent folder
- InventoryFolderWithChildren parent;
- if (collection.Folders.TryGetValue(folder.ParentID, out parent))
- lock (parent.Children) parent.Children.Add(folder.ID, folder);
- ret = BackendResponse.Success;
- }
- catch (Exception ex)
- {
- m_log.Error("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
- ret = BackendResponse.Failure;
- }
- }
- else
- {
- ret = BackendResponse.NotFound;
- }
- server.MetricsProvider.LogInventoryCreate(EXTENSION_NAME, ret, owner, true, DateTime.Now);
- return ret;
- }
- public BackendResponse TryCreateInventory(Uri owner, InventoryFolderWithChildren rootFolder)
- {
- BackendResponse ret;
- lock (inventories)
- {
- if (!inventories.ContainsKey(owner))
- {
- InventoryCollection collection = new InventoryCollection();
- collection.UserID = rootFolder.Owner;
- collection.Folders = new Dictionary<UUID, InventoryFolderWithChildren>();
- collection.Folders.Add(rootFolder.ID, rootFolder);
- collection.Items = new Dictionary<UUID, InventoryItemBase>();
- inventories.Add(owner, collection);
- ret = BackendResponse.Success;
- }
- else
- {
- ret = BackendResponse.Failure;
- }
- }
- if (ret == BackendResponse.Success)
- {
- string path = Path.Combine(DEFAULT_INVENTORY_DIR, rootFolder.Owner.ToString());
- try
- {
- // Create the directory for this agent
- Directory.CreateDirectory(path);
- // Create an index.txt containing the UUID and URI for this agent
- string[] index = new string[] { rootFolder.Owner.ToString(), owner.ToString() };
- File.WriteAllLines(Path.Combine(path, "index.txt"), index);
- // Create the root folder file
- SaveFolder(rootFolder);
- }
- catch (Exception ex)
- {
- m_log.Error("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
- ret = BackendResponse.Failure;
- }
- }
- server.MetricsProvider.LogInventoryCreateInventory(EXTENSION_NAME, ret, DateTime.Now);
- return ret;
- }
- public BackendResponse TryDeleteItem(Uri owner, UUID itemID)
- {
- BackendResponse ret;
- InventoryCollection collection;
- InventoryItemBase item;
- if (inventories.TryGetValue(owner, out collection) && collection.Items.TryGetValue(itemID, out item))
- {
- // Remove the item from its parent folder
- InventoryFolderWithChildren parent;
- if (collection.Folders.TryGetValue(item.Folder, out parent))
- lock (parent.Children) parent.Children.Remove(itemID);
- // Remove the item from the collection
- lock (collection) collection.Items.Remove(itemID);
- // Remove from the active gestures list if applicable
- if (item.InvType == (int)InventoryType.Gesture)
- {
- lock (activeGestures)
- {
- for (int i = 0; i < activeGestures[owner].Count; i++)
- {
- if (activeGestures[owner][i].ID == itemID)
- {
- activeGestures[owner].RemoveAt(i);
- break;
- }
- }
- }
- }
- // Delete the file. We don't know exactly what the file name is,
- // so search for it
- string path = PathFromURI(owner);
- string[] matches = Directory.GetFiles(path, String.Format("*{0}.item", itemID), SearchOption.TopDirectoryOnly);
- foreach (string match in matches)
- {
- try { File.Delete(match); }
- catch (Exception ex) { m_log.ErrorFormat("[SIMPLEINVENTORYSTORAGE]: Failed to delete file {0}: {1}", match, ex.Message); }
- }
- ret = BackendResponse.Success;
- }
- else
- {
- ret = BackendResponse.NotFound;
- }
- server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, itemID, false, DateTime.Now);
- return ret;
- }
- public BackendResponse TryDeleteFolder(Uri owner, UUID folderID)
- {
- BackendResponse ret;
- InventoryCollection collection;
- InventoryFolderWithChildren folder;
- if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
- {
- // Remove the folder from its parent folder
- InventoryFolderWithChildren parent;
- if (collection.Folders.TryGetValue(folder.ParentID, out parent))
- lock (parent.Children) parent.Children.Remove(folderID);
- // Remove the folder from the collection
- lock (collection) collection.Items.Remove(folderID);
- // Delete the folder file. We don't know exactly what the file name is,
- // so search for it
- string path = PathFromURI(owner);
- string[] matches = Directory.GetFiles(path, String.Format("*{0}.folder", folderID), SearchOption.TopDirectoryOnly);
- foreach (string match in matches)
- {
- try { File.Delete(match); }
- catch (Exception ex) { m_log.ErrorFormat("[SIMPLEINVENTORYSTORAGE]: Failed to delete folder file {0}: {1}", match, ex.Message); }
- }
- ret = BackendResponse.Success;
- }
- else
- {
- ret = BackendResponse.NotFound;
- }
- server.MetricsProvider.LogInventoryDelete(EXTENSION_NAME, ret, owner, folderID, true, DateTime.Now);
- return ret;
- }
- public BackendResponse TryPurgeFolder(Uri owner, UUID folderID)
- {
- BackendResponse ret;
- InventoryCollection collection;
- InventoryFolderWithChildren folder;
- if (inventories.TryGetValue(owner, out collection) && collection.Folders.TryGetValue(folderID, out folder))
- {
- // Delete all of the folder children
- foreach (InventoryNodeBase obj in new List<InventoryNodeBase>(folder.Children.Values))
- {
- if (obj is InventoryItemBase)
- {
- TryDeleteItem(owner, (obj as InventoryItemBase).ID);
- }
- else
- {
- InventoryFolderWithChildren childFolder = obj as InventoryFolderWithChildren;
- TryPurgeFolder(owner, childFolder.ID);
- TryDeleteFolder(owner, childFolder.ID);
- }
- }
- ret = BackendResponse.Success;
- }
- else
- {
- ret = BackendResponse.NotFound;
- }
- server.MetricsProvider.LogInventoryPurgeFolder(EXTENSION_NAME, ret, owner, folderID, DateTime.Now);
- return ret;
- }
- #endregion Required Interfaces
- void SaveItem(InventoryItemBase item)
- {
- string filename = String.Format("{0}-{1}.item", SanitizeFilename(item.Name), item.ID);
- string path = Path.Combine(DEFAULT_INVENTORY_DIR, item.Owner.ToString());
- path = Path.Combine(path, filename);
- using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
- {
- itemSerializer.Serialize(stream, item);
- stream.Flush();
- }
- }
- void SaveFolder(InventoryFolderWithChildren folder)
- {
- string filename = String.Format("{0}-{1}.folder", SanitizeFilename(folder.Name), folder.ID);
- string path = Path.Combine(DEFAULT_INVENTORY_DIR, folder.Owner.ToString());
- path = Path.Combine(path, filename);
- using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
- {
- folderSerializer.Serialize(stream, folder);
- stream.Flush();
- }
- }
- string SanitizeFilename(string filename)
- {
- string output = filename;
- if (output.Length > 64)
- output = output.Substring(0, 64);
- foreach (char i in Path.GetInvalidFileNameChars())
- output = output.Replace(i, '_');
- return output;
- }
- static string PathFromURI(Uri uri)
- {
- byte[] hash = OpenMetaverse.Utils.SHA1(Encoding.UTF8.GetBytes(uri.ToString()));
- StringBuilder digest = new StringBuilder(40);
- // Convert the hash to a hex string
- foreach (byte b in hash)
- digest.AppendFormat(OpenMetaverse.Utils.EnUsCulture, "{0:x2}", b);
- return Path.Combine(DEFAULT_INVENTORY_DIR, digest.ToString());
- }
- void LoadFiles(string folder)
- {
- // Try to create the directory if it doesn't already exist
- if (!Directory.Exists(folder))
- {
- try { Directory.CreateDirectory(folder); }
- catch (Exception ex)
- {
- m_log.Warn("[SIMPLEINVENTORYSTORAGE]: " + ex.Message);
- return;
- }
- }
- try
- {
- string[] agentFolders = Directory.GetDirectories(DEFAULT_INVENTORY_DIR);
- for (int i = 0; i < agentFolders.Length; i++)
- {
- string foldername = agentFolders[i];
- string indexPath = Path.Combine(foldername, "index.txt");
- UUID ownerID = UUID.Zero;
- Uri owner = null;
- try
- {
- string[] index = File.ReadAllLines(indexPath);
- ownerID = UUID.Parse(index[0]);
- owner = new Uri(index[1]);
- }
- catch (Exception ex)
- {
- m_log.WarnFormat("[SIMPLEINVENTORYSTORAGE]: Failed loading the index file {0}: {1}", indexPath, ex.Message);
- }
- if (ownerID != UUID.Zero && owner != null)
- {
- // Initialize the active gestures list for this agent
- activeGestures.Add(owner, new List<InventoryItemBase>());
- InventoryCollection collection = new InventoryCollection();
- collection.UserID = ownerID;
- // Load all of the folders for this agent
- string[] folders = Directory.GetFiles(foldername, "*.folder", SearchOption.TopDirectoryOnly);
- collection.Folders = new Dictionary<UUID,InventoryFolderWithChildren>(folders.Length);
- for (int j = 0; j < folders.Length; j++)
- {
- InventoryFolderWithChildren invFolder = (InventoryFolderWithChildren)folderSerializer.Deserialize(
- new FileStream(folders[j], FileMode.Open, FileAccess.Read));
- collection.Folders[invFolder.ID] = invFolder;
- }
- // Iterate over the folders collection, adding children to their parents
- foreach (InventoryFolderWithChildren invFolder in collection.Folders.Values)
- {
- InventoryFolderWithChildren parent;
- if (collection.Folders.TryGetValue(invFolder.ParentID, out parent))
- parent.Children[invFolder.ID] = invFolder;
- }
- // Load all of the items for this agent
- string[] files = Directory.GetFiles(foldername, "*.item", SearchOption.TopDirectoryOnly);
- collection.Items = new Dictionary<UUID, InventoryItemBase>(files.Length);
- for (int j = 0; j < files.Length; j++)
- {
- InventoryItemBase invItem = (InventoryItemBase)itemSerializer.Deserialize(
- new FileStream(files[j], FileMode.Open, FileAccess.Read));
- collection.Items[invItem.ID] = invItem;
- // Add items to their parent folders
- InventoryFolderWithChildren parent;
- if (collection.Folders.TryGetValue(invItem.Folder, out parent))
- parent.Children[invItem.ID] = invItem;
- // Add active gestures to our list
- if (invItem.InvType == (int)InventoryType.Gesture && invItem.Flags != 0)
- activeGestures[owner].Add(invItem);
- }
- inventories.Add(owner, collection);
- }
- }
- }
- catch (Exception ex)
- {
- m_log.ErrorFormat("[SIMPLEINVENTORYSTORAGE]: Failed loading inventory from {0}: {1}", folder, ex.Message);
- }
- }
- #region IPlugin implementation
- public void Initialise(AssetInventoryServer server)
- {
- this.server = server;
- LoadFiles(DEFAULT_INVENTORY_DIR);
- m_log.InfoFormat("[SIMPLEINVENTORYSTORAGE]: Initialized the inventory index with data for {0} avatars",
- inventories.Count);
- }
- /// <summary>
- /// <para>Initialises asset interface</para>
- /// </summary>
- public void Initialise()
- {
- m_log.InfoFormat("[SIMPLEINVENTORYSTORAGE]: {0} cannot be default-initialized!", Name);
- throw new PluginNotInitialisedException(Name);
- }
- public void Dispose()
- {
- }
- public string Version
- {
- // TODO: this should be something meaningful and not hardcoded?
- get { return "0.1"; }
- }
- public string Name
- {
- get { return "SimpleInventoryStorage"; }
- }
- #endregion IPlugin implementation
- }
- }
|