aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/MessageTransferModule.cs
diff options
context:
space:
mode:
authorMelanie Thielker2008-11-16 00:47:21 +0000
committerMelanie Thielker2008-11-16 00:47:21 +0000
commit27e557eb9857ccc34ae3588c4e0ff43bd5e6644a (patch)
tree742ab8738481d93ebc03fe94c80b6333c65631b0 /OpenSim/Region/Environment/Modules/Avatar/InstantMessage/MessageTransferModule.cs
parentChanged sculpted prim texture scaling method to bilinear to reduce scaling ar... (diff)
downloadopensim-SC-27e557eb9857ccc34ae3588c4e0ff43bd5e6644a.zip
opensim-SC-27e557eb9857ccc34ae3588c4e0ff43bd5e6644a.tar.gz
opensim-SC-27e557eb9857ccc34ae3588c4e0ff43bd5e6644a.tar.bz2
opensim-SC-27e557eb9857ccc34ae3588c4e0ff43bd5e6644a.tar.xz
Introduces the message transfer module. It splits the transfer mechanics off
the IM module and makes it into a module of it's own, which can be used by all other modules. Removes some ugly hacks. Refer to the IM module to see how it's used. Also fixes the persistence issue (Mantis #2598)
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}