diff options
Diffstat (limited to 'OpenSim/Region/Environment/Modules/XMLRPCModule.cs')
-rw-r--r-- | OpenSim/Region/Environment/Modules/XMLRPCModule.cs | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/XMLRPCModule.cs b/OpenSim/Region/Environment/Modules/XMLRPCModule.cs new file mode 100644 index 0000000..50ed776 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/XMLRPCModule.cs | |||
@@ -0,0 +1,360 @@ | |||
1 | using System; | ||
2 | using System.IO; | ||
3 | using System.Net.Sockets; | ||
4 | using System.Threading; | ||
5 | using libsecondlife; | ||
6 | using OpenSim.Framework.Interfaces; | ||
7 | using OpenSim.Framework.Utilities; | ||
8 | using OpenSim.Region.Environment.Interfaces; | ||
9 | using OpenSim.Region.Environment.Scenes; | ||
10 | using OpenSim.Framework.Servers; | ||
11 | using Nwc.XmlRpc; | ||
12 | using System.Collections; | ||
13 | using System.Collections.Generic; | ||
14 | |||
15 | /***************************************************** | ||
16 | * | ||
17 | * XMLRPCModule | ||
18 | * | ||
19 | * Module for accepting incoming communications from | ||
20 | * external XMLRPC client and calling a remote data | ||
21 | * procedure for a registered data channel/prim. | ||
22 | * | ||
23 | * | ||
24 | * 1. On module load, open a listener port | ||
25 | * 2. Attach an XMLRPC handler | ||
26 | * 3. When a request is received: | ||
27 | * 3.1 Parse into components: channel key, int, string | ||
28 | * 3.2 Look up registered channel listeners | ||
29 | * 3.3 Call the channel (prim) remote data method | ||
30 | * 3.4 Capture the response (llRemoteDataReply) | ||
31 | * 3.5 Return response to client caller | ||
32 | * 3.6 If no response from llRemoteDataReply within | ||
33 | * RemoteReplyScriptTimeout, generate script timeout fault | ||
34 | * | ||
35 | * Prims in script must: | ||
36 | * 1. Open a remote data channel | ||
37 | * 1.1 Generate a channel ID | ||
38 | * 1.2 Register primid,channelid pair with module | ||
39 | * 2. Implement the remote data procedure handler | ||
40 | * | ||
41 | * llOpenRemoteDataChannel | ||
42 | * llRemoteDataReply | ||
43 | * remote_data(integer type, key channel, key messageid, string sender, integer ival, string sval) | ||
44 | * llCloseRemoteDataChannel | ||
45 | * | ||
46 | * **************************************************/ | ||
47 | namespace OpenSim.Region.Environment.Modules | ||
48 | { | ||
49 | public class XMLRPCModule : IRegionModule, IXMLRPC | ||
50 | { | ||
51 | private Scene m_scene; | ||
52 | private Queue<RPCRequestInfo> rpcQueue = new Queue<RPCRequestInfo>(); | ||
53 | private object XMLRPCListLock = new object(); | ||
54 | private string m_name = "XMLRPCModule"; | ||
55 | private int RemoteReplyScriptWait = 100; | ||
56 | private int RemoteReplyScriptTimeout = 300; | ||
57 | |||
58 | // <channel id, RPCChannelInfo> | ||
59 | private Dictionary<LLUUID, RPCChannelInfo> m_openChannels; | ||
60 | |||
61 | // <channel id, RPCRequestInfo> | ||
62 | private Dictionary<LLUUID, RPCRequestInfo> m_pendingResponse; | ||
63 | |||
64 | public XMLRPCModule() | ||
65 | { | ||
66 | } | ||
67 | |||
68 | public void Initialise(Scene scene) | ||
69 | { | ||
70 | m_scene = scene; | ||
71 | |||
72 | m_scene.RegisterModuleInterface<IXMLRPC>(this); | ||
73 | |||
74 | m_openChannels = new Dictionary<LLUUID, RPCChannelInfo>(); | ||
75 | m_pendingResponse = new Dictionary<LLUUID, RPCRequestInfo>(); | ||
76 | |||
77 | // Start http server | ||
78 | // Attach xmlrpc handlers | ||
79 | BaseHttpServer httpServer = new BaseHttpServer(20800); | ||
80 | httpServer.AddXmlRPCHandler("llRemoteData", this.XmlRpcRemoteData); | ||
81 | httpServer.Start(); | ||
82 | } | ||
83 | |||
84 | public void PostInitialise() | ||
85 | { | ||
86 | } | ||
87 | |||
88 | public void CloseDown() | ||
89 | { | ||
90 | } | ||
91 | |||
92 | public string GetName() | ||
93 | { | ||
94 | return m_name; | ||
95 | } | ||
96 | |||
97 | public bool IsSharedModule() | ||
98 | { | ||
99 | return false; | ||
100 | } | ||
101 | |||
102 | /********************************************** | ||
103 | * OpenXMLRPCChannel | ||
104 | * | ||
105 | * Generate a LLUUID channel key and add it and | ||
106 | * the prim id to dictionary <channelUUID, primUUID> | ||
107 | * | ||
108 | * First check if there is a channel assigned for | ||
109 | * this itemID. If there is, then someone called | ||
110 | * llOpenRemoteDataChannel twice. Just return the | ||
111 | * original channel. Other option is to delete the | ||
112 | * current channel and assign a new one. | ||
113 | * | ||
114 | * ********************************************/ | ||
115 | public LLUUID OpenXMLRPCChannel(uint localID, LLUUID itemID) | ||
116 | { | ||
117 | LLUUID channel = null; | ||
118 | |||
119 | //Is a dupe? | ||
120 | foreach (RPCChannelInfo ci in m_openChannels.Values) | ||
121 | { | ||
122 | if (ci.GetItemID().Equals(itemID)) | ||
123 | { | ||
124 | // return the original channel ID for this item | ||
125 | channel = ci.GetChannelID(); | ||
126 | break; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | if ( (channel == null) || (channel.Equals(LLUUID.Zero)) ) | ||
131 | { | ||
132 | channel = LLUUID.Random(); | ||
133 | RPCChannelInfo rpcChanInfo = new RPCChannelInfo(localID, itemID, channel); | ||
134 | lock (XMLRPCListLock) | ||
135 | { | ||
136 | m_openChannels.Add(channel, rpcChanInfo); | ||
137 | } | ||
138 | |||
139 | } | ||
140 | |||
141 | return channel; | ||
142 | } | ||
143 | |||
144 | /********************************************** | ||
145 | * Remote Data Reply | ||
146 | * | ||
147 | * Response to RPC message | ||
148 | * | ||
149 | *********************************************/ | ||
150 | public void RemoteDataReply(string channel, string message_id, string sdata, int idata) | ||
151 | { | ||
152 | RPCRequestInfo rpcInfo; | ||
153 | LLUUID message_key = new LLUUID(message_id); | ||
154 | |||
155 | if (m_pendingResponse.TryGetValue(message_key, out rpcInfo)) | ||
156 | { | ||
157 | rpcInfo.SetRetval(sdata); | ||
158 | rpcInfo.SetProcessed(true); | ||
159 | |||
160 | lock (XMLRPCListLock) | ||
161 | { | ||
162 | m_pendingResponse.Remove(message_key); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | } | ||
167 | |||
168 | /********************************************** | ||
169 | * CloseXMLRPCChannel | ||
170 | * | ||
171 | * Remove channel from dictionary | ||
172 | * | ||
173 | *********************************************/ | ||
174 | public void CloseXMLRPCChannel(LLUUID channelKey) | ||
175 | { | ||
176 | if(m_openChannels.ContainsKey(channelKey)) | ||
177 | m_openChannels.Remove(channelKey); | ||
178 | } | ||
179 | |||
180 | |||
181 | public XmlRpcResponse XmlRpcRemoteData(XmlRpcRequest request) | ||
182 | { | ||
183 | |||
184 | XmlRpcResponse response = new XmlRpcResponse(); | ||
185 | |||
186 | Hashtable requestData = (Hashtable)request.Params[0]; | ||
187 | bool GoodXML = (requestData.Contains("Channel") && requestData.Contains("IntValue") && requestData.Contains("StringValue")); | ||
188 | |||
189 | if (GoodXML) | ||
190 | { | ||
191 | LLUUID channel = new LLUUID((string)requestData["Channel"]); | ||
192 | RPCChannelInfo rpcChanInfo; | ||
193 | if (m_openChannels.TryGetValue(channel, out rpcChanInfo)) | ||
194 | { | ||
195 | string intVal = (string)requestData["IntValue"]; | ||
196 | string strVal = (string)requestData["StringValue"]; | ||
197 | |||
198 | RPCRequestInfo rpcInfo; | ||
199 | |||
200 | lock (XMLRPCListLock) | ||
201 | { | ||
202 | rpcInfo = new RPCRequestInfo(rpcChanInfo.GetLocalID(), rpcChanInfo.GetItemID(), channel, strVal, intVal); | ||
203 | rpcQueue.Enqueue(rpcInfo); | ||
204 | } | ||
205 | |||
206 | int timeoutCtr = 0; | ||
207 | |||
208 | while(!rpcInfo.IsProcessed() && (timeoutCtr < RemoteReplyScriptTimeout)) | ||
209 | { | ||
210 | Thread.Sleep(RemoteReplyScriptWait); | ||
211 | timeoutCtr += RemoteReplyScriptWait; | ||
212 | } | ||
213 | if (rpcInfo.IsProcessed()) | ||
214 | { | ||
215 | response.Value = rpcInfo.GetRetval(); | ||
216 | rpcInfo = null; | ||
217 | } | ||
218 | else | ||
219 | { | ||
220 | response.SetFault(-1, "Script timeout"); | ||
221 | lock (XMLRPCListLock) | ||
222 | { | ||
223 | m_pendingResponse.Remove(rpcInfo.GetMessageID()); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | } | ||
228 | else | ||
229 | { | ||
230 | response.SetFault(-1, "Invalid channel"); | ||
231 | } | ||
232 | |||
233 | } | ||
234 | |||
235 | return response; | ||
236 | } | ||
237 | |||
238 | public bool hasRequests() | ||
239 | { | ||
240 | return (rpcQueue.Count > 0); | ||
241 | } | ||
242 | |||
243 | public RPCRequestInfo GetNextRequest() | ||
244 | { | ||
245 | lock (XMLRPCListLock) | ||
246 | { | ||
247 | RPCRequestInfo rpcInfo = rpcQueue.Dequeue(); | ||
248 | m_pendingResponse.Add(rpcInfo.GetMessageID(), rpcInfo); | ||
249 | return rpcInfo; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | } | ||
254 | |||
255 | /************************************************************** | ||
256 | * | ||
257 | * Class RPCRequestInfo | ||
258 | * | ||
259 | * Holds details about incoming requests until they are picked | ||
260 | * from the queue by LSLLongCmdHandler | ||
261 | * ***********************************************************/ | ||
262 | public class RPCRequestInfo | ||
263 | { | ||
264 | private string m_StrVal; | ||
265 | private string m_IntVal; | ||
266 | private bool m_processed; | ||
267 | private string m_resp; | ||
268 | private uint m_localID; | ||
269 | private LLUUID m_ItemID; | ||
270 | private LLUUID m_MessageID; | ||
271 | private LLUUID m_ChannelKey; | ||
272 | |||
273 | public RPCRequestInfo(uint localID, LLUUID itemID, LLUUID channelKey, string strVal, string intVal) | ||
274 | { | ||
275 | m_localID = localID; | ||
276 | m_StrVal = strVal; | ||
277 | m_IntVal = intVal; | ||
278 | m_ItemID = itemID; | ||
279 | m_ChannelKey = channelKey; | ||
280 | m_MessageID = LLUUID.Random(); | ||
281 | m_processed = false; | ||
282 | m_resp = ""; | ||
283 | } | ||
284 | |||
285 | public bool IsProcessed() | ||
286 | { | ||
287 | return m_processed; | ||
288 | } | ||
289 | public LLUUID GetChannelKey() | ||
290 | { | ||
291 | return m_ChannelKey; | ||
292 | } | ||
293 | |||
294 | public void SetProcessed(bool processed) | ||
295 | { | ||
296 | m_processed = processed; | ||
297 | } | ||
298 | public void SetRetval(string resp) | ||
299 | { | ||
300 | m_resp = resp; | ||
301 | } | ||
302 | public string GetRetval() | ||
303 | { | ||
304 | return m_resp; | ||
305 | } | ||
306 | public uint GetLocalID() | ||
307 | { | ||
308 | return m_localID; | ||
309 | } | ||
310 | public LLUUID GetItemID() | ||
311 | { | ||
312 | return m_ItemID; | ||
313 | } | ||
314 | public string GetStrVal() | ||
315 | { | ||
316 | return m_StrVal; | ||
317 | } | ||
318 | public int GetIntValue() | ||
319 | { | ||
320 | return int.Parse(m_IntVal); | ||
321 | } | ||
322 | public LLUUID GetMessageID() | ||
323 | { | ||
324 | return m_MessageID; | ||
325 | } | ||
326 | |||
327 | |||
328 | } | ||
329 | |||
330 | public class RPCChannelInfo | ||
331 | { | ||
332 | private LLUUID m_itemID; | ||
333 | private uint m_localID; | ||
334 | private LLUUID m_ChannelKey; | ||
335 | |||
336 | public RPCChannelInfo(uint localID, LLUUID itemID, LLUUID channelID) | ||
337 | { | ||
338 | m_ChannelKey = channelID; | ||
339 | m_localID = localID; | ||
340 | m_itemID = itemID; | ||
341 | } | ||
342 | |||
343 | public LLUUID GetItemID() | ||
344 | { | ||
345 | return m_itemID; | ||
346 | } | ||
347 | |||
348 | public LLUUID GetChannelID() | ||
349 | { | ||
350 | return m_ChannelKey; | ||
351 | } | ||
352 | |||
353 | public uint GetLocalID() | ||
354 | { | ||
355 | return m_localID; | ||
356 | } | ||
357 | |||
358 | } | ||
359 | |||
360 | } | ||