diff options
author | Dr Scofield | 2009-02-10 13:10:57 +0000 |
---|---|---|
committer | Dr Scofield | 2009-02-10 13:10:57 +0000 |
commit | 180be7de07014aa33bc6066f12a0819b731c1c9d (patch) | |
tree | 3aa13af3cda4b808fa9453655875327699b61311 /OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs | |
parent | Stopgap measure: To use gridlaunch, or GUI, start opensim with (diff) | |
download | opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.zip opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.gz opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.bz2 opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.xz |
this is step 2 of 2 of the OpenSim.Region.Environment refactor.
NOTHING has been deleted or moved off to forge at this point. what
has happened is that OpenSim.Region.Environment.Modules has been split
in two:
- OpenSim.Region.CoreModules: all those modules that are either
directly or indirectly referenced from other OpenSim packages, or
that provide functionality that the OpenSim developer community
considers core functionality:
CoreModules/Agent/AssetTransaction
CoreModules/Agent/Capabilities
CoreModules/Agent/TextureDownload
CoreModules/Agent/TextureSender
CoreModules/Agent/TextureSender/Tests
CoreModules/Agent/Xfer
CoreModules/Avatar/AvatarFactory
CoreModules/Avatar/Chat/ChatModule
CoreModules/Avatar/Combat
CoreModules/Avatar/Currency/SampleMoney
CoreModules/Avatar/Dialog
CoreModules/Avatar/Friends
CoreModules/Avatar/Gestures
CoreModules/Avatar/Groups
CoreModules/Avatar/InstantMessage
CoreModules/Avatar/Inventory
CoreModules/Avatar/Inventory/Archiver
CoreModules/Avatar/Inventory/Transfer
CoreModules/Avatar/Lure
CoreModules/Avatar/ObjectCaps
CoreModules/Avatar/Profiles
CoreModules/Communications/Local
CoreModules/Communications/REST
CoreModules/Framework/EventQueue
CoreModules/Framework/InterfaceCommander
CoreModules/Hypergrid
CoreModules/InterGrid
CoreModules/Scripting/DynamicTexture
CoreModules/Scripting/EMailModules
CoreModules/Scripting/HttpRequest
CoreModules/Scripting/LoadImageURL
CoreModules/Scripting/VectorRender
CoreModules/Scripting/WorldComm
CoreModules/Scripting/XMLRPC
CoreModules/World/Archiver
CoreModules/World/Archiver/Tests
CoreModules/World/Estate
CoreModules/World/Land
CoreModules/World/Permissions
CoreModules/World/Serialiser
CoreModules/World/Sound
CoreModules/World/Sun
CoreModules/World/Terrain
CoreModules/World/Terrain/DefaultEffects
CoreModules/World/Terrain/DefaultEffects/bin
CoreModules/World/Terrain/DefaultEffects/bin/Debug
CoreModules/World/Terrain/Effects
CoreModules/World/Terrain/FileLoaders
CoreModules/World/Terrain/FloodBrushes
CoreModules/World/Terrain/PaintBrushes
CoreModules/World/Terrain/Tests
CoreModules/World/Vegetation
CoreModules/World/Wind
CoreModules/World/WorldMap
- OpenSim.Region.OptionalModules: all those modules that are not core
modules:
OptionalModules/Avatar/Chat/IRC-stuff
OptionalModules/Avatar/Concierge
OptionalModules/Avatar/Voice/AsterixVoice
OptionalModules/Avatar/Voice/SIPVoice
OptionalModules/ContentManagementSystem
OptionalModules/Grid/Interregion
OptionalModules/Python
OptionalModules/SvnSerialiser
OptionalModules/World/NPC
OptionalModules/World/TreePopulator
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs new file mode 100644 index 0000000..91c22eb --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs | |||
@@ -0,0 +1,655 @@ | |||
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 | */ | ||
27 | using System; | ||
28 | using System.Collections; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Net; | ||
32 | using System.Threading; | ||
33 | using OpenMetaverse; | ||
34 | using log4net; | ||
35 | using Nini.Config; | ||
36 | using Nwc.XmlRpc; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Framework.Client; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | ||
43 | { | ||
44 | public class MessageTransferModule : IRegionModule, IMessageTransferModule | ||
45 | { | ||
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | |||
48 | // private bool m_Enabled = false; | ||
49 | private bool m_Gridmode = false; | ||
50 | private List<Scene> m_Scenes = new List<Scene>(); | ||
51 | private Dictionary<UUID, ulong> m_UserRegionMap = new Dictionary<UUID, ulong>(); | ||
52 | |||
53 | public void Initialise(Scene scene, IConfigSource config) | ||
54 | { | ||
55 | IConfig cnf = config.Configs["Messaging"]; | ||
56 | if (cnf != null && cnf.GetString( | ||
57 | "MessageTransferModule", "MessageTransferModule") != | ||
58 | "MessageTransferModule") | ||
59 | return; | ||
60 | |||
61 | cnf = config.Configs["Startup"]; | ||
62 | if (cnf != null) | ||
63 | m_Gridmode = cnf.GetBoolean("gridmode", false); | ||
64 | |||
65 | // m_Enabled = true; | ||
66 | |||
67 | lock (m_Scenes) | ||
68 | { | ||
69 | if (m_Scenes.Count == 0) | ||
70 | { | ||
71 | scene.CommsManager.HttpServer.AddXmlRPCHandler( | ||
72 | "grid_instant_message", processXMLRPCGridInstantMessage); | ||
73 | } | ||
74 | |||
75 | scene.RegisterModuleInterface<IMessageTransferModule>(this); | ||
76 | m_Scenes.Add(scene); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | public void PostInitialise() | ||
81 | { | ||
82 | } | ||
83 | |||
84 | public void Close() | ||
85 | { | ||
86 | } | ||
87 | |||
88 | public string Name | ||
89 | { | ||
90 | get { return "MessageTransferModule"; } | ||
91 | } | ||
92 | |||
93 | public bool IsSharedModule | ||
94 | { | ||
95 | get { return true; } | ||
96 | } | ||
97 | |||
98 | public void SendInstantMessage(GridInstantMessage im, MessageResultNotification result) | ||
99 | { | ||
100 | UUID toAgentID = new UUID(im.toAgentID); | ||
101 | |||
102 | m_log.DebugFormat("[INSTANT MESSAGE]: Attempting delivery of IM from {0} to {1}", im.fromAgentName, toAgentID.ToString()); | ||
103 | |||
104 | // Try root avatar only first | ||
105 | foreach (Scene scene in m_Scenes) | ||
106 | { | ||
107 | if (scene.Entities.ContainsKey(toAgentID) && | ||
108 | scene.Entities[toAgentID] is ScenePresence) | ||
109 | { | ||
110 | m_log.DebugFormat("[INSTANT MESSAGE]: Looking for {0} in {1}", toAgentID.ToString(), scene.RegionInfo.RegionName); | ||
111 | // Local message | ||
112 | ScenePresence user = (ScenePresence) scene.Entities[toAgentID]; | ||
113 | if (!user.IsChildAgent) | ||
114 | { | ||
115 | m_log.DebugFormat("[INSTANT MESSAGE]: Delivering to client"); | ||
116 | user.ControllingClient.SendInstantMessage( | ||
117 | new UUID(im.fromAgentID), | ||
118 | im.message, | ||
119 | new UUID(im.toAgentID), | ||
120 | im.fromAgentName, | ||
121 | im.dialog, | ||
122 | im.timestamp, | ||
123 | new UUID(im.imSessionID), | ||
124 | im.fromGroup, | ||
125 | im.binaryBucket); | ||
126 | // Message sent | ||
127 | result(true); | ||
128 | return; | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | // try child avatar second | ||
134 | foreach (Scene scene in m_Scenes) | ||
135 | { | ||
136 | // m_log.DebugFormat( | ||
137 | // "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName); | ||
138 | |||
139 | if (scene.Entities.ContainsKey(toAgentID) && | ||
140 | scene.Entities[toAgentID] is ScenePresence) | ||
141 | { | ||
142 | // Local message | ||
143 | ScenePresence user = (ScenePresence) scene.Entities[toAgentID]; | ||
144 | |||
145 | m_log.DebugFormat("[INSTANT MESSAGE]: Delivering to client"); | ||
146 | user.ControllingClient.SendInstantMessage( | ||
147 | new UUID(im.fromAgentID), | ||
148 | im.message, | ||
149 | new UUID(im.toAgentID), | ||
150 | im.fromAgentName, | ||
151 | im.dialog, | ||
152 | im.timestamp, | ||
153 | new UUID(im.imSessionID), | ||
154 | im.fromGroup, | ||
155 | im.binaryBucket); | ||
156 | // Message sent | ||
157 | result(true); | ||
158 | return; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | if (m_Gridmode) | ||
163 | { | ||
164 | //m_log.DebugFormat("[INSTANT MESSAGE]: Delivering via grid"); | ||
165 | // Still here, try send via Grid | ||
166 | SendGridInstantMessageViaXMLRPC(im, result); | ||
167 | return; | ||
168 | } | ||
169 | |||
170 | //m_log.DebugFormat("[INSTANT MESSAGE]: Undeliverable"); | ||
171 | result(false); | ||
172 | return; | ||
173 | } | ||
174 | |||
175 | /// <summary> | ||
176 | /// Process a XMLRPC Grid Instant Message | ||
177 | /// </summary> | ||
178 | /// <param name="request">XMLRPC parameters | ||
179 | /// </param> | ||
180 | /// <returns>Nothing much</returns> | ||
181 | protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request) | ||
182 | { | ||
183 | bool successful = false; | ||
184 | |||
185 | // TODO: For now, as IMs seem to be a bit unreliable on OSGrid, catch all exception that | ||
186 | // happen here and aren't caught and log them. | ||
187 | try | ||
188 | { | ||
189 | // various rational defaults | ||
190 | UUID fromAgentID = UUID.Zero; | ||
191 | UUID toAgentID = UUID.Zero; | ||
192 | UUID imSessionID = UUID.Zero; | ||
193 | uint timestamp = 0; | ||
194 | string fromAgentName = ""; | ||
195 | string message = ""; | ||
196 | byte dialog = (byte)0; | ||
197 | bool fromGroup = false; | ||
198 | byte offline = (byte)0; | ||
199 | uint ParentEstateID=0; | ||
200 | Vector3 Position = Vector3.Zero; | ||
201 | UUID RegionID = UUID.Zero ; | ||
202 | byte[] binaryBucket = new byte[0]; | ||
203 | |||
204 | float pos_x = 0; | ||
205 | float pos_y = 0; | ||
206 | float pos_z = 0; | ||
207 | //m_log.Info("Processing IM"); | ||
208 | |||
209 | |||
210 | Hashtable requestData = (Hashtable)request.Params[0]; | ||
211 | // Check if it's got all the data | ||
212 | if (requestData.ContainsKey("from_agent_id") | ||
213 | && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id") | ||
214 | && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name") | ||
215 | && requestData.ContainsKey("message") && requestData.ContainsKey("dialog") | ||
216 | && requestData.ContainsKey("from_group") | ||
217 | && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id") | ||
218 | && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y") | ||
219 | && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id") | ||
220 | && requestData.ContainsKey("binary_bucket")) | ||
221 | { | ||
222 | // Do the easy way of validating the UUIDs | ||
223 | UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID); | ||
224 | UUID.TryParse((string)requestData["to_agent_id"], out toAgentID); | ||
225 | UUID.TryParse((string)requestData["im_session_id"], out imSessionID); | ||
226 | UUID.TryParse((string)requestData["region_id"], out RegionID); | ||
227 | |||
228 | try | ||
229 | { | ||
230 | timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]); | ||
231 | } | ||
232 | catch (ArgumentException) | ||
233 | { | ||
234 | } | ||
235 | catch (FormatException) | ||
236 | { | ||
237 | } | ||
238 | catch (OverflowException) | ||
239 | { | ||
240 | } | ||
241 | |||
242 | fromAgentName = (string)requestData["from_agent_name"]; | ||
243 | message = (string)requestData["message"]; | ||
244 | |||
245 | // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them. | ||
246 | string requestData1 = (string)requestData["dialog"]; | ||
247 | if (string.IsNullOrEmpty(requestData1)) | ||
248 | { | ||
249 | dialog = 0; | ||
250 | } | ||
251 | else | ||
252 | { | ||
253 | byte[] dialogdata = Convert.FromBase64String(requestData1); | ||
254 | dialog = dialogdata[0]; | ||
255 | } | ||
256 | |||
257 | if ((string)requestData["from_group"] == "TRUE") | ||
258 | fromGroup = true; | ||
259 | |||
260 | string requestData2 = (string)requestData["offline"]; | ||
261 | if (String.IsNullOrEmpty(requestData2)) | ||
262 | { | ||
263 | offline = 0; | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | byte[] offlinedata = Convert.FromBase64String(requestData2); | ||
268 | offline = offlinedata[0]; | ||
269 | } | ||
270 | |||
271 | try | ||
272 | { | ||
273 | ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]); | ||
274 | } | ||
275 | catch (ArgumentException) | ||
276 | { | ||
277 | } | ||
278 | catch (FormatException) | ||
279 | { | ||
280 | } | ||
281 | catch (OverflowException) | ||
282 | { | ||
283 | } | ||
284 | |||
285 | try | ||
286 | { | ||
287 | pos_x = (uint)Convert.ToInt32((string)requestData["position_x"]); | ||
288 | } | ||
289 | catch (ArgumentException) | ||
290 | { | ||
291 | } | ||
292 | catch (FormatException) | ||
293 | { | ||
294 | } | ||
295 | catch (OverflowException) | ||
296 | { | ||
297 | } | ||
298 | try | ||
299 | { | ||
300 | pos_y = (uint)Convert.ToInt32((string)requestData["position_y"]); | ||
301 | } | ||
302 | catch (ArgumentException) | ||
303 | { | ||
304 | } | ||
305 | catch (FormatException) | ||
306 | { | ||
307 | } | ||
308 | catch (OverflowException) | ||
309 | { | ||
310 | } | ||
311 | try | ||
312 | { | ||
313 | pos_z = (uint)Convert.ToInt32((string)requestData["position_z"]); | ||
314 | } | ||
315 | catch (ArgumentException) | ||
316 | { | ||
317 | } | ||
318 | catch (FormatException) | ||
319 | { | ||
320 | } | ||
321 | catch (OverflowException) | ||
322 | { | ||
323 | } | ||
324 | |||
325 | Position = new Vector3(pos_x, pos_y, pos_z); | ||
326 | |||
327 | string requestData3 = (string)requestData["binary_bucket"]; | ||
328 | if (string.IsNullOrEmpty(requestData3)) | ||
329 | { | ||
330 | binaryBucket = new byte[0]; | ||
331 | } | ||
332 | else | ||
333 | { | ||
334 | binaryBucket = Convert.FromBase64String(requestData3); | ||
335 | } | ||
336 | |||
337 | // Create a New GridInstantMessageObject the the data | ||
338 | GridInstantMessage gim = new GridInstantMessage(); | ||
339 | gim.fromAgentID = fromAgentID.Guid; | ||
340 | gim.fromAgentName = fromAgentName; | ||
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 | catch (Exception e) | ||
384 | { | ||
385 | m_log.Error("[INSTANT MESSAGE]: Caught unexpected exception:", e); | ||
386 | successful = false; | ||
387 | } | ||
388 | |||
389 | //Send response back to region calling if it was successful | ||
390 | // calling region uses this to know when to look up a user's location again. | ||
391 | XmlRpcResponse resp = new XmlRpcResponse(); | ||
392 | Hashtable respdata = new Hashtable(); | ||
393 | if (successful) | ||
394 | respdata["success"] = "TRUE"; | ||
395 | else | ||
396 | respdata["success"] = "FALSE"; | ||
397 | resp.Value = respdata; | ||
398 | |||
399 | return resp; | ||
400 | } | ||
401 | |||
402 | /// <summary> | ||
403 | /// delegate for sending a grid instant message asynchronously | ||
404 | /// </summary> | ||
405 | public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result, ulong prevRegionHandle); | ||
406 | |||
407 | private void GridInstantMessageCompleted(IAsyncResult iar) | ||
408 | { | ||
409 | GridInstantMessageDelegate icon = | ||
410 | (GridInstantMessageDelegate)iar.AsyncState; | ||
411 | icon.EndInvoke(iar); | ||
412 | } | ||
413 | |||
414 | |||
415 | protected virtual void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result) | ||
416 | { | ||
417 | GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync; | ||
418 | |||
419 | d.BeginInvoke(im, result, 0, GridInstantMessageCompleted, d); | ||
420 | } | ||
421 | |||
422 | /// <summary> | ||
423 | /// Recursive SendGridInstantMessage over XMLRPC method. | ||
424 | /// This is called from within a dedicated thread. | ||
425 | /// The first time this is called, prevRegionHandle will be 0 Subsequent times this is called from | ||
426 | /// itself, prevRegionHandle will be the last region handle that we tried to send. | ||
427 | /// If the handles are the same, we look up the user's location using the grid. | ||
428 | /// If the handles are still the same, we end. The send failed. | ||
429 | /// </summary> | ||
430 | /// <param name="prevRegionHandle"> | ||
431 | /// Pass in 0 the first time this method is called. It will be called recursively with the last | ||
432 | /// regionhandle tried | ||
433 | /// </param> | ||
434 | protected virtual void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result, ulong prevRegionHandle) | ||
435 | { | ||
436 | UUID toAgentID = new UUID(im.toAgentID); | ||
437 | |||
438 | UserAgentData upd = null; | ||
439 | |||
440 | bool lookupAgent = false; | ||
441 | |||
442 | lock (m_UserRegionMap) | ||
443 | { | ||
444 | if (m_UserRegionMap.ContainsKey(toAgentID)) | ||
445 | { | ||
446 | upd = new UserAgentData(); | ||
447 | upd.AgentOnline = true; | ||
448 | upd.Handle = m_UserRegionMap[toAgentID]; | ||
449 | |||
450 | // We need to compare the current regionhandle with the previous region handle | ||
451 | // or the recursive loop will never end because it will never try to lookup the agent again | ||
452 | if (prevRegionHandle == upd.Handle) | ||
453 | { | ||
454 | lookupAgent = true; | ||
455 | } | ||
456 | } | ||
457 | else | ||
458 | { | ||
459 | lookupAgent = true; | ||
460 | } | ||
461 | } | ||
462 | |||
463 | |||
464 | // Are we needing to look-up an agent? | ||
465 | if (lookupAgent) | ||
466 | { | ||
467 | // Non-cached user agent lookup. | ||
468 | upd = m_Scenes[0].CommsManager.UserService.GetAgentByUUID(toAgentID); | ||
469 | |||
470 | if (upd != null) | ||
471 | { | ||
472 | // check if we've tried this before.. | ||
473 | // This is one way to end the recursive loop | ||
474 | // | ||
475 | if (upd.Handle == prevRegionHandle) | ||
476 | { | ||
477 | m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | ||
478 | result(false); | ||
479 | return; | ||
480 | } | ||
481 | } | ||
482 | else | ||
483 | { | ||
484 | m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | ||
485 | result(false); | ||
486 | return; | ||
487 | } | ||
488 | } | ||
489 | |||
490 | if (upd != null) | ||
491 | { | ||
492 | if (upd.AgentOnline) | ||
493 | { | ||
494 | RegionInfo reginfo = m_Scenes[0].SceneGridService.RequestNeighbouringRegionInfo(upd.Handle); | ||
495 | if (reginfo != null) | ||
496 | { | ||
497 | Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); | ||
498 | // Not actually used anymore, left in for compatibility | ||
499 | // Remove at next interface change | ||
500 | // | ||
501 | msgdata["region_handle"] = 0; | ||
502 | bool imresult = doIMSending(reginfo, msgdata); | ||
503 | if (imresult) | ||
504 | { | ||
505 | // IM delivery successful, so store the Agent's location in our local cache. | ||
506 | lock (m_UserRegionMap) | ||
507 | { | ||
508 | if (m_UserRegionMap.ContainsKey(toAgentID)) | ||
509 | { | ||
510 | m_UserRegionMap[toAgentID] = upd.Handle; | ||
511 | } | ||
512 | else | ||
513 | { | ||
514 | m_UserRegionMap.Add(toAgentID, upd.Handle); | ||
515 | } | ||
516 | } | ||
517 | result(true); | ||
518 | } | ||
519 | else | ||
520 | { | ||
521 | // try again, but lookup user this time. | ||
522 | // Warning, this must call the Async version | ||
523 | // of this method or we'll be making thousands of threads | ||
524 | // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync | ||
525 | // The version that spawns the thread is SendGridInstantMessageViaXMLRPC | ||
526 | |||
527 | // This is recursive!!!!! | ||
528 | SendGridInstantMessageViaXMLRPCAsync(im, result, | ||
529 | upd.Handle); | ||
530 | } | ||
531 | |||
532 | } | ||
533 | else | ||
534 | { | ||
535 | m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", upd.Handle); | ||
536 | result(false); | ||
537 | } | ||
538 | } | ||
539 | else | ||
540 | { | ||
541 | result(false); | ||
542 | } | ||
543 | } | ||
544 | else | ||
545 | { | ||
546 | m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find user {0}", toAgentID); | ||
547 | result(false); | ||
548 | } | ||
549 | |||
550 | } | ||
551 | |||
552 | /// <summary> | ||
553 | /// This actually does the XMLRPC Request | ||
554 | /// </summary> | ||
555 | /// <param name="reginfo">RegionInfo we pull the data out of to send the request to</param> | ||
556 | /// <param name="xmlrpcdata">The Instant Message data Hashtable</param> | ||
557 | /// <returns>Bool if the message was successfully delivered at the other side.</returns> | ||
558 | private bool doIMSending(RegionInfo reginfo, Hashtable xmlrpcdata) | ||
559 | { | ||
560 | |||
561 | ArrayList SendParams = new ArrayList(); | ||
562 | SendParams.Add(xmlrpcdata); | ||
563 | XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams); | ||
564 | try | ||
565 | { | ||
566 | |||
567 | XmlRpcResponse GridResp = GridReq.Send("http://" + reginfo.ExternalHostName + ":" + reginfo.HttpPort, 3000); | ||
568 | |||
569 | Hashtable responseData = (Hashtable)GridResp.Value; | ||
570 | |||
571 | if (responseData.ContainsKey("success")) | ||
572 | { | ||
573 | if ((string)responseData["success"] == "TRUE") | ||
574 | { | ||
575 | return true; | ||
576 | } | ||
577 | else | ||
578 | { | ||
579 | return false; | ||
580 | } | ||
581 | } | ||
582 | else | ||
583 | { | ||
584 | return false; | ||
585 | } | ||
586 | } | ||
587 | catch (WebException e) | ||
588 | { | ||
589 | m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to http://{0}:{1} the host didn't respond ({2})", | ||
590 | reginfo.ExternalHostName, reginfo.HttpPort, e.Message); | ||
591 | } | ||
592 | |||
593 | return false; | ||
594 | } | ||
595 | |||
596 | /// <summary> | ||
597 | /// Get ulong region handle for region by it's Region UUID. | ||
598 | /// We use region handles over grid comms because there's all sorts of free and cool caching. | ||
599 | /// </summary> | ||
600 | /// <param name="regionID">UUID of region to get the region handle for</param> | ||
601 | /// <returns></returns> | ||
602 | // private ulong getLocalRegionHandleFromUUID(UUID regionID) | ||
603 | // { | ||
604 | // ulong returnhandle = 0; | ||
605 | // | ||
606 | // lock (m_Scenes) | ||
607 | // { | ||
608 | // foreach (Scene sn in m_Scenes) | ||
609 | // { | ||
610 | // if (sn.RegionInfo.RegionID == regionID) | ||
611 | // { | ||
612 | // returnhandle = sn.RegionInfo.RegionHandle; | ||
613 | // break; | ||
614 | // } | ||
615 | // } | ||
616 | // } | ||
617 | // return returnhandle; | ||
618 | // } | ||
619 | |||
620 | /// <summary> | ||
621 | /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC | ||
622 | /// </summary> | ||
623 | /// <param name="msg">The GridInstantMessage object</param> | ||
624 | /// <returns>Hashtable containing the XMLRPC request</returns> | ||
625 | private Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg) | ||
626 | { | ||
627 | Hashtable gim = new Hashtable(); | ||
628 | gim["from_agent_id"] = msg.fromAgentID.ToString(); | ||
629 | // Kept for compatibility | ||
630 | gim["from_agent_session"] = UUID.Zero.ToString(); | ||
631 | gim["to_agent_id"] = msg.toAgentID.ToString(); | ||
632 | gim["im_session_id"] = msg.imSessionID.ToString(); | ||
633 | gim["timestamp"] = msg.timestamp.ToString(); | ||
634 | gim["from_agent_name"] = msg.fromAgentName; | ||
635 | gim["message"] = msg.message; | ||
636 | byte[] dialogdata = new byte[1];dialogdata[0] = msg.dialog; | ||
637 | gim["dialog"] = Convert.ToBase64String(dialogdata,Base64FormattingOptions.None); | ||
638 | |||
639 | if (msg.fromGroup) | ||
640 | gim["from_group"] = "TRUE"; | ||
641 | else | ||
642 | gim["from_group"] = "FALSE"; | ||
643 | byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline; | ||
644 | gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None); | ||
645 | gim["parent_estate_id"] = msg.ParentEstateID.ToString(); | ||
646 | gim["position_x"] = msg.Position.X.ToString(); | ||
647 | gim["position_y"] = msg.Position.Y.ToString(); | ||
648 | gim["position_z"] = msg.Position.Z.ToString(); | ||
649 | gim["region_id"] = msg.RegionID.ToString(); | ||
650 | gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); | ||
651 | return gim; | ||
652 | } | ||
653 | |||
654 | } | ||
655 | } | ||