/* * Copyright (c) Contributors * 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 OpenSim 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 Mono.Addins; using System; using System.Reflection; using System.Threading; using System.Text; using System.Net; using System.Net.Sockets; using log4net; using Nini.Config; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Scripting; using System.Collections.Generic; using System.Text.RegularExpressions; using PermissionMask = OpenSim.Framework.PermissionMask; namespace OpenSim.Region.OptionalModules.Scripting.JsonStore { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "JsonStoreScriptModule")] public class JsonStoreScriptModule : INonSharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private IConfig m_config = null; private bool m_enabled = false; private Scene m_scene = null; private IScriptModuleComms m_comms; private IJsonStoreModule m_store; private Dictionary> m_scriptStores = new Dictionary>(); #region Region Module interface // ----------------------------------------------------------------- /// /// Name of this shared module is it's class name /// // ----------------------------------------------------------------- public string Name { get { return this.GetType().Name; } } // ----------------------------------------------------------------- /// /// Initialise this shared module /// /// this region is getting initialised /// nini config, we are not using this // ----------------------------------------------------------------- public void Initialise(IConfigSource config) { try { if ((m_config = config.Configs["JsonStore"]) == null) { // There is no configuration, the module is disabled // m_log.InfoFormat("[JsonStoreScripts] no configuration info"); return; } m_enabled = m_config.GetBoolean("Enabled", m_enabled); } catch (Exception e) { m_log.ErrorFormat("[JsonStoreScripts]: initialization error: {0}", e.Message); return; } if (m_enabled) m_log.DebugFormat("[JsonStoreScripts]: module is enabled"); } // ----------------------------------------------------------------- /// /// everything is loaded, perform post load configuration /// // ----------------------------------------------------------------- public void PostInitialise() { } // ----------------------------------------------------------------- /// /// Nothing to do on close /// // ----------------------------------------------------------------- public void Close() { } // ----------------------------------------------------------------- /// /// // ----------------------------------------------------------------- public void AddRegion(Scene scene) { scene.EventManager.OnScriptReset += HandleScriptReset; scene.EventManager.OnRemoveScript += HandleScriptReset; } // ----------------------------------------------------------------- /// /// // ----------------------------------------------------------------- public void RemoveRegion(Scene scene) { scene.EventManager.OnScriptReset -= HandleScriptReset; scene.EventManager.OnRemoveScript -= HandleScriptReset; // need to remove all references to the scene in the subscription // list to enable full garbage collection of the scene object } // ----------------------------------------------------------------- /// /// // ----------------------------------------------------------------- private void HandleScriptReset(uint localID, UUID itemID) { HashSet stores; lock (m_scriptStores) { if (! m_scriptStores.TryGetValue(itemID, out stores)) return; m_scriptStores.Remove(itemID); } foreach (UUID id in stores) m_store.DestroyStore(id); } // ----------------------------------------------------------------- /// /// Called when all modules have been added for a region. This is /// where we hook up events /// // ----------------------------------------------------------------- public void RegionLoaded(Scene scene) { if (m_enabled) { m_scene = scene; m_comms = m_scene.RequestModuleInterface(); if (m_comms == null) { m_log.ErrorFormat("[JsonStoreScripts]: ScriptModuleComms interface not defined"); m_enabled = false; return; } m_store = m_scene.RequestModuleInterface(); if (m_store == null) { m_log.ErrorFormat("[JsonStoreScripts]: JsonModule interface not defined"); m_enabled = false; return; } try { m_comms.RegisterScriptInvocations(this); m_comms.RegisterConstants(this); } catch (Exception e) { // See http://opensimulator.org/mantis/view.php?id=5971 for more information m_log.WarnFormat("[JsonStoreScripts]: script method registration failed; {0}", e.Message); m_enabled = false; } } } /// ----------------------------------------------------------------- /// /// // ----------------------------------------------------------------- public Type ReplaceableInterface { get { return null; } } #endregion #region ScriptConstantsInterface [ScriptConstant] public static readonly int JSON_NODETYPE_UNDEF = (int)JsonStoreNodeType.Undefined; [ScriptConstant] public static readonly int JSON_NODETYPE_OBJECT = (int)JsonStoreNodeType.Object; [ScriptConstant] public static readonly int JSON_NODETYPE_ARRAY = (int)JsonStoreNodeType.Array; [ScriptConstant] public static readonly int JSON_NODETYPE_VALUE = (int)JsonStoreNodeType.Value; [ScriptConstant] public static readonly int JSON_VALUETYPE_UNDEF = (int)JsonStoreValueType.Undefined; [ScriptConstant] public static readonly int JSON_VALUETYPE_BOOLEAN = (int)JsonStoreValueType.Boolean; [ScriptConstant] public static readonly int JSON_VALUETYPE_INTEGER = (int)JsonStoreValueType.Integer; [ScriptConstant] public static readonly int JSON_VALUETYPE_FLOAT = (int)JsonStoreValueType.Float; [ScriptConstant] public static readonly int JSON_VALUETYPE_STRING = (int)JsonStoreValueType.String; #endregion #region ScriptInvocationInteface // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public UUID JsonAttachObjectStore(UUID hostID, UUID scriptID) { UUID uuid = UUID.Zero; if (! m_store.AttachObjectStore(hostID)) GenerateRuntimeError("Failed to create Json store"); return hostID; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public UUID JsonCreateStore(UUID hostID, UUID scriptID, string value) { UUID uuid = UUID.Zero; if (! m_store.CreateStore(value, ref uuid)) GenerateRuntimeError("Failed to create Json store"); lock (m_scriptStores) { if (! m_scriptStores.ContainsKey(scriptID)) m_scriptStores[scriptID] = new HashSet(); m_scriptStores[scriptID].Add(uuid); } return uuid; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public int JsonDestroyStore(UUID hostID, UUID scriptID, UUID storeID) { lock(m_scriptStores) { if (m_scriptStores.ContainsKey(scriptID)) m_scriptStores[scriptID].Remove(storeID); } return m_store.DestroyStore(storeID) ? 1 : 0; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public int JsonTestStore(UUID hostID, UUID scriptID, UUID storeID) { return m_store.TestStore(storeID) ? 1 : 0; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public UUID JsonRezAtRoot(UUID hostID, UUID scriptID, string item, Vector3 pos, Vector3 vel, Quaternion rot, string param) { UUID reqID = UUID.Random(); Util.FireAndForget( o => DoJsonRezObject(hostID, scriptID, reqID, item, pos, vel, rot, param), null, "JsonStoreScriptModule.DoJsonRezObject"); return reqID; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public UUID JsonReadNotecard(UUID hostID, UUID scriptID, UUID storeID, string path, string notecardIdentifier) { UUID reqID = UUID.Random(); Util.FireAndForget( o => DoJsonReadNotecard(reqID, hostID, scriptID, storeID, path, notecardIdentifier), null, "JsonStoreScriptModule.JsonReadNotecard"); return reqID; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public UUID JsonWriteNotecard(UUID hostID, UUID scriptID, UUID storeID, string path, string name) { UUID reqID = UUID.Random(); Util.FireAndForget( o => DoJsonWriteNotecard(reqID,hostID,scriptID,storeID,path,name), null, "JsonStoreScriptModule.DoJsonWriteNotecard"); return reqID; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public string JsonList2Path(UUID hostID, UUID scriptID, object[] pathlist) { string ipath = ConvertList2Path(pathlist); string opath; if (JsonStore.CanonicalPathExpression(ipath,out opath)) return opath; // This won't parse if passed to the other routines as opposed to // returning an empty string which is a valid path and would overwrite // the entire store return "**INVALID**"; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public int JsonGetNodeType(UUID hostID, UUID scriptID, UUID storeID, string path) { return (int)m_store.GetNodeType(storeID,path); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public int JsonGetValueType(UUID hostID, UUID scriptID, UUID storeID, string path) { return (int)m_store.GetValueType(storeID,path); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public int JsonSetValue(UUID hostID, UUID scriptID, UUID storeID, string path, string value) { return m_store.SetValue(storeID,path,value,false) ? 1 : 0; } [ScriptInvocation] public int JsonSetJson(UUID hostID, UUID scriptID, UUID storeID, string path, string value) { return m_store.SetValue(storeID,path,value,true) ? 1 : 0; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public int JsonRemoveValue(UUID hostID, UUID scriptID, UUID storeID, string path) { return m_store.RemoveValue(storeID,path) ? 1 : 0; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public int JsonGetArrayLength(UUID hostID, UUID scriptID, UUID storeID, string path) { return m_store.GetArrayLength(storeID,path); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public string JsonGetValue(UUID hostID, UUID scriptID, UUID storeID, string path) { string value = String.Empty; m_store.GetValue(storeID,path,false,out value); return value; } [ScriptInvocation] public string JsonGetJson(UUID hostID, UUID scriptID, UUID storeID, string path) { string value = String.Empty; m_store.GetValue(storeID,path,true, out value); return value; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public UUID JsonTakeValue(UUID hostID, UUID scriptID, UUID storeID, string path) { UUID reqID = UUID.Random(); Util.FireAndForget( o => DoJsonTakeValue(scriptID,reqID,storeID,path,false), null, "JsonStoreScriptModule.DoJsonTakeValue"); return reqID; } [ScriptInvocation] public UUID JsonTakeValueJson(UUID hostID, UUID scriptID, UUID storeID, string path) { UUID reqID = UUID.Random(); Util.FireAndForget( o => DoJsonTakeValue(scriptID,reqID,storeID,path,true), null, "JsonStoreScriptModule.DoJsonTakeValueJson"); return reqID; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- [ScriptInvocation] public UUID JsonReadValue(UUID hostID, UUID scriptID, UUID storeID, string path) { UUID reqID = UUID.Random(); Util.FireAndForget( o => DoJsonReadValue(scriptID,reqID,storeID,path,false), null, "JsonStoreScriptModule.DoJsonReadValue"); return reqID; } [ScriptInvocation] public UUID JsonReadValueJson(UUID hostID, UUID scriptID, UUID storeID, string path) { UUID reqID = UUID.Random(); Util.FireAndForget( o => DoJsonReadValue(scriptID,reqID,storeID,path,true), null, "JsonStoreScriptModule.DoJsonReadValueJson"); return reqID; } #endregion // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- protected void GenerateRuntimeError(string msg) { m_log.InfoFormat("[JsonStore] runtime error: {0}",msg); throw new Exception("JsonStore Runtime Error: " + msg); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- protected void DispatchValue(UUID scriptID, UUID reqID, string value) { m_comms.DispatchReply(scriptID,1,value,reqID.ToString()); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- private void DoJsonTakeValue(UUID scriptID, UUID reqID, UUID storeID, string path, bool useJson) { try { m_store.TakeValue(storeID,path,useJson,delegate(string value) { DispatchValue(scriptID,reqID,value); }); return; } catch (Exception e) { m_log.InfoFormat("[JsonStoreScripts]: unable to retrieve value; {0}",e.ToString()); } DispatchValue(scriptID,reqID,String.Empty); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- private void DoJsonReadValue(UUID scriptID, UUID reqID, UUID storeID, string path, bool useJson) { try { m_store.ReadValue(storeID,path,useJson,delegate(string value) { DispatchValue(scriptID,reqID,value); }); return; } catch (Exception e) { m_log.InfoFormat("[JsonStoreScripts]: unable to retrieve value; {0}",e.ToString()); } DispatchValue(scriptID,reqID,String.Empty); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- private void DoJsonReadNotecard( UUID reqID, UUID hostID, UUID scriptID, UUID storeID, string path, string notecardIdentifier) { UUID assetID; if (!UUID.TryParse(notecardIdentifier, out assetID)) { SceneObjectPart part = m_scene.GetSceneObjectPart(hostID); assetID = ScriptUtils.GetAssetIdFromItemName(part, notecardIdentifier, (int)AssetType.Notecard); } AssetBase a = m_scene.AssetService.Get(assetID.ToString()); if (a == null) GenerateRuntimeError(String.Format("Unable to find notecard asset {0}", assetID)); if (a.Type != (sbyte)AssetType.Notecard) GenerateRuntimeError(String.Format("Invalid notecard asset {0}", assetID)); m_log.DebugFormat("[JsonStoreScripts]: read notecard in context {0}",storeID); try { string jsondata = SLUtil.ParseNotecardToString(Encoding.UTF8.GetString(a.Data)); int result = m_store.SetValue(storeID, path, jsondata,true) ? 1 : 0; m_comms.DispatchReply(scriptID, result, "", reqID.ToString()); return; } catch (Exception e) { m_log.WarnFormat("[JsonStoreScripts]: Json parsing failed; {0}", e.Message); } GenerateRuntimeError(String.Format("Json parsing failed for {0}", assetID)); m_comms.DispatchReply(scriptID, 0, "", reqID.ToString()); } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- private void DoJsonWriteNotecard(UUID reqID, UUID hostID, UUID scriptID, UUID storeID, string path, string name) { string data; if (! m_store.GetValue(storeID,path,true, out data)) { m_comms.DispatchReply(scriptID,0,UUID.Zero.ToString(),reqID.ToString()); return; } SceneObjectPart host = m_scene.GetSceneObjectPart(hostID); // Create new asset UUID assetID = UUID.Random(); AssetBase asset = new AssetBase(assetID, name, (sbyte)AssetType.Notecard, host.OwnerID.ToString()); asset.Description = "Json store"; int textLength = data.Length; data = "Linden text version 2\n{\nLLEmbeddedItems version 1\n{\ncount 0\n}\nText length " + textLength.ToString() + "\n" + data + "}\n"; asset.Data = Util.UTF8.GetBytes(data); m_scene.AssetService.Store(asset); // Create Task Entry TaskInventoryItem taskItem = new TaskInventoryItem(); taskItem.ResetIDs(host.UUID); taskItem.ParentID = host.UUID; taskItem.CreationDate = (uint)Util.UnixTimeSinceEpoch(); taskItem.Name = asset.Name; taskItem.Description = asset.Description; taskItem.Type = (int)AssetType.Notecard; taskItem.InvType = (int)InventoryType.Notecard; taskItem.OwnerID = host.OwnerID; taskItem.CreatorID = host.OwnerID; taskItem.BasePermissions = (uint)PermissionMask.All; taskItem.CurrentPermissions = (uint)PermissionMask.All; taskItem.EveryonePermissions = 0; taskItem.NextPermissions = (uint)PermissionMask.All; taskItem.GroupID = host.GroupID; taskItem.GroupPermissions = 0; taskItem.Flags = 0; taskItem.PermsGranter = UUID.Zero; taskItem.PermsMask = 0; taskItem.AssetID = asset.FullID; host.Inventory.AddInventoryItem(taskItem, false); m_comms.DispatchReply(scriptID,1,assetID.ToString(),reqID.ToString()); } // ----------------------------------------------------------------- /// /// Convert a list of values that are path components to a single string path /// // ----------------------------------------------------------------- protected static Regex m_ArrayPattern = new Regex("^([0-9]+|\\+)$"); private string ConvertList2Path(object[] pathlist) { string path = ""; for (int i = 0; i < pathlist.Length; i++) { string token = ""; if (pathlist[i] is string) { token = pathlist[i].ToString(); // Check to see if this is a bare number which would not be a valid // identifier otherwise if (m_ArrayPattern.IsMatch(token)) token = '[' + token + ']'; } else if (pathlist[i] is int) { token = "[" + pathlist[i].ToString() + "]"; } else { token = "." + pathlist[i].ToString() + "."; } path += token + "."; } return path; } // ----------------------------------------------------------------- /// /// /// // ----------------------------------------------------------------- private void DoJsonRezObject(UUID hostID, UUID scriptID, UUID reqID, string name, Vector3 pos, Vector3 vel, Quaternion rot, string param) { if (Double.IsNaN(rot.X) || Double.IsNaN(rot.Y) || Double.IsNaN(rot.Z) || Double.IsNaN(rot.W)) { GenerateRuntimeError("Invalid rez rotation"); return; } SceneObjectGroup host = m_scene.GetSceneObjectGroup(hostID); if (host == null) { GenerateRuntimeError(String.Format("Unable to find rezzing host '{0}'",hostID)); return; } // hpos = host.RootPart.GetWorldPosition() // float dist = (float)llVecDist(hpos, pos); // if (dist > m_ScriptDistanceFactor * 10.0f) // return; TaskInventoryItem item = host.RootPart.Inventory.GetInventoryItem(name); if (item == null) { GenerateRuntimeError(String.Format("Unable to find object to rez '{0}'",name)); return; } if (item.InvType != (int)InventoryType.Object) { GenerateRuntimeError("Can't create requested object; object is missing from database"); return; } List objlist; List veclist; bool success = host.RootPart.Inventory.GetRezReadySceneObjects(item, out objlist, out veclist); if (! success) { GenerateRuntimeError("Failed to create object"); return; } int totalPrims = 0; foreach (SceneObjectGroup group in objlist) totalPrims += group.PrimCount; if (! m_scene.Permissions.CanRezObject(totalPrims, item.OwnerID, pos)) { GenerateRuntimeError("Not allowed to create the object"); return; } if (! m_scene.Permissions.BypassPermissions()) { if ((item.CurrentPermissions & (uint)PermissionMask.Copy) == 0) host.RootPart.Inventory.RemoveInventoryItem(item.ItemID); } for (int i = 0; i < objlist.Count; i++) { SceneObjectGroup group = objlist[i]; Vector3 curpos = pos + veclist[i]; if (group.IsAttachment == false && group.RootPart.Shape.State != 0) { group.RootPart.AttachedPos = group.AbsolutePosition; group.RootPart.Shape.LastAttachPoint = (byte)group.AttachmentPoint; } group.FromPartID = host.RootPart.UUID; m_scene.AddNewSceneObject(group, true, curpos, rot, vel); UUID storeID = group.UUID; if (! m_store.CreateStore(param, ref storeID)) { GenerateRuntimeError("Unable to create jsonstore for new object"); continue; } // We can only call this after adding the scene object, since the scene object references the scene // to find out if scripts should be activated at all. group.RootPart.SetDieAtEdge(true); group.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 3); group.ResumeScripts(); group.ScheduleGroupForFullUpdate(); // send the reply back to the host object, use the integer param to indicate the number // of remaining objects m_comms.DispatchReply(scriptID, objlist.Count-i-1, group.RootPart.UUID.ToString(), reqID.ToString()); } } } }