clockwerk-opensim – Blame information for rev 1

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