aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs')
-rw-r--r--OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs726
1 files changed, 726 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
new file mode 100644
index 0000000..942c130
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
@@ -0,0 +1,726 @@
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 OpenMetaverse;
35using log4net;
36using Nini.Config;
37using Nwc.XmlRpc;
38using OpenSim.Framework;
39using OpenSim.Framework.Servers;
40using OpenSim.Region.Framework.Interfaces;
41using OpenSim.Region.Framework.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.CoreModules.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 string m_name = "XMLRPCModule";
83
84 // <channel id, RPCChannelInfo>
85 private Dictionary<UUID, RPCChannelInfo> m_openChannels;
86 private Dictionary<UUID, SendRemoteDataRequest> m_pendingSRDResponses;
87 private int m_remoteDataPort = 0;
88
89 private Dictionary<UUID, RPCRequestInfo> m_rpcPending;
90 private Dictionary<UUID, RPCRequestInfo> m_rpcPendingResponses;
91 private List<Scene> m_scenes = new List<Scene>();
92 private int RemoteReplyScriptTimeout = 9000;
93 private int RemoteReplyScriptWait = 300;
94 private object XMLRPCListLock = new object();
95
96 #region IRegionModule Members
97
98 public void Initialise(Scene scene, IConfigSource config)
99 {
100 // We need to create these early because the scripts might be calling
101 // But since this gets called for every region, we need to make sure they
102 // get called only one time (or we lose any open channels)
103 if (null == m_openChannels)
104 {
105 m_openChannels = new Dictionary<UUID, RPCChannelInfo>();
106 m_rpcPending = new Dictionary<UUID, RPCRequestInfo>();
107 m_rpcPendingResponses = new Dictionary<UUID, RPCRequestInfo>();
108 m_pendingSRDResponses = new Dictionary<UUID, SendRemoteDataRequest>();
109
110 try
111 {
112 m_remoteDataPort = config.Configs["Network"].GetInt("remoteDataPort", m_remoteDataPort);
113 }
114 catch (Exception)
115 {
116 }
117 }
118
119 if (!m_scenes.Contains(scene))
120 {
121 m_scenes.Add(scene);
122
123 scene.RegisterModuleInterface<IXMLRPC>(this);
124 }
125 }
126
127 public void PostInitialise()
128 {
129 if (IsEnabled())
130 {
131 // Start http server
132 // Attach xmlrpc handlers
133 m_log.Info("[REMOTE_DATA]: " +
134 "Starting XMLRPC Server on port " + m_remoteDataPort + " for llRemoteData commands.");
135 BaseHttpServer httpServer = new BaseHttpServer((uint) m_remoteDataPort);
136 httpServer.AddXmlRPCHandler("llRemoteData", XmlRpcRemoteData);
137 httpServer.Start();
138 }
139 }
140
141 public void Close()
142 {
143 }
144
145 public string Name
146 {
147 get { return m_name; }
148 }
149
150 public bool IsSharedModule
151 {
152 get { return true; }
153 }
154
155 #endregion
156
157 #region IXMLRPC Members
158
159 public bool IsEnabled()
160 {
161 return (m_remoteDataPort > 0);
162 }
163
164 /**********************************************
165 * OpenXMLRPCChannel
166 *
167 * Generate a UUID channel key and add it and
168 * the prim id to dictionary <channelUUID, primUUID>
169 *
170 * A custom channel key can be proposed.
171 * Otherwise, passing UUID.Zero will generate
172 * and return a random channel
173 *
174 * First check if there is a channel assigned for
175 * this itemID. If there is, then someone called
176 * llOpenRemoteDataChannel twice. Just return the
177 * original channel. Other option is to delete the
178 * current channel and assign a new one.
179 *
180 * ********************************************/
181
182 public UUID OpenXMLRPCChannel(uint localID, UUID itemID, UUID channelID)
183 {
184 UUID newChannel = UUID.Zero;
185
186 // This should no longer happen, but the check is reasonable anyway
187 if (null == m_openChannels)
188 {
189 m_log.Warn("[RemoteDataReply] Attempt to open channel before initialization is complete");
190 return newChannel;
191 }
192
193 //Is a dupe?
194 foreach (RPCChannelInfo ci in m_openChannels.Values)
195 {
196 if (ci.GetItemID().Equals(itemID))
197 {
198 // return the original channel ID for this item
199 newChannel = ci.GetChannelID();
200 break;
201 }
202 }
203
204 if (newChannel == UUID.Zero)
205 {
206 newChannel = (channelID == UUID.Zero) ? UUID.Random() : channelID;
207 RPCChannelInfo rpcChanInfo = new RPCChannelInfo(localID, itemID, newChannel);
208 lock (XMLRPCListLock)
209 {
210 m_openChannels.Add(newChannel, rpcChanInfo);
211 }
212 }
213
214 return newChannel;
215 }
216
217 // Delete channels based on itemID
218 // for when a script is deleted
219 public void DeleteChannels(UUID itemID)
220 {
221 if (m_openChannels != null)
222 {
223 ArrayList tmp = new ArrayList();
224
225 lock (XMLRPCListLock)
226 {
227 foreach (RPCChannelInfo li in m_openChannels.Values)
228 {
229 if (li.GetItemID().Equals(itemID))
230 {
231 tmp.Add(itemID);
232 }
233 }
234
235 IEnumerator tmpEnumerator = tmp.GetEnumerator();
236 while (tmpEnumerator.MoveNext())
237 m_openChannels.Remove((UUID) tmpEnumerator.Current);
238 }
239 }
240 }
241
242 /**********************************************
243 * Remote Data Reply
244 *
245 * Response to RPC message
246 *
247 *********************************************/
248
249 public void RemoteDataReply(string channel, string message_id, string sdata, int idata)
250 {
251 UUID message_key = new UUID(message_id);
252 UUID channel_key = new UUID(channel);
253
254 RPCRequestInfo rpcInfo = null;
255
256 if (message_key == UUID.Zero)
257 {
258 foreach (RPCRequestInfo oneRpcInfo in m_rpcPendingResponses.Values)
259 if (oneRpcInfo.GetChannelKey() == channel_key)
260 rpcInfo = oneRpcInfo;
261 }
262 else
263 {
264 m_rpcPendingResponses.TryGetValue(message_key, out rpcInfo);
265 }
266
267 if (rpcInfo != null)
268 {
269 rpcInfo.SetStrRetval(sdata);
270 rpcInfo.SetIntRetval(idata);
271 rpcInfo.SetProcessed(true);
272 m_rpcPendingResponses.Remove(message_key);
273 }
274 else
275 {
276 m_log.Warn("[RemoteDataReply]: Channel or message_id not found");
277 }
278 }
279
280 /**********************************************
281 * CloseXMLRPCChannel
282 *
283 * Remove channel from dictionary
284 *
285 *********************************************/
286
287 public void CloseXMLRPCChannel(UUID channelKey)
288 {
289 if (m_openChannels.ContainsKey(channelKey))
290 m_openChannels.Remove(channelKey);
291 }
292
293
294 public bool hasRequests()
295 {
296 lock (XMLRPCListLock)
297 {
298 if (m_rpcPending != null)
299 return (m_rpcPending.Count > 0);
300 else
301 return false;
302 }
303 }
304
305 public IXmlRpcRequestInfo GetNextCompletedRequest()
306 {
307 if (m_rpcPending != null)
308 {
309 lock (XMLRPCListLock)
310 {
311 foreach (UUID luid in m_rpcPending.Keys)
312 {
313 RPCRequestInfo tmpReq;
314
315 if (m_rpcPending.TryGetValue(luid, out tmpReq))
316 {
317 if (!tmpReq.IsProcessed()) return tmpReq;
318 }
319 }
320 }
321 }
322 return null;
323 }
324
325 public void RemoveCompletedRequest(UUID id)
326 {
327 lock (XMLRPCListLock)
328 {
329 RPCRequestInfo tmp;
330 if (m_rpcPending.TryGetValue(id, out tmp))
331 {
332 m_rpcPending.Remove(id);
333 m_rpcPendingResponses.Add(id, tmp);
334 }
335 else
336 {
337 Console.WriteLine("UNABLE TO REMOVE COMPLETED REQUEST");
338 }
339 }
340 }
341
342 public UUID SendRemoteData(uint localID, UUID itemID, string channel, string dest, int idata, string sdata)
343 {
344 SendRemoteDataRequest req = new SendRemoteDataRequest(
345 localID, itemID, channel, dest, idata, sdata
346 );
347 m_pendingSRDResponses.Add(req.GetReqID(), req);
348 req.Process();
349 return req.ReqID;
350 }
351
352 public IServiceRequest GetNextCompletedSRDRequest()
353 {
354 if (m_pendingSRDResponses != null)
355 {
356 lock (XMLRPCListLock)
357 {
358 foreach (UUID luid in m_pendingSRDResponses.Keys)
359 {
360 SendRemoteDataRequest tmpReq;
361
362 if (m_pendingSRDResponses.TryGetValue(luid, out tmpReq))
363 {
364 if (tmpReq.Finished)
365 return tmpReq;
366 }
367 }
368 }
369 }
370 return null;
371 }
372
373 public void RemoveCompletedSRDRequest(UUID id)
374 {
375 lock (XMLRPCListLock)
376 {
377 SendRemoteDataRequest tmpReq;
378 if (m_pendingSRDResponses.TryGetValue(id, out tmpReq))
379 {
380 m_pendingSRDResponses.Remove(id);
381 }
382 }
383 }
384
385 public void CancelSRDRequests(UUID itemID)
386 {
387 if (m_pendingSRDResponses != null)
388 {
389 lock (XMLRPCListLock)
390 {
391 foreach (SendRemoteDataRequest li in m_pendingSRDResponses.Values)
392 {
393 if (li.ItemID.Equals(itemID))
394 m_pendingSRDResponses.Remove(li.GetReqID());
395 }
396 }
397 }
398 }
399
400 #endregion
401
402 public XmlRpcResponse XmlRpcRemoteData(XmlRpcRequest request)
403 {
404 XmlRpcResponse response = new XmlRpcResponse();
405
406 Hashtable requestData = (Hashtable) request.Params[0];
407 bool GoodXML = (requestData.Contains("Channel") && requestData.Contains("IntValue") &&
408 requestData.Contains("StringValue"));
409
410 if (GoodXML)
411 {
412 UUID channel = new UUID((string) requestData["Channel"]);
413 RPCChannelInfo rpcChanInfo;
414 if (m_openChannels.TryGetValue(channel, out rpcChanInfo))
415 {
416 string intVal = Convert.ToInt32(requestData["IntValue"]).ToString();
417 string strVal = (string) requestData["StringValue"];
418
419 RPCRequestInfo rpcInfo;
420
421 lock (XMLRPCListLock)
422 {
423 rpcInfo =
424 new RPCRequestInfo(rpcChanInfo.GetLocalID(), rpcChanInfo.GetItemID(), channel, strVal,
425 intVal);
426 m_rpcPending.Add(rpcInfo.GetMessageID(), rpcInfo);
427 }
428
429 int timeoutCtr = 0;
430
431 while (!rpcInfo.IsProcessed() && (timeoutCtr < RemoteReplyScriptTimeout))
432 {
433 Thread.Sleep(RemoteReplyScriptWait);
434 timeoutCtr += RemoteReplyScriptWait;
435 }
436 if (rpcInfo.IsProcessed())
437 {
438 Hashtable param = new Hashtable();
439 param["StringValue"] = rpcInfo.GetStrRetval();
440 param["IntValue"] = rpcInfo.GetIntRetval();
441
442 ArrayList parameters = new ArrayList();
443 parameters.Add(param);
444
445 response.Value = parameters;
446 rpcInfo = null;
447 }
448 else
449 {
450 response.SetFault(-1, "Script timeout");
451 rpcInfo = null;
452 }
453 }
454 else
455 {
456 response.SetFault(-1, "Invalid channel");
457 }
458 }
459
460 return response;
461 }
462 }
463
464 public class RPCRequestInfo: IXmlRpcRequestInfo
465 {
466 private UUID m_ChannelKey;
467 private string m_IntVal;
468 private UUID m_ItemID;
469 private uint m_localID;
470 private UUID m_MessageID;
471 private bool m_processed;
472 private int m_respInt;
473 private string m_respStr;
474 private string m_StrVal;
475
476 public RPCRequestInfo(uint localID, UUID itemID, UUID channelKey, string strVal, string intVal)
477 {
478 m_localID = localID;
479 m_StrVal = strVal;
480 m_IntVal = intVal;
481 m_ItemID = itemID;
482 m_ChannelKey = channelKey;
483 m_MessageID = UUID.Random();
484 m_processed = false;
485 m_respStr = String.Empty;
486 m_respInt = 0;
487 }
488
489 public bool IsProcessed()
490 {
491 return m_processed;
492 }
493
494 public UUID GetChannelKey()
495 {
496 return m_ChannelKey;
497 }
498
499 public void SetProcessed(bool processed)
500 {
501 m_processed = processed;
502 }
503
504 public void SetStrRetval(string resp)
505 {
506 m_respStr = resp;
507 }
508
509 public string GetStrRetval()
510 {
511 return m_respStr;
512 }
513
514 public void SetIntRetval(int resp)
515 {
516 m_respInt = resp;
517 }
518
519 public int GetIntRetval()
520 {
521 return m_respInt;
522 }
523
524 public uint GetLocalID()
525 {
526 return m_localID;
527 }
528
529 public UUID GetItemID()
530 {
531 return m_ItemID;
532 }
533
534 public string GetStrVal()
535 {
536 return m_StrVal;
537 }
538
539 public int GetIntValue()
540 {
541 return int.Parse(m_IntVal);
542 }
543
544 public UUID GetMessageID()
545 {
546 return m_MessageID;
547 }
548 }
549
550 public class RPCChannelInfo
551 {
552 private UUID m_ChannelKey;
553 private UUID m_itemID;
554 private uint m_localID;
555
556 public RPCChannelInfo(uint localID, UUID itemID, UUID channelID)
557 {
558 m_ChannelKey = channelID;
559 m_localID = localID;
560 m_itemID = itemID;
561 }
562
563 public UUID GetItemID()
564 {
565 return m_itemID;
566 }
567
568 public UUID GetChannelID()
569 {
570 return m_ChannelKey;
571 }
572
573 public uint GetLocalID()
574 {
575 return m_localID;
576 }
577 }
578
579 public class SendRemoteDataRequest: IServiceRequest
580 {
581 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
582
583 public string Channel;
584 public string DestURL;
585 private bool _finished;
586 public bool Finished
587 {
588 get { return _finished; }
589 set { _finished = value; }
590 }
591 private Thread httpThread;
592 public int Idata;
593 private UUID _itemID;
594 public UUID ItemID
595 {
596 get { return _itemID; }
597 set { _itemID = value; }
598 }
599 private uint _localID;
600 public uint LocalID
601 {
602 get { return _localID; }
603 set { _localID = value; }
604 }
605 private UUID _reqID;
606 public UUID ReqID
607 {
608 get { return _reqID; }
609 set { _reqID = value; }
610 }
611 public XmlRpcRequest Request;
612 public int ResponseIdata;
613 public string ResponseSdata;
614 public string Sdata;
615
616 public SendRemoteDataRequest(uint localID, UUID itemID, string channel, string dest, int idata, string sdata)
617 {
618 this.Channel = channel;
619 DestURL = dest;
620 this.Idata = idata;
621 this.Sdata = sdata;
622 ItemID = itemID;
623 LocalID = localID;
624
625 ReqID = UUID.Random();
626 }
627
628 public void Process()
629 {
630 httpThread = new Thread(SendRequest);
631 httpThread.Name = "HttpRequestThread";
632 httpThread.Priority = ThreadPriority.BelowNormal;
633 httpThread.IsBackground = true;
634 _finished = false;
635 httpThread.Start();
636 ThreadTracker.Add(httpThread);
637 }
638
639 /*
640 * TODO: More work on the response codes. Right now
641 * returning 200 for success or 499 for exception
642 */
643
644 public void SendRequest()
645 {
646 Hashtable param = new Hashtable();
647
648 // Check if channel is an UUID
649 // if not, use as method name
650 UUID parseUID;
651 string mName = "llRemoteData";
652 if ((Channel != null) && (Channel != ""))
653 if (!UUID.TryParse(Channel, out parseUID))
654 mName = Channel;
655 else
656 param["Channel"] = Channel;
657
658 param["StringValue"] = Sdata;
659 param["IntValue"] = Convert.ToString(Idata);
660
661 ArrayList parameters = new ArrayList();
662 parameters.Add(param);
663 XmlRpcRequest req = new XmlRpcRequest(mName, parameters);
664 try
665 {
666 XmlRpcResponse resp = req.Send(DestURL, 30000);
667 if (resp != null)
668 {
669 Hashtable respParms;
670 if (resp.Value.GetType().Equals(typeof(System.Collections.Hashtable)))
671 {
672 respParms = (Hashtable) resp.Value;
673 }
674 else
675 {
676 ArrayList respData = (ArrayList) resp.Value;
677 respParms = (Hashtable) respData[0];
678 }
679 if (respParms != null)
680 {
681 if (respParms.Contains("StringValue"))
682 {
683 Sdata = (string) respParms["StringValue"];
684 }
685 if (respParms.Contains("IntValue"))
686 {
687 Idata = Convert.ToInt32((string) respParms["IntValue"]);
688 }
689 if (respParms.Contains("faultString"))
690 {
691 Sdata = (string) respParms["faultString"];
692 }
693 if (respParms.Contains("faultCode"))
694 {
695 Idata = Convert.ToInt32(respParms["faultCode"]);
696 }
697 }
698 }
699 }
700 catch (Exception we)
701 {
702 Sdata = we.Message;
703 m_log.Warn("[SendRemoteDataRequest]: Request failed");
704 m_log.Warn(we.StackTrace);
705 }
706
707 _finished = true;
708 }
709
710 public void Stop()
711 {
712 try
713 {
714 httpThread.Abort();
715 }
716 catch (Exception)
717 {
718 }
719 }
720
721 public UUID GetReqID()
722 {
723 return ReqID;
724 }
725 }
726}