diff options
Diffstat (limited to 'indra/newview/llinventorymodel.cpp')
-rwxr-xr-x[-rw-r--r--] | indra/newview/llinventorymodel.cpp | 2322 |
1 files changed, 1719 insertions, 603 deletions
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 53835f0166..f92332dea5 100644..100755 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2014, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,11 +25,16 @@ */ #include "llviewerprecompiledheaders.h" + +#include <typeinfo> + #include "llinventorymodel.h" +#include "llaisapi.h" #include "llagent.h" #include "llagentwearables.h" #include "llappearancemgr.h" +#include "llclipboard.h" #include "llinventorypanel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" @@ -46,6 +51,11 @@ #include "llviewerregion.h" #include "llcallbacklist.h" #include "llvoavatarself.h" +#include "llgesturemgr.h" +#include "llsdutil.h" +#include "bufferarray.h" +#include "bufferstream.h" +#include "llcorehttputil.h" //#define DIFF_INVENTORY_FILES #ifdef DIFF_INVENTORY_FILES @@ -62,7 +72,8 @@ BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE; ///---------------------------------------------------------------------------- //BOOL decompress_file(const char* src_filename, const char* dst_filename); -const char CACHE_FORMAT_STRING[] = "%s.inv"; +static const char CACHE_FORMAT_STRING[] = "%s.inv"; +static const char * const LOG_INV("Inventory"); struct InventoryIDPtrLess { @@ -100,17 +111,7 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) { S32 descendents_server = c->getDescendentCount(); - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - mModel->getDirectDescendentsOf( - c->getUUID(), - cats, - items); - S32 descendents_actual = 0; - if(cats && items) - { - descendents_actual = cats->count() + items->count(); - } + S32 descendents_actual = c->getViewerDescendentCount(); if(descendents_server == descendents_actual) { mCachedCatIDs.insert(c->getUUID()); @@ -130,23 +131,32 @@ LLInventoryModel gInventory; // Default constructor LLInventoryModel::LLInventoryModel() -: mModifyMask(LLInventoryObserver::ALL), - mChangedItemIDs(), +: // These are now ordered, keep them that way. + mBacklinkMMap(), + mIsAgentInvUsable(false), + mRootFolderID(), + mLibraryRootFolderID(), + mLibraryOwnerID(), mCategoryMap(), mItemMap(), - mCategoryLock(), - mItemLock(), - mLastItem(NULL), mParentChildCategoryTree(), mParentChildItemTree(), - mObservers(), - mRootFolderID(), - mLibraryRootFolderID(), - mLibraryOwnerID(), + mLastItem(NULL), mIsNotifyObservers(FALSE), - mIsAgentInvUsable(false) -{ -} + mModifyMask(LLInventoryObserver::ALL), + mChangedItemIDs(), + mObservers(), + mHttpRequestFG(NULL), + mHttpRequestBG(NULL), + mHttpOptions(NULL), + mHttpHeaders(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpPriorityFG(0), + mHttpPriorityBG(0), + mCategoryLock(), + mItemLock() +{} + // Destroys the object LLInventoryModel::~LLInventoryModel() @@ -166,6 +176,22 @@ void LLInventoryModel::cleanupInventory() delete observer; } mObservers.clear(); + + // Run down HTTP transport + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + delete mHttpRequestFG; + mHttpRequestFG = NULL; + delete mHttpRequestBG; + mHttpRequestBG = NULL; } // This is a convenience function to check if one object has a parent @@ -217,6 +243,55 @@ const LLViewerInventoryCategory *LLInventoryModel::getFirstNondefaultParent(cons return NULL; } +// +// Search up the parent chain until we get to the specified parent, then return the first child category under it +// +const LLViewerInventoryCategory* LLInventoryModel::getFirstDescendantOf(const LLUUID& master_parent_id, const LLUUID& obj_id) const +{ + if (master_parent_id == obj_id) + { + return NULL; + } + + const LLViewerInventoryCategory* current_cat = getCategory(obj_id); + + if (current_cat == NULL) + { + current_cat = getCategory(getObject(obj_id)->getParentUUID()); + } + + while (current_cat != NULL) + { + const LLUUID& current_parent_id = current_cat->getParentUUID(); + + if (current_parent_id == master_parent_id) + { + return current_cat; + } + + current_cat = getCategory(current_parent_id); + } + + return NULL; +} + +bool LLInventoryModel::getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const +{ + LLInventoryObject *object = getObject(object_id); + while (object && object->getParentUUID().notNull()) + { + LLInventoryObject *parent_object = getObject(object->getParentUUID()); + if (!parent_object) + { + LL_WARNS(LOG_INV) << "unable to trace topmost ancestor, missing item for uuid " << object->getParentUUID() << LL_ENDL; + return false; + } + object = parent_object; + } + result = object->getUUID(); + return true; +} + // Get the object by id. Returns NULL if not found. LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const { @@ -334,17 +409,72 @@ void LLInventoryModel::unlockDirectDescendentArrays(const LLUUID& cat_id) mItemLock[cat_id] = false; } -// findCategoryUUIDForType() returns the uuid of the category that -// specifies 'type' as what it defaults to containing. The category is -// not necessarily only for that type. *NOTE: This will create a new -// inventory category on the fly if one does not exist. -const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder, - bool find_in_library) +void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::EType type) +{ + // Make a list of folders that are not "main_id" and are of "type" + std::vector<LLUUID> folder_ids; + for (cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) + { + LLViewerInventoryCategory* cat = cit->second; + if ((cat->getPreferredType() == type) && (cat->getUUID() != main_id)) + { + folder_ids.push_back(cat->getUUID()); + } + } + + // Iterate through those folders + for (std::vector<LLUUID>::iterator folder_ids_it = folder_ids.begin(); folder_ids_it != folder_ids.end(); ++folder_ids_it) + { + LLUUID folder_id = (*folder_ids_it); + + // Get the content of this folder + cat_array_t* cats; + item_array_t* items; + getDirectDescendentsOf(folder_id, cats, items); + + // Move all items to the main folder + // Note : we get the list of UUIDs and iterate on them instead of iterating directly on item_array_t + // elements. This is because moving elements modify the maps and, consequently, invalidate iterators on them. + // This "gather and iterate" method is verbose but resilient. + std::vector<LLUUID> list_uuids; + for (item_array_t::const_iterator it = items->begin(); it != items->end(); ++it) + { + list_uuids.push_back((*it)->getUUID()); + } + for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it) + { + LLViewerInventoryItem* item = getItem(*it); + changeItemParent(item, main_id, TRUE); + } + + // Move all folders to the main folder + list_uuids.clear(); + for (cat_array_t::const_iterator it = cats->begin(); it != cats->end(); ++it) + { + list_uuids.push_back((*it)->getUUID()); + } + for (std::vector<LLUUID>::const_iterator it = list_uuids.begin(); it != list_uuids.end(); ++it) + { + LLViewerInventoryCategory* cat = getCategory(*it); + changeCategoryParent(cat, main_id, TRUE); + } + + // Purge the emptied folder + // Note: we'd like to use purgeObject() but it doesn't cleanly eliminate the folder + // which leads to issues further down the road when the folder is found again + //purgeObject(folder_id); + // We remove the folder and empty the trash instead which seems to work + removeCategory(folder_id); + gInventory.emptyFolderType("", LLFolderType::FT_TRASH); + } +} + +const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( + LLFolderType::EType preferred_type, + bool create_folder, + const LLUUID& root_id) { LLUUID rv = LLUUID::null; - - const LLUUID &root_id = (find_in_library) ? gInventory.getLibraryRootFolderID() : gInventory.getRootFolderID(); if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) { rv = root_id; @@ -355,19 +485,22 @@ const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType prefe cats = get_ptr_in_map(mParentChildCategoryTree, root_id); if(cats) { - S32 count = cats->count(); + S32 count = cats->size(); for(S32 i = 0; i < count; ++i) { - if(cats->get(i)->getPreferredType() == preferred_type) + if(cats->at(i)->getPreferredType() == preferred_type) { - rv = cats->get(i)->getUUID(); - break; + const LLUUID& folder_id = cats->at(i)->getUUID(); + if (rv.isNull() || folder_id < rv) + { + rv = folder_id; + } } } } } - if(rv.isNull() && isInventoryUsable() && (create_folder && !find_in_library)) + if(rv.isNull() && isInventoryUsable() && create_folder) { if(root_id.notNull()) { @@ -377,24 +510,93 @@ const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType prefe return rv; } +// findCategoryUUIDForType() returns the uuid of the category that +// specifies 'type' as what it defaults to containing. The category is +// not necessarily only for that type. *NOTE: This will create a new +// inventory category on the fly if one does not exist. +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +{ + return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getRootFolderID()); +} + +const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +{ + return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getLibraryRootFolderID()); +} + +class LLCreateInventoryCategoryResponder : public LLHTTPClient::Responder +{ + LOG_CLASS(LLCreateInventoryCategoryResponder); +public: + LLCreateInventoryCategoryResponder(LLInventoryModel* model, + boost::optional<inventory_func_type> callback): + mModel(model), + mCallback(callback) + { + } + +protected: + virtual void httpFailure() + { + LL_WARNS(LOG_INV) << dumpResponse() << LL_ENDL; + } + + virtual void httpSuccess() + { + //Server has created folder. + const LLSD& content = getContent(); + if (!content.isMap() || !content.has("folder_id")) + { + failureResult(HTTP_INTERNAL_ERROR, "Malformed response contents", content); + return; + } + LLUUID category_id = content["folder_id"].asUUID(); + + LL_DEBUGS(LOG_INV) << ll_pretty_print_sd(content) << LL_ENDL; + // Add the category to the internal representation + LLPointer<LLViewerInventoryCategory> cat = + new LLViewerInventoryCategory( category_id, + content["parent_id"].asUUID(), + (LLFolderType::EType)content["type"].asInteger(), + content["name"].asString(), + gAgent.getID() ); + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL); + cat->setDescendentCount(0); + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + mModel->accountForUpdate(update); + mModel->updateCategory(cat); + + if (mCallback) + { + mCallback.get()(category_id); + } + } + +private: + boost::optional<inventory_func_type> mCallback; + LLInventoryModel* mModel; +}; + // Convenience function to create a new category. You could call // updateCategory() with a newly generated UUID category, but this // version will take care of details like what the name should be // based on preferred type. Returns the UUID of the new category. LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, - const std::string& pname) + const std::string& pname, + boost::optional<inventory_func_type> callback) { + LLUUID id; if(!isInventoryUsable()) { - llwarns << "Inventory is broken." << llendl; + LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL; return id; } if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup()) { - lldebugs << "Attempt to create undefined category." << llendl; + LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL; return id; } @@ -408,6 +610,34 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, { name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type)); } + + LLViewerRegion* viewer_region = gAgent.getRegion(); + std::string url; + if ( viewer_region ) + url = viewer_region->getCapability("CreateInventoryCategory"); + + if (!url.empty() && callback.get_ptr()) + { + //Let's use the new capability. + + LLSD request, body; + body["folder_id"] = id; + body["parent_id"] = parent_id; + body["type"] = (LLSD::Integer) preferred_type; + body["name"] = name; + + request["message"] = "CreateInventoryCategory"; + request["payload"] = body; + + LL_DEBUGS(LOG_INV) << "create category request: " << ll_pretty_print_sd(request) << LL_ENDL; + // viewer_region->getCapAPI().post(request); + LLHTTPClient::post( + url, + body, + new LLCreateInventoryCategoryResponder(this, callback) ); + + return LLUUID::null; + } // Add the category to the internal representation LLPointer<LLViewerInventoryCategory> cat = @@ -433,7 +663,41 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, return id; } -// Starting with the object specified, add it's descendents to the +// This is optimized for the case that we just want to know whether a +// category has any immediate children meeting a condition, without +// needing to recurse or build up any lists. +bool LLInventoryModel::hasMatchingDirectDescendent(const LLUUID& cat_id, + LLInventoryCollectFunctor& filter) +{ + LLInventoryModel::cat_array_t *cats; + LLInventoryModel::item_array_t *items; + getDirectDescendentsOf(cat_id, cats, items); + if (cats) + { + for (LLInventoryModel::cat_array_t::const_iterator it = cats->begin(); + it != cats->end(); ++it) + { + if (filter(*it,NULL)) + { + return true; + } + } + } + if (items) + { + for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); + it != items->end(); ++it) + { + if (filter(NULL,*it)) + { + return true; + } + } + } + return false; +} + +// Starting with the object specified, add its descendents to the // array provided, but do not add the inventory object specified by // id. There is no guaranteed order. Neither array will be erased // before adding objects to it. Do not store a copy of the pointers @@ -464,8 +728,7 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, cat_array_t& cats, item_array_t& items, BOOL include_trash, - LLInventoryCollectFunctor& add, - BOOL follow_folder_links) + LLInventoryCollectFunctor& add) { // Start with categories if(!include_trash) @@ -477,13 +740,13 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id); if(cat_array) { - S32 count = cat_array->count(); + S32 count = cat_array->size(); for(S32 i = 0; i < count; ++i) { - LLViewerInventoryCategory* cat = cat_array->get(i); + LLViewerInventoryCategory* cat = cat_array->at(i); if(add(cat,NULL)) { - cats.put(cat); + cats.push_back(cat); } collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add); } @@ -492,46 +755,16 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, LLViewerInventoryItem* item = NULL; item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id); - // Follow folder links recursively. Currently never goes more - // than one level deep (for current outfit support) - // Note: if making it fully recursive, need more checking against infinite loops. - if (follow_folder_links && item_array) - { - S32 count = item_array->count(); - for(S32 i = 0; i < count; ++i) - { - item = item_array->get(i); - if (item && item->getActualType() == LLAssetType::AT_LINK_FOLDER) - { - LLViewerInventoryCategory *linked_cat = item->getLinkedCategory(); - if (linked_cat && linked_cat->getPreferredType() != LLFolderType::FT_OUTFIT) - // BAP - was - // LLAssetType::lookupIsEnsembleCategoryType(linked_cat->getPreferredType())) - // Change back once ensemble typing is in place. - { - if(add(linked_cat,NULL)) - { - // BAP should this be added here? May not - // matter if it's only being used in current - // outfit traversal. - cats.put(LLPointer<LLViewerInventoryCategory>(linked_cat)); - } - collectDescendentsIf(linked_cat->getUUID(), cats, items, include_trash, add, FALSE); - } - } - } - } - // Move onto items if(item_array) { - S32 count = item_array->count(); + S32 count = item_array->size(); for(S32 i = 0; i < count; ++i) { - item = item_array->get(i); + item = item_array->at(i); if(add(NULL, item)) { - items.put(item); + items.push_back(item); } } } @@ -543,26 +776,7 @@ void LLInventoryModel::addChangedMaskForLinks(const LLUUID& object_id, U32 mask) if (!obj || obj->getIsLinkType()) return; - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - LLLinkedItemIDMatches is_linked_item_match(object_id); - collectDescendentsIf(gInventory.getRootFolderID(), - cat_array, - item_array, - LLInventoryModel::INCLUDE_TRASH, - is_linked_item_match); - if (cat_array.empty() && item_array.empty()) - { - return; - } - for (LLInventoryModel::cat_array_t::iterator cat_iter = cat_array.begin(); - cat_iter != cat_array.end(); - cat_iter++) - { - LLViewerInventoryCategory *linked_cat = (*cat_iter); - addChangedMask(mask, linked_cat->getUUID()); - }; - + LLInventoryModel::item_array_t item_array = collectLinksTo(object_id); for (LLInventoryModel::item_array_t::iterator iter = item_array.begin(); iter != item_array.end(); iter++) @@ -590,17 +804,27 @@ LLViewerInventoryItem* LLInventoryModel::getLinkedItem(const LLUUID& object_id) return object_id.notNull() ? getItem(getLinkedItemID(object_id)) : NULL; } -LLInventoryModel::item_array_t LLInventoryModel::collectLinkedItems(const LLUUID& id, - const LLUUID& start_folder_id) +LLInventoryModel::item_array_t LLInventoryModel::collectLinksTo(const LLUUID& id) { + // Get item list via collectDescendents (slow!) item_array_t items; - LLInventoryModel::cat_array_t cat_array; - LLLinkedItemIDMatches is_linked_item_match(id); - collectDescendentsIf((start_folder_id == LLUUID::null ? gInventory.getRootFolderID() : start_folder_id), - cat_array, - items, - LLInventoryModel::INCLUDE_TRASH, - is_linked_item_match); + const LLInventoryObject *obj = getObject(id); + // FIXME - should be as in next line, but this is causing a + // stack-smashing crash of cause TBD... check in the REBUILD code. + //if (obj && obj->getIsLinkType()) + if (!obj || obj->getIsLinkType()) + return items; + + std::pair<backlink_mmap_t::iterator, backlink_mmap_t::iterator> range = mBacklinkMMap.equal_range(id); + for (backlink_mmap_t::iterator it = range.first; it != range.second; ++it) + { + LLViewerInventoryItem *item = getItem(it->second); + if (item) + { + items.push_back(item); + } + } + return items; } @@ -617,9 +841,8 @@ bool LLInventoryModel::isInventoryUsable() const // Calling this method with an inventory item will either change an // existing item with a matching item_id, or will add the item to the // current inventory. -U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) +U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item, U32 mask) { - U32 mask = LLInventoryObserver::NONE; if(item->getUUID().isNull()) { return mask; @@ -627,11 +850,19 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) if(!isInventoryUsable()) { - llwarns << "Inventory is broken." << llendl; + LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL; return mask; } - LLViewerInventoryItem* old_item = getItem(item->getUUID()); + // We're hiding mesh types +#if 0 + if (item->getType() == LLAssetType::AT_MESH) + { + return mask; + } +#endif + + LLPointer<LLViewerInventoryItem> old_item = getItem(item->getUUID()); LLPointer<LLViewerInventoryItem> new_item; if(old_item) { @@ -647,12 +878,12 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id); if(item_array) { - item_array->removeObj(old_item); + vector_replace_with_last(*item_array, old_item); } item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id); if(item_array) { - item_array->put(old_item); + item_array->push_back(old_item); } mask |= LLInventoryObserver::STRUCTURE; } @@ -678,11 +909,11 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) { // *FIX: bit of a hack to call update server from here... new_item->updateServer(TRUE); - item_array->put(new_item); + item_array->push_back(new_item); } else { - llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl; + LL_WARNS(LOG_INV) << "Couldn't find parent-child item tree for " << new_item->getName() << LL_ENDL; } } else @@ -697,17 +928,22 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) { parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); new_item->setParent(parent_id); + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate new_folder(parent_id, 1); + update.push_back(new_folder); + accountForUpdate(update); + } item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id); if(item_array) { - item_array->put(new_item); + item_array->push_back(new_item); } else { // Whoops! No such parent, make one. - llinfos << "Lost item: " << new_item->getUUID() << " - " - << new_item->getName() << llendl; + LL_INFOS(LOG_INV) << "Lost item: " << new_item->getUUID() << " - " + << new_item->getName() << LL_ENDL; parent_id = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); new_item->setParent(parent_id); item_array = get_ptr_in_map(mParentChildItemTree, parent_id); @@ -716,11 +952,11 @@ U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item) // *FIX: bit of a hack to call update server from // here... new_item->updateServer(TRUE); - item_array->put(new_item); + item_array->push_back(new_item); } else { - llwarns << "Lost and found Not there!!" << llendl; + LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL; } } } @@ -785,7 +1021,7 @@ LLInventoryModel::item_array_t* LLInventoryModel::getUnlockedItemArray(const LLU // Calling this method with an inventory category will either change // an existing item with the matching id, or it will add the category. -void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) +void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 mask) { if(cat->getUUID().isNull()) { @@ -794,15 +1030,14 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) if(!isInventoryUsable()) { - llwarns << "Inventory is broken." << llendl; + LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL; return; } - LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID()); + LLPointer<LLViewerInventoryCategory> old_cat = getCategory(cat->getUUID()); if(old_cat) { // We already have an old category, modify it's values - U32 mask = LLInventoryObserver::NONE; LLUUID old_parent_id = old_cat->getParentUUID(); LLUUID new_parent_id = cat->getParentUUID(); if(old_parent_id != new_parent_id) @@ -812,14 +1047,15 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) cat_array = getUnlockedCatArray(old_parent_id); if(cat_array) { - cat_array->removeObj(old_cat); + vector_replace_with_last(*cat_array, old_cat); } cat_array = getUnlockedCatArray(new_parent_id); if(cat_array) { - cat_array->put(old_cat); + cat_array->push_back(old_cat); } mask |= LLInventoryObserver::STRUCTURE; + mask |= LLInventoryObserver::INTERNAL; } if(old_cat->getName() != cat->getName()) { @@ -835,12 +1071,12 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) new_cat->copyViewerCategory(cat); addCategory(new_cat); - // make sure this category is correctly referenced by it's parent. + // make sure this category is correctly referenced by its parent. cat_array_t* cat_array; cat_array = getUnlockedCatArray(cat->getParentUUID()); if(cat_array) { - cat_array->put(new_cat); + cat_array->push_back(new_cat); } // make space in the tree for this category's children. @@ -856,57 +1092,308 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id) { - lldebugs << "LLInventoryModel::moveObject()" << llendl; + LL_DEBUGS(LOG_INV) << "LLInventoryModel::moveObject()" << LL_ENDL; if(!isInventoryUsable()) { - llwarns << "Inventory is broken." << llendl; + LL_WARNS(LOG_INV) << "Inventory is broken." << LL_ENDL; return; } if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id)) { - llwarns << "Could not move inventory object " << object_id << " to " - << cat_id << llendl; + LL_WARNS(LOG_INV) << "Could not move inventory object " << object_id << " to " + << cat_id << LL_ENDL; return; } - LLViewerInventoryCategory* cat = getCategory(object_id); + LLPointer<LLViewerInventoryCategory> cat = getCategory(object_id); if(cat && (cat->getParentUUID() != cat_id)) { cat_array_t* cat_array; cat_array = getUnlockedCatArray(cat->getParentUUID()); - if(cat_array) cat_array->removeObj(cat); + if(cat_array) vector_replace_with_last(*cat_array, cat); cat_array = getUnlockedCatArray(cat_id); cat->setParent(cat_id); - if(cat_array) cat_array->put(cat); + if(cat_array) cat_array->push_back(cat); addChangedMask(LLInventoryObserver::STRUCTURE, object_id); return; } - LLViewerInventoryItem* item = getItem(object_id); + LLPointer<LLViewerInventoryItem> item = getItem(object_id); if(item && (item->getParentUUID() != cat_id)) { item_array_t* item_array; item_array = getUnlockedItemArray(item->getParentUUID()); - if(item_array) item_array->removeObj(item); + if(item_array) vector_replace_with_last(*item_array, item); item_array = getUnlockedItemArray(cat_id); item->setParent(cat_id); - if(item_array) item_array->put(item); + if(item_array) item_array->push_back(item); addChangedMask(LLInventoryObserver::STRUCTURE, object_id); return; } } +// Migrated from llinventoryfunctions +void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item, + const LLUUID& new_parent_id, + BOOL restamp) +{ + if (item->getParentUUID() == new_parent_id) + { + LL_DEBUGS(LOG_INV) << "'" << item->getName() << "' (" << item->getUUID() + << ") is already in folder " << new_parent_id << LL_ENDL; + } + else + { + LL_INFOS(LOG_INV) << "Moving '" << item->getName() << "' (" << item->getUUID() + << ") from " << item->getParentUUID() << " to folder " + << new_parent_id << LL_ENDL; + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); + update.push_back(new_folder); + accountForUpdate(update); + + LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); + new_item->setParent(new_parent_id); + new_item->updateParentOnServer(restamp); + updateItem(new_item); + notifyObservers(); + } +} + +// Migrated from llinventoryfunctions +void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat, + const LLUUID& new_parent_id, + BOOL restamp) +{ + if (!cat) + { + return; + } + + // Can't move a folder into a child of itself. + if (isObjectDescendentOf(new_parent_id, cat->getUUID())) + { + return; + } + + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(new_parent_id, 1); + update.push_back(new_folder); + accountForUpdate(update); + + LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat); + new_cat->setParent(new_parent_id); + new_cat->updateParentOnServer(restamp); + updateCategory(new_cat); + notifyObservers(); +} + +void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update) +{ + LLTimer timer; + if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); + } + + AISUpdate ais_update(update); // parse update llsd into stuff to do. + ais_update.doUpdate(); // execute the updates in the appropriate order. + LL_INFOS(LOG_INV) << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL; +} + +// Does not appear to be used currently. +void LLInventoryModel::onItemUpdated(const LLUUID& item_id, const LLSD& updates, bool update_parent_version) +{ + U32 mask = LLInventoryObserver::NONE; + + LLPointer<LLViewerInventoryItem> item = gInventory.getItem(item_id); + LL_DEBUGS(LOG_INV) << "item_id: [" << item_id << "] name " << (item ? item->getName() : "(NOT FOUND)") << LL_ENDL; + if(item) + { + for (LLSD::map_const_iterator it = updates.beginMap(); + it != updates.endMap(); ++it) + { + if (it->first == "name") + { + LL_INFOS(LOG_INV) << "Updating name from " << item->getName() << " to " << it->second.asString() << LL_ENDL; + item->rename(it->second.asString()); + mask |= LLInventoryObserver::LABEL; + } + else if (it->first == "desc") + { + LL_INFOS(LOG_INV) << "Updating description from " << item->getActualDescription() + << " to " << it->second.asString() << LL_ENDL; + item->setDescription(it->second.asString()); + } + else + { + LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL; + } + } + mask |= LLInventoryObserver::INTERNAL; + addChangedMask(mask, item->getUUID()); + if (update_parent_version) + { + // Descendent count is unchanged, but folder version incremented. + LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), 0); + accountForUpdate(up); + } + notifyObservers(); // do we want to be able to make this optional? + } +} + +// Not used? +void LLInventoryModel::onCategoryUpdated(const LLUUID& cat_id, const LLSD& updates) +{ + U32 mask = LLInventoryObserver::NONE; + + LLPointer<LLViewerInventoryCategory> cat = gInventory.getCategory(cat_id); + LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (cat ? cat->getName() : "(NOT FOUND)") << LL_ENDL; + if(cat) + { + for (LLSD::map_const_iterator it = updates.beginMap(); + it != updates.endMap(); ++it) + { + if (it->first == "name") + { + LL_INFOS(LOG_INV) << "Updating name from " << cat->getName() << " to " << it->second.asString() << LL_ENDL; + cat->rename(it->second.asString()); + mask |= LLInventoryObserver::LABEL; + } + else + { + LL_ERRS(LOG_INV) << "unhandled updates for field: " << it->first << LL_ENDL; + } + } + mask |= LLInventoryObserver::INTERNAL; + addChangedMask(mask, cat->getUUID()); + notifyObservers(); // do we want to be able to make this optional? + } +} + +// Update model after descendents have been purged. +void LLInventoryModel::onDescendentsPurgedFromServer(const LLUUID& object_id, bool fix_broken_links) +{ + LLPointer<LLViewerInventoryCategory> cat = getCategory(object_id); + if (cat.notNull()) + { + // do the cache accounting + S32 descendents = cat->getDescendentCount(); + if(descendents > 0) + { + LLInventoryModel::LLCategoryUpdate up(object_id, -descendents); + accountForUpdate(up); + } + + // we know that descendent count is 0, however since the + // accounting may actually not do an update, we should force + // it here. + cat->setDescendentCount(0); + + // unceremoniously remove anything we have locally stored. + LLInventoryModel::cat_array_t categories; + LLInventoryModel::item_array_t items; + collectDescendents(object_id, + categories, + items, + LLInventoryModel::INCLUDE_TRASH); + S32 count = items.size(); + + LLUUID uu_id; + for(S32 i = 0; i < count; ++i) + { + uu_id = items.at(i)->getUUID(); + + // This check prevents the deletion of a previously deleted item. + // This is necessary because deletion is not done in a hierarchical + // order. The current item may have been already deleted as a child + // of its deleted parent. + if (getItem(uu_id)) + { + deleteObject(uu_id, fix_broken_links); + } + } + + count = categories.size(); + // Slightly kludgy way to make sure categories are removed + // only after their child categories have gone away. + + // FIXME: Would probably make more sense to have this whole + // descendent-clearing thing be a post-order recursive + // function to get the leaf-up behavior automatically. + S32 deleted_count; + S32 total_deleted_count = 0; + do + { + deleted_count = 0; + for(S32 i = 0; i < count; ++i) + { + uu_id = categories.at(i)->getUUID(); + if (getCategory(uu_id)) + { + cat_array_t* cat_list = getUnlockedCatArray(uu_id); + if (!cat_list || (cat_list->size() == 0)) + { + deleteObject(uu_id, fix_broken_links); + deleted_count++; + } + } + } + total_deleted_count += deleted_count; + } + while (deleted_count > 0); + if (total_deleted_count != count) + { + LL_WARNS(LOG_INV) << "Unexpected count of categories deleted, got " + << total_deleted_count << " expected " << count << LL_ENDL; + } + //gInventory.validate(); + } +} + +// Update model after an item is confirmed as removed from +// server. Works for categories or items. +void LLInventoryModel::onObjectDeletedFromServer(const LLUUID& object_id, bool fix_broken_links, bool update_parent_version, bool do_notify_observers) +{ + LLPointer<LLInventoryObject> obj = getObject(object_id); + if(obj) + { + if (getCategory(object_id)) + { + // For category, need to delete/update all children first. + onDescendentsPurgedFromServer(object_id, fix_broken_links); + } + + + // From item/cat removeFromServer() + if (update_parent_version) + { + LLInventoryModel::LLCategoryUpdate up(obj->getParentUUID(), -1); + accountForUpdate(up); + } + + // From purgeObject() + LLPreview::hide(object_id); + deleteObject(object_id, fix_broken_links, do_notify_observers); + } +} + + // Delete a particular inventory object by ID. -void LLInventoryModel::deleteObject(const LLUUID& id) +void LLInventoryModel::deleteObject(const LLUUID& id, bool fix_broken_links, bool do_notify_observers) { - lldebugs << "LLInventoryModel::deleteObject()" << llendl; + LL_DEBUGS(LOG_INV) << "LLInventoryModel::deleteObject()" << LL_ENDL; LLPointer<LLInventoryObject> obj = getObject(id); if (!obj) { - llwarns << "Deleting non-existent object [ id: " << id << " ] " << llendl; + LL_WARNS(LOG_INV) << "Deleting non-existent object [ id: " << id << " ] " << LL_ENDL; return; } - lldebugs << "Deleting inventory object " << id << llendl; + LL_DEBUGS(LOG_INV) << "Deleting inventory object " << id << LL_ENDL; mLastItem = NULL; LLUUID parent_id = obj->getParentUUID(); mCategoryMap.erase(id); @@ -915,124 +1402,76 @@ void LLInventoryModel::deleteObject(const LLUUID& id) item_array_t* item_list = getUnlockedItemArray(parent_id); if(item_list) { - LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); - item_list->removeObj(item); + LLPointer<LLViewerInventoryItem> item = (LLViewerInventoryItem*)((LLInventoryObject*)obj); + vector_replace_with_last(*item_list, item); } cat_array_t* cat_list = getUnlockedCatArray(parent_id); if(cat_list) { - LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); - cat_list->removeObj(cat); + LLPointer<LLViewerInventoryCategory> cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj); + vector_replace_with_last(*cat_list, cat); } item_list = getUnlockedItemArray(id); if(item_list) { + if (item_list->size()) + { + LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child items" << LL_ENDL; + } delete item_list; mParentChildItemTree.erase(id); } cat_list = getUnlockedCatArray(id); if(cat_list) { + if (cat_list->size()) + { + LL_WARNS(LOG_INV) << "Deleting cat " << id << " while it still has child cats" << LL_ENDL; + } delete cat_list; mParentChildCategoryTree.erase(id); } addChangedMask(LLInventoryObserver::REMOVE, id); - obj = NULL; // delete obj - updateLinkedObjectsFromPurge(id); - gInventory.notifyObservers(); -} -// Delete a particular inventory item by ID, and remove it from the server. -void LLInventoryModel::purgeObject(const LLUUID &id) -{ - lldebugs << "LLInventoryModel::purgeObject() [ id: " << id << " ] " << llendl; - LLPointer<LLInventoryObject> obj = getObject(id); - if(obj) + bool is_link_type = obj->getIsLinkType(); + if (is_link_type) + { + removeBacklinkInfo(obj->getUUID(), obj->getLinkedUUID()); + } + + // Can't have links to links, so there's no need for this update + // if the item removed is a link. Can also skip if source of the + // update is getting broken link info separately. + obj = NULL; // delete obj + if (fix_broken_links && !is_link_type) { - obj->removeFromServer(); - LLPreview::hide(id); - deleteObject(id); + updateLinkedObjectsFromPurge(id); + } + if (do_notify_observers) + { + notifyObservers(); } } void LLInventoryModel::updateLinkedObjectsFromPurge(const LLUUID &baseobj_id) { - LLInventoryModel::item_array_t item_array = collectLinkedItems(baseobj_id); + LLInventoryModel::item_array_t item_array = collectLinksTo(baseobj_id); // REBUILD is expensive, so clear the current change list first else // everything else on the changelist will also get rebuilt. - gInventory.notifyObservers(); - for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); - iter != item_array.end(); - iter++) - { - const LLViewerInventoryItem *linked_item = (*iter); - const LLUUID &item_id = linked_item->getUUID(); - if (item_id == baseobj_id) continue; - addChangedMask(LLInventoryObserver::REBUILD, item_id); - } - gInventory.notifyObservers(); -} - -// This is a method which collects the descendents of the id -// provided. If the category is not found, no action is -// taken. This method goes through the long winded process of -// cancelling any calling cards, removing server representation of -// folders, items, etc in a fairly efficient manner. -void LLInventoryModel::purgeDescendentsOf(const LLUUID& id) -{ - EHasChildren children = categoryHasChildren(id); - if(children == CHILDREN_NO) + if (item_array.size() > 0) { - llinfos << "Not purging descendents of " << id << llendl; - return; - } - LLPointer<LLViewerInventoryCategory> cat = getCategory(id); - if(cat.notNull()) - { - // do the cache accounting - llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName() - << llendl; - S32 descendents = cat->getDescendentCount(); - if(descendents > 0) - { - LLCategoryUpdate up(id, -descendents); - accountForUpdate(up); - } - - // we know that descendent count is 0, aide since the - // accounting may actually not do an update, we should force - // it here. - cat->setDescendentCount(0); - - // send it upstream - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("PurgeInventoryDescendents"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("FolderID", id); - gAgent.sendReliableMessage(); - - // unceremoniously remove anything we have locally stored. - cat_array_t categories; - item_array_t items; - collectDescendents(id, - categories, - items, - INCLUDE_TRASH); - S32 count = items.count(); - S32 i; - for(i = 0; i < count; ++i) - { - deleteObject(items.get(i)->getUUID()); - } - count = categories.count(); - for(i = 0; i < count; ++i) + notifyObservers(); + for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) { - deleteObject(categories.get(i)->getUUID()); + const LLViewerInventoryItem *linked_item = (*iter); + const LLUUID &item_id = linked_item->getUUID(); + if (item_id == baseobj_id) continue; + addChangedMask(LLInventoryObserver::REBUILD, item_id); } + notifyObservers(); } } @@ -1055,23 +1494,25 @@ BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer) const void LLInventoryModel::idleNotifyObservers() { + // *FIX: Think I want this conditional or moved elsewhere... + handleResponses(true); + if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0)) { return; } - notifyObservers(""); + notifyObservers(); } // Call this method when it's time to update everyone on a new state. -// The optional argument 'service_name' is used by Agent Inventory Service [DEV-20328] -void LLInventoryModel::notifyObservers(const std::string service_name) +void LLInventoryModel::notifyObservers() { if (mIsNotifyObservers) { // Within notifyObservers, something called notifyObservers // again. This type of recursion is unsafe because it causes items to be // processed twice, and this can easily lead to infinite loops. - llwarns << "Call was made to notifyObservers within notifyObservers!" << llendl; + LL_WARNS(LOG_INV) << "Call was made to notifyObservers within notifyObservers!" << LL_ENDL; return; } @@ -1080,16 +1521,7 @@ void LLInventoryModel::notifyObservers(const std::string service_name) iter != mObservers.end(); ) { LLInventoryObserver* observer = *iter; - - if (service_name.empty()) - { - observer->changed(mModifyMask); - } - else - { - observer->mMessageName = service_name; - observer->changed(mModifyMask); - } + observer->changed(mModifyMask); // safe way to increment since changed may delete entries! (@!##%@!@&*!) iter = mObservers.upper_bound(observer); @@ -1097,6 +1529,7 @@ void LLInventoryModel::notifyObservers(const std::string service_name) mModifyMask = LLInventoryObserver::NONE; mChangedItemIDs.clear(); + mAddedItemIDs.clear(); mIsNotifyObservers = FALSE; } @@ -1109,108 +1542,53 @@ void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) // Something marked an item for change within a call to notifyObservers // (which is in the process of processing the list of items marked for change). // This means the change may fail to be processed. - llwarns << "Adding changed mask within notify observers! Change will likely be lost." << llendl; + LL_WARNS(LOG_INV) << "Adding changed mask within notify observers! Change will likely be lost." << LL_ENDL; + LLViewerInventoryItem *item = getItem(referent); + if (item) + { + LL_WARNS(LOG_INV) << "Item " << item->getName() << LL_ENDL; + } + else + { + LLViewerInventoryCategory *cat = getCategory(referent); + if (cat) + { + LL_WARNS(LOG_INV) << "Category " << cat->getName() << LL_ENDL; + } + } } mModifyMask |= mask; if (referent.notNull()) { mChangedItemIDs.insert(referent); - } - - // Update all linked items. Starting with just LABEL because I'm - // not sure what else might need to be accounted for this. - if (mModifyMask & LLInventoryObserver::LABEL) - { - addChangedMaskForLinks(referent, LLInventoryObserver::LABEL); - } -} -// If we get back a normal response, handle it here -void LLInventoryModel::fetchInventoryResponder::result(const LLSD& content) -{ - start_new_inventory_observer(); - - /*LLUUID agent_id; - agent_id = content["agent_id"].asUUID(); - if(agent_id != gAgent.getID()) - { - llwarns << "Got a inventory update for the wrong agent: " << agent_id - << llendl; - return; - }*/ - item_array_t items; - update_map_t update; - S32 count = content["items"].size(); - bool all_one_folder = true; - LLUUID folder_id; - // Does this loop ever execute more than once? - for(S32 i = 0; i < count; ++i) - { - LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; - titem->unpackMessage(content["items"][i]); - - lldebugs << "LLInventoryModel::messageUpdateCore() item id:" - << titem->getUUID() << llendl; - items.push_back(titem); - // examine update for changes. - LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); - if(itemp) + if (mask & LLInventoryObserver::ADD) { - if(titem->getParentUUID() == itemp->getParentUUID()) - { - update[titem->getParentUUID()]; - } - else - { - ++update[titem->getParentUUID()]; - --update[itemp->getParentUUID()]; - } + mAddedItemIDs.insert(referent); } - else - { - ++update[titem->getParentUUID()]; - } - if (folder_id.isNull()) - { - folder_id = titem->getParentUUID(); - } - else + + // Update all linked items. Starting with just LABEL because I'm + // not sure what else might need to be accounted for this. + if (mask & LLInventoryObserver::LABEL) { - all_one_folder = false; + addChangedMaskForLinks(referent, LLInventoryObserver::LABEL); } } - - U32 changes = 0x0; - //as above, this loop never seems to loop more than once per call - for (item_array_t::iterator it = items.begin(); it != items.end(); ++it) - { - changes |= gInventory.updateItem(*it); - } - gInventory.notifyObservers("fetchinventory"); - gViewerWindow->getWindow()->decBusyCount(); -} - -//If we get back an error (not found, etc...), handle it here -void LLInventoryModel::fetchInventoryResponder::error(U32 status, const std::string& reason) -{ - llinfos << "fetchInventory::error " - << status << ": " << reason << llendl; - gInventory.notifyObservers("fetchinventory"); } bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const { if(folder_id.isNull()) { - llwarns << "Calling fetch descendents on NULL folder id!" << llendl; + LL_WARNS(LOG_INV) << "Calling fetch descendents on NULL folder id!" << LL_ENDL; return false; } LLViewerInventoryCategory* cat = getCategory(folder_id); if(!cat) { - llwarns << "Asked to fetch descendents of non-existent folder: " - << folder_id << llendl; + LL_WARNS(LOG_INV) << "Asked to fetch descendents of non-existent folder: " + << folder_id << LL_ENDL; return false; } //S32 known_descendents = 0; @@ -1218,11 +1596,11 @@ bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id) const //item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id); //if(categories) //{ - // known_descendents += categories->count(); + // known_descendents += categories->size(); //} //if(items) //{ - // known_descendents += items->count(); + // known_descendents += items->size(); //} return cat->fetch(); } @@ -1231,12 +1609,12 @@ void LLInventoryModel::cache( const LLUUID& parent_folder_id, const LLUUID& agent_id) { - lldebugs << "Caching " << parent_folder_id << " for " << agent_id - << llendl; + LL_DEBUGS(LOG_INV) << "Caching " << parent_folder_id << " for " << agent_id + << LL_ENDL; LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id); if(!root_cat) return; cat_array_t categories; - categories.put(root_cat); + categories.push_back(root_cat); item_array_t items; LLCanCache can_cache(this); @@ -1257,21 +1635,27 @@ void LLInventoryModel::cache( gzip_filename.append(".gz"); if(gzip_file(inventory_filename, gzip_filename)) { - lldebugs << "Successfully compressed " << inventory_filename << llendl; + LL_DEBUGS(LOG_INV) << "Successfully compressed " << inventory_filename << LL_ENDL; LLFile::remove(inventory_filename); } else { - llwarns << "Unable to compress " << inventory_filename << llendl; + LL_WARNS(LOG_INV) << "Unable to compress " << inventory_filename << LL_ENDL; } } void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) { - //llinfos << "LLInventoryModel::addCategory()" << llendl; + //LL_INFOS(LOG_INV) << "LLInventoryModel::addCategory()" << LL_ENDL; if(category) { + // We aren't displaying the Meshes folder + if (category->mPreferredType == LLFolderType::FT_MESH) + { + return; + } + // try to localize default names first. See EXT-8319, EXT-7051. category->localizeName(); @@ -1281,6 +1665,47 @@ void LLInventoryModel::addCategory(LLViewerInventoryCategory* category) } } +bool LLInventoryModel::hasBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) const +{ + std::pair <backlink_mmap_t::const_iterator, backlink_mmap_t::const_iterator> range; + range = mBacklinkMMap.equal_range(target_id); + for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it) + { + if (it->second == link_id) + { + return true; + } + } + return false; +} + +void LLInventoryModel::addBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) +{ + if (!hasBacklinkInfo(link_id, target_id)) + { + mBacklinkMMap.insert(std::make_pair(target_id, link_id)); + } +} + +void LLInventoryModel::removeBacklinkInfo(const LLUUID& link_id, const LLUUID& target_id) +{ + std::pair <backlink_mmap_t::iterator, backlink_mmap_t::iterator> range; + range = mBacklinkMMap.equal_range(target_id); + for (backlink_mmap_t::iterator it = range.first; it != range.second; ) + { + if (it->second == link_id) + { + backlink_mmap_t::iterator delete_it = it; // iterator will be invalidated by erase. + ++it; + mBacklinkMMap.erase(delete_it); + } + else + { + ++it; + } + } +} + void LLInventoryModel::addItem(LLViewerInventoryItem* item) { llassert(item); @@ -1292,7 +1717,9 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) if ((item->getType() == LLAssetType::AT_NONE) || LLAssetType::lookup(item->getType()) == LLAssetType::badLookup()) { - llwarns << "Got bad asset type for item [ name: " << item->getName() << " type: " << item->getType() << " inv-type: " << item->getInventoryType() << " ], ignoring." << llendl; + LL_WARNS(LOG_INV) << "Got bad asset type for item [ name: " << item->getName() + << " type: " << item->getType() + << " inv-type: " << item->getInventoryType() << " ], ignoring." << LL_ENDL; return; } @@ -1300,9 +1727,17 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) // The item will show up as a broken link. if (item->getIsBrokenLink()) { - llinfos << "Adding broken link [ name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << llendl; + LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName() + << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + } + if (item->getIsLinkType()) + { + // Add back-link from linked-to UUID. + const LLUUID& link_id = item->getUUID(); + const LLUUID& target_id = item->getLinkedUUID(); + addBacklinkInfo(link_id, target_id); } - mItemMap[item->getUUID()] = item; } } @@ -1310,7 +1745,7 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) // Empty the entire contents void LLInventoryModel::empty() { -// llinfos << "LLInventoryModel::empty()" << llendl; +// LL_INFOS(LOG_INV) << "LLInventoryModel::empty()" << LL_ENDL; std::for_each( mParentChildCategoryTree.begin(), mParentChildCategoryTree.end(), @@ -1321,6 +1756,7 @@ void LLInventoryModel::empty() mParentChildItemTree.end(), DeletePairedPointer()); mParentChildItemTree.clear(); + mBacklinkMMap.clear(); // forget all backlink information. mCategoryMap.clear(); // remove all references (should delete entries) mItemMap.clear(); // remove all references (should delete entries) mLastItem = NULL; @@ -1332,42 +1768,39 @@ void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update) const LLViewerInventoryCategory* cat = getCategory(update.mCategoryID); if(cat) { - bool accounted = false; S32 version = cat->getVersion(); if(version != LLViewerInventoryCategory::VERSION_UNKNOWN) { S32 descendents_server = cat->getDescendentCount(); - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - getDirectDescendentsOf(update.mCategoryID, cats, items); - S32 descendents_actual = 0; - if(cats && items) - { - descendents_actual = cats->count() + items->count(); - } + S32 descendents_actual = cat->getViewerDescendentCount(); if(descendents_server == descendents_actual) { - accounted = true; descendents_actual += update.mDescendentDelta; cat->setDescendentCount(descendents_actual); cat->setVersion(++version); - lldebugs << "accounted: '" << cat->getName() << "' " - << version << " with " << descendents_actual - << " descendents." << llendl; + LL_DEBUGS(LOG_INV) << "accounted: '" << cat->getName() << "' " + << version << " with " << descendents_actual + << " descendents." << LL_ENDL; + } + else + { + // Error condition, this means that the category did not register that + // it got new descendents (perhaps because it is still being loaded) + // which means its descendent count will be wrong. + LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version:" + << version << " due to mismatched descendent count: server == " + << descendents_server << ", viewer == " << descendents_actual << LL_ENDL; } } - if(!accounted) + else { - // Error condition, this means that the category did not register that - // it got new descendents (perhaps because it is still being loaded) - // which means its descendent count will be wrong. - llwarns << "Accounting failed for '" << cat->getName() << "' version:" - << version << llendl; + LL_WARNS(LOG_INV) << "Accounting failed for '" << cat->getName() << "' version: unknown (" + << version << ")" << LL_ENDL; } } else { - llwarns << "No category found for update " << update.mCategoryID << llendl; + LL_WARNS(LOG_INV) << "No category found for update " << update.mCategoryID << LL_ENDL; } } @@ -1396,47 +1829,6 @@ void LLInventoryModel::accountForUpdate( } } - -/* -void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id) -{ - LLViewerInventoryCategory* cat = getCategory(category_id); - if(cat) - { - S32 version = cat->getVersion(); - if(LLViewerInventoryCategory::VERSION_UNKNOWN != version) - { - cat->setVersion(version + 1); - llinfos << "IncrementVersion: " << cat->getName() << " " - << cat->getVersion() << llendl; - } - else - { - llinfos << "Attempt to increment version when unknown: " - << category_id << llendl; - } - } - else - { - llinfos << "Attempt to increment category: " << category_id << llendl; - } -} -void LLInventoryModel::incrementCategorySetVersion( - const std::set<LLUUID>& categories) -{ - if(!categories.empty()) - { - std::set<LLUUID>::const_iterator it = categories.begin(); - std::set<LLUUID>::const_iterator end = categories.end(); - for(; it != end; ++it) - { - incrementCategoryVersion(*it); - } - } -} -*/ - - LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren( const LLUUID& cat_id) const { @@ -1458,12 +1850,12 @@ LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren( // Shouldn't have to run this, but who knows. parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID()); - if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0) + if (cat_it != mParentChildCategoryTree.end() && cat_it->second->size() > 0) { return CHILDREN_YES; } parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID()); - if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0) + if (item_it != mParentChildItemTree.end() && item_it->second->size() > 0) { return CHILDREN_YES; } @@ -1477,14 +1869,7 @@ bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN)) { S32 descendents_server = cat->getDescendentCount(); - LLInventoryModel::cat_array_t* cats; - LLInventoryModel::item_array_t* items; - getDirectDescendentsOf(cat_id, cats, items); - S32 descendents_actual = 0; - if(cats && items) - { - descendents_actual = cats->count() + items->count(); - } + S32 descendents_actual = cat->getViewerDescendentCount(); if(descendents_server == descendents_actual) { return true; @@ -1497,7 +1882,7 @@ bool LLInventoryModel::loadSkeleton( const LLSD& options, const LLUUID& owner_id) { - lldebugs << "importing inventory skeleton for " << owner_id << llendl; + LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL; typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t; cat_set_t temp_cats; @@ -1534,7 +1919,7 @@ bool LLInventoryModel::loadSkeleton( } else { - llwarns << "Unable to import near " << name.asString() << llendl; + LL_WARNS(LOG_INV) << "Unable to import near " << name.asString() << LL_ENDL; rv = false; } } @@ -1546,6 +1931,7 @@ bool LLInventoryModel::loadSkeleton( update_map_t child_counts; cat_array_t categories; item_array_t items; + item_array_t possible_broken_links; cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded. std::string owner_id_str; owner_id.toString(owner_id_str); @@ -1570,7 +1956,7 @@ bool LLInventoryModel::loadSkeleton( } else { - llinfos << "Unable to gunzip " << gzip_filename << llendl; + LL_INFOS(LOG_INV) << "Unable to gunzip " << gzip_filename << LL_ENDL; } } bool is_cache_obsolete = false; @@ -1580,7 +1966,7 @@ bool LLInventoryModel::loadSkeleton( // found to generate a set of categories we should add. We // will go through each category loaded and if the version // does not match, invalidate the version. - S32 count = categories.count(); + S32 count = categories.size(); cat_set_t::iterator not_cached = temp_cats.end(); std::set<LLUUID> cached_ids; for(S32 i = 0; i < count; ++i) @@ -1594,7 +1980,7 @@ bool LLInventoryModel::loadSkeleton( LLViewerInventoryCategory* tcat = *cit; // we can safely ignore anything loaded from file, but - // not sent down in the skeleton. + // not sent down in the skeleton. Must have been removed from inventory. if(cit == not_cached) { continue; @@ -1632,6 +2018,8 @@ bool LLInventoryModel::loadSkeleton( // Add all the items loaded which are parented to a // category with a correctly cached parent S32 bad_link_count = 0; + S32 good_link_count = 0; + S32 recovered_link_count = 0; cat_map_t::iterator unparented = mCategoryMap.end(); for(item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); @@ -1648,26 +2036,56 @@ bool LLInventoryModel::loadSkeleton( // This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache. if (item->getIsBrokenLink()) { - bad_link_count++; - lldebugs << "Attempted to add cached link item without baseobj present ( name: " - << item->getName() << " itemID: " << item->getUUID() - << " assetID: " << item->getAssetUUID() - << " ). Ignoring and invalidating " << cat->getName() << " . " << llendl; - invalid_categories.insert(cit->second); + //bad_link_count++; + LL_DEBUGS(LOG_INV) << "Attempted to add cached link item without baseobj present ( name: " + << item->getName() << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() + << " ). Ignoring and invalidating " << cat->getName() << " . " << LL_ENDL; + possible_broken_links.push_back(item); continue; } + else if (item->getIsLinkType()) + { + good_link_count++; + } addItem(item); cached_item_count += 1; ++child_counts[cat->getUUID()]; } } } - if (bad_link_count > 0) + if (possible_broken_links.size() > 0) { - llinfos << "Attempted to add " << bad_link_count - << " cached link items without baseobj present. " - << "The corresponding categories were invalidated." << llendl; + for(item_array_t::const_iterator item_iter = possible_broken_links.begin(); + item_iter != possible_broken_links.end(); + ++item_iter) + { + LLViewerInventoryItem *item = (*item_iter).get(); + const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID()); + const LLViewerInventoryCategory* cat = cit->second.get(); + if (item->getIsBrokenLink()) + { + bad_link_count++; + invalid_categories.insert(cit->second); + //LL_INFOS(LOG_INV) << "link still broken: " << item->getName() << " in folder " << cat->getName() << LL_ENDL; + } + else + { + // was marked as broken because of loading order, its actually fine to load + addItem(item); + cached_item_count += 1; + ++child_counts[cat->getUUID()]; + recovered_link_count++; + } + } + + LL_INFOS(LOG_INV) << "Attempted to add " << bad_link_count + << " cached link items without baseobj present. " + << good_link_count << " link items were successfully added. " + << recovered_link_count << " links added in recovery. " + << "The corresponding categories were invalidated." << LL_ENDL; } + } else { @@ -1689,8 +2107,9 @@ bool LLInventoryModel::loadSkeleton( { LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); cat->setVersion(NO_VERSION); - llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl; + LL_DEBUGS(LOG_INV) << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << LL_ENDL; } + LL_INFOS(LOG_INV) << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << LL_ENDL; // At this point, we need to set the known descendents for each // category which successfully cached so that we do not @@ -1722,15 +2141,15 @@ bool LLInventoryModel::loadSkeleton( if(is_cache_obsolete) { // If out of date, remove the gzipped file too. - llwarns << "Inv cache out of date, removing" << llendl; + LL_WARNS(LOG_INV) << "Inv cache out of date, removing" << LL_ENDL; LLFile::remove(gzip_filename); } categories.clear(); // will unref and delete entries } - llinfos << "Successfully loaded " << cached_category_count - << " categories and " << cached_item_count << " items from cache." - << llendl; + LL_INFOS(LOG_INV) << "Successfully loaded " << cached_category_count + << " categories and " << cached_item_count << " items from cache." + << LL_ENDL; return rv; } @@ -1740,7 +2159,7 @@ bool LLInventoryModel::loadSkeleton( // should be sufficient for our needs. void LLInventoryModel::buildParentChildMap() { - llinfos << "LLInventoryModel::buildParentChildMap()" << llendl; + LL_INFOS(LOG_INV) << "LLInventoryModel::buildParentChildMap()" << LL_ENDL; // *NOTE: I am skipping the logic around folder version // synchronization here because it seems if a folder is lost, we @@ -1757,7 +2176,7 @@ void LLInventoryModel::buildParentChildMap() for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) { LLViewerInventoryCategory* cat = cit->second; - cats.put(cat); + cats.push_back(cat); if (mParentChildCategoryTree.count(cat->getUUID()) == 0) { llassert_always(mCategoryLock[cat->getUUID()] == false); @@ -1785,16 +2204,21 @@ void LLInventoryModel::buildParentChildMap() // Now we have a structure with all of the categories that we can // iterate over and insert into the correct place in the child // category tree. - S32 count = cats.count(); + S32 count = cats.size(); S32 i; S32 lost = 0; + cat_array_t lost_cats; for(i = 0; i < count; ++i) { - LLViewerInventoryCategory* cat = cats.get(i); + LLViewerInventoryCategory* cat = cats.at(i); catsp = getUnlockedCatArray(cat->getParentUUID()); - if(catsp) + if(catsp && + // Only the two root folders should be children of null. + // Others should go to lost & found. + (cat->getParentUUID().notNull() || + cat->getPreferredType() == LLFolderType::FT_ROOT_INVENTORY )) { - catsp->put(cat); + catsp->push_back(cat); } else { @@ -1804,40 +2228,51 @@ void LLInventoryModel::buildParentChildMap() // implement it, we would need a set or map of uuid pairs // which would be (folder_id, new_parent_id) to be sent up // to the server. - llinfos << "Lost categroy: " << cat->getUUID() << " - " - << cat->getName() << llendl; + LL_INFOS(LOG_INV) << "Lost category: " << cat->getUUID() << " - " + << cat->getName() << LL_ENDL; ++lost; - // plop it into the lost & found. - LLFolderType::EType pref = cat->getPreferredType(); - if(LLFolderType::FT_NONE == pref) - { - cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); - } - else if(LLFolderType::FT_ROOT_INVENTORY == pref) - { - // it's the root - cat->setParent(LLUUID::null); - } - else - { - // it's a protected folder. - cat->setParent(gInventory.getRootFolderID()); - } - cat->updateServer(TRUE); - catsp = getUnlockedCatArray(cat->getParentUUID()); - if(catsp) - { - catsp->put(cat); - } - else - { - llwarns << "Lost and found Not there!!" << llendl; - } + lost_cats.push_back(cat); } } if(lost) { - llwarns << "Found " << lost << " lost categories." << llendl; + LL_WARNS(LOG_INV) << "Found " << lost << " lost categories." << LL_ENDL; + } + + // Do moves in a separate pass to make sure we've properly filed + // the FT_LOST_AND_FOUND category before we try to find its UUID. + for(i = 0; i<lost_cats.size(); ++i) + { + LLViewerInventoryCategory *cat = lost_cats.at(i); + + // plop it into the lost & found. + LLFolderType::EType pref = cat->getPreferredType(); + if(LLFolderType::FT_NONE == pref) + { + cat->setParent(findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); + } + else if(LLFolderType::FT_ROOT_INVENTORY == pref) + { + // it's the root + cat->setParent(LLUUID::null); + } + else + { + // it's a protected folder. + cat->setParent(gInventory.getRootFolderID()); + } + // FIXME note that updateServer() fails with protected + // types, so this will not work as intended in that case. + cat->updateServer(TRUE); + catsp = getUnlockedCatArray(cat->getParentUUID()); + if(catsp) + { + catsp->push_back(cat); + } + else + { + LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL; + } } const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null); @@ -1854,25 +2289,25 @@ void LLInventoryModel::buildParentChildMap() for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit) { item = (*iit).second; - items.put(item); + items.push_back(item); } } - count = items.count(); + count = items.size(); lost = 0; uuid_vec_t lost_item_ids; for(i = 0; i < count; ++i) { LLPointer<LLViewerInventoryItem> item; - item = items.get(i); + item = items.at(i); itemsp = getUnlockedItemArray(item->getParentUUID()); if(itemsp) { - itemsp->put(item); + itemsp->push_back(item); } else { - llinfos << "Lost item: " << item->getUUID() << " - " - << item->getName() << llendl; + LL_INFOS(LOG_INV) << "Lost item: " << item->getUUID() << " - " + << item->getName() << LL_ENDL; ++lost; // plop it into the lost & found. // @@ -1884,17 +2319,17 @@ void LLInventoryModel::buildParentChildMap() itemsp = getUnlockedItemArray(item->getParentUUID()); if(itemsp) { - itemsp->put(item); + itemsp->push_back(item); } else { - llwarns << "Lost and found Not there!!" << llendl; + LL_WARNS(LOG_INV) << "Lost and found Not there!!" << LL_ENDL; } } } if(lost) { - llwarns << "Found " << lost << " lost items." << llendl; + LL_WARNS(LOG_INV) << "Found " << lost << " lost items." << LL_ENDL; LLMessageSystem* msg = gMessageSystem; BOOL start_new_message = TRUE; const LLUUID lnf = findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); @@ -1963,11 +2398,97 @@ void LLInventoryModel::buildParentChildMap() // The inv tree is built. mIsAgentInvUsable = true; - llinfos << "Inventory initialized, notifying observers" << llendl; - addChangedMask(LLInventoryObserver::ALL, LLUUID::null); - notifyObservers(); + // notifyObservers() has been moved to + // llstartup/idle_startup() after this func completes. + // Allows some system categories to be created before + // observers start firing. } } + + if (!gInventory.validate()) + { + LL_WARNS(LOG_INV) << "model failed validity check!" << LL_ENDL; + } +} + +// Would normally do this at construction but that's too early +// in the process for gInventory. Have the first requestPost() +// call set things up. +void LLInventoryModel::initHttpRequest() +{ + if (! mHttpRequestFG) + { + // Haven't initialized, get to it + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + + mHttpRequestFG = new LLCore::HttpRequest; + mHttpRequestBG = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpOptions->setTransferTimeout(300); + mHttpOptions->setUseRetryAfter(true); + // mHttpOptions->setTrace(2); // Do tracing of requests + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_LLSD_XML); + mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); + mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_INVENTORY); + } +} + +void LLInventoryModel::handleResponses(bool foreground) +{ + if (foreground && mHttpRequestFG) + { + mHttpRequestFG->update(0); + } + else if (! foreground && mHttpRequestBG) + { + mHttpRequestBG->update(50000L); + } +} + +LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground, + const std::string & url, + const LLSD & body, + LLCore::HttpHandler * handler, + const char * const message) +{ + if (! mHttpRequestFG) + { + // We do the initialization late and lazily as this class is + // statically-constructed and not all the bits are ready at + // that time. + initHttpRequest(); + } + + LLCore::HttpRequest * request(foreground ? mHttpRequestFG : mHttpRequestBG); + LLCore::HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + handle = LLCoreHttpUtil::requestPostWithLLSD(request, + mHttpPolicyClass, + (foreground ? mHttpPriorityFG : mHttpPriorityBG), + url, + body, + mHttpOptions, + mHttpHeaders, + handler); + if (LLCORE_HTTP_HANDLE_INVALID == handle) + { + LLCore::HttpStatus status(request->getStatus()); + LL_WARNS(LOG_INV) << "HTTP POST request failed for " << message + << ", Status: " << status.toTerseString() + << " Reason: '" << status.toString() << "'" + << LL_ENDL; + delete handler; + } + return handle; +} + +void LLInventoryModel::createCommonSystemCategories() +{ + gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH,true); + gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true); + gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD,true); + gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS,true); } struct LLUUIDAndName @@ -2010,14 +2531,14 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, { if(filename.empty()) { - llerrs << "Filename is Null!" << llendl; + LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL; return false; } - llinfos << "LLInventoryModel::loadFromFile(" << filename << ")" << llendl; + LL_INFOS(LOG_INV) << "LLInventoryModel::loadFromFile(" << filename << ")" << LL_ENDL; LLFILE* file = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ if(!file) { - llinfos << "unable to load inventory from: " << filename << llendl; + LL_INFOS(LOG_INV) << "unable to load inventory from: " << filename << LL_ENDL; return false; } // *NOTE: This buffer size is hard coded into scanf() below. @@ -2052,11 +2573,11 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null); if(inv_cat->importFileLocal(file)) { - categories.put(inv_cat); + categories.push_back(inv_cat); } else { - llwarns << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << llendl; + LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << LL_ENDL; //delete inv_cat; // automatic when inv_cat is reassigned or destroyed } } @@ -2074,25 +2595,25 @@ bool LLInventoryModel::loadFromFile(const std::string& filename, if(inv_item->getUUID().isNull()) { //delete inv_item; // automatic when inv_cat is reassigned or destroyed - llwarns << "Ignoring inventory with null item id: " - << inv_item->getName() << llendl; + LL_WARNS(LOG_INV) << "Ignoring inventory with null item id: " + << inv_item->getName() << LL_ENDL; } else { - items.put(inv_item); + items.push_back(inv_item); } } else { - llwarns << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << llendl; + LL_WARNS(LOG_INV) << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << LL_ENDL; //delete inv_item; // automatic when inv_cat is reassigned or destroyed } } else { - llwarns << "Unknown token in inventory file '" << keyword << "'" - << llendl; + LL_WARNS(LOG_INV) << "Unknown token in inventory file '" << keyword << "'" + << LL_ENDL; } } fclose(file); @@ -2108,19 +2629,19 @@ bool LLInventoryModel::saveToFile(const std::string& filename, { if(filename.empty()) { - llerrs << "Filename is Null!" << llendl; + LL_ERRS(LOG_INV) << "Filename is Null!" << LL_ENDL; return false; } - llinfos << "LLInventoryModel::saveToFile(" << filename << ")" << llendl; + LL_INFOS(LOG_INV) << "LLInventoryModel::saveToFile(" << filename << ")" << LL_ENDL; LLFILE* file = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/ if(!file) { - llwarns << "unable to save inventory to: " << filename << llendl; + LL_WARNS(LOG_INV) << "unable to save inventory to: " << filename << LL_ENDL; return false; } fprintf(file, "\tinv_cache_version\t%d\n",sCurrentInvCacheVersion); - S32 count = categories.count(); + S32 count = categories.size(); S32 i; for(i = 0; i < count; ++i) { @@ -2131,7 +2652,7 @@ bool LLInventoryModel::saveToFile(const std::string& filename, } } - count = items.count(); + count = items.size(); for(i = 0; i < count; ++i) { items[i]->exportFile(file); @@ -2163,6 +2684,9 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg) msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder, processRemoveInventoryFolder, NULL); + msg->setHandlerFuncFast(_PREHASH_RemoveInventoryObjects, + processRemoveInventoryObjects, + NULL); //msg->setHandlerFuncFast(_PREHASH_ExchangeCallingCard, // processExchangeCallingcard, // NULL); @@ -2188,7 +2712,7 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg) void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, void**) { // do accounting and highlight new items if they arrive - if (gInventory.messageUpdateCore(msg, true)) + if (gInventory.messageUpdateCore(msg, true, LLInventoryObserver::UPDATE_CREATE)) { U32 callback_id; LLUUID item_id; @@ -2208,7 +2732,7 @@ void LLInventoryModel::processFetchInventoryReply(LLMessageSystem* msg, void**) } -bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) +bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32 mask) { //make sure our added inventory observer is active start_new_inventory_observer(); @@ -2217,22 +2741,21 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "Got a inventory update for the wrong agent: " << agent_id - << llendl; + LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id + << LL_ENDL; return false; } item_array_t items; update_map_t update; S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); - bool all_one_folder = true; LLUUID folder_id; // Does this loop ever execute more than once? for(S32 i = 0; i < count; ++i) { LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; titem->unpackMessage(msg, _PREHASH_InventoryData, i); - lldebugs << "LLInventoryModel::messageUpdateCore() item id:" - << titem->getUUID() << llendl; + LL_DEBUGS(LOG_INV) << "LLInventoryModel::messageUpdateCore() item id: " + << titem->getUUID() << LL_ENDL; items.push_back(titem); // examine update for changes. LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); @@ -2256,10 +2779,6 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) { folder_id = titem->getParentUUID(); } - else - { - all_one_folder = false; - } } if(account) { @@ -2267,10 +2786,14 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) } U32 changes = 0x0; + if (account) + { + mask |= LLInventoryObserver::CREATE; + } //as above, this loop never seems to loop more than once per call for (item_array_t::iterator it = items.begin(); it != items.end(); ++it) { - changes |= gInventory.updateItem(*it); + changes |= gInventory.updateItem(*it, mask); } gInventory.notifyObservers(); gViewerWindow->getWindow()->decBusyCount(); @@ -2279,26 +2802,21 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account) } // static -void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) +void LLInventoryModel::removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg, const char* msg_label) { - lldebugs << "LLInventoryModel::processRemoveInventoryItem()" << llendl; - LLUUID agent_id, item_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); - if(agent_id != gAgent.getID()) - { - llwarns << "Got a RemoveInventoryItem for the wrong agent." - << llendl; - return; - } - S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData); + LLUUID item_id; + S32 count = msg->getNumberOfBlocksFast(msg_label); + LL_DEBUGS(LOG_INV) << "Message has " << count << " item blocks" << LL_ENDL; uuid_vec_t item_ids; update_map_t update; for(S32 i = 0; i < count; ++i) { - msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i); + msg->getUUIDFast(msg_label, _PREHASH_ItemID, item_id, i); + LL_DEBUGS(LOG_INV) << "Checking for item-to-be-removed " << item_id << LL_ENDL; LLViewerInventoryItem* itemp = gInventory.getItem(item_id); if(itemp) { + LL_DEBUGS(LOG_INV) << "Item will be removed " << item_id << LL_ENDL; // we only bother with the delete and account if we found // the item - this is usually a back-up for permissions, // so frequently the item will already be gone. @@ -2309,8 +2827,24 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) gInventory.accountForUpdate(update); for(uuid_vec_t::iterator it = item_ids.begin(); it != item_ids.end(); ++it) { + LL_DEBUGS(LOG_INV) << "Calling deleteObject " << *it << LL_ENDL; gInventory.deleteObject(*it); } +} + +// static +void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) +{ + LL_DEBUGS(LOG_INV) << "LLInventoryModel::processRemoveInventoryItem()" << LL_ENDL; + LLUUID agent_id, item_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); + if(agent_id != gAgent.getID()) + { + LL_WARNS(LOG_INV) << "Got a RemoveInventoryItem for the wrong agent." + << LL_ENDL; + return; + } + LLInventoryModel::removeInventoryItem(agent_id, msg, _PREHASH_InventoryData); gInventory.notifyObservers(); } @@ -2318,14 +2852,14 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, void**) { - lldebugs << "LLInventoryModel::processUpdateInventoryFolder()" << llendl; + LL_DEBUGS(LOG_INV) << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL; LLUUID agent_id, folder_id, parent_id; //char name[DB_INV_ITEM_NAME_BUF_SIZE]; msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "Got an UpdateInventoryFolder for the wrong agent." - << llendl; + LL_WARNS(LOG_INV) << "Got an UpdateInventoryFolder for the wrong agent." + << LL_ENDL; return; } LLPointer<LLViewerInventoryCategory> lastfolder; // hack @@ -2375,18 +2909,10 @@ void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, } // static -void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg, - void**) +void LLInventoryModel::removeInventoryFolder(LLUUID agent_id, + LLMessageSystem* msg) { - lldebugs << "LLInventoryModel::processRemoveInventoryFolder()" << llendl; - LLUUID agent_id, folder_id; - msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id); - if(agent_id != gAgent.getID()) - { - llwarns << "Got a RemoveInventoryFolder for the wrong agent." - << llendl; - return; - } + LLUUID folder_id; uuid_vec_t folder_ids; update_map_t update; S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); @@ -2405,6 +2931,42 @@ void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg, { gInventory.deleteObject(*it); } +} + +// static +void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg, + void**) +{ + LL_DEBUGS() << "LLInventoryModel::processRemoveInventoryFolder()" << LL_ENDL; + LLUUID agent_id, session_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); + if(agent_id != gAgent.getID()) + { + LL_WARNS() << "Got a RemoveInventoryFolder for the wrong agent." + << LL_ENDL; + return; + } + LLInventoryModel::removeInventoryFolder( agent_id, msg ); + gInventory.notifyObservers(); +} + +// static +void LLInventoryModel::processRemoveInventoryObjects(LLMessageSystem* msg, + void**) +{ + LL_DEBUGS() << "LLInventoryModel::processRemoveInventoryObjects()" << LL_ENDL; + LLUUID agent_id, session_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); + if(agent_id != gAgent.getID()) + { + LL_WARNS() << "Got a RemoveInventoryObjects for the wrong agent." + << LL_ENDL; + return; + } + LLInventoryModel::removeInventoryFolder( agent_id, msg ); + LLInventoryModel::removeInventoryItem( agent_id, msg, _PREHASH_ItemData ); gInventory.notifyObservers(); } @@ -2416,8 +2978,8 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg, msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "Got a SaveAssetIntoInventory message for the wrong agent." - << llendl; + LL_WARNS() << "Got a SaveAssetIntoInventory message for the wrong agent." + << LL_ENDL; return; } @@ -2427,8 +2989,8 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg, // The viewer ignores the asset id because this message is only // used for attachments/objects, so the asset id is not used in // the viewer anyway. - lldebugs << "LLInventoryModel::processSaveAssetIntoInventory itemID=" - << item_id << llendl; + LL_DEBUGS() << "LLInventoryModel::processSaveAssetIntoInventory itemID=" + << item_id << LL_ENDL; LLViewerInventoryItem* item = gInventory.getItem( item_id ); if( item ) { @@ -2439,8 +3001,8 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg, } else { - llinfos << "LLInventoryModel::processSaveAssetIntoInventory item" - " not found: " << item_id << llendl; + LL_INFOS() << "LLInventoryModel::processSaveAssetIntoInventory item" + " not found: " << item_id << LL_ENDL; } if(gViewerWindow) { @@ -2463,13 +3025,13 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "Got a BulkUpdateInventory for the wrong agent." << llendl; + LL_WARNS() << "Got a BulkUpdateInventory for the wrong agent." << LL_ENDL; return; } LLUUID tid; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid); #ifndef LL_RELEASE_FOR_DOWNLOAD - llinfos << "Bulk inventory: " << tid << llendl; + LL_DEBUGS("Inventory") << "Bulk inventory: " << tid << LL_ENDL; #endif update_map_t update; @@ -2481,9 +3043,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) { LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID()); tfolder->unpackMessage(msg, _PREHASH_FolderData, i); - //llinfos << "unpaked folder '" << tfolder->getName() << "' (" - // << tfolder->getUUID() << ") in " << tfolder->getParentUUID() - // << llendl; + LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' (" + << tfolder->getUUID() << ") in " << tfolder->getParentUUID() + << LL_ENDL; if(tfolder->getUUID().notNull()) { folders.push_back(tfolder); @@ -2523,11 +3085,11 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) { LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; titem->unpackMessage(msg, _PREHASH_ItemData, i); - //llinfos << "unpaked item '" << titem->getName() << "' in " - // << titem->getParentUUID() << llendl; + LL_DEBUGS("Inventory") << "unpacked item '" << titem->getName() << "' in " + << titem->getParentUUID() << LL_ENDL; U32 callback_id; msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id); - if(titem->getUUID().notNull()) + if(titem->getUUID().notNull() ) // && callback_id.notNull() ) { items.push_back(titem); cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID())); @@ -2584,7 +3146,7 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) LLInventoryState::sWearNewClothing = FALSE; } - if (tid == LLInventoryState::sWearNewClothingTransactionID) + if (tid.notNull() && tid == LLInventoryState::sWearNewClothingTransactionID) { count = wearable_ids.size(); for (i = 0; i < count; ++i) @@ -2601,6 +3163,9 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) InventoryCallbackInfo cbinfo = (*inv_it); gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID); } + + //gInventory.validate(); + // Don't show the inventory. We used to call showAgentInventory here. //LLFloaterInventory* view = LLFloaterInventory::getActiveInventory(); //if(view) @@ -2630,7 +3195,7 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "Got a UpdateInventoryItem for the wrong agent." << llendl; + LL_WARNS() << "Got a UpdateInventoryItem for the wrong agent." << LL_ENDL; return; } LLUUID parent_id; @@ -2659,7 +3224,8 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) // If the item has already been added (e.g. from link prefetch), then it doesn't need to be re-added. if (gInventory.getItem(titem->getUUID())) { - lldebugs << "Skipping prefetched item [ Name: " << titem->getName() << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << llendl; + LL_DEBUGS("Inventory") << "Skipping prefetched item [ Name: " << titem->getName() + << " | Type: " << titem->getActualType() << " | ItemUUID: " << titem->getUUID() << " ] " << LL_ENDL; continue; } gInventory.updateItem(titem); @@ -2681,13 +3247,13 @@ void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**) // static void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**) { - lldebugs << "LLInventoryModel::processMoveInventoryItem()" << llendl; + LL_DEBUGS() << "LLInventoryModel::processMoveInventoryItem()" << LL_ENDL; LLUUID agent_id; msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); if(agent_id != gAgent.getID()) { - llwarns << "Got a MoveInventoryItem message for the wrong agent." - << llendl; + LL_WARNS() << "Got a MoveInventoryItem message for the wrong agent." + << LL_ENDL; return; } @@ -2706,8 +3272,8 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**) msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_FolderID, folder_id, i); msg->getString("InventoryData", "NewName", new_name, i); - lldebugs << "moving item " << item_id << " to folder " - << folder_id << llendl; + LL_DEBUGS() << "moving item " << item_id << " to folder " + << folder_id << LL_ENDL; update_list_t update; LLCategoryUpdate old_folder(item->getParentUUID(), -1); update.push_back(old_folder); @@ -2725,7 +3291,7 @@ void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**) } else { - llinfos << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << llendl; + LL_INFOS() << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << LL_ENDL; } } if(anything_changed) @@ -2745,8 +3311,7 @@ bool LLInventoryModel::callbackEmptyFolderType(const LLSD& notification, const L if (option == 0) // YES { const LLUUID folder_id = findCategoryUUIDForType(preferred_type); - purgeDescendentsOf(folder_id); - notifyObservers(); + purge_descendents_of(folder_id, NULL); } return false; } @@ -2761,8 +3326,7 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLFolderT else { const LLUUID folder_id = findCategoryUUIDForType(preferred_type); - purgeDescendentsOf(folder_id); - notifyObservers(); + purge_descendents_of(folder_id, NULL); } } @@ -2771,21 +3335,77 @@ void LLInventoryModel::emptyFolderType(const std::string notification, LLFolderT void LLInventoryModel::removeItem(const LLUUID& item_id) { LLViewerInventoryItem* item = getItem(item_id); - const LLUUID new_parent = findCategoryUUIDForType(LLFolderType::FT_TRASH); - if (item && item->getParentUUID() != new_parent) + if (! item) { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1); - update.push_back(new_folder); - accountForUpdate(update); + LL_WARNS("Inventory") << "couldn't find inventory item " << item_id << LL_ENDL; + } + else + { + const LLUUID new_parent = findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (new_parent.notNull()) + { + LL_INFOS("Inventory") << "Moving to Trash (" << new_parent << "):" << LL_ENDL; + changeItemParent(item, new_parent, TRUE); + } + } +} - LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item); - new_item->setParent(new_parent); - new_item->updateParentOnServer(TRUE); - updateItem(new_item); - notifyObservers(); +void LLInventoryModel::removeCategory(const LLUUID& category_id) +{ + if (! get_is_category_removable(this, category_id)) + { + return; + } + + // Look for any gestures and deactivate them + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + collectDescendents(category_id, descendent_categories, descendent_items, FALSE); + + for (LLInventoryModel::item_array_t::const_iterator iter = descendent_items.begin(); + iter != descendent_items.end(); + ++iter) + { + const LLViewerInventoryItem* item = (*iter); + const LLUUID& item_id = item->getUUID(); + if (item->getType() == LLAssetType::AT_GESTURE + && LLGestureMgr::instance().isGestureActive(item_id)) + { + LLGestureMgr::instance().deactivateGesture(item_id); + } + } + + LLViewerInventoryCategory* cat = getCategory(category_id); + if (cat) + { + const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (trash_id.notNull()) + { + changeCategoryParent(cat, trash_id, TRUE); + } + } +} + +void LLInventoryModel::removeObject(const LLUUID& object_id) +{ + LLInventoryObject* obj = getObject(object_id); + if (dynamic_cast<LLViewerInventoryItem*>(obj)) + { + removeItem(object_id); + } + else if (dynamic_cast<LLViewerInventoryCategory*>(obj)) + { + removeCategory(object_id); + } + else if (obj) + { + LL_WARNS("Inventory") << "object ID " << object_id + << " is an object of unrecognized class " + << typeid(*obj).name() << LL_ENDL; + } + else + { + LL_WARNS("Inventory") << "object ID " << object_id << " not found" << LL_ENDL; } } @@ -2825,50 +3445,73 @@ BOOL LLInventoryModel::getIsFirstTimeInViewer2() // Do not call this before parentchild map is built. if (!gInventory.mIsAgentInvUsable) { - llwarns << "Parent Child Map not yet built; guessing as first time in viewer2." << llendl; + LL_WARNS() << "Parent Child Map not yet built; guessing as first time in viewer2." << LL_ENDL; return TRUE; } return sFirstTimeInViewer2; } -static LLInventoryModel::item_array_t::iterator find_item_iter_by_uuid(LLInventoryModel::item_array_t& items, const LLUUID& id) +LLInventoryModel::item_array_t::iterator LLInventoryModel::findItemIterByUUID(LLInventoryModel::item_array_t& items, const LLUUID& id) { - LLInventoryModel::item_array_t::iterator result = items.end(); + LLInventoryModel::item_array_t::iterator curr_item = items.begin(); - for (LLInventoryModel::item_array_t::iterator i = items.begin(); i != items.end(); ++i) + while (curr_item != items.end()) { - if ((*i)->getUUID() == id) + if ((*curr_item)->getUUID() == id) { - result = i; break; } + ++curr_item; } - return result; + return curr_item; } // static // * @param[in, out] items - vector with items to be updated. It should be sorted in a right way // * before calling this method. // * @param src_item_id - LLUUID of inventory item to be moved in new position -// * @param dest_item_id - LLUUID of inventory item before which source item should be placed. -void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id) +// * @param dest_item_id - LLUUID of inventory item before (or after) which source item should +// * be placed. +// * @param insert_before - bool indicating if src_item_id should be placed before or after +// * dest_item_id. Default is true. +void LLInventoryModel::updateItemsOrder(LLInventoryModel::item_array_t& items, const LLUUID& src_item_id, const LLUUID& dest_item_id, bool insert_before) { - LLInventoryModel::item_array_t::iterator it_src = find_item_iter_by_uuid(items, src_item_id); - LLInventoryModel::item_array_t::iterator it_dest = find_item_iter_by_uuid(items, dest_item_id); + LLInventoryModel::item_array_t::iterator it_src = findItemIterByUUID(items, src_item_id); + LLInventoryModel::item_array_t::iterator it_dest = findItemIterByUUID(items, dest_item_id); - if (it_src == items.end() || it_dest == items.end()) return; + // If one of the passed UUID is not in the item list, bail out + if ((it_src == items.end()) || (it_dest == items.end())) + return; + // Erase the source element from the list, keep a copy before erasing. LLViewerInventoryItem* src_item = *it_src; items.erase(it_src); - // target iterator can not be valid because the container was changed, so update it. - it_dest = find_item_iter_by_uuid(items, dest_item_id); - items.insert(it_dest, src_item); + // Note: Target iterator is not valid anymore because the container was changed, so update it. + it_dest = findItemIterByUUID(items, dest_item_id); + + // Go to the next element if one wishes to insert after the dest element + if (!insert_before) + { + ++it_dest; + } + + // Reinsert the source item in the right place + if (it_dest != items.end()) + { + items.insert(it_dest, src_item); + } + else + { + // Append to the list if it_dest reached the end + items.push_back(src_item); + } } //* @param[in] items vector of items in order to be saved. +/* void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& items) { int sortField = 0; @@ -2890,7 +3533,7 @@ void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& item notifyObservers(); } - +*/ // See also LLInventorySort where landmarks in the Favorites folder are sorted. class LLViewerInventoryItemSort { @@ -2906,14 +3549,15 @@ public: * * @param[in, out] items - array of items, not sorted. */ -static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) -{ - static LLViewerInventoryItemSort sort_functor; - std::sort(items.begin(), items.end(), sort_functor); -} +//static void rearrange_item_order_by_sort_field(LLInventoryModel::item_array_t& items) +//{ +// static LLViewerInventoryItemSort sort_functor; +// std::sort(items.begin(), items.end(), sort_functor); +//} // * @param source_item_id - LLUUID of the source item to be moved into new position // * @param target_item_id - LLUUID of the target item before which source item should be placed. +/* void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, const LLUUID& target_item_id) { LLInventoryModel::cat_array_t cats; @@ -2930,43 +3574,342 @@ void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, saveItemsOrder(items); } - +*/ //---------------------------------------------------------------------------- // *NOTE: DEBUG functionality void LLInventoryModel::dumpInventory() const { - llinfos << "\nBegin Inventory Dump\n**********************:" << llendl; - llinfos << "mCategory[] contains " << mCategoryMap.size() << " items." << llendl; + LL_INFOS() << "\nBegin Inventory Dump\n**********************:" << LL_ENDL; + LL_INFOS() << "mCategory[] contains " << mCategoryMap.size() << " items." << LL_ENDL; for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) { const LLViewerInventoryCategory* cat = cit->second; if(cat) { - llinfos << " " << cat->getUUID() << " '" << cat->getName() << "' " + LL_INFOS() << " " << cat->getUUID() << " '" << cat->getName() << "' " << cat->getVersion() << " " << cat->getDescendentCount() - << llendl; + << LL_ENDL; } else { - llinfos << " NULL!" << llendl; + LL_INFOS() << " NULL!" << LL_ENDL; } } - llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl; + LL_INFOS() << "mItemMap[] contains " << mItemMap.size() << " items." << LL_ENDL; for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit) { const LLViewerInventoryItem* item = iit->second; if(item) { - llinfos << " " << item->getUUID() << " " - << item->getName() << llendl; + LL_INFOS() << " " << item->getUUID() << " " + << item->getName() << LL_ENDL; } else { - llinfos << " NULL!" << llendl; + LL_INFOS() << " NULL!" << LL_ENDL; } } - llinfos << "\n**********************\nEnd Inventory Dump" << llendl; + LL_INFOS() << "\n**********************\nEnd Inventory Dump" << LL_ENDL; +} + +// Do various integrity checks on model, logging issues found and +// returning an overall good/bad flag. +bool LLInventoryModel::validate() const +{ + bool valid = true; + + if (getRootFolderID().isNull()) + { + LL_WARNS() << "no root folder id" << LL_ENDL; + valid = false; + } + if (getLibraryRootFolderID().isNull()) + { + LL_WARNS() << "no root folder id" << LL_ENDL; + valid = false; + } + + if (mCategoryMap.size() + 1 != mParentChildCategoryTree.size()) + { + // ParentChild should be one larger because of the special entry for null uuid. + LL_INFOS() << "unexpected sizes: cat map size " << mCategoryMap.size() + << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL; + valid = false; + } + S32 cat_lock = 0; + S32 item_lock = 0; + S32 desc_unknown_count = 0; + S32 version_unknown_count = 0; + for(cat_map_t::const_iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit) + { + const LLUUID& cat_id = cit->first; + const LLViewerInventoryCategory *cat = cit->second; + if (!cat) + { + LL_WARNS() << "invalid cat" << LL_ENDL; + valid = false; + continue; + } + if (cat_id != cat->getUUID()) + { + LL_WARNS() << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL; + valid = false; + } + + if (cat->getParentUUID().isNull()) + { + if (cat_id != getRootFolderID() && cat_id != getLibraryRootFolderID()) + { + LL_WARNS() << "cat " << cat_id << " has no parent, but is not root (" + << getRootFolderID() << ") or library root (" + << getLibraryRootFolderID() << ")" << LL_ENDL; + } + } + cat_array_t* cats; + item_array_t* items; + getDirectDescendentsOf(cat_id,cats,items); + if (!cats || !items) + { + LL_WARNS() << "invalid direct descendents for " << cat_id << LL_ENDL; + valid = false; + continue; + } + if (cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) + { + desc_unknown_count++; + } + else if (cats->size() + items->size() != cat->getDescendentCount()) + { + LL_WARNS() << "invalid desc count for " << cat_id << " name [" << cat->getName() + << "] parent " << cat->getParentUUID() + << " cached " << cat->getDescendentCount() + << " expected " << cats->size() << "+" << items->size() + << "=" << cats->size() +items->size() << LL_ENDL; + valid = false; + } + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + version_unknown_count++; + } + if (mCategoryLock.count(cat_id)) + { + cat_lock++; + } + if (mItemLock.count(cat_id)) + { + item_lock++; + } + for (S32 i = 0; i<items->size(); i++) + { + LLViewerInventoryItem *item = items->at(i); + + if (!item) + { + LL_WARNS() << "null item at index " << i << " for cat " << cat_id << LL_ENDL; + valid = false; + continue; + } + + const LLUUID& item_id = item->getUUID(); + + if (item->getParentUUID() != cat_id) + { + LL_WARNS() << "wrong parent for " << item_id << " found " + << item->getParentUUID() << " expected " << cat_id + << LL_ENDL; + valid = false; + } + + + // Entries in items and mItemMap should correspond. + item_map_t::const_iterator it = mItemMap.find(item_id); + if (it == mItemMap.end()) + { + LL_WARNS() << "item " << item_id << " found as child of " + << cat_id << " but not in top level mItemMap" << LL_ENDL; + valid = false; + } + else + { + LLViewerInventoryItem *top_item = it->second; + if (top_item != item) + { + LL_WARNS() << "item mismatch, item_id " << item_id + << " top level entry is different, uuid " << top_item->getUUID() << LL_ENDL; + } + } + + // Topmost ancestor should be root or library. + LLUUID topmost_ancestor_id; + bool found = getObjectTopmostAncestor(item_id, topmost_ancestor_id); + if (!found) + { + LL_WARNS() << "unable to find topmost ancestor for " << item_id << LL_ENDL; + valid = false; + } + else + { + if (topmost_ancestor_id != getRootFolderID() && + topmost_ancestor_id != getLibraryRootFolderID()) + { + LL_WARNS() << "unrecognized top level ancestor for " << item_id + << " got " << topmost_ancestor_id + << " expected " << getRootFolderID() + << " or " << getLibraryRootFolderID() << LL_ENDL; + valid = false; + } + } + } + + // Does this category appear as a child of its supposed parent? + const LLUUID& parent_id = cat->getParentUUID(); + if (!parent_id.isNull()) + { + cat_array_t* cats; + item_array_t* items; + getDirectDescendentsOf(parent_id,cats,items); + if (!cats) + { + LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() + << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL; + valid = false; + } + else + { + bool found = false; + for (S32 i = 0; i<cats->size(); i++) + { + LLViewerInventoryCategory *kid_cat = cats->at(i); + if (kid_cat == cat) + { + found = true; + break; + } + } + if (!found) + { + LL_WARNS() << "cat " << cat_id << " name [" << cat->getName() + << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL; + } + } + } + } + + for(item_map_t::const_iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit) + { + const LLUUID& item_id = iit->first; + LLViewerInventoryItem *item = iit->second; + if (item->getUUID() != item_id) + { + LL_WARNS() << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL; + valid = false; + } + + const LLUUID& parent_id = item->getParentUUID(); + if (parent_id.isNull()) + { + LL_WARNS() << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL; + } + else + { + cat_array_t* cats; + item_array_t* items; + getDirectDescendentsOf(parent_id,cats,items); + if (!items) + { + LL_WARNS() << "item " << item_id << " name [" << item->getName() + << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL; + } + else + { + bool found = false; + for (S32 i=0; i<items->size(); ++i) + { + if (items->at(i) == item) + { + found = true; + break; + } + } + if (!found) + { + LL_WARNS() << "item " << item_id << " name [" << item->getName() + << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL; + } + } + + } + // Link checking + if (item->getIsLinkType()) + { + const LLUUID& link_id = item->getUUID(); + const LLUUID& target_id = item->getLinkedUUID(); + LLViewerInventoryItem *target_item = getItem(target_id); + LLViewerInventoryCategory *target_cat = getCategory(target_id); + // Linked-to UUID should have back reference to this link. + if (!hasBacklinkInfo(link_id, target_id)) + { + LL_WARNS() << "link " << item->getUUID() << " type " << item->getActualType() + << " missing backlink info at target_id " << target_id + << LL_ENDL; + } + // Links should have referents. + if (item->getActualType() == LLAssetType::AT_LINK && !target_item) + { + LL_WARNS() << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL; + } + else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER && !target_cat) + { + LL_WARNS() << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL; + } + if (target_item && target_item->getIsLinkType()) + { + LL_WARNS() << "link " << item->getName() << " references a link item " + << target_item->getName() << " " << target_item->getUUID() << LL_ENDL; + } + + // Links should not have backlinks. + std::pair<backlink_mmap_t::const_iterator, backlink_mmap_t::const_iterator> range = mBacklinkMMap.equal_range(link_id); + if (range.first != range.second) + { + LL_WARNS() << "Link item " << item->getName() << " has backlinks!" << LL_ENDL; + } + } + else + { + // Check the backlinks of a non-link item. + const LLUUID& target_id = item->getUUID(); + std::pair<backlink_mmap_t::const_iterator, backlink_mmap_t::const_iterator> range = mBacklinkMMap.equal_range(target_id); + for (backlink_mmap_t::const_iterator it = range.first; it != range.second; ++it) + { + const LLUUID& link_id = it->second; + LLViewerInventoryItem *link_item = getItem(link_id); + if (!link_item || !link_item->getIsLinkType()) + { + LL_WARNS() << "invalid backlink from target " << item->getName() << " to " << link_id << LL_ENDL; + } + } + } + } + + if (cat_lock > 0 || item_lock > 0) + { + LL_INFOS() << "Found locks on some categories: sub-cat arrays " + << cat_lock << ", item arrays " << item_lock << LL_ENDL; + } + if (desc_unknown_count != 0) + { + LL_INFOS() << "Found " << desc_unknown_count << " cats with unknown descendent count" << LL_ENDL; + } + if (version_unknown_count != 0) + { + LL_INFOS() << "Found " << version_unknown_count << " cats with unknown version" << LL_ENDL; + } + + LL_INFOS() << "Validate done, valid = " << (U32) valid << LL_ENDL; + + return valid; } ///---------------------------------------------------------------------------- @@ -2974,7 +3917,7 @@ void LLInventoryModel::dumpInventory() const ///---------------------------------------------------------------------------- -/* +#if 0 BOOL decompress_file(const char* src_filename, const char* dst_filename) { BOOL rv = FALSE; @@ -3013,4 +3956,177 @@ BOOL decompress_file(const char* src_filename, const char* dst_filename) if(dst != NULL) fclose(dst); return rv; } -*/ +#endif + + +///---------------------------------------------------------------------------- +/// Class LLInventoryModel::FetchItemHttpHandler +///---------------------------------------------------------------------------- + +LLInventoryModel::FetchItemHttpHandler::FetchItemHttpHandler(const LLSD & request_sd) + : LLCore::HttpHandler(), + mRequestSD(request_sd) +{} + +LLInventoryModel::FetchItemHttpHandler::~FetchItemHttpHandler() +{} + +void LLInventoryModel::FetchItemHttpHandler::onCompleted(LLCore::HttpHandle handle, + LLCore::HttpResponse * response) +{ + do // Single-pass do-while used for common exit handling + { + LLCore::HttpStatus status(response->getStatus()); + // status = LLCore::HttpStatus(404); // Dev tool to force error handling + if (! status) + { + processFailure(status, response); + break; // Goto common exit + } + + LLCore::BufferArray * body(response->getBody()); + // body = NULL; // Dev tool to force error handling + if (! body || ! body->size()) + { + LL_WARNS(LOG_INV) << "Missing data in inventory item query." << LL_ENDL; + processFailure("HTTP response for inventory item query missing body", response); + break; // Goto common exit + } + + // body->write(0, "Garbage Response", 16); // Dev tool to force error handling + LLSD body_llsd; + if (! LLCoreHttpUtil::responseToLLSD(response, true, body_llsd)) + { + // INFOS-level logging will occur on the parsed failure + processFailure("HTTP response for inventory item query has malformed LLSD", response); + break; // Goto common exit + } + + // Expect top-level structure to be a map + // body_llsd = LLSD::emptyArray(); // Dev tool to force error handling + if (! body_llsd.isMap()) + { + processFailure("LLSD response for inventory item not a map", response); + break; // Goto common exit + } + + // Check for 200-with-error failures + // + // Original Responder-based serivce model didn't check for these errors. + // It may be more robust to ignore this condition. With aggregated requests, + // an error in one inventory item might take down the entire request. + // So if this instead broke up the aggregated items into single requests, + // maybe that would make progress. Or perhaps there's structured information + // that can tell us what went wrong. Need to dig into this and firm up + // the API. + // + // body_llsd["error"] = LLSD::emptyMap(); // Dev tool to force error handling + // body_llsd["error"]["identifier"] = "Development"; + // body_llsd["error"]["message"] = "You left development code in the viewer"; + if (body_llsd.has("error")) + { + processFailure("Inventory application error (200-with-error)", response); + break; // Goto common exit + } + + // Okay, process data if possible + processData(body_llsd, response); + } + while (false); + + // Must delete on completion. + delete this; +} + +void LLInventoryModel::FetchItemHttpHandler::processData(LLSD & content, LLCore::HttpResponse * response) +{ + start_new_inventory_observer(); + +#if 0 + LLUUID agent_id; + agent_id = content["agent_id"].asUUID(); + if (agent_id != gAgent.getID()) + { + LL_WARNS(LOG_INV) << "Got a inventory update for the wrong agent: " << agent_id + << LL_ENDL; + return; + } +#endif + + LLInventoryModel::item_array_t items; + LLInventoryModel::update_map_t update; + LLUUID folder_id; + LLSD content_items(content["items"]); + const S32 count(content_items.size()); + + // Does this loop ever execute more than once? + for (S32 i(0); i < count; ++i) + { + LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem; + titem->unpackMessage(content_items[i]); + + LL_DEBUGS(LOG_INV) << "ItemHttpHandler::httpSuccess item id: " + << titem->getUUID() << LL_ENDL; + items.push_back(titem); + + // examine update for changes. + LLViewerInventoryItem * itemp(gInventory.getItem(titem->getUUID())); + + if (itemp) + { + if (titem->getParentUUID() == itemp->getParentUUID()) + { + update[titem->getParentUUID()]; + } + else + { + ++update[titem->getParentUUID()]; + --update[itemp->getParentUUID()]; + } + } + else + { + ++update[titem->getParentUUID()]; + } + + if (folder_id.isNull()) + { + folder_id = titem->getParentUUID(); + } + } + + // as above, this loop never seems to loop more than once per call + U32 changes(0U); + for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) + { + changes |= gInventory.updateItem(*it); + } + // *HUH: Have computed 'changes', nothing uses it. + + gInventory.notifyObservers(); + gViewerWindow->getWindow()->decBusyCount(); +} + + +void LLInventoryModel::FetchItemHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::HttpResponse * response) +{ + const std::string & ct(response->getContentType()); + LL_WARNS(LOG_INV) << "Inventory item fetch failure\n" + << "[Status: " << status.toTerseString() << "]\n" + << "[Reason: " << status.toString() << "]\n" + << "[Content-type: " << ct << "]\n" + << "[Content (abridged): " + << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; + gInventory.notifyObservers(); +} + +void LLInventoryModel::FetchItemHttpHandler::processFailure(const char * const reason, LLCore::HttpResponse * response) +{ + LL_WARNS(LOG_INV) << "Inventory item fetch failure\n" + << "[Status: internal error]\n" + << "[Reason: " << reason << "]\n" + << "[Content (abridged): " + << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; + gInventory.notifyObservers(); +} + |