opensim-development – Blame information for rev 1

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