MessageService.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Net;
  29. using System.Net.Sockets;
  30. using System.Collections;
  31. using System.Collections.Generic;
  32. using System.Threading;
  33. //using System.Xml;
  34. using libsecondlife;
  35. using Nwc.XmlRpc;
  36. using OpenSim.Framework;
  37. using OpenSim.Framework.Console;
  38. using OpenSim.Framework.Data;
  39. using OpenSim.Framework.Servers;
  40. using FriendRights = libsecondlife.FriendRights;
  41. namespace OpenSim.Grid.MessagingServer
  42. {
  43. public class MessageService
  44. {
  45. private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
  46. private MessageServerConfig m_cfg;
  47. //A hashtable of all current presences this server knows about
  48. private Hashtable m_presences = new Hashtable();
  49. //a hashtable of all current regions this server knows about
  50. private Hashtable m_regionInfoCache = new Hashtable();
  51. //A hashtable containing lists of UUIDs keyed by UUID for fast backreferencing
  52. private Hashtable m_presence_BackReferences = new Hashtable();
  53. // Hashtable containing work units that need to be processed
  54. private Hashtable m_unProcessedWorkUnits = new Hashtable();
  55. public MessageService(MessageServerConfig cfg)
  56. {
  57. m_cfg = cfg;
  58. }
  59. #region RegionComms Methods
  60. #endregion
  61. #region FriendList Methods
  62. /// <summary>
  63. /// Process Friendlist subscriptions for a user
  64. /// The login method calls this for a User
  65. /// </summary>
  66. /// <param name="userpresence">The Agent we're processing the friendlist subscriptions</param>
  67. public void ProcessFriendListSubscriptions(UserPresenceData userpresence)
  68. {
  69. lock (m_presences)
  70. {
  71. if (!m_presences.Contains(userpresence.agentData.AgentID))
  72. m_presences.Add(userpresence.agentData.AgentID, userpresence);
  73. }
  74. List<FriendListItem> uFriendList = userpresence.friendData;
  75. for (int i = 0; i < uFriendList.Count; i++)
  76. {
  77. //m_presence_BackReferences.Add(userpresence.agentData.AgentID, uFriendList[i].Friend);
  78. // m_presence_BackReferences.Add(uFriendList[i].Friend, userpresence.agentData.AgentID);
  79. if (m_presences.Contains(uFriendList[i].Friend))
  80. {
  81. UserPresenceData friendup = (UserPresenceData)m_presences[uFriendList[i].Friend];
  82. // Add backreference
  83. SubscribeToPresenceUpdates(userpresence, friendup, uFriendList[i],i);
  84. }
  85. }
  86. }
  87. /// <summary>
  88. /// Does the necessary work to subscribe one agent to another's presence notifications
  89. /// Gets called by ProcessFriendListSubscriptions. You shouldn't call this directly
  90. /// unless you know what you're doing
  91. /// </summary>
  92. /// <param name="userpresence">P1</param>
  93. /// <param name="friendpresence">P2</param>
  94. /// <param name="uFriendListItem"></param>
  95. /// <param name="uFriendListIndex"></param>
  96. public void SubscribeToPresenceUpdates(UserPresenceData userpresence, UserPresenceData friendpresence,
  97. FriendListItem uFriendListItem, int uFriendListIndex)
  98. {
  99. if ((uFriendListItem.FriendListOwnerPerms & (uint)FriendRights.CanSeeOnline) != 0)
  100. {
  101. // Subscribe and Send Out updates
  102. if (!friendpresence.subscriptionData.Contains(friendpresence.agentData.AgentID))
  103. {
  104. userpresence.subscriptionData.Add(friendpresence.agentData.AgentID);
  105. //Send Region Notice....
  106. }
  107. else
  108. {
  109. // we need to send out online status update, but the user is already subscribed
  110. }
  111. PresenceInformer friendlistupdater = new PresenceInformer();
  112. friendlistupdater.presence1 = friendpresence;
  113. friendlistupdater.presence2 = userpresence;
  114. WaitCallback cb = new WaitCallback(friendlistupdater.go);
  115. ThreadPool.QueueUserWorkItem(cb);
  116. //SendRegionPresenceUpdate(friendpresence, userpresence);
  117. }
  118. if ((uFriendListItem.FriendPerms & (uint)FriendRights.CanSeeOnline) != 0)
  119. {
  120. if (!friendpresence.subscriptionData.Contains(userpresence.agentData.AgentID))
  121. {
  122. friendpresence.subscriptionData.Add(userpresence.agentData.AgentID);
  123. //Send Region Notice....
  124. }
  125. else
  126. {
  127. // we need to send out online status update, but the user is already subscribed
  128. }
  129. PresenceInformer friendlistupdater = new PresenceInformer();
  130. friendlistupdater.presence1 = userpresence;
  131. friendlistupdater.presence2 = friendpresence;
  132. WaitCallback cb2 = new WaitCallback(friendlistupdater.go);
  133. ThreadPool.QueueUserWorkItem(cb2);
  134. //SendRegionPresenceUpdate(userpresence, friendpresence);
  135. }
  136. }
  137. /// <summary>
  138. /// Adds a backreference so presence specific data doesn't have to be
  139. /// enumerated for each logged in user every time someone logs on or off.
  140. /// </summary>
  141. /// <param name="agentID"></param>
  142. /// <param name="friendID"></param>
  143. public void addBackReference(LLUUID agentID, LLUUID friendID)
  144. {
  145. if (m_presence_BackReferences.Contains(friendID))
  146. {
  147. List<LLUUID> presenseBackReferences = (List<LLUUID>)m_presence_BackReferences[friendID];
  148. if (!presenseBackReferences.Contains(agentID))
  149. {
  150. presenseBackReferences.Add(agentID);
  151. }
  152. m_presence_BackReferences[friendID] = presenseBackReferences;
  153. }
  154. else
  155. {
  156. List<LLUUID> presenceBackReferences = new List<LLUUID>();
  157. presenceBackReferences.Add(agentID);
  158. m_presence_BackReferences[friendID] = presenceBackReferences;
  159. }
  160. }
  161. /// <summary>
  162. /// Removes a backreference to free up some memory
  163. /// </summary>
  164. /// <param name="agentID"></param>
  165. /// <param name="friendID"></param>
  166. public void removeBackReference(LLUUID agentID, LLUUID friendID)
  167. {
  168. if (m_presence_BackReferences.Contains(friendID))
  169. {
  170. List<LLUUID> presenseBackReferences = (List<LLUUID>)m_presence_BackReferences[friendID];
  171. if (presenseBackReferences.Contains(agentID))
  172. {
  173. presenseBackReferences.Remove(agentID);
  174. }
  175. // If there are no more backreferences for this agent,
  176. // remove it to free up memory.
  177. if (presenseBackReferences.Count == 0)
  178. {
  179. m_presence_BackReferences.Remove(agentID);
  180. }
  181. }
  182. }
  183. /// <summary>
  184. /// Logoff Processor. Call this to clean up agent presence data and send logoff presence notifications
  185. /// </summary>
  186. /// <param name="AgentID"></param>
  187. private void ProcessLogOff(LLUUID AgentID)
  188. {
  189. UserPresenceData AgentData = null;
  190. List<LLUUID> AgentsNeedingNotification = new List<LLUUID>();
  191. UserPresenceData friendd = null;
  192. lock (m_presences)
  193. {
  194. if (m_presences.Contains(AgentID))
  195. {
  196. AgentData = (UserPresenceData)m_presences[AgentID];
  197. }
  198. }
  199. if (AgentData != null)
  200. {
  201. AgentsNeedingNotification = AgentData.subscriptionData;
  202. //lock (m_presence_BackReferences)
  203. //{
  204. //if (m_presence_BackReferences.Contains(AgentID))
  205. //{
  206. //AgentsNeedingNotification = (List<LLUUID>)m_presence_BackReferences[AgentID];
  207. //}
  208. //}
  209. for (int i = 0; i < AgentsNeedingNotification.Count; i++)
  210. {
  211. // TODO: Do Region Notifications
  212. lock(m_presences)
  213. {
  214. if (m_presences.Contains(AgentsNeedingNotification[i]))
  215. {
  216. friendd = (UserPresenceData)m_presences[AgentsNeedingNotification[i]];
  217. }
  218. }
  219. // This might need to be enumerated and checked before we try to remove it.
  220. if (friendd != null)
  221. {
  222. lock (friendd)
  223. {
  224. friendd.subscriptionData.Remove(AgentID);
  225. List<FriendListItem> fl = friendd.friendData;
  226. for (int j = 0; j < fl.Count; j++)
  227. {
  228. if (fl[j].Friend == AgentID)
  229. {
  230. fl[j].onlinestatus = false;
  231. }
  232. }
  233. friendd.friendData = fl;
  234. m_presences[AgentsNeedingNotification[i]] = friendd;
  235. }
  236. PresenceInformer friendlistupdater = new PresenceInformer();
  237. friendlistupdater.presence1 = AgentData;
  238. friendlistupdater.presence2 = friendd;
  239. WaitCallback cb3 = new WaitCallback(friendlistupdater.go);
  240. ThreadPool.QueueUserWorkItem(cb3);
  241. //SendRegionPresenceUpdate(AgentData, friendd);
  242. //removeBackReference(AgentID, AgentsNeedingNotification[i]);
  243. }
  244. }
  245. }
  246. }
  247. #endregion
  248. #region UserServer Comms
  249. /// <summary>
  250. /// Returns a list of FriendsListItems that describe the friends and permissions in the friend relationship for LLUUID friendslistowner
  251. /// </summary>
  252. /// <param name="friendlistowner">The agent that we're retreiving the friends Data.</param>
  253. public List<FriendListItem> GetUserFriendList(LLUUID friendlistowner)
  254. {
  255. List<FriendListItem> buddylist = new List<FriendListItem>();
  256. try
  257. {
  258. Hashtable param = new Hashtable();
  259. param["ownerID"] = friendlistowner.UUID.ToString();
  260. IList parameters = new ArrayList();
  261. parameters.Add(param);
  262. XmlRpcRequest req = new XmlRpcRequest("get_user_friend_list", parameters);
  263. XmlRpcResponse resp = req.Send(m_cfg.UserServerURL, 3000);
  264. Hashtable respData = (Hashtable)resp.Value;
  265. if (respData.Contains("avcount"))
  266. {
  267. buddylist = ConvertXMLRPCDataToFriendListItemList(respData);
  268. }
  269. }
  270. catch (WebException e)
  271. {
  272. m_log.Warn("Error when trying to fetch Avatar's friends list: " +
  273. e.Message);
  274. // Return Empty list (no friends)
  275. }
  276. return buddylist;
  277. }
  278. /// <summary>
  279. /// Converts XMLRPC Friend List to FriendListItem Object
  280. /// </summary>
  281. /// <param name="data">XMLRPC response data Hashtable</param>
  282. /// <returns></returns>
  283. public List<FriendListItem> ConvertXMLRPCDataToFriendListItemList(Hashtable data)
  284. {
  285. List<FriendListItem> buddylist = new List<FriendListItem>();
  286. int buddycount = Convert.ToInt32((string)data["avcount"]);
  287. for (int i = 0; i < buddycount; i++)
  288. {
  289. FriendListItem buddylistitem = new FriendListItem();
  290. buddylistitem.FriendListOwner = new LLUUID((string)data["ownerID" + i.ToString()]);
  291. buddylistitem.Friend = new LLUUID((string)data["friendID" + i.ToString()]);
  292. buddylistitem.FriendListOwnerPerms = (uint)Convert.ToInt32((string)data["ownerPerms" + i.ToString()]);
  293. buddylistitem.FriendPerms = (uint)Convert.ToInt32((string)data["friendPerms" + i.ToString()]);
  294. buddylist.Add(buddylistitem);
  295. }
  296. return buddylist;
  297. }
  298. /// <summary>
  299. /// UserServer sends an expect_user method
  300. /// this handles the method and provisions the
  301. /// necessary info for presence to work
  302. /// </summary>
  303. /// <param name="request">UserServer Data</param>
  304. /// <returns></returns>
  305. public XmlRpcResponse UserLoggedOn(XmlRpcRequest request)
  306. {
  307. m_log.Info("[LOGON]: User logged on, building indexes for user");
  308. Hashtable requestData = (Hashtable)request.Params[0];
  309. //requestData["sendkey"] = serv.sendkey;
  310. //requestData["agentid"] = agentID.ToString();
  311. //requestData["sessionid"] = sessionID.ToString();
  312. //requestData["regionid"] = RegionID.ToString();
  313. //requestData["regionhandle"] = regionhandle.ToString();
  314. //requestData["positionx"] = positionX.ToString();
  315. //requestData["positiony"] = positionY.ToString();
  316. //requestData["positionz"] = positionZ.ToString();
  317. //requestData["firstname"] = firstname;
  318. //requestData["lastname"] = lastname;
  319. AgentCircuitData agentData = new AgentCircuitData();
  320. agentData.SessionID = new LLUUID((string)requestData["sessionid"]);
  321. agentData.SecureSessionID = new LLUUID((string)requestData["secure_session_id"]);
  322. agentData.firstname = (string)requestData["firstname"];
  323. agentData.lastname = (string)requestData["lastname"];
  324. agentData.AgentID = new LLUUID((string)requestData["agentid"]);
  325. agentData.circuitcode = Convert.ToUInt32(requestData["circuit_code"]);
  326. agentData.CapsPath = (string)requestData["caps_path"];
  327. if (requestData.ContainsKey("child_agent") && requestData["child_agent"].Equals("1"))
  328. {
  329. agentData.child = true;
  330. }
  331. else
  332. {
  333. agentData.startpos =
  334. new LLVector3(Convert.ToUInt32(requestData["positionx"]),
  335. Convert.ToUInt32(requestData["positiony"]),
  336. Convert.ToUInt32(requestData["positionz"]));
  337. agentData.child = false;
  338. }
  339. ulong regionHandle = Convert.ToUInt64((string)requestData["regionhandle"]);
  340. UserPresenceData up = new UserPresenceData();
  341. up.agentData = agentData;
  342. List<FriendListItem> flData = GetUserFriendList(agentData.AgentID);
  343. up.friendData = flData;
  344. RegionProfileData riData = GetRegionInfo(regionHandle);
  345. up.regionData = riData;
  346. ProcessFriendListSubscriptions(up);
  347. return new XmlRpcResponse();
  348. }
  349. /// <summary>
  350. /// The UserServer got a Logoff message
  351. /// Cleanup time for that user. Send out presence notifications
  352. /// </summary>
  353. /// <param name="request"></param>
  354. /// <returns></returns>
  355. public XmlRpcResponse UserLoggedOff(XmlRpcRequest request)
  356. {
  357. Hashtable requestData = (Hashtable)request.Params[0];
  358. LLUUID AgentID = new LLUUID((string)requestData["agentid"]);
  359. ProcessLogOff(AgentID);
  360. return new XmlRpcResponse();
  361. }
  362. #endregion
  363. #region regioninfo gathering
  364. /// <summary>
  365. /// Gets and caches a RegionInfo object from the gridserver based on regionhandle
  366. /// if the regionhandle is already cached, use the cached values
  367. /// </summary>
  368. /// <param name="regionhandle">handle to the XY of the region we're looking for</param>
  369. /// <returns>A RegionInfo object to stick in the presence info</returns>
  370. public RegionProfileData GetRegionInfo(ulong regionhandle)
  371. {
  372. RegionProfileData regionInfo = null;
  373. if (m_regionInfoCache.Contains(regionhandle))
  374. {
  375. regionInfo = (RegionProfileData)m_regionInfoCache[regionhandle];
  376. }
  377. else
  378. {
  379. regionInfo = RequestRegionInfo(regionhandle);
  380. }
  381. return regionInfo;
  382. }
  383. /// <summary>
  384. /// Get RegionProfileData from the GridServer
  385. /// We'll Cache this information and use it for presence updates
  386. /// </summary>
  387. /// <param name="regionHandle"></param>
  388. /// <returns></returns>
  389. public RegionProfileData RequestRegionInfo(ulong regionHandle)
  390. { RegionProfileData regionProfile = null;
  391. try
  392. {
  393. Hashtable requestData = new Hashtable();
  394. requestData["region_handle"] = regionHandle.ToString();
  395. requestData["authkey"] = m_cfg.GridSendKey;
  396. ArrayList SendParams = new ArrayList();
  397. SendParams.Add(requestData);
  398. XmlRpcRequest GridReq = new XmlRpcRequest("simulator_data_request", SendParams);
  399. XmlRpcResponse GridResp = GridReq.Send(m_cfg.GridServerURL, 3000);
  400. Hashtable responseData = (Hashtable)GridResp.Value;
  401. if (responseData.ContainsKey("error"))
  402. {
  403. m_log.Error("[GRID]: error received from grid server" + responseData["error"]);
  404. return null;
  405. }
  406. uint regX = Convert.ToUInt32((string)responseData["region_locx"]);
  407. uint regY = Convert.ToUInt32((string)responseData["region_locy"]);
  408. string internalIpStr = (string)responseData["sim_ip"];
  409. uint port = Convert.ToUInt32(responseData["sim_port"]);
  410. string externalUri = (string)responseData["sim_uri"];
  411. string neighbourExternalUri = externalUri;
  412. regionProfile = new RegionProfileData();
  413. regionProfile.httpPort = (uint)Convert.ToInt32((string)responseData["http_port"]);
  414. regionProfile.httpServerURI = "http://" + internalIpStr + ":" + regionProfile.httpPort + "/";
  415. regionProfile.regionHandle = Helpers.UIntsToLong((regX * Constants.RegionSize), (regY * Constants.RegionSize));
  416. regionProfile.regionLocX = regX;
  417. regionProfile.regionLocY = regY;
  418. regionProfile.remotingPort = Convert.ToUInt32((string)responseData["remoting_port"]);
  419. regionProfile.UUID = new LLUUID((string)responseData["region_UUID"]);
  420. regionProfile.regionName = (string)responseData["region_name"];
  421. lock (m_regionInfoCache)
  422. {
  423. if (!m_regionInfoCache.Contains(regionHandle))
  424. {
  425. m_regionInfoCache.Add(regionHandle, regionProfile);
  426. }
  427. }
  428. }
  429. catch (WebException)
  430. {
  431. m_log.Error("[GRID]: " +
  432. "Region lookup failed for: " + regionHandle.ToString() +
  433. " - Is the GridServer down?");
  434. return null;
  435. }
  436. return regionProfile;
  437. }
  438. public bool registerWithUserServer ()
  439. {
  440. Hashtable UserParams = new Hashtable();
  441. // Login / Authentication
  442. if (m_cfg.HttpSSL)
  443. {
  444. UserParams["uri"] = "https://" + m_cfg.MessageServerIP + ":" + m_cfg.HttpPort;
  445. }
  446. else
  447. {
  448. UserParams["uri"] = "http://" + m_cfg.MessageServerIP + ":" + m_cfg.HttpPort;
  449. }
  450. UserParams["recvkey"] = m_cfg.UserRecvKey;
  451. UserParams["sendkey"] = m_cfg.UserRecvKey;
  452. // Package into an XMLRPC Request
  453. ArrayList SendParams = new ArrayList();
  454. SendParams.Add(UserParams);
  455. // Send Request
  456. XmlRpcRequest UserReq;
  457. XmlRpcResponse UserResp;
  458. try
  459. {
  460. UserReq = new XmlRpcRequest("register_messageserver", SendParams);
  461. UserResp = UserReq.Send(m_cfg.UserServerURL, 16000);
  462. } catch (Exception ex)
  463. {
  464. m_log.Error("Unable to connect to grid. Grid server not running?");
  465. throw(ex);
  466. }
  467. Hashtable GridRespData = (Hashtable)UserResp.Value;
  468. Hashtable griddatahash = GridRespData;
  469. // Process Response
  470. if (GridRespData.ContainsKey("responsestring"))
  471. {
  472. return true;
  473. }
  474. else
  475. {
  476. return false;
  477. }
  478. }
  479. public bool deregisterWithUserServer()
  480. {
  481. Hashtable UserParams = new Hashtable();
  482. // Login / Authentication
  483. if (m_cfg.HttpSSL)
  484. {
  485. UserParams["uri"] = "https://" + m_cfg.MessageServerIP + ":" + m_cfg.HttpPort;
  486. }
  487. else
  488. {
  489. UserParams["uri"] = "http://" + m_cfg.MessageServerIP + ":" + m_cfg.HttpPort;
  490. }
  491. UserParams["recvkey"] = m_cfg.UserRecvKey;
  492. UserParams["sendkey"] = m_cfg.UserRecvKey;
  493. // Package into an XMLRPC Request
  494. ArrayList SendParams = new ArrayList();
  495. SendParams.Add(UserParams);
  496. // Send Request
  497. XmlRpcRequest UserReq;
  498. XmlRpcResponse UserResp;
  499. try
  500. {
  501. UserReq = new XmlRpcRequest("deregister_messageserver", SendParams);
  502. UserResp = UserReq.Send(m_cfg.UserServerURL, 16000);
  503. }
  504. catch (Exception ex)
  505. {
  506. m_log.Error("Unable to connect to grid. Grid server not running?");
  507. throw (ex);
  508. }
  509. Hashtable UserRespData = (Hashtable)UserResp.Value;
  510. Hashtable userdatahash = UserRespData;
  511. // Process Response
  512. if (UserRespData.ContainsKey("responsestring"))
  513. {
  514. return true;
  515. }
  516. else
  517. {
  518. return false;
  519. }
  520. }
  521. #endregion
  522. }
  523. }