UserManagerBase.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  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.Collections;
  29. using System.Collections.Generic;
  30. using System.Reflection;
  31. using System.Security.Cryptography;
  32. using libsecondlife;
  33. using libsecondlife.StructuredData;
  34. using log4net;
  35. using Nwc.XmlRpc;
  36. using OpenSim.Framework.Statistics;
  37. namespace OpenSim.Framework.Communications
  38. {
  39. /// <summary>
  40. /// Base class for user management (create, read, etc)
  41. /// </summary>
  42. public abstract class UserManagerBase : IUserService
  43. {
  44. private static readonly ILog m_log
  45. = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  46. public UserConfig _config;
  47. private Dictionary<string, IUserData> _plugins = new Dictionary<string, IUserData>();
  48. /// <summary>
  49. /// Adds a new user server plugin - user servers will be requested in the order they were loaded.
  50. /// </summary>
  51. /// <param name="FileName">The filename to the user server plugin DLL</param>
  52. public void AddPlugin(string FileName, string connect)
  53. {
  54. if (!String.IsNullOrEmpty(FileName))
  55. {
  56. m_log.Info("[USERSTORAGE]: Attempting to load " + FileName);
  57. Assembly pluginAssembly = Assembly.LoadFrom(FileName);
  58. m_log.Info("[USERSTORAGE]: Found " + pluginAssembly.GetTypes().Length + " interfaces.");
  59. foreach (Type pluginType in pluginAssembly.GetTypes())
  60. {
  61. if (!pluginType.IsAbstract)
  62. {
  63. Type typeInterface = pluginType.GetInterface("IUserData", true);
  64. if (typeInterface != null)
  65. {
  66. IUserData plug =
  67. (IUserData) Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString()));
  68. AddPlugin(plug, connect);
  69. }
  70. }
  71. }
  72. }
  73. }
  74. public void AddPlugin(IUserData plug, string connect)
  75. {
  76. plug.Initialise(connect);
  77. _plugins.Add(plug.Name, plug);
  78. m_log.Info("[USERSTORAGE]: Added IUserData Interface");
  79. }
  80. #region Get UserProfile
  81. // see IUserService
  82. public UserProfileData GetUserProfile(string fname, string lname)
  83. {
  84. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  85. {
  86. UserProfileData profile = plugin.Value.GetUserByName(fname, lname);
  87. if (profile != null)
  88. {
  89. profile.CurrentAgent = getUserAgent(profile.ID);
  90. return profile;
  91. }
  92. }
  93. return null;
  94. }
  95. // see IUserService
  96. public UserProfileData GetUserProfile(LLUUID uuid)
  97. {
  98. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  99. {
  100. UserProfileData profile = plugin.Value.GetUserByUUID(uuid);
  101. if (null != profile)
  102. {
  103. profile.CurrentAgent = getUserAgent(profile.ID);
  104. return profile;
  105. }
  106. }
  107. return null;
  108. }
  109. public List<AvatarPickerAvatar> GenerateAgentPickerRequestResponse(LLUUID queryID, string query)
  110. {
  111. List<AvatarPickerAvatar> pickerlist = new List<AvatarPickerAvatar>();
  112. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  113. {
  114. try
  115. {
  116. pickerlist = plugin.Value.GeneratePickerResults(queryID, query);
  117. }
  118. catch (Exception)
  119. {
  120. m_log.Info("[USERSTORAGE]: Unable to generate AgentPickerData via " + plugin.Key + "(" + query + ")");
  121. return new List<AvatarPickerAvatar>();
  122. }
  123. }
  124. return pickerlist;
  125. }
  126. /// <summary>
  127. /// Set's user profile from data object
  128. /// </summary>
  129. /// <param name="data"></param>
  130. /// <returns></returns>
  131. public bool setUserProfile(UserProfileData data)
  132. {
  133. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  134. {
  135. try
  136. {
  137. plugin.Value.UpdateUserProfile(data);
  138. return true;
  139. }
  140. catch (Exception e)
  141. {
  142. m_log.Info("[USERSTORAGE]: Unable to set user via " + plugin.Key + "(" + e.ToString() + ")");
  143. }
  144. }
  145. return false;
  146. }
  147. #endregion
  148. #region Get UserAgent
  149. /// <summary>
  150. /// Loads a user agent by uuid (not called directly)
  151. /// </summary>
  152. /// <param name="uuid">The agent's UUID</param>
  153. /// <returns>Agent profiles</returns>
  154. public UserAgentData getUserAgent(LLUUID uuid)
  155. {
  156. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  157. {
  158. try
  159. {
  160. return plugin.Value.GetAgentByUUID(uuid);
  161. }
  162. catch (Exception e)
  163. {
  164. m_log.Info("[USERSTORAGE]: Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  165. }
  166. }
  167. return null;
  168. }
  169. /// <summary>
  170. /// Loads a user's friend list
  171. /// </summary>
  172. /// <param name="name">the UUID of the friend list owner</param>
  173. /// <returns>A List of FriendListItems that contains info about the user's friends</returns>
  174. public List<FriendListItem> GetUserFriendList(LLUUID ownerID)
  175. {
  176. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  177. {
  178. try
  179. {
  180. return plugin.Value.GetUserFriendList(ownerID);
  181. }
  182. catch (Exception e)
  183. {
  184. m_log.Info("[USERSTORAGE]: Unable to GetUserFriendList via " + plugin.Key + "(" + e.ToString() + ")");
  185. }
  186. }
  187. return null;
  188. }
  189. public void StoreWebLoginKey(LLUUID agentID, LLUUID webLoginKey)
  190. {
  191. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  192. {
  193. try
  194. {
  195. plugin.Value.StoreWebLoginKey(agentID, webLoginKey);
  196. }
  197. catch (Exception e)
  198. {
  199. m_log.Info("[USERSTORAGE]: Unable to Store WebLoginKey via " + plugin.Key + "(" + e.ToString() + ")");
  200. }
  201. }
  202. }
  203. public void AddNewUserFriend(LLUUID friendlistowner, LLUUID friend, uint perms)
  204. {
  205. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  206. {
  207. try
  208. {
  209. plugin.Value.AddNewUserFriend(friendlistowner,friend,perms);
  210. }
  211. catch (Exception e)
  212. {
  213. m_log.Info("[USERSTORAGE]: Unable to AddNewUserFriend via " + plugin.Key + "(" + e.ToString() + ")");
  214. }
  215. }
  216. }
  217. public void RemoveUserFriend(LLUUID friendlistowner, LLUUID friend)
  218. {
  219. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  220. {
  221. try
  222. {
  223. plugin.Value.RemoveUserFriend(friendlistowner, friend);
  224. }
  225. catch (Exception e)
  226. {
  227. m_log.Info("[USERSTORAGE]: Unable to RemoveUserFriend via " + plugin.Key + "(" + e.ToString() + ")");
  228. }
  229. }
  230. }
  231. public void UpdateUserFriendPerms(LLUUID friendlistowner, LLUUID friend, uint perms)
  232. {
  233. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  234. {
  235. try
  236. {
  237. plugin.Value.UpdateUserFriendPerms(friendlistowner, friend, perms);
  238. }
  239. catch (Exception e)
  240. {
  241. m_log.Info("[USERSTORAGE]: Unable to UpdateUserFriendPerms via " + plugin.Key + "(" + e.ToString() + ")");
  242. }
  243. }
  244. }
  245. /// <summary>
  246. /// Loads a user agent by name (not called directly)
  247. /// </summary>
  248. /// <param name="name">The agent's name</param>
  249. /// <returns>A user agent</returns>
  250. public UserAgentData getUserAgent(string name)
  251. {
  252. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  253. {
  254. try
  255. {
  256. return plugin.Value.GetAgentByName(name);
  257. }
  258. catch (Exception e)
  259. {
  260. m_log.Info("[USERSTORAGE]: Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  261. }
  262. }
  263. return null;
  264. }
  265. /// <summary>
  266. /// Resets the currentAgent in the user profile
  267. /// </summary>
  268. /// <param name="agentID">The agent's ID</param>
  269. public void clearUserAgent(LLUUID agentID)
  270. {
  271. UserProfileData profile = GetUserProfile(agentID);
  272. profile.CurrentAgent = null;
  273. setUserProfile(profile);
  274. }
  275. /// <summary>
  276. /// Loads a user agent by name (not called directly)
  277. /// </summary>
  278. /// <param name="fname">The agent's firstname</param>
  279. /// <param name="lname">The agent's lastname</param>
  280. /// <returns>A user agent</returns>
  281. public UserAgentData getUserAgent(string fname, string lname)
  282. {
  283. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  284. {
  285. try
  286. {
  287. return plugin.Value.GetAgentByName(fname, lname);
  288. }
  289. catch (Exception e)
  290. {
  291. m_log.Info("[USERSTORAGE]: Unable to find user via " + plugin.Key + "(" + e.ToString() + ")");
  292. }
  293. }
  294. return null;
  295. }
  296. #endregion
  297. #region CreateAgent
  298. /// <summary>
  299. /// Creates and initialises a new user agent - make sure to use CommitAgent when done to submit to the DB
  300. /// </summary>
  301. /// <param name="profile">The users profile</param>
  302. /// <param name="request">The users loginrequest</param>
  303. public void CreateAgent(UserProfileData profile, XmlRpcRequest request)
  304. {
  305. Hashtable requestData = (Hashtable) request.Params[0];
  306. UserAgentData agent = new UserAgentData();
  307. // User connection
  308. agent.AgentOnline = true;
  309. // Generate sessions
  310. RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider();
  311. byte[] randDataS = new byte[16];
  312. byte[] randDataSS = new byte[16];
  313. rand.GetBytes(randDataS);
  314. rand.GetBytes(randDataSS);
  315. agent.SecureSessionID = new LLUUID(randDataSS, 0);
  316. agent.SessionID = new LLUUID(randDataS, 0);
  317. // Profile UUID
  318. agent.ProfileID = profile.ID;
  319. // Current position (from Home)
  320. agent.Handle = profile.HomeRegion;
  321. agent.Position = profile.HomeLocation;
  322. // If user specified additional start, use that
  323. if (requestData.ContainsKey("start"))
  324. {
  325. string startLoc = ((string)requestData["start"]).Trim();
  326. if (("last" == startLoc) && (profile.CurrentAgent != null))
  327. {
  328. if ((profile.CurrentAgent.Position.X > 0)
  329. && (profile.CurrentAgent.Position.Y > 0)
  330. && (profile.CurrentAgent.Position.Z > 0)
  331. )
  332. {
  333. // TODO: Right now, currentRegion has not been used in GridServer for requesting region.
  334. // TODO: It is only using currentHandle.
  335. agent.Region = profile.CurrentAgent.Region;
  336. agent.Handle = profile.CurrentAgent.Handle;
  337. agent.Position = profile.CurrentAgent.Position;
  338. }
  339. }
  340. // if (!(startLoc == "last" || startLoc == "home"))
  341. // {
  342. // // Format: uri:Ahern&162&213&34
  343. // try
  344. // {
  345. // string[] parts = startLoc.Remove(0, 4).Split('&');
  346. // //string region = parts[0];
  347. //
  348. // ////////////////////////////////////////////////////
  349. // //SimProfile SimInfo = new SimProfile();
  350. // //SimInfo = SimInfo.LoadFromGrid(theUser.currentAgent.currentHandle, _config.GridServerURL, _config.GridSendKey, _config.GridRecvKey);
  351. // }
  352. // catch (Exception)
  353. // {
  354. // }
  355. // }
  356. }
  357. // What time did the user login?
  358. agent.LoginTime = Util.UnixTimeSinceEpoch();
  359. agent.LogoutTime = 0;
  360. // Current location
  361. agent.InitialRegion = LLUUID.Zero; // Fill in later
  362. agent.Region = LLUUID.Zero; // Fill in later
  363. profile.CurrentAgent = agent;
  364. }
  365. /// <summary>
  366. /// Process a user logoff from OpenSim.
  367. /// </summary>
  368. /// <param name="userid"></param>
  369. /// <param name="regionid"></param>
  370. /// <param name="regionhandle"></param>
  371. /// <param name="posx"></param>
  372. /// <param name="posy"></param>
  373. /// <param name="posz"></param>
  374. public void LogOffUser(LLUUID userid, LLUUID regionid, ulong regionhandle, float posx, float posy, float posz)
  375. {
  376. if (StatsManager.UserStats != null)
  377. StatsManager.UserStats.AddLogout();
  378. UserProfileData userProfile;
  379. UserAgentData userAgent;
  380. LLVector3 currentPos = new LLVector3(posx, posy, posz);
  381. userProfile = GetUserProfile(userid);
  382. if (userProfile != null)
  383. {
  384. // This line needs to be in side the above if statement or the UserServer will crash on some logouts.
  385. m_log.Info("[LOGOUT]: " + userProfile.FirstName + " " + userProfile.SurName + " from " + regionhandle + "(" + posx + "," + posy + "," + posz + ")");
  386. userAgent = userProfile.CurrentAgent;
  387. if (userAgent != null)
  388. {
  389. userAgent.AgentOnline = false;
  390. userAgent.LogoutTime = Util.UnixTimeSinceEpoch();
  391. //userAgent.sessionID = LLUUID.Zero;
  392. if (regionid != LLUUID.Zero)
  393. {
  394. userAgent.Region = regionid;
  395. }
  396. userAgent.Handle = regionhandle;
  397. userAgent.Position = currentPos;
  398. userProfile.CurrentAgent = userAgent;
  399. CommitAgent(ref userProfile);
  400. }
  401. else
  402. {
  403. // If currentagent is null, we can't reference it here or the UserServer crashes!
  404. m_log.Info("[LOGOUT]: didn't save logout position: " + userid.ToString());
  405. }
  406. }
  407. else
  408. {
  409. m_log.Warn("[LOGOUT]: Unknown User logged out");
  410. }
  411. }
  412. public void CreateAgent(UserProfileData profile, LLSD request)
  413. {
  414. UserAgentData agent = new UserAgentData();
  415. // User connection
  416. agent.AgentOnline = true;
  417. // Generate sessions
  418. RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider();
  419. byte[] randDataS = new byte[16];
  420. byte[] randDataSS = new byte[16];
  421. rand.GetBytes(randDataS);
  422. rand.GetBytes(randDataSS);
  423. agent.SecureSessionID = new LLUUID(randDataSS, 0);
  424. agent.SessionID = new LLUUID(randDataS, 0);
  425. // Profile UUID
  426. agent.ProfileID = profile.ID;
  427. // Current position (from Home)
  428. agent.Handle = profile.HomeRegion;
  429. agent.Position = profile.HomeLocation;
  430. // What time did the user login?
  431. agent.LoginTime = Util.UnixTimeSinceEpoch();
  432. agent.LogoutTime = 0;
  433. // Current location
  434. agent.InitialRegion = LLUUID.Zero; // Fill in later
  435. agent.Region = LLUUID.Zero; // Fill in later
  436. profile.CurrentAgent = agent;
  437. }
  438. /// <summary>
  439. /// Saves a target agent to the database
  440. /// </summary>
  441. /// <param name="profile">The users profile</param>
  442. /// <returns>Successful?</returns>
  443. public bool CommitAgent(ref UserProfileData profile)
  444. {
  445. // TODO: how is this function different from setUserProfile? -> Add AddUserAgent() here and commit both tables "users" and "agents"
  446. // TODO: what is the logic should be?
  447. bool ret = false;
  448. ret = AddUserAgent(profile.CurrentAgent);
  449. ret = ret & setUserProfile(profile);
  450. return ret;
  451. }
  452. #endregion
  453. /// <summary>
  454. ///
  455. /// </summary>
  456. /// <param name="user"></param>
  457. public LLUUID AddUserProfile(string firstName, string lastName, string pass, uint regX, uint regY)
  458. {
  459. UserProfileData user = new UserProfileData();
  460. user.HomeLocation = new LLVector3(128, 128, 100);
  461. user.ID = LLUUID.Random();
  462. user.FirstName = firstName;
  463. user.SurName = lastName;
  464. user.PasswordHash = pass;
  465. user.PasswordSalt = String.Empty;
  466. user.Created = Util.UnixTimeSinceEpoch();
  467. user.HomeLookAt = new LLVector3(100, 100, 100);
  468. user.HomeRegionX = regX;
  469. user.HomeRegionY = regY;
  470. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  471. {
  472. try
  473. {
  474. plugin.Value.AddNewUserProfile(user);
  475. }
  476. catch (Exception e)
  477. {
  478. m_log.Info("[USERSTORAGE]: Unable to add user via " + plugin.Key + "(" + e.ToString() + ")");
  479. }
  480. }
  481. return user.ID;
  482. }
  483. public bool UpdateUserProfileProperties(UserProfileData UserProfile)
  484. {
  485. if (null == GetUserProfile(UserProfile.ID))
  486. {
  487. m_log.Info("[USERSTORAGE]: Failed to find User by UUID " + UserProfile.ID.ToString());
  488. return false;
  489. }
  490. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  491. {
  492. try
  493. {
  494. plugin.Value.UpdateUserProfile(UserProfile);
  495. }
  496. catch (Exception e)
  497. {
  498. m_log.Info("[USERSTORAGE]: Unable to update user " + UserProfile.ID.ToString()
  499. + " via " + plugin.Key + "(" + e.ToString() + ")");
  500. return false;
  501. }
  502. }
  503. return true;
  504. }
  505. public abstract UserProfileData SetupMasterUser(string firstName, string lastName);
  506. public abstract UserProfileData SetupMasterUser(string firstName, string lastName, string password);
  507. public abstract UserProfileData SetupMasterUser(LLUUID uuid);
  508. /// <summary>
  509. /// Add agent to DB
  510. /// </summary>
  511. /// <param name="agentdata">The agent data to be added</param>
  512. public bool AddUserAgent(UserAgentData agentdata)
  513. {
  514. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  515. {
  516. try
  517. {
  518. plugin.Value.AddNewUserAgent(agentdata);
  519. return true;
  520. }
  521. catch (Exception e)
  522. {
  523. m_log.Info("[USERSTORAGE]: Unable to add agent via " + plugin.Key + "(" + e.ToString() + ")");
  524. }
  525. }
  526. return false;
  527. }
  528. /// Appearance
  529. /// TODO: stubs for now to get us to a compiling state gently
  530. public AvatarAppearance GetUserAppearance(LLUUID user)
  531. {
  532. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  533. {
  534. try
  535. {
  536. return plugin.Value.GetUserAppearance(user);
  537. }
  538. catch (Exception e)
  539. {
  540. m_log.InfoFormat("[USERSTORAGE]: Unable to find user appearance {0} via {1} ({2})", user.ToString(), plugin.Key, e.ToString());
  541. }
  542. }
  543. return null;
  544. }
  545. public void UpdateUserAppearance(LLUUID user, AvatarAppearance appearance)
  546. {
  547. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  548. {
  549. try
  550. {
  551. plugin.Value.UpdateUserAppearance(user, appearance);
  552. }
  553. catch (Exception e)
  554. {
  555. m_log.InfoFormat("[USERSTORAGE]: Unable to update user appearance {0} via {1} ({2})", user.ToString(), plugin.Key, e.ToString());
  556. }
  557. }
  558. }
  559. public void AddAttachment(LLUUID user, LLUUID item)
  560. {
  561. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  562. {
  563. try
  564. {
  565. plugin.Value.AddAttachment(user, item);
  566. }
  567. catch (Exception e)
  568. {
  569. m_log.InfoFormat("[USERSTORAGE]: Unable to attach {3} => {0} via {1} ({2})", user.ToString(), plugin.Key, e.ToString(), item.ToString());
  570. }
  571. }
  572. }
  573. public void RemoveAttachment(LLUUID user, LLUUID item)
  574. {
  575. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  576. {
  577. try
  578. {
  579. plugin.Value.RemoveAttachment(user, item);
  580. }
  581. catch (Exception e)
  582. {
  583. m_log.InfoFormat("[USERSTORAGE]: Unable to remove attachment {3} => {0} via {1} ({2})", user.ToString(), plugin.Key, e.ToString(), item.ToString());
  584. }
  585. }
  586. }
  587. public List<LLUUID> GetAttachments(LLUUID user)
  588. {
  589. foreach (KeyValuePair<string, IUserData> plugin in _plugins)
  590. {
  591. try
  592. {
  593. return plugin.Value.GetAttachments(user);
  594. }
  595. catch (Exception e)
  596. {
  597. m_log.InfoFormat("[USERSTORAGE]: Unable to get attachments for {0} via {1} ({2})", user.ToString(), plugin.Key, e.ToString());
  598. }
  599. }
  600. return new List<LLUUID>();
  601. }
  602. }
  603. }