MessageTransferModule.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  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 OpenSimulator 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.Net;
  31. using System.Reflection;
  32. using log4net;
  33. using Mono.Addins;
  34. using Nini.Config;
  35. using Nwc.XmlRpc;
  36. using OpenMetaverse;
  37. using OpenSim.Framework;
  38. using OpenSim.Framework.Servers;
  39. using OpenSim.Region.Framework.Interfaces;
  40. using OpenSim.Region.Framework.Scenes;
  41. using GridRegion = OpenSim.Services.Interfaces.GridRegion;
  42. using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
  43. using OpenSim.Services.Interfaces;
  44. using System.Net.Http;
  45. namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
  46. {
  47. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MessageTransferModule")]
  48. public class MessageTransferModule : ISharedRegionModule, IMessageTransferModule
  49. {
  50. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  51. private bool m_Enabled = false;
  52. protected string m_MessageKey = String.Empty;
  53. protected List<Scene> m_Scenes = new List<Scene>();
  54. protected Dictionary<UUID, UUID> m_UserRegionMap = new Dictionary<UUID, UUID>();
  55. public event UndeliveredMessage OnUndeliveredMessage;
  56. public static ObjectJobEngine IMXMLRPCSendWorkers;
  57. private IPresenceService m_PresenceService;
  58. protected IPresenceService PresenceService
  59. {
  60. get
  61. {
  62. if (m_PresenceService == null)
  63. m_PresenceService = m_Scenes[0].RequestModuleInterface<IPresenceService>();
  64. return m_PresenceService;
  65. }
  66. }
  67. public virtual void Initialise(IConfigSource config)
  68. {
  69. IConfig cnf = config.Configs["Messaging"];
  70. if (cnf != null)
  71. {
  72. if (cnf.GetString("MessageTransferModule", "MessageTransferModule") != "MessageTransferModule")
  73. {
  74. return;
  75. }
  76. m_MessageKey = cnf.GetString("MessageKey", String.Empty);
  77. }
  78. m_log.Debug("[MESSAGE TRANSFER]: Module enabled");
  79. m_Enabled = true;
  80. IMXMLRPCSendWorkers = new ObjectJobEngine(DoSendIMviaXMLRPC, "IMXMLRPCSendWorkers", 1000, 3);
  81. }
  82. public virtual void AddRegion(Scene scene)
  83. {
  84. if (!m_Enabled)
  85. return;
  86. lock (m_Scenes)
  87. {
  88. m_log.Debug("[MESSAGE TRANSFER]: Message transfer module active");
  89. scene.RegisterModuleInterface<IMessageTransferModule>(this);
  90. m_Scenes.Add(scene);
  91. }
  92. }
  93. public virtual void PostInitialise()
  94. {
  95. if (!m_Enabled)
  96. return;
  97. MainServer.Instance.AddXmlRPCHandler("grid_instant_message", processXMLRPCGridInstantMessage);
  98. }
  99. public virtual void RegionLoaded(Scene scene)
  100. {
  101. }
  102. public virtual void RemoveRegion(Scene scene)
  103. {
  104. if (!m_Enabled)
  105. return;
  106. lock (m_Scenes)
  107. {
  108. m_Scenes.Remove(scene);
  109. }
  110. }
  111. public virtual void Close()
  112. {
  113. }
  114. public virtual string Name
  115. {
  116. get { return "MessageTransferModule"; }
  117. }
  118. public virtual Type ReplaceableInterface
  119. {
  120. get { return null; }
  121. }
  122. public virtual void SendInstantMessage(GridInstantMessage im, MessageResultNotification result)
  123. {
  124. UUID toAgentID = new UUID(im.toAgentID);
  125. if (toAgentID.IsZero())
  126. return;
  127. ScenePresence achildsp = null;
  128. // Try root avatar first
  129. foreach (Scene scene in m_Scenes)
  130. {
  131. //m_log.DebugFormat(
  132. // "[HG INSTANT MESSAGE]: Looking for root agent {0} in {1}",
  133. // toAgentID.ToString(), scene.RegionInfo.RegionName);
  134. ScenePresence sp = scene.GetScenePresence(toAgentID);
  135. if (sp != null && !sp.IsDeleted)
  136. {
  137. if (sp.IsChildAgent)
  138. achildsp = sp;
  139. else
  140. {
  141. // m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID);
  142. sp.ControllingClient.SendInstantMessage(im);
  143. result(true);
  144. return;
  145. }
  146. }
  147. }
  148. if (achildsp != null)
  149. {
  150. // m_log.DebugFormat("[HG INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID);
  151. achildsp.ControllingClient.SendInstantMessage(im);
  152. result(true);
  153. return;
  154. }
  155. //m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID);
  156. SendGridInstantMessageViaXMLRPC(im, result);
  157. }
  158. public virtual void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result)
  159. {
  160. UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage;
  161. // If this event has handlers, then an IM from an agent will be
  162. // considered delivered. This will suppress the error message.
  163. //
  164. if (handlerUndeliveredMessage != null)
  165. {
  166. handlerUndeliveredMessage(im);
  167. if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent)
  168. result(true);
  169. else
  170. result(false);
  171. return;
  172. }
  173. //m_log.DebugFormat("[INSTANT MESSAGE]: Undeliverable");
  174. result(false);
  175. }
  176. /// <summary>
  177. /// Process a XMLRPC Grid Instant Message
  178. /// </summary>
  179. /// <param name="request">XMLRPC parameters
  180. /// </param>
  181. /// <returns>Nothing much</returns>
  182. protected virtual XmlRpcResponse processXMLRPCGridInstantMessage(XmlRpcRequest request, IPEndPoint remoteClient)
  183. {
  184. bool successful = false;
  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 = Array.Empty<byte>();
  203. float pos_x = 0;
  204. float pos_y = 0;
  205. float pos_z = 0;
  206. //m_log.Info("Processing IM");
  207. Hashtable requestData = (Hashtable)request.Params[0];
  208. // Check if it's got all the data
  209. if (requestData.ContainsKey("from_agent_id")
  210. && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id")
  211. && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name")
  212. && requestData.ContainsKey("message") && requestData.ContainsKey("dialog")
  213. && requestData.ContainsKey("from_group")
  214. && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id")
  215. && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y")
  216. && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id")
  217. && requestData.ContainsKey("binary_bucket"))
  218. {
  219. if (m_MessageKey != String.Empty)
  220. {
  221. XmlRpcResponse error_resp = new XmlRpcResponse();
  222. Hashtable error_respdata = new Hashtable();
  223. error_respdata["success"] = "FALSE";
  224. error_resp.Value = error_respdata;
  225. if (!requestData.Contains("message_key"))
  226. return error_resp;
  227. if (m_MessageKey != (string)requestData["message_key"])
  228. return error_resp;
  229. }
  230. // Do the easy way of validating the UUIDs
  231. UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID);
  232. UUID.TryParse((string)requestData["to_agent_id"], out toAgentID);
  233. UUID.TryParse((string)requestData["im_session_id"], out imSessionID);
  234. UUID.TryParse((string)requestData["region_id"], out RegionID);
  235. try
  236. {
  237. timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]);
  238. }
  239. catch (ArgumentException)
  240. {
  241. }
  242. catch (FormatException)
  243. {
  244. }
  245. catch (OverflowException)
  246. {
  247. }
  248. fromAgentName = (string)requestData["from_agent_name"];
  249. message = (string)requestData["message"];
  250. if (message == null)
  251. message = string.Empty;
  252. // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them.
  253. string requestData1 = (string)requestData["dialog"];
  254. if (string.IsNullOrEmpty(requestData1))
  255. {
  256. dialog = 0;
  257. }
  258. else
  259. {
  260. byte[] dialogdata = Convert.FromBase64String(requestData1);
  261. dialog = dialogdata[0];
  262. }
  263. if ((string)requestData["from_group"] == "TRUE")
  264. fromGroup = true;
  265. string requestData2 = (string)requestData["offline"];
  266. if (String.IsNullOrEmpty(requestData2))
  267. {
  268. offline = 0;
  269. }
  270. else
  271. {
  272. byte[] offlinedata = Convert.FromBase64String(requestData2);
  273. offline = offlinedata[0];
  274. }
  275. try
  276. {
  277. ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]);
  278. }
  279. catch (ArgumentException)
  280. {
  281. }
  282. catch (FormatException)
  283. {
  284. }
  285. catch (OverflowException)
  286. {
  287. }
  288. try
  289. {
  290. pos_x = (uint)Convert.ToInt32((string)requestData["position_x"]);
  291. }
  292. catch (ArgumentException)
  293. {
  294. }
  295. catch (FormatException)
  296. {
  297. }
  298. catch (OverflowException)
  299. {
  300. }
  301. try
  302. {
  303. pos_y = (uint)Convert.ToInt32((string)requestData["position_y"]);
  304. }
  305. catch (ArgumentException)
  306. {
  307. }
  308. catch (FormatException)
  309. {
  310. }
  311. catch (OverflowException)
  312. {
  313. }
  314. try
  315. {
  316. pos_z = (uint)Convert.ToInt32((string)requestData["position_z"]);
  317. }
  318. catch (ArgumentException)
  319. {
  320. }
  321. catch (FormatException)
  322. {
  323. }
  324. catch (OverflowException)
  325. {
  326. }
  327. Position = new Vector3(pos_x, pos_y, pos_z);
  328. string requestData3 = (string)requestData["binary_bucket"];
  329. if (string.IsNullOrEmpty(requestData3))
  330. {
  331. binaryBucket = Array.Empty<byte>();
  332. }
  333. else
  334. {
  335. binaryBucket = Convert.FromBase64String(requestData3);
  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. // Trigger the Instant message in the scene.
  353. foreach (Scene scene in m_Scenes)
  354. {
  355. ScenePresence sp = scene.GetScenePresence(toAgentID);
  356. if (sp != null && !sp.IsChildAgent)
  357. {
  358. scene.EventManager.TriggerIncomingInstantMessage(gim);
  359. successful = true;
  360. }
  361. }
  362. if (!successful)
  363. {
  364. // If the message can't be delivered to an agent, it
  365. // is likely to be a group IM. On a group IM, the
  366. // imSessionID = toAgentID = group id. Raise the
  367. // unhandled IM event to give the groups module
  368. // a chance to pick it up. We raise that in a random
  369. // scene, since the groups module is shared.
  370. //
  371. m_Scenes[0].EventManager.TriggerUnhandledInstantMessage(gim);
  372. }
  373. }
  374. }
  375. catch (Exception e)
  376. {
  377. m_log.Error("[INSTANT MESSAGE]: Caught unexpected exception:", e);
  378. successful = false;
  379. }
  380. //Send response back to region calling if it was successful
  381. // calling region uses this to know when to look up a user's location again.
  382. XmlRpcResponse resp = new XmlRpcResponse();
  383. Hashtable respdata = new Hashtable();
  384. if (successful)
  385. respdata["success"] = "TRUE";
  386. else
  387. respdata["success"] = "FALSE";
  388. resp.Value = respdata;
  389. return resp;
  390. }
  391. private class GIMData {
  392. public GridInstantMessage im;
  393. public MessageResultNotification result;
  394. };
  395. private void SendGridInstantMessageViaXMLRPC(GridInstantMessage im, MessageResultNotification result)
  396. {
  397. GIMData gim = new GIMData()
  398. {
  399. im = im,
  400. result = result
  401. };
  402. IMXMLRPCSendWorkers.Enqueue(gim);
  403. }
  404. private void DoSendIMviaXMLRPC(object o)
  405. {
  406. try
  407. {
  408. GIMData data = o as GIMData;
  409. if (o == null)
  410. return;
  411. SendGridInstantMessageViaXMLRPCAsync(data.im, data.result, UUID.Zero);
  412. }
  413. catch (Exception e)
  414. {
  415. m_log.Error("[SendGridInstantMessageViaXMLRPC]: exception " + e.Message);
  416. }
  417. }
  418. /// <summary>
  419. /// Internal SendGridInstantMessage over XMLRPC method.
  420. /// </summary>
  421. /// <param name="prevRegionHandle">
  422. /// Pass in 0 the first time this method is called. It will be called recursively with the last
  423. /// regionhandle tried
  424. /// </param>
  425. private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result, UUID prevRegionID)
  426. {
  427. UUID toAgentID = new UUID(im.toAgentID);
  428. PresenceInfo upd = null;
  429. bool lookupAgent = false;
  430. lock (m_UserRegionMap)
  431. {
  432. if (m_UserRegionMap.ContainsKey(toAgentID))
  433. {
  434. upd = new PresenceInfo();
  435. upd.RegionID = m_UserRegionMap[toAgentID];
  436. // We need to compare the current regionhandle with the previous region handle
  437. // or the recursive loop will never end because it will never try to lookup the agent again
  438. if (prevRegionID == upd.RegionID)
  439. {
  440. lookupAgent = true;
  441. }
  442. }
  443. else
  444. {
  445. lookupAgent = true;
  446. }
  447. }
  448. // Are we needing to look-up an agent?
  449. if (lookupAgent)
  450. {
  451. // Non-cached user agent lookup.
  452. PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() });
  453. if (presences != null && presences.Length > 0)
  454. {
  455. foreach (PresenceInfo p in presences)
  456. {
  457. if (!p.RegionID.IsZero())
  458. {
  459. upd = p;
  460. break;
  461. }
  462. }
  463. }
  464. if (upd != null)
  465. {
  466. // check if we've tried this before..
  467. // This is one way to end the recursive loop
  468. //
  469. if (upd.RegionID == prevRegionID)
  470. {
  471. // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
  472. HandleUndeliverableMessage(im, result);
  473. return;
  474. }
  475. }
  476. else
  477. {
  478. // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message");
  479. HandleUndeliverableMessage(im, result);
  480. return;
  481. }
  482. }
  483. if (upd != null)
  484. {
  485. GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(UUID.Zero,
  486. upd.RegionID);
  487. if (reginfo != null)
  488. {
  489. Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im);
  490. // Not actually used anymore, left in for compatibility
  491. // Remove at next interface change
  492. //
  493. msgdata["region_handle"] = 0;
  494. bool imresult = doIMSending(reginfo, msgdata);
  495. if (imresult)
  496. {
  497. // IM delivery successful, so store the Agent's location in our local cache.
  498. lock (m_UserRegionMap)
  499. {
  500. if (m_UserRegionMap.ContainsKey(toAgentID))
  501. {
  502. m_UserRegionMap[toAgentID] = upd.RegionID;
  503. }
  504. else
  505. {
  506. m_UserRegionMap.Add(toAgentID, upd.RegionID);
  507. }
  508. }
  509. result(true);
  510. }
  511. else
  512. {
  513. // try again, but lookup user this time.
  514. // Warning, this must call the Async version
  515. // of this method or we'll be making thousands of threads
  516. // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync
  517. // The version that spawns the thread is SendGridInstantMessageViaXMLRPC
  518. // This is recursive!!!!!
  519. SendGridInstantMessageViaXMLRPCAsync(im, result, upd.RegionID);
  520. }
  521. }
  522. else
  523. {
  524. m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", upd.RegionID);
  525. HandleUndeliverableMessage(im, result);
  526. }
  527. }
  528. else
  529. {
  530. HandleUndeliverableMessage(im, result);
  531. }
  532. }
  533. /// <summary>
  534. /// This actually does the XMLRPC Request
  535. /// </summary>
  536. /// <param name="reginfo">RegionInfo we pull the data out of to send the request to</param>
  537. /// <param name="xmlrpcdata">The Instant Message data Hashtable</param>
  538. /// <returns>Bool if the message was successfully delivered at the other side.</returns>
  539. protected virtual bool doIMSending(GridRegion reginfo, Hashtable xmlrpcdata)
  540. {
  541. ArrayList SendParams = new ArrayList();
  542. SendParams.Add(xmlrpcdata);
  543. XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams);
  544. HttpClient hclient = null;
  545. try
  546. {
  547. hclient = WebUtil.GetNewGlobalHttpClient(10000);
  548. XmlRpcResponse GridResp = GridReq.Send(reginfo.ServerURI, hclient);
  549. Hashtable responseData = (Hashtable)GridResp.Value;
  550. if (responseData.ContainsKey("success"))
  551. {
  552. if ((string)responseData["success"] == "TRUE")
  553. {
  554. return true;
  555. }
  556. else
  557. {
  558. return false;
  559. }
  560. }
  561. else
  562. {
  563. return false;
  564. }
  565. }
  566. catch (Exception e)
  567. {
  568. m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to {0} : {1}", reginfo.ServerURI.ToString(), e.Message);
  569. }
  570. finally
  571. {
  572. hclient?.Dispose();
  573. }
  574. return false;
  575. }
  576. /// <summary>
  577. /// Get ulong region handle for region by it's Region UUID.
  578. /// We use region handles over grid comms because there's all sorts of free and cool caching.
  579. /// </summary>
  580. /// <param name="regionID">UUID of region to get the region handle for</param>
  581. /// <returns></returns>
  582. // private virtual ulong getLocalRegionHandleFromUUID(UUID regionID)
  583. // {
  584. // ulong returnhandle = 0;
  585. //
  586. // lock (m_Scenes)
  587. // {
  588. // foreach (Scene sn in m_Scenes)
  589. // {
  590. // if (sn.RegionInfo.RegionID == regionID)
  591. // {
  592. // returnhandle = sn.RegionInfo.RegionHandle;
  593. // break;
  594. // }
  595. // }
  596. // }
  597. // return returnhandle;
  598. // }
  599. /// <summary>
  600. /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC
  601. /// </summary>
  602. /// <param name="msg">The GridInstantMessage object</param>
  603. /// <returns>Hashtable containing the XMLRPC request</returns>
  604. protected virtual Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg)
  605. {
  606. Hashtable gim = new Hashtable();
  607. gim["from_agent_id"] = msg.fromAgentID.ToString();
  608. // Kept for compatibility
  609. gim["from_agent_session"] = UUID.Zero.ToString();
  610. gim["to_agent_id"] = msg.toAgentID.ToString();
  611. gim["im_session_id"] = msg.imSessionID.ToString();
  612. gim["timestamp"] = msg.timestamp.ToString();
  613. gim["from_agent_name"] = msg.fromAgentName;
  614. gim["message"] = msg.message;
  615. byte[] dialogdata = new byte[1];dialogdata[0] = msg.dialog;
  616. gim["dialog"] = Convert.ToBase64String(dialogdata,Base64FormattingOptions.None);
  617. if (msg.fromGroup)
  618. gim["from_group"] = "TRUE";
  619. else
  620. gim["from_group"] = "FALSE";
  621. byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline;
  622. gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None);
  623. gim["parent_estate_id"] = msg.ParentEstateID.ToString();
  624. gim["position_x"] = msg.Position.X.ToString();
  625. gim["position_y"] = msg.Position.Y.ToString();
  626. gim["position_z"] = msg.Position.Z.ToString();
  627. gim["region_id"] = new UUID(msg.RegionID).ToString();
  628. gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None);
  629. if (m_MessageKey != String.Empty)
  630. gim["message_key"] = m_MessageKey;
  631. return gim;
  632. }
  633. }
  634. }