diff options
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs')
-rw-r--r-- | OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs | 689 |
1 files changed, 56 insertions, 633 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs index 805f7cb..1b7eb97 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs | |||
@@ -36,6 +36,7 @@ using Nini.Config; | |||
36 | using Nwc.XmlRpc; | 36 | using Nwc.XmlRpc; |
37 | using OpenSim.Framework; | 37 | using OpenSim.Framework; |
38 | using OpenSim.Framework.Client; | 38 | using OpenSim.Framework.Client; |
39 | using OpenSim.Region.Interfaces; | ||
39 | using OpenSim.Region.Environment.Interfaces; | 40 | using OpenSim.Region.Environment.Interfaces; |
40 | using OpenSim.Region.Environment.Scenes; | 41 | using 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 | } |