aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.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/InstantMessageModule.cs
parentChanged sculpted prim texture scaling method to bilinear to reduce scaling ar... (diff)
downloadopensim-SC_OLD-27e557eb9857ccc34ae3588c4e0ff43bd5e6644a.zip
opensim-SC_OLD-27e557eb9857ccc34ae3588c4e0ff43bd5e6644a.tar.gz
opensim-SC_OLD-27e557eb9857ccc34ae3588c4e0ff43bd5e6644a.tar.bz2
opensim-SC_OLD-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/InstantMessageModule.cs')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs689
1 files changed, 56 insertions, 633 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs
index 805f7cb..1b7eb97 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs
@@ -36,6 +36,7 @@ using Nini.Config;
36using Nwc.XmlRpc; 36using Nwc.XmlRpc;
37using OpenSim.Framework; 37using OpenSim.Framework;
38using OpenSim.Framework.Client; 38using OpenSim.Framework.Client;
39using OpenSim.Region.Interfaces;
39using OpenSim.Region.Environment.Interfaces; 40using OpenSim.Region.Environment.Interfaces;
40using OpenSim.Region.Environment.Scenes; 41using OpenSim.Region.Environment.Scenes;
41 42
@@ -46,12 +47,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage
46 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47 48
48 private readonly List<Scene> m_scenes = new List<Scene>(); 49 private readonly List<Scene> m_scenes = new List<Scene>();
49 private Dictionary<UUID, ulong> m_userRegionMap = new Dictionary<UUID, ulong>();
50 50
51 #region IRegionModule Members 51 #region IRegionModule Members
52 52
53 private bool gridmode = false; 53 private bool gridmode = false;
54 54
55 private IMessageTransferModule m_TransferModule = null;
55 56
56 public void Initialise(Scene scene, IConfigSource config) 57 public void Initialise(Scene scene, IConfigSource config)
57 { 58 {
@@ -65,18 +66,11 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage
65 66
66 lock (m_scenes) 67 lock (m_scenes)
67 { 68 {
68 if (m_scenes.Count == 0)
69 {
70 //scene.AddXmlRPCHandler("avatar_location_update", processPresenceUpdate);
71 scene.AddXmlRPCHandler("grid_instant_message", processXMLRPCGridInstantMessage);
72 ReadConfig(config);
73 }
74
75 if (!m_scenes.Contains(scene)) 69 if (!m_scenes.Contains(scene))
76 { 70 {
77 m_scenes.Add(scene); 71 m_scenes.Add(scene);
78 scene.EventManager.OnClientConnect += OnClientConnect; 72 scene.EventManager.OnClientConnect += OnClientConnect;
79 scene.EventManager.OnGridInstantMessage += OnGridInstantMessage; 73 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
80 } 74 }
81 } 75 }
82 } 76 }
@@ -90,17 +84,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage
90 } 84 }
91 } 85 }
92 86
93 private void ReadConfig(IConfigSource config)
94 {
95 IConfig cnf = config.Configs["Startup"];
96 if (cnf != null)
97 {
98 gridmode = cnf.GetBoolean("gridmode", false);
99 }
100 }
101
102 public void PostInitialise() 87 public void PostInitialise()
103 { 88 {
89 m_TransferModule =
90 m_scenes[0].RequestModuleInterface<IMessageTransferModule>();
91
92 if (m_TransferModule == null)
93 m_log.Error("[INSTANT MESSAGE]: No message transfer module, "+
94 "IM will not work!");
104 } 95 }
105 96
106 public void Close() 97 public void Close()
@@ -120,639 +111,71 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage
120 #endregion 111 #endregion
121 112
122 private void OnInstantMessage(IClientAPI client, UUID fromAgentID, 113 private void OnInstantMessage(IClientAPI client, UUID fromAgentID,
123 UUID fromAgentSession, UUID toAgentID, 114 UUID fromAgentSession, UUID toAgentID,
124 UUID imSessionID, uint timestamp, string fromAgentName, 115 UUID imSessionID, uint timestamp, string fromAgentName,
125 string message, byte dialog, bool fromGroup, byte offline, 116 string message, byte dialog, bool fromGroup, byte offline,
126 uint ParentEstateID, Vector3 Position, UUID RegionID, 117 uint ParentEstateID, Vector3 Position, UUID RegionID,
127 byte[] binaryBucket) 118 byte[] binaryBucket)
128 { 119 {
129 bool dialogHandledElsewhere 120 // This module handles exclusively private text IM from user
130 = ( dialog == (byte) InstantMessageDialog.FriendshipOffered 121 // to user. All others will be caught in other modules
131 || dialog == (byte) InstantMessageDialog.FriendshipAccepted 122 //
132 || dialog == (byte) InstantMessageDialog.FriendshipDeclined 123 if ( dialog != (byte)InstantMessageDialog.MessageFromAgent
133 || dialog == (byte) InstantMessageDialog.InventoryOffered 124 && dialog != (byte)InstantMessageDialog.StartTyping
134 || dialog == (byte) InstantMessageDialog.InventoryAccepted 125 && dialog != (byte)InstantMessageDialog.StopTyping)
135 || dialog == (byte) InstantMessageDialog.InventoryDeclined
136 || dialog == (byte) InstantMessageDialog.GroupNoticeInventoryAccepted
137 || dialog == (byte) InstantMessageDialog.GroupNoticeInventoryDeclined
138 || dialog == (byte) InstantMessageDialog.GroupInvitationAccept
139 || dialog == (byte) InstantMessageDialog.GroupInvitationDecline
140 || dialog == (byte) InstantMessageDialog.GroupNotice);
141
142 // IM dialogs need to be pre-processed and have their sessionID filled by the server
143 // so the sim can match the transaction on the return packet.
144
145 // Don't process IMs that are handled elsewhere (e.g. friend dialog
146 // IMs) with a non-UUID.Zero agent session, as those have been send
147 // by a client (either directly or from another region via
148 // inter-region communication) and will be processed in another
149 // module (e.g. the friends-module).
150 // IMs with fromAgentSession == UUID.Zero come from the server, and
151 // have to be passed to the matching viewer
152 if (!dialogHandledElsewhere || fromAgentSession == UUID.Zero)
153 { 126 {
154 // Try root avatar only first
155 foreach (Scene scene in m_scenes)
156 {
157 if (scene.Entities.ContainsKey(toAgentID) && scene.Entities[toAgentID] is ScenePresence)
158 {
159 // Local message
160 ScenePresence user = (ScenePresence) scene.Entities[toAgentID];
161 if (!user.IsChildAgent)
162 {
163 user.ControllingClient.SendInstantMessage(fromAgentID, message,
164 toAgentID, fromAgentName, dialog,
165 timestamp, imSessionID, fromGroup, binaryBucket);
166 // Message sent
167 return;
168 }
169 }
170 }
171
172 // try child avatar second
173 foreach (Scene scene in m_scenes)
174 {
175 if (scene.Entities.ContainsKey(toAgentID) && scene.Entities[toAgentID] is ScenePresence)
176 {
177 // Local message
178 ScenePresence user = (ScenePresence) scene.Entities[toAgentID];
179
180 user.ControllingClient.SendInstantMessage(fromAgentID, message,
181 toAgentID, fromAgentName, dialog,
182 timestamp, imSessionID, fromGroup, binaryBucket);
183 // Message sent
184 return;
185 }
186 }
187 if (gridmode)
188 {
189 // Still here, try send via Grid
190
191 // don't send session drop yet, as it's not reliable somehow.
192 if (dialog != (byte)InstantMessageDialog.SessionDrop)
193 {
194 SendGridInstantMessageViaXMLRPC(client, fromAgentID,
195 fromAgentSession, toAgentID,
196 imSessionID, timestamp, fromAgentName,
197 message, dialog, fromGroup, offline,
198 ParentEstateID, Position, RegionID,
199 binaryBucket, getLocalRegionHandleFromUUID(RegionID), 0);
200 }
201 }
202 else
203 {
204 if (client != null)
205 {
206 if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop)
207 client.SendInstantMessage(toAgentID, "Unable to send instant message. User is not logged in.", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch());// SendAlertMessage("Unable to send instant message");
208 }
209 }
210 }
211
212
213 }
214
215 // Trusty OSG1 called method. This method also gets called from the FriendsModule
216 // Turns out the sim has to send an instant message to the user to get it to show an accepted friend.
217 /// <summary>
218 ///
219 /// </summary>
220 /// <param name="msg"></param>
221 private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver which)
222 {
223 if ((which & InstantMessageReceiver.IMModule) == 0)
224 return; 127 return;
225
226 // Trigger the above event handler
227 OnInstantMessage(null, new UUID(msg.fromAgentID), new UUID(msg.fromAgentSession),
228 new UUID(msg.toAgentID), new UUID(msg.imSessionID), msg.timestamp, msg.fromAgentName,
229 msg.message, msg.dialog, msg.fromGroup, msg.offline, msg.ParentEstateID,
230 new Vector3(msg.Position.X, msg.Position.Y, msg.Position.Z), new UUID(msg.RegionID),
231 msg.binaryBucket);
232 }
233
234
235 /// <summary>
236 /// Process a XMLRPC Grid Instant Message
237 /// </summary>
238 /// <param name="request">XMLRPC parameters from_agent_id from_agent_session to_agent_id im_session_id timestamp
239 /// from_agent_name message dialog from_group offline parent_estate_id position_x position_y position_z region_id
240 /// binary_bucket region_handle</param>
241 /// <returns>Nothing much</returns>
242 protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request)
243 {
244 bool successful = false;
245 // various rational defaults
246 UUID fromAgentID = UUID.Zero;
247 UUID fromAgentSession = UUID.Zero;
248 UUID toAgentID = UUID.Zero;
249 UUID imSessionID = UUID.Zero;
250 uint timestamp = 0;
251 string fromAgentName = "";
252 string message = "";
253 byte dialog = (byte)0;
254 bool fromGroup = false;
255 byte offline = (byte)0;
256 uint ParentEstateID=0;
257 Vector3 Position = Vector3.Zero;
258 UUID RegionID = UUID.Zero ;
259 byte[] binaryBucket = new byte[0];
260
261 float pos_x = 0;
262 float pos_y = 0;
263 float pos_z = 0;
264 //m_log.Info("Processing IM");
265
266
267 Hashtable requestData = (Hashtable)request.Params[0];
268 // Check if it's got all the data
269 if (requestData.ContainsKey("from_agent_id") && requestData.ContainsKey("from_agent_session")
270 && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id")
271 && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name")
272 && requestData.ContainsKey("message") && requestData.ContainsKey("dialog")
273 && requestData.ContainsKey("from_group")
274 && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id")
275 && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y")
276 && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id")
277 && requestData.ContainsKey("binary_bucket") && requestData.ContainsKey("region_handle"))
278 {
279 // Do the easy way of validating the UUIDs
280 UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID);
281 UUID.TryParse((string)requestData["from_agent_session"], out fromAgentSession);
282 UUID.TryParse((string)requestData["to_agent_id"], out toAgentID);
283 UUID.TryParse((string)requestData["im_session_id"], out imSessionID);
284 UUID.TryParse((string)requestData["region_id"], out RegionID);
285
286 # region timestamp
287 try
288 {
289 timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]);
290 }
291 catch (ArgumentException)
292 {
293 }
294 catch (FormatException)
295 {
296 }
297 catch (OverflowException)
298 {
299 }
300 # endregion
301
302 fromAgentName = (string)requestData["from_agent_name"];
303 message = (string)requestData["message"];
304
305 // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them.
306 string requestData1 = (string)requestData["dialog"];
307 if (string.IsNullOrEmpty(requestData1))
308 {
309 dialog = 0;
310 }
311 else
312 {
313 byte[] dialogdata = Convert.FromBase64String(requestData1);
314 dialog = dialogdata[0];
315 }
316
317 if ((string)requestData["from_group"] == "TRUE")
318 fromGroup = true;
319
320 string requestData2 = (string)requestData["offline"];
321 if (String.IsNullOrEmpty(requestData2))
322 {
323 offline = 0;
324 }
325 else
326 {
327 byte[] offlinedata = Convert.FromBase64String(requestData2);
328 offline = offlinedata[0];
329 }
330
331 # region ParentEstateID
332 try
333 {
334 ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]);
335 }
336 catch (ArgumentException)
337 {
338 }
339 catch (FormatException)
340 {
341 }
342 catch (OverflowException)
343 {
344 }
345 # endregion
346
347 # region pos_x
348 try
349 {
350 pos_x = (uint)Convert.ToInt32((string)requestData["position_x"]);
351 }
352 catch (ArgumentException)
353 {
354 }
355 catch (FormatException)
356 {
357 }
358 catch (OverflowException)
359 {
360 }
361 # endregion
362 # region pos_y
363 try
364 {
365 pos_y = (uint)Convert.ToInt32((string)requestData["position_y"]);
366 }
367 catch (ArgumentException)
368 {
369 }
370 catch (FormatException)
371 {
372 }
373 catch (OverflowException)
374 {
375 }
376 # endregion
377 # region pos_z
378 try
379 {
380 pos_z = (uint)Convert.ToInt32((string)requestData["position_z"]);
381 }
382 catch (ArgumentException)
383 {
384 }
385 catch (FormatException)
386 {
387 }
388 catch (OverflowException)
389 {
390 }
391 # endregion
392
393 Position = new Vector3(pos_x, pos_y, pos_z);
394
395 string requestData3 = (string)requestData["binary_bucket"];
396 if (string.IsNullOrEmpty(requestData3))
397 {
398 binaryBucket = new byte[0];
399 }
400 else
401 {
402 binaryBucket = Convert.FromBase64String(requestData3);
403 }
404
405 // Create a New GridInstantMessageObject the the data
406 GridInstantMessage gim = new GridInstantMessage();
407 gim.fromAgentID = fromAgentID.Guid;
408 gim.fromAgentName = fromAgentName;
409 gim.fromAgentSession = fromAgentSession.Guid;
410 gim.fromGroup = fromGroup;
411 gim.imSessionID = imSessionID.Guid;
412 gim.RegionID = RegionID.Guid;
413 gim.timestamp = timestamp;
414 gim.toAgentID = toAgentID.Guid;
415 gim.message = message;
416 gim.dialog = dialog;
417 gim.offline = offline;
418 gim.ParentEstateID = ParentEstateID;
419 gim.Position = Position;
420 gim.binaryBucket = binaryBucket;
421
422
423 // Trigger the Instant message in the scene.
424 foreach (Scene scene in m_scenes)
425 {
426 if (scene.Entities.ContainsKey(toAgentID) && scene.Entities[toAgentID] is ScenePresence)
427 {
428 // Local message
429 ScenePresence user = (ScenePresence)scene.Entities[toAgentID];
430 if (!user.IsChildAgent)
431 {
432 scene.EventManager.TriggerGridInstantMessage(gim, InstantMessageReceiver.FriendsModule | InstantMessageReceiver.GroupsModule | InstantMessageReceiver.IMModule);
433 successful = true;
434 }
435 }
436 }
437 //OnGridInstantMessage(gim);
438
439 } 128 }
440 129
441 //Send response back to region calling if it was successful 130 GridInstantMessage im = new GridInstantMessage(client.Scene,
442 // calling region uses this to know when to look up a user's location again. 131 fromAgentID, fromAgentName, fromAgentSession, toAgentID,
443 XmlRpcResponse resp = new XmlRpcResponse(); 132 dialog, fromGroup, message, imSessionID,
444 Hashtable respdata = new Hashtable(); 133 offline != 0 ? true : false, Position,
445 if (successful) 134 binaryBucket);
446 respdata["success"] = "TRUE";
447 else
448 respdata["success"] = "FALSE";
449 resp.Value = respdata;
450
451 return resp;
452 }
453
454 #region Asynchronous setup
455 /// <summary>
456 /// delegate for sending a grid instant message asynchronously
457 /// </summary>
458 /// <param name="client"></param>
459 /// <param name="fromAgentID"></param>
460 /// <param name="fromAgentSession"></param>
461 /// <param name="toAgentID"></param>
462 /// <param name="imSessionID"></param>
463 /// <param name="timestamp"></param>
464 /// <param name="fromAgentName"></param>
465 /// <param name="message"></param>
466 /// <param name="dialog"></param>
467 /// <param name="fromGroup"></param>
468 /// <param name="offline"></param>
469 /// <param name="ParentEstateID"></param>
470 /// <param name="Position"></param>
471 /// <param name="RegionID"></param>
472 /// <param name="binaryBucket"></param>
473 /// <param name="regionhandle"></param>
474 /// <param name="prevRegionHandle"></param>
475 public delegate void GridInstantMessageDelegate(IClientAPI client, UUID fromAgentID,
476 UUID fromAgentSession, UUID toAgentID,
477 UUID imSessionID, uint timestamp, string fromAgentName,
478 string message, byte dialog, bool fromGroup, byte offline,
479 uint ParentEstateID, Vector3 Position, UUID RegionID,
480 byte[] binaryBucket, ulong regionhandle, ulong prevRegionHandle);
481
482 private void GridInstantMessageCompleted(IAsyncResult iar)
483 {
484 GridInstantMessageDelegate icon = (GridInstantMessageDelegate)iar.AsyncState;
485 icon.EndInvoke(iar);
486 }
487
488
489 protected virtual void SendGridInstantMessageViaXMLRPC(IClientAPI client, UUID fromAgentID,
490 UUID fromAgentSession, UUID toAgentID,
491 UUID imSessionID, uint timestamp, string fromAgentName,
492 string message, byte dialog, bool fromGroup, byte offline,
493 uint ParentEstateID, Vector3 Position, UUID RegionID,
494 byte[] binaryBucket, ulong regionhandle, ulong prevRegionHandle)
495 {
496 GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync;
497
498 d.BeginInvoke(client,fromAgentID,
499 fromAgentSession,toAgentID,
500 imSessionID,timestamp, fromAgentName,
501 message, dialog, fromGroup, offline,
502 ParentEstateID, Position, RegionID,
503 binaryBucket, regionhandle, prevRegionHandle,
504 GridInstantMessageCompleted,
505 d);
506 }
507
508 #endregion
509
510
511 /// <summary>
512 /// Recursive SendGridInstantMessage over XMLRPC method. The prevRegionHandle contains the last regionhandle tried
513 /// if it's the same as the user's looked up region handle, then we end the recursive loop
514 /// </summary>
515 /// <param name="prevRegionHandle"></param>
516 protected virtual void SendGridInstantMessageViaXMLRPCAsync(IClientAPI client, UUID fromAgentID,
517 UUID fromAgentSession, UUID toAgentID,
518 UUID imSessionID, uint timestamp, string fromAgentName,
519 string message, byte dialog, bool fromGroup, byte offline,
520 uint ParentEstateID, Vector3 Position, UUID RegionID,
521 byte[] binaryBucket, ulong regionhandle, ulong prevRegionHandle)
522 {
523 UserAgentData upd = null;
524
525 bool lookupAgent = false;
526
527 lock (m_userRegionMap)
528 {
529 if (m_userRegionMap.ContainsKey(toAgentID) && prevRegionHandle == 0)
530 {
531 upd = new UserAgentData();
532 upd.AgentOnline = true;
533 upd.Handle = m_userRegionMap[toAgentID];
534
535 }
536 else
537 {
538 lookupAgent = true;
539
540 135
541 } 136 if (m_TransferModule != null)
542 }
543
544 // Are we needing to look-up an agent?
545 if (lookupAgent)
546 { 137 {
547 // Non-cached user agent lookup. 138 m_TransferModule.SendInstantMessage(im,
548 upd = m_scenes[0].CommsManager.UserService.GetAgentByUUID(toAgentID); 139 delegate(bool success)
549
550 if (upd != null)
551 {
552 // check if we've tried this before.. This is one way to end the recursive loop
553 if (upd.Handle == prevRegionHandle)
554 { 140 {
555 m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); 141 if (dialog == (uint)InstantMessageDialog.StartTyping ||
556 if (client != null) 142 dialog == (uint)InstantMessageDialog.StopTyping)
557 { 143 {
558 if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop) 144 return;
559 client.SendInstantMessage(toAgentID, "Unable to send instant message", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch());
560 } 145 }
561 return;
562 }
563 }
564 else
565 {
566 m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
567 if (client != null)
568 {
569 if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop)
570 client.SendInstantMessage(toAgentID, "Unable to send instant message", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch());
571 }
572 return;
573 }
574 }
575 146
576 if (upd != null) 147 if ((client != null) && !success)
577 {
578 if (upd.AgentOnline)
579 {
580 RegionInfo reginfo = m_scenes[0].SceneGridService.RequestNeighbouringRegionInfo(upd.Handle);
581 if (reginfo != null)
582 {
583 GridInstantMessage msg = new GridInstantMessage();
584 msg.fromAgentID = fromAgentID.Guid;
585 msg.fromAgentSession = fromAgentSession.Guid;
586 msg.toAgentID = toAgentID.Guid;
587 msg.imSessionID = imSessionID.Guid;
588 msg.timestamp = timestamp;
589 msg.fromAgentName = fromAgentName;
590 msg.message = message;
591 msg.dialog = dialog;
592 msg.fromGroup = fromGroup;
593 msg.offline = offline;
594 msg.ParentEstateID = ParentEstateID;
595 msg.Position = Position;
596 msg.RegionID = RegionID.Guid;
597 msg.binaryBucket = binaryBucket;
598
599 Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(msg);
600 msgdata["region_handle"] = getLocalRegionHandleFromUUID(RegionID);
601 bool imresult = doIMSending(reginfo, msgdata);
602 if (imresult)
603 { 148 {
604 // IM delivery successful, so store the Agent's location in our local cache. 149 client.SendInstantMessage(toAgentID,
605 lock (m_userRegionMap) 150 "Unable to send instant message. "+
606 { 151 "User is not logged in.",
607 if (m_userRegionMap.ContainsKey(toAgentID)) 152 fromAgentID, "System",
608 { 153 (byte)InstantMessageDialog.BusyAutoResponse,
609 m_userRegionMap[toAgentID] = upd.Handle; 154 (uint)Util.UnixTimeSinceEpoch());
610 }
611 else
612 {
613 m_userRegionMap.Add(toAgentID, upd.Handle);
614 }
615 }
616 //m_log.Info("[GRID INSTANT MESSAGE]: Successfully sent a message");
617 }
618 else
619 {
620 // try again, but lookup user this time.
621 // Warning, this must call the Async version
622 // of this method or we'll be making thousands of threads
623 // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync
624 // The version that spawns the thread is SendGridInstantMessageViaXMLRPC
625
626 // This is recursive!!!!!
627 SendGridInstantMessageViaXMLRPCAsync(client, fromAgentID,
628 fromAgentSession, toAgentID,
629 imSessionID, timestamp, fromAgentName,
630 message, dialog, fromGroup, offline,
631 ParentEstateID, Position, RegionID,
632 binaryBucket, regionhandle, upd.Handle);
633 } 155 }
634
635 }
636 }
637 else
638 {
639 // send Agent Offline message
640 if (client != null)
641 {
642 if (dialog != (byte)InstantMessageDialog.StartTyping && dialog != (byte)InstantMessageDialog.StopTyping && dialog != (byte)InstantMessageDialog.SessionDrop)
643 client.SendInstantMessage(toAgentID, "Unable to send instant message: Agent Offline", fromAgentID, "System", (byte)InstantMessageDialog.BusyAutoResponse, (uint)Util.UnixTimeSinceEpoch());// SendAlertMessage("Unable to send instant message");
644 }
645 }
646 }
647 else
648 {
649 // send Agent doesn't exist message
650 if (client != null)
651 client.SendInstantMessage(toAgentID, "Unable to send instant message: Are you sure this agent exists anymore?", fromAgentID, "System", (byte)InstantMessageDialog.MessageFromObject, (uint)Util.UnixTimeSinceEpoch());// SendAlertMessage("Unable to send instant message");
652 }
653
654 }
655
656 /// <summary>
657 /// This actually does the XMLRPC Request
658 /// </summary>
659 /// <param name="reginfo">RegionInfo we pull the data out of to send the request to</param>
660 /// <param name="xmlrpcdata">The Instant Message data Hashtable</param>
661 /// <returns>Bool if the message was successfully delivered at the other side.</returns>
662 private bool doIMSending(RegionInfo reginfo, Hashtable xmlrpcdata)
663 {
664
665 ArrayList SendParams = new ArrayList();
666 SendParams.Add(xmlrpcdata);
667 XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams);
668 try
669 {
670
671 XmlRpcResponse GridResp = GridReq.Send("http://" + reginfo.ExternalHostName + ":" + reginfo.HttpPort, 3000);
672
673 Hashtable responseData = (Hashtable)GridResp.Value;
674
675 if (responseData.ContainsKey("success"))
676 {
677 if ((string)responseData["success"] == "TRUE")
678 {
679 return true;
680 }
681 else
682 {
683 return false;
684 }
685 }
686 else
687 {
688 return false;
689 }
690 }
691 catch (WebException e)
692 {
693 m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to http://{0}:{1} the host didn't respond ({2})",
694 reginfo.ExternalHostName, reginfo.HttpPort, e.Message);
695 }
696
697 return false;
698 }
699
700 /// <summary>
701 /// Get ulong region handle for region by it's Region UUID.
702 /// We use region handles over grid comms because there's all sorts of free and cool caching.
703 /// </summary>
704 /// <param name="regionID">UUID of region to get the region handle for</param>
705 /// <returns></returns>
706 private ulong getLocalRegionHandleFromUUID(UUID regionID)
707 {
708 ulong returnhandle = 0;
709
710 lock (m_scenes)
711 {
712 foreach (Scene sn in m_scenes)
713 {
714 if (sn.RegionInfo.RegionID == regionID)
715 {
716 returnhandle = sn.RegionInfo.RegionHandle;
717 break;
718 } 156 }
719 } 157 );
720 } 158 }
721 return returnhandle;
722 } 159 }
723 160
724 /// <summary> 161 /// <summary>
725 /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC 162 ///
726 /// </summary> 163 /// </summary>
727 /// <param name="msg">The GridInstantMessage object</param> 164 /// <param name="msg"></param>
728 /// <returns>Hashtable containing the XMLRPC request</returns> 165 private void OnGridInstantMessage(GridInstantMessage msg)
729 private Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg) 166 {
730 { 167 // Just call the Text IM handler above
731 Hashtable gim = new Hashtable(); 168 // This event won't be raised unless we have that agent,
732 gim["from_agent_id"] = msg.fromAgentID.ToString(); 169 // so we can depend on the above not trying to send
733 gim["from_agent_session"] = msg.fromAgentSession.ToString(); 170 // via grid again
734 gim["to_agent_id"] = msg.toAgentID.ToString(); 171 //
735 gim["im_session_id"] = msg.imSessionID.ToString(); 172 OnInstantMessage(null, new UUID(msg.fromAgentID),
736 gim["timestamp"] = msg.timestamp.ToString(); 173 new UUID(msg.fromAgentSession),
737 gim["from_agent_name"] = msg.fromAgentName; 174 new UUID(msg.toAgentID), new UUID(msg.imSessionID),
738 gim["message"] = msg.message; 175 msg.timestamp, msg.fromAgentName, msg.message,
739 byte[] dialogdata = new byte[1];dialogdata[0] = msg.dialog; 176 msg.dialog, msg.fromGroup, msg.offline,
740 gim["dialog"] = Convert.ToBase64String(dialogdata,Base64FormattingOptions.None); 177 msg.ParentEstateID, msg.Position,
741 178 new UUID(msg.RegionID), msg.binaryBucket);
742 if (msg.fromGroup)
743 gim["from_group"] = "TRUE";
744 else
745 gim["from_group"] = "FALSE";
746 byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline;
747 gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None);
748 gim["parent_estate_id"] = msg.ParentEstateID.ToString();
749 gim["position_x"] = msg.Position.X.ToString();
750 gim["position_y"] = msg.Position.Y.ToString();
751 gim["position_z"] = msg.Position.Z.ToString();
752 gim["region_id"] = msg.RegionID.ToString();
753 gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None);
754 return gim;
755 } 179 }
756
757 } 180 }
758} 181}