/*
* 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;
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;
#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)
{
}
// -----------------------------------------------------------------
///
///
// -----------------------------------------------------------------
public void RemoveRegion(Scene scene)
{
// need to remove all references to the scene in the subscription
// list to enable full garbage collection of the scene object
}
// -----------------------------------------------------------------
///
/// 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.RegisterScriptInvocation(this, "JsonCreateStore");
// m_comms.RegisterScriptInvocation(this, "JsonAttachObjectStore");
// m_comms.RegisterScriptInvocation(this, "JsonDestroyStore");
// m_comms.RegisterScriptInvocation(this, "JsonTestStore");
// m_comms.RegisterScriptInvocation(this, "JsonReadNotecard");
// m_comms.RegisterScriptInvocation(this, "JsonWriteNotecard");
// m_comms.RegisterScriptInvocation(this, "JsonTestPathList");
// m_comms.RegisterScriptInvocation(this, "JsonTestPath");
// m_comms.RegisterScriptInvocation(this, "JsonTestPathJson");
// m_comms.RegisterScriptInvocation(this, "JsonGetValue");
// m_comms.RegisterScriptInvocation(this, "JsonGetValueJson");
// m_comms.RegisterScriptInvocation(this, "JsonTakeValue");
// m_comms.RegisterScriptInvocation(this, "JsonTakeValueJson");
// m_comms.RegisterScriptInvocation(this, "JsonReadValue");
// m_comms.RegisterScriptInvocation(this, "JsonReadValueJson");
// m_comms.RegisterScriptInvocation(this, "JsonSetValue");
// m_comms.RegisterScriptInvocation(this, "JsonSetValueJson");
// m_comms.RegisterScriptInvocation(this, "JsonRemoveValue");
}
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 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");
return uuid;
}
// -----------------------------------------------------------------
///
///
///
// -----------------------------------------------------------------
[ScriptInvocation]
public int JsonDestroyStore(UUID hostID, UUID scriptID, UUID 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 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));
return reqID;
}
// -----------------------------------------------------------------
///
///
///
// -----------------------------------------------------------------
[ScriptInvocation]
public UUID JsonWriteNotecard(UUID hostID, UUID scriptID, UUID storeID, string path, string name)
{
UUID reqID = UUID.Random();
Util.FireAndForget(delegate(object o) { DoJsonWriteNotecard(reqID,hostID,scriptID,storeID,path,name); });
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 JsonTestPath(UUID hostID, UUID scriptID, UUID storeID, string path)
{
return m_store.TestPath(storeID,path,false) ? 1 : 0;
}
[ScriptInvocation]
public int JsonTestPathJson(UUID hostID, UUID scriptID, UUID storeID, string path)
{
return m_store.TestPath(storeID,path,true) ? 1 : 0;
}
// -----------------------------------------------------------------
///
///
///
// -----------------------------------------------------------------
[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 JsonSetValueJson(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 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 JsonGetValueJson(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(delegate(object o) { DoJsonTakeValue(scriptID,reqID,storeID,path,false); });
return reqID;
}
[ScriptInvocation]
public UUID JsonTakeValueJson(UUID hostID, UUID scriptID, UUID storeID, string path)
{
UUID reqID = UUID.Random();
Util.FireAndForget(delegate(object o) { DoJsonTakeValue(scriptID,reqID,storeID,path,true); });
return reqID;
}
// -----------------------------------------------------------------
///
///
///
// -----------------------------------------------------------------
[ScriptInvocation]
public UUID JsonReadValue(UUID hostID, UUID scriptID, UUID storeID, string path)
{
UUID reqID = UUID.Random();
Util.FireAndForget(delegate(object o) { DoJsonReadValue(scriptID,reqID,storeID,path,false); });
return reqID;
}
[ScriptInvocation]
public UUID JsonReadValueJson(UUID hostID, UUID scriptID, UUID storeID, string path)
{
UUID reqID = UUID.Random();
Util.FireAndForget(delegate(object o) { DoJsonReadValue(scriptID,reqID,storeID,path,true); });
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;
}
}
}