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:
Dr Scofield
2009-02-10 13:10:57 +00:00
parent a1393db7f0
commit 180be7de07
173 changed files with 320 additions and 390 deletions

View File

@@ -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
}
}

View 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;
}
}
}

View File

@@ -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)
{
}
}
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View 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 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;
}
}
}

View 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;
}
}
}