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