/* * 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.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Reflection; using log4net; using OpenMetaverse; using OpenSim.Framework; namespace OpenSim.Data.MSSQL { /// /// A MSSQL interface for the inventory server /// public class MSSQLInventoryData : IInventoryDataPlugin { private const string _migrationStore = "InventoryStore"; private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// /// The database manager /// private MSSQLManager database; #region IPlugin members [Obsolete("Cannot be default-initialized!")] public void Initialise() { m_log.Info("[MSSQLInventoryData]: " + Name + " cannot be default-initialized!"); throw new PluginNotInitialisedException(Name); } /// /// Loads and initialises the MSSQL inventory storage interface /// /// connect string /// use mssql_connection.ini public void Initialise(string connectionString) { if (!string.IsNullOrEmpty(connectionString)) { database = new MSSQLManager(connectionString); } else { IniFile gridDataMSSqlFile = new IniFile("mssql_connection.ini"); string settingDataSource = gridDataMSSqlFile.ParseFileReadValue("data_source"); string settingInitialCatalog = gridDataMSSqlFile.ParseFileReadValue("initial_catalog"); string settingPersistSecurityInfo = gridDataMSSqlFile.ParseFileReadValue("persist_security_info"); string settingUserId = gridDataMSSqlFile.ParseFileReadValue("user_id"); string settingPassword = gridDataMSSqlFile.ParseFileReadValue("password"); database = new MSSQLManager(settingDataSource, settingInitialCatalog, settingPersistSecurityInfo, settingUserId, settingPassword); } //New migrations check of store database.CheckMigration(_migrationStore); } /// /// The name of this DB provider /// /// A string containing the name of the DB provider public string Name { get { return "MSSQL Inventory Data Interface"; } } /// /// Closes this DB provider /// public void Dispose() { database = null; } /// /// Returns the version of this DB provider /// /// A string containing the DB provider public string Version { get { return database.getVersion(); } } #endregion #region Folder methods /// /// Returns a list of the root folders within a users inventory /// /// The user whos inventory is to be searched /// A list of folder objects public List getUserRootFolders(UUID user) { return getInventoryFolders(UUID.Zero, user); } /// /// see InventoryItemBase.getUserRootFolder /// /// the User UUID /// public InventoryFolderBase getUserRootFolder(UUID user) { List items = getUserRootFolders(user); InventoryFolderBase rootFolder = null; // There should only ever be one root folder for a user. However, if there's more // than one we'll simply use the first one rather than failing. It would be even // nicer to print some message to this effect, but this feels like it's too low a // to put such a message out, and it's too minor right now to spare the time to // suitably refactor. if (items.Count > 0) { rootFolder = items[0]; } return rootFolder; } /// /// Returns a list of folders in a users inventory contained within the specified folder /// /// The folder to search /// A list of inventory folders public List getInventoryFolders(UUID parentID) { return getInventoryFolders(parentID, UUID.Zero); } /// /// Returns a specified inventory folder /// /// The folder to return /// A folder class public InventoryFolderBase getInventoryFolder(UUID folderID) { using (AutoClosingSqlCommand command = database.Query("SELECT * FROM inventoryfolders WHERE folderID = @folderID")) { command.Parameters.Add(database.CreateParameter("folderID", folderID)); using (IDataReader reader = command.ExecuteReader()) { if (reader.Read()) { return readInventoryFolder(reader); } } } m_log.InfoFormat("[INVENTORY DB] : Found no inventory folder with ID : {0}", folderID); return null; } /// /// Returns all child folders in the hierarchy from the parent folder and down. /// Does not return the parent folder itself. /// /// The folder to get subfolders for /// A list of inventory folders public List getFolderHierarchy(UUID parentID) { //Note maybe change this to use a Dataset that loading in all folders of a user and then go throw it that way. //Note this is changed so it opens only one connection to the database and not everytime it wants to get data. List folders = new List(); using (AutoClosingSqlCommand command = database.Query("SELECT * FROM inventoryfolders WHERE parentFolderID = @parentID")) { command.Parameters.Add(database.CreateParameter("@parentID", parentID)); folders.AddRange(getInventoryFolders(command)); List tempFolders = new List(); foreach (InventoryFolderBase folderBase in folders) { tempFolders.AddRange(getFolderHierarchy(folderBase.ID, command)); } if (tempFolders.Count > 0) { folders.AddRange(tempFolders); } } return folders; } /// /// Creates a new inventory folder /// /// Folder to create public void addInventoryFolder(InventoryFolderBase folder) { string sql = @"INSERT INTO inventoryfolders ([folderID], [agentID], [parentFolderID], [folderName], [type], [version]) VALUES (@folderID, @agentID, @parentFolderID, @folderName, @type, @version);"; using (AutoClosingSqlCommand command = database.Query(sql)) { command.Parameters.Add(database.CreateParameter("folderID", folder.ID)); command.Parameters.Add(database.CreateParameter("agentID", folder.Owner)); command.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); command.Parameters.Add(database.CreateParameter("folderName", folder.Name)); command.Parameters.Add(database.CreateParameter("type", folder.Type)); command.Parameters.Add(database.CreateParameter("version", folder.Version)); try { //IDbCommand result = database.Query(sql, param); command.ExecuteNonQuery(); } catch (Exception e) { m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); } } } /// /// Updates an inventory folder /// /// Folder to update public void updateInventoryFolder(InventoryFolderBase folder) { string sql = @"UPDATE inventoryfolders SET folderID = @folderID, agentID = @agentID, parentFolderID = @parentFolderID, folderName = @folderName, type = @type, version = @version WHERE folderID = @keyFolderID"; using (AutoClosingSqlCommand command = database.Query(sql)) { command.Parameters.Add(database.CreateParameter("folderID", folder.ID)); command.Parameters.Add(database.CreateParameter("agentID", folder.Owner)); command.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); command.Parameters.Add(database.CreateParameter("folderName", folder.Name)); command.Parameters.Add(database.CreateParameter("type", folder.Type)); command.Parameters.Add(database.CreateParameter("version", folder.Version)); command.Parameters.Add(database.CreateParameter("@keyFolderID", folder.ID)); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); } } } /// /// Updates an inventory folder /// /// Folder to update public void moveInventoryFolder(InventoryFolderBase folder) { string sql = @"UPDATE inventoryfolders SET parentFolderID = @parentFolderID WHERE folderID = @folderID"; using (IDbCommand command = database.Query(sql)) { command.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); command.Parameters.Add(database.CreateParameter("@folderID", folder.ID)); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); } } } /// /// Delete an inventory folder /// /// Id of folder to delete public void deleteInventoryFolder(UUID folderID) { using (SqlConnection connection = database.DatabaseConnection()) { List subFolders; using (SqlCommand command = new SqlCommand("SELECT * FROM inventoryfolders WHERE parentFolderID = @parentID", connection)) { command.Parameters.Add(database.CreateParameter("@parentID", UUID.Zero)); AutoClosingSqlCommand autoCommand = new AutoClosingSqlCommand(command); subFolders = getFolderHierarchy(folderID, autoCommand); } //Delete all sub-folders foreach (InventoryFolderBase f in subFolders) { DeleteOneFolder(f.ID, connection); DeleteItemsInFolder(f.ID, connection); } //Delete the actual row DeleteOneFolder(folderID, connection); DeleteItemsInFolder(folderID, connection); connection.Close(); } } #endregion #region Item Methods /// /// Returns a list of items in a specified folder /// /// The folder to search /// A list containing inventory items public List getInventoryInFolder(UUID folderID) { using (AutoClosingSqlCommand command = database.Query("SELECT * FROM inventoryitems WHERE parentFolderID = @parentFolderID")) { command.Parameters.Add(database.CreateParameter("parentFolderID", folderID)); List items = new List(); using (SqlDataReader reader = command.ExecuteReader()) { while (reader.Read()) { items.Add(readInventoryItem(reader)); } } return items; } } /// /// Returns a specified inventory item /// /// The item ID /// An inventory item public InventoryItemBase getInventoryItem(UUID itemID) { using (AutoClosingSqlCommand result = database.Query("SELECT * FROM inventoryitems WHERE inventoryID = @inventoryID")) { result.Parameters.Add(database.CreateParameter("inventoryID", itemID)); using (IDataReader reader = result.ExecuteReader()) { if (reader.Read()) { return readInventoryItem(reader); } } } m_log.InfoFormat("[INVENTORY DB]: Found no inventory item with ID : {0}", itemID); return null; } /// /// Adds a specified item to the database /// /// The inventory item public void addInventoryItem(InventoryItemBase item) { if (getInventoryItem(item.ID) != null) { updateInventoryItem(item); return; } string sql = @"INSERT INTO inventoryitems ([inventoryID], [assetID], [assetType], [parentFolderID], [avatarID], [inventoryName], [inventoryDescription], [inventoryNextPermissions], [inventoryCurrentPermissions], [invType], [creatorID], [inventoryBasePermissions], [inventoryEveryOnePermissions], [inventoryGroupPermissions], [salePrice], [saleType], [creationDate], [groupID], [groupOwned], [flags]) VALUES (@inventoryID, @assetID, @assetType, @parentFolderID, @avatarID, @inventoryName, @inventoryDescription, @inventoryNextPermissions, @inventoryCurrentPermissions, @invType, @creatorID, @inventoryBasePermissions, @inventoryEveryOnePermissions, @inventoryGroupPermissions, @salePrice, @saleType, @creationDate, @groupID, @groupOwned, @flags)"; string itemName = item.Name; if (item.Name.Length > 64) { itemName = item.Name.Substring(0, 64); m_log.Warn("[INVENTORY DB]: Name field truncated from " + item.Name.Length.ToString() + " to " + itemName.Length.ToString() + " characters"); } string itemDesc = item.Description; if (item.Description.Length > 128) { itemDesc = item.Description.Substring(0, 128); m_log.Warn("[INVENTORY DB]: Description field truncated from " + item.Description.Length.ToString() + " to " + itemDesc.Length.ToString() + " characters"); } using (AutoClosingSqlCommand command = database.Query(sql)) { command.Parameters.Add(database.CreateParameter("inventoryID", item.ID)); command.Parameters.Add(database.CreateParameter("assetID", item.AssetID)); command.Parameters.Add(database.CreateParameter("assetType", item.AssetType)); command.Parameters.Add(database.CreateParameter("parentFolderID", item.Folder)); command.Parameters.Add(database.CreateParameter("avatarID", item.Owner)); command.Parameters.Add(database.CreateParameter("inventoryName", itemName)); command.Parameters.Add(database.CreateParameter("inventoryDescription", itemDesc)); command.Parameters.Add(database.CreateParameter("inventoryNextPermissions", item.NextPermissions)); command.Parameters.Add(database.CreateParameter("inventoryCurrentPermissions", item.CurrentPermissions)); command.Parameters.Add(database.CreateParameter("invType", item.InvType)); command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorId)); command.Parameters.Add(database.CreateParameter("inventoryBasePermissions", item.BasePermissions)); command.Parameters.Add(database.CreateParameter("inventoryEveryOnePermissions", item.EveryOnePermissions)); command.Parameters.Add(database.CreateParameter("inventoryGroupPermissions", item.GroupPermissions)); command.Parameters.Add(database.CreateParameter("salePrice", item.SalePrice)); command.Parameters.Add(database.CreateParameter("saleType", item.SaleType)); command.Parameters.Add(database.CreateParameter("creationDate", item.CreationDate)); command.Parameters.Add(database.CreateParameter("groupID", item.GroupID)); command.Parameters.Add(database.CreateParameter("groupOwned", item.GroupOwned)); command.Parameters.Add(database.CreateParameter("flags", item.Flags)); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error("[INVENTORY DB]: Error inserting item :" + e.Message); } } sql = "UPDATE inventoryfolders SET version = version + 1 WHERE folderID = @folderID"; using (AutoClosingSqlCommand command = database.Query(sql)) { command.Parameters.Add(database.CreateParameter("folderID", item.Folder.ToString())); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error("[INVENTORY DB] Error updating inventory folder for new item :" + e.Message); } } } /// /// Updates the specified inventory item /// /// Inventory item to update public void updateInventoryItem(InventoryItemBase item) { string sql = @"UPDATE inventoryitems SET inventoryID = @inventoryID, assetID = @assetID, assetType = @assetType, parentFolderID = @parentFolderID, avatarID = @avatarID, inventoryName = @inventoryName, inventoryDescription = @inventoryDescription, inventoryNextPermissions = @inventoryNextPermissions, inventoryCurrentPermissions = @inventoryCurrentPermissions, invType = @invType, creatorID = @creatorID, inventoryBasePermissions = @inventoryBasePermissions, inventoryEveryOnePermissions = @inventoryEveryOnePermissions, salePrice = @salePrice, saleType = @saleType, creationDate = @creationDate, groupID = @groupID, groupOwned = @groupOwned, flags = @flags WHERE inventoryID = @keyInventoryID"; using (AutoClosingSqlCommand command = database.Query(sql)) { command.Parameters.Add(database.CreateParameter("inventoryID", item.ID)); command.Parameters.Add(database.CreateParameter("assetID", item.AssetID)); command.Parameters.Add(database.CreateParameter("assetType", item.AssetType)); command.Parameters.Add(database.CreateParameter("parentFolderID", item.Folder)); command.Parameters.Add(database.CreateParameter("avatarID", item.Owner)); command.Parameters.Add(database.CreateParameter("inventoryName", item.Name)); command.Parameters.Add(database.CreateParameter("inventoryDescription", item.Description)); command.Parameters.Add(database.CreateParameter("inventoryNextPermissions", item.NextPermissions)); command.Parameters.Add(database.CreateParameter("inventoryCurrentPermissions", item.CurrentPermissions)); command.Parameters.Add(database.CreateParameter("invType", item.InvType)); command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorIdAsUuid)); command.Parameters.Add(database.CreateParameter("inventoryBasePermissions", item.BasePermissions)); command.Parameters.Add(database.CreateParameter("inventoryEveryOnePermissions", item.EveryOnePermissions)); command.Parameters.Add(database.CreateParameter("salePrice", item.SalePrice)); command.Parameters.Add(database.CreateParameter("saleType", item.SaleType)); command.Parameters.Add(database.CreateParameter("creationDate", item.CreationDate)); command.Parameters.Add(database.CreateParameter("groupID", item.GroupID)); command.Parameters.Add(database.CreateParameter("groupOwned", item.GroupOwned)); command.Parameters.Add(database.CreateParameter("flags", item.Flags)); command.Parameters.Add(database.CreateParameter("@keyInventoryID", item.ID)); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error("[INVENTORY DB]: Error updating item :" + e.Message); } } } // See IInventoryDataPlugin /// /// Delete an item in inventory database /// /// the item UUID public void deleteInventoryItem(UUID itemID) { using (AutoClosingSqlCommand command = database.Query("DELETE FROM inventoryitems WHERE inventoryID=@inventoryID")) { command.Parameters.Add(database.CreateParameter("inventoryID", itemID)); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error("[INVENTORY DB]: Error deleting item :" + e.Message); } } } public InventoryItemBase queryInventoryItem(UUID itemID) { return getInventoryItem(itemID); } public InventoryFolderBase queryInventoryFolder(UUID folderID) { return getInventoryFolder(folderID); } /// /// Returns all activated gesture-items in the inventory of the specified avatar. /// /// The of the avatar /// /// The list of gestures (s) /// public List fetchActiveGestures(UUID avatarID) { using (AutoClosingSqlCommand command = database.Query("SELECT * FROM inventoryitems WHERE avatarId = @uuid AND assetType = @assetType and flags = 1")) { command.Parameters.Add(database.CreateParameter("uuid", avatarID)); command.Parameters.Add(database.CreateParameter("assetType", (int)AssetType.Gesture)); using (IDataReader reader = command.ExecuteReader()) { List gestureList = new List(); while (reader.Read()) { gestureList.Add(readInventoryItem(reader)); } return gestureList; } } } #endregion #region Private methods /// /// Delete an item in inventory database /// /// the item ID /// connection to the database private void DeleteItemsInFolder(UUID folderID, SqlConnection connection) { using (SqlCommand command = new SqlCommand("DELETE FROM inventoryitems WHERE folderID=@folderID", connection)) { command.Parameters.Add(database.CreateParameter("folderID", folderID)); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error("[INVENTORY DB] Error deleting item :" + e.Message); } } } /// /// Gets the folder hierarchy in a loop. /// /// parent ID. /// SQL command/connection to database /// private static List getFolderHierarchy(UUID parentID, AutoClosingSqlCommand command) { command.Parameters["@parentID"].Value = parentID.Guid; //.ToString(); List folders = getInventoryFolders(command); if (folders.Count > 0) { List tempFolders = new List(); foreach (InventoryFolderBase folderBase in folders) { tempFolders.AddRange(getFolderHierarchy(folderBase.ID, command)); } if (tempFolders.Count > 0) { folders.AddRange(tempFolders); } } return folders; } /// /// Gets the inventory folders. /// /// parentID, use UUID.Zero to get root /// user id, use UUID.Zero, if you want all folders from a parentID. /// private List getInventoryFolders(UUID parentID, UUID user) { using (AutoClosingSqlCommand command = database.Query("SELECT * FROM inventoryfolders WHERE parentFolderID = @parentID AND agentID LIKE @uuid")) { if (user == UUID.Zero) { command.Parameters.Add(database.CreateParameter("uuid", "%")); } else { command.Parameters.Add(database.CreateParameter("uuid", user)); } command.Parameters.Add(database.CreateParameter("parentID", parentID)); return getInventoryFolders(command); } } /// /// Gets the inventory folders. /// /// SQLcommand. /// private static List getInventoryFolders(AutoClosingSqlCommand command) { using (IDataReader reader = command.ExecuteReader()) { List items = new List(); while (reader.Read()) { items.Add(readInventoryFolder(reader)); } return items; } } /// /// Reads a list of inventory folders returned by a query. /// /// A MSSQL Data Reader /// A List containing inventory folders protected static InventoryFolderBase readInventoryFolder(IDataReader reader) { try { InventoryFolderBase folder = new InventoryFolderBase(); folder.Owner = new UUID((Guid)reader["agentID"]); folder.ParentID = new UUID((Guid)reader["parentFolderID"]); folder.ID = new UUID((Guid)reader["folderID"]); folder.Name = (string)reader["folderName"]; folder.Type = (short)reader["type"]; folder.Version = Convert.ToUInt16(reader["version"]); return folder; } catch (Exception e) { m_log.Error("[INVENTORY DB] Error reading inventory folder :" + e.Message); } return null; } /// /// Reads a one item from an SQL result /// /// The SQL Result /// the item read private static InventoryItemBase readInventoryItem(IDataRecord reader) { try { InventoryItemBase item = new InventoryItemBase(); item.ID = new UUID((Guid)reader["inventoryID"]); item.AssetID = new UUID((Guid)reader["assetID"]); item.AssetType = Convert.ToInt32(reader["assetType"].ToString()); item.Folder = new UUID((Guid)reader["parentFolderID"]); item.Owner = new UUID((Guid)reader["avatarID"]); item.Name = reader["inventoryName"].ToString(); item.Description = reader["inventoryDescription"].ToString(); item.NextPermissions = Convert.ToUInt32(reader["inventoryNextPermissions"]); item.CurrentPermissions = Convert.ToUInt32(reader["inventoryCurrentPermissions"]); item.InvType = Convert.ToInt32(reader["invType"].ToString()); item.CreatorId = ((Guid)reader["creatorID"]).ToString(); item.BasePermissions = Convert.ToUInt32(reader["inventoryBasePermissions"]); item.EveryOnePermissions = Convert.ToUInt32(reader["inventoryEveryOnePermissions"]); item.GroupPermissions = Convert.ToUInt32(reader["inventoryGroupPermissions"]); item.SalePrice = Convert.ToInt32(reader["salePrice"]); item.SaleType = Convert.ToByte(reader["saleType"]); item.CreationDate = Convert.ToInt32(reader["creationDate"]); item.GroupID = new UUID((Guid)reader["groupID"]); item.GroupOwned = Convert.ToBoolean(reader["groupOwned"]); item.Flags = Convert.ToUInt32(reader["flags"]); return item; } catch (SqlException e) { m_log.Error("[INVENTORY DB]: Error reading inventory item :" + e.Message); } return null; } /// /// Delete a folder in inventory databasae /// /// the folder UUID /// connection to database private void DeleteOneFolder(UUID folderID, SqlConnection connection) { try { using (SqlCommand command = new SqlCommand("DELETE FROM inventoryfolders WHERE folderID=@folderID", connection)) { command.Parameters.Add(database.CreateParameter("folderID", folderID)); command.ExecuteNonQuery(); } } catch (SqlException e) { m_log.Error("[INVENTORY DB]: Error deleting folder :" + e.Message); } } #endregion } }