aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Scripting/XMLRPC
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Scripting/XMLRPC')
-rw-r--r--OpenSim/Region/Environment/Modules/Scripting/XMLRPC/XMLRPCModule.cs672
1 files changed, 672 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/Scripting/XMLRPC/XMLRPCModule.cs b/OpenSim/Region/Environment/Modules/Scripting/XMLRPC/XMLRPCModule.cs
new file mode 100644
index 0000000..1139b4b
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/Scripting/XMLRPC/XMLRPCModule.cs
@@ -0,0 +1,672 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Net;
32using System.Reflection;
33using System.Threading;
34using libsecondlife;
35using log4net;
36using Nini.Config;
37using Nwc.XmlRpc;
38using OpenSim.Framework;
39using OpenSim.Framework.Servers;
40using OpenSim.Region.Environment.Interfaces;
41using OpenSim.Region.Environment.Scenes;
42
43/*****************************************************
44 *
45 * XMLRPCModule
46 *
47 * Module for accepting incoming communications from
48 * external XMLRPC client and calling a remote data
49 * procedure for a registered data channel/prim.
50 *
51 *
52 * 1. On module load, open a listener port
53 * 2. Attach an XMLRPC handler
54 * 3. When a request is received:
55 * 3.1 Parse into components: channel key, int, string
56 * 3.2 Look up registered channel listeners
57 * 3.3 Call the channel (prim) remote data method
58 * 3.4 Capture the response (llRemoteDataReply)
59 * 3.5 Return response to client caller
60 * 3.6 If no response from llRemoteDataReply within
61 * RemoteReplyScriptTimeout, generate script timeout fault
62 *
63 * Prims in script must:
64 * 1. Open a remote data channel
65 * 1.1 Generate a channel ID
66 * 1.2 Register primid,channelid pair with module
67 * 2. Implement the remote data procedure handler
68 *
69 * llOpenRemoteDataChannel
70 * llRemoteDataReply
71 * remote_data(integer type, key channel, key messageid, string sender, integer ival, string sval)
72 * llCloseRemoteDataChannel
73 *
74 * **************************************************/
75
76namespace OpenSim.Region.Environment.Modules.Scripting.XMLRPC
77{
78 public class XMLRPCModule : IRegionModule, IXMLRPC
79 {
80 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
81
82 private object XMLRPCListLock = new object();
83 private string m_name = "XMLRPCModule";
84 private int RemoteReplyScriptWait = 300;
85 private int RemoteReplyScriptTimeout = 9000;
86 private int m_remoteDataPort = 0;
87 private List<Scene> m_scenes = new List<Scene>();
88
89 // <channel id, RPCChannelInfo>
90 private Dictionary<LLUUID, RPCChannelInfo> m_openChannels;
91
92 private Dictionary<LLUUID, RPCRequestInfo> m_rpcPending;
93 private Dictionary<LLUUID, RPCRequestInfo> m_rpcPendingResponses;
94
95 private Dictionary<LLUUID, SendRemoteDataRequest> m_pendingSRDResponses;
96
97 public void Initialise(Scene scene, IConfigSource config)
98 {
99 try
100 {
101 m_remoteDataPort = config.Configs["Network"].GetInt("remoteDataPort", m_remoteDataPort);
102 }
103 catch (Exception)
104 {
105 }
106
107 if (!m_scenes.Contains(scene))
108 {
109 m_scenes.Add(scene);
110
111 scene.RegisterModuleInterface<IXMLRPC>(this);
112 }
113 }
114
115 public void PostInitialise()
116 {
117 if (IsEnabled())
118 {
119 m_openChannels = new Dictionary<LLUUID, RPCChannelInfo>();
120 m_rpcPending = new Dictionary<LLUUID, RPCRequestInfo>();
121 m_rpcPendingResponses = new Dictionary<LLUUID, RPCRequestInfo>();
122 m_pendingSRDResponses = new Dictionary<LLUUID, SendRemoteDataRequest>();
123
124 // Start http server
125 // Attach xmlrpc handlers
126 m_log.Info("[REMOTE_DATA]: " +
127 "Starting XMLRPC Server on port " + m_remoteDataPort + " for llRemoteData commands.");
128 BaseHttpServer httpServer = new BaseHttpServer((uint)m_remoteDataPort);
129 httpServer.AddXmlRPCHandler("llRemoteData", XmlRpcRemoteData);
130 httpServer.Start();
131 }
132 }
133
134 public void Close()
135 {
136 }
137
138 public string Name
139 {
140 get { return m_name; }
141 }
142
143 public bool IsSharedModule
144 {
145 get { return true; }
146 }
147
148 public bool IsEnabled()
149 {
150 return (m_remoteDataPort > 0);
151 }
152
153 /**********************************************
154 * OpenXMLRPCChannel
155 *
156 * Generate a LLUUID channel key and add it and
157 * the prim id to dictionary <channelUUID, primUUID>
158 *
159 * First check if there is a channel assigned for
160 * this itemID. If there is, then someone called
161 * llOpenRemoteDataChannel twice. Just return the
162 * original channel. Other option is to delete the
163 * current channel and assign a new one.
164 *
165 * ********************************************/
166
167 public LLUUID OpenXMLRPCChannel(uint localID, LLUUID itemID)
168 {
169 LLUUID channel = new LLUUID();
170
171 //Is a dupe?
172 foreach (RPCChannelInfo ci in m_openChannels.Values)
173 {
174 if (ci.GetItemID().Equals(itemID))
175 {
176 // return the original channel ID for this item
177 channel = ci.GetChannelID();
178 break;
179 }
180 }
181
182 if (channel == LLUUID.Zero)
183 {
184 channel = LLUUID.Random();
185 RPCChannelInfo rpcChanInfo = new RPCChannelInfo(localID, itemID, channel);
186 lock (XMLRPCListLock)
187 {
188 m_openChannels.Add(channel, rpcChanInfo);
189 }
190 }
191
192 return channel;
193 }
194
195 // Delete channels based on itemID
196 // for when a script is deleted
197 public void DeleteChannels(LLUUID itemID)
198 {
199
200 if (m_openChannels != null)
201 {
202 ArrayList tmp = new ArrayList();
203
204 lock (XMLRPCListLock)
205 {
206 foreach (RPCChannelInfo li in m_openChannels.Values)
207 {
208 if (li.GetItemID().Equals(itemID))
209 {
210 tmp.Add(itemID);
211 }
212 }
213
214 IEnumerator tmpEnumerator = tmp.GetEnumerator();
215 while ( tmpEnumerator.MoveNext() )
216 m_openChannels.Remove((LLUUID)tmpEnumerator.Current);
217 }
218
219 }
220
221 }
222
223 /**********************************************
224 * Remote Data Reply
225 *
226 * Response to RPC message
227 *
228 *********************************************/
229
230 public void RemoteDataReply(string channel, string message_id, string sdata, int idata)
231 {
232 RPCRequestInfo rpcInfo;
233 LLUUID message_key = new LLUUID(message_id);
234
235 if (m_rpcPendingResponses.TryGetValue(message_key, out rpcInfo))
236 {
237 rpcInfo.SetStrRetval(sdata);
238 rpcInfo.SetIntRetval(idata);
239 rpcInfo.SetProcessed(true);
240 m_rpcPendingResponses.Remove(message_key);
241 }
242 }
243
244 /**********************************************
245 * CloseXMLRPCChannel
246 *
247 * Remove channel from dictionary
248 *
249 *********************************************/
250
251 public void CloseXMLRPCChannel(LLUUID channelKey)
252 {
253 if (m_openChannels.ContainsKey(channelKey))
254 m_openChannels.Remove(channelKey);
255 }
256
257
258 public XmlRpcResponse XmlRpcRemoteData(XmlRpcRequest request)
259 {
260 XmlRpcResponse response = new XmlRpcResponse();
261
262 Hashtable requestData = (Hashtable)request.Params[0];
263 bool GoodXML = (requestData.Contains("Channel") && requestData.Contains("IntValue") &&
264 requestData.Contains("StringValue"));
265
266 if (GoodXML)
267 {
268 LLUUID channel = new LLUUID((string)requestData["Channel"]);
269 RPCChannelInfo rpcChanInfo;
270 if (m_openChannels.TryGetValue(channel, out rpcChanInfo))
271 {
272 string intVal = (string)requestData["IntValue"];
273 string strVal = (string)requestData["StringValue"];
274
275 RPCRequestInfo rpcInfo;
276
277 lock (XMLRPCListLock)
278 {
279 rpcInfo =
280 new RPCRequestInfo(rpcChanInfo.GetLocalID(), rpcChanInfo.GetItemID(), channel, strVal,
281 intVal);
282 m_rpcPending.Add(rpcInfo.GetMessageID(), rpcInfo);
283 }
284
285 int timeoutCtr = 0;
286
287 while (!rpcInfo.IsProcessed() && (timeoutCtr < RemoteReplyScriptTimeout))
288 {
289 Thread.Sleep(RemoteReplyScriptWait);
290 timeoutCtr += RemoteReplyScriptWait;
291 }
292 if (rpcInfo.IsProcessed())
293 {
294 Hashtable param = new Hashtable();
295 param["StringValue"] = rpcInfo.GetStrRetval();
296 param["IntValue"] = Convert.ToString(rpcInfo.GetIntRetval());
297
298 ArrayList parameters = new ArrayList();
299 parameters.Add(param);
300
301 response.Value = parameters;
302 rpcInfo = null;
303 }
304 else
305 {
306 response.SetFault(-1, "Script timeout");
307 rpcInfo = null;
308 }
309 }
310 else
311 {
312 response.SetFault(-1, "Invalid channel");
313 }
314 }
315
316 return response;
317 }
318
319 public bool hasRequests()
320 {
321 lock (XMLRPCListLock)
322 {
323 if (m_rpcPending != null)
324 return (m_rpcPending.Count > 0);
325 else
326 return false;
327 }
328 }
329
330 public RPCRequestInfo GetNextCompletedRequest()
331 {
332 if (m_rpcPending != null)
333 {
334 lock (XMLRPCListLock)
335 {
336 foreach (LLUUID luid in m_rpcPending.Keys)
337 {
338 RPCRequestInfo tmpReq;
339
340 if (m_rpcPending.TryGetValue(luid, out tmpReq))
341 {
342
343 if (!tmpReq.IsProcessed()) return tmpReq;
344 }
345 }
346 }
347 }
348 return null;
349 }
350
351 public void RemoveCompletedRequest(LLUUID id)
352 {
353 lock (XMLRPCListLock)
354 {
355 RPCRequestInfo tmp;
356 if (m_rpcPending.TryGetValue(id, out tmp))
357 {
358 m_rpcPending.Remove(id);
359 m_rpcPendingResponses.Add(id, tmp);
360 }
361 else
362 {
363 Console.WriteLine("UNABLE TO REMOVE COMPLETED REQUEST");
364 }
365 }
366 }
367
368 public LLUUID SendRemoteData(uint localID, LLUUID itemID, string channel, string dest, int idata, string sdata)
369 {
370
371 SendRemoteDataRequest req = new SendRemoteDataRequest(
372 localID, itemID, channel, dest, idata, sdata
373 );
374 m_pendingSRDResponses.Add(req.GetReqID(), req);
375 return req.process();
376
377 }
378
379 public SendRemoteDataRequest GetNextCompletedSRDRequest()
380 {
381 if (m_pendingSRDResponses != null)
382 {
383 lock (XMLRPCListLock)
384 {
385 foreach (LLUUID luid in m_pendingSRDResponses.Keys)
386 {
387 SendRemoteDataRequest tmpReq;
388
389 if (m_pendingSRDResponses.TryGetValue(luid, out tmpReq))
390 {
391 if (tmpReq.finished)
392 return tmpReq;
393 }
394 }
395 }
396 }
397 return null;
398 }
399
400 public void RemoveCompletedSRDRequest(LLUUID id)
401 {
402 lock (XMLRPCListLock)
403 {
404 SendRemoteDataRequest tmpReq;
405 if (m_pendingSRDResponses.TryGetValue(id, out tmpReq))
406 {
407 m_pendingSRDResponses.Remove(id);
408 }
409 }
410 }
411
412 public void CancelSRDRequests(LLUUID itemID)
413 {
414 if (m_pendingSRDResponses != null)
415 {
416 lock (XMLRPCListLock)
417 {
418 foreach (SendRemoteDataRequest li in m_pendingSRDResponses.Values)
419 {
420 if (li.m_itemID.Equals(itemID))
421 m_pendingSRDResponses.Remove(li.GetReqID());
422 }
423 }
424 }
425 }
426 }
427
428 public class RPCRequestInfo
429 {
430 private string m_StrVal;
431 private string m_IntVal;
432 private bool m_processed;
433 private string m_respStr;
434 private int m_respInt;
435 private uint m_localID;
436 private LLUUID m_ItemID;
437 private LLUUID m_MessageID;
438 private LLUUID m_ChannelKey;
439
440 public RPCRequestInfo(uint localID, LLUUID itemID, LLUUID channelKey, string strVal, string intVal)
441 {
442 m_localID = localID;
443 m_StrVal = strVal;
444 m_IntVal = intVal;
445 m_ItemID = itemID;
446 m_ChannelKey = channelKey;
447 m_MessageID = LLUUID.Random();
448 m_processed = false;
449 m_respStr = String.Empty;
450 m_respInt = 0;
451 }
452
453 public bool IsProcessed()
454 {
455 return m_processed;
456 }
457
458 public LLUUID GetChannelKey()
459 {
460 return m_ChannelKey;
461 }
462
463 public void SetProcessed(bool processed)
464 {
465 m_processed = processed;
466 }
467
468 public void SetStrRetval(string resp)
469 {
470 m_respStr = resp;
471 }
472
473 public string GetStrRetval()
474 {
475 return m_respStr;
476 }
477 public void SetIntRetval(int resp)
478 {
479 m_respInt = resp;
480 }
481
482 public int GetIntRetval()
483 {
484 return m_respInt;
485 }
486 public uint GetLocalID()
487 {
488 return m_localID;
489 }
490
491 public LLUUID GetItemID()
492 {
493 return m_ItemID;
494 }
495
496 public string GetStrVal()
497 {
498 return m_StrVal;
499 }
500
501 public int GetIntValue()
502 {
503 return int.Parse(m_IntVal);
504 }
505
506 public LLUUID GetMessageID()
507 {
508 return m_MessageID;
509 }
510 }
511
512 public class RPCChannelInfo
513 {
514 private LLUUID m_itemID;
515 private uint m_localID;
516 private LLUUID m_ChannelKey;
517
518 public RPCChannelInfo(uint localID, LLUUID itemID, LLUUID channelID)
519 {
520 m_ChannelKey = channelID;
521 m_localID = localID;
522 m_itemID = itemID;
523 }
524
525 public LLUUID GetItemID()
526 {
527 return m_itemID;
528 }
529
530 public LLUUID GetChannelID()
531 {
532 return m_ChannelKey;
533 }
534
535 public uint GetLocalID()
536 {
537 return m_localID;
538 }
539
540 }
541
542 public class SendRemoteDataRequest
543 {
544
545 public LLUUID reqID;
546 public string destURL;
547 public string channel;
548 public string sdata;
549 public int idata;
550 public bool finished;
551 public string response_sdata;
552 public int response_idata;
553 public XmlRpcRequest request;
554 private Thread httpThread;
555 public LLUUID m_itemID;
556 public uint m_localID;
557 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
558
559 public SendRemoteDataRequest(uint localID, LLUUID itemID, string channel, string dest, int idata, string sdata)
560 {
561
562 this.channel = channel;
563 this.destURL = dest;
564 this.idata = idata;
565 this.sdata = sdata;
566 m_itemID = itemID;
567 m_localID = localID;
568
569 reqID = LLUUID.Random();
570
571 }
572
573 public LLUUID process()
574 {
575 httpThread = new Thread(SendRequest);
576 httpThread.Name = "HttpRequestThread";
577 httpThread.Priority = ThreadPriority.BelowNormal;
578 httpThread.IsBackground = true;
579 finished = false;
580 httpThread.Start();
581 ThreadTracker.Add(httpThread);
582
583 return reqID;
584
585 }
586
587 /*
588 * TODO: More work on the response codes. Right now
589 * returning 200 for success or 499 for exception
590 */
591
592 public void SendRequest()
593 {
594 Hashtable param = new Hashtable();
595
596 // Check if channel is an LLUUID
597 // if not, use as method name
598 LLUUID parseUID;
599 string mName = "llRemoteData";
600 if( (channel != null) && (channel != "") )
601 if( !LLUUID.TryParse(channel, out parseUID) )
602 mName = channel;
603 else
604 param["Channel"] = channel;
605
606 param["StringValue"] = sdata;
607 param["IntValue"] = Convert.ToString(idata);
608
609 ArrayList parameters = new ArrayList();
610 parameters.Add(param);
611 XmlRpcRequest req = new XmlRpcRequest(mName, parameters);
612 try
613 {
614 XmlRpcResponse resp = req.Send(destURL, 30000);
615 if (resp != null)
616 {
617 Hashtable respParms;
618 if(resp.Value.GetType().Equals(Type.GetType("System.Collections.Hashtable"))) {
619 respParms = (Hashtable)resp.Value;
620 }
621 else {
622 ArrayList respData = (ArrayList)resp.Value;
623 respParms = (Hashtable)respData[0];
624 }
625 if (respParms != null)
626 {
627 if (respParms.Contains("StringValue"))
628 {
629 sdata = (string)respParms["StringValue"];
630 }
631 if (respParms.Contains("IntValue"))
632 {
633 idata = Convert.ToInt32((string)respParms["IntValue"]);
634 }
635 if (respParms.Contains("faultString"))
636 {
637 sdata = (string)respParms["faultString"];
638 }
639 if (respParms.Contains("faultCode"))
640 {
641 idata = Convert.ToInt32(respParms["faultCode"]);
642 }
643 }
644 }
645 }
646 catch (WebException we)
647 {
648 sdata = we.Message;
649 m_log.Warn("[SendRemoteDataRequest]: Request failed");
650 m_log.Warn(we.StackTrace);
651 }
652
653 finished = true;
654 }
655
656 public void Stop()
657 {
658 try
659 {
660 httpThread.Abort();
661 }
662 catch (Exception)
663 {
664 }
665 }
666
667 public LLUUID GetReqID()
668 {
669 return reqID;
670 }
671 }
672} \ No newline at end of file