MessageTransferModule.cs 26 KB

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