aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Avatar')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs58
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs7
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs689
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/InstantMessage/MessageTransferModule.cs626
4 files changed, 722 insertions, 658 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
index 58251cb..72c64ad 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
@@ -36,6 +36,7 @@ using Nwc.XmlRpc;
36using OpenSim.Framework; 36using OpenSim.Framework;
37using OpenSim.Framework.Communications.Cache; 37using OpenSim.Framework.Communications.Cache;
38using OpenSim.Framework.Servers; 38using OpenSim.Framework.Servers;
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
@@ -105,6 +106,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
105 106
106 private Scene m_initialScene; // saves a lookup if we don't have a specific scene 107 private Scene m_initialScene; // saves a lookup if we don't have a specific scene
107 private Dictionary<ulong, Scene> m_scenes = new Dictionary<ulong,Scene>(); 108 private Dictionary<ulong, Scene> m_scenes = new Dictionary<ulong,Scene>();
109 private IMessageTransferModule m_TransferModule = null;
108 110
109 #region IRegionModule Members 111 #region IRegionModule Members
110 112
@@ -124,7 +126,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
124 m_scenes[scene.RegionInfo.RegionHandle] = scene; 126 m_scenes[scene.RegionInfo.RegionHandle] = scene;
125 } 127 }
126 scene.EventManager.OnNewClient += OnNewClient; 128 scene.EventManager.OnNewClient += OnNewClient;
127 scene.EventManager.OnGridInstantMessage += OnGridInstantMessage; 129 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
128 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; 130 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
129 scene.EventManager.OnMakeChildAgent += MakeChildAgent; 131 scene.EventManager.OnMakeChildAgent += MakeChildAgent;
130 scene.EventManager.OnClientClosed += ClientClosed; 132 scene.EventManager.OnClientClosed += ClientClosed;
@@ -132,6 +134,10 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
132 134
133 public void PostInitialise() 135 public void PostInitialise()
134 { 136 {
137 List<Scene> scenes = new List<Scene>(m_scenes.Values);
138 m_TransferModule = scenes[0].RequestModuleInterface<IMessageTransferModule>();
139 if (m_TransferModule == null)
140 m_log.Error("[FRIENDS]: Unable to find a message transfer module, friendship offers will not work");
135 } 141 }
136 142
137 public void Close() 143 public void Close()
@@ -434,11 +440,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
434 // we don't want to get that new IM into here if we aren't local, as only on the destination 440 // we don't want to get that new IM into here if we aren't local, as only on the destination
435 // should receive it. If we *are* local, *we* are the destination, so we have to receive it. 441 // should receive it. If we *are* local, *we* are the destination, so we have to receive it.
436 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here. 442 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here.
437 InstantMessageReceiver recv = InstantMessageReceiver.IMModule;
438 if (GetAnyPresenceFromAgentID(toAgentID) != null) recv |= InstantMessageReceiver.FriendsModule;
439 443
440 // We don't really care which local scene we pipe it through. 444 // We don't really care which local scene we pipe it through.
441 m_initialScene.TriggerGridInstantMessage(msg, recv); 445 if (m_TransferModule != null)
446 {
447 m_TransferModule.SendInstantMessage(msg,
448 delegate(bool success) {}
449 );
450 }
442 } 451 }
443 else 452 else
444 { 453 {
@@ -531,17 +540,20 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
531 } 540 }
532 } 541 }
533 542
534 private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver whichModule) 543 private void OnGridInstantMessage(GridInstantMessage msg)
535 { 544 {
536 if ((whichModule & InstantMessageReceiver.FriendsModule) == 0) 545 // Just call the IM handler above
537 return; 546 // This event won't be raised unless we have that agent,
538 547 // so we can depend on the above not trying to send
539 // Trigger the above event handler 548 // via grid again
540 OnInstantMessage(null, new UUID(msg.fromAgentID), new UUID(msg.fromAgentSession), 549 //
541 new UUID(msg.toAgentID), new UUID(msg.imSessionID), msg.timestamp, msg.fromAgentName, 550 OnInstantMessage(null, new UUID(msg.fromAgentID),
542 msg.message, msg.dialog, msg.fromGroup, msg.offline, msg.ParentEstateID, 551 new UUID(msg.fromAgentSession),
543 new Vector3(msg.Position.X, msg.Position.Y, msg.Position.Z), new UUID(msg.RegionID), 552 new UUID(msg.toAgentID), new UUID(msg.imSessionID),
544 msg.binaryBucket); 553 msg.timestamp, msg.fromAgentName, msg.message,
554 msg.dialog, msg.fromGroup, msg.offline,
555 msg.ParentEstateID, msg.Position,
556 new UUID(msg.RegionID), msg.binaryBucket);
545 } 557 }
546 558
547 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID transactionID, List<UUID> callingCardFolders) 559 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID transactionID, List<UUID> callingCardFolders)
@@ -607,12 +619,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
607 // we don't want to get that new IM into here if we aren't local, as only on the destination 619 // we don't want to get that new IM into here if we aren't local, as only on the destination
608 // should receive it. If we *are* local, *we* are the destination, so we have to receive it. 620 // should receive it. If we *are* local, *we* are the destination, so we have to receive it.
609 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here. 621 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here.
610 InstantMessageReceiver recv = InstantMessageReceiver.IMModule;
611 if (GetAnyPresenceFromAgentID(friendID) != null) recv |= InstantMessageReceiver.FriendsModule;
612 622
613 // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler 623 // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler
614 // of the type 39 IM 624 // of the type 39 IM
615 SceneAgentIn.TriggerGridInstantMessage(msg, recv); 625 if (m_TransferModule != null)
626 {
627 m_TransferModule.SendInstantMessage(msg,
628 delegate(bool success) {}
629 );
630 }
616 631
617 // tell client that new friend is online 632 // tell client that new friend is online
618 client.SendAgentOnline(new UUID[] { friendID }); 633 client.SendAgentOnline(new UUID[] { friendID });
@@ -664,12 +679,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
664 // we don't want to get that new IM into here if we aren't local, as only on the destination 679 // we don't want to get that new IM into here if we aren't local, as only on the destination
665 // should receive it. If we *are* local, *we* are the destination, so we have to receive it. 680 // should receive it. If we *are* local, *we* are the destination, so we have to receive it.
666 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here. 681 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here.
667 InstantMessageReceiver recv = InstantMessageReceiver.IMModule;
668 if (GetAnyPresenceFromAgentID(friendID) != null) recv |= InstantMessageReceiver.FriendsModule;
669 682
670 // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler 683 // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler
671 // of the type 39 IM 684 // of the type 39 IM
672 SceneAgentIn.TriggerGridInstantMessage(msg, recv); 685 if (m_TransferModule != null)
686 {
687 m_TransferModule.SendInstantMessage(msg,
688 delegate(bool success) {}
689 );
690 }
673 } 691 }
674 692
675 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID) 693 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID)
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs
index d9a5393..c291c16 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/Groups/GroupsModule.cs
@@ -99,7 +99,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Groups
99 99
100 scene.EventManager.OnNewClient += OnNewClient; 100 scene.EventManager.OnNewClient += OnNewClient;
101 scene.EventManager.OnClientClosed += OnClientClosed; 101 scene.EventManager.OnClientClosed += OnClientClosed;
102 scene.EventManager.OnGridInstantMessage += 102 scene.EventManager.OnIncomingInstantMessage +=
103 OnGridInstantMessage; 103 OnGridInstantMessage;
104 } 104 }
105 105
@@ -187,11 +187,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Groups
187 { 187 {
188 } 188 }
189 189
190 private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver whichModule) 190 private void OnGridInstantMessage(GridInstantMessage msg)
191 { 191 {
192 if ((whichModule & InstantMessageReceiver.GroupsModule) == 0)
193 return;
194
195 // Trigger the above event handler 192 // Trigger the above event handler
196 OnInstantMessage(null, new UUID(msg.fromAgentID), 193 OnInstantMessage(null, new UUID(msg.fromAgentID),
197 new UUID(msg.fromAgentSession), 194 new UUID(msg.fromAgentSession),
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}
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}