PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/newview/llinventorymodel.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 2300 lines | 1797 code | 179 blank | 324 comment | 351 complexity | 9c674e438052e4ca7170b7741ab502a2 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llinventorymodel.cpp
  3. * @brief Implementation of the inventory model used to track agent inventory.
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llinventorymodel.h"
  28. #include "llagent.h"
  29. #include "llagentwearables.h"
  30. #include "llappearancemgr.h"
  31. #include "llinventorypanel.h"
  32. #include "llinventorybridge.h"
  33. #include "llinventoryfunctions.h"
  34. #include "llinventoryobserver.h"
  35. #include "llinventorypanel.h"
  36. #include "llnotificationsutil.h"
  37. #include "llwindow.h"
  38. #include "llviewercontrol.h"
  39. #include "llpreview.h"
  40. #include "llviewermessage.h"
  41. #include "llviewerfoldertype.h"
  42. #include "llviewerwindow.h"
  43. #include "llappviewer.h"
  44. #include "llviewerregion.h"
  45. #include "llcallbacklist.h"
  46. #include "llvoavatarself.h"
  47. //#define DIFF_INVENTORY_FILES
  48. #ifdef DIFF_INVENTORY_FILES
  49. #include "process.h"
  50. #endif
  51. // Increment this if the inventory contents change in a non-backwards-compatible way.
  52. // For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect.
  53. const S32 LLInventoryModel::sCurrentInvCacheVersion = 2;
  54. BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE;
  55. ///----------------------------------------------------------------------------
  56. /// Local function declarations, constants, enums, and typedefs
  57. ///----------------------------------------------------------------------------
  58. //BOOL decompress_file(const char* src_filename, const char* dst_filename);
  59. const char CACHE_FORMAT_STRING[] = "%s.inv";
  60. struct InventoryIDPtrLess
  61. {
  62. bool operator()(const LLViewerInventoryCategory* i1, const LLViewerInventoryCategory* i2) const
  63. {
  64. return (i1->getUUID() < i2->getUUID());
  65. }
  66. };
  67. class LLCanCache : public LLInventoryCollectFunctor
  68. {
  69. public:
  70. LLCanCache(LLInventoryModel* model) : mModel(model) {}
  71. virtual ~LLCanCache() {}
  72. virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
  73. protected:
  74. LLInventoryModel* mModel;
  75. std::set<LLUUID> mCachedCatIDs;
  76. };
  77. bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
  78. {
  79. bool rv = false;
  80. if(item)
  81. {
  82. if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end())
  83. {
  84. rv = true;
  85. }
  86. }
  87. else if(cat)
  88. {
  89. // HACK: downcast
  90. LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat;
  91. if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
  92. {
  93. S32 descendents_server = c->getDescendentCount();
  94. LLInventoryModel::cat_array_t* cats;
  95. LLInventoryModel::item_array_t* items;
  96. mModel->getDirectDescendentsOf(
  97. c->getUUID(),
  98. cats,
  99. items);
  100. S32 descendents_actual = 0;
  101. if(cats && items)
  102. {
  103. descendents_actual = cats->count() + items->count();
  104. }
  105. if(descendents_server == descendents_actual)
  106. {
  107. mCachedCatIDs.insert(c->getUUID());
  108. rv = true;
  109. }
  110. }
  111. }
  112. return rv;
  113. }
  114. ///----------------------------------------------------------------------------
  115. /// Class LLInventoryModel
  116. ///----------------------------------------------------------------------------
  117. // global for the agent inventory.
  118. LLInventoryModel gInventory;
  119. // Default constructor
  120. LLInventoryModel::LLInventoryModel()
  121. : mModifyMask(LLInventoryObserver::ALL),
  122. mChangedItemIDs(),
  123. mCategoryMap(),
  124. mItemMap(),
  125. mCategoryLock(),
  126. mItemLock(),
  127. mLastItem(NULL),
  128. mParentChildCategoryTree(),
  129. mParentChildItemTree(),
  130. mObservers(),
  131. mRootFolderID(),
  132. mLibraryRootFolderID(),
  133. mLibraryOwnerID(),
  134. mIsNotifyObservers(FALSE),
  135. mIsAgentInvUsable(false)
  136. {
  137. }
  138. // Destroys the object
  139. LLInventoryModel::~LLInventoryModel()
  140. {
  141. cleanupInventory();
  142. }
  143. void LLInventoryModel::cleanupInventory()
  144. {
  145. empty();
  146. // Deleting one observer might erase others from the list, so always pop off the front
  147. while (!mObservers.empty())
  148. {
  149. observer_list_t::iterator iter = mObservers.begin();
  150. LLInventoryObserver* observer = *iter;
  151. mObservers.erase(iter);
  152. delete observer;
  153. }
  154. mObservers.clear();
  155. }
  156. // This is a convenience function to check if one object has a parent
  157. // chain up to the category specified by UUID.
  158. BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
  159. const LLUUID& cat_id) const
  160. {
  161. if (obj_id == cat_id) return TRUE;
  162. const LLInventoryObject* obj = getObject(obj_id);
  163. while(obj)
  164. {
  165. const LLUUID& parent_id = obj->getParentUUID();
  166. if( parent_id.isNull() )
  167. {
  168. return FALSE;
  169. }
  170. if(parent_id == cat_id)
  171. {
  172. return TRUE;
  173. }
  174. // Since we're scanning up the parents, we only need to check
  175. // in the category list.
  176. obj = getCategory(parent_id);
  177. }
  178. return FALSE;
  179. }
  180. const LLViewerInventoryCategory *LLInventoryModel::getFirstNondefaultParent(const LLUUID& obj_id) const
  181. {
  182. const LLInventoryObject* obj = getObject(obj_id);
  183. // Search up the parent chain until we get to root or an acceptable folder.
  184. // This assumes there are no cycles in the tree else we'll get a hang.
  185. LLUUID parent_id = obj->getParentUUID();
  186. while (!parent_id.isNull())
  187. {
  188. const LLViewerInventoryCategory *cat = getCategory(parent_id);
  189. if (!cat) break;
  190. const LLFolderType::EType folder_type = cat->getPreferredType();
  191. if (folder_type != LLFolderType::FT_NONE &&
  192. folder_type != LLFolderType::FT_ROOT_INVENTORY &&
  193. !LLFolderType::lookupIsEnsembleType(folder_type))
  194. {
  195. return cat;
  196. }
  197. parent_id = cat->getParentUUID();
  198. }
  199. return NULL;
  200. }
  201. //
  202. // Search up the parent chain until we get to the specified parent, then return the first child category under it
  203. //
  204. const LLViewerInventoryCategory* LLInventoryModel::getFirstDescendantOf(const LLUUID& master_parent_id, const LLUUID& obj_id) const
  205. {
  206. if (master_parent_id == obj_id)
  207. {
  208. return NULL;
  209. }
  210. const LLViewerInventoryCategory* current_cat = getCategory(obj_id);
  211. if (current_cat == NULL)
  212. {
  213. current_cat = getCategory(getObject(obj_id)->getParentUUID());
  214. }
  215. while (current_cat != NULL)
  216. {
  217. const LLUUID& current_parent_id = current_cat->getParentUUID();
  218. if (current_parent_id == master_parent_id)
  219. {
  220. return current_cat;
  221. }
  222. current_cat = getCategory(current_parent_id);
  223. }
  224. return NULL;
  225. }
  226. // Get the object by id. Returns NULL if not found.
  227. LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const
  228. {
  229. LLViewerInventoryCategory* cat = getCategory(id);
  230. if (cat)
  231. {
  232. return cat;
  233. }
  234. LLViewerInventoryItem* item = getItem(id);
  235. if (item)
  236. {
  237. return item;
  238. }
  239. return NULL;
  240. }
  241. // Get the item by id. Returns NULL if not found.
  242. LLViewerInventoryItem* LLInventoryModel::getItem(const LLUUID& id) const
  243. {
  244. LLViewerInventoryItem* item = NULL;
  245. if(mLastItem.notNull() && mLastItem->getUUID() == id)
  246. {
  247. item = mLastItem;
  248. }
  249. else
  250. {
  251. item_map_t::const_iterator iter = mItemMap.find(id);
  252. if (iter != mItemMap.end())
  253. {
  254. item = iter->second;
  255. mLastItem = item;
  256. }
  257. }
  258. return item;
  259. }
  260. // Get the category by id. Returns NULL if not found
  261. LLViewerInventoryCategory* LLInventoryModel::getCategory(const LLUUID& id) const
  262. {
  263. LLViewerInventoryCategory* category = NULL;
  264. cat_map_t::const_iterator iter = mCategoryMap.find(id);
  265. if (iter != mCategoryMap.end())
  266. {
  267. category = iter->second;
  268. }
  269. return category;
  270. }
  271. S32 LLInventoryModel::getItemCount() const
  272. {
  273. return mItemMap.size();
  274. }
  275. S32 LLInventoryModel::getCategoryCount() const
  276. {
  277. return mCategoryMap.size();
  278. }
  279. // Return the direct descendents of the id provided. The array
  280. // provided points straight into the guts of this object, and
  281. // should only be used for read operations, since modifications
  282. // may invalidate the internal state of the inventory. Set passed
  283. // in values to NULL if the call fails.
  284. void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
  285. cat_array_t*& categories,
  286. item_array_t*& items) const
  287. {
  288. categories = get_ptr_in_map(mParentChildCategoryTree, cat_id);
  289. items = get_ptr_in_map(mParentChildItemTree, cat_id);
  290. }
  291. LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const
  292. {
  293. LLInventoryModel::cat_array_t* cat_array;
  294. LLInventoryModel::item_array_t* item_array;
  295. getDirectDescendentsOf(cat_id,cat_array,item_array);
  296. LLMD5 item_name_hash;
  297. if (!item_array)
  298. {
  299. item_name_hash.finalize();
  300. return item_name_hash;
  301. }
  302. for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin();
  303. iter != item_array->end();
  304. iter++)
  305. {
  306. const LLViewerInventoryItem *item = (*iter);
  307. if (!item)
  308. continue;
  309. item_name_hash.update(item->getName());
  310. }
  311. item_name_hash.finalize();
  312. return item_name_hash;
  313. }
  314. // SJB: Added version to lock the arrays to catch potential logic bugs
  315. void LLInventoryModel::lockDirectDescendentArrays(const LLUUID& cat_id,
  316. cat_array_t*& categories,
  317. item_array_t*& items)
  318. {
  319. getDirectDescendentsOf(cat_id, categories, items);
  320. if (categories)
  321. {
  322. mCategoryLock[cat_id] = true;
  323. }
  324. if (items)
  325. {
  326. mItemLock[cat_id] = true;
  327. }
  328. }
  329. void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id)
  330. {
  331. mCategoryLock[cat_id] = false;
  332. mItemLock[cat_id] = false;
  333. }
  334. // findCategoryUUIDForType() returns the uuid of the category that
  335. // specifies 'type' as what it defaults to containing. The category is
  336. // not necessarily only for that type. *NOTE: This will create a new
  337. // inventory category on the fly if one does not exist.
  338. const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type,
  339. bool create_folder,
  340. bool find_in_library)
  341. {
  342. LLUUID rv = LLUUID::null;
  343. const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID();
  344. if(LLFolderType::FT_ROOT_INVENTORY == preferred_type)
  345. {
  346. rv = root_id;
  347. }
  348. else if (root_id.notNull())
  349. {
  350. cat_array_t* cats = NULL;
  351. cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
  352. if(cats)
  353. {
  354. S32 count = cats->count();
  355. for(S32 i = 0; i < count; ++i)
  356. {
  357. if(cats->get(i)->getPreferredType() == preferred_type)
  358. {
  359. rv = cats->get(i)->getUUID();
  360. break;
  361. }
  362. }
  363. }
  364. }
  365. if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library))
  366. {
  367. if(root_id.notNull())
  368. {
  369. return createNewCategory(root_id, preferred_type, LLStringUtil::null);
  370. }
  371. }
  372. return rv;
  373. }
  374. class LLCreateInventoryCategoryResponder : public LLHTTPClient::Responder
  375. {
  376. public:
  377. LLCreateInventoryCategoryResponder(LLInventoryModel* model,
  378. void (*callback)(const LLSD&, void*),
  379. void* user_data) :
  380. mModel(model),
  381. mCallback(callback),
  382. mData(user_data)
  383. {
  384. }
  385. virtual void error(U32 status, const std::string& reason)
  386. {
  387. LL_WARNS("InvAPI") << "CreateInventoryCategory failed. status = " << status << ", reasion = \"" << reason << "\"" << LL_ENDL;
  388. }
  389. virtual void result(const LLSD& content)
  390. {
  391. //Server has created folder.
  392. LLUUID category_id = content["folder_id"].asUUID();
  393. // Add the category to the internal representation
  394. LLPointer<LLViewerInventoryCategory> cat =
  395. new LLViewerInventoryCategory( category_id,
  396. content["parent_id"].asUUID(),
  397. (LLFolderType::EType)content["type"].asInteger(),
  398. content["name"].asString(),
  399. gAgent.getID() );
  400. cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
  401. cat->setDescendentCount(0);
  402. LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1);
  403. mModel->accountForUpdate(update);
  404. mModel->updateCategory(cat);
  405. if (mCallback && mData)
  406. {
  407. mCallback(content, mData);
  408. }
  409. }
  410. private:
  411. void (*mCallback)(const LLSD&, void*);
  412. void* mData;
  413. LLInventoryModel* mModel;
  414. };
  415. // Convenience function to create a new category. You could call
  416. // updateCategory() with a newly generated UUID category, but this
  417. // version will take care of details like what the name should be
  418. // based on preferred type. Returns the UUID of the new category.
  419. LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
  420. LLFolderType::EType preferred_type,
  421. const std::string& pname,
  422. void (*callback)(const LLSD&, void*), //Default to NULL
  423. void* user_data) //Default to NULL
  424. {
  425. LLUUID id;
  426. if(!isInventoryUsable())
  427. {
  428. llwarns << "Inventory is broken." << llendl;
  429. return id;
  430. }
  431. if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup())
  432. {
  433. lldebugs << "Attempt to create undefined category." << llendl;
  434. return id;
  435. }
  436. id.generate();
  437. std::string name = pname;
  438. if(!pname.empty())
  439. {
  440. name.assign(pname);
  441. }
  442. else
  443. {
  444. name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type));
  445. }
  446. if ( callback && user_data ) //callback required for acked message.
  447. {
  448. LLViewerRegion* viewer_region = gAgent.getRegion();
  449. std::string url;
  450. if ( viewer_region )
  451. url = viewer_region->getCapability("CreateInventoryCategory");
  452. if (!url.empty())
  453. {
  454. //Let's use the new capability.
  455. LLSD request, body;
  456. body["folder_id"] = id;
  457. body["parent_id"] = parent_id;
  458. body["type"] = (LLSD::Integer) preferred_type;
  459. body["name"] = name;
  460. request["message"] = "CreateInventoryCategory";
  461. request["payload"] = body;
  462. // viewer_region->getCapAPI().post(request);
  463. LLHTTPClient::post(
  464. url,
  465. body,
  466. new LLCreateInventoryCategoryResponder(this, callback, user_data) );
  467. return LLUUID::null;
  468. }
  469. }
  470. // Add the category to the internal representation
  471. LLPointer<LLViewerInventoryCategory> cat =
  472. new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
  473. cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
  474. cat->setDescendentCount(0);
  475. LLCategoryUpdate update(cat->getParentUUID(), 1);
  476. accountForUpdate(update);
  477. updateCategory(cat);
  478. // Create the category on the server. We do this to prevent people
  479. // from munging their protected folders.
  480. LLMessageSystem* msg = gMessageSystem;
  481. msg->newMessage("CreateInventoryFolder");
  482. msg->nextBlock("AgentData");
  483. msg->addUUID("AgentID", gAgent.getID());
  484. msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
  485. msg->nextBlock("FolderData");
  486. cat->packMessage(msg);
  487. gAgent.sendReliableMessage();
  488. // return the folder id of the newly created folder
  489. return id;
  490. }
  491. // Starting with the object specified, add it's descendents to the
  492. // array provided, but do not add the inventory object specified by
  493. // id. There is no guaranteed order. Neither array will be erased
  494. // before adding objects to it. Do not store a copy of the pointers
  495. // collected - use them, and collect them again later if you need to
  496. // reference the same objects.
  497. class LLAlwaysCollect : public LLInventoryCollectFunctor
  498. {
  499. public:
  500. virtual ~LLAlwaysCollect() {}
  501. virtual bool operator()(LLInventoryCategory* cat,
  502. LLInventoryItem* item)
  503. {
  504. return TRUE;
  505. }
  506. };
  507. void LLInventoryModel::collectDescendents(const LLUUID& id,
  508. cat_array_t& cats,
  509. item_array_t& items,
  510. BOOL include_trash)
  511. {
  512. LLAlwaysCollect always;
  513. collectDescendentsIf(id, cats, items, include_trash, always);
  514. }
  515. void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
  516. cat_array_t& cats,
  517. item_array_t& items,
  518. BOOL include_trash,
  519. LLInventoryCollectFunctor& add,
  520. BOOL follow_folder_links)
  521. {
  522. // Start with categories
  523. if(!include_trash)
  524. {
  525. const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH);
  526. if(trash_id.notNull() && (trash_id == id))
  527. return;
  528. }
  529. cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
  530. if(cat_array)
  531. {
  532. S32 count = cat_array->count();
  533. for(S32 i = 0; i < count; ++i)
  534. {
  535. LLViewerInventoryCategory* cat = cat_array->get(i);
  536. if(add(cat,NULL))
  537. {
  538. cats.put(cat);
  539. }
  540. collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add);
  541. }
  542. }
  543. LLViewerInventoryItem* item = NULL;
  544. item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
  545. // Follow folder links recursively. Currently never goes more
  546. // than one level deep (for current outfit support)
  547. // Note: if making it fully recursive, need more checking against infinite loops.
  548. if (follow_folder_links && item_array)
  549. {
  550. S32 count = item_array->count();
  551. for(S32 i = 0; i < count; ++i)
  552. {
  553. item = item_array->get(i);
  554. if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER)
  555. {
  556. LLViewerInventoryCategory *linked_cat = item->getLinkedCategory();
  557. if (linked_cat && linked_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
  558. // BAP - was
  559. // LLAssetType::lookupIsEnsembleCategoryType(linked_cat->getPreferredType()))
  560. // Change back once ensemble typing is in place.
  561. {
  562. if(add(linked_cat,NULL))
  563. {
  564. // BAP should this be added here? May not
  565. // matter if it's only being used in current
  566. // outfit traversal.
  567. cats.put(LLPointer<LLViewerInventoryCategory>(linked_cat));
  568. }
  569. collectDescendentsIf(linked_cat->getUUID(), cats, items, include_trash, add, FALSE);
  570. }
  571. }
  572. }
  573. }
  574. // Move onto items
  575. if(item_array)
  576. {
  577. S32 count = item_array->count();
  578. for(S32 i = 0; i < count; ++i)
  579. {
  580. item = item_array->get(i);
  581. if(add(NULL, item))
  582. {
  583. items.put(item);
  584. }
  585. }
  586. }
  587. }
  588. void LLInventoryModel::addChangedMaskForLinks(const LLUUID& object_id, U32 mask)
  589. {
  590. const LLInventoryObject *obj = getObject(object_id);
  591. if (!obj || obj->getIsLinkType())
  592. return;
  593. LLInventoryModel::cat_array_t cat_array;
  594. LLInventoryModel::item_array_t item_array;
  595. LLLinkedItemIDMatches is_linked_item_match(object_id);
  596. collectDescendentsIf(gInventory.getRootFolderID(),
  597. cat_array,
  598. item_array,
  599. LLInventoryModel::INCLUDE_TRASH,
  600. is_linked_item_match);
  601. if (cat_array.empty() && item_array.empty())
  602. {
  603. return;
  604. }
  605. for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin();
  606. cat_iter != cat_array.end();
  607. cat_iter++)
  608. {
  609. LLViewerInventoryCategory *linked_cat = (*cat_iter);
  610. addChangedMask(mask, linked_cat->getUUID());
  611. };
  612. for (LLInventoryModel::item_array_t::iterator iter = item_array.begin();
  613. iter != item_array.end();
  614. iter++)
  615. {
  616. LLViewerInventoryItem *linked_item = (*iter);
  617. addChangedMask(mask, linked_item->getUUID());
  618. };
  619. }
  620. const LLUUID& LLInventoryModel::getLinkedItemID(const LLUUID& object_id) const
  621. {
  622. const LLInventoryItem *item = gInventory.getItem(object_id);
  623. if (!item)
  624. {
  625. return object_id;
  626. }
  627. // Find the base item in case this a link (if it's not a link,
  628. // this will just be inv_item_id)
  629. return item->getLinkedUUID();
  630. }
  631. LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) const
  632. {
  633. return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL;
  634. }
  635. LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id,
  636. const LLUUID& start_folder_id)
  637. {
  638. item_array_t items;
  639. LLInventoryModel::cat_array_t cat_array;
  640. LLLinkedItemIDMatches is_linked_item_match(id);
  641. collectDescendentsIf((start_folder_id == LLUUID::null ? gInventory.getRootFolderID() : start_folder_id),
  642. cat_array,
  643. items,
  644. LLInventoryModel::INCLUDE_TRASH,
  645. is_linked_item_match);
  646. return items;
  647. }
  648. bool LLInventoryModel::isInventoryUsable() const
  649. {
  650. bool result = false;
  651. if(gInventory.getRootFolderID().notNull() && mIsAgentInvUsable)
  652. {
  653. result = true;
  654. }
  655. return result;
  656. }
  657. // Calling this method with an inventory item will either change an
  658. // existing item with a matching item_id, or will add the item to the
  659. // current inventory.
  660. U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
  661. {
  662. U32 mask = LLInventoryObserver::NONE;
  663. if(item->getUUID().isNull())
  664. {
  665. return mask;
  666. }
  667. if(!isInventoryUsable())
  668. {
  669. llwarns << "Inventory is broken." << llendl;
  670. return mask;
  671. }
  672. // We're hiding mesh types
  673. #if 0
  674. if (item->getType() == LLAssetType::AT_MESH)
  675. {
  676. return mask;
  677. }
  678. #endif
  679. LLViewerInventoryItem* old_item = getItem(item->getUUID());
  680. LLPointer<LLViewerInventoryItem> new_item;
  681. if(old_item)
  682. {
  683. // We already have an old item, modify its values
  684. new_item = old_item;
  685. LLUUID old_parent_id = old_item->getParentUUID();
  686. LLUUID new_parent_id = item->getParentUUID();
  687. if(old_parent_id != new_parent_id)
  688. {
  689. // need to update the parent-child tree
  690. item_array_t* item_array;
  691. item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id);
  692. if(item_array)
  693. {
  694. item_array->removeObj(old_item);
  695. }
  696. item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id);
  697. if(item_array)
  698. {
  699. item_array->put(old_item);
  700. }
  701. mask |= LLInventoryObserver::STRUCTURE;
  702. }
  703. if(old_item->getName() != item->getName())
  704. {
  705. mask |= LLInventoryObserver::LABEL;
  706. }
  707. old_item->copyViewerItem(item);
  708. mask |= LLInventoryObserver::INTERNAL;
  709. }
  710. else
  711. {
  712. // Simply add this item
  713. new_item = new LLViewerInventoryItem(item);
  714. addItem(new_item);
  715. if(item->getParentUUID().isNull())
  716. {
  717. const LLUUID category_id = findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(new_item->getType()));
  718. new_item->setParent(category_id);
  719. item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id);
  720. if( item_array )
  721. {
  722. // *FIX: bit of a hack to call update server from here...
  723. new_item->updateServer(TRUE);
  724. item_array->put(new_item);
  725. }
  726. else
  727. {
  728. llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl;
  729. }
  730. }
  731. else
  732. {
  733. // *NOTE: The general scheme is that if every byte of the
  734. // uuid is 0, except for the last one or two,the use the
  735. // last two bytes of the parent id, and match that up
  736. // against the type. For now, we're only worried about
  737. // lost & found.
  738. LLUUID parent_id = item->getParentUUID();
  739. if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID)
  740. {
  741. parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
  742. new_item->setParent(parent_id);
  743. }
  744. item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
  745. if(item_array)
  746. {
  747. item_array->put(new_item);
  748. }
  749. else
  750. {
  751. // Whoops! No such parent, make one.
  752. llinfos << "Lost item: " << new_item->getUUID() << " - "
  753. << new_item->getName() << llendl;
  754. parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
  755. new_item->setParent(parent_id);
  756. item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
  757. if(item_array)
  758. {
  759. // *FIX: bit of a hack to call update server from
  760. // here...
  761. new_item->updateServer(TRUE);
  762. item_array->put(new_item);
  763. }
  764. else
  765. {
  766. llwarns << "Lost and found Not there!!" << llendl;
  767. }
  768. }
  769. }
  770. mask |= LLInventoryObserver::ADD;
  771. }
  772. if(new_item->getType() == LLAssetType::AT_CALLINGCARD)
  773. {
  774. mask |= LLInventoryObserver::CALLING_CARD;
  775. // Handle user created calling cards.
  776. // Target ID is stored in the description field of the card.
  777. LLUUID id;
  778. std::string desc = new_item->getDescription();
  779. BOOL isId = desc.empty() ? FALSE : id.set(desc, FALSE);
  780. if (isId)
  781. {
  782. // Valid UUID; set the item UUID and rename it
  783. new_item->setCreator(id);
  784. std::string avatar_name;
  785. if (gCacheName->getFullName(id, avatar_name))
  786. {
  787. new_item->rename(avatar_name);
  788. mask |= LLInventoryObserver::LABEL;
  789. }
  790. else
  791. {
  792. // Fetch the current name
  793. gCacheName->get(id, FALSE,
  794. boost::bind(&LLViewerInventoryItem::onCallingCardNameLookup, new_item.get(),
  795. _1, _2, _3));
  796. }
  797. }
  798. }
  799. else if (new_item->getType() == LLAssetType::AT_GESTURE)
  800. {
  801. mask |= LLInventoryObserver::GESTURE;
  802. }
  803. addChangedMask(mask, new_item->getUUID());
  804. return mask;
  805. }
  806. LLInventoryModel::cat_array_t* LLInventoryModel::getUnlockedCatArray(const LLUUID& id)
  807. {
  808. cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
  809. if (cat_array)
  810. {
  811. llassert_always(mCategoryLock[id] == false);
  812. }
  813. return cat_array;
  814. }
  815. LLInventoryModel::item_array_t* LLInventoryModel::getUnlockedItemArray(const LLUUID& id)
  816. {
  817. item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
  818. if (item_array)
  819. {
  820. llassert_always(mItemLock[id] == false);
  821. }
  822. return item_array;
  823. }
  824. // Calling this method with an inventory category will either change
  825. // an existing item with the matching id, or it will add the category.
  826. void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
  827. {
  828. if(cat->getUUID().isNull())
  829. {
  830. return;
  831. }
  832. if(!isInventoryUsable())
  833. {
  834. llwarns << "Inventory is broken." << llendl;
  835. return;
  836. }
  837. LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
  838. if(old_cat)
  839. {
  840. // We already have an old category, modify it's values
  841. U32 mask = LLInventoryObserver::NONE;
  842. LLUUID old_parent_id = old_cat->getParentUUID();
  843. LLUUID new_parent_id = cat->getParentUUID();
  844. if(old_parent_id != new_parent_id)
  845. {
  846. // need to update the parent-child tree
  847. cat_array_t* cat_array;
  848. cat_array = getUnlockedCatArray(old_parent_id);
  849. if(cat_array)
  850. {
  851. cat_array->removeObj(old_cat);
  852. }
  853. cat_array = getUnlockedCatArray(new_parent_id);
  854. if(cat_array)
  855. {
  856. cat_array->put(old_cat);
  857. }
  858. mask |= LLInventoryObserver::STRUCTURE;
  859. }
  860. if(old_cat->getName() != cat->getName())
  861. {
  862. mask |= LLInventoryObserver::LABEL;
  863. }
  864. old_cat->copyViewerCategory(cat);
  865. addChangedMask(mask, cat->getUUID());
  866. }
  867. else
  868. {
  869. // add this category
  870. LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat->getParentUUID());
  871. new_cat->copyViewerCategory(cat);
  872. addCategory(new_cat);
  873. // make sure this category is correctly referenced by it's parent.
  874. cat_array_t* cat_array;
  875. cat_array = getUnlockedCatArray(cat->getParentUUID());
  876. if(cat_array)
  877. {
  878. cat_array->put(new_cat);
  879. }
  880. // make space in the tree for this category's children.
  881. llassert_always(mCategoryLock[new_cat->getUUID()] == false);
  882. llassert_always(mItemLock[new_cat->getUUID()] == false);
  883. cat_array_t* catsp = new cat_array_t;
  884. item_array_t* itemsp = new item_array_t;
  885. mParentChildCategoryTree[new_cat->getUUID()] = catsp;
  886. mParentChildItemTree[new_cat->getUUID()] = itemsp;
  887. addChangedMask(LLInventoryObserver::ADD, cat->getUUID());
  888. }
  889. }
  890. void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
  891. {
  892. lldebugs << "LLInventoryModel::moveObject()" << llendl;
  893. if(!isInventoryUsable())
  894. {
  895. llwarns << "Inventory is broken." << llendl;
  896. return;
  897. }
  898. if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))
  899. {
  900. llwarns << "Could not move inventory object " << object_id << " to "
  901. << cat_id << llendl;
  902. return;
  903. }
  904. LLViewerInventoryCategory* cat = getCategory(object_id);
  905. if(cat && (cat->getParentUUID() != cat_id))
  906. {
  907. cat_array_t* cat_array;
  908. cat_array = getUnlockedCatArray(cat->getParentUUID());
  909. if(cat_array) cat_array->removeObj(cat);
  910. cat_array = getUnlockedCatArray(cat_id);
  911. cat->setParent(cat_id);
  912. if(cat_array) cat_array->put(cat);
  913. addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
  914. return;
  915. }
  916. LLViewerInventoryItem* item = getItem(object_id);
  917. if(item && (item->getParentUUID() != cat_id))
  918. {
  919. item_array_t* item_array;
  920. item_array = getUnlockedItemArray(item->getParentUUID());
  921. if(item_array) item_array->removeObj(item);
  922. item_array = getUnlockedItemArray(cat_id);
  923. item->setParent(cat_id);
  924. if(item_array) item_array->put(item);
  925. addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
  926. return;
  927. }
  928. }
  929. // Delete a particular inventory object by ID.
  930. void LLInventoryModel::deleteObject(const LLUUID& id)
  931. {
  932. lldebugs << "LLInventoryModel::deleteObject()" << llendl;
  933. LLPointer<LLInventoryObject> obj = getObject(id);
  934. if (!obj)
  935. {
  936. llwarns << "Deleting non-existent object [ id: " << id << " ] " << llendl;
  937. return;
  938. }
  939. lldebugs << "Deleting inventory object " << id << llendl;
  940. mLastItem = NULL;
  941. LLUUID parent_id = obj->getParentUUID();
  942. mCategoryMap.erase(id);
  943. mItemMap.erase(id);
  944. //mInventory.erase(id);
  945. item_array_t* item_list = getUnlockedItemArray(parent_id);
  946. if(item_list)
  947. {
  948. LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
  949. item_list->removeObj(item);
  950. }
  951. cat_array_t* cat_list = getUnlockedCatArray(parent_id);
  952. if(cat_list)
  953. {
  954. LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
  955. cat_list->removeObj(cat);
  956. }
  957. item_list = getUnlockedItemArray(id);
  958. if(item_list)
  959. {
  960. delete item_list;
  961. mParentChildItemTree.erase(id);
  962. }
  963. cat_list = getUnlockedCatArray(id);
  964. if(cat_list)
  965. {
  966. delete cat_list;
  967. mParentChildCategoryTree.erase(id);
  968. }
  969. addChangedMask(LLInventoryObserver::REMOVE, id);
  970. obj = NULL; // delete obj
  971. updateLinkedObjectsFromPurge(id);
  972. gInventory.notifyObservers();
  973. }
  974. // Delete a particular inventory item by ID, and remove it from the server.
  975. void LLInventoryModel::purgeObject(const LLUUID &id)
  976. {
  977. lldebugs << "LLInventoryModel::purgeObject() [ id: " << id << " ] " << llendl;
  978. LLPointer<LLInventoryObject> obj = getObject(id);
  979. if(obj)
  980. {
  981. obj->removeFromServer();
  982. LLPreview::hide(id);
  983. deleteObject(id);
  984. }
  985. }
  986. void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id)
  987. {
  988. LLInventoryModel::item_array_t item_array = collectLinkedItems(baseobj_id);
  989. // REBUILD is expensive, so clear the current change list first else
  990. // everything else on the changelist will also get rebuilt.
  991. gInventory.notifyObservers();
  992. for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin();
  993. iter != item_array.end();
  994. iter++)
  995. {
  996. const LLViewerInventoryItem *linked_item = (*iter);
  997. const LLUUID &item_id = linked_item->getUUID();
  998. if (item_id == baseobj_id) continue;
  999. addChangedMask(LLInventoryObserver::REBUILD, item_id);
  1000. }
  1001. gInventory.notifyObservers();
  1002. }
  1003. // This is a method which collects the descendents of the id
  1004. // provided. If the category is not found, no action is
  1005. // taken. This method goes through the long winded process of
  1006. // cancelling any calling cards, removing server representation of
  1007. // folders, items, etc in a fairly efficient manner.
  1008. void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
  1009. {
  1010. EHasChildren children = categoryHasChildren(id);
  1011. if(children == CHILDREN_NO)
  1012. {
  1013. llinfos << "Not purging descendents of " << id << llendl;
  1014. return;
  1015. }
  1016. LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
  1017. if(cat.notNull())
  1018. {
  1019. // do the cache accounting
  1020. llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
  1021. << llendl;
  1022. S32 descendents = cat->getDescendentCount();
  1023. if(descendents > 0)
  1024. {
  1025. LLCategoryUpdate up(id, -descendents);
  1026. accountForUpdate(up);
  1027. }
  1028. // we know that descendent count is 0, aide since the
  1029. // accounting may actually not do an update, we should force
  1030. // it here.
  1031. cat->setDescendentCount(0);
  1032. // send it upstream
  1033. LLMessageSystem* msg = gMessageSystem;
  1034. msg->newMessage("PurgeInventoryDescendents");
  1035. msg->nextBlock("AgentData");
  1036. msg->addUUID("AgentID", gAgent.getID());
  1037. msg->addUUID("SessionID", gAgent.getSessionID());
  1038. msg->nextBlock("InventoryData");
  1039. msg->addUUID("FolderID", id);
  1040. gAgent.sendReliableMessage();
  1041. // unceremoniously remove anything we have locally stored.
  1042. cat_array_t categories;
  1043. item_array_t items;
  1044. collectDescendents(id,
  1045. categories,
  1046. items,
  1047. INCLUDE_TRASH);
  1048. S32 count = items.count();
  1049. S32 i;
  1050. for(i = 0; i < count; ++i)
  1051. {
  1052. deleteObject(items.get(i)->getUUID());
  1053. }
  1054. count = categories.count();
  1055. for(i = 0; i < count; ++i)
  1056. {
  1057. deleteObject(categories.get(i)->getUUID());
  1058. }
  1059. }
  1060. }
  1061. // Add/remove an observer. If the observer is destroyed, be sure to
  1062. // remove it.
  1063. void LLInventoryModel::addObserver(LLInventoryObserver* observer)
  1064. {
  1065. mObservers.insert(observer);
  1066. }
  1067. void LLInventoryModel::removeObserver(LLInventoryObserver* observer)
  1068. {
  1069. mObservers.erase(observer);
  1070. }
  1071. BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const
  1072. {
  1073. return mObservers.find(observer) != mObservers.end();
  1074. }
  1075. void LLInventoryModel::idleNotifyObservers()
  1076. {
  1077. if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0))
  1078. {
  1079. return;
  1080. }
  1081. notifyObservers();
  1082. }
  1083. // Call this method when it's time to update everyone on a new state.
  1084. void LLInventoryModel::notifyObservers()
  1085. {
  1086. if (mIsNotifyObservers)
  1087. {
  1088. // Within notifyObservers, something called notifyObservers
  1089. // again. This type of recursion is unsafe because it causes items to be
  1090. // processed twice, and this can easily lead to infinite loops.
  1091. llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl;
  1092. return;
  1093. }
  1094. mIsNotifyObservers = TRUE;
  1095. for (observer_list_t::iterator iter = mObservers.begin();
  1096. iter != mObservers.end(); )
  1097. {
  1098. LLInventoryObserver* observer = *iter;
  1099. observer->changed(mModifyMask);
  1100. // safe way to increment since changed may delete entries! (@!##%@!@&*!)
  1101. iter = mObservers.upper_bound(observer);
  1102. }
  1103. mModifyMask = LLInventoryObserver::NONE;
  1104. mChangedItemIDs.clear();
  1105. mIsNotifyObservers = FALSE;
  1106. }
  1107. // store flag for change
  1108. // and id of object change applies to
  1109. void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)
  1110. {
  1111. if (mIsNotifyObservers)
  1112. {
  1113. // Something marked an item for change within a call to notifyObservers
  1114. // (which is in the process of processing the list of items marked for change).
  1115. // This means the change may fail to be processed.
  1116. llwarns << "Adding changed mask within notify observers! Change will likely be lost." << llendl;
  1117. }
  1118. mModifyMask |= mask;
  1119. if (referent.notNull())
  1120. {
  1121. mChangedItemIDs.insert(referent);
  1122. }
  1123. // Update all linked items. Starting with just LABEL because I'm
  1124. // not sure what else might need to be accounted for this.
  1125. if (mModifyMask & LLInventoryObserver::LABEL)
  1126. {
  1127. addChangedMaskForLinks(referent, LLInventoryObserver::LABEL);
  1128. }
  1129. }
  1130. // If we get back a normal response, handle it here
  1131. void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content)
  1132. {
  1133. start_new_inventory_observer();
  1134. /*LLUUID agent_id;
  1135. agent_id = content["agent_id"].asUUID();
  1136. if(agent_id != gAgent.getID())
  1137. {
  1138. llwarns << "Got a inventory update for the wrong agent: " << agent_id
  1139. << llendl;
  1140. return;
  1141. }*/
  1142. item_array_t items;
  1143. update_map_t update;
  1144. S32 count = content["items"].size();
  1145. bool all_one_folder = true;
  1146. LLUUID folder_id;
  1147. // Does this loop ever execute more than once?
  1148. for(S32 i = 0; i < count; ++i)
  1149. {
  1150. LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
  1151. titem->unpackMessage(content["items"][i]);
  1152. lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
  1153. << titem->getUUID() << llendl;
  1154. items.push_back(titem);
  1155. // examine update for changes.
  1156. LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
  1157. if(itemp)
  1158. {
  1159. if(titem->getParentUUID() == itemp->getParentUUID())
  1160. {
  1161. update[titem->getParentUUID()];
  1162. }
  1163. else
  1164. {
  1165. ++update[titem->getParentUUID()];
  1166. --update[itemp->getParentUUID()];
  1167. }
  1168. }
  1169. else
  1170. {
  1171. ++update[titem->getParentUUID()];
  1172. }
  1173. if (folder_id.isNull())
  1174. {
  1175. folder_id = titem->getParentUUID();
  1176. }
  1177. else
  1178. {
  1179. all_one_folder = false;
  1180. }
  1181. }
  1182. U32 changes = 0x0;
  1183. //as above, this loop never seems to loop more than once per call
  1184. for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
  1185. {
  1186. changes |= gInventory.updateItem(*it);
  1187. }
  1188. gInventory.notifyObservers();
  1189. gViewerWindow->getWindow()->decBusyCount();
  1190. }
  1191. //If we get back an error (not found, etc...), handle it here
  1192. void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason)
  1193. {
  1194. llinfos << "fetchInventory::error "
  1195. << status << ": " << reason << llendl;
  1196. gInventory.notifyObservers();
  1197. }
  1198. bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const
  1199. {
  1200. if(folder_id.isNull())
  1201. {
  1202. llwarns << "Calling fetch descendents on NULL folder id!" << llendl;
  1203. return false;
  1204. }
  1205. LLViewerInventoryCategory* cat = getCategory(folder_id);
  1206. if(!cat)
  1207. {
  1208. llwarns << "Asked to fetch descendents of non-existent folder: "
  1209. << folder_id << llendl;
  1210. return false;
  1211. }
  1212. //S32 known_descendents = 0;
  1213. ///cat_array_t* categories = get_ptr_in_map(mParentChildCategoryTree, folder_id);
  1214. //item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id);
  1215. //if(categories)
  1216. //{
  1217. // known_descendents += categories->count();
  1218. //}
  1219. //if(items)
  1220. //{
  1221. // known_descendents += items->count();
  1222. //}
  1223. return cat->fetch();
  1224. }
  1225. void LLInventoryModel::cache(
  1226. const LLUUID& parent_folder_id,
  1227. const LLUUID& agent_id)
  1228. {
  1229. lldebugs << "Caching " << parent_folder_id << " for " << agent_id
  1230. << llendl;
  1231. LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);
  1232. if(!root_cat) return;
  1233. cat_array_t categories;
  1234. categories.put(root_cat);
  1235. item_array_t items;
  1236. LLCanCache can_cache(this);
  1237. can_cache(root_cat, NULL);
  1238. collectDescendentsIf(
  1239. parent_folder_id,
  1240. categories,
  1241. items,
  1242. INCLUDE_TRASH,
  1243. can_cache);
  1244. std::string agent_id_str;
  1245. std::string inventory_filename;
  1246. agent_id.toString(agent_id_str);
  1247. std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_str));
  1248. inventory_filename = llformat(CACHE_FORMAT_STRING, path.c_str());
  1249. saveToFile(inventory_filename, categories, items);
  1250. std::string gzip_filename(inventory_filename);
  1251. gzip_filename.append(".gz");
  1252. if(gzip_file(inventory_filename, gzip_filename))
  1253. {
  1254. lldebugs << "Successfully compressed " << inventory_filename << llendl;
  1255. LLFile::remove(inventory_filename);
  1256. }
  1257. else
  1258. {
  1259. llwarns << "Unable to compress " << inventory_filename << llendl;
  1260. }
  1261. }
  1262. void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
  1263. {
  1264. //llinfos << "LLInventoryModel::addCategory()" << llendl;
  1265. if(category)
  1266. {
  1267. // We aren't displaying the Meshes folder
  1268. if (category->mPreferredType == LLFolderType::FT_MESH)
  1269. {
  1270. return;
  1271. }
  1272. // try to localize default names first. See EXT-8319, EXT-7051.
  1273. category->localizeName();
  1274. // Insert category uniquely into the map
  1275. mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
  1276. //mInventory[category->getUUID()] = category;
  1277. }
  1278. }
  1279. void LLInventoryModel::addItem(LLViewerInventoryItem* item)
  1280. {
  1281. llassert(item);
  1282. if(item)
  1283. {
  1284. // This can happen if assettype enums from llassettype.h ever change.
  1285. // For example, there is a known backwards compatibility issue in some viewer prototypes prior to when
  1286. // the AT_LINK enum changed from 23 to 24.
  1287. if ((item->getType() == LLAssetType::AT_NONE)
  1288. || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup())
  1289. {
  1290. llwarns << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << llendl;
  1291. return;
  1292. }
  1293. // This condition means that we tried to add a link without the baseobj being in memory.
  1294. // The item will show up as a broken link.
  1295. if (item->getIsBrokenLink())
  1296. {
  1297. llinfos << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << llendl;
  1298. }
  1299. mItemMap[item->getUUID()] = item;
  1300. }
  1301. }
  1302. // Empty the entire contents
  1303. void LLInventoryModel::empty()
  1304. {
  1305. // llinfos << "LLInventoryModel::empty()" << llendl;
  1306. std::for_each(
  1307. mParentChildCategoryTree.begin(),
  1308. mParentChildCategoryTree.end(),
  1309. DeletePairedPointer());
  1310. mParentChildCategoryTree.clear();
  1311. std::for_each(
  1312. mParentChildItemTree.begin(),
  1313. mParentChildItemTree.end(),
  1314. DeletePairedPointer());
  1315. mParentChildItemTree.clear();
  1316. mCategoryMap.clear(); // remove all references (should delete entries)
  1317. mItemMap.clear(); // remove all references (should delete entries)
  1318. mLastItem = NULL;
  1319. //mInventory.clear();
  1320. }
  1321. void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const
  1322. {
  1323. LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
  1324. if(cat)
  1325. {
  1326. bool accounted = false;
  1327. S32 version = cat->getVersion();
  1328. if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
  1329. {
  1330. S32 descendents_server = cat->getDescendentCount();
  1331. LLInventoryModel::cat_array_t* cats;
  1332. LLInventoryModel::item_array_t* items;
  1333. getDirectDescendentsOf(update.mCategoryID, cats, items);
  1334. S32 descendents_actual = 0;
  1335. if(cats && items)
  1336. {
  1337. descendents_actual = cats->count() + items->count();
  1338. }
  1339. if(descendents_server == descendents_actual)
  1340. {
  1341. accounted = true;
  1342. descendents_actual += update.mDescendentDelta;
  1343. cat->setDescendentCount(descendents_actual);
  1344. cat->setVersion(++version);
  1345. lldebugs << "accounted: '" << cat->getName() << "' "
  1346. << version << " with " << descendents_actual
  1347. << " descendents." << llendl;
  1348. }
  1349. }
  1350. if(!accounted)
  1351. {
  1352. // Error condition, this means that the category did not register that
  1353. // it got new descendents (perhaps because it is still being loaded)
  1354. // which means its descendent count will be wrong.
  1355. llwarns << "Accounting failed for '" << cat->getName() << "' version:"
  1356. << version << llendl;
  1357. }
  1358. }
  1359. else
  1360. {
  1361. llwarns << "No category found for update " << update.mCategoryID << llendl;
  1362. }
  1363. }
  1364. void LLInventoryModel::accountForUpdate(
  1365. const LLInventoryModel::update_list_t& update)
  1366. {
  1367. update_list_t::const_iterator it = update.begin();
  1368. update_list_t::const_iterator end = update.end();
  1369. for(; it != end; ++it)
  1370. {
  1371. accountForUpdate(*it);
  1372. }
  1373. }
  1374. void LLInventoryModel::accountForUpdate(
  1375. const LLInventoryModel::update_map_t& update)
  1376. {
  1377. LLCategoryUpdate up;
  1378. update_map_t::const_iterator it = update.begin();
  1379. update_map_t::const_iterator end = update.end();
  1380. for(; it != end; ++it)
  1381. {
  1382. up.mCategoryID = (*it).first;
  1383. up.mDescendentDelta = (*it).second.mValue;
  1384. accountForUpdate(up);
  1385. }
  1386. }
  1387. /*
  1388. void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id)
  1389. {
  1390. LLViewerInventoryCategory* cat = getCategory(category_id);
  1391. if(cat)
  1392. {
  1393. S32 version = cat->getVersion();
  1394. if(LLViewerInventoryCategory::VERSION_UNKNOWN != version)
  1395. {
  1396. cat->setVersion(version + 1);
  1397. llinfos << "IncrementVersion: " << cat->getName() << " "
  1398. << cat->getVersion() << llendl;
  1399. }
  1400. else
  1401. {
  1402. llinfos << "Attempt to increment version when unknown: "
  1403. << category_id << llendl;
  1404. }
  1405. }
  1406. else
  1407. {
  1408. llinfos << "Attempt to increment category: " << category_id << llendl;
  1409. }
  1410. }
  1411. void LLInventoryModel::incrementCategorySetVersion(
  1412. const std::set<LLUUID>& categories)
  1413. {
  1414. if(!categories.empty())
  1415. {
  1416. std::set<LLUUID>::const_iterator it = categories.begin();
  1417. std::set<LLUUID>::const_iterator end = categories.end();
  1418. for(; it != end; ++it)
  1419. {
  1420. incrementCategoryVersion(*it);
  1421. }
  1422. }
  1423. }
  1424. */
  1425. LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
  1426. const LLUUID& cat_id) const
  1427. {
  1428. LLViewerInventoryCategory* cat = getCategory(cat_id);
  1429. if(!cat) return CHILDREN_NO;
  1430. if(cat->getDescendentCount() > 0)
  1431. {
  1432. return CHILDREN_YES;
  1433. }
  1434. if(cat->getDescendentCount() == 0)
  1435. {
  1436. return CHILDREN_NO;
  1437. }
  1438. if((cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
  1439. || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
  1440. {
  1441. return CHILDREN_MAYBE;
  1442. }
  1443. // Shouldn't have to run this, but who knows.
  1444. parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID());
  1445. if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0)
  1446. {
  1447. return CHILDREN_YES;
  1448. }
  1449. parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID());
  1450. if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0)
  1451. {
  1452. return CHILDREN_YES;
  1453. }
  1454. return CHILDREN_NO;
  1455. }
  1456. bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
  1457. {
  1458. LLViewerInventoryCategory* cat = getCategory(cat_id);
  1459. if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
  1460. {
  1461. S32 descendents_server = cat->getDescendentCount();
  1462. LLInventoryModel::cat_array_t* cats;
  1463. LLInventoryModel::item_array_t* items;
  1464. getDirectDescendentsOf(cat_id, cats, items);
  1465. S32 descendents_actual = 0;
  1466. if(cats && items)
  1467. {
  1468. descendents_actual = cats->count() + items->count();
  1469. }
  1470. if(descendents_server == descendents_actual)
  1471. {
  1472. return true;
  1473. }
  1474. }
  1475. return false;
  1476. }
  1477. bool LLInventoryModel::loadSkeleton(
  1478. const LLSD& options,
  1479. const LLUUID& owner_id)
  1480. {
  1481. lldebugs << "importing inventory skeleton for " << owner_id << llendl;
  1482. typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
  1483. cat_set_t temp_cats;
  1484. bool rv = true;
  1485. for(LLSD::array_const_iterator it = options.beginArray(),
  1486. end = options.endArray(); it != end; ++it)
  1487. {
  1488. LLSD name = (*it)["name"];
  1489. LLSD folder_id = (*it)["folder_id"];
  1490. LLSD parent_id = (*it)["parent_id"];
  1491. LLSD version = (*it)["version"];
  1492. if(name.isDefined()
  1493. && folder_id.isDefined()
  1494. && parent_id.isDefined()
  1495. && version.isDefined()
  1496. && folder_id.asUUID().notNull() // if an id is null, it locks the viewer.
  1497. )
  1498. {
  1499. LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id);
  1500. cat->rename(name.asString());
  1501. cat->setUUID(folder_id.asUUID());
  1502. cat->setParent(parent_id.asUUID());
  1503. LLFolderType::EType preferred_type = LLFolderType::FT_NONE;
  1504. LLSD type_default = (*it)["type_default"];
  1505. if(type_default.isDefined())
  1506. {
  1507. preferred_type = (LLFolderType::EType)type_default.asInteger();
  1508. }
  1509. cat->setPreferredType(preferred_type);
  1510. cat->setVersion(version.asInteger());
  1511. temp_cats.insert(cat);
  1512. }
  1513. else
  1514. {
  1515. llwarns << "Unable to import near " << name.asString() << llendl;
  1516. rv = false;
  1517. }
  1518. }
  1519. S32 cached_category_count = 0;
  1520. S32 cached_item_count = 0;
  1521. if(!temp_cats.empty())
  1522. {
  1523. update_map_t child_counts;
  1524. cat_array_t categories;
  1525. item_array_t items;
  1526. cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded.
  1527. std::string owner_id_str;
  1528. owner_id.toString(owner_id_str);
  1529. std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str));
  1530. std::string inventory_filename;
  1531. inventory_filename = llformat(CACHE_FORMAT_STRING, path.c_str());
  1532. const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN;
  1533. std::string gzip_filename(inventory_filename);
  1534. gzip_filename.append(".gz");
  1535. LLFILE* fp = LLFile::fopen(gzip_filename, "rb");
  1536. bool remove_inventory_file = false;
  1537. if(fp)
  1538. {
  1539. fclose(fp);
  1540. fp = NULL;
  1541. if(gunzip_file(gzip_filename, inventory_filename))
  1542. {
  1543. // we only want to remove the inventory file if it was
  1544. // gzipped before we loaded, and we successfully
  1545. // gunziped it.
  1546. remove_inventory_file = true;
  1547. }
  1548. else
  1549. {
  1550. llinfos << "Unable to gunzip " << gzip_filename << llendl;
  1551. }
  1552. }
  1553. bool is_cache_obsolete = false;
  1554. if(loadFromFile(inventory_filename, categories, items, is_cache_obsolete))
  1555. {
  1556. // We were able to find a cache of files. So, use what we
  1557. // found to generate a set of categories we should add. We
  1558. // will go through each category loaded and if the version
  1559. // does not match, invalidate the version.
  1560. S32 count = categories.count();
  1561. cat_set_t::iterator not_cached = temp_cats.end();
  1562. std::set<LLUUID> cached_ids;
  1563. for(S32 i = 0; i < count; ++i)
  1564. {
  1565. LLViewerInventoryCategory* cat = categories[i];
  1566. cat_set_t::iterator cit = temp_cats.find(cat);
  1567. if (cit == temp_cats.end())
  1568. {
  1569. continue; // cache corruption?? not sure why this happens -SJB
  1570. }
  1571. LLViewerInventoryCategory* tcat = *cit;
  1572. // we can safely ignore anything loaded from file, but
  1573. // not sent down in the skeleton.
  1574. if(cit == not_cached)
  1575. {
  1576. continue;
  1577. }
  1578. if(cat->getVersion() != tcat->getVersion())
  1579. {
  1580. // if the cached version does not match the server version,
  1581. // throw away the version we have so we can fetch the
  1582. // correct contents the next time the viewer opens the folder.
  1583. tcat->setVersion(NO_VERSION);
  1584. }
  1585. else
  1586. {
  1587. cached_ids.insert(tcat->getUUID());
  1588. }
  1589. }
  1590. // go ahead and add the cats returned during the download
  1591. std::set<LLUUID>::const_iterator not_cached_id = cached_ids.end();
  1592. cached_category_count = cached_ids.size();
  1593. for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
  1594. {
  1595. if(cached_ids.find((*it)->getUUID()) == not_cached_id)
  1596. {
  1597. // this check is performed so that we do not
  1598. // mark new folders in the skeleton (and not in cache)
  1599. // as being cached.
  1600. LLViewerInventoryCategory *llvic = (*it);
  1601. llvic->setVersion(NO_VERSION);
  1602. }
  1603. addCategory(*it);
  1604. ++child_counts[(*it)->getParentUUID()];
  1605. }
  1606. // Add all the items loaded which are parented to a
  1607. // category with a correctly cached parent
  1608. S32 bad_link_count = 0;
  1609. cat_map_t::iterator unparented = mCategoryMap.end();
  1610. for(item_array_t::const_iterator item_iter = items.begin();
  1611. item_iter != items.end();
  1612. ++item_iter)
  1613. {
  1614. LLViewerInventoryItem *item = (*item_iter).get();
  1615. const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID());
  1616. if(cit != unparented)
  1617. {
  1618. const LLViewerInventoryCategory* cat = cit->second.get();
  1619. if(cat->getVersion() != NO_VERSION)
  1620. {
  1621. // This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache.
  1622. if (item->getIsBrokenLink())
  1623. {
  1624. bad_link_count++;
  1625. lldebugs << "Attempted to add cached link item without baseobj present ( name: "
  1626. << item->getName() << " itemID: " << item->getUUID()
  1627. << " assetID: " << item->getAssetUUID()
  1628. << " ). Ignoring and invalidating " << cat->getName() << " . " << llendl;
  1629. invalid_categories.insert(cit->second);
  1630. continue;
  1631. }
  1632. addItem(item);
  1633. cached_item_count += 1;
  1634. ++child_counts[cat->getUUID()];
  1635. }
  1636. }
  1637. }
  1638. if (bad_link_count > 0)
  1639. {
  1640. llinfos << "Attempted to add " << bad_link_count
  1641. << " cached link items without baseobj present. "
  1642. << "The corresponding categories were invalidated." << llendl;
  1643. }
  1644. }
  1645. else
  1646. {
  1647. // go ahead and add everything after stripping the version
  1648. // information.
  1649. for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
  1650. {
  1651. LLViewerInventoryCategory *llvic = (*it);
  1652. llvic->setVersion(NO_VERSION);
  1653. addCategory(*it);
  1654. }
  1655. }
  1656. // Invalidate all categories that failed fetching descendents for whatever
  1657. // reason (e.g. one of the descendents was a broken link).
  1658. for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin();
  1659. invalid_cat_it != invalid_categories.end();
  1660. invalid_cat_it++)
  1661. {
  1662. LLViewerInventoryCategory* cat = (*invalid_cat_it).get();
  1663. cat->setVersion(NO_VERSION);
  1664. llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl;
  1665. }
  1666. // At this point, we need to set the known descendents for each
  1667. // category which successfully cached so that we do not
  1668. // needlessly fetch descendents for categories which we have.
  1669. update_map_t::const_iterator no_child_counts = child_counts.end();
  1670. for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
  1671. {
  1672. LLViewerInventoryCategory* cat = (*it).get();
  1673. if(cat->getVersion() != NO_VERSION)
  1674. {
  1675. update_map_t::const_iterator the_count = child_counts.find(cat->getUUID());
  1676. if(the_count != no_child_counts)
  1677. {
  1678. const S32 num_descendents = (*the_count).second.mValue;
  1679. cat->setDescendentCount(num_descendents);
  1680. }
  1681. else
  1682. {
  1683. cat->setDescendentCount(0);
  1684. }
  1685. }
  1686. }
  1687. if(remove_inventory_file)
  1688. {
  1689. // clean up the gunzipped file.
  1690. LLFile::remove(inventory_filename);
  1691. }
  1692. if(is_cache_obsolete)
  1693. {
  1694. // If out of date, remove the gzipped file too.
  1695. llwarns << "Inv cache out of date, removing" << llendl;
  1696. LLFile::remove(gzip_filename);
  1697. }
  1698. categories.clear(); // will unref and delete entries
  1699. }
  1700. llinfos << "Successfully loaded " << cached_category_count
  1701. << " categories and " << cached_item_count << " items from cache."
  1702. << llendl;
  1703. return rv;
  1704. }
  1705. // This is a brute force method to rebuild the entire parent-child
  1706. // relations. The overall operation has O(NlogN) performance, which
  1707. // should be sufficient for our needs.
  1708. void LLInventoryModel::buildParentChildMap()
  1709. {
  1710. llinfos << "LLInventoryModel::buildParentChildMap()" << llendl;
  1711. // *NOTE: I am skipping the logic around folder version
  1712. // synchronization here because it seems if a folder is lost, we
  1713. // might actually want to invalidate it at that point - not
  1714. // attempt to cache. More time & thought is necessary.
  1715. // First the categories. We'll copy all of the categories into a
  1716. // temporary container to iterate over (oh for real iterators.)
  1717. // While we're at it, we'll allocate the arrays in the trees.
  1718. cat_array_t cats;
  1719. cat_array_t* catsp;
  1720. item_array_t* itemsp;
  1721. for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
  1722. {
  1723. LLViewerInventoryCategory* cat = cit->second;
  1724. cats.put(cat);
  1725. if (mParentChildCategoryTree.count(cat->getUUID()) == 0)
  1726. {
  1727. llassert_always(mCategoryLock[cat->getUUID()] == false);
  1728. catsp = new cat_array_t;
  1729. mParentChildCategoryTree[cat->getUUID()] = catsp;
  1730. }
  1731. if (mParentChildItemTree.count(cat->getUUID()) == 0)
  1732. {
  1733. llassert_always(mItemLock[cat->getUUID()] == false);
  1734. itemsp = new item_array_t;
  1735. mParentChildItemTree[cat->getUUID()] = itemsp;
  1736. }
  1737. }
  1738. // Insert a special parent for the root - so that lookups on
  1739. // LLUUID::null as the parent work correctly. This is kind of a
  1740. // blatent wastes of space since we allocate a block of memory for
  1741. // the array, but whatever - it's not that much space.
  1742. if (mParentChildCategoryTree.count(LLUUID::null) == 0)
  1743. {
  1744. catsp = new cat_array_t;
  1745. mParentChildCategoryTree[LLUUID::null] = catsp;
  1746. }
  1747. // Now we have a structure with all of the categories that we can
  1748. // iterate over and insert into the correct place in the child
  1749. // category tree.
  1750. S32 count = cats.count();
  1751. S32 i;
  1752. S32 lost = 0;
  1753. for(i = 0; i < count; ++i)
  1754. {
  1755. LLViewerInventoryCategory* cat = cats.get(i);
  1756. catsp = getUnlockedCatArray(cat->getParentUUID());
  1757. if(catsp)
  1758. {
  1759. catsp->put(cat);
  1760. }
  1761. else
  1762. {
  1763. // *NOTE: This process could be a lot more efficient if we
  1764. // used the new MoveInventoryFolder message, but we would
  1765. // have to continue to do the update & build here. So, to
  1766. // implement it, we would need a set or map of uuid pairs
  1767. // which would be (folder_id, new_parent_id) to be sent up
  1768. // to the server.
  1769. llinfos << "Lost categroy: " << cat->getUUID() << " - "
  1770. << cat->getName() << llendl;
  1771. ++lost;
  1772. // plop it into the lost & found.
  1773. LLFolderType::EType pref = cat->getPreferredType();
  1774. if(LLFolderType::FT_NONE == pref)
  1775. {
  1776. cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
  1777. }
  1778. else if(LLFolderType::FT_ROOT_INVENTORY == pref)
  1779. {
  1780. // it's the root
  1781. cat->setParent(LLUUID::null);
  1782. }
  1783. else
  1784. {
  1785. // it's a protected folder.
  1786. cat->setParent(gInventory.getRootFolderID());
  1787. }
  1788. cat->updateServer(TRUE);
  1789. catsp = getUnlockedCatArray(cat->getParentUUID());
  1790. if(catsp)
  1791. {
  1792. catsp->put(cat);
  1793. }
  1794. else
  1795. {
  1796. llwarns << "Lost and found Not there!!" << llendl;
  1797. }
  1798. }
  1799. }
  1800. if(lost)
  1801. {
  1802. llwarns << "Found " << lost << " lost categories." << llendl;
  1803. }
  1804. const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null);
  1805. sFirstTimeInViewer2 = !COF_exists || gAgent.isFirstLogin();
  1806. // Now the items. We allocated in the last step, so now all we
  1807. // have to do is iterate over the items and put them in the right
  1808. // place.
  1809. item_array_t items;
  1810. if(!mItemMap.empty())
  1811. {
  1812. LLPointer<LLViewerInventoryItem> item;
  1813. for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
  1814. {
  1815. item = (*iit).second;
  1816. items.put(item);
  1817. }
  1818. }
  1819. count = items.count();
  1820. lost = 0;
  1821. uuid_vec_t lost_item_ids;
  1822. for(i = 0; i < count; ++i)
  1823. {
  1824. LLPointer<LLViewerInventoryItem> item;
  1825. item = items.get(i);
  1826. itemsp = getUnlockedItemArray(item->getParentUUID());
  1827. if(itemsp)
  1828. {
  1829. itemsp->put(item);
  1830. }
  1831. else
  1832. {
  1833. llinfos << "Lost item: " << item->getUUID() << " - "
  1834. << item->getName() << llendl;
  1835. ++lost;
  1836. // plop it into the lost & found.
  1837. //
  1838. item->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND));
  1839. // move it later using a special message to move items. If
  1840. // we update server here, the client might crash.
  1841. //item->updateServer();
  1842. lost_item_ids.push_back(item->getUUID());
  1843. itemsp = getUnlockedItemArray(item->getParentUUID());
  1844. if(itemsp)
  1845. {
  1846. itemsp->put(item);
  1847. }
  1848. else
  1849. {
  1850. llwarns << "Lost and found Not there!!" << llendl;
  1851. }
  1852. }
  1853. }
  1854. if(lost)
  1855. {
  1856. llwarns << "Found " << lost << " lost items." << llendl;
  1857. LLMessageSystem* msg = gMessageSystem;
  1858. BOOL start_new_message = TRUE;
  1859. const LLUUID lnf = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND);
  1860. for(uuid_vec_t::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it)
  1861. {
  1862. if(start_new_message)
  1863. {
  1864. start_new_message = FALSE;
  1865. msg->newMessageFast(_PREHASH_MoveInventoryItem);
  1866. msg->nextBlockFast(_PREHASH_AgentData);
  1867. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  1868. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  1869. msg->addBOOLFast(_PREHASH_Stamp, FALSE);
  1870. }
  1871. msg->nextBlockFast(_PREHASH_InventoryData);
  1872. msg->addUUIDFast(_PREHASH_ItemID, (*it));
  1873. msg->addUUIDFast(_PREHASH_FolderID, lnf);
  1874. msg->addString("NewName", NULL);
  1875. if(msg->isSendFull(NULL))
  1876. {
  1877. start_new_message = TRUE;
  1878. gAgent.sendReliableMessage();
  1879. }
  1880. }
  1881. if(!start_new_message)
  1882. {
  1883. gAgent.sendReliableMessage();
  1884. }
  1885. }
  1886. const LLUUID &agent_inv_root_id = gInventory.getRootFolderID();
  1887. if (agent_inv_root_id.notNull())
  1888. {
  1889. cat_array_t* catsp = get_ptr_in_map(mParentChildCategoryTree, agent_inv_root_id);
  1890. if(catsp)
  1891. {
  1892. // *HACK - fix root inventory folder
  1893. // some accounts has pbroken inventory root folders
  1894. std::string name = "My Inventory";
  1895. LLUUID prev_root_id = mRootFolderID;
  1896. for (parent_cat_map_t::const_iterator it = mParentChildCategoryTree.begin(),
  1897. it_end = mParentChildCategoryTree.end(); it != it_end; ++it)
  1898. {
  1899. cat_array_t* cat_array = it->second;
  1900. for (cat_array_t::const_iterator cat_it = cat_array->begin(),
  1901. cat_it_end = cat_array->end(); cat_it != cat_it_end; ++cat_it)
  1902. {
  1903. LLPointer<LLViewerInventoryCategory> category = *cat_it;
  1904. if(category && category->getPreferredType() != LLFolderType::FT_ROOT_INVENTORY)
  1905. continue;
  1906. if ( category && 0 == LLStringUtil::compareInsensitive(name, category->getName()) )
  1907. {
  1908. if(category->getUUID()!=mRootFolderID)
  1909. {
  1910. LLUUID& new_inv_root_folder_id = const_cast<LLUUID&>(mRootFolderID);
  1911. new_inv_root_folder_id = category->getUUID();
  1912. }
  1913. }
  1914. }
  1915. }
  1916. // 'My Inventory',
  1917. // root of the agent's inv found.
  1918. // The inv tree is built.
  1919. mIsAgentInvUsable = true;
  1920. llinfos << "Inventory initialized, notifying observers" << llendl;
  1921. addChangedMask(LLInventoryObserver::ALL, LLUUID::null);
  1922. notifyObservers();
  1923. }
  1924. }
  1925. }
  1926. struct LLUUIDAndName
  1927. {
  1928. LLUUIDAndName() {}
  1929. LLUUIDAndName(const LLUUID& id, const std::string& name);
  1930. bool operator==(const LLUUIDAndName& rhs) const;
  1931. bool operator<(const LLUUIDAndName& rhs) const;
  1932. bool operator>(const LLUUIDAndName& rhs) const;
  1933. LLUUID mID;
  1934. std::string mName;
  1935. };
  1936. LLUUIDAndName::LLUUIDAndName(const LLUUID& id, const std::string& name) :
  1937. mID(id), mName(name)
  1938. {
  1939. }
  1940. bool LLUUIDAndName::operator==(const LLUUIDAndName& rhs) const
  1941. {
  1942. return ((mID == rhs.mID) && (mName == rhs.mName));
  1943. }
  1944. bool LLUUIDAndName::operator<(const LLUUIDAndName& rhs) const
  1945. {
  1946. return (mID < rhs.mID);
  1947. }
  1948. bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const
  1949. {
  1950. return (mID > rhs.mID);
  1951. }
  1952. // static
  1953. bool LLInventoryModel::loadFromFile(const std::string& filename,
  1954. LLInventoryModel::cat_array_t& categories,
  1955. LLInventoryModel::item_array_t& items,
  1956. bool &is_cache_obsolete)
  1957. {
  1958. if(filename.empty())
  1959. {
  1960. llerrs << "Filename is Null!" << llendl;
  1961. return false;
  1962. }
  1963. llinfos << "LLInventoryModel::loadFromFile(" << filename << ")" << llendl;
  1964. LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
  1965. if(!file)
  1966. {
  1967. llinfos << "unable to load inventory from: " << filename << llendl;
  1968. return false;
  1969. }
  1970. // *NOTE: This buffer size is hard coded into scanf() below.
  1971. char buffer[MAX_STRING]; /*Flawfinder: ignore*/
  1972. char keyword[MAX_STRING]; /*Flawfinder: ignore*/
  1973. char value[MAX_STRING]; /*Flawfinder: ignore*/
  1974. is_cache_obsolete = true; // Obsolete until proven current
  1975. while(!feof(file) && fgets(buffer, MAX_STRING, file))
  1976. {
  1977. sscanf(buffer, " %126s %126s", keyword, value); /* Flawfinder: ignore */
  1978. if(0 == strcmp("inv_cache_version", keyword))
  1979. {
  1980. S32 version;
  1981. int succ = sscanf(value,"%d",&version);
  1982. if ((1 == succ) && (version == sCurrentInvCacheVersion))
  1983. {
  1984. // Cache is up to date
  1985. is_cache_obsolete = false;
  1986. continue;
  1987. }
  1988. else
  1989. {
  1990. // Cache is out of date
  1991. break;
  1992. }
  1993. }
  1994. else if(0 == strcmp("inv_category", keyword))
  1995. {
  1996. if (is_cache_obsolete)
  1997. break;
  1998. LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null);
  1999. if(inv_cat->importFileLocal(file))
  2000. {
  2001. categories.put(inv_cat);
  2002. }
  2003. else
  2004. {
  2005. llwarns << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << llendl;
  2006. //delete inv_cat; // automatic when inv_cat is reassigned or destroyed
  2007. }
  2008. }
  2009. else if(0 == strcmp("inv_item", keyword))
  2010. {
  2011. if (is_cache_obsolete)
  2012. break;
  2013. LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem;
  2014. if( inv_item->importFileLocal(file) )
  2015. {
  2016. // *FIX: Need a better solution, this prevents the
  2017. // application from freezing, but breaks inventory
  2018. // caching.
  2019. if(inv_item->getUUID().isNull())
  2020. {
  2021. //delete inv_item; // automatic when inv_cat is reassigned or destroyed
  2022. llwarns << "Ignoring inventory with null item id: "
  2023. << inv_item->getName() << llendl;
  2024. }
  2025. else
  2026. {
  2027. items.put(inv_item);
  2028. }
  2029. }
  2030. else
  2031. {
  2032. llwarns << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << llendl;
  2033. //delete inv_item; // automatic when inv_cat is reassigned or destroyed
  2034. }
  2035. }
  2036. else
  2037. {
  2038. llwarns << "Unknown token in inventory file '" << keyword << "'"
  2039. << llendl;
  2040. }
  2041. }
  2042. fclose(file);
  2043. if (is_cache_obsolete)
  2044. return false;
  2045. return true;
  2046. }
  2047. // static
  2048. bool LLInventoryModel::saveToFile(const std::string& filename,
  2049. const cat_array_t& categories,
  2050. const item_array_t& items)
  2051. {
  2052. if(filename.empty())
  2053. {
  2054. llerrs << "Filename is Null!" << llendl;
  2055. return false;
  2056. }
  2057. llinfos << "LLInventoryModel::saveToFile(" << filename << ")" << llendl;
  2058. LLFILE* file = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
  2059. if(!file)
  2060. {
  2061. llwarns << "unable to save inventory to: " << filename << llendl;
  2062. return false;
  2063. }
  2064. fprintf(file, "\tinv_cache_version\t%d\n",sCurrentInvCacheVersion);
  2065. S32 count = categories.count();
  2066. S32 i;
  2067. for(i = 0; i < count; ++i)
  2068. {
  2069. LLViewerInventoryCategory* cat = categories[i];
  2070. if(cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
  2071. {
  2072. cat->exportFileLocal(file);
  2073. }
  2074. }
  2075. count = items.count();
  2076. for(i = 0; i < count; ++i)
  2077. {
  2078. items[i]->exportFile(file);
  2079. }
  2080. fclose(file);
  2081. return true;
  2082. }
  2083. // message handling functionality
  2084. // static
  2085. void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
  2086. {
  2087. //msg->setHandlerFuncFast(_PREHASH_InventoryUpdate,
  2088. // processInventoryUpdate,
  2089. // NULL);
  2090. //msg->setHandlerFuncFast(_PREHASH_UseCachedInventory,
  2091. // processUseCachedInventory,
  2092. // NULL);
  2093. msg->setHandlerFuncFast(_PREHASH_UpdateCreateInventoryItem,
  2094. processUpdateCreateInventoryItem,
  2095. NULL);
  2096. msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem,
  2097. processRemoveInventoryItem,
  2098. NULL);
  2099. msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder,
  2100. processUpdateInventoryFolder,
  2101. NULL);
  2102. msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,
  2103. processRemoveInventoryFolder,
  2104. NULL);
  2105. msg->setHandlerFuncFast(_PREHASH_RemoveInventoryObjects,
  2106. processRemoveInventoryObjects,
  2107. NULL);
  2108. //msg->setHandlerFuncFast(_PREHASH_ExchangeCallingCard,
  2109. // processExchangeCallingcard,
  2110. // NULL);
  2111. //msg->setHandlerFuncFast(_PREHASH_AddCallingCard,
  2112. // processAddCallingcard,
  2113. // NULL);
  2114. //msg->setHandlerFuncFast(_PREHASH_DeclineCallingCard,
  2115. // processDeclineCallingcard,
  2116. // NULL);
  2117. msg->setHandlerFuncFast(_PREHASH_SaveAssetIntoInventory,
  2118. processSaveAssetIntoInventory,
  2119. NULL);
  2120. msg->setHandlerFuncFast(_PREHASH_BulkUpdateInventory,
  2121. processBu