mirror of
https://github.com/opensim/opensim.git
synced 2026-08-07 01:35:46 +08:00
this is step 2 of 2 of the OpenSim.Region.Environment refactor.
NOTHING has been deleted or moved off to forge at this point. what
has happened is that OpenSim.Region.Environment.Modules has been split
in two:
- OpenSim.Region.CoreModules: all those modules that are either
directly or indirectly referenced from other OpenSim packages, or
that provide functionality that the OpenSim developer community
considers core functionality:
CoreModules/Agent/AssetTransaction
CoreModules/Agent/Capabilities
CoreModules/Agent/TextureDownload
CoreModules/Agent/TextureSender
CoreModules/Agent/TextureSender/Tests
CoreModules/Agent/Xfer
CoreModules/Avatar/AvatarFactory
CoreModules/Avatar/Chat/ChatModule
CoreModules/Avatar/Combat
CoreModules/Avatar/Currency/SampleMoney
CoreModules/Avatar/Dialog
CoreModules/Avatar/Friends
CoreModules/Avatar/Gestures
CoreModules/Avatar/Groups
CoreModules/Avatar/InstantMessage
CoreModules/Avatar/Inventory
CoreModules/Avatar/Inventory/Archiver
CoreModules/Avatar/Inventory/Transfer
CoreModules/Avatar/Lure
CoreModules/Avatar/ObjectCaps
CoreModules/Avatar/Profiles
CoreModules/Communications/Local
CoreModules/Communications/REST
CoreModules/Framework/EventQueue
CoreModules/Framework/InterfaceCommander
CoreModules/Hypergrid
CoreModules/InterGrid
CoreModules/Scripting/DynamicTexture
CoreModules/Scripting/EMailModules
CoreModules/Scripting/HttpRequest
CoreModules/Scripting/LoadImageURL
CoreModules/Scripting/VectorRender
CoreModules/Scripting/WorldComm
CoreModules/Scripting/XMLRPC
CoreModules/World/Archiver
CoreModules/World/Archiver/Tests
CoreModules/World/Estate
CoreModules/World/Land
CoreModules/World/Permissions
CoreModules/World/Serialiser
CoreModules/World/Sound
CoreModules/World/Sun
CoreModules/World/Terrain
CoreModules/World/Terrain/DefaultEffects
CoreModules/World/Terrain/DefaultEffects/bin
CoreModules/World/Terrain/DefaultEffects/bin/Debug
CoreModules/World/Terrain/Effects
CoreModules/World/Terrain/FileLoaders
CoreModules/World/Terrain/FloodBrushes
CoreModules/World/Terrain/PaintBrushes
CoreModules/World/Terrain/Tests
CoreModules/World/Vegetation
CoreModules/World/Wind
CoreModules/World/WorldMap
- OpenSim.Region.OptionalModules: all those modules that are not core
modules:
OptionalModules/Avatar/Chat/IRC-stuff
OptionalModules/Avatar/Concierge
OptionalModules/Avatar/Voice/AsterixVoice
OptionalModules/Avatar/Voice/SIPVoice
OptionalModules/ContentManagementSystem
OptionalModules/Grid/Interregion
OptionalModules/Python
OptionalModules/SvnSerialiser
OptionalModules/World/NPC
OptionalModules/World/TreePopulator
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* 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 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using Nini.Config;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
|
||||
{
|
||||
public class DynamicTextureModule : IRegionModule, IDynamicTextureManager
|
||||
{
|
||||
private Dictionary<UUID, Scene> RegisteredScenes = new Dictionary<UUID, Scene>();
|
||||
|
||||
private Dictionary<string, IDynamicTextureRender> RenderPlugins =
|
||||
new Dictionary<string, IDynamicTextureRender>();
|
||||
|
||||
private Dictionary<UUID, DynamicTextureUpdater> Updaters = new Dictionary<UUID, DynamicTextureUpdater>();
|
||||
|
||||
#region IDynamicTextureManager Members
|
||||
|
||||
public void RegisterRender(string handleType, IDynamicTextureRender render)
|
||||
{
|
||||
if (!RenderPlugins.ContainsKey(handleType))
|
||||
{
|
||||
RenderPlugins.Add(handleType, render);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by code which actually renders the dynamic texture to supply texture data.
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="data"></param>
|
||||
public void ReturnData(UUID id, byte[] data)
|
||||
{
|
||||
if (Updaters.ContainsKey(id))
|
||||
{
|
||||
DynamicTextureUpdater updater = Updaters[id];
|
||||
if (RegisteredScenes.ContainsKey(updater.SimUUID))
|
||||
{
|
||||
Scene scene = RegisteredScenes[updater.SimUUID];
|
||||
updater.DataReceived(data, scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
|
||||
string extraParams, int updateTimer)
|
||||
{
|
||||
return AddDynamicTextureURL(simID, primID, contentType, url, extraParams, updateTimer, false, 255);
|
||||
}
|
||||
|
||||
public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
|
||||
string extraParams, int updateTimer, bool SetBlending, byte AlphaValue)
|
||||
{
|
||||
if (RenderPlugins.ContainsKey(contentType))
|
||||
{
|
||||
//Console.WriteLine("dynamic texture being created: " + url + " of type " + contentType);
|
||||
|
||||
DynamicTextureUpdater updater = new DynamicTextureUpdater();
|
||||
updater.SimUUID = simID;
|
||||
updater.PrimID = primID;
|
||||
updater.ContentType = contentType;
|
||||
updater.Url = url;
|
||||
updater.UpdateTimer = updateTimer;
|
||||
updater.UpdaterID = UUID.Random();
|
||||
updater.Params = extraParams;
|
||||
updater.BlendWithOldTexture = SetBlending;
|
||||
updater.FrontAlpha = AlphaValue;
|
||||
|
||||
if (!Updaters.ContainsKey(updater.UpdaterID))
|
||||
{
|
||||
Updaters.Add(updater.UpdaterID, updater);
|
||||
}
|
||||
|
||||
RenderPlugins[contentType].AsyncConvertUrl(updater.UpdaterID, url, extraParams);
|
||||
return updater.UpdaterID;
|
||||
}
|
||||
return UUID.Zero;
|
||||
}
|
||||
|
||||
public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data,
|
||||
string extraParams, int updateTimer)
|
||||
{
|
||||
return AddDynamicTextureData(simID, primID, contentType, data, extraParams, updateTimer, false, 255);
|
||||
}
|
||||
|
||||
public UUID AddDynamicTextureData(UUID simID, UUID primID, string contentType, string data,
|
||||
string extraParams, int updateTimer, bool SetBlending, byte AlphaValue)
|
||||
{
|
||||
if (RenderPlugins.ContainsKey(contentType))
|
||||
{
|
||||
DynamicTextureUpdater updater = new DynamicTextureUpdater();
|
||||
updater.SimUUID = simID;
|
||||
updater.PrimID = primID;
|
||||
updater.ContentType = contentType;
|
||||
updater.BodyData = data;
|
||||
updater.UpdateTimer = updateTimer;
|
||||
updater.UpdaterID = UUID.Random();
|
||||
updater.Params = extraParams;
|
||||
updater.BlendWithOldTexture = SetBlending;
|
||||
updater.FrontAlpha = AlphaValue;
|
||||
|
||||
if (!Updaters.ContainsKey(updater.UpdaterID))
|
||||
{
|
||||
Updaters.Add(updater.UpdaterID, updater);
|
||||
}
|
||||
|
||||
RenderPlugins[contentType].AsyncConvertData(updater.UpdaterID, data, extraParams);
|
||||
return updater.UpdaterID;
|
||||
}
|
||||
return UUID.Zero;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
|
||||
{
|
||||
RegisteredScenes.Add(scene.RegionInfo.RegionID, scene);
|
||||
scene.RegisterModuleInterface<IDynamicTextureManager>(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "DynamicTextureModule"; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nested type: DynamicTextureUpdater
|
||||
|
||||
public class DynamicTextureUpdater
|
||||
{
|
||||
public bool BlendWithOldTexture = false;
|
||||
public string BodyData;
|
||||
public string ContentType;
|
||||
public byte FrontAlpha = 255;
|
||||
public UUID LastAssetID;
|
||||
public string Params;
|
||||
public UUID PrimID;
|
||||
public bool SetNewFrontAlpha = false;
|
||||
public UUID SimUUID;
|
||||
public UUID UpdaterID;
|
||||
public int UpdateTimer;
|
||||
public string Url;
|
||||
|
||||
public DynamicTextureUpdater()
|
||||
{
|
||||
LastAssetID = UUID.Zero;
|
||||
UpdateTimer = 0;
|
||||
BodyData = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once new texture data has been received for this updater.
|
||||
/// </summary>
|
||||
public void DataReceived(byte[] data, Scene scene)
|
||||
{
|
||||
SceneObjectPart part = scene.GetSceneObjectPart(PrimID);
|
||||
byte[] assetData;
|
||||
AssetBase oldAsset = null;
|
||||
|
||||
if (BlendWithOldTexture)
|
||||
{
|
||||
UUID lastTextureID = part.Shape.Textures.DefaultTexture.TextureID;
|
||||
oldAsset = scene.AssetCache.GetAsset(lastTextureID, true);
|
||||
if (oldAsset != null)
|
||||
{
|
||||
assetData = BlendTextures(data, oldAsset.Data, SetNewFrontAlpha, FrontAlpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
assetData = new byte[data.Length];
|
||||
Array.Copy(data, assetData, data.Length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assetData = new byte[data.Length];
|
||||
Array.Copy(data, assetData, data.Length);
|
||||
}
|
||||
|
||||
// Create a new asset for user
|
||||
AssetBase asset = new AssetBase();
|
||||
asset.Metadata.FullID = UUID.Random();
|
||||
asset.Data = assetData;
|
||||
asset.Metadata.Name = "DynamicImage" + Util.RandomClass.Next(1, 10000);
|
||||
asset.Metadata.Type = 0;
|
||||
asset.Metadata.Description = "dynamic image";
|
||||
asset.Metadata.Local = false;
|
||||
asset.Metadata.Temporary = true;
|
||||
scene.AssetCache.AddAsset(asset);
|
||||
|
||||
LastAssetID = asset.Metadata.FullID;
|
||||
|
||||
IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>();
|
||||
if (cacheLayerDecode != null)
|
||||
{
|
||||
cacheLayerDecode.syncdecode(asset.Metadata.FullID, asset.Data);
|
||||
}
|
||||
cacheLayerDecode = null;
|
||||
|
||||
// mostly keep the values from before
|
||||
Primitive.TextureEntry tmptex = part.Shape.Textures;
|
||||
|
||||
// remove the old asset from the cache
|
||||
UUID oldID = tmptex.DefaultTexture.TextureID;
|
||||
scene.AssetCache.ExpireAsset(oldID);
|
||||
|
||||
tmptex.DefaultTexture.TextureID = asset.Metadata.FullID;
|
||||
// I'm pretty sure we always want to force this to true
|
||||
tmptex.DefaultTexture.Fullbright = true;
|
||||
|
||||
part.Shape.Textures = tmptex;
|
||||
part.ScheduleFullUpdate();
|
||||
}
|
||||
|
||||
private byte[] BlendTextures(byte[] frontImage, byte[] backImage, bool setNewAlpha, byte newAlpha)
|
||||
{
|
||||
ManagedImage managedImage;
|
||||
Image image;
|
||||
|
||||
if (OpenJPEG.DecodeToImage(frontImage, out managedImage, out image))
|
||||
{
|
||||
Bitmap image1 = new Bitmap(image);
|
||||
|
||||
if (OpenJPEG.DecodeToImage(backImage, out managedImage, out image))
|
||||
{
|
||||
Bitmap image2 = new Bitmap(image);
|
||||
|
||||
if (setNewAlpha)
|
||||
SetAlpha(ref image1, newAlpha);
|
||||
|
||||
Bitmap joint = MergeBitMaps(image1, image2);
|
||||
|
||||
byte[] result = new byte[0];
|
||||
|
||||
try
|
||||
{
|
||||
result = OpenJPEG.EncodeFromImage(joint, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console.WriteLine(
|
||||
"[DYNAMICTEXTUREMODULE]: OpenJpeg Encode Failed. Empty byte data returned!");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Bitmap MergeBitMaps(Bitmap front, Bitmap back)
|
||||
{
|
||||
Bitmap joint;
|
||||
Graphics jG;
|
||||
|
||||
joint = new Bitmap(back.Width, back.Height, PixelFormat.Format32bppArgb);
|
||||
jG = Graphics.FromImage(joint);
|
||||
|
||||
jG.DrawImage(back, 0, 0, back.Width, back.Height);
|
||||
jG.DrawImage(front, 0, 0, back.Width, back.Height);
|
||||
|
||||
return joint;
|
||||
}
|
||||
|
||||
private void SetAlpha(ref Bitmap b, byte alpha)
|
||||
{
|
||||
for (int w = 0; w < b.Width; w++)
|
||||
{
|
||||
for (int h = 0; h < b.Height; h++)
|
||||
{
|
||||
b.SetPixel(w, h, Color.FromArgb(alpha, b.GetPixel(w, h)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
288
OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs
Normal file
288
OpenSim/Region/CoreModules/Scripting/EMailModules/EmailModule.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* 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 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 System;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using log4net;
|
||||
using Nini.Config;
|
||||
using DotNetOpenMail;
|
||||
using DotNetOpenMail.SmtpAuth;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.EmailModules
|
||||
{
|
||||
public class EmailModule : IEmailModule
|
||||
{
|
||||
//
|
||||
// Log
|
||||
//
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
//
|
||||
// Module vars
|
||||
//
|
||||
private IConfigSource m_Config;
|
||||
private string m_HostName = string.Empty;
|
||||
//private string m_RegionName = string.Empty;
|
||||
private string SMTP_SERVER_HOSTNAME = string.Empty;
|
||||
private int SMTP_SERVER_PORT = 25;
|
||||
private string SMTP_SERVER_LOGIN = string.Empty;
|
||||
private string SMTP_SERVER_PASSWORD = string.Empty;
|
||||
|
||||
// Scenes by Region Handle
|
||||
private Dictionary<ulong, Scene> m_Scenes =
|
||||
new Dictionary<ulong, Scene>();
|
||||
|
||||
private bool m_Enabled = false;
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
m_Config = config;
|
||||
IConfig SMTPConfig;
|
||||
|
||||
//FIXME: RegionName is correct??
|
||||
//m_RegionName = scene.RegionInfo.RegionName;
|
||||
|
||||
IConfig startupConfig = m_Config.Configs["Startup"];
|
||||
|
||||
m_Enabled = (startupConfig.GetString("emailmodule", "DefaultEmailModule") == "DefaultEmailModule");
|
||||
|
||||
//Load SMTP SERVER config
|
||||
try
|
||||
{
|
||||
if ((SMTPConfig = m_Config.Configs["SMTP"]) == null)
|
||||
{
|
||||
m_log.InfoFormat("[SMTP] SMTP server not configured");
|
||||
m_Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SMTPConfig.GetBoolean("enabled", false))
|
||||
{
|
||||
m_log.InfoFormat("[SMTP] module disabled in configuration");
|
||||
m_Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_HostName = SMTPConfig.GetString("host_domain_header_from", m_HostName);
|
||||
SMTP_SERVER_HOSTNAME = SMTPConfig.GetString("SMTP_SERVER_HOSTNAME",SMTP_SERVER_HOSTNAME);
|
||||
SMTP_SERVER_PORT = SMTPConfig.GetInt("SMTP_SERVER_PORT", SMTP_SERVER_PORT);
|
||||
SMTP_SERVER_LOGIN = SMTPConfig.GetString("SMTP_SERVER_LOGIN", SMTP_SERVER_LOGIN);
|
||||
SMTP_SERVER_PASSWORD = SMTPConfig.GetString("SMTP_SERVER_PASSWORD", SMTP_SERVER_PASSWORD);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error("[EMAIL] DefaultEmailModule not configured: "+ e.Message);
|
||||
m_Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// It's a go!
|
||||
if (m_Enabled)
|
||||
{
|
||||
lock (m_Scenes)
|
||||
{
|
||||
// Claim the interface slot
|
||||
scene.RegisterModuleInterface<IEmailModule>(this);
|
||||
|
||||
// Add to scene list
|
||||
if (m_Scenes.ContainsKey(scene.RegionInfo.RegionHandle))
|
||||
{
|
||||
m_Scenes[scene.RegionInfo.RegionHandle] = scene;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Scenes.Add(scene.RegionInfo.RegionHandle, scene);
|
||||
}
|
||||
}
|
||||
|
||||
m_log.Info("[EMAIL] Activated DefaultEmailModule");
|
||||
}
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "DefaultEmailModule"; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="seconds"></param>
|
||||
private void DelayInSeconds(int seconds)
|
||||
{
|
||||
TimeSpan DiffDelay = new TimeSpan(0, 0, seconds);
|
||||
DateTime EndDelay = DateTime.Now.Add(DiffDelay);
|
||||
while (DateTime.Now < EndDelay)
|
||||
{
|
||||
;//Do nothing!!
|
||||
}
|
||||
}
|
||||
|
||||
private SceneObjectPart findPrim(UUID objectID, out string ObjectRegionName)
|
||||
{
|
||||
lock (m_Scenes)
|
||||
{
|
||||
foreach (Scene s in m_Scenes.Values)
|
||||
{
|
||||
SceneObjectPart part = s.GetSceneObjectPart(objectID);
|
||||
if (part != null)
|
||||
{
|
||||
ObjectRegionName = s.RegionInfo.RegionName;
|
||||
return part;
|
||||
}
|
||||
}
|
||||
}
|
||||
ObjectRegionName = string.Empty;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void resolveNamePositionRegionName(UUID objectID, out string ObjectName, out string ObjectAbsolutePosition, out string ObjectRegionName)
|
||||
{
|
||||
string m_ObjectRegionName;
|
||||
SceneObjectPart part = findPrim(objectID, out m_ObjectRegionName);
|
||||
if (part != null)
|
||||
{
|
||||
ObjectAbsolutePosition = part.AbsolutePosition.ToString();
|
||||
ObjectName = part.Name;
|
||||
ObjectRegionName = m_ObjectRegionName;
|
||||
return;
|
||||
}
|
||||
ObjectAbsolutePosition = part.AbsolutePosition.ToString();
|
||||
ObjectName = part.Name;
|
||||
ObjectRegionName = m_ObjectRegionName;
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SendMail function utilized by llEMail
|
||||
/// </summary>
|
||||
/// <param name="objectID"></param>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="subject"></param>
|
||||
/// <param name="body"></param>
|
||||
public void SendEmail(UUID objectID, string address, string subject, string body)
|
||||
{
|
||||
//Check if address is empty
|
||||
if (address == string.Empty)
|
||||
return;
|
||||
|
||||
//FIXED:Check the email is correct form in REGEX
|
||||
string EMailpatternStrict = @"^(([^<>()[\]\\.,;:\s@\""]+"
|
||||
+ @"(\.[^<>()[\]\\.,;:\s@\""]+)*)|(\"".+\""))@"
|
||||
+ @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
|
||||
+ @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
|
||||
+ @"[a-zA-Z]{2,}))$";
|
||||
Regex EMailreStrict = new Regex(EMailpatternStrict);
|
||||
bool isEMailStrictMatch = EMailreStrict.IsMatch(address);
|
||||
if (!isEMailStrictMatch)
|
||||
{
|
||||
m_log.Error("[EMAIL] REGEX Problem in EMail Address: "+address);
|
||||
return;
|
||||
}
|
||||
//FIXME:Check if subject + body = 4096 Byte
|
||||
if ((subject.Length + body.Length) > 1024)
|
||||
{
|
||||
m_log.Error("[EMAIL] subject + body > 1024 Byte");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string LastObjectName = string.Empty;
|
||||
string LastObjectPosition = string.Empty;
|
||||
string LastObjectRegionName = string.Empty;
|
||||
//DONE: Message as Second Life style
|
||||
//20 second delay - AntiSpam System - for now only 10 seconds
|
||||
DelayInSeconds(10);
|
||||
//Creation EmailMessage
|
||||
EmailMessage emailMessage = new EmailMessage();
|
||||
//From
|
||||
emailMessage.FromAddress = new EmailAddress(objectID.ToString()+"@"+m_HostName);
|
||||
//To - Only One
|
||||
emailMessage.AddToAddress(new EmailAddress(address));
|
||||
//Subject
|
||||
emailMessage.Subject = subject;
|
||||
//TEXT Body
|
||||
resolveNamePositionRegionName(objectID, out LastObjectName, out LastObjectPosition, out LastObjectRegionName);
|
||||
emailMessage.TextPart = new TextAttachment("Object-Name: " + LastObjectName +
|
||||
"\r\nRegion: " + LastObjectRegionName + "\r\nLocal-Position: " +
|
||||
LastObjectPosition+"\r\n\r\n\r\n" + body);
|
||||
//HTML Body
|
||||
emailMessage.HtmlPart = new HtmlAttachment("<html><body><p>" +
|
||||
"<BR>Object-Name: " + LastObjectName +
|
||||
"<BR>Region: " + LastObjectRegionName +
|
||||
"<BR>Local-Position: " + LastObjectPosition + "<BR><BR><BR>"
|
||||
+body+"\r\n</p></body><html>");
|
||||
|
||||
//Set SMTP SERVER config
|
||||
SmtpServer smtpServer=new SmtpServer(SMTP_SERVER_HOSTNAME,SMTP_SERVER_PORT);
|
||||
//Authentication
|
||||
smtpServer.SmtpAuthToken=new SmtpAuthToken(SMTP_SERVER_LOGIN, SMTP_SERVER_PASSWORD);
|
||||
//Send Email Message
|
||||
emailMessage.Send(smtpServer);
|
||||
//Log
|
||||
m_log.Info("[EMAIL] EMail sent to: " + address + " from object: " + objectID.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Error("[EMAIL] DefaultEmailModule Exception: "+e.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="objectID"></param>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="subject"></param>
|
||||
/// <returns></returns>
|
||||
public Email GetNextEmail(UUID objectID, string sender, string subject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* 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 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using Nini.Config;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using System.Collections;
|
||||
|
||||
/*****************************************************
|
||||
*
|
||||
* ScriptsHttpRequests
|
||||
*
|
||||
* Implements the llHttpRequest and http_response
|
||||
* callback.
|
||||
*
|
||||
* Some stuff was already in LSLLongCmdHandler, and then
|
||||
* there was this file with a stub class in it. So,
|
||||
* I am moving some of the objects and functions out of
|
||||
* LSLLongCmdHandler, such as the HttpRequestClass, the
|
||||
* start and stop methods, and setting up pending and
|
||||
* completed queues. These are processed in the
|
||||
* LSLLongCmdHandler polling loop. Similiar to the
|
||||
* XMLRPCModule, since that seems to work.
|
||||
*
|
||||
* //TODO
|
||||
*
|
||||
* This probably needs some throttling mechanism but
|
||||
* it's wide open right now. This applies to both
|
||||
* number of requests and data volume.
|
||||
*
|
||||
* Linden puts all kinds of header fields in the requests.
|
||||
* Not doing any of that:
|
||||
* User-Agent
|
||||
* X-SecondLife-Shard
|
||||
* X-SecondLife-Object-Name
|
||||
* X-SecondLife-Object-Key
|
||||
* X-SecondLife-Region
|
||||
* X-SecondLife-Local-Position
|
||||
* X-SecondLife-Local-Velocity
|
||||
* X-SecondLife-Local-Rotation
|
||||
* X-SecondLife-Owner-Name
|
||||
* X-SecondLife-Owner-Key
|
||||
*
|
||||
* HTTPS support
|
||||
*
|
||||
* Configurable timeout?
|
||||
* Configurable max response size?
|
||||
* Configurable
|
||||
*
|
||||
* **************************************************/
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
|
||||
{
|
||||
public class HttpRequestModule : IRegionModule, IHttpRequestModule
|
||||
{
|
||||
private object HttpListLock = new object();
|
||||
private int httpTimeout = 30000;
|
||||
private string m_name = "HttpScriptRequests";
|
||||
|
||||
private string m_proxyurl = "";
|
||||
private string m_proxyexcepts = "";
|
||||
|
||||
// <request id, HttpRequestClass>
|
||||
private Dictionary<UUID, HttpRequestClass> m_pendingRequests;
|
||||
private Scene m_scene;
|
||||
// private Queue<HttpRequestClass> rpcQueue = new Queue<HttpRequestClass>();
|
||||
|
||||
public HttpRequestModule()
|
||||
{
|
||||
}
|
||||
|
||||
#region IHttpRequestModule Members
|
||||
|
||||
public UUID MakeHttpRequest(string url, string parameters, string body)
|
||||
{
|
||||
return UUID.Zero;
|
||||
}
|
||||
|
||||
public UUID StartHttpRequest(uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body)
|
||||
{
|
||||
UUID reqID = UUID.Random();
|
||||
HttpRequestClass htc = new HttpRequestClass();
|
||||
|
||||
// Partial implementation: support for parameter flags needed
|
||||
// see http://wiki.secondlife.com/wiki/LlHTTPRequest
|
||||
//
|
||||
// Parameters are expected in {key, value, ... , key, value}
|
||||
if (parameters != null)
|
||||
{
|
||||
string[] parms = parameters.ToArray();
|
||||
for (int i = 0; i < parms.Length; i += 2)
|
||||
{
|
||||
switch (Int32.Parse(parms[i]))
|
||||
{
|
||||
case (int)HttpRequestConstants.HTTP_METHOD:
|
||||
|
||||
htc.HttpMethod = parms[i + 1];
|
||||
break;
|
||||
|
||||
case (int)HttpRequestConstants.HTTP_MIMETYPE:
|
||||
|
||||
htc.HttpMIMEType = parms[i + 1];
|
||||
break;
|
||||
|
||||
case (int)HttpRequestConstants.HTTP_BODY_MAXLENGTH:
|
||||
|
||||
// TODO implement me
|
||||
break;
|
||||
|
||||
case (int)HttpRequestConstants.HTTP_VERIFY_CERT:
|
||||
|
||||
// TODO implement me
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
htc.LocalID = localID;
|
||||
htc.ItemID = itemID;
|
||||
htc.Url = url;
|
||||
htc.ReqID = reqID;
|
||||
htc.HttpTimeout = httpTimeout;
|
||||
htc.OutboundBody = body;
|
||||
htc.ResponseHeaders = headers;
|
||||
htc.proxyurl = m_proxyurl;
|
||||
htc.proxyexcepts = m_proxyexcepts;
|
||||
|
||||
lock (HttpListLock)
|
||||
{
|
||||
m_pendingRequests.Add(reqID, htc);
|
||||
}
|
||||
|
||||
htc.Process();
|
||||
|
||||
return reqID;
|
||||
}
|
||||
|
||||
public void StopHttpRequest(uint m_localID, UUID m_itemID)
|
||||
{
|
||||
if (m_pendingRequests != null)
|
||||
{
|
||||
lock (HttpListLock)
|
||||
{
|
||||
HttpRequestClass tmpReq;
|
||||
if (m_pendingRequests.TryGetValue(m_itemID, out tmpReq))
|
||||
{
|
||||
tmpReq.Stop();
|
||||
m_pendingRequests.Remove(m_itemID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* Not sure how important ordering is is here - the next first
|
||||
* one completed in the list is returned, based soley on its list
|
||||
* position, not the order in which the request was started or
|
||||
* finsihed. I thought about setting up a queue for this, but
|
||||
* it will need some refactoring and this works 'enough' right now
|
||||
*/
|
||||
|
||||
public IServiceRequest GetNextCompletedRequest()
|
||||
{
|
||||
lock (HttpListLock)
|
||||
{
|
||||
foreach (UUID luid in m_pendingRequests.Keys)
|
||||
{
|
||||
HttpRequestClass tmpReq;
|
||||
|
||||
if (m_pendingRequests.TryGetValue(luid, out tmpReq))
|
||||
{
|
||||
if (tmpReq.Finished)
|
||||
{
|
||||
return tmpReq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void RemoveCompletedRequest(UUID id)
|
||||
{
|
||||
lock (HttpListLock)
|
||||
{
|
||||
HttpRequestClass tmpReq;
|
||||
if (m_pendingRequests.TryGetValue(id, out tmpReq))
|
||||
{
|
||||
tmpReq.Stop();
|
||||
tmpReq = null;
|
||||
m_pendingRequests.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
m_scene = scene;
|
||||
|
||||
m_scene.RegisterModuleInterface<IHttpRequestModule>(this);
|
||||
|
||||
m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
|
||||
m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
|
||||
|
||||
m_pendingRequests = new Dictionary<UUID, HttpRequestClass>();
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return m_name; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class HttpRequestClass: IServiceRequest
|
||||
{
|
||||
// Constants for parameters
|
||||
// public const int HTTP_BODY_MAXLENGTH = 2;
|
||||
// public const int HTTP_METHOD = 0;
|
||||
// public const int HTTP_MIMETYPE = 1;
|
||||
// public const int HTTP_VERIFY_CERT = 3;
|
||||
private bool _finished;
|
||||
public bool Finished
|
||||
{
|
||||
get { return _finished; }
|
||||
}
|
||||
// public int HttpBodyMaxLen = 2048; // not implemented
|
||||
|
||||
// Parameter members and default values
|
||||
public string HttpMethod = "GET";
|
||||
public string HttpMIMEType = "text/plain;charset=utf-8";
|
||||
public int HttpTimeout;
|
||||
// public bool HttpVerifyCert = true; // not implemented
|
||||
private Thread httpThread;
|
||||
|
||||
// Request info
|
||||
private UUID _itemID;
|
||||
public UUID ItemID
|
||||
{
|
||||
get { return _itemID; }
|
||||
set { _itemID = value; }
|
||||
}
|
||||
private uint _localID;
|
||||
public uint LocalID
|
||||
{
|
||||
get { return _localID; }
|
||||
set { _localID = value; }
|
||||
}
|
||||
public DateTime Next;
|
||||
public string proxyurl;
|
||||
public string proxyexcepts;
|
||||
public string OutboundBody;
|
||||
private UUID _reqID;
|
||||
public UUID ReqID
|
||||
{
|
||||
get { return _reqID; }
|
||||
set { _reqID = value; }
|
||||
}
|
||||
public HttpWebRequest Request;
|
||||
public string ResponseBody;
|
||||
public List<string> ResponseMetadata;
|
||||
public Dictionary<string, string> ResponseHeaders;
|
||||
public int Status;
|
||||
public string Url;
|
||||
|
||||
public void Process()
|
||||
{
|
||||
httpThread = new Thread(SendRequest);
|
||||
httpThread.Name = "HttpRequestThread";
|
||||
httpThread.Priority = ThreadPriority.BelowNormal;
|
||||
httpThread.IsBackground = true;
|
||||
_finished = false;
|
||||
httpThread.Start();
|
||||
ThreadTracker.Add(httpThread);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: More work on the response codes. Right now
|
||||
* returning 200 for success or 499 for exception
|
||||
*/
|
||||
|
||||
public void SendRequest()
|
||||
{
|
||||
HttpWebResponse response = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
byte[] buf = new byte[8192];
|
||||
string tempString = null;
|
||||
int count = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Request = (HttpWebRequest) WebRequest.Create(Url);
|
||||
Request.Method = HttpMethod;
|
||||
Request.ContentType = HttpMIMEType;
|
||||
|
||||
if (proxyurl != null && proxyurl.Length > 0)
|
||||
{
|
||||
if (proxyexcepts != null && proxyexcepts.Length > 0)
|
||||
{
|
||||
string[] elist = proxyexcepts.Split(';');
|
||||
Request.Proxy = new WebProxy(proxyurl, true, elist);
|
||||
}
|
||||
else
|
||||
{
|
||||
Request.Proxy = new WebProxy(proxyurl, true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, string> entry in ResponseHeaders)
|
||||
Request.Headers[entry.Key] = entry.Value;
|
||||
|
||||
// Encode outbound data
|
||||
if (OutboundBody.Length > 0)
|
||||
{
|
||||
byte[] data = Encoding.UTF8.GetBytes(OutboundBody);
|
||||
|
||||
Request.ContentLength = data.Length;
|
||||
Stream bstream = Request.GetRequestStream();
|
||||
bstream.Write(data, 0, data.Length);
|
||||
bstream.Close();
|
||||
}
|
||||
|
||||
Request.Timeout = HttpTimeout;
|
||||
// execute the request
|
||||
response = (HttpWebResponse) Request.GetResponse();
|
||||
|
||||
Stream resStream = response.GetResponseStream();
|
||||
|
||||
do
|
||||
{
|
||||
// fill the buffer with data
|
||||
count = resStream.Read(buf, 0, buf.Length);
|
||||
|
||||
// make sure we read some data
|
||||
if (count != 0)
|
||||
{
|
||||
// translate from bytes to ASCII text
|
||||
tempString = Encoding.UTF8.GetString(buf, 0, count);
|
||||
|
||||
// continue building the string
|
||||
sb.Append(tempString);
|
||||
}
|
||||
} while (count > 0); // any more data to read?
|
||||
|
||||
ResponseBody = sb.ToString();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is WebException && ((WebException)e).Status == WebExceptionStatus.ProtocolError)
|
||||
{
|
||||
HttpWebResponse webRsp = (HttpWebResponse)((WebException)e).Response;
|
||||
Status = (int)webRsp.StatusCode;
|
||||
ResponseBody = webRsp.StatusDescription;
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = (int)OSHttpStatusCode.ClientErrorJoker;
|
||||
ResponseBody = e.Message;
|
||||
}
|
||||
|
||||
_finished = true;
|
||||
return;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (response != null)
|
||||
response.Close();
|
||||
}
|
||||
|
||||
Status = (int)OSHttpStatusCode.SuccessOk;
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
httpThread.Abort();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* 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 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 System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using Nini.Config;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
|
||||
{
|
||||
public class LoadImageURLModule : IRegionModule, IDynamicTextureRender
|
||||
{
|
||||
private string m_name = "LoadImageURL";
|
||||
private Scene m_scene;
|
||||
private IDynamicTextureManager m_textureManager;
|
||||
|
||||
private string m_proxyurl = "";
|
||||
private string m_proxyexcepts = "";
|
||||
|
||||
#region IDynamicTextureRender Members
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
public string GetContentType()
|
||||
{
|
||||
return ("image");
|
||||
}
|
||||
|
||||
public bool SupportsAsynchronous()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] ConvertUrl(string url, string extraParams)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] ConvertStream(Stream data, string extraParams)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
|
||||
{
|
||||
MakeHttpRequest(url, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
if (m_scene == null)
|
||||
{
|
||||
m_scene = scene;
|
||||
}
|
||||
|
||||
m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
|
||||
m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
m_textureManager = m_scene.RequestModuleInterface<IDynamicTextureManager>();
|
||||
if (m_textureManager != null)
|
||||
{
|
||||
m_textureManager.RegisterRender(GetContentType(), this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return m_name; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void MakeHttpRequest(string url, UUID requestID)
|
||||
{
|
||||
WebRequest request = HttpWebRequest.Create(url);
|
||||
|
||||
if (m_proxyurl != null && m_proxyurl.Length > 0)
|
||||
{
|
||||
if (m_proxyexcepts != null && m_proxyexcepts.Length > 0)
|
||||
{
|
||||
string[] elist = m_proxyexcepts.Split(';');
|
||||
request.Proxy = new WebProxy(m_proxyurl, true, elist);
|
||||
}
|
||||
else
|
||||
{
|
||||
request.Proxy = new WebProxy(m_proxyurl, true);
|
||||
}
|
||||
}
|
||||
|
||||
RequestState state = new RequestState((HttpWebRequest) request, requestID);
|
||||
// IAsyncResult result = request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state);
|
||||
request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state);
|
||||
|
||||
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
|
||||
state.TimeOfRequest = (int) t.TotalSeconds;
|
||||
}
|
||||
|
||||
private void HttpRequestReturn(IAsyncResult result)
|
||||
{
|
||||
RequestState state = (RequestState) result.AsyncState;
|
||||
WebRequest request = (WebRequest) state.Request;
|
||||
try
|
||||
{
|
||||
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
Bitmap image = new Bitmap(response.GetResponseStream());
|
||||
Size newsize;
|
||||
|
||||
// TODO: make this a bit less hard coded
|
||||
if ((image.Height < 64) && (image.Width < 64))
|
||||
{
|
||||
newsize = new Size(32, 32);
|
||||
}
|
||||
else if ((image.Height < 128) && (image.Width < 128))
|
||||
{
|
||||
newsize = new Size(64, 64);
|
||||
}
|
||||
else if ((image.Height < 256) && (image.Width < 256))
|
||||
{
|
||||
newsize = new Size(128, 128);
|
||||
}
|
||||
else if ((image.Height < 512 && image.Width < 512))
|
||||
{
|
||||
newsize = new Size(256, 256);
|
||||
}
|
||||
else if ((image.Height < 1024 && image.Width < 1024))
|
||||
{
|
||||
newsize = new Size(512, 512);
|
||||
}
|
||||
else
|
||||
{
|
||||
newsize = new Size(1024, 1024);
|
||||
}
|
||||
|
||||
Bitmap resize = new Bitmap(image, newsize);
|
||||
byte[] imageJ2000 = new byte[0];
|
||||
|
||||
try
|
||||
{
|
||||
imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console.WriteLine(
|
||||
"[LOADIMAGEURLMODULE]: OpenJpeg Encode Failed. Empty byte data returned!");
|
||||
}
|
||||
|
||||
m_textureManager.ReturnData(state.RequestID, imageJ2000);
|
||||
}
|
||||
}
|
||||
catch (WebException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#region Nested type: RequestState
|
||||
|
||||
public class RequestState
|
||||
{
|
||||
public HttpWebRequest Request = null;
|
||||
public UUID RequestID = UUID.Zero;
|
||||
public int TimeOfRequest = 0;
|
||||
|
||||
public RequestState(HttpWebRequest request, UUID requestID)
|
||||
{
|
||||
Request = request;
|
||||
RequestID = requestID;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* 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 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 System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using Nini.Config;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
//using Cairo;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.VectorRender
|
||||
{
|
||||
public class VectorRenderModule : IRegionModule, IDynamicTextureRender
|
||||
{
|
||||
private string m_name = "VectorRenderModule";
|
||||
private Scene m_scene;
|
||||
private IDynamicTextureManager m_textureManager;
|
||||
|
||||
public VectorRenderModule()
|
||||
{
|
||||
}
|
||||
|
||||
#region IDynamicTextureRender Members
|
||||
|
||||
public string GetContentType()
|
||||
{
|
||||
return ("vector");
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
public bool SupportsAsynchronous()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] ConvertUrl(string url, string extraParams)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] ConvertStream(Stream data, string extraParams)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
|
||||
{
|
||||
Draw(bodyData, id, extraParams);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
if (m_scene == null)
|
||||
{
|
||||
m_scene = scene;
|
||||
}
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
m_textureManager = m_scene.RequestModuleInterface<IDynamicTextureManager>();
|
||||
if (m_textureManager != null)
|
||||
{
|
||||
m_textureManager.RegisterRender(GetContentType(), this);
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return m_name; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void Draw(string data, UUID id, string extraParams)
|
||||
{
|
||||
// We need to cater for old scripts that didnt use extraParams neatly, they use either an integer size which represents both width and height, or setalpha
|
||||
// we will now support multiple comma seperated params in the form width:256,height:512,alpha:255
|
||||
int width = 256;
|
||||
int height = 256;
|
||||
int alpha = 255; // 0 is transparent
|
||||
|
||||
char[] paramDelimiter = { ',' };
|
||||
char[] nvpDelimiter = { ':' };
|
||||
|
||||
extraParams = extraParams.Trim();
|
||||
extraParams = extraParams.ToLower();
|
||||
|
||||
string[] nvps = extraParams.Split(paramDelimiter);
|
||||
|
||||
int temp = -1;
|
||||
foreach (string pair in nvps)
|
||||
{
|
||||
string[] nvp = pair.Split(nvpDelimiter);
|
||||
string name = "";
|
||||
string value = "";
|
||||
|
||||
if (nvp[0] != null)
|
||||
{
|
||||
name = nvp[0].Trim();
|
||||
}
|
||||
|
||||
if (nvp.Length == 2)
|
||||
{
|
||||
value = nvp[1].Trim();
|
||||
}
|
||||
|
||||
switch (name)
|
||||
{
|
||||
case "width":
|
||||
temp = parseIntParam(value);
|
||||
if (temp != -1)
|
||||
{
|
||||
if (temp < 1)
|
||||
{
|
||||
width = 1;
|
||||
}
|
||||
else if (temp > 2048)
|
||||
{
|
||||
width = 2048;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = temp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "height":
|
||||
temp = parseIntParam(value);
|
||||
if (temp != -1)
|
||||
{
|
||||
if (temp < 1)
|
||||
{
|
||||
height = 1;
|
||||
}
|
||||
else if (temp > 2048)
|
||||
{
|
||||
height = 2048;
|
||||
}
|
||||
else
|
||||
{
|
||||
height = temp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "alpha":
|
||||
temp = parseIntParam(value);
|
||||
if (temp != -1)
|
||||
{
|
||||
if (temp < 0)
|
||||
{
|
||||
alpha = 0;
|
||||
}
|
||||
else if (temp > 255)
|
||||
{
|
||||
alpha = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha = temp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "":
|
||||
// blank string has been passed do nothing just use defaults
|
||||
break;
|
||||
default: // this is all for backwards compat, all a bit ugly hopfully can be removed in future
|
||||
// could be either set alpha or just an int
|
||||
if (name == "setalpha")
|
||||
{
|
||||
alpha = 0; // set the texture to have transparent background (maintains backwards compat)
|
||||
}
|
||||
else
|
||||
{
|
||||
// this function used to accept an int on its own that represented both
|
||||
// width and height, this is to maintain backwards compat, could be removed
|
||||
// but would break existing scripts
|
||||
temp = parseIntParam(name);
|
||||
if (temp != -1)
|
||||
{
|
||||
if (temp > 1024)
|
||||
temp = 1024;
|
||||
|
||||
if (temp < 128)
|
||||
temp = 128;
|
||||
|
||||
width = temp;
|
||||
height = temp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
|
||||
|
||||
Graphics graph = Graphics.FromImage(bitmap);
|
||||
|
||||
// this is really just to save people filling the
|
||||
// background white in their scripts, only do when fully opaque
|
||||
if (alpha == 255)
|
||||
{
|
||||
graph.FillRectangle(new SolidBrush(Color.White), 0, 0, width, height);
|
||||
}
|
||||
|
||||
for (int w = 0; w < bitmap.Width; w++)
|
||||
{
|
||||
for (int h = 0; h < bitmap.Height; h++)
|
||||
{
|
||||
bitmap.SetPixel(w, h, Color.FromArgb(alpha, bitmap.GetPixel(w, h)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GDIDraw(data, graph);
|
||||
|
||||
byte[] imageJ2000 = new byte[0];
|
||||
|
||||
try
|
||||
{
|
||||
imageJ2000 = OpenJPEG.EncodeFromImage(bitmap, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Console.WriteLine(
|
||||
"[VECTORRENDERMODULE]: OpenJpeg Encode Failed. Empty byte data returned!");
|
||||
}
|
||||
m_textureManager.ReturnData(id, imageJ2000);
|
||||
}
|
||||
|
||||
private int parseIntParam(string strInt)
|
||||
{
|
||||
int parsed;
|
||||
try
|
||||
{
|
||||
parsed = Convert.ToInt32(strInt);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//Ckrinke: Add a WriteLine to remove the warning about 'e' defined but not used
|
||||
// Console.WriteLine("Problem with Draw. Please verify parameters." + e.ToString());
|
||||
parsed = -1;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
private void CairoDraw(string data, System.Drawing.Graphics graph)
|
||||
{
|
||||
using (Win32Surface draw = new Win32Surface(graph.GetHdc()))
|
||||
{
|
||||
Context contex = new Context(draw);
|
||||
|
||||
contex.Antialias = Antialias.None; //fastest method but low quality
|
||||
contex.LineWidth = 7;
|
||||
char[] lineDelimiter = { ';' };
|
||||
char[] partsDelimiter = { ',' };
|
||||
string[] lines = data.Split(lineDelimiter);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
string nextLine = line.Trim();
|
||||
|
||||
if (nextLine.StartsWith("MoveTO"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, ref x, ref y);
|
||||
contex.MoveTo(x, y);
|
||||
}
|
||||
else if (nextLine.StartsWith("LineTo"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, ref x, ref y);
|
||||
contex.LineTo(x, y);
|
||||
contex.Stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
graph.ReleaseHdc();
|
||||
}
|
||||
*/
|
||||
|
||||
private void GDIDraw(string data, Graphics graph)
|
||||
{
|
||||
Point startPoint = new Point(0, 0);
|
||||
Point endPoint = new Point(0, 0);
|
||||
Pen drawPen = new Pen(Color.Black, 7);
|
||||
string fontName = "Arial";
|
||||
float fontSize = 14;
|
||||
Font myFont = new Font(fontName, fontSize);
|
||||
SolidBrush myBrush = new SolidBrush(Color.Black);
|
||||
char[] lineDelimiter = {';'};
|
||||
char[] partsDelimiter = {','};
|
||||
string[] lines = data.Split(lineDelimiter);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
string nextLine = line.Trim();
|
||||
//replace with switch, or even better, do some proper parsing
|
||||
if (nextLine.StartsWith("MoveTo"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
|
||||
startPoint.X = (int) x;
|
||||
startPoint.Y = (int) y;
|
||||
}
|
||||
else if (nextLine.StartsWith("LineTo"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, 6, ref x, ref y);
|
||||
endPoint.X = (int) x;
|
||||
endPoint.Y = (int) y;
|
||||
graph.DrawLine(drawPen, startPoint, endPoint);
|
||||
startPoint.X = endPoint.X;
|
||||
startPoint.Y = endPoint.Y;
|
||||
}
|
||||
else if (nextLine.StartsWith("Text"))
|
||||
{
|
||||
nextLine = nextLine.Remove(0, 4);
|
||||
nextLine = nextLine.Trim();
|
||||
graph.DrawString(nextLine, myFont, myBrush, startPoint);
|
||||
}
|
||||
else if (nextLine.StartsWith("Image"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, 5, ref x, ref y);
|
||||
endPoint.X = (int) x;
|
||||
endPoint.Y = (int) y;
|
||||
Image image = ImageHttpRequest(nextLine);
|
||||
graph.DrawImage(image, (float) startPoint.X, (float) startPoint.Y, x, y);
|
||||
startPoint.X += endPoint.X;
|
||||
startPoint.Y += endPoint.Y;
|
||||
}
|
||||
else if (nextLine.StartsWith("Rectangle"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, 9, ref x, ref y);
|
||||
endPoint.X = (int) x;
|
||||
endPoint.Y = (int) y;
|
||||
graph.DrawRectangle(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
|
||||
startPoint.X += endPoint.X;
|
||||
startPoint.Y += endPoint.Y;
|
||||
}
|
||||
else if (nextLine.StartsWith("FillRectangle"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, 13, ref x, ref y);
|
||||
endPoint.X = (int) x;
|
||||
endPoint.Y = (int) y;
|
||||
graph.FillRectangle(myBrush, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
|
||||
startPoint.X += endPoint.X;
|
||||
startPoint.Y += endPoint.Y;
|
||||
}
|
||||
else if (nextLine.StartsWith("Ellipse"))
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
GetParams(partsDelimiter, ref nextLine, 7, ref x, ref y);
|
||||
endPoint.X = (int) x;
|
||||
endPoint.Y = (int) y;
|
||||
graph.DrawEllipse(drawPen, startPoint.X, startPoint.Y, endPoint.X, endPoint.Y);
|
||||
startPoint.X += endPoint.X;
|
||||
startPoint.Y += endPoint.Y;
|
||||
}
|
||||
else if (nextLine.StartsWith("FontSize"))
|
||||
{
|
||||
nextLine = nextLine.Remove(0, 8);
|
||||
nextLine = nextLine.Trim();
|
||||
fontSize = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
|
||||
myFont = new Font(fontName, fontSize);
|
||||
}
|
||||
else if (nextLine.StartsWith("FontName"))
|
||||
{
|
||||
nextLine = nextLine.Remove(0, 8);
|
||||
fontName = nextLine.Trim();
|
||||
myFont = new Font(fontName, fontSize);
|
||||
}
|
||||
else if (nextLine.StartsWith("PenSize"))
|
||||
{
|
||||
nextLine = nextLine.Remove(0, 7);
|
||||
nextLine = nextLine.Trim();
|
||||
float size = Convert.ToSingle(nextLine, CultureInfo.InvariantCulture);
|
||||
drawPen.Width = size;
|
||||
}
|
||||
else if (nextLine.StartsWith("PenColour"))
|
||||
{
|
||||
nextLine = nextLine.Remove(0, 9);
|
||||
nextLine = nextLine.Trim();
|
||||
int hex = 0;
|
||||
|
||||
Color newColour;
|
||||
if (Int32.TryParse(nextLine, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hex))
|
||||
{
|
||||
newColour = Color.FromArgb(hex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this doesn't fail, it just returns black if nothing is found
|
||||
newColour = Color.FromName(nextLine);
|
||||
}
|
||||
|
||||
myBrush.Color = newColour;
|
||||
drawPen.Color = newColour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetParams(char[] partsDelimiter, ref string line, int startLength, ref float x, ref float y)
|
||||
{
|
||||
line = line.Remove(0, startLength);
|
||||
string[] parts = line.Split(partsDelimiter);
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
string xVal = parts[0].Trim();
|
||||
string yVal = parts[1].Trim();
|
||||
x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
|
||||
y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);
|
||||
}
|
||||
else if (parts.Length > 2)
|
||||
{
|
||||
string xVal = parts[0].Trim();
|
||||
string yVal = parts[1].Trim();
|
||||
x = Convert.ToSingle(xVal, CultureInfo.InvariantCulture);
|
||||
y = Convert.ToSingle(yVal, CultureInfo.InvariantCulture);
|
||||
|
||||
line = "";
|
||||
for (int i = 2; i < parts.Length; i++)
|
||||
{
|
||||
line = line + parts[i].Trim();
|
||||
line = line + " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap ImageHttpRequest(string url)
|
||||
{
|
||||
WebRequest request = HttpWebRequest.Create(url);
|
||||
//Ckrinke: Comment out for now as 'str' is unused. Bring it back into play later when it is used.
|
||||
//Ckrinke Stream str = null;
|
||||
HttpWebResponse response = (HttpWebResponse) (request).GetResponse();
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
Bitmap image = new Bitmap(response.GetResponseStream());
|
||||
return image;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* 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 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using OpenMetaverse;
|
||||
using Nini.Config;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
// using log4net;
|
||||
// using System.Reflection;
|
||||
|
||||
|
||||
/*****************************************************
|
||||
*
|
||||
* WorldCommModule
|
||||
*
|
||||
*
|
||||
* Holding place for world comms - basically llListen
|
||||
* function implementation.
|
||||
*
|
||||
* lLListen(integer channel, string name, key id, string msg)
|
||||
* The name, id, and msg arguments specify the filtering
|
||||
* criteria. You can pass the empty string
|
||||
* (or NULL_KEY for id) for these to set a completely
|
||||
* open filter; this causes the listen() event handler to be
|
||||
* invoked for all chat on the channel. To listen only
|
||||
* for chat spoken by a specific object or avatar,
|
||||
* specify the name and/or id arguments. To listen
|
||||
* only for a specific command, specify the
|
||||
* (case-sensitive) msg argument. If msg is not empty,
|
||||
* listener will only hear strings which are exactly equal
|
||||
* to msg. You can also use all the arguments to establish
|
||||
* the most restrictive filtering criteria.
|
||||
*
|
||||
* It might be useful for each listener to maintain a message
|
||||
* digest, with a list of recent messages by UUID. This can
|
||||
* be used to prevent in-world repeater loops. However, the
|
||||
* linden functions do not have this capability, so for now
|
||||
* thats the way it works.
|
||||
* Instead it blocks messages originating from the same prim.
|
||||
* (not Object!)
|
||||
*
|
||||
* For LSL compliance, note the following:
|
||||
* (Tested again 1.21.1 on May 2, 2008)
|
||||
* 1. 'id' has to be parsed into a UUID. None-UUID keys are
|
||||
* to be replaced by the ZeroID key. (Well, TryParse does
|
||||
* that for us.
|
||||
* 2. Setting up an listen event from the same script, with the
|
||||
* same filter settings (including step 1), returns the same
|
||||
* handle as the original filter.
|
||||
* 3. (TODO) handles should be script-local. Starting from 1.
|
||||
* Might be actually easier to map the global handle into
|
||||
* script-local handle in the ScriptEngine. Not sure if its
|
||||
* worth the effort tho.
|
||||
*
|
||||
* **************************************************/
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.WorldComm
|
||||
{
|
||||
public class WorldCommModule : IRegionModule, IWorldComm
|
||||
{
|
||||
// private static readonly ILog m_log =
|
||||
// LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private ListenerManager m_listenerManager;
|
||||
private Queue m_pending;
|
||||
private Queue m_pendingQ;
|
||||
private Scene m_scene;
|
||||
private int m_whisperdistance = 10;
|
||||
private int m_saydistance = 30;
|
||||
private int m_shoutdistance = 100;
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
// wrap this in a try block so that defaults will work if
|
||||
// the config file doesn't specify otherwise.
|
||||
int maxlisteners = 1000;
|
||||
int maxhandles = 64;
|
||||
try
|
||||
{
|
||||
m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance);
|
||||
m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance);
|
||||
m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance);
|
||||
maxlisteners = config.Configs["Chat"].GetInt("max_listens_per_region", maxlisteners);
|
||||
maxhandles = config.Configs["Chat"].GetInt("max_listens_per_script", maxhandles);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
if (maxlisteners < 1) maxlisteners = int.MaxValue;
|
||||
if (maxhandles < 1) maxhandles = int.MaxValue;
|
||||
|
||||
m_scene = scene;
|
||||
m_scene.RegisterModuleInterface<IWorldComm>(this);
|
||||
m_listenerManager = new ListenerManager(maxlisteners, maxhandles);
|
||||
m_scene.EventManager.OnChatFromClient += DeliverClientMessage;
|
||||
m_scene.EventManager.OnChatBroadcast += DeliverClientMessage;
|
||||
m_pendingQ = new Queue();
|
||||
m_pending = Queue.Synchronized(m_pendingQ);
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "WorldCommModule"; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IWorldComm Members
|
||||
|
||||
/// <summary>
|
||||
/// Create a listen event callback with the specified filters.
|
||||
/// The parameters localID,itemID are needed to uniquely identify
|
||||
/// the script during 'peek' time. Parameter hostID is needed to
|
||||
/// determine the position of the script.
|
||||
/// </summary>
|
||||
/// <param name="localID">localID of the script engine</param>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
/// <param name="hostID">UUID of the SceneObjectPart</param>
|
||||
/// <param name="channel">channel to listen on</param>
|
||||
/// <param name="name">name to filter on</param>
|
||||
/// <param name="id">key to filter on (user given, could be totally faked)</param>
|
||||
/// <param name="msg">msg to filter on</param>
|
||||
/// <returns>number of the scripts handle</returns>
|
||||
public int Listen(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg)
|
||||
{
|
||||
return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE).
|
||||
/// The handle used is returned from Listen()
|
||||
/// </summary>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
/// <param name="handle">handle returned by Listen()</param>
|
||||
/// <param name="active">temp. activate or deactivate the Listen()</param>
|
||||
public void ListenControl(UUID itemID, int handle, int active)
|
||||
{
|
||||
if (active == 1)
|
||||
m_listenerManager.Activate(itemID, handle);
|
||||
else if (active == 0)
|
||||
m_listenerManager.Dectivate(itemID, handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the listen event callback with handle
|
||||
/// </summary>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
/// <param name="handle">handle returned by Listen()</param>
|
||||
public void ListenRemove(UUID itemID, int handle)
|
||||
{
|
||||
m_listenerManager.Remove(itemID, handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all listen event callbacks for the given itemID
|
||||
/// (script engine)
|
||||
/// </summary>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
public void DeleteListener(UUID itemID)
|
||||
{
|
||||
m_listenerManager.DeleteListener(itemID);
|
||||
}
|
||||
|
||||
|
||||
protected static Vector3 CenterOfRegion = new Vector3(128, 128, 20);
|
||||
|
||||
public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg)
|
||||
{
|
||||
Vector3 position;
|
||||
SceneObjectPart source;
|
||||
ScenePresence avatar;
|
||||
|
||||
if ((source = m_scene.GetSceneObjectPart(id)) != null)
|
||||
position = source.AbsolutePosition;
|
||||
else if ((avatar = m_scene.GetScenePresence(id)) != null)
|
||||
position = avatar.AbsolutePosition;
|
||||
else if (ChatTypeEnum.Region == type)
|
||||
position = CenterOfRegion;
|
||||
else
|
||||
return;
|
||||
|
||||
DeliverMessage(type, channel, name, id, msg, position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method scans over the objects which registered an interest in listen callbacks.
|
||||
/// For everyone it finds, it checks if it fits the given filter. If it does, then
|
||||
/// enqueue the message for delivery to the objects listen event handler.
|
||||
/// The enqueued ListenerInfo no longer has filter values, but the actually trigged values.
|
||||
/// Objects that do an llSay have their messages delivered here and for nearby avatars,
|
||||
/// the OnChatFromClient event is used.
|
||||
/// </summary>
|
||||
/// <param name="type">type of delvery (whisper,say,shout or regionwide)</param>
|
||||
/// <param name="channel">channel to sent on</param>
|
||||
/// <param name="name">name of sender (object or avatar)</param>
|
||||
/// <param name="id">key of sender (object or avatar)</param>
|
||||
/// <param name="msg">msg to sent</param>
|
||||
public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg, Vector3 position)
|
||||
{
|
||||
// m_log.DebugFormat("[WorldComm] got[2] type {0}, channel {1}, name {2}, id {3}, msg {4}",
|
||||
// type, channel, name, id, msg);
|
||||
|
||||
// Determine which listen event filters match the given set of arguments, this results
|
||||
// in a limited set of listeners, each belonging a host. If the host is in range, add them
|
||||
// to the pending queue.
|
||||
foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg))
|
||||
{
|
||||
// Dont process if this message is from yourself!
|
||||
if (li.GetHostID().Equals(id))
|
||||
continue;
|
||||
|
||||
SceneObjectPart sPart = m_scene.GetSceneObjectPart(li.GetHostID());
|
||||
if (sPart == null)
|
||||
continue;
|
||||
|
||||
double dis = Util.GetDistanceTo(sPart.AbsolutePosition, position);
|
||||
switch (type)
|
||||
{
|
||||
case ChatTypeEnum.Whisper:
|
||||
if (dis < m_whisperdistance)
|
||||
{
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ChatTypeEnum.Say:
|
||||
if (dis < m_saydistance)
|
||||
{
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ChatTypeEnum.Shout:
|
||||
if (dis < m_shoutdistance)
|
||||
{
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ChatTypeEnum.Region:
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Are there any listen events ready to be dispatched?
|
||||
/// </summary>
|
||||
/// <returns>boolean indication</returns>
|
||||
public bool HasMessages()
|
||||
{
|
||||
return (m_pending.Count > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop the first availlable listen event from the queue
|
||||
/// </summary>
|
||||
/// <returns>ListenerInfo with filter filled in</returns>
|
||||
public IWorldCommListenerInfo GetNextMessage()
|
||||
{
|
||||
ListenerInfo li = null;
|
||||
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
li = (ListenerInfo) m_pending.Dequeue();
|
||||
}
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/********************************************************************
|
||||
*
|
||||
* Listener Stuff
|
||||
*
|
||||
* *****************************************************************/
|
||||
|
||||
private void DeliverClientMessage(Object sender, OSChatMessage e)
|
||||
{
|
||||
if (null != e.Sender)
|
||||
DeliverMessage(e.Type, e.Channel, e.Sender.Name, e.Sender.AgentId, e.Message, e.Position);
|
||||
else
|
||||
DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero, e.Message, e.Position);
|
||||
}
|
||||
|
||||
public Object[] GetSerializationData(UUID itemID)
|
||||
{
|
||||
return m_listenerManager.GetSerializationData(itemID);
|
||||
}
|
||||
|
||||
public void CreateFromData(uint localID, UUID itemID, UUID hostID,
|
||||
Object[] data)
|
||||
{
|
||||
m_listenerManager.AddFromData(localID, itemID, hostID, data);
|
||||
}
|
||||
}
|
||||
|
||||
public class ListenerManager
|
||||
{
|
||||
private Dictionary<int, List<ListenerInfo>> m_listeners = new Dictionary<int, List<ListenerInfo>>();
|
||||
private int m_maxlisteners;
|
||||
private int m_maxhandles;
|
||||
private int m_curlisteners;
|
||||
|
||||
public ListenerManager(int maxlisteners, int maxhandles)
|
||||
{
|
||||
m_maxlisteners = maxlisteners;
|
||||
m_maxhandles = maxhandles;
|
||||
m_curlisteners = 0;
|
||||
}
|
||||
|
||||
public int AddListener(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg)
|
||||
{
|
||||
// do we already have a match on this particular filter event?
|
||||
List<ListenerInfo> coll = GetListeners(itemID, channel, name, id, msg);
|
||||
|
||||
if (coll.Count > 0)
|
||||
{
|
||||
// special case, called with same filter settings, return same handle
|
||||
// (2008-05-02, tested on 1.21.1 server, still holds)
|
||||
return coll[0].GetHandle();
|
||||
}
|
||||
|
||||
if (m_curlisteners < m_maxlisteners)
|
||||
{
|
||||
int newHandle = GetNewHandle(itemID);
|
||||
|
||||
if (newHandle > 0)
|
||||
{
|
||||
ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg);
|
||||
|
||||
lock (m_listeners)
|
||||
{
|
||||
List<ListenerInfo> listeners;
|
||||
if (!m_listeners.TryGetValue(channel,out listeners))
|
||||
{
|
||||
listeners = new List<ListenerInfo>();
|
||||
m_listeners.Add(channel, listeners);
|
||||
}
|
||||
listeners.Add(li);
|
||||
m_curlisteners++;
|
||||
}
|
||||
|
||||
return newHandle;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Remove(UUID itemID, int handle)
|
||||
{
|
||||
lock (m_listeners)
|
||||
{
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID) && li.GetHandle().Equals(handle))
|
||||
{
|
||||
lis.Value.Remove(li);
|
||||
if (lis.Value.Count == 0)
|
||||
{
|
||||
m_listeners.Remove(lis.Key);
|
||||
m_curlisteners--;
|
||||
}
|
||||
// there should be only one, so we bail out early
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteListener(UUID itemID)
|
||||
{
|
||||
List<int> emptyChannels = new List<int>();
|
||||
List<ListenerInfo> removedListeners = new List<ListenerInfo>();
|
||||
|
||||
lock (m_listeners)
|
||||
{
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID))
|
||||
{
|
||||
// store them first, else the enumerated bails on us
|
||||
removedListeners.Add(li);
|
||||
}
|
||||
}
|
||||
foreach (ListenerInfo li in removedListeners)
|
||||
{
|
||||
lis.Value.Remove(li);
|
||||
m_curlisteners--;
|
||||
}
|
||||
removedListeners.Clear();
|
||||
if (lis.Value.Count == 0)
|
||||
{
|
||||
// again, store first, remove later
|
||||
emptyChannels.Add(lis.Key);
|
||||
}
|
||||
}
|
||||
foreach (int channel in emptyChannels)
|
||||
{
|
||||
m_listeners.Remove(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate(UUID itemID, int handle)
|
||||
{
|
||||
lock (m_listeners)
|
||||
{
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
|
||||
{
|
||||
li.Activate();
|
||||
// only one, bail out
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dectivate(UUID itemID, int handle)
|
||||
{
|
||||
lock (m_listeners)
|
||||
{
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
|
||||
{
|
||||
li.Deactivate();
|
||||
// only one, bail out
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// non-locked access, since its always called in the context of the lock
|
||||
private int GetNewHandle(UUID itemID)
|
||||
{
|
||||
List<int> handles = new List<int>();
|
||||
|
||||
// build a list of used keys for this specific itemID...
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID))
|
||||
handles.Add(li.GetHandle());
|
||||
}
|
||||
}
|
||||
|
||||
// Note: 0 is NOT a valid handle for llListen() to return
|
||||
for (int i = 1; i <= m_maxhandles; i++)
|
||||
{
|
||||
if (!handles.Contains(i))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Theres probably a more clever and efficient way to
|
||||
// do this, maybe with regex.
|
||||
// PM2008: Ha, one could even be smart and define a specialized Enumerator.
|
||||
public List<ListenerInfo> GetListeners(UUID itemID, int channel, string name, UUID id, string msg)
|
||||
{
|
||||
List<ListenerInfo> collection = new List<ListenerInfo>();
|
||||
|
||||
lock (m_listeners)
|
||||
{
|
||||
List<ListenerInfo> listeners;
|
||||
if (!m_listeners.TryGetValue(channel,out listeners))
|
||||
{
|
||||
return collection;
|
||||
}
|
||||
|
||||
foreach (ListenerInfo li in listeners)
|
||||
{
|
||||
if (!li.IsActive())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!itemID.Equals(UUID.Zero) && !li.GetItemID().Equals(itemID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (li.GetName().Length > 0 && !li.GetName().Equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!li.GetID().Equals(UUID.Zero) && !li.GetID().Equals(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
collection.Add(li);
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
public Object[] GetSerializationData(UUID itemID)
|
||||
{
|
||||
List<Object> data = new List<Object>();
|
||||
|
||||
foreach (List<ListenerInfo> list in m_listeners.Values)
|
||||
{
|
||||
foreach (ListenerInfo l in list)
|
||||
{
|
||||
if (l.GetItemID() == itemID)
|
||||
data.AddRange(l.GetSerializationData());
|
||||
}
|
||||
}
|
||||
return (Object[])data.ToArray();
|
||||
}
|
||||
|
||||
public void AddFromData(uint localID, UUID itemID, UUID hostID,
|
||||
Object[] data)
|
||||
{
|
||||
int idx = 0;
|
||||
Object[] item = new Object[6];
|
||||
|
||||
while (idx < data.Length)
|
||||
{
|
||||
Array.Copy(data, idx, item, 0, 6);
|
||||
|
||||
ListenerInfo info =
|
||||
ListenerInfo.FromData(localID, itemID, hostID, item);
|
||||
|
||||
if (!m_listeners.ContainsKey((int)item[2]))
|
||||
m_listeners.Add((int)item[2], new List<ListenerInfo>());
|
||||
m_listeners[(int)item[2]].Add(info);
|
||||
|
||||
idx+=6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ListenerInfo: IWorldCommListenerInfo
|
||||
{
|
||||
private bool m_active; // Listener is active or not
|
||||
private int m_handle; // Assigned handle of this listener
|
||||
private uint m_localID; // Local ID from script engine
|
||||
private UUID m_itemID; // ID of the host script engine
|
||||
private UUID m_hostID; // ID of the host/scene part
|
||||
private int m_channel; // Channel
|
||||
private UUID m_id; // ID to filter messages from
|
||||
private string m_name; // Object name to filter messages from
|
||||
private string m_message; // The message
|
||||
|
||||
public ListenerInfo(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message)
|
||||
{
|
||||
Initialise(handle, localID, ItemID, hostID, channel, name, id, message);
|
||||
}
|
||||
|
||||
public ListenerInfo(ListenerInfo li, string name, UUID id, string message)
|
||||
{
|
||||
Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message);
|
||||
}
|
||||
|
||||
private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name,
|
||||
UUID id, string message)
|
||||
{
|
||||
m_active = true;
|
||||
m_handle = handle;
|
||||
m_localID = localID;
|
||||
m_itemID = ItemID;
|
||||
m_hostID = hostID;
|
||||
m_channel = channel;
|
||||
m_name = name;
|
||||
m_id = id;
|
||||
m_message = message;
|
||||
}
|
||||
|
||||
public Object[] GetSerializationData()
|
||||
{
|
||||
Object[] data = new Object[6];
|
||||
|
||||
data[0] = m_active;
|
||||
data[1] = m_handle;
|
||||
data[2] = m_channel;
|
||||
data[3] = m_name;
|
||||
data[4] = m_id;
|
||||
data[5] = m_message;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data)
|
||||
{
|
||||
ListenerInfo linfo = new ListenerInfo((int)data[1], localID,
|
||||
ItemID, hostID, (int)data[2], (string)data[3],
|
||||
(UUID)data[4], (string)data[5]);
|
||||
linfo.m_active=(bool)data[0];
|
||||
|
||||
return linfo;
|
||||
}
|
||||
|
||||
public UUID GetItemID()
|
||||
{
|
||||
return m_itemID;
|
||||
}
|
||||
|
||||
public UUID GetHostID()
|
||||
{
|
||||
return m_hostID;
|
||||
}
|
||||
|
||||
public int GetChannel()
|
||||
{
|
||||
return m_channel;
|
||||
}
|
||||
|
||||
public uint GetLocalID()
|
||||
{
|
||||
return m_localID;
|
||||
}
|
||||
|
||||
public int GetHandle()
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
public string GetMessage()
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
public bool IsActive()
|
||||
{
|
||||
return m_active;
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
m_active = false;
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
public UUID GetID()
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
726
OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
Normal file
726
OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
Normal file
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* 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 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using log4net;
|
||||
using Nini.Config;
|
||||
using Nwc.XmlRpc;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Servers;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
/*****************************************************
|
||||
*
|
||||
* XMLRPCModule
|
||||
*
|
||||
* Module for accepting incoming communications from
|
||||
* external XMLRPC client and calling a remote data
|
||||
* procedure for a registered data channel/prim.
|
||||
*
|
||||
*
|
||||
* 1. On module load, open a listener port
|
||||
* 2. Attach an XMLRPC handler
|
||||
* 3. When a request is received:
|
||||
* 3.1 Parse into components: channel key, int, string
|
||||
* 3.2 Look up registered channel listeners
|
||||
* 3.3 Call the channel (prim) remote data method
|
||||
* 3.4 Capture the response (llRemoteDataReply)
|
||||
* 3.5 Return response to client caller
|
||||
* 3.6 If no response from llRemoteDataReply within
|
||||
* RemoteReplyScriptTimeout, generate script timeout fault
|
||||
*
|
||||
* Prims in script must:
|
||||
* 1. Open a remote data channel
|
||||
* 1.1 Generate a channel ID
|
||||
* 1.2 Register primid,channelid pair with module
|
||||
* 2. Implement the remote data procedure handler
|
||||
*
|
||||
* llOpenRemoteDataChannel
|
||||
* llRemoteDataReply
|
||||
* remote_data(integer type, key channel, key messageid, string sender, integer ival, string sval)
|
||||
* llCloseRemoteDataChannel
|
||||
*
|
||||
* **************************************************/
|
||||
|
||||
namespace OpenSim.Region.CoreModules.Scripting.XMLRPC
|
||||
{
|
||||
public class XMLRPCModule : IRegionModule, IXMLRPC
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private string m_name = "XMLRPCModule";
|
||||
|
||||
// <channel id, RPCChannelInfo>
|
||||
private Dictionary<UUID, RPCChannelInfo> m_openChannels;
|
||||
private Dictionary<UUID, SendRemoteDataRequest> m_pendingSRDResponses;
|
||||
private int m_remoteDataPort = 0;
|
||||
|
||||
private Dictionary<UUID, RPCRequestInfo> m_rpcPending;
|
||||
private Dictionary<UUID, RPCRequestInfo> m_rpcPendingResponses;
|
||||
private List<Scene> m_scenes = new List<Scene>();
|
||||
private int RemoteReplyScriptTimeout = 9000;
|
||||
private int RemoteReplyScriptWait = 300;
|
||||
private object XMLRPCListLock = new object();
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
// We need to create these early because the scripts might be calling
|
||||
// But since this gets called for every region, we need to make sure they
|
||||
// get called only one time (or we lose any open channels)
|
||||
if (null == m_openChannels)
|
||||
{
|
||||
m_openChannels = new Dictionary<UUID, RPCChannelInfo>();
|
||||
m_rpcPending = new Dictionary<UUID, RPCRequestInfo>();
|
||||
m_rpcPendingResponses = new Dictionary<UUID, RPCRequestInfo>();
|
||||
m_pendingSRDResponses = new Dictionary<UUID, SendRemoteDataRequest>();
|
||||
|
||||
try
|
||||
{
|
||||
m_remoteDataPort = config.Configs["Network"].GetInt("remoteDataPort", m_remoteDataPort);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_scenes.Contains(scene))
|
||||
{
|
||||
m_scenes.Add(scene);
|
||||
|
||||
scene.RegisterModuleInterface<IXMLRPC>(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
if (IsEnabled())
|
||||
{
|
||||
// Start http server
|
||||
// Attach xmlrpc handlers
|
||||
m_log.Info("[REMOTE_DATA]: " +
|
||||
"Starting XMLRPC Server on port " + m_remoteDataPort + " for llRemoteData commands.");
|
||||
BaseHttpServer httpServer = new BaseHttpServer((uint) m_remoteDataPort);
|
||||
httpServer.AddXmlRPCHandler("llRemoteData", XmlRpcRemoteData);
|
||||
httpServer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return m_name; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IXMLRPC Members
|
||||
|
||||
public bool IsEnabled()
|
||||
{
|
||||
return (m_remoteDataPort > 0);
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* OpenXMLRPCChannel
|
||||
*
|
||||
* Generate a UUID channel key and add it and
|
||||
* the prim id to dictionary <channelUUID, primUUID>
|
||||
*
|
||||
* A custom channel key can be proposed.
|
||||
* Otherwise, passing UUID.Zero will generate
|
||||
* and return a random channel
|
||||
*
|
||||
* First check if there is a channel assigned for
|
||||
* this itemID. If there is, then someone called
|
||||
* llOpenRemoteDataChannel twice. Just return the
|
||||
* original channel. Other option is to delete the
|
||||
* current channel and assign a new one.
|
||||
*
|
||||
* ********************************************/
|
||||
|
||||
public UUID OpenXMLRPCChannel(uint localID, UUID itemID, UUID channelID)
|
||||
{
|
||||
UUID newChannel = UUID.Zero;
|
||||
|
||||
// This should no longer happen, but the check is reasonable anyway
|
||||
if (null == m_openChannels)
|
||||
{
|
||||
m_log.Warn("[RemoteDataReply] Attempt to open channel before initialization is complete");
|
||||
return newChannel;
|
||||
}
|
||||
|
||||
//Is a dupe?
|
||||
foreach (RPCChannelInfo ci in m_openChannels.Values)
|
||||
{
|
||||
if (ci.GetItemID().Equals(itemID))
|
||||
{
|
||||
// return the original channel ID for this item
|
||||
newChannel = ci.GetChannelID();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newChannel == UUID.Zero)
|
||||
{
|
||||
newChannel = (channelID == UUID.Zero) ? UUID.Random() : channelID;
|
||||
RPCChannelInfo rpcChanInfo = new RPCChannelInfo(localID, itemID, newChannel);
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
m_openChannels.Add(newChannel, rpcChanInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return newChannel;
|
||||
}
|
||||
|
||||
// Delete channels based on itemID
|
||||
// for when a script is deleted
|
||||
public void DeleteChannels(UUID itemID)
|
||||
{
|
||||
if (m_openChannels != null)
|
||||
{
|
||||
ArrayList tmp = new ArrayList();
|
||||
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
foreach (RPCChannelInfo li in m_openChannels.Values)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID))
|
||||
{
|
||||
tmp.Add(itemID);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator tmpEnumerator = tmp.GetEnumerator();
|
||||
while (tmpEnumerator.MoveNext())
|
||||
m_openChannels.Remove((UUID) tmpEnumerator.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* Remote Data Reply
|
||||
*
|
||||
* Response to RPC message
|
||||
*
|
||||
*********************************************/
|
||||
|
||||
public void RemoteDataReply(string channel, string message_id, string sdata, int idata)
|
||||
{
|
||||
UUID message_key = new UUID(message_id);
|
||||
UUID channel_key = new UUID(channel);
|
||||
|
||||
RPCRequestInfo rpcInfo = null;
|
||||
|
||||
if (message_key == UUID.Zero)
|
||||
{
|
||||
foreach (RPCRequestInfo oneRpcInfo in m_rpcPendingResponses.Values)
|
||||
if (oneRpcInfo.GetChannelKey() == channel_key)
|
||||
rpcInfo = oneRpcInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rpcPendingResponses.TryGetValue(message_key, out rpcInfo);
|
||||
}
|
||||
|
||||
if (rpcInfo != null)
|
||||
{
|
||||
rpcInfo.SetStrRetval(sdata);
|
||||
rpcInfo.SetIntRetval(idata);
|
||||
rpcInfo.SetProcessed(true);
|
||||
m_rpcPendingResponses.Remove(message_key);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.Warn("[RemoteDataReply]: Channel or message_id not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************
|
||||
* CloseXMLRPCChannel
|
||||
*
|
||||
* Remove channel from dictionary
|
||||
*
|
||||
*********************************************/
|
||||
|
||||
public void CloseXMLRPCChannel(UUID channelKey)
|
||||
{
|
||||
if (m_openChannels.ContainsKey(channelKey))
|
||||
m_openChannels.Remove(channelKey);
|
||||
}
|
||||
|
||||
|
||||
public bool hasRequests()
|
||||
{
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
if (m_rpcPending != null)
|
||||
return (m_rpcPending.Count > 0);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public IXmlRpcRequestInfo GetNextCompletedRequest()
|
||||
{
|
||||
if (m_rpcPending != null)
|
||||
{
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
foreach (UUID luid in m_rpcPending.Keys)
|
||||
{
|
||||
RPCRequestInfo tmpReq;
|
||||
|
||||
if (m_rpcPending.TryGetValue(luid, out tmpReq))
|
||||
{
|
||||
if (!tmpReq.IsProcessed()) return tmpReq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void RemoveCompletedRequest(UUID id)
|
||||
{
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
RPCRequestInfo tmp;
|
||||
if (m_rpcPending.TryGetValue(id, out tmp))
|
||||
{
|
||||
m_rpcPending.Remove(id);
|
||||
m_rpcPendingResponses.Add(id, tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("UNABLE TO REMOVE COMPLETED REQUEST");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UUID SendRemoteData(uint localID, UUID itemID, string channel, string dest, int idata, string sdata)
|
||||
{
|
||||
SendRemoteDataRequest req = new SendRemoteDataRequest(
|
||||
localID, itemID, channel, dest, idata, sdata
|
||||
);
|
||||
m_pendingSRDResponses.Add(req.GetReqID(), req);
|
||||
req.Process();
|
||||
return req.ReqID;
|
||||
}
|
||||
|
||||
public IServiceRequest GetNextCompletedSRDRequest()
|
||||
{
|
||||
if (m_pendingSRDResponses != null)
|
||||
{
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
foreach (UUID luid in m_pendingSRDResponses.Keys)
|
||||
{
|
||||
SendRemoteDataRequest tmpReq;
|
||||
|
||||
if (m_pendingSRDResponses.TryGetValue(luid, out tmpReq))
|
||||
{
|
||||
if (tmpReq.Finished)
|
||||
return tmpReq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void RemoveCompletedSRDRequest(UUID id)
|
||||
{
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
SendRemoteDataRequest tmpReq;
|
||||
if (m_pendingSRDResponses.TryGetValue(id, out tmpReq))
|
||||
{
|
||||
m_pendingSRDResponses.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelSRDRequests(UUID itemID)
|
||||
{
|
||||
if (m_pendingSRDResponses != null)
|
||||
{
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
foreach (SendRemoteDataRequest li in m_pendingSRDResponses.Values)
|
||||
{
|
||||
if (li.ItemID.Equals(itemID))
|
||||
m_pendingSRDResponses.Remove(li.GetReqID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public XmlRpcResponse XmlRpcRemoteData(XmlRpcRequest request)
|
||||
{
|
||||
XmlRpcResponse response = new XmlRpcResponse();
|
||||
|
||||
Hashtable requestData = (Hashtable) request.Params[0];
|
||||
bool GoodXML = (requestData.Contains("Channel") && requestData.Contains("IntValue") &&
|
||||
requestData.Contains("StringValue"));
|
||||
|
||||
if (GoodXML)
|
||||
{
|
||||
UUID channel = new UUID((string) requestData["Channel"]);
|
||||
RPCChannelInfo rpcChanInfo;
|
||||
if (m_openChannels.TryGetValue(channel, out rpcChanInfo))
|
||||
{
|
||||
string intVal = Convert.ToInt32(requestData["IntValue"]).ToString();
|
||||
string strVal = (string) requestData["StringValue"];
|
||||
|
||||
RPCRequestInfo rpcInfo;
|
||||
|
||||
lock (XMLRPCListLock)
|
||||
{
|
||||
rpcInfo =
|
||||
new RPCRequestInfo(rpcChanInfo.GetLocalID(), rpcChanInfo.GetItemID(), channel, strVal,
|
||||
intVal);
|
||||
m_rpcPending.Add(rpcInfo.GetMessageID(), rpcInfo);
|
||||
}
|
||||
|
||||
int timeoutCtr = 0;
|
||||
|
||||
while (!rpcInfo.IsProcessed() && (timeoutCtr < RemoteReplyScriptTimeout))
|
||||
{
|
||||
Thread.Sleep(RemoteReplyScriptWait);
|
||||
timeoutCtr += RemoteReplyScriptWait;
|
||||
}
|
||||
if (rpcInfo.IsProcessed())
|
||||
{
|
||||
Hashtable param = new Hashtable();
|
||||
param["StringValue"] = rpcInfo.GetStrRetval();
|
||||
param["IntValue"] = rpcInfo.GetIntRetval();
|
||||
|
||||
ArrayList parameters = new ArrayList();
|
||||
parameters.Add(param);
|
||||
|
||||
response.Value = parameters;
|
||||
rpcInfo = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
response.SetFault(-1, "Script timeout");
|
||||
rpcInfo = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response.SetFault(-1, "Invalid channel");
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public class RPCRequestInfo: IXmlRpcRequestInfo
|
||||
{
|
||||
private UUID m_ChannelKey;
|
||||
private string m_IntVal;
|
||||
private UUID m_ItemID;
|
||||
private uint m_localID;
|
||||
private UUID m_MessageID;
|
||||
private bool m_processed;
|
||||
private int m_respInt;
|
||||
private string m_respStr;
|
||||
private string m_StrVal;
|
||||
|
||||
public RPCRequestInfo(uint localID, UUID itemID, UUID channelKey, string strVal, string intVal)
|
||||
{
|
||||
m_localID = localID;
|
||||
m_StrVal = strVal;
|
||||
m_IntVal = intVal;
|
||||
m_ItemID = itemID;
|
||||
m_ChannelKey = channelKey;
|
||||
m_MessageID = UUID.Random();
|
||||
m_processed = false;
|
||||
m_respStr = String.Empty;
|
||||
m_respInt = 0;
|
||||
}
|
||||
|
||||
public bool IsProcessed()
|
||||
{
|
||||
return m_processed;
|
||||
}
|
||||
|
||||
public UUID GetChannelKey()
|
||||
{
|
||||
return m_ChannelKey;
|
||||
}
|
||||
|
||||
public void SetProcessed(bool processed)
|
||||
{
|
||||
m_processed = processed;
|
||||
}
|
||||
|
||||
public void SetStrRetval(string resp)
|
||||
{
|
||||
m_respStr = resp;
|
||||
}
|
||||
|
||||
public string GetStrRetval()
|
||||
{
|
||||
return m_respStr;
|
||||
}
|
||||
|
||||
public void SetIntRetval(int resp)
|
||||
{
|
||||
m_respInt = resp;
|
||||
}
|
||||
|
||||
public int GetIntRetval()
|
||||
{
|
||||
return m_respInt;
|
||||
}
|
||||
|
||||
public uint GetLocalID()
|
||||
{
|
||||
return m_localID;
|
||||
}
|
||||
|
||||
public UUID GetItemID()
|
||||
{
|
||||
return m_ItemID;
|
||||
}
|
||||
|
||||
public string GetStrVal()
|
||||
{
|
||||
return m_StrVal;
|
||||
}
|
||||
|
||||
public int GetIntValue()
|
||||
{
|
||||
return int.Parse(m_IntVal);
|
||||
}
|
||||
|
||||
public UUID GetMessageID()
|
||||
{
|
||||
return m_MessageID;
|
||||
}
|
||||
}
|
||||
|
||||
public class RPCChannelInfo
|
||||
{
|
||||
private UUID m_ChannelKey;
|
||||
private UUID m_itemID;
|
||||
private uint m_localID;
|
||||
|
||||
public RPCChannelInfo(uint localID, UUID itemID, UUID channelID)
|
||||
{
|
||||
m_ChannelKey = channelID;
|
||||
m_localID = localID;
|
||||
m_itemID = itemID;
|
||||
}
|
||||
|
||||
public UUID GetItemID()
|
||||
{
|
||||
return m_itemID;
|
||||
}
|
||||
|
||||
public UUID GetChannelID()
|
||||
{
|
||||
return m_ChannelKey;
|
||||
}
|
||||
|
||||
public uint GetLocalID()
|
||||
{
|
||||
return m_localID;
|
||||
}
|
||||
}
|
||||
|
||||
public class SendRemoteDataRequest: IServiceRequest
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public string Channel;
|
||||
public string DestURL;
|
||||
private bool _finished;
|
||||
public bool Finished
|
||||
{
|
||||
get { return _finished; }
|
||||
set { _finished = value; }
|
||||
}
|
||||
private Thread httpThread;
|
||||
public int Idata;
|
||||
private UUID _itemID;
|
||||
public UUID ItemID
|
||||
{
|
||||
get { return _itemID; }
|
||||
set { _itemID = value; }
|
||||
}
|
||||
private uint _localID;
|
||||
public uint LocalID
|
||||
{
|
||||
get { return _localID; }
|
||||
set { _localID = value; }
|
||||
}
|
||||
private UUID _reqID;
|
||||
public UUID ReqID
|
||||
{
|
||||
get { return _reqID; }
|
||||
set { _reqID = value; }
|
||||
}
|
||||
public XmlRpcRequest Request;
|
||||
public int ResponseIdata;
|
||||
public string ResponseSdata;
|
||||
public string Sdata;
|
||||
|
||||
public SendRemoteDataRequest(uint localID, UUID itemID, string channel, string dest, int idata, string sdata)
|
||||
{
|
||||
this.Channel = channel;
|
||||
DestURL = dest;
|
||||
this.Idata = idata;
|
||||
this.Sdata = sdata;
|
||||
ItemID = itemID;
|
||||
LocalID = localID;
|
||||
|
||||
ReqID = UUID.Random();
|
||||
}
|
||||
|
||||
public void Process()
|
||||
{
|
||||
httpThread = new Thread(SendRequest);
|
||||
httpThread.Name = "HttpRequestThread";
|
||||
httpThread.Priority = ThreadPriority.BelowNormal;
|
||||
httpThread.IsBackground = true;
|
||||
_finished = false;
|
||||
httpThread.Start();
|
||||
ThreadTracker.Add(httpThread);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: More work on the response codes. Right now
|
||||
* returning 200 for success or 499 for exception
|
||||
*/
|
||||
|
||||
public void SendRequest()
|
||||
{
|
||||
Hashtable param = new Hashtable();
|
||||
|
||||
// Check if channel is an UUID
|
||||
// if not, use as method name
|
||||
UUID parseUID;
|
||||
string mName = "llRemoteData";
|
||||
if ((Channel != null) && (Channel != ""))
|
||||
if (!UUID.TryParse(Channel, out parseUID))
|
||||
mName = Channel;
|
||||
else
|
||||
param["Channel"] = Channel;
|
||||
|
||||
param["StringValue"] = Sdata;
|
||||
param["IntValue"] = Convert.ToString(Idata);
|
||||
|
||||
ArrayList parameters = new ArrayList();
|
||||
parameters.Add(param);
|
||||
XmlRpcRequest req = new XmlRpcRequest(mName, parameters);
|
||||
try
|
||||
{
|
||||
XmlRpcResponse resp = req.Send(DestURL, 30000);
|
||||
if (resp != null)
|
||||
{
|
||||
Hashtable respParms;
|
||||
if (resp.Value.GetType().Equals(typeof(System.Collections.Hashtable)))
|
||||
{
|
||||
respParms = (Hashtable) resp.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
ArrayList respData = (ArrayList) resp.Value;
|
||||
respParms = (Hashtable) respData[0];
|
||||
}
|
||||
if (respParms != null)
|
||||
{
|
||||
if (respParms.Contains("StringValue"))
|
||||
{
|
||||
Sdata = (string) respParms["StringValue"];
|
||||
}
|
||||
if (respParms.Contains("IntValue"))
|
||||
{
|
||||
Idata = Convert.ToInt32((string) respParms["IntValue"]);
|
||||
}
|
||||
if (respParms.Contains("faultString"))
|
||||
{
|
||||
Sdata = (string) respParms["faultString"];
|
||||
}
|
||||
if (respParms.Contains("faultCode"))
|
||||
{
|
||||
Idata = Convert.ToInt32(respParms["faultCode"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception we)
|
||||
{
|
||||
Sdata = we.Message;
|
||||
m_log.Warn("[SendRemoteDataRequest]: Request failed");
|
||||
m_log.Warn(we.StackTrace);
|
||||
}
|
||||
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
httpThread.Abort();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public UUID GetReqID()
|
||||
{
|
||||
return ReqID;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user