diff --git a/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs index 5340bcd094..d8164e718a 100644 --- a/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs +++ b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs @@ -114,7 +114,6 @@ namespace OpenSim.OfflineIM scene.ForEachClient(delegate(IClientAPI client) { client.OnRetrieveInstantMessages -= RetrieveInstantMessages; - client.OnMuteListRequest -= OnMuteListRequest; }); } @@ -162,7 +161,6 @@ namespace OpenSim.OfflineIM private void OnNewClient(IClientAPI client) { client.OnRetrieveInstantMessages += RetrieveInstantMessages; - client.OnMuteListRequest += OnMuteListRequest; } private void RetrieveInstantMessages(IClientAPI client) @@ -194,20 +192,6 @@ namespace OpenSim.OfflineIM } } - // Apparently this is needed in order for the viewer to request the IMs. - private void OnMuteListRequest(IClientAPI client, uint crc) - { - m_log.DebugFormat("[OfflineIM.V2] Got mute list request for crc {0}", crc); - string filename = "mutes" + client.AgentId.ToString(); - - IXfer xfer = client.Scene.RequestModuleInterface(); - if (xfer != null) - { - xfer.AddNewFile(filename, new Byte[0]); - client.SendMuteListUpdate(filename); - } - } - private void UndeliveredMessage(GridInstantMessage im) { if (im.dialog != (byte)InstantMessageDialog.MessageFromObject && diff --git a/OpenSim/Data/PGSQL/PGSQLFSAssetData.cs b/OpenSim/Data/PGSQL/PGSQLFSAssetData.cs new file mode 100644 index 0000000000..59b857c61f --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLFSAssetData.cs @@ -0,0 +1,316 @@ +/* + * 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.Data; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using log4net; +using OpenMetaverse; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLFSAssetData : IFSAssetDataPlugin + { + private const string _migrationStore = "FSAssetStore"; + private static string m_Table = "fsassets"; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private long m_ticksToEpoch; + + private PGSQLManager m_database; + private string m_connectionString; + + public PGSQLFSAssetData() + { + } + + public void Initialise(string connect, string realm, int UpdateAccessTime) + { + DaysBetweenAccessTimeUpdates = UpdateAccessTime; + + m_ticksToEpoch = new System.DateTime(1970, 1, 1).Ticks; + + m_connectionString = connect; + m_database = new PGSQLManager(m_connectionString); + + //New migration to check for DB changes + m_database.CheckMigration(_migrationStore); + } + + public void Initialise() + { + throw new NotImplementedException(); + } + + /// + /// Number of days that must pass before we update the access time on an asset when it has been fetched + /// Config option to change this is "DaysBetweenAccessTimeUpdates" + /// + private int DaysBetweenAccessTimeUpdates = 0; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #region IPlugin Members + + public string Version { get { return "1.0.0.0"; } } + + public void Dispose() { } + + public string Name + { + get { return "PGSQL FSAsset storage engine"; } + } + + #endregion + + #region IFSAssetDataPlugin Members + + public AssetMetadata Get(string id, out string hash) + { + hash = String.Empty; + AssetMetadata meta = null; + UUID uuid = new UUID(id); + + string query = String.Format("select \"id\", \"type\", \"hash\", \"create_time\", \"access_time\", \"asset_flags\" from {0} where \"id\" = :id", m_Table); + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + dbcon.Open(); + cmd.Parameters.Add(m_database.CreateParameter("id", uuid)); + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + if (reader.Read()) + { + meta = new AssetMetadata(); + hash = reader["hash"].ToString(); + meta.ID = id; + meta.FullID = uuid; + meta.Name = String.Empty; + meta.Description = String.Empty; + meta.Type = (sbyte)Convert.ToInt32(reader["type"]); + meta.ContentType = SLUtil.SLAssetTypeToContentType(meta.Type); + meta.CreationDate = Util.ToDateTime(Convert.ToInt32(reader["create_time"])); + meta.Flags = (AssetFlags)Convert.ToInt32(reader["asset_flags"]); + int atime = Convert.ToInt32(reader["access_time"]); + UpdateAccessTime(atime, uuid); + } + } + } + + return meta; + } + + private void UpdateAccessTime(int AccessTime, UUID id) + { + // Reduce DB work by only updating access time if asset hasn't recently been accessed + // 0 By Default, Config option is "DaysBetweenAccessTimeUpdates" + if (DaysBetweenAccessTimeUpdates > 0 && (DateTime.UtcNow - Utils.UnixTimeToDateTime(AccessTime)).TotalDays < DaysBetweenAccessTimeUpdates) + return; + + string query = String.Format("UPDATE {0} SET \"access_time\" = :access_time WHERE \"id\" = :id", m_Table); + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + dbcon.Open(); + int now = (int)((System.DateTime.Now.Ticks - m_ticksToEpoch) / 10000000); + cmd.Parameters.Add(m_database.CreateParameter("id", id)); + cmd.Parameters.Add(m_database.CreateParameter("access_time", now)); + cmd.ExecuteNonQuery(); + } + } + + public bool Store(AssetMetadata meta, string hash) + { + try + { + bool found = false; + string oldhash; + AssetMetadata existingAsset = Get(meta.ID, out oldhash); + + string query = String.Format("UPDATE {0} SET \"access_time\" = :access_time WHERE \"id\" = :id", m_Table); + if (existingAsset == null) + { + query = String.Format("insert into {0} (\"id\", \"type\", \"hash\", \"asset_flags\", \"create_time\", \"access_time\") values ( :id, :type, :hash, :asset_flags, :create_time, :access_time)", m_Table); + found = true; + } + + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + dbcon.Open(); + int now = (int)((System.DateTime.Now.Ticks - m_ticksToEpoch) / 10000000); + cmd.Parameters.Add(m_database.CreateParameter("id", meta.FullID)); + cmd.Parameters.Add(m_database.CreateParameter("type", meta.Type)); + cmd.Parameters.Add(m_database.CreateParameter("hash", hash)); + cmd.Parameters.Add(m_database.CreateParameter("asset_flags", Convert.ToInt32(meta.Flags))); + cmd.Parameters.Add(m_database.CreateParameter("create_time", now)); + cmd.Parameters.Add(m_database.CreateParameter("access_time", now)); + cmd.ExecuteNonQuery(); + } + return found; + } + catch(Exception e) + { + m_log.Error("[PGSQL FSASSETS] Failed to store asset with ID " + meta.ID); + m_log.Error(e.ToString()); + return false; + } + } + + /// + /// Check if the assets exist in the database. + /// + /// The asset UUID's + /// For each asset: true if it exists, false otherwise + public bool[] AssetsExist(UUID[] uuids) + { + if (uuids.Length == 0) + return new bool[0]; + + HashSet exists = new HashSet(); + + string ids = "'" + string.Join("','", uuids) + "'"; + string query = string.Format("select \"id\" from {1} where id in ({0})", ids, m_Table); + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + dbcon.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + while (reader.Read()) + { + UUID id = DBGuid.FromDB(reader["id"]);; + exists.Add(id); + } + } + } + + bool[] results = new bool[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + results[i] = exists.Contains(uuids[i]); + return results; + } + + public int Count() + { + int count = 0; + string query = String.Format("select count(*) as count from {0}", m_Table); + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + dbcon.Open(); + IDataReader reader = cmd.ExecuteReader(); + reader.Read(); + count = Convert.ToInt32(reader["count"]); + reader.Close(); + } + + return count; + } + + public bool Delete(string id) + { + string query = String.Format("delete from {0} where \"id\" = :id", m_Table); + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + dbcon.Open(); + cmd.Parameters.Add(m_database.CreateParameter("id", new UUID(id))); + cmd.ExecuteNonQuery(); + } + + return true; + } + + public void Import(string conn, string table, int start, int count, bool force, FSStoreDelegate store) + { + int imported = 0; + string limit = String.Empty; + if(count != -1) + { + limit = String.Format(" limit {0} offset {1}", start, count); + } + string query = String.Format("select * from {0}{1}", table, limit); + try + { + using (NpgsqlConnection remote = new NpgsqlConnection(conn)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, remote)) + { + remote.Open(); + MainConsole.Instance.Output("Querying database"); + MainConsole.Instance.Output("Reading data"); + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + while (reader.Read()) + { + if ((imported % 100) == 0) + { + MainConsole.Instance.Output(String.Format("{0} assets imported so far", imported)); + } + + AssetBase asset = new AssetBase(); + AssetMetadata meta = new AssetMetadata(); + + meta.ID = reader["id"].ToString(); + meta.FullID = new UUID(meta.ID); + + meta.Name = String.Empty; + meta.Description = String.Empty; + meta.Type = (sbyte)Convert.ToInt32(reader["assetType"]); + meta.ContentType = SLUtil.SLAssetTypeToContentType(meta.Type); + meta.CreationDate = Util.ToDateTime(Convert.ToInt32(reader["create_time"])); + + asset.Metadata = meta; + asset.Data = (byte[])reader["data"]; + + store(asset, force); + + imported++; + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PGSQL FSASSETS]: Error importing assets: {0}", + e.Message.ToString()); + return; + } + + MainConsole.Instance.Output(String.Format("Import done, {0} assets imported", imported)); + } + + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/Resources/FSAssetStore.migrations b/OpenSim/Data/PGSQL/Resources/FSAssetStore.migrations new file mode 100644 index 0000000000..3a072e566e --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/FSAssetStore.migrations @@ -0,0 +1,14 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE fsassets ( + "id" uuid NOT NULL PRIMARY KEY, + "type" integer NOT NULL, + "hash" char(64) NOT NULL, + "create_time" integer NOT NULL DEFAULT '0', + "access_time" integer NOT NULL DEFAULT '0', + "asset_flags" integer NOT NULL DEFAULT '0' +); + +COMMIT; diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 057ca17b0d..f29cdf4c43 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1209,10 +1209,10 @@ namespace OpenSim.Region.Framework.Scenes if (group.GetInventoryItem(localID, itemID) != null) { - if (item.Type == 10) + if (item.Type == (int)InventoryType.LSL) { part.RemoveScriptEvents(itemID); - EventManager.TriggerRemoveScript(localID, itemID); + part.ParentGroup.AddActiveScriptCount(-1); } group.RemoveInventoryItem(localID, itemID); @@ -1317,7 +1317,7 @@ namespace OpenSim.Region.Framework.Scenes if (taskItem.Type == (int)AssetType.LSLText) { part.RemoveScriptEvents(itemId); - EventManager.TriggerRemoveScript(part.LocalId, itemId); + part.ParentGroup.AddActiveScriptCount(-1); } part.Inventory.RemoveInventoryItem(itemId); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs index f778367ce6..bf217a5f3c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs @@ -358,7 +358,7 @@ namespace OpenSim.Region.Framework.Scenes SceneObjectPart part = parts[i]; if(m_DeepEffectivePermsInvalid) - part.AggregateInnerPerms(); + part.AggregatedInnerPermsForGroup(); owner &= part.AggregatedInnerOwnerPerms; group &= part.AggregatedInnerGroupPerms; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 7bde7c0593..532263a658 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2579,8 +2579,30 @@ namespace OpenSim.Region.Framework.Scenes AggregatedInnerOwnerPerms = owner & mask; AggregatedInnerGroupPerms = group & mask; AggregatedInnerEveryonePerms = everyone & mask; - if(ParentGroup != null) - ParentGroup.InvalidateEffectivePerms(); + } + if(ParentGroup != null) + ParentGroup.InvalidateEffectivePerms(); + } + + // same as above but called during group Effective Permission validation + public void AggregatedInnerPermsForGroup() + { + // assuming child prims permissions masks are irrelevant on a linkset + // root part is handle at SOG since its masks are the sog masks + const uint mask = (uint)PermissionMask.AllEffective; + + uint owner = mask; + uint group = mask; + uint everyone = mask; + + lock(InnerPermsLock) // do we really need this? + { + if(Inventory != null) + Inventory.AggregateInnerPerms(ref owner, ref group, ref everyone); + + AggregatedInnerOwnerPerms = owner & mask; + AggregatedInnerGroupPerms = group & mask; + AggregatedInnerEveryonePerms = everyone & mask; } } @@ -3817,7 +3839,8 @@ namespace OpenSim.Region.Framework.Scenes Byte[] buf = Shape.Textures.GetBytes(); Primitive.TextureEntry tex = new Primitive.TextureEntry(buf, 0, buf.Length); Color4 texcolor; - if (face >= 0 && face < GetNumberOfSides()) + int nsides = GetNumberOfSides(); + if (face >= 0 && face < nsides) { texcolor = tex.CreateFace((uint)face).RGBA; texcolor.R = clippedColor.X; @@ -3833,7 +3856,7 @@ namespace OpenSim.Region.Framework.Scenes } else if (face == ALL_SIDES) { - for (uint i = 0; i < GetNumberOfSides(); i++) + for (uint i = 0; i < nsides; i++) { if (tex.FaceTextures[i] != null) { @@ -5138,20 +5161,20 @@ namespace OpenSim.Region.Framework.Scenes Changed changeFlags = 0; - Primitive.TextureEntryFace fallbackNewFace = newTex.DefaultTexture; - Primitive.TextureEntryFace fallbackOldFace = oldTex.DefaultTexture; + Primitive.TextureEntryFace defaultNewFace = newTex.DefaultTexture; + Primitive.TextureEntryFace defaultOldFace = oldTex.DefaultTexture; // On Incoming packets, sometimes newText.DefaultTexture is null. The assumption is that all // other prim-sides are set, but apparently that's not always the case. Lets assume packet/data corruption at this point. - if (fallbackNewFace == null) + if (defaultNewFace == null) { - fallbackNewFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0); - newTex.DefaultTexture = fallbackNewFace; + defaultNewFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0); + newTex.DefaultTexture = defaultNewFace; } - if (fallbackOldFace == null) + if (defaultOldFace == null) { - fallbackOldFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0); - oldTex.DefaultTexture = fallbackOldFace; + defaultOldFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0); + oldTex.DefaultTexture = defaultOldFace; } // Materials capable viewers can send a ObjectImage packet @@ -5161,13 +5184,11 @@ namespace OpenSim.Region.Framework.Scenes // we should ignore any changes and not update Shape.TextureEntry bool otherFieldsChanged = false; - - for (int i = 0 ; i < GetNumberOfSides(); i++) + int nsides = GetNumberOfSides(); + for (int i = 0 ; i < nsides; i++) { - - Primitive.TextureEntryFace newFace = newTex.DefaultTexture; - Primitive.TextureEntryFace oldFace = oldTex.DefaultTexture; - + Primitive.TextureEntryFace newFace = defaultNewFace; + Primitive.TextureEntryFace oldFace = defaultOldFace; if (oldTex.FaceTextures[i] != null) oldFace = oldTex.FaceTextures[i]; if (newTex.FaceTextures[i] != null) @@ -5202,17 +5223,17 @@ namespace OpenSim.Region.Framework.Scenes if (oldFace.Rotation != newFace.Rotation) otherFieldsChanged = true; if (oldFace.Shiny != newFace.Shiny) otherFieldsChanged = true; if (oldFace.TexMapType != newFace.TexMapType) otherFieldsChanged = true; + if(otherFieldsChanged) + changeFlags |= Changed.TEXTURE; } } - if (changeFlags != 0 || otherFieldsChanged) - { - m_shape.TextureEntry = newTex.GetBytes(); - if (changeFlags != 0) - TriggerScriptChangedEvent(changeFlags); - ParentGroup.HasGroupChanged = true; - ScheduleFullUpdate(); - } + if (changeFlags == 0) + return; + m_shape.TextureEntry = newTex.GetBytes(); + TriggerScriptChangedEvent(changeFlags); + ParentGroup.HasGroupChanged = true; + ScheduleFullUpdate(); } internal void UpdatePhysicsSubscribedEvents() diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index d8f2b80ae5..b22bda0f93 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -1081,7 +1081,7 @@ namespace OpenSim.Region.Framework.Scenes { int type = m_items[itemID].InvType; m_items.LockItemsForRead(false); - if (type == 10) // Script + if (type == (int)InventoryType.LSL) // Script { m_part.ParentGroup.Scene.EventManager.TriggerRemoveScript(m_part.LocalId, itemID); } @@ -1101,7 +1101,7 @@ namespace OpenSim.Region.Framework.Scenes m_items.LockItemsForRead(true); foreach (TaskInventoryItem item in m_items.Values) { - if (item.Type == 10) + if (item.Type == (int)InventoryType.LSL) { scriptcount++; } diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 0ef6cfcdbe..2908e9c71a 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -706,14 +706,16 @@ ;; For standalones, this is the storage dll. ; StorageProvider = OpenSim.Data.MySQL.dll - ;# {MuteListModule} {OfflineMessageModule:OfflineMessageModule} {} {} None - ;; Mute list handler (not yet implemented). MUST BE SET to allow offline - ;; messages to work + ; Mute list handler + ; the provided MuteListModule is just a Dummy + ; you will need a external module for proper suport. + ; if you keep both 2 following settings comment, viewers will use mutes in their caches + ; if you uncoment both (url is ignored) the mutes will be deleted at login. + + ;# {MuteListModule} {MuteListModule:MuteListModule} {} {} None ; MuteListModule = MuteListModule - ;# {MuteListURL} {OfflineMessageModule:OfflineMessageModule} {} {} http://yourserver/Mute.php - ;; URL of the web service that serves mute lists. Not currently used, but - ;; must be set to allow offline messaging to work. + ;# {MuteListURL} {MuteListModule:MuteListModule} {} {} http://yourserver/Mute.php ; MuteListURL = http://yourserver/Mute.php ;; Control whether group invites and notices are stored for offline users.