390 lines
12 KiB
C#
390 lines
12 KiB
C#
/*
|
|
* 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.IO;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using libsecondlife;
|
|
using OpenSim.Framework.Interfaces;
|
|
using OpenSim.Framework.Utilities;
|
|
using OpenSim.Region.Environment.Interfaces;
|
|
using OpenSim.Region.Environment.Scenes;
|
|
using OpenSim.Framework.Servers;
|
|
using Nwc.XmlRpc;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Nini.Config;
|
|
|
|
/*****************************************************
|
|
*
|
|
* 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.Environment.Modules
|
|
{
|
|
public class XMLRPCModule : IRegionModule, IXMLRPC
|
|
{
|
|
private Scene m_scene;
|
|
private Queue<RPCRequestInfo> rpcQueue = new Queue<RPCRequestInfo>();
|
|
private object XMLRPCListLock = new object();
|
|
private string m_name = "XMLRPCModule";
|
|
private int RemoteReplyScriptWait = 100;
|
|
private int RemoteReplyScriptTimeout = 300;
|
|
|
|
// <channel id, RPCChannelInfo>
|
|
private Dictionary<LLUUID, RPCChannelInfo> m_openChannels;
|
|
|
|
// <channel id, RPCRequestInfo>
|
|
private Dictionary<LLUUID, RPCRequestInfo> m_pendingResponse;
|
|
|
|
public XMLRPCModule()
|
|
{
|
|
}
|
|
|
|
public void Initialise(Scene scene, IConfigSource config)
|
|
{
|
|
m_scene = scene;
|
|
|
|
m_scene.RegisterModuleInterface<IXMLRPC>(this);
|
|
|
|
m_openChannels = new Dictionary<LLUUID, RPCChannelInfo>();
|
|
m_pendingResponse = new Dictionary<LLUUID, RPCRequestInfo>();
|
|
|
|
// Start http server
|
|
// Attach xmlrpc handlers
|
|
BaseHttpServer httpServer = new BaseHttpServer(20800);
|
|
httpServer.AddXmlRPCHandler("llRemoteData", this.XmlRpcRemoteData);
|
|
httpServer.Start();
|
|
}
|
|
|
|
public void PostInitialise()
|
|
{
|
|
}
|
|
|
|
public void Close()
|
|
{
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get { return m_name; }
|
|
}
|
|
|
|
public bool IsSharedModule
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
/**********************************************
|
|
* OpenXMLRPCChannel
|
|
*
|
|
* Generate a LLUUID channel key and add it and
|
|
* the prim id to dictionary <channelUUID, primUUID>
|
|
*
|
|
* 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 LLUUID OpenXMLRPCChannel(uint localID, LLUUID itemID)
|
|
{
|
|
LLUUID channel = null;
|
|
|
|
//Is a dupe?
|
|
foreach (RPCChannelInfo ci in m_openChannels.Values)
|
|
{
|
|
if (ci.GetItemID().Equals(itemID))
|
|
{
|
|
// return the original channel ID for this item
|
|
channel = ci.GetChannelID();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( (channel == null) || (channel.Equals(LLUUID.Zero)) )
|
|
{
|
|
channel = LLUUID.Random();
|
|
RPCChannelInfo rpcChanInfo = new RPCChannelInfo(localID, itemID, channel);
|
|
lock (XMLRPCListLock)
|
|
{
|
|
m_openChannels.Add(channel, rpcChanInfo);
|
|
}
|
|
|
|
}
|
|
|
|
return channel;
|
|
}
|
|
|
|
/**********************************************
|
|
* Remote Data Reply
|
|
*
|
|
* Response to RPC message
|
|
*
|
|
*********************************************/
|
|
public void RemoteDataReply(string channel, string message_id, string sdata, int idata)
|
|
{
|
|
RPCRequestInfo rpcInfo;
|
|
LLUUID message_key = new LLUUID(message_id);
|
|
|
|
if (m_pendingResponse.TryGetValue(message_key, out rpcInfo))
|
|
{
|
|
rpcInfo.SetRetval(sdata);
|
|
rpcInfo.SetProcessed(true);
|
|
|
|
lock (XMLRPCListLock)
|
|
{
|
|
m_pendingResponse.Remove(message_key);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**********************************************
|
|
* CloseXMLRPCChannel
|
|
*
|
|
* Remove channel from dictionary
|
|
*
|
|
*********************************************/
|
|
public void CloseXMLRPCChannel(LLUUID channelKey)
|
|
{
|
|
if(m_openChannels.ContainsKey(channelKey))
|
|
m_openChannels.Remove(channelKey);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
LLUUID channel = new LLUUID((string)requestData["Channel"]);
|
|
RPCChannelInfo rpcChanInfo;
|
|
if (m_openChannels.TryGetValue(channel, out rpcChanInfo))
|
|
{
|
|
string intVal = (string)requestData["IntValue"];
|
|
string strVal = (string)requestData["StringValue"];
|
|
|
|
RPCRequestInfo rpcInfo;
|
|
|
|
lock (XMLRPCListLock)
|
|
{
|
|
rpcInfo = new RPCRequestInfo(rpcChanInfo.GetLocalID(), rpcChanInfo.GetItemID(), channel, strVal, intVal);
|
|
rpcQueue.Enqueue(rpcInfo);
|
|
}
|
|
|
|
int timeoutCtr = 0;
|
|
|
|
while(!rpcInfo.IsProcessed() && (timeoutCtr < RemoteReplyScriptTimeout))
|
|
{
|
|
Thread.Sleep(RemoteReplyScriptWait);
|
|
timeoutCtr += RemoteReplyScriptWait;
|
|
}
|
|
if (rpcInfo.IsProcessed())
|
|
{
|
|
response.Value = rpcInfo.GetRetval();
|
|
rpcInfo = null;
|
|
}
|
|
else
|
|
{
|
|
response.SetFault(-1, "Script timeout");
|
|
lock (XMLRPCListLock)
|
|
{
|
|
m_pendingResponse.Remove(rpcInfo.GetMessageID());
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
response.SetFault(-1, "Invalid channel");
|
|
}
|
|
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
public bool hasRequests()
|
|
{
|
|
return (rpcQueue.Count > 0);
|
|
}
|
|
|
|
public RPCRequestInfo GetNextRequest()
|
|
{
|
|
lock (XMLRPCListLock)
|
|
{
|
|
RPCRequestInfo rpcInfo = rpcQueue.Dequeue();
|
|
m_pendingResponse.Add(rpcInfo.GetMessageID(), rpcInfo);
|
|
return rpcInfo;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**************************************************************
|
|
*
|
|
* Class RPCRequestInfo
|
|
*
|
|
* Holds details about incoming requests until they are picked
|
|
* from the queue by LSLLongCmdHandler
|
|
* ***********************************************************/
|
|
public class RPCRequestInfo
|
|
{
|
|
private string m_StrVal;
|
|
private string m_IntVal;
|
|
private bool m_processed;
|
|
private string m_resp;
|
|
private uint m_localID;
|
|
private LLUUID m_ItemID;
|
|
private LLUUID m_MessageID;
|
|
private LLUUID m_ChannelKey;
|
|
|
|
public RPCRequestInfo(uint localID, LLUUID itemID, LLUUID channelKey, string strVal, string intVal)
|
|
{
|
|
m_localID = localID;
|
|
m_StrVal = strVal;
|
|
m_IntVal = intVal;
|
|
m_ItemID = itemID;
|
|
m_ChannelKey = channelKey;
|
|
m_MessageID = LLUUID.Random();
|
|
m_processed = false;
|
|
m_resp = "";
|
|
}
|
|
|
|
public bool IsProcessed()
|
|
{
|
|
return m_processed;
|
|
}
|
|
public LLUUID GetChannelKey()
|
|
{
|
|
return m_ChannelKey;
|
|
}
|
|
|
|
public void SetProcessed(bool processed)
|
|
{
|
|
m_processed = processed;
|
|
}
|
|
public void SetRetval(string resp)
|
|
{
|
|
m_resp = resp;
|
|
}
|
|
public string GetRetval()
|
|
{
|
|
return m_resp;
|
|
}
|
|
public uint GetLocalID()
|
|
{
|
|
return m_localID;
|
|
}
|
|
public LLUUID GetItemID()
|
|
{
|
|
return m_ItemID;
|
|
}
|
|
public string GetStrVal()
|
|
{
|
|
return m_StrVal;
|
|
}
|
|
public int GetIntValue()
|
|
{
|
|
return int.Parse(m_IntVal);
|
|
}
|
|
public LLUUID GetMessageID()
|
|
{
|
|
return m_MessageID;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public class RPCChannelInfo
|
|
{
|
|
private LLUUID m_itemID;
|
|
private uint m_localID;
|
|
private LLUUID m_ChannelKey;
|
|
|
|
public RPCChannelInfo(uint localID, LLUUID itemID, LLUUID channelID)
|
|
{
|
|
m_ChannelKey = channelID;
|
|
m_localID = localID;
|
|
m_itemID = itemID;
|
|
}
|
|
|
|
public LLUUID GetItemID()
|
|
{
|
|
return m_itemID;
|
|
}
|
|
|
|
public LLUUID GetChannelID()
|
|
{
|
|
return m_ChannelKey;
|
|
}
|
|
|
|
public uint GetLocalID()
|
|
{
|
|
return m_localID;
|
|
}
|
|
|
|
}
|
|
|
|
}
|