CachedUserInfo.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  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.Generic;
  29. using System.Reflection;
  30. using System.Threading;
  31. using OpenMetaverse;
  32. using log4net;
  33. namespace OpenSim.Framework.Communications.Cache
  34. {
  35. internal delegate void AddItemDelegate(InventoryItemBase itemInfo);
  36. internal delegate void UpdateItemDelegate(InventoryItemBase itemInfo);
  37. internal delegate void DeleteItemDelegate(UUID itemID);
  38. internal delegate void CreateFolderDelegate(string folderName, UUID folderID, ushort folderType, UUID parentID);
  39. internal delegate void MoveFolderDelegate(UUID folderID, UUID parentID);
  40. internal delegate void PurgeFolderDelegate(UUID folderID);
  41. internal delegate void UpdateFolderDelegate(string name, UUID folderID, ushort type, UUID parentID);
  42. internal delegate void SendInventoryDescendentsDelegate(
  43. IClientAPI client, UUID folderID, bool fetchFolders, bool fetchItems);
  44. public delegate void OnItemReceivedDelegate(UUID itemID);
  45. /// <summary>
  46. /// Stores user profile and inventory data received from backend services for a particular user.
  47. /// </summary>
  48. public class CachedUserInfo
  49. {
  50. public event OnItemReceivedDelegate OnItemReceived;
  51. private static readonly ILog m_log
  52. = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  53. /// <summary>
  54. /// The comms manager holds references to services (user, grid, inventory, etc.)
  55. /// </summary>
  56. private readonly CommunicationsManager m_commsManager;
  57. public UserProfileData UserProfile { get { return m_userProfile; } }
  58. private readonly UserProfileData m_userProfile;
  59. /// <summary>
  60. /// Have we received the user's inventory from the inventory service?
  61. /// </summary>
  62. public bool HasReceivedInventory { get { return m_hasReceivedInventory; } }
  63. private bool m_hasReceivedInventory;
  64. /// <summary>
  65. /// Inventory requests waiting for receipt of this user's inventory from the inventory service.
  66. /// </summary>
  67. private readonly IList<IInventoryRequest> m_pendingRequests = new List<IInventoryRequest>();
  68. /// <summary>
  69. /// The root folder of this user's inventory. Returns null if the root folder has not yet been received.
  70. /// </summary>
  71. public InventoryFolderImpl RootFolder { get { return m_rootFolder; } }
  72. private InventoryFolderImpl m_rootFolder;
  73. public UUID SessionID
  74. {
  75. get { return m_session_id; }
  76. set { m_session_id = value; }
  77. }
  78. private UUID m_session_id = UUID.Zero;
  79. /// <summary>
  80. /// Constructor
  81. /// </summary>
  82. /// <param name="commsManager"></param>
  83. /// <param name="userProfile"></param>
  84. public CachedUserInfo(CommunicationsManager commsManager, UserProfileData userProfile)
  85. {
  86. m_commsManager = commsManager;
  87. m_userProfile = userProfile;
  88. }
  89. /// <summary>
  90. /// This allows a request to be added to be processed once we receive a user's inventory
  91. /// from the inventory service. If we already have the inventory, the request
  92. /// is executed immediately instead.
  93. /// </summary>
  94. /// <param name="parent"></param>
  95. protected void AddRequest(IInventoryRequest request)
  96. {
  97. lock (m_pendingRequests)
  98. {
  99. if (HasReceivedInventory)
  100. {
  101. request.Execute();
  102. }
  103. else
  104. {
  105. m_pendingRequests.Add(request);
  106. }
  107. }
  108. }
  109. /// <summary>
  110. /// Helper function for InventoryReceive() - Store a folder temporarily until we've received entire folder list
  111. /// </summary>
  112. /// <param name="folder"></param>
  113. private void AddFolderToDictionary(InventoryFolderImpl folder, IDictionary<UUID, IList<InventoryFolderImpl>> dictionary)
  114. {
  115. UUID parentFolderId = folder.ParentID;
  116. if (dictionary.ContainsKey(parentFolderId))
  117. dictionary[parentFolderId].Add(folder);
  118. else
  119. {
  120. IList<InventoryFolderImpl> folders = new List<InventoryFolderImpl>();
  121. folders.Add(folder);
  122. dictionary[parentFolderId] = folders;
  123. }
  124. }
  125. /// <summary>
  126. /// Recursively, in depth-first order, add all the folders we've received (stored
  127. /// in a dictionary indexed by parent ID) into the tree that describes user folder
  128. /// heirarchy
  129. /// Any folder that is resolved into the tree is also added to resolvedFolderDictionary,
  130. /// indexed by folder ID.
  131. /// </summary>
  132. /// <param name="parentId">
  133. /// A <see cref="UUID"/>
  134. /// </param>
  135. private void ResolveReceivedFolders(InventoryFolderImpl parentFolder,
  136. IDictionary<UUID, IList<InventoryFolderImpl>> receivedFolderDictionary,
  137. IDictionary<UUID, InventoryFolderImpl> resolvedFolderDictionary)
  138. {
  139. if (receivedFolderDictionary.ContainsKey(parentFolder.ID))
  140. {
  141. List<InventoryFolderImpl> resolvedFolders = new List<InventoryFolderImpl>(); // Folders we've resolved with this invocation
  142. foreach (InventoryFolderImpl folder in receivedFolderDictionary[parentFolder.ID])
  143. {
  144. if (parentFolder.ContainsChildFolder(folder.ID))
  145. {
  146. m_log.WarnFormat(
  147. "[INVENTORY CACHE]: Received folder {0} {1} from inventory service which has already been received",
  148. folder.Name, folder.ID);
  149. }
  150. else
  151. {
  152. if (resolvedFolderDictionary.ContainsKey(folder.ID))
  153. {
  154. m_log.WarnFormat(
  155. "[INVENTORY CACHE]: Received folder {0} {1} from inventory service has already been received but with different parent",
  156. folder.Name, folder.ID);
  157. }
  158. else
  159. {
  160. resolvedFolders.Add(folder);
  161. resolvedFolderDictionary[folder.ID] = folder;
  162. parentFolder.AddChildFolder(folder);
  163. }
  164. }
  165. } // foreach (folder in pendingCategorizationFolders[parentFolder.ID])
  166. receivedFolderDictionary.Remove(parentFolder.ID);
  167. foreach (InventoryFolderImpl folder in resolvedFolders)
  168. ResolveReceivedFolders(folder, receivedFolderDictionary, resolvedFolderDictionary);
  169. } // if (receivedFolderDictionary.ContainsKey(parentFolder.ID))
  170. }
  171. /// <summary>
  172. /// Drop all cached inventory.
  173. /// </summary>
  174. public void DropInventory()
  175. {
  176. m_log.Debug("[INVENTORY CACHE]: DropInventory called");
  177. // Make sure there aren't pending requests around when we do this
  178. // FIXME: There is still a race condition where an inventory operation can be requested (since these aren't being locked).
  179. // Will have to extend locking to exclude this very soon.
  180. lock (m_pendingRequests)
  181. {
  182. m_hasReceivedInventory = false;
  183. m_rootFolder = null;
  184. }
  185. }
  186. /// <summary>
  187. /// Callback invoked when the inventory is received from an async request to the inventory service
  188. /// </summary>
  189. /// <param name="userID"></param>
  190. /// <param name="inventoryCollection"></param>
  191. public void InventoryReceive(ICollection<InventoryFolderImpl> folders, ICollection<InventoryItemBase> items)
  192. {
  193. // FIXME: Exceptions thrown upwards never appear on the console. Could fix further up if these
  194. // are simply being swallowed
  195. try
  196. {
  197. // collection of all received folders, indexed by their parent ID
  198. IDictionary<UUID, IList<InventoryFolderImpl>> receivedFolders =
  199. new Dictionary<UUID, IList<InventoryFolderImpl>>();
  200. // collection of all folders that have been placed into the folder heirarchy starting at m_rootFolder
  201. // This dictonary exists so we don't have to do an InventoryFolderImpl.FindFolder(), which is O(n) on the
  202. // number of folders in our inventory.
  203. // Maybe we should make this structure a member so we can skip InventoryFolderImpl.FindFolder() calls later too?
  204. IDictionary<UUID, InventoryFolderImpl> resolvedFolders =
  205. new Dictionary<UUID, InventoryFolderImpl>();
  206. // Take all received folders, find the root folder, and put ther rest into
  207. // the pendingCategorizationFolders collection
  208. foreach (InventoryFolderImpl folder in folders)
  209. AddFolderToDictionary(folder, receivedFolders);
  210. if (!receivedFolders.ContainsKey(UUID.Zero))
  211. throw new Exception("Database did not return a root inventory folder");
  212. else
  213. {
  214. IList<InventoryFolderImpl> rootFolderList = receivedFolders[UUID.Zero];
  215. m_rootFolder = rootFolderList[0];
  216. resolvedFolders[m_rootFolder.ID] = m_rootFolder;
  217. if (rootFolderList.Count > 1)
  218. {
  219. for (int i = 1; i < rootFolderList.Count; i++)
  220. {
  221. m_log.WarnFormat(
  222. "[INVENTORY CACHE]: Discarding extra root folder {0}. Using previously received root folder {1}",
  223. rootFolderList[i].ID, RootFolder.ID);
  224. }
  225. }
  226. receivedFolders.Remove(UUID.Zero);
  227. }
  228. // Now take the pendingCategorizationFolders collection, and turn that into a tree,
  229. // with the root being RootFolder
  230. if (RootFolder != null)
  231. ResolveReceivedFolders(RootFolder, receivedFolders, resolvedFolders);
  232. // Generate a warning for folders that are not part of the heirarchy
  233. foreach (KeyValuePair<UUID, IList<InventoryFolderImpl>> folderList in receivedFolders)
  234. {
  235. foreach (InventoryFolderImpl folder in folderList.Value)
  236. m_log.WarnFormat("[INVENTORY CACHE]: Malformed Database: Unresolved Pending Folder {0}", folder.Name);
  237. }
  238. // Take all ther received items and put them into the folder tree heirarchy
  239. foreach (InventoryItemBase item in items) {
  240. InventoryFolderImpl folder = resolvedFolders.ContainsKey(item.Folder) ? resolvedFolders[item.Folder] : null;
  241. ItemReceive(item, folder );
  242. }
  243. }
  244. catch (Exception e)
  245. {
  246. m_log.ErrorFormat("[INVENTORY CACHE]: Error processing inventory received from inventory service, {0}", e);
  247. }
  248. // Deal with pending requests
  249. lock (m_pendingRequests)
  250. {
  251. // We're going to change inventory status within the lock to avoid a race condition
  252. // where requests are processed after the AddRequest() method has been called.
  253. m_hasReceivedInventory = true;
  254. foreach (IInventoryRequest request in m_pendingRequests)
  255. {
  256. request.Execute();
  257. }
  258. }
  259. }
  260. /// <summary>
  261. /// Callback invoked when an item is received from an async request to the inventory service.
  262. ///
  263. /// We're assuming here that items are always received after all the folders
  264. /// received.
  265. /// If folder is null, we will search for it starting from RootFolder (an O(n) operation),
  266. /// otherwise we'll just put it into folder
  267. /// </summary>
  268. /// <param name="folderInfo"></param>
  269. private void ItemReceive(InventoryItemBase itemInfo, InventoryFolderImpl folder)
  270. {
  271. // m_log.DebugFormat(
  272. // "[INVENTORY CACHE]: Received item {0} {1} for user {2}",
  273. // itemInfo.Name, itemInfo.ID, userID);
  274. if (folder == null && RootFolder != null)
  275. folder = RootFolder.FindFolder(itemInfo.Folder);
  276. if (null == folder)
  277. {
  278. m_log.WarnFormat(
  279. "Received item {0} {1} but its folder {2} does not exist",
  280. itemInfo.Name, itemInfo.ID, itemInfo.Folder);
  281. return;
  282. }
  283. lock (folder.Items)
  284. {
  285. folder.Items[itemInfo.ID] = itemInfo;
  286. }
  287. if (OnItemReceived != null)
  288. OnItemReceived(itemInfo.ID);
  289. }
  290. /// <summary>
  291. /// Create a folder in this agent's inventory.
  292. /// </summary>
  293. ///
  294. /// If the inventory service has not yet delievered the inventory
  295. /// for this user then the request will be queued.
  296. ///
  297. /// <param name="parentID"></param>
  298. /// <returns></returns>
  299. public bool CreateFolder(string folderName, UUID folderID, ushort folderType, UUID parentID)
  300. {
  301. // m_log.DebugFormat(
  302. // "[AGENT INVENTORY]: Creating inventory folder {0} {1} for {2} {3}", folderID, folderName, remoteClient.Name, remoteClient.AgentId);
  303. if (m_hasReceivedInventory)
  304. {
  305. InventoryFolderImpl parentFolder = RootFolder.FindFolder(parentID);
  306. if (null == parentFolder)
  307. {
  308. m_log.WarnFormat(
  309. "[AGENT INVENTORY]: Tried to create folder {0} {1} but the parent {2} does not exist",
  310. folderName, folderID, parentID);
  311. return false;
  312. }
  313. InventoryFolderImpl createdFolder = parentFolder.CreateChildFolder(folderID, folderName, folderType);
  314. if (createdFolder != null)
  315. {
  316. InventoryFolderBase createdBaseFolder = new InventoryFolderBase();
  317. createdBaseFolder.Owner = createdFolder.Owner;
  318. createdBaseFolder.ID = createdFolder.ID;
  319. createdBaseFolder.Name = createdFolder.Name;
  320. createdBaseFolder.ParentID = createdFolder.ParentID;
  321. createdBaseFolder.Type = createdFolder.Type;
  322. createdBaseFolder.Version = createdFolder.Version;
  323. if (m_commsManager.SecureInventoryService != null)
  324. {
  325. m_commsManager.SecureInventoryService.AddFolder(createdBaseFolder, m_session_id);
  326. }
  327. else
  328. {
  329. m_commsManager.InventoryService.AddFolder(createdBaseFolder);
  330. }
  331. return true;
  332. }
  333. else
  334. {
  335. m_log.WarnFormat(
  336. "[AGENT INVENTORY]: Tried to create folder {0} {1} but the folder already exists",
  337. folderName, folderID);
  338. return false;
  339. }
  340. }
  341. else
  342. {
  343. AddRequest(
  344. new InventoryRequest(
  345. Delegate.CreateDelegate(typeof(CreateFolderDelegate), this, "CreateFolder"),
  346. new object[] { folderName, folderID, folderType, parentID }));
  347. return true;
  348. }
  349. }
  350. /// <summary>
  351. /// Handle a client request to update the inventory folder
  352. /// </summary>
  353. ///
  354. /// If the inventory service has not yet delievered the inventory
  355. /// for this user then the request will be queued.
  356. ///
  357. /// FIXME: We call add new inventory folder because in the data layer, we happen to use an SQL REPLACE
  358. /// so this will work to rename an existing folder. Needless to say, to rely on this is very confusing,
  359. /// and needs to be changed.
  360. ///
  361. /// <param name="folderID"></param>
  362. /// <param name="type"></param>
  363. /// <param name="name"></param>
  364. /// <param name="parentID"></param>
  365. public bool UpdateFolder(string name, UUID folderID, ushort type, UUID parentID)
  366. {
  367. // m_log.DebugFormat(
  368. // "[AGENT INVENTORY]: Updating inventory folder {0} {1} for {2} {3}", folderID, name, remoteClient.Name, remoteClient.AgentId);
  369. if (m_hasReceivedInventory)
  370. {
  371. InventoryFolderImpl folder = RootFolder.FindFolder(folderID);
  372. // Delegate movement if updated parent id isn't the same as the existing parentId
  373. if (folder.ParentID != parentID)
  374. MoveFolder(folderID, parentID);
  375. InventoryFolderBase baseFolder = new InventoryFolderBase();
  376. baseFolder.Owner = m_userProfile.ID;
  377. baseFolder.ID = folderID;
  378. baseFolder.Name = name;
  379. baseFolder.ParentID = parentID;
  380. baseFolder.Type = (short)type;
  381. baseFolder.Version = RootFolder.Version;
  382. if (m_commsManager.SecureInventoryService != null)
  383. {
  384. m_commsManager.SecureInventoryService.UpdateFolder(baseFolder, m_session_id);
  385. }
  386. else
  387. {
  388. m_commsManager.InventoryService.UpdateFolder(baseFolder);
  389. }
  390. folder.Name = name;
  391. folder.Type = (short)type;
  392. }
  393. else
  394. {
  395. AddRequest(
  396. new InventoryRequest(
  397. Delegate.CreateDelegate(typeof(UpdateFolderDelegate), this, "UpdateFolder"),
  398. new object[] { name, folderID, type, parentID }));
  399. }
  400. return true;
  401. }
  402. /// <summary>
  403. /// Handle an inventory folder move request from the client.
  404. ///
  405. /// If the inventory service has not yet delievered the inventory
  406. /// for this user then the request will be queued.
  407. /// </summary>
  408. ///
  409. /// <param name="folderID"></param>
  410. /// <param name="parentID"></param>
  411. /// <returns>
  412. /// true if the delete was successful, or if it was queued pending folder receipt
  413. /// false if the folder to be deleted did not exist.
  414. /// </returns>
  415. public bool MoveFolder(UUID folderID, UUID parentID)
  416. {
  417. // m_log.DebugFormat(
  418. // "[AGENT INVENTORY]: Moving inventory folder {0} into folder {1} for {2} {3}",
  419. // parentID, remoteClient.Name, remoteClient.Name, remoteClient.AgentId);
  420. if (m_hasReceivedInventory)
  421. {
  422. InventoryFolderBase baseFolder = new InventoryFolderBase();
  423. baseFolder.Owner = m_userProfile.ID;
  424. baseFolder.ID = folderID;
  425. baseFolder.ParentID = parentID;
  426. if (m_commsManager.SecureInventoryService != null)
  427. {
  428. m_commsManager.SecureInventoryService.MoveFolder(baseFolder, m_session_id);
  429. }
  430. else
  431. {
  432. m_commsManager.InventoryService.MoveFolder(baseFolder);
  433. }
  434. InventoryFolderImpl folder = RootFolder.FindFolder(folderID);
  435. InventoryFolderImpl parentFolder = RootFolder.FindFolder(parentID);
  436. if (parentFolder != null && folder != null)
  437. {
  438. InventoryFolderImpl oldParentFolder = RootFolder.FindFolder(folder.ParentID);
  439. if (oldParentFolder != null)
  440. {
  441. oldParentFolder.RemoveChildFolder(folderID);
  442. parentFolder.AddChildFolder(folder);
  443. }
  444. else
  445. {
  446. return false;
  447. }
  448. }
  449. else
  450. {
  451. return false;
  452. }
  453. return true;
  454. }
  455. else
  456. {
  457. AddRequest(
  458. new InventoryRequest(
  459. Delegate.CreateDelegate(typeof(MoveFolderDelegate), this, "MoveFolder"),
  460. new object[] { folderID, parentID }));
  461. return true;
  462. }
  463. }
  464. /// <summary>
  465. /// This method will delete all the items and folders in the given folder.
  466. /// </summary>
  467. /// If the inventory service has not yet delievered the inventory
  468. /// for this user then the request will be queued.
  469. ///
  470. /// <param name="folderID"></param>
  471. public bool PurgeFolder(UUID folderID)
  472. {
  473. // m_log.InfoFormat("[AGENT INVENTORY]: Purging folder {0} for {1} uuid {2}",
  474. // folderID, remoteClient.Name, remoteClient.AgentId);
  475. if (m_hasReceivedInventory)
  476. {
  477. InventoryFolderImpl purgedFolder = RootFolder.FindFolder(folderID);
  478. if (purgedFolder != null)
  479. {
  480. // XXX Nasty - have to create a new object to hold details we already have
  481. InventoryFolderBase purgedBaseFolder = new InventoryFolderBase();
  482. purgedBaseFolder.Owner = purgedFolder.Owner;
  483. purgedBaseFolder.ID = purgedFolder.ID;
  484. purgedBaseFolder.Name = purgedFolder.Name;
  485. purgedBaseFolder.ParentID = purgedFolder.ParentID;
  486. purgedBaseFolder.Type = purgedFolder.Type;
  487. purgedBaseFolder.Version = purgedFolder.Version;
  488. if (m_commsManager.SecureInventoryService != null)
  489. {
  490. m_commsManager.SecureInventoryService.PurgeFolder(purgedBaseFolder, m_session_id);
  491. }
  492. else
  493. {
  494. m_commsManager.InventoryService.PurgeFolder(purgedBaseFolder);
  495. }
  496. purgedFolder.Purge();
  497. return true;
  498. }
  499. }
  500. else
  501. {
  502. AddRequest(
  503. new InventoryRequest(
  504. Delegate.CreateDelegate(typeof(PurgeFolderDelegate), this, "PurgeFolder"),
  505. new object[] { folderID }));
  506. return true;
  507. }
  508. return false;
  509. }
  510. /// <summary>
  511. /// Add an item to the user's inventory.
  512. /// </summary>
  513. /// If the item has no folder set (i.e. it is UUID.Zero), then it is placed in the most appropriate folder
  514. /// for that type.
  515. /// <param name="itemInfo"></param>
  516. public void AddItem(InventoryItemBase item)
  517. {
  518. if (m_hasReceivedInventory)
  519. {
  520. if (item.Folder == UUID.Zero)
  521. {
  522. InventoryFolderImpl f = FindFolderForType(item.AssetType);
  523. if (f != null)
  524. item.Folder = f.ID;
  525. else
  526. item.Folder = RootFolder.ID;
  527. }
  528. ItemReceive(item, null);
  529. if (m_commsManager.SecureInventoryService != null)
  530. {
  531. m_commsManager.SecureInventoryService.AddItem(item, m_session_id);
  532. }
  533. else
  534. {
  535. m_commsManager.InventoryService.AddItem(item);
  536. }
  537. }
  538. else
  539. {
  540. AddRequest(
  541. new InventoryRequest(
  542. Delegate.CreateDelegate(typeof(AddItemDelegate), this, "AddItem"),
  543. new object[] { item }));
  544. }
  545. }
  546. /// <summary>
  547. /// Update an item in the user's inventory
  548. /// </summary>
  549. /// <param name="userID"></param>
  550. /// <param name="itemInfo"></param>
  551. public void UpdateItem(InventoryItemBase item)
  552. {
  553. if (m_hasReceivedInventory)
  554. {
  555. if (m_commsManager.SecureInventoryService != null)
  556. {
  557. m_commsManager.SecureInventoryService.UpdateItem(item, m_session_id);
  558. }
  559. else
  560. {
  561. m_commsManager.InventoryService.UpdateItem(item);
  562. }
  563. }
  564. else
  565. {
  566. AddRequest(
  567. new InventoryRequest(
  568. Delegate.CreateDelegate(typeof(UpdateItemDelegate), this, "UpdateItem"),
  569. new object[] { item }));
  570. }
  571. }
  572. /// <summary>
  573. /// Delete an item from the user's inventory
  574. ///
  575. /// If the inventory service has not yet delievered the inventory
  576. /// for this user then the request will be queued.
  577. /// </summary>
  578. /// <param name="itemID"></param>
  579. /// <returns>
  580. /// true on a successful delete or a if the request is queued.
  581. /// Returns false on an immediate failure
  582. /// </returns>
  583. public bool DeleteItem(UUID itemID)
  584. {
  585. if (m_hasReceivedInventory)
  586. {
  587. // XXX For historical reasons (grid comms), we need to retrieve the whole item in order to delete, even though
  588. // really only the item id is required.
  589. InventoryItemBase item = RootFolder.FindItem(itemID);
  590. if (null == item)
  591. {
  592. m_log.WarnFormat("[AGENT INVENTORY]: Tried to delete item {0} which does not exist", itemID);
  593. return false;
  594. }
  595. if (RootFolder.DeleteItem(item.ID))
  596. {
  597. if (m_commsManager.SecureInventoryService != null)
  598. {
  599. return m_commsManager.SecureInventoryService.DeleteItem(item, m_session_id);
  600. }
  601. else
  602. {
  603. return m_commsManager.InventoryService.DeleteItem(item);
  604. }
  605. }
  606. }
  607. else
  608. {
  609. AddRequest(
  610. new InventoryRequest(
  611. Delegate.CreateDelegate(typeof(DeleteItemDelegate), this, "DeleteItem"),
  612. new object[] { itemID }));
  613. return true;
  614. }
  615. return false;
  616. }
  617. /// <summary>
  618. /// Send details of the inventory items and/or folders in a given folder to the client.
  619. /// </summary>
  620. /// <param name="client"></param>
  621. /// <param name="folderID"></param>
  622. /// <param name="fetchFolders"></param>
  623. /// <param name="fetchItems"></param>
  624. /// <returns>true if the request was queued or successfully processed, false otherwise</returns>
  625. public bool SendInventoryDecendents(IClientAPI client, UUID folderID, bool fetchFolders, bool fetchItems)
  626. {
  627. if (m_hasReceivedInventory)
  628. {
  629. InventoryFolderImpl folder;
  630. if ((folder = RootFolder.FindFolder(folderID)) != null)
  631. {
  632. // m_log.DebugFormat(
  633. // "[AGENT INVENTORY]: Found folder {0} for client {1}",
  634. // folderID, remoteClient.AgentId);
  635. client.SendInventoryFolderDetails(
  636. client.AgentId, folderID, folder.RequestListOfItems(),
  637. folder.RequestListOfFolders(), fetchFolders, fetchItems);
  638. return true;
  639. }
  640. else
  641. {
  642. m_log.WarnFormat(
  643. "[AGENT INVENTORY]: Could not find folder {0} requested by user {1} {2}",
  644. folderID, client.Name, client.AgentId);
  645. return false;
  646. }
  647. }
  648. else
  649. {
  650. AddRequest(
  651. new InventoryRequest(
  652. Delegate.CreateDelegate(typeof(SendInventoryDescendentsDelegate), this, "SendInventoryDecendents", false, false),
  653. new object[] { client, folderID, fetchFolders, fetchItems }));
  654. return true;
  655. }
  656. }
  657. /// <summary>
  658. /// Find an appropriate folder for the given asset type
  659. /// </summary>
  660. /// <param name="type"></param>
  661. /// <returns>null if no appropriate folder exists</returns>
  662. public InventoryFolderImpl FindFolderForType(int type)
  663. {
  664. if (RootFolder == null)
  665. return null;
  666. return RootFolder.FindFolderForType(type);
  667. }
  668. }
  669. /// <summary>
  670. /// Should be implemented by callers which require a callback when the user's inventory is received
  671. /// </summary>
  672. public interface IInventoryRequest
  673. {
  674. /// <summary>
  675. /// This is the method executed once we have received the user's inventory by which the request can be fulfilled.
  676. /// </summary>
  677. void Execute();
  678. }
  679. /// <summary>
  680. /// Generic inventory request
  681. /// </summary>
  682. class InventoryRequest : IInventoryRequest
  683. {
  684. private Delegate m_delegate;
  685. private Object[] m_args;
  686. internal InventoryRequest(Delegate delegat, Object[] args)
  687. {
  688. m_delegate = delegat;
  689. m_args = args;
  690. }
  691. public void Execute()
  692. {
  693. if (m_delegate != null)
  694. m_delegate.DynamicInvoke(m_args);
  695. }
  696. }
  697. }