diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs | 297 |
1 files changed, 196 insertions, 101 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs index 2462ff8..efb9421 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs | |||
@@ -50,6 +50,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
51 | 51 | ||
52 | private bool m_Enabled = false; | 52 | private bool m_Enabled = false; |
53 | protected string m_MessageKey = String.Empty; | ||
53 | protected List<Scene> m_Scenes = new List<Scene>(); | 54 | protected List<Scene> m_Scenes = new List<Scene>(); |
54 | protected Dictionary<UUID, UUID> m_UserRegionMap = new Dictionary<UUID, UUID>(); | 55 | protected Dictionary<UUID, UUID> m_UserRegionMap = new Dictionary<UUID, UUID>(); |
55 | 56 | ||
@@ -69,14 +70,17 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
69 | public virtual void Initialise(IConfigSource config) | 70 | public virtual void Initialise(IConfigSource config) |
70 | { | 71 | { |
71 | IConfig cnf = config.Configs["Messaging"]; | 72 | IConfig cnf = config.Configs["Messaging"]; |
72 | if (cnf != null && cnf.GetString( | 73 | if (cnf != null) |
73 | "MessageTransferModule", "MessageTransferModule") != | ||
74 | "MessageTransferModule") | ||
75 | { | 74 | { |
76 | m_log.Debug("[MESSAGE TRANSFER]: Disabled by configuration"); | 75 | if (cnf.GetString("MessageTransferModule", |
77 | return; | 76 | "MessageTransferModule") != "MessageTransferModule") |
78 | } | 77 | { |
78 | return; | ||
79 | } | ||
79 | 80 | ||
81 | m_MessageKey = cnf.GetString("MessageKey", String.Empty); | ||
82 | } | ||
83 | m_log.Debug("[MESSAGE TRANSFER]: Module enabled"); | ||
80 | m_Enabled = true; | 84 | m_Enabled = true; |
81 | } | 85 | } |
82 | 86 | ||
@@ -135,53 +139,45 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
135 | { | 139 | { |
136 | UUID toAgentID = new UUID(im.toAgentID); | 140 | UUID toAgentID = new UUID(im.toAgentID); |
137 | 141 | ||
142 | if (toAgentID == UUID.Zero) | ||
143 | return; | ||
144 | |||
145 | IClientAPI client = null; | ||
146 | |||
138 | // Try root avatar only first | 147 | // Try root avatar only first |
139 | foreach (Scene scene in m_Scenes) | 148 | foreach (Scene scene in m_Scenes) |
140 | { | 149 | { |
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); | 150 | ScenePresence sp = scene.GetScenePresence(toAgentID); |
146 | if (sp != null && !sp.IsChildAgent) | 151 | if (sp != null && !sp.IsDeleted && sp.ControllingClient.IsActive) |
147 | { | 152 | { |
148 | // Local message | 153 | // actualy don't send via child agents |
149 | // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID); | 154 | // ims can be complex things, and not sure viewers will not mess up |
150 | 155 | if(sp.IsChildAgent) | |
151 | sp.ControllingClient.SendInstantMessage(im); | 156 | continue; |
152 | 157 | ||
153 | // Message sent | 158 | client = sp.ControllingClient; |
154 | result(true); | 159 | if(!sp.IsChildAgent) |
155 | return; | 160 | break; |
156 | } | 161 | } |
157 | } | 162 | } |
158 | 163 | ||
159 | // try child avatar second | 164 | if(client != null) |
160 | foreach (Scene scene in m_Scenes) | ||
161 | { | 165 | { |
162 | // m_log.DebugFormat( | 166 | // Local message |
163 | // "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName); | 167 | // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID); |
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 | 168 | ||
171 | sp.ControllingClient.SendInstantMessage(im); | 169 | client.SendInstantMessage(im); |
172 | 170 | ||
173 | // Message sent | 171 | // Message sent |
174 | result(true); | 172 | result(true); |
175 | return; | 173 | return; |
176 | } | ||
177 | } | 174 | } |
178 | |||
179 | // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID); | 175 | // m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID); |
180 | 176 | ||
181 | SendGridInstantMessageViaXMLRPC(im, result); | 177 | SendGridInstantMessageViaXMLRPC(im, result); |
182 | } | 178 | } |
183 | 179 | ||
184 | public void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result) | 180 | public virtual void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result) |
185 | { | 181 | { |
186 | UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage; | 182 | UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage; |
187 | 183 | ||
@@ -211,10 +207,10 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
211 | protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request, IPEndPoint remoteClient) | 207 | protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request, IPEndPoint remoteClient) |
212 | { | 208 | { |
213 | bool successful = false; | 209 | bool successful = false; |
214 | 210 | ||
215 | // TODO: For now, as IMs seem to be a bit unreliable on OSGrid, catch all exception that | 211 | // 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. | 212 | // happen here and aren't caught and log them. |
217 | try | 213 | try |
218 | { | 214 | { |
219 | // various rational defaults | 215 | // various rational defaults |
220 | UUID fromAgentID = UUID.Zero; | 216 | UUID fromAgentID = UUID.Zero; |
@@ -236,7 +232,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
236 | float pos_z = 0; | 232 | float pos_z = 0; |
237 | //m_log.Info("Processing IM"); | 233 | //m_log.Info("Processing IM"); |
238 | 234 | ||
239 | |||
240 | Hashtable requestData = (Hashtable)request.Params[0]; | 235 | Hashtable requestData = (Hashtable)request.Params[0]; |
241 | // Check if it's got all the data | 236 | // Check if it's got all the data |
242 | if (requestData.ContainsKey("from_agent_id") | 237 | if (requestData.ContainsKey("from_agent_id") |
@@ -249,6 +244,19 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
249 | && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id") | 244 | && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id") |
250 | && requestData.ContainsKey("binary_bucket")) | 245 | && requestData.ContainsKey("binary_bucket")) |
251 | { | 246 | { |
247 | if (m_MessageKey != String.Empty) | ||
248 | { | ||
249 | XmlRpcResponse error_resp = new XmlRpcResponse(); | ||
250 | Hashtable error_respdata = new Hashtable(); | ||
251 | error_respdata["success"] = "FALSE"; | ||
252 | error_resp.Value = error_respdata; | ||
253 | |||
254 | if (!requestData.Contains("message_key")) | ||
255 | return error_resp; | ||
256 | if (m_MessageKey != (string)requestData["message_key"]) | ||
257 | return error_resp; | ||
258 | } | ||
259 | |||
252 | // Do the easy way of validating the UUIDs | 260 | // Do the easy way of validating the UUIDs |
253 | UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID); | 261 | UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID); |
254 | UUID.TryParse((string)requestData["to_agent_id"], out toAgentID); | 262 | UUID.TryParse((string)requestData["to_agent_id"], out toAgentID); |
@@ -382,7 +390,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
382 | gim.Position = Position; | 390 | gim.Position = Position; |
383 | gim.binaryBucket = binaryBucket; | 391 | gim.binaryBucket = binaryBucket; |
384 | 392 | ||
385 | |||
386 | // Trigger the Instant message in the scene. | 393 | // Trigger the Instant message in the scene. |
387 | foreach (Scene scene in m_Scenes) | 394 | foreach (Scene scene in m_Scenes) |
388 | { | 395 | { |
@@ -425,104 +432,191 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
425 | return resp; | 432 | return resp; |
426 | } | 433 | } |
427 | 434 | ||
435 | |||
428 | /// <summary> | 436 | /// <summary> |
429 | /// delegate for sending a grid instant message asynchronously | 437 | /// delegate for sending a grid instant message asynchronously |
430 | /// </summary> | 438 | /// </summary> |
431 | public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result); | 439 | private delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result); |
440 | |||
441 | private class GIM { | ||
442 | public GridInstantMessage im; | ||
443 | public MessageResultNotification result; | ||
444 | }; | ||
432 | 445 | ||
433 | protected virtual void GridInstantMessageCompleted(IAsyncResult iar) | 446 | private Queue<GIM> pendingInstantMessages = new Queue<GIM>(); |
447 | private int numInstantMessageThreads = 0; | ||
448 | |||
449 | private void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result) | ||
434 | { | 450 | { |
435 | GridInstantMessageDelegate icon = | 451 | lock (pendingInstantMessages) { |
436 | (GridInstantMessageDelegate)iar.AsyncState; | 452 | if (numInstantMessageThreads >= 4) { |
437 | icon.EndInvoke(iar); | 453 | GIM gim = new GIM(); |
454 | gim.im = im; | ||
455 | gim.result = result; | ||
456 | pendingInstantMessages.Enqueue(gim); | ||
457 | } else { | ||
458 | ++ numInstantMessageThreads; | ||
459 | //m_log.DebugFormat("[SendGridInstantMessageViaXMLRPC]: ++numInstantMessageThreads={0}", numInstantMessageThreads); | ||
460 | GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsyncMain; | ||
461 | d.BeginInvoke(im, result, GridInstantMessageCompleted, d); | ||
462 | } | ||
463 | } | ||
438 | } | 464 | } |
439 | 465 | ||
440 | 466 | ||
441 | protected virtual void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result) | 467 | private void GridInstantMessageCompleted(IAsyncResult iar) |
442 | { | 468 | { |
443 | GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync; | 469 | GridInstantMessageDelegate d = (GridInstantMessageDelegate)iar.AsyncState; |
444 | 470 | d.EndInvoke(iar); | |
445 | d.BeginInvoke(im, result, GridInstantMessageCompleted, d); | ||
446 | } | 471 | } |
447 | 472 | ||
448 | /// <summary> | 473 | /// <summary> |
449 | /// Internal SendGridInstantMessage over XMLRPC method. | 474 | /// Internal SendGridInstantMessage over XMLRPC method. |
450 | /// </summary> | 475 | /// </summary> |
451 | /// <remarks> | 476 | |
452 | /// This is called from within a dedicated thread. | 477 | /// <param name="prevRegionHandle"> |
453 | /// </remarks> | 478 | /// Pass in 0 the first time this method is called. It will be called recursively with the last |
454 | private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result) | 479 | /// regionhandle tried |
480 | /// </param> | ||
481 | private void SendGridInstantMessageViaXMLRPCAsyncMain(GridInstantMessage im, MessageResultNotification result) | ||
482 | { | ||
483 | GIM gim; | ||
484 | do { | ||
485 | try { | ||
486 | SendGridInstantMessageViaXMLRPCAsync(im, result, UUID.Zero); | ||
487 | } catch (Exception e) { | ||
488 | m_log.Error("[SendGridInstantMessageViaXMLRPC]: exception " + e.Message); | ||
489 | } | ||
490 | lock (pendingInstantMessages) { | ||
491 | if (pendingInstantMessages.Count > 0) { | ||
492 | gim = pendingInstantMessages.Dequeue(); | ||
493 | im = gim.im; | ||
494 | result = gim.result; | ||
495 | } else { | ||
496 | gim = null; | ||
497 | -- numInstantMessageThreads; | ||
498 | //m_log.DebugFormat("[SendGridInstantMessageViaXMLRPC]: --numInstantMessageThreads={0}", numInstantMessageThreads); | ||
499 | } | ||
500 | } | ||
501 | } while (gim != null); | ||
502 | } | ||
503 | |||
504 | private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result, UUID prevRegionID) | ||
455 | { | 505 | { |
506 | |||
456 | UUID toAgentID = new UUID(im.toAgentID); | 507 | UUID toAgentID = new UUID(im.toAgentID); |
457 | UUID regionID; | 508 | PresenceInfo upd = null; |
458 | bool needToLookupAgent; | 509 | bool lookupAgent = false; |
459 | 510 | ||
460 | lock (m_UserRegionMap) | 511 | lock (m_UserRegionMap) |
461 | needToLookupAgent = !m_UserRegionMap.TryGetValue(toAgentID, out regionID); | ||
462 | |||
463 | while (true) | ||
464 | { | 512 | { |
465 | if (needToLookupAgent) | 513 | if (m_UserRegionMap.ContainsKey(toAgentID)) |
466 | { | 514 | { |
467 | PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() }); | 515 | upd = new PresenceInfo(); |
468 | 516 | upd.RegionID = m_UserRegionMap[toAgentID]; | |
469 | UUID foundRegionID = UUID.Zero; | ||
470 | 517 | ||
471 | if (presences != null) | 518 | // We need to compare the current regionhandle with the previous region handle |
519 | // or the recursive loop will never end because it will never try to lookup the agent again | ||
520 | if (prevRegionID == upd.RegionID) | ||
472 | { | 521 | { |
473 | foreach (PresenceInfo p in presences) | 522 | lookupAgent = true; |
474 | { | ||
475 | if (p.RegionID != UUID.Zero) | ||
476 | { | ||
477 | foundRegionID = p.RegionID; | ||
478 | break; | ||
479 | } | ||
480 | } | ||
481 | } | 523 | } |
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 | } | 524 | } |
489 | 525 | else | |
490 | GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, regionID); | ||
491 | if (reginfo == null) | ||
492 | { | 526 | { |
493 | m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", regionID); | 527 | lookupAgent = true; |
494 | break; | ||
495 | } | 528 | } |
529 | } | ||
496 | 530 | ||
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 | 531 | ||
502 | // If the message delivery was successful, then cache the entry. | 532 | // Are we needing to look-up an agent? |
503 | if (imresult) | 533 | if (lookupAgent) |
534 | { | ||
535 | // Non-cached user agent lookup. | ||
536 | PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() }); | ||
537 | if (presences != null && presences.Length > 0) | ||
504 | { | 538 | { |
505 | lock (m_UserRegionMap) | 539 | foreach (PresenceInfo p in presences) |
506 | { | 540 | { |
507 | m_UserRegionMap[toAgentID] = regionID; | 541 | if (p.RegionID != UUID.Zero) |
542 | { | ||
543 | upd = p; | ||
544 | break; | ||
545 | } | ||
508 | } | 546 | } |
509 | result(true); | ||
510 | return; | ||
511 | } | 547 | } |
512 | 548 | ||
513 | // If we reach this point in the first iteration of the while, then we may have unsuccessfully tried | 549 | if (upd != null) |
514 | // to use a locally cached region ID. All subsequent attempts need to lookup agent details from | 550 | { |
515 | // the presence service. | 551 | // check if we've tried this before.. |
516 | needToLookupAgent = true; | 552 | // This is one way to end the recursive loop |
553 | // | ||
554 | if (upd.RegionID == prevRegionID) | ||
555 | { | ||
556 | // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | ||
557 | HandleUndeliverableMessage(im, result); | ||
558 | return; | ||
559 | } | ||
560 | } | ||
561 | else | ||
562 | { | ||
563 | // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | ||
564 | HandleUndeliverableMessage(im, result); | ||
565 | return; | ||
566 | } | ||
517 | } | 567 | } |
518 | 568 | ||
519 | // If we reached this point then the message was not deliverable. Remove the bad cache entry and | 569 | if (upd != null) |
520 | // signal the delivery failure. | 570 | { |
521 | lock (m_UserRegionMap) | 571 | GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(UUID.Zero, |
522 | m_UserRegionMap.Remove(toAgentID); | 572 | upd.RegionID); |
573 | if (reginfo != null) | ||
574 | { | ||
575 | Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); | ||
576 | // Not actually used anymore, left in for compatibility | ||
577 | // Remove at next interface change | ||
578 | // | ||
579 | msgdata["region_handle"] = 0; | ||
580 | bool imresult = doIMSending(reginfo, msgdata); | ||
581 | if (imresult) | ||
582 | { | ||
583 | // IM delivery successful, so store the Agent's location in our local cache. | ||
584 | lock (m_UserRegionMap) | ||
585 | { | ||
586 | if (m_UserRegionMap.ContainsKey(toAgentID)) | ||
587 | { | ||
588 | m_UserRegionMap[toAgentID] = upd.RegionID; | ||
589 | } | ||
590 | else | ||
591 | { | ||
592 | m_UserRegionMap.Add(toAgentID, upd.RegionID); | ||
593 | } | ||
594 | } | ||
595 | result(true); | ||
596 | } | ||
597 | else | ||
598 | { | ||
599 | // try again, but lookup user this time. | ||
600 | // Warning, this must call the Async version | ||
601 | // of this method or we'll be making thousands of threads | ||
602 | // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync | ||
603 | // The version that spawns the thread is SendGridInstantMessageViaXMLRPC | ||
523 | 604 | ||
524 | // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | 605 | // This is recursive!!!!! |
525 | HandleUndeliverableMessage(im, result); | 606 | SendGridInstantMessageViaXMLRPCAsync(im, result, |
607 | upd.RegionID); | ||
608 | } | ||
609 | } | ||
610 | else | ||
611 | { | ||
612 | m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", upd.RegionID); | ||
613 | HandleUndeliverableMessage(im, result); | ||
614 | } | ||
615 | } | ||
616 | else | ||
617 | { | ||
618 | HandleUndeliverableMessage(im, result); | ||
619 | } | ||
526 | } | 620 | } |
527 | 621 | ||
528 | /// <summary> | 622 | /// <summary> |
@@ -622,8 +716,9 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
622 | gim["position_z"] = msg.Position.Z.ToString(); | 716 | gim["position_z"] = msg.Position.Z.ToString(); |
623 | gim["region_id"] = new UUID(msg.RegionID).ToString(); | 717 | gim["region_id"] = new UUID(msg.RegionID).ToString(); |
624 | gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); | 718 | gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); |
719 | if (m_MessageKey != String.Empty) | ||
720 | gim["message_key"] = m_MessageKey; | ||
625 | return gim; | 721 | return gim; |
626 | } | 722 | } |
627 | |||
628 | } | 723 | } |
629 | } | 724 | } |