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 using System;
28 using System.Collections;
29 using System.Collections.Generic;
30 using System.Net;
31 using System.Reflection;
32 using log4net;
33 using Mono.Addins;
34 using Nini.Config;
35 using Nwc.XmlRpc;
36 using OpenMetaverse;
37 using OpenSim.Framework;
38 using OpenSim.Framework.Servers;
39 using OpenSim.Region.Framework.Interfaces;
40 using OpenSim.Region.Framework.Scenes;
41 using GridRegion = OpenSim.Services.Interfaces.GridRegion;
42 using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
43 using OpenSim.Services.Interfaces;
44  
45 namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
46 {
47 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MessageTransferModule")]
48 public class MessageTransferModule : ISharedRegionModule, IMessageTransferModule
49 {
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51  
52 private bool m_Enabled = false;
53 protected List<Scene> m_Scenes = new List<Scene>();
54 protected Dictionary<UUID, UUID> m_UserRegionMap = new Dictionary<UUID, UUID>();
55  
56 public event UndeliveredMessage OnUndeliveredMessage;
57  
58 private IPresenceService m_PresenceService;
59 protected IPresenceService PresenceService
60 {
61 get
62 {
63 if (m_PresenceService == null)
64 m_PresenceService = m_Scenes[0].RequestModuleInterface<IPresenceService>();
65 return m_PresenceService;
66 }
67 }
68  
69 public virtual void Initialise(IConfigSource config)
70 {
71 IConfig cnf = config.Configs["Messaging"];
72 if (cnf != null && cnf.GetString(
73 "MessageTransferModule", "MessageTransferModule") !=
74 "MessageTransferModule")
75 {
76 m_log.Debug("[MESSAGE TRANSFER]: Disabled by configuration");
77 return;
78 }
79  
80 m_Enabled = true;
81 }
82  
83 public virtual void AddRegion(Scene scene)
84 {
85 if (!m_Enabled)
86 return;
87  
88 lock (m_Scenes)
89 {
90 m_log.Debug("[MESSAGE TRANSFER]: Message transfer module active");
91 scene.RegisterModuleInterface<IMessageTransferModule>(this);
92 m_Scenes.Add(scene);
93 }
94 }
95  
96 public virtual void PostInitialise()
97 {
98 if (!m_Enabled)
99 return;
100  
101 MainServer.Instance.AddXmlRPCHandler(
102 "grid_instant_message", processXMLRPCGridInstantMessage);
103 }
104  
105 public virtual void RegionLoaded(Scene scene)
106 {
107 }
108  
109 public virtual void RemoveRegion(Scene scene)
110 {
111 if (!m_Enabled)
112 return;
113  
114 lock (m_Scenes)
115 {
116 m_Scenes.Remove(scene);
117 }
118 }
119  
120 public virtual void Close()
121 {
122 }
123  
124 public virtual string Name
125 {
126 get { return "MessageTransferModule"; }
127 }
128  
129 public virtual Type ReplaceableInterface
130 {
131 get { return null; }
132 }
133  
134 public virtual void SendInstantMessage(GridInstantMessage im, MessageResultNotification result)
135 {
136 UUID toAgentID = new UUID(im.toAgentID);
137  
138 // Try root avatar only first
139 foreach (Scene scene in m_Scenes)
140 {
141 // m_log.DebugFormat(
142 // "[INSTANT MESSAGE]: Looking for root agent {0} in {1}",
143 // toAgentID.ToString(), scene.RegionInfo.RegionName);
144  
145 ScenePresence sp = scene.GetScenePresence(toAgentID);
146 if (sp != null && !sp.IsChildAgent)
147 {
148 // Local message
149 // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID);
150  
151 sp.ControllingClient.SendInstantMessage(im);
152  
153 // Message sent
154 result(true);
155 return;
156 }
157 }
158  
159 // try child avatar second
160 foreach (Scene scene in m_Scenes)
161 {
162 // m_log.DebugFormat(
163 // "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName);
164  
165 ScenePresence sp = scene.GetScenePresence(toAgentID);
166 if (sp != null)
167 {
168 // Local message
169 // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID);
170  
171 sp.ControllingClient.SendInstantMessage(im);
172  
173 // Message sent
174 result(true);
175 return;
176 }
177 }
178  
179 // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID);
180  
181 SendGridInstantMessageViaXMLRPC(im, result);
182 }
183  
184 public void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result)
185 {
186 UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage;
187  
188 // If this event has handlers, then an IM from an agent will be
189 // considered delivered. This will suppress the error message.
190 //
191 if (handlerUndeliveredMessage != null)
192 {
193 handlerUndeliveredMessage(im);
194 if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent)
195 result(true);
196 else
197 result(false);
198 return;
199 }
200  
201 //m_log.DebugFormat("[INSTANT MESSAGE]: Undeliverable");
202 result(false);
203 }
204  
205 /// <summary>
206 /// Process a XMLRPC Grid Instant Message
207 /// </summary>
208 /// <param name="request">XMLRPC parameters
209 /// </param>
210 /// <returns>Nothing much</returns>
211 protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request, IPEndPoint remoteClient)
212 {
213 bool successful = false;
214  
215 // TODO: For now, as IMs seem to be a bit unreliable on OSGrid, catch all exception that
216 // happen here and aren't caught and log them.
217 try
218 {
219 // various rational defaults
220 UUID fromAgentID = UUID.Zero;
221 UUID toAgentID = UUID.Zero;
222 UUID imSessionID = UUID.Zero;
223 uint timestamp = 0;
224 string fromAgentName = "";
225 string message = "";
226 byte dialog = (byte)0;
227 bool fromGroup = false;
228 byte offline = (byte)0;
229 uint ParentEstateID=0;
230 Vector3 Position = Vector3.Zero;
231 UUID RegionID = UUID.Zero ;
232 byte[] binaryBucket = new byte[0];
233  
234 float pos_x = 0;
235 float pos_y = 0;
236 float pos_z = 0;
237 //m_log.Info("Processing IM");
238  
239  
240 Hashtable requestData = (Hashtable)request.Params[0];
241 // Check if it's got all the data
242 if (requestData.ContainsKey("from_agent_id")
243 && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id")
244 && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name")
245 && requestData.ContainsKey("message") && requestData.ContainsKey("dialog")
246 && requestData.ContainsKey("from_group")
247 && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id")
248 && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y")
249 && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id")
250 && requestData.ContainsKey("binary_bucket"))
251 {
252 // Do the easy way of validating the UUIDs
253 UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID);
254 UUID.TryParse((string)requestData["to_agent_id"], out toAgentID);
255 UUID.TryParse((string)requestData["im_session_id"], out imSessionID);
256 UUID.TryParse((string)requestData["region_id"], out RegionID);
257  
258 try
259 {
260 timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]);
261 }
262 catch (ArgumentException)
263 {
264 }
265 catch (FormatException)
266 {
267 }
268 catch (OverflowException)
269 {
270 }
271  
272 fromAgentName = (string)requestData["from_agent_name"];
273 message = (string)requestData["message"];
274 if (message == null)
275 message = string.Empty;
276  
277 // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them.
278 string requestData1 = (string)requestData["dialog"];
279 if (string.IsNullOrEmpty(requestData1))
280 {
281 dialog = 0;
282 }
283 else
284 {
285 byte[] dialogdata = Convert.FromBase64String(requestData1);
286 dialog = dialogdata[0];
287 }
288  
289 if ((string)requestData["from_group"] == "TRUE")
290 fromGroup = true;
291  
292 string requestData2 = (string)requestData["offline"];
293 if (String.IsNullOrEmpty(requestData2))
294 {
295 offline = 0;
296 }
297 else
298 {
299 byte[] offlinedata = Convert.FromBase64String(requestData2);
300 offline = offlinedata[0];
301 }
302  
303 try
304 {
305 ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]);
306 }
307 catch (ArgumentException)
308 {
309 }
310 catch (FormatException)
311 {
312 }
313 catch (OverflowException)
314 {
315 }
316  
317 try
318 {
319 pos_x = (uint)Convert.ToInt32((string)requestData["position_x"]);
320 }
321 catch (ArgumentException)
322 {
323 }
324 catch (FormatException)
325 {
326 }
327 catch (OverflowException)
328 {
329 }
330 try
331 {
332 pos_y = (uint)Convert.ToInt32((string)requestData["position_y"]);
333 }
334 catch (ArgumentException)
335 {
336 }
337 catch (FormatException)
338 {
339 }
340 catch (OverflowException)
341 {
342 }
343 try
344 {
345 pos_z = (uint)Convert.ToInt32((string)requestData["position_z"]);
346 }
347 catch (ArgumentException)
348 {
349 }
350 catch (FormatException)
351 {
352 }
353 catch (OverflowException)
354 {
355 }
356  
357 Position = new Vector3(pos_x, pos_y, pos_z);
358  
359 string requestData3 = (string)requestData["binary_bucket"];
360 if (string.IsNullOrEmpty(requestData3))
361 {
362 binaryBucket = new byte[0];
363 }
364 else
365 {
366 binaryBucket = Convert.FromBase64String(requestData3);
367 }
368  
369 // Create a New GridInstantMessageObject the the data
370 GridInstantMessage gim = new GridInstantMessage();
371 gim.fromAgentID = fromAgentID.Guid;
372 gim.fromAgentName = fromAgentName;
373 gim.fromGroup = fromGroup;
374 gim.imSessionID = imSessionID.Guid;
375 gim.RegionID = RegionID.Guid;
376 gim.timestamp = timestamp;
377 gim.toAgentID = toAgentID.Guid;
378 gim.message = message;
379 gim.dialog = dialog;
380 gim.offline = offline;
381 gim.ParentEstateID = ParentEstateID;
382 gim.Position = Position;
383 gim.binaryBucket = binaryBucket;
384  
385  
386 // Trigger the Instant message in the scene.
387 foreach (Scene scene in m_Scenes)
388 {
389 ScenePresence sp = scene.GetScenePresence(toAgentID);
390 if (sp != null && !sp.IsChildAgent)
391 {
392 scene.EventManager.TriggerIncomingInstantMessage(gim);
393 successful = true;
394 }
395 }
396 if (!successful)
397 {
398 // If the message can't be delivered to an agent, it
399 // is likely to be a group IM. On a group IM, the
400 // imSessionID = toAgentID = group id. Raise the
401 // unhandled IM event to give the groups module
402 // a chance to pick it up. We raise that in a random
403 // scene, since the groups module is shared.
404 //
405 m_Scenes[0].EventManager.TriggerUnhandledInstantMessage(gim);
406 }
407 }
408 }
409 catch (Exception e)
410 {
411 m_log.Error("[INSTANT MESSAGE]: Caught unexpected exception:", e);
412 successful = false;
413 }
414  
415 //Send response back to region calling if it was successful
416 // calling region uses this to know when to look up a user's location again.
417 XmlRpcResponse resp = new XmlRpcResponse();
418 Hashtable respdata = new Hashtable();
419 if (successful)
420 respdata["success"] = "TRUE";
421 else
422 respdata["success"] = "FALSE";
423 resp.Value = respdata;
424  
425 return resp;
426 }
427  
428 /// <summary>
429 /// delegate for sending a grid instant message asynchronously
430 /// </summary>
431 public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result);
432  
433 protected virtual void GridInstantMessageCompleted(IAsyncResult iar)
434 {
435 GridInstantMessageDelegate icon =
436 (GridInstantMessageDelegate)iar.AsyncState;
437 icon.EndInvoke(iar);
438 }
439  
440  
441 protected virtual void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result)
442 {
443 GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync;
444  
445 d.BeginInvoke(im, result, GridInstantMessageCompleted, d);
446 }
447  
448 /// <summary>
449 /// Internal SendGridInstantMessage over XMLRPC method.
450 /// </summary>
451 /// <remarks>
452 /// This is called from within a dedicated thread.
453 /// </remarks>
454 private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result)
455 {
456 UUID toAgentID = new UUID(im.toAgentID);
457 UUID regionID;
458 bool needToLookupAgent;
459  
460 lock (m_UserRegionMap)
461 needToLookupAgent = !m_UserRegionMap.TryGetValue(toAgentID, out regionID);
462  
463 while (true)
464 {
465 if (needToLookupAgent)
466 {
467 PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() });
468  
469 UUID foundRegionID = UUID.Zero;
470  
471 if (presences != null)
472 {
473 foreach (PresenceInfo p in presences)
474 {
475 if (p.RegionID != UUID.Zero)
476 {
477 foundRegionID = p.RegionID;
478 break;
479 }
480 }
481 }
482  
483 // If not found or the found region is the same as the last lookup, then message is undeliverable
484 if (foundRegionID == UUID.Zero || foundRegionID == regionID)
485 break;
486 else
487 regionID = foundRegionID;
488 }
489  
490 GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, regionID);
491 if (reginfo == null)
492 {
493 m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", regionID);
494 break;
495 }
496  
497 // Try to send the message to the agent via the retrieved region.
498 Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im);
499 msgdata["region_handle"] = 0;
500 bool imresult = doIMSending(reginfo, msgdata);
501  
502 // If the message delivery was successful, then cache the entry.
503 if (imresult)
504 {
505 lock (m_UserRegionMap)
506 {
507 m_UserRegionMap[toAgentID] = regionID;
508 }
509 result(true);
510 return;
511 }
512  
513 // If we reach this point in the first iteration of the while, then we may have unsuccessfully tried
514 // to use a locally cached region ID. All subsequent attempts need to lookup agent details from
515 // the presence service.
516 needToLookupAgent = true;
517 }
518  
519 // If we reached this point then the message was not deliverable. Remove the bad cache entry and
520 // signal the delivery failure.
521 lock (m_UserRegionMap)
522 m_UserRegionMap.Remove(toAgentID);
523  
524 // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
525 HandleUndeliverableMessage(im, result);
526 }
527  
528 /// <summary>
529 /// This actually does the XMLRPC Request
530 /// </summary>
531 /// <param name="reginfo">RegionInfo we pull the data out of to send the request to</param>
532 /// <param name="xmlrpcdata">The Instant Message data Hashtable</param>
533 /// <returns>Bool if the message was successfully delivered at the other side.</returns>
534 protected virtual bool doIMSending(GridRegion reginfo, Hashtable xmlrpcdata)
535 {
536 ArrayList SendParams = new ArrayList();
537 SendParams.Add(xmlrpcdata);
538 XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams);
539 try
540 {
541  
542 XmlRpcResponse GridResp = GridReq.Send(reginfo.ServerURI, 3000);
543  
544 Hashtable responseData = (Hashtable)GridResp.Value;
545  
546 if (responseData.ContainsKey("success"))
547 {
548 if ((string)responseData["success"] == "TRUE")
549 {
550 return true;
551 }
552 else
553 {
554 return false;
555 }
556 }
557 else
558 {
559 return false;
560 }
561 }
562 catch (WebException e)
563 {
564 m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to {0} the host didn't respond " + e.ToString(), reginfo.ServerURI.ToString());
565 }
566  
567 return false;
568 }
569  
570 /// <summary>
571 /// Get ulong region handle for region by it's Region UUID.
572 /// We use region handles over grid comms because there's all sorts of free and cool caching.
573 /// </summary>
574 /// <param name="regionID">UUID of region to get the region handle for</param>
575 /// <returns></returns>
576 // private virtual ulong getLocalRegionHandleFromUUID(UUID regionID)
577 // {
578 // ulong returnhandle = 0;
579 //
580 // lock (m_Scenes)
581 // {
582 // foreach (Scene sn in m_Scenes)
583 // {
584 // if (sn.RegionInfo.RegionID == regionID)
585 // {
586 // returnhandle = sn.RegionInfo.RegionHandle;
587 // break;
588 // }
589 // }
590 // }
591 // return returnhandle;
592 // }
593  
594 /// <summary>
595 /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC
596 /// </summary>
597 /// <param name="msg">The GridInstantMessage object</param>
598 /// <returns>Hashtable containing the XMLRPC request</returns>
599 protected virtual Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg)
600 {
601 Hashtable gim = new Hashtable();
602 gim["from_agent_id"] = msg.fromAgentID.ToString();
603 // Kept for compatibility
604 gim["from_agent_session"] = UUID.Zero.ToString();
605 gim["to_agent_id"] = msg.toAgentID.ToString();
606 gim["im_session_id"] = msg.imSessionID.ToString();
607 gim["timestamp"] = msg.timestamp.ToString();
608 gim["from_agent_name"] = msg.fromAgentName;
609 gim["message"] = msg.message;
610 byte[] dialogdata = new byte[1];dialogdata[0] = msg.dialog;
611 gim["dialog"] = Convert.ToBase64String(dialogdata,Base64FormattingOptions.None);
612  
613 if (msg.fromGroup)
614 gim["from_group"] = "TRUE";
615 else
616 gim["from_group"] = "FALSE";
617 byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline;
618 gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None);
619 gim["parent_estate_id"] = msg.ParentEstateID.ToString();
620 gim["position_x"] = msg.Position.X.ToString();
621 gim["position_y"] = msg.Position.Y.ToString();
622 gim["position_z"] = msg.Position.Z.ToString();
623 gim["region_id"] = new UUID(msg.RegionID).ToString();
624 gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None);
625 return gim;
626 }
627  
628 }
629 }