llinventorymodel.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 
00034 #include "llinventorymodel.h"
00035 
00036 #include "llassetstorage.h"
00037 #include "llcrc.h"
00038 #include "lldir.h"
00039 #include "llsys.h"
00040 #include "llxfermanager.h"
00041 #include "message.h"
00042 
00043 #include "llagent.h"
00044 #include "llfloater.h"
00045 #include "llfocusmgr.h"
00046 #include "llinventoryview.h"
00047 #include "llviewerinventory.h"
00048 #include "llviewermessage.h"
00049 #include "llviewerwindow.h"
00050 #include "llviewerregion.h"
00051 #include "llappviewer.h"
00052 #include "lldbstrings.h"
00053 #include "llviewerstats.h"
00054 #include "llmutelist.h"
00055 #include "llnotify.h"
00056 #include "llcallbacklist.h"
00057 #include "llpreview.h"
00058 #include "llviewercontrol.h"
00059 #include "llsdutil.h"
00060 #include <deque>
00061 
00062 //#define DIFF_INVENTORY_FILES
00063 #ifdef DIFF_INVENTORY_FILES
00064 #include "process.h"
00065 #endif
00066 
00067 BOOL LLInventoryModel::sBackgroundFetchActive = FALSE;
00068 BOOL LLInventoryModel::sAllFoldersFetched = FALSE;
00069 BOOL LLInventoryModel::sFullFetchStarted = FALSE;
00070 S32  LLInventoryModel::sNumFetchRetries = 0;
00071 F32  LLInventoryModel::sMinTimeBetweenFetches = 0.3f;
00072 F32  LLInventoryModel::sMaxTimeBetweenFetches = 10.f;
00073 BOOL LLInventoryModel::sTimelyFetchPending = FALSE;
00074 LLFrameTimer LLInventoryModel::sFetchTimer;
00075 LLInventoryModel::cat_map_t LLInventoryModel::sBulkFetchMap;
00076 S16 LLInventoryModel::sBulkFetchCount = 0;
00077 
00078 // RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue
00079 static std::deque<LLUUID> sFetchQueue;
00080 
00084 
00085 //BOOL decompress_file(const char* src_filename, const char* dst_filename);
00086 const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f;
00087 const S32 MAX_FETCH_RETRIES = 5;
00088 const char CACHE_FORMAT_STRING[] = "%s.inv"; 
00089 const char* NEW_CATEGORY_NAME = "New Folder";
00090 const char* NEW_CATEGORY_NAMES[LLAssetType::AT_COUNT] =
00091 {
00092         "Textures",                     // AT_TEXTURE
00093         "Sounds",                       // AT_SOUND
00094         "Calling Cards",        // AT_CALLINGCARD
00095         "Landmarks",            // AT_LANDMARK
00096         "Scripts",                      // AT_SCRIPT (deprecated?)
00097         "Clothing",                     // AT_CLOTHING
00098         "Objects",                      // AT_OBJECT
00099         "Notecards",            // AT_NOTECARD
00100         "New Folder",           // AT_CATEGORY
00101         "Inventory",            // AT_ROOT_CATEGORY
00102         "Scripts",                      // AT_LSL_TEXT
00103         "Scripts",                      // AT_LSL_BYTECODE
00104         "Uncompressed Images",  // AT_TEXTURE_TGA
00105         "Body Parts",           // AT_BODYPART
00106         "Trash",                        // AT_TRASH
00107         "Photo Album",          // AT_SNAPSHOT_CATEGORY
00108         "Lost And Found",       // AT_LOST_AND_FOUND
00109         "Uncompressed Sounds",  // AT_SOUND_WAV
00110         "Uncompressed Images",  // AT_IMAGE_TGA
00111         "Uncompressed Images",  // AT_IMAGE_JPEG
00112         "Animations",           // AT_ANIMATION
00113         "Gestures",                     // AT_GESTURE
00114 };
00115 
00116 struct InventoryIDPtrLess
00117 {
00118         bool operator()(const LLViewerInventoryCategory* i1, const LLViewerInventoryCategory* i2) const
00119         {
00120                 return (i1->getUUID() < i2->getUUID());
00121         }
00122 };
00123 
00124 class LLCanCache : public LLInventoryCollectFunctor 
00125 {
00126 public:
00127         LLCanCache(LLInventoryModel* model) : mModel(model) {}
00128         virtual ~LLCanCache() {}
00129         virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
00130 protected:
00131         LLInventoryModel* mModel;
00132         std::set<LLUUID> mCachedCatIDs;
00133 };
00134 
00135 bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
00136 {
00137         bool rv = false;
00138         if(item)
00139         {
00140                 if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end())
00141                 {
00142                         rv = true;
00143                 }
00144         }
00145         else if(cat)
00146         {
00147                 // HACK: downcast
00148                 LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat;
00149                 if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
00150                 {
00151                         S32 descendents_server = c->getDescendentCount();
00152                         LLInventoryModel::cat_array_t* cats;
00153                         LLInventoryModel::item_array_t* items;
00154                         mModel->getDirectDescendentsOf(
00155                                 c->getUUID(),
00156                                 cats,
00157                                 items);
00158                         S32 descendents_actual = 0;
00159                         if(cats && items)
00160                         {
00161                                 descendents_actual = cats->count() + items->count();
00162                         }
00163                         if(descendents_server == descendents_actual)
00164                         {
00165                                 mCachedCatIDs.insert(c->getUUID());
00166                                 rv = true;
00167                         }
00168                 }
00169         }
00170         return rv;
00171 }
00172 
00176 
00177 // global for the agent inventory.
00178 LLInventoryModel gInventory;
00179 
00180 // Default constructor
00181 LLInventoryModel::LLInventoryModel() :
00182         mModifyMask(LLInventoryObserver::ALL),
00183         mLastItem(NULL),
00184         mIsAgentInvUsable(false)
00185 {
00186 }
00187 
00188 // Destroys the object
00189 LLInventoryModel::~LLInventoryModel()
00190 {
00191         empty();
00192         for (observer_list_t::iterator iter = mObservers.begin();
00193                  iter != mObservers.end(); ++iter)
00194         {
00195                 delete *iter;
00196         }
00197 }
00198 
00199 // This is a convenience function to check if one object has a parent
00200 // chain up to the category specified by UUID.
00201 BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
00202                                                                                         const LLUUID& cat_id)
00203 {
00204         LLInventoryObject* obj = getObject(obj_id);
00205         while(obj)
00206         {
00207                 const LLUUID& parent_id = obj->getParentUUID();
00208                 if( parent_id.isNull() )
00209                 {
00210                         return FALSE;
00211                 }
00212                 if(parent_id == cat_id)
00213                 {
00214                         return TRUE;
00215                 }
00216                 // Since we're scanning up the parents, we only need to check
00217                 // in the category list.
00218                 obj = getCategory(parent_id);
00219         }
00220         return FALSE;
00221 }
00222 
00223 // Get the object by id. Returns NULL if not found.
00224 LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const
00225 {
00226         LLViewerInventoryCategory* cat = getCategory(id);
00227         if (cat)
00228         {
00229                 return cat;
00230         }
00231         LLViewerInventoryItem* item = getItem(id);
00232         if (item)
00233         {
00234                 return item;
00235         }
00236         return NULL;
00237 }
00238 
00239 // Get the item by id. Returns NULL if not found.
00240 LLViewerInventoryItem* LLInventoryModel::getItem(const LLUUID& id) const
00241 {
00242         LLViewerInventoryItem* item = NULL;
00243         if(mLastItem.notNull() && mLastItem->getUUID() == id)
00244         {
00245                 item = mLastItem;
00246         }
00247         else
00248         {
00249                 item_map_t::const_iterator iter = mItemMap.find(id);
00250                 if (iter != mItemMap.end())
00251                 {
00252                         item = iter->second;
00253                         mLastItem = item;
00254                 }
00255         }
00256         return item;
00257 }
00258 
00259 // Get the category by id. Returns NULL if not found
00260 LLViewerInventoryCategory* LLInventoryModel::getCategory(const LLUUID& id) const
00261 {
00262         LLViewerInventoryCategory* category = NULL;
00263         cat_map_t::const_iterator iter = mCategoryMap.find(id);
00264         if (iter != mCategoryMap.end())
00265         {
00266                 category = iter->second;
00267         }
00268         return category;
00269 }
00270 
00271 S32 LLInventoryModel::getItemCount() const
00272 {
00273         return mItemMap.size();
00274 }
00275 
00276 S32 LLInventoryModel::getCategoryCount() const
00277 {
00278         return mCategoryMap.size();
00279 }
00280 
00281 // Return the direct descendents of the id provided. The array
00282 // provided points straight into the guts of this object, and
00283 // should only be used for read operations, since modifications
00284 // may invalidate the internal state of the inventory. Set passed
00285 // in values to NULL if the call fails.
00286 void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
00287                                                                                           cat_array_t*& categories,
00288                                                                                           item_array_t*& items) const
00289 {
00290         categories = get_ptr_in_map(mParentChildCategoryTree, cat_id);
00291         items = get_ptr_in_map(mParentChildItemTree, cat_id);
00292 }
00293 
00294 // findCategoryUUIDForType() returns the uuid of the category that
00295 // specifies 'type' as what it defaults to containing. The category is
00296 // not necessarily only for that type. *NOTE: This will create a new
00297 // inventory category on the fly if one does not exist.
00298 LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t, bool create_folder)
00299 {
00300         LLUUID rv = findCatUUID(t);
00301         if(rv.isNull() && isInventoryUsable() && create_folder)
00302         {
00303                 LLUUID root_id = gAgent.getInventoryRootID();
00304                 if(root_id.notNull())
00305                 {
00306                         rv = createNewCategory(root_id, t, NULL);
00307                 }
00308         }
00309         return rv;
00310 }
00311 
00312 // Internal method which looks for a category with the specified
00313 // preferred type. Returns LLUUID::null if not found.
00314 LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type)
00315 {
00316         LLUUID root_id = gAgent.getInventoryRootID();
00317         if(LLAssetType::AT_CATEGORY == preferred_type)
00318         {
00319                 return root_id;
00320         }
00321         if(root_id.notNull())
00322         {
00323                 cat_array_t* cats = NULL;
00324                 cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
00325                 if(cats)
00326                 {
00327                         S32 count = cats->count();
00328                         for(S32 i = 0; i < count; ++i)
00329                         {
00330                                 if(cats->get(i)->getPreferredType() == preferred_type)
00331                                 {
00332                                         return cats->get(i)->getUUID();
00333                                 }
00334                         }
00335                 }
00336         }
00337         return LLUUID::null;
00338 }
00339 
00340 // Convenience function to create a new category. You could call
00341 // updateCategory() with a newly generated UUID category, but this
00342 // version will take care of details like what the name should be
00343 // based on preferred type. Returns the UUID of the new category.
00344 LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
00345                                                                                    LLAssetType::EType preferred_type,
00346                                                                                    const LLString& pname)
00347 {
00348         LLUUID id;
00349         if(!isInventoryUsable())
00350         {
00351                 llwarns << "Inventory is broken." << llendl;
00352                 return id;
00353         }
00354 
00355         id.generate();
00356         LLString name = pname;
00357         if(!pname.empty())
00358         {
00359                 name.assign(pname);
00360         }
00361         else if((preferred_type >= LLAssetType::AT_TEXTURE) &&
00362                         (preferred_type < LLAssetType::AT_COUNT))
00363         {
00364                 name.assign(NEW_CATEGORY_NAMES[preferred_type]);
00365         }
00366         else
00367         {
00368                 name.assign(NEW_CATEGORY_NAME);
00369         }
00370 
00371         // Add the category to the internal representation
00372         LLPointer<LLViewerInventoryCategory> cat =
00373                 new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
00374         cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
00375         cat->setDescendentCount(0);
00376         LLCategoryUpdate update(cat->getParentUUID(), 1);
00377         accountForUpdate(update);
00378         updateCategory(cat);
00379 
00380         // Create the category on the server. We do this to prevent people
00381         // from munging their protected folders.
00382         LLMessageSystem* msg = gMessageSystem;
00383         msg->newMessage("CreateInventoryFolder");
00384         msg->nextBlock("AgentData");
00385         msg->addUUID("AgentID", gAgent.getID());
00386         msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
00387         msg->nextBlock("FolderData");
00388         cat->packMessage(msg);
00389         gAgent.sendReliableMessage();
00390 
00391         // return the folder id of the newly created folder
00392         return id;
00393 }
00394 
00395 // Starting with the object specified, add it's descendents to the
00396 // array provided, but do not add the inventory object specified by
00397 // id. There is no guaranteed order. Neither array will be erased
00398 // before adding objects to it. Do not store a copy of the pointers
00399 // collected - use them, and collect them again later if you need to
00400 // reference the same objects.
00401 
00402 class LLAlwaysCollect : public LLInventoryCollectFunctor
00403 {
00404 public:
00405         virtual ~LLAlwaysCollect() {}
00406         virtual bool operator()(LLInventoryCategory* cat,
00407                                                         LLInventoryItem* item)
00408         {
00409                 return TRUE;
00410         }
00411 };
00412 
00413 void LLInventoryModel::collectDescendents(const LLUUID& id,
00414                                                                                   cat_array_t& cats,
00415                                                                                   item_array_t& items,
00416                                                                                   BOOL include_trash)
00417 {
00418         LLAlwaysCollect always;
00419         collectDescendentsIf(id, cats, items, include_trash, always);
00420 }
00421 
00422 void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
00423                                                                                         cat_array_t& cats,
00424                                                                                         item_array_t& items,
00425                                                                                         BOOL include_trash,
00426                                                                                         LLInventoryCollectFunctor& add)
00427 {
00428         // Start with categories
00429         if(!include_trash)
00430         {
00431                 LLUUID trash_id(findCatUUID(LLAssetType::AT_TRASH));
00432                 if(trash_id.notNull() && (trash_id == id))
00433                         return;
00434         }
00435         cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
00436         if(cat_array)
00437         {
00438                 S32 count = cat_array->count();
00439                 for(S32 i = 0; i < count; ++i)
00440                 {
00441                         LLViewerInventoryCategory* cat = cat_array->get(i);
00442                         if(add(cat,NULL))
00443                         {
00444                                 cats.put(cat);
00445                         }
00446                         collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add);
00447                 }
00448         }
00449 
00450         // Move onto items
00451         LLViewerInventoryItem* item = NULL;
00452         item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
00453         if(item_array)
00454         {
00455                 S32 count = item_array->count();
00456                 for(S32 i = 0; i < count; ++i)
00457                 {
00458                         item = item_array->get(i);
00459                         if(add(NULL, item))
00460                         {
00461                                 items.put(item);
00462                         }
00463                 }
00464         }
00465 }
00466 
00467 // Generates a string containing the path to the item specified by
00468 // item_id.
00469 void LLInventoryModel::appendPath(const LLUUID& id, LLString& path)
00470 {
00471         LLString temp;
00472         LLInventoryObject* obj = getObject(id);
00473         LLUUID parent_id;
00474         if(obj) parent_id = obj->getParentUUID();
00475         LLString forward_slash("/");
00476         while(obj)
00477         {
00478                 obj = getCategory(parent_id);
00479                 if(obj)
00480                 {
00481                         temp.assign(forward_slash + obj->getName() + temp);
00482                         parent_id = obj->getParentUUID();
00483                 }
00484         }
00485         path.append(temp);
00486 }
00487 
00488 bool LLInventoryModel::isInventoryUsable()
00489 {
00490         bool result = false;
00491         if(gAgent.getInventoryRootID().notNull() && mIsAgentInvUsable)
00492         {
00493                 result = true;
00494         }
00495         return result;  
00496 }
00497 
00498 // Calling this method with an inventory item will either change an
00499 // existing item with a matching item_id, or will add the item to the
00500 // current inventory.
00501 U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
00502 {
00503         U32 mask = LLInventoryObserver::NONE;
00504         if(item->getUUID().isNull())
00505         {
00506                 return mask;
00507         }
00508 
00509         if(!isInventoryUsable())
00510         {
00511                 llwarns << "Inventory is broken." << llendl;
00512                 return mask;
00513         }
00514 
00515         LLViewerInventoryItem* old_item = getItem(item->getUUID());
00516         if(old_item)
00517         {
00518                 // We already have an old item, modify it's values
00519                 LLUUID old_parent_id = old_item->getParentUUID();
00520                 LLUUID new_parent_id = item->getParentUUID();
00521                 if(old_parent_id != new_parent_id)
00522                 {
00523                         // need to update the parent-child tree
00524                         item_array_t* item_array;
00525                         item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id);
00526                         if(item_array)
00527                         {
00528                                 item_array->removeObj(old_item);
00529                         }
00530                         item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id);
00531                         if(item_array)
00532                         {
00533                                 item_array->put(old_item);
00534                         }
00535                         mask |= LLInventoryObserver::STRUCTURE;
00536                 }
00537                 if(old_item->getName() != item->getName())
00538                 {
00539                         mask |= LLInventoryObserver::LABEL;
00540                 }
00541                 old_item->copyViewerItem(item);
00542                 mask |= LLInventoryObserver::INTERNAL;
00543         }
00544         else
00545         {
00546                 // Simply add this item
00547                 LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
00548                 addItem(new_item);
00549 
00550                 if(item->getParentUUID().isNull())
00551                 {
00552                         LLUUID category_id = findCategoryUUIDForType(new_item->getType());
00553                         new_item->setParent(category_id);
00554                         item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id);
00555                         if( item_array )
00556                         {
00557                                 // *FIX: bit of a hack to call update server from here...
00558                                 new_item->updateServer(TRUE);
00559                                 item_array->put(new_item);
00560                         }
00561                         else
00562                         {
00563                                 llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl;
00564                         }
00565                 }
00566                 else
00567                 {
00568                         // *NOTE: The general scheme is that if every byte of the
00569                         // uuid is 0, except for the last one or two,the use the
00570                         // last two bytes of the parent id, and match that up
00571                         // against the type. For now, we're only worried about
00572                         // lost & found.
00573                         LLUUID parent_id = item->getParentUUID();
00574                         if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID)
00575                         {
00576                                 parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
00577                                 new_item->setParent(parent_id);
00578                         }
00579                         item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
00580                         if(item_array)
00581                         {
00582                                 item_array->put(new_item);
00583                         }
00584                         else
00585                         {
00586                                 // Whoops! No such parent, make one.
00587                                 llinfos << "Lost item: " << new_item->getUUID() << " - "
00588                                                 << new_item->getName() << llendl;
00589                                 parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
00590                                 new_item->setParent(parent_id);
00591                                 item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
00592                                 if(item_array)
00593                                 {
00594                                         // *FIX: bit of a hack to call update server from
00595                                         // here...
00596                                         new_item->updateServer(TRUE);
00597                                         item_array->put(new_item);
00598                                 }
00599                                 else
00600                                 {
00601                                         llwarns << "Lost and found Not there!!" << llendl;
00602                                 }
00603                         }
00604                 }
00605                 mask |= LLInventoryObserver::ADD;
00606         }
00607         if(item->getType() == LLAssetType::AT_CALLINGCARD)
00608         {
00609                 mask |= LLInventoryObserver::CALLING_CARD;
00610         }
00611         addChangedMask(mask, item->getUUID());
00612         return mask;
00613 }
00614 
00615 // Calling this method with an inventory category will either change
00616 // an existing item with the matching id, or it will add the category.
00617 void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
00618 {
00619         if(cat->getUUID().isNull())
00620         {
00621                 return;
00622         }
00623 
00624         if(!isInventoryUsable())
00625         {
00626                 llwarns << "Inventory is broken." << llendl;
00627                 return;
00628         }
00629 
00630         LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
00631         if(old_cat)
00632         {
00633                 // We already have an old category, modify it's values
00634                 U32 mask = LLInventoryObserver::NONE;
00635                 LLUUID old_parent_id = old_cat->getParentUUID();
00636                 LLUUID new_parent_id = cat->getParentUUID();
00637                 if(old_parent_id != new_parent_id)
00638                 {
00639                         // need to update the parent-child tree
00640                         cat_array_t* cat_array;
00641                         cat_array = get_ptr_in_map(mParentChildCategoryTree, old_parent_id);
00642                         if(cat_array)
00643                         {
00644                                 cat_array->removeObj(old_cat);
00645                         }
00646                         cat_array = get_ptr_in_map(mParentChildCategoryTree, new_parent_id);
00647                         if(cat_array)
00648                         {
00649                                 cat_array->put(old_cat);
00650                         }
00651                         mask |= LLInventoryObserver::STRUCTURE;
00652                 }
00653                 if(old_cat->getName() != cat->getName())
00654                 {
00655                         mask |= LLInventoryObserver::LABEL;
00656                 }
00657                 old_cat->copyViewerCategory(cat);
00658                 addChangedMask(mask, cat->getUUID());
00659         }
00660         else
00661         {
00662                 // add this category
00663                 LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat->getParentUUID());
00664                 new_cat->copyViewerCategory(cat);
00665                 addCategory(new_cat);
00666 
00667                 // make sure this category is correctly referenced by it's parent.
00668                 cat_array_t* cat_array;
00669                 cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
00670                 if(cat_array)
00671                 {
00672                         cat_array->put(new_cat);
00673                 }
00674 
00675                 // make space in the tree for this category's children.
00676                 cat_array_t* catsp = new cat_array_t;
00677                 item_array_t* itemsp = new item_array_t;
00678                 mParentChildCategoryTree[new_cat->getUUID()] = catsp;
00679                 mParentChildItemTree[new_cat->getUUID()] = itemsp;
00680                 addChangedMask(LLInventoryObserver::ADD, cat->getUUID());
00681         }
00682 }
00683 
00684 void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
00685 {
00686         lldebugs << "LLInventoryModel::moveObject()" << llendl;
00687         if(!isInventoryUsable())
00688         {
00689                 llwarns << "Inventory is broken." << llendl;
00690                 return;
00691         }
00692 
00693         if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))
00694         {
00695                 llwarns << "Could not move inventory object " << object_id << " to "
00696                                 << cat_id << llendl;
00697                 return;
00698         }
00699         LLViewerInventoryCategory* cat = getCategory(object_id);
00700         if(cat && (cat->getParentUUID() != cat_id))
00701         {
00702                 cat_array_t* cat_array;
00703                 cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
00704                 if(cat_array) cat_array->removeObj(cat);
00705                 cat_array = get_ptr_in_map(mParentChildCategoryTree, cat_id);
00706                 cat->setParent(cat_id);
00707                 if(cat_array) cat_array->put(cat);
00708                 addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
00709                 return;
00710         }
00711         LLViewerInventoryItem* item = getItem(object_id);
00712         if(item && (item->getParentUUID() != cat_id))
00713         {
00714                 item_array_t* item_array;
00715                 item_array = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
00716                 if(item_array) item_array->removeObj(item);
00717                 item_array = get_ptr_in_map(mParentChildItemTree, cat_id);
00718                 item->setParent(cat_id);
00719                 if(item_array) item_array->put(item);
00720                 addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
00721                 return;
00722         }
00723 }
00724 
00725 // Delete a particular inventory object by ID.
00726 void LLInventoryModel::deleteObject(const LLUUID& id)
00727 {
00728         lldebugs << "LLInventoryModel::deleteObject()" << llendl;
00729         LLPointer<LLInventoryObject> obj = getObject(id);
00730         if(obj)
00731         {
00732                 lldebugs << "Deleting inventory object " << id << llendl;
00733                 mLastItem = NULL;
00734                 LLUUID parent_id = obj->getParentUUID();
00735                 mCategoryMap.erase(id);
00736                 mItemMap.erase(id);
00737                 //mInventory.erase(id);
00738                 item_array_t* item_list = get_ptr_in_map(mParentChildItemTree, parent_id);
00739                 if(item_list)
00740                 {
00741                         LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
00742                         item_list->removeObj(item);
00743                 }
00744                 cat_array_t* cat_list = get_ptr_in_map(mParentChildCategoryTree, parent_id);
00745                 if(cat_list)
00746                 {
00747                         LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
00748                         cat_list->removeObj(cat);
00749                 }
00750                 item_list = get_ptr_in_map(mParentChildItemTree, id);
00751                 if(item_list)
00752                 {
00753                         delete item_list;
00754                         mParentChildItemTree.erase(id);
00755                 }
00756                 cat_list = get_ptr_in_map(mParentChildCategoryTree, id);
00757                 if(cat_list)
00758                 {
00759                         delete cat_list;
00760                         mParentChildCategoryTree.erase(id);
00761                 }
00762                 addChangedMask(LLInventoryObserver::REMOVE, id);
00763                 obj = NULL; // delete obj
00764         }
00765 }
00766 
00767 // This is a method which collects the descendents of the id
00768 // provided. If the category is not found, no action is
00769 // taken. This method goes through the long winded process of
00770 // cancelling any calling cards, removing server representation of
00771 // folders, items, etc in a fairly efficient manner.
00772 void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
00773 {
00774         EHasChildren children = categoryHasChildren(id);
00775         if(children == CHILDREN_NO)
00776         {
00777                 llinfos << "Not purging descendents of " << id << llendl;
00778                 return;
00779         }
00780         LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
00781         if(cat.notNull())
00782         {
00783                 // do the cache accounting
00784                 llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
00785                                 << llendl;
00786                 S32 descendents = cat->getDescendentCount();
00787                 if(descendents > 0)
00788                 {
00789                         LLCategoryUpdate up(id, -descendents);
00790                         accountForUpdate(up);
00791                 }
00792 
00793                 // we know that descendent count is 0, aide since the
00794                 // accounting may actually not do an update, we should force
00795                 // it here.
00796                 cat->setDescendentCount(0);
00797 
00798                 // send it upstream
00799                 LLMessageSystem* msg = gMessageSystem;
00800                 msg->newMessage("PurgeInventoryDescendents");
00801                 msg->nextBlock("AgentData");
00802                 msg->addUUID("AgentID", gAgent.getID());
00803                 msg->addUUID("SessionID", gAgent.getSessionID());
00804                 msg->nextBlock("InventoryData");
00805                 msg->addUUID("FolderID", id);
00806                 gAgent.sendReliableMessage();
00807 
00808                 // unceremoniously remove anything we have locally stored.
00809                 cat_array_t categories;
00810                 item_array_t items;
00811                 collectDescendents(id,
00812                                                    categories,
00813                                                    items,
00814                                                    INCLUDE_TRASH);
00815                 S32 count = items.count();
00816                 S32 i;
00817                 for(i = 0; i < count; ++i)
00818                 {
00819                         deleteObject(items.get(i)->getUUID());
00820                 }
00821                 count = categories.count();
00822                 for(i = 0; i < count; ++i)
00823                 {
00824                         deleteObject(categories.get(i)->getUUID());
00825                 }
00826         }
00827 }
00828 
00829 void LLInventoryModel::deleteFromServer(LLDynamicArray<LLUUID>& category_ids,
00830                                                                                 LLDynamicArray<LLUUID>& item_ids)
00831 {
00832         // Store off tre UUIDS of parents which are being deleted (thus no
00833         // need to increment) and the parents which are being modified. We
00834         // have to increment the version of the parent with each message
00835         // sent upstream since the dataserver will increment each unique
00836         // parent per update message.
00837         std::set<LLUUID> ignore_parents;
00838         update_map_t inc_parents;
00839 
00840         S32 i;
00841         S32 count = category_ids.count();
00842         BOOL start_new_message = TRUE;
00843         LLMessageSystem* msg = gMessageSystem;
00844         LLPointer<LLViewerInventoryCategory> cat;
00845         for(i = 0; i < count; i++)
00846         {
00847                 if(start_new_message)
00848                 {
00849                         start_new_message = FALSE;
00850                         msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
00851                         msg->nextBlockFast(_PREHASH_AgentData);
00852                         msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00853                         msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00854                 }
00855                 LLUUID cat_id = category_ids.get(i);
00856 
00857                 msg->nextBlockFast(_PREHASH_FolderData);
00858                 msg->addUUIDFast(_PREHASH_FolderID, cat_id);
00859                 cat = getCategory(cat_id);
00860                 ignore_parents.insert(cat_id);
00861                 addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, cat_id);
00862                 if(cat.notNull() && (ignore_parents.find(cat->getParentUUID())==ignore_parents.end()))
00863                 {
00864                         --inc_parents[cat->getParentUUID()];
00865                 }
00866                 if(msg->isSendFullFast(_PREHASH_FolderData))
00867                 {
00868                         start_new_message = TRUE;
00869                         msg->nextBlockFast(_PREHASH_ItemData);
00870                         msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
00871                         gAgent.sendReliableMessage();
00872                         accountForUpdate(inc_parents);
00873                         inc_parents.clear();
00874                 }
00875         }
00876 
00877         count = item_ids.count();
00878         std::set<LLUUID>::iterator not_ignored = ignore_parents.end();
00879         LLPointer<LLViewerInventoryItem> item;
00880         if((0 == count) && (!start_new_message))
00881         {
00882                 msg->nextBlockFast(_PREHASH_ItemData);
00883                 msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
00884         }
00885         for(i = 0; i < count; i++)
00886         {
00887                 if(start_new_message)
00888                 {
00889                         start_new_message = FALSE;
00890                         msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
00891                         msg->nextBlockFast(_PREHASH_AgentData);
00892                         msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00893                         msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00894                         msg->nextBlockFast(_PREHASH_FolderData);
00895                         msg->addUUIDFast(_PREHASH_FolderID, LLUUID::null);
00896                 }
00897                 LLUUID item_id = item_ids.get(i);
00898                 msg->nextBlockFast(_PREHASH_ItemData);
00899                 msg->addUUIDFast(_PREHASH_ItemID, item_id);
00900                 item = getItem(item_id);
00901                 addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, item_id);
00902                 if(item.notNull() && (ignore_parents.find(item->getParentUUID()) == not_ignored))
00903                 {
00904                         --inc_parents[item->getParentUUID()];
00905                 }
00906                 if(msg->isSendFullFast(_PREHASH_ItemData))
00907                 {
00908                         start_new_message = TRUE;
00909                         gAgent.sendReliableMessage();
00910                         accountForUpdate(inc_parents);
00911                         inc_parents.clear();
00912                 }
00913         }
00914         if(!start_new_message)
00915         {
00916                 gAgent.sendReliableMessage();
00917                 accountForUpdate(inc_parents);
00918         }
00919 }
00920 
00921 // Add/remove an observer. If the observer is destroyed, be sure to
00922 // remove it.
00923 void LLInventoryModel::addObserver(LLInventoryObserver* observer)
00924 {
00925         mObservers.insert(observer);
00926 }
00927         
00928 void LLInventoryModel::removeObserver(LLInventoryObserver* observer)
00929 {
00930         mObservers.erase(observer);
00931 }
00932 
00933 BOOL LLInventoryModel::containsObserver(LLInventoryObserver* observer)
00934 {
00935         return mObservers.find(observer) != mObservers.end();
00936 }
00937 
00938 // Call this method when it's time to update everyone on a new state,
00939 // by default, the inventory model will not update observers
00940 // automatically.
00941 void LLInventoryModel::notifyObservers()
00942 {
00943         for (observer_list_t::iterator iter = mObservers.begin();
00944                  iter != mObservers.end(); )
00945         {
00946                 LLInventoryObserver* observer = *iter;
00947                 observer->changed(mModifyMask);
00948                 // safe way to incrament since changed may delete entries! (@!##%@!@&*!)
00949                 iter = mObservers.upper_bound(observer); 
00950         }
00951 
00952         mModifyMask = LLInventoryObserver::NONE;
00953         mChangedItemIDs.clear();
00954 }
00955 
00956 // store flag for change
00957 // and id of object change applies to
00958 void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent) 
00959 { 
00960         mModifyMask |= mask; 
00961         if (referent.notNull())
00962         {
00963                 mChangedItemIDs.insert(referent);
00964         }
00965 }
00966 
00967 // This method to prepares a set of mock inventory which provides
00968 // minimal functionality before the actual arrival of inventory.
00969 /*
00970 void LLInventoryModel::mock(const LLUUID& root_id)
00971 {
00972         llinfos << "LLInventoryModel::mock() " << root_id << llendl;
00973         if(root_id.isNull())
00974         {
00975                 llwarns << "Not a valid root id" << llendl;
00976                 return;
00977         }
00978         LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(
00979                 root_id,
00980                 LLUUID::null,
00981                 LLAssetType::AT_CATEGORY,
00982                 NEW_CATEGORY_NAMES[LLAssetType::AT_ROOT_CATEGORY],
00983                 gAgent.getID());
00984         addCategory(cat);
00985         gInventory.buildParentChildMap();
00986 }
00987 */
00988 
00989 void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id)
00990 {
00991         LLViewerInventoryCategory* cat = getCategory(folder_id);
00992         if(!cat)
00993         {
00994                 llwarns << "Asked to fetch descendents of non-existent folder: "
00995                                 << folder_id << llendl;
00996                 return;
00997         }
00998         //S32 known_descendents = 0;
01000         //item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id);
01001         //if(categories)
01002         //{
01003         //      known_descendents += categories->count();
01004         //}
01005         //if(items)
01006         //{
01007         //      known_descendents += items->count();
01008         //}
01009         if(!cat->fetchDescendents())
01010         {
01011                 //llinfos << "Not fetching descendents" << llendl;
01012         }
01013 }
01014 
01015 //Initialize statics.
01016 LLAlertDialog* LLInventoryModel::fetchDescendentsResponder::sRetryDialog=NULL;
01017 LLSD LLInventoryModel::fetchDescendentsResponder::sRetrySD;
01018 
01019 bool LLInventoryModel::isBulkFetchProcessingComplete()
01020 {
01021         return ( (sFetchQueue.empty() 
01022                         && sBulkFetchMap.empty() 
01023                         && sBulkFetchCount==0)  ?  TRUE : FALSE ) ;
01024 }
01025 
01026 //If we get back a normal response, handle it here
01027 void  LLInventoryModel::fetchDescendentsResponder::result(const LLSD& content)
01028 {       
01029         if (content.has("folders"))     
01030         {
01031                 for(LLSD::array_const_iterator folder_it = content["folders"].beginArray();
01032                         folder_it != content["folders"].endArray();
01033                         ++folder_it)
01034                 {       
01035                         LLSD folder_sd = *folder_it;
01036                         
01037 
01038                         LLUUID agent_id = folder_sd["agent-id"];
01039 
01040                         if(agent_id != gAgent.getID())  //This should never happen.
01041                         {
01042                                 llwarns << "Got a UpdateInventoryItem for the wrong agent."
01043                                                 << llendl;
01044                                 break;
01045                         }
01046                         LLUUID parent_id = folder_sd["folder-id"];
01047                         LLUUID owner_id = folder_sd["owner-id"];
01048                         S32    version  = (S32)folder_sd["version"].asInteger();
01049                         S32    descendents = (S32)folder_sd["descendents"].asInteger();
01050                         LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
01051                         for(LLSD::array_const_iterator category_it = folder_sd["categories"].beginArray();
01052                                 category_it != folder_sd["categories"].endArray();
01053                                 ++category_it)
01054                         {       
01055                                 LLSD category = *category_it;
01056                                 tcategory->fromLLSD(category); 
01057                                                         
01058                                 if (sFullFetchStarted)
01059                                 {
01060                                         sFetchQueue.push_back(tcategory->getUUID());
01061                                 }
01062                                 else if ( !gInventory.isCategoryComplete(tcategory->getUUID()) )
01063                                 {
01064                                         gInventory.updateCategory(tcategory);
01065                                 }
01066 
01067                         }
01068                         LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
01069                         for(LLSD::array_const_iterator item_it = folder_sd["items"].beginArray();
01070                                 item_it != folder_sd["items"].endArray();
01071                                 ++item_it)
01072                         {       
01073                                 LLSD item = *item_it;
01074                                 titem->unpackMessage(item);
01075                                 
01076                                 gInventory.updateItem(titem);
01077                         }
01078 
01079                         // set version and descendentcount according to message.
01080                         LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id);
01081                         if(cat)
01082                         {
01083                                 cat->setVersion(version);
01084                                 cat->setDescendentCount(descendents);
01085                         }
01086 
01087                 }
01088         }
01089                 
01090         if (content.has("bad-folders"))
01091         {
01092                 for(LLSD::array_const_iterator folder_it = content["bad-folders"].beginArray();
01093                         folder_it != content["bad-folders"].endArray();
01094                         ++folder_it)
01095                 {       
01096                         LLSD folder_sd = *folder_it;
01097                         
01098                         //These folders failed on the dataserver.  We probably don't want to retry them.
01099                         llinfos << "Folder " << folder_sd["folder-id"].asString() 
01100                                         << "Error: " << folder_sd["error"].asString() << llendl;
01101                 }
01102         }
01103 
01104         LLInventoryModel::incrBulkFetch(-1);
01105         
01106         if (isBulkFetchProcessingComplete())
01107         {
01108                 llinfos << "Inventory fetch completed" << llendl;
01109                 if (sFullFetchStarted)
01110                 {
01111                         sAllFoldersFetched = TRUE;
01112                 }
01113                 stopBackgroundFetch();
01114         }
01115         
01116         gInventory.notifyObservers();
01117 }
01118 
01119 //If we get back an error (not found, etc...), handle it here
01120 void LLInventoryModel::fetchDescendentsResponder::error(U32 status, const std::string& reason)
01121 {
01122         llinfos << "fetchDescendentsResponder::error "
01123                 << status << ": " << reason << llendl;
01124                                                 
01125         LLInventoryModel::incrBulkFetch(-1);
01126 
01127         if (status==499)                //timed out.  Let's be awesome!
01128         {
01129                 for(LLSD::array_const_iterator folder_it = mRequestSD["folders"].beginArray();
01130                         folder_it != mRequestSD["folders"].endArray();
01131                         ++folder_it)
01132                 {       
01133                         LLSD folder_sd = *folder_it;
01134                         sRetrySD["folders"].append(folder_sd);
01135                 }
01136                 sMinTimeBetweenFetches = 10.0f; //Add 10 seconds for every time out in this sequence.
01137                 
01138                 if (!sRetryDialog)                      //The dialog isn't up.  Prompt the resident.
01139                 {
01140                         sRetryDialog = gViewerWindow->alertXml("RetryFetchInventoryDescendents", onClickRetry, this);
01141                 }
01142         }
01143         else
01144         {
01145                 if (isBulkFetchProcessingComplete())
01146                 {
01147                         if (sFullFetchStarted)
01148                         {
01149                                 sAllFoldersFetched = TRUE;
01150                         }
01151                         stopBackgroundFetch();
01152                 }
01153         }
01154         gInventory.notifyObservers();
01155 }
01156 
01157 void LLInventoryModel::fetchDescendentsResponder::onClickRetry(S32 option, void* userdata)
01158 {
01159         if (option == 0)
01160         {
01161                 std::string url;
01162 
01163                 LLViewerRegion * agent_region = gAgent.getRegion();
01164                 if (agent_region)
01165                 {
01166                         url = agent_region->getCapability("FetchInventoryDescendents");
01167                 }
01168 
01169                 if (!url.empty()) //Capability found.  Build up LLSD and use it.
01170                 {
01171                         LLSD body = sRetrySD;
01172                         LLInventoryModel::incrBulkFetch(1);
01173                         LLHTTPClient::post(url, body, new LLInventoryModel::fetchDescendentsResponder(body),300);
01174                 }
01175         }
01176         else
01177         {
01178                 if (isBulkFetchProcessingComplete())
01179                 {
01180                         if (sFullFetchStarted)
01181                         {
01182                                 sAllFoldersFetched = TRUE;
01183                         }
01184                         stopBackgroundFetch();
01185                 }
01186         }
01187         sRetryDialog=NULL;
01188         sRetrySD.clear();
01189 }
01190 
01191 //static   Bundle up a bunch of requests to send all at once.
01192 void LLInventoryModel::bulkFetch(std::string url)
01193 {
01194         //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped.
01195         //If there are items in sFetchQueue, we want to check the time since the last bulkFetch was 
01196         //sent.  If it exceeds our retry time, go ahead and fire off another batch.  
01197         //Stopbackgroundfetch will be run from the Responder instead of here.  
01198 
01199         S16 max_concurrent_fetches=8;
01200         F32 new_min_time = 0.5f;                        //HACK!  Clean this up when old code goes away entirely.
01201         if (sMinTimeBetweenFetches <= new_min_time) sMinTimeBetweenFetches=new_min_time;  //HACK!  See above.
01202         
01203         if(gDisconnected 
01204         || sBulkFetchCount > max_concurrent_fetches
01205         || sFetchTimer.getElapsedTimeF32() < sMinTimeBetweenFetches)
01206         {
01207                 return; // just bail if we are disconnected.
01208         }       
01209 
01210         //HACK.  This is inelegant.  We're shuffling a dequeue to a map to get rid of 
01211         //redundant requests.  When we get rid of the old code entirely, we can change
01212         //the dequeue to a map.  In the new model, there is no benefit to queue order.
01213         U32 folder_count=0;
01214         U32 max_batch_size=10;
01215         while( !(sFetchQueue.empty() ) )
01216         {
01217                 LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front());
01218                 
01219                 if (cat)
01220                 {
01221                         if ( !gInventory.isCategoryComplete(cat->getUUID()) )   //grab this folder.
01222                         {
01223                                 sBulkFetchMap[(cat->getUUID())] = cat;
01224                         }
01225                         else if (sFullFetchStarted)
01226                         {       //Already have this folder but append child folders to list.
01227                                 // add all children to queue
01228                                 parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID());
01229                                 if (cat_it != gInventory.mParentChildCategoryTree.end())
01230                                 {
01231                                         cat_array_t* child_categories = cat_it->second;
01232 
01233                                         for (S32 child_num = 0; child_num < child_categories->count(); child_num++)
01234                                         {
01235                                                 sFetchQueue.push_back(child_categories->get(child_num)->getUUID());
01236                                         }
01237                                 }
01238 
01239                         }
01240                 }
01241                 sFetchQueue.pop_front();
01242         }
01243                 
01244 
01245         if (!sBulkFetchMap.empty())     //There's stuff to fetch.
01246         {
01247                 U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1;
01248 
01249                 LLSD body;
01250                 
01251                 cat_map_t::iterator iter=sBulkFetchMap.begin();
01252                 while( iter!=sBulkFetchMap.end() && (folder_count < max_batch_size) )
01253                 {
01254                         LLViewerInventoryCategory* cat = iter->second;
01255                         
01256                         if (cat && !gInventory.isCategoryComplete(cat->getUUID()) )     //Category exists
01257                         {
01258                                 BOOL fetchItems=TRUE;
01259                                 if ( sFullFetchStarted 
01260                                         && gInventory.isCategoryComplete(cat->getUUID()) )
01261                                 {
01262                                         fetchItems=FALSE;
01263                                 }
01264                                 
01265                                 LLSD folder_sd;
01266                                 folder_sd["folder-id"]          = cat->getUUID();
01267                                 folder_sd["owner-id"]           = cat->getOwnerID();
01268                                 folder_sd["sort-order"]         = (LLSD::Integer)sort_order;
01269                                 folder_sd["fetch-folders"]      = (LLSD::Boolean)sFullFetchStarted;
01270                                 folder_sd["fetch-items"]        = (LLSD::Boolean)fetchItems;
01271                                 body["folders"].append(folder_sd);
01272 
01273                                 folder_count++;
01274                         }
01275                         sBulkFetchMap.erase(iter);
01276                         iter=sBulkFetchMap.begin();
01277                 }
01278         
01279                 if (iter == sBulkFetchMap.end()) sBulkFetchMap.clear();
01280                 
01281                 if (folder_count > 0)
01282                 {
01283                         sBulkFetchCount++;
01284                         
01285                         LLHTTPClient::post(url, body, new LLInventoryModel::fetchDescendentsResponder(body));
01286                         sFetchTimer.reset();
01287                 }
01288                 
01289         }       
01290         
01291         if (isBulkFetchProcessingComplete())
01292         {
01293                 if (sFullFetchStarted)
01294                 {
01295                         sAllFoldersFetched = TRUE;
01296                 }
01297                 stopBackgroundFetch();
01298         }       
01299 }
01300 
01301 // static
01302 bool LLInventoryModel::isEverythingFetched()
01303 {
01304         return (sAllFoldersFetched ? true : false);
01305 }
01306 
01307 //static
01308 BOOL LLInventoryModel::backgroundFetchActive()
01309 {
01310         return sBackgroundFetchActive;
01311 }
01312 
01313 //static 
01314 void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
01315 {
01316         if (!sAllFoldersFetched)
01317         {
01318                 sBackgroundFetchActive = TRUE;
01319                 if (cat_id.isNull())
01320                 {
01321                         if (!sFullFetchStarted)
01322                         {
01323                                 sFullFetchStarted = TRUE;
01324                                 sFetchQueue.push_back(gInventoryLibraryRoot);
01325                                 sFetchQueue.push_back(gAgent.getInventoryRootID());
01326                                 gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
01327                         }
01328                 }
01329                 else
01330                 {
01331                         // specific folder requests go to front of queue
01332                         if (sFetchQueue.empty() || sFetchQueue.front() != cat_id)
01333                         {
01334                                 sFetchQueue.push_front(cat_id);
01335                                 gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
01336                         }
01337                 }
01338         }
01339 }
01340 
01341 //static
01342 void LLInventoryModel::stopBackgroundFetch()
01343 {
01344         if (sBackgroundFetchActive)
01345         {
01346                 sBackgroundFetchActive = FALSE;
01347                 gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL);
01348                 sBulkFetchCount=0;
01349                 sMinTimeBetweenFetches=0.0f;
01350 //              sFullFetchStarted=FALSE;
01351         }
01352 }
01353 
01354 //static 
01355 void LLInventoryModel::backgroundFetch(void*)
01356 {
01357         if (sBackgroundFetchActive)
01358         {
01359                 //If we'll be using the capability, we'll be sending batches and the background thing isn't as important.
01360                 std::string url;
01361 
01362                 LLViewerRegion * agent_region = gAgent.getRegion();
01363                 if (agent_region)
01364                 {
01365                         url = agent_region->getCapability("FetchInventoryDescendents");
01366                 }
01367 
01368                 if (!url.empty()) 
01369                 {
01370                         bulkFetch(url);
01371                         return;
01372                 }
01373                 
01374                 //DEPRECATED OLD CODE FOLLOWS.
01375                 // no more categories to fetch, stop fetch process
01376                 if (sFetchQueue.empty())
01377                 {
01378                         llinfos << "Inventory fetch completed" << llendl;
01379                         if (sFullFetchStarted)
01380                         {
01381                                 sAllFoldersFetched = TRUE;
01382                         }
01383                         stopBackgroundFetch();
01384                         return;
01385                 }
01386 
01387                 F32 fast_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.1f);
01388                 F32 slow_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.5f);
01389                 if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() > slow_fetch_time)
01390                 {
01391                         // double timeouts on failure
01392                         sMinTimeBetweenFetches = llmin(sMinTimeBetweenFetches * 2.f, 10.f);
01393                         sMaxTimeBetweenFetches = llmin(sMaxTimeBetweenFetches * 2.f, 120.f);
01394                         llinfos << "Inventory fetch times grown to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
01395                         // fetch is no longer considered "timely" although we will wait for full time-out
01396                         sTimelyFetchPending = FALSE;
01397                 }
01398 
01399                 while(1)
01400                 {
01401                         if (sFetchQueue.empty())
01402                         {
01403                                 break;
01404                         }
01405 
01406                         if(gDisconnected)
01407                         {
01408                                 // just bail if we are disconnected.
01409                                 break;
01410                         }
01411 
01412                         LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front());
01413 
01414                         // category has been deleted, remove from queue.
01415                         if (!cat)
01416                         {
01417                                 sFetchQueue.pop_front();
01418                                 continue;
01419                         }
01420                         
01421                         if (sFetchTimer.getElapsedTimeF32() > sMinTimeBetweenFetches && 
01422                                 LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
01423                         {
01424                                 // category exists but has no children yet, fetch the descendants
01425                                 // for now, just request every time and rely on retry timer to throttle
01426                                 if (cat->fetchDescendents())
01427                                 {
01428                                         sFetchTimer.reset();
01429                                         sTimelyFetchPending = TRUE;
01430                                 }
01431                                 else
01432                                 {
01433                                         //  The catagory also tracks if it has expired and here it says it hasn't
01434                                         //  yet.  Get out of here because nothing is going to happen until we
01435                                         //  update the timers.
01436                                         break;
01437                                 }
01438                         }
01439                         // do I have all my children?
01440                         else if (gInventory.isCategoryComplete(sFetchQueue.front()))
01441                         {
01442                                 // finished with this category, remove from queue
01443                                 sFetchQueue.pop_front();
01444 
01445                                 // add all children to queue
01446                                 parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID());
01447                                 if (cat_it != gInventory.mParentChildCategoryTree.end())
01448                                 {
01449                                         cat_array_t* child_categories = cat_it->second;
01450 
01451                                         for (S32 child_num = 0; child_num < child_categories->count(); child_num++)
01452                                         {
01453                                                 sFetchQueue.push_back(child_categories->get(child_num)->getUUID());
01454                                         }
01455                                 }
01456 
01457                                 // we received a response in less than the fast time
01458                                 if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() < fast_fetch_time)
01459                                 {
01460                                         // shrink timeouts based on success
01461                                         sMinTimeBetweenFetches = llmax(sMinTimeBetweenFetches * 0.8f, 0.3f);
01462                                         sMaxTimeBetweenFetches = llmax(sMaxTimeBetweenFetches * 0.8f, 10.f);
01463                                         //llinfos << "Inventory fetch times shrunk to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
01464                                 }
01465 
01466                                 sTimelyFetchPending = FALSE;
01467                                 continue;
01468                         }
01469                         else if (sFetchTimer.getElapsedTimeF32() > sMaxTimeBetweenFetches)
01470                         {
01471                                 // received first packet, but our num descendants does not match db's num descendants
01472                                 // so try again later
01473                                 LLUUID fetch_id = sFetchQueue.front();
01474                                 sFetchQueue.pop_front();
01475 
01476                                 if (sNumFetchRetries++ < MAX_FETCH_RETRIES)
01477                                 {
01478                                         // push on back of queue
01479                                         sFetchQueue.push_back(fetch_id);
01480                                 }
01481                                 sTimelyFetchPending = FALSE;
01482                                 sFetchTimer.reset();
01483                                 break;
01484                         }
01485 
01486                         // not enough time has elapsed to do a new fetch
01487                         break;
01488                 }
01489         }
01490 }
01491 
01492 void LLInventoryModel::cache(
01493         const LLUUID& parent_folder_id,
01494         const LLUUID& agent_id)
01495 {
01496         lldebugs << "Caching " << parent_folder_id << " for " << agent_id
01497                          << llendl;
01498         LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);
01499         if(!root_cat) return;
01500         cat_array_t categories;
01501         categories.put(root_cat);
01502         item_array_t items;
01503 
01504         LLCanCache can_cache(this);
01505         can_cache(root_cat, NULL);
01506         collectDescendentsIf(
01507                 parent_folder_id,
01508                 categories,
01509                 items,
01510                 INCLUDE_TRASH,
01511                 can_cache);
01512         char agent_id_str[UUID_STR_LENGTH];             /*Flawfinder: ignore*/
01513         char inventory_filename[LL_MAX_PATH];           /*Flawfinder: ignore*/
01514         agent_id.toString(agent_id_str);
01515         std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_str));
01516         snprintf(                       /* Flawfinder: ignore */
01517                 inventory_filename,
01518                 LL_MAX_PATH,
01519                 CACHE_FORMAT_STRING,
01520                 path.c_str());
01521         saveToFile(inventory_filename, categories, items);
01522         std::string gzip_filename(inventory_filename);
01523         gzip_filename.append(".gz");
01524         if(gzip_file(inventory_filename, gzip_filename.c_str()))
01525         {
01526                 lldebugs << "Successfully compressed " << inventory_filename << llendl;
01527                 LLFile::remove(inventory_filename);
01528         }
01529         else
01530         {
01531                 llwarns << "Unable to compress " << inventory_filename << llendl;
01532         }
01533 }
01534 
01535 
01536 void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
01537 {
01538         //llinfos << "LLInventoryModel::addCategory()" << llendl;
01539         if(category)
01540         {
01541                 // Insert category uniquely into the map
01542                 mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
01543                 //mInventory[category->getUUID()] = category;
01544         }
01545 }
01546 
01547 void LLInventoryModel::addItem(LLViewerInventoryItem* item)
01548 {
01549         //llinfos << "LLInventoryModel::addItem()" << llendl;
01550         if(item)
01551         {
01552                 mItemMap[item->getUUID()] = item;
01553                 //mInventory[item->getUUID()] = item;
01554         }
01555 }
01556 
01557 // Empty the entire contents
01558 void LLInventoryModel::empty()
01559 {
01560 //      llinfos << "LLInventoryModel::empty()" << llendl;
01561         std::for_each(
01562                 mParentChildCategoryTree.begin(),
01563                 mParentChildCategoryTree.end(),
01564                 DeletePairedPointer());
01565         mParentChildCategoryTree.clear();
01566         std::for_each(
01567                 mParentChildItemTree.begin(),
01568                 mParentChildItemTree.end(),
01569                 DeletePairedPointer());
01570         mParentChildItemTree.clear();
01571         mCategoryMap.clear(); // remove all references (should delete entries)
01572         mItemMap.clear(); // remove all references (should delete entries)
01573         mLastItem = NULL;
01574         //mInventory.clear();
01575 }
01576 
01577 void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update)
01578 {
01579         LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
01580         if(cat)
01581         {
01582                 bool accounted = false;
01583                 S32 version = cat->getVersion();
01584                 if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
01585                 {
01586                         S32 descendents_server = cat->getDescendentCount();
01587                         LLInventoryModel::cat_array_t* cats;
01588                         LLInventoryModel::item_array_t* items;
01589                         getDirectDescendentsOf(update.mCategoryID, cats, items);
01590                         S32 descendents_actual = 0;
01591                         if(cats && items)
01592                         {
01593                                 descendents_actual = cats->count() + items->count();
01594                         }
01595                         if(descendents_server == descendents_actual)
01596                         {
01597                                 accounted = true;
01598                                 descendents_actual += update.mDescendentDelta;
01599                                 cat->setDescendentCount(descendents_actual);
01600                                 cat->setVersion(++version);
01601                                 llinfos << "accounted: '" << cat->getName() << "' "
01602                                                 << version << " with " << descendents_actual
01603                                                 << " descendents." << llendl;
01604                         }
01605                 }
01606                 if(!accounted)
01607                 {
01608                         lldebugs << "No accounting for: '" << cat->getName() << "' "
01609                                          << version << llendl;
01610                 }
01611         }
01612         else
01613         {
01614                 llwarns << "No category found for update " << update.mCategoryID
01615                                 << llendl;
01616         }
01617 }
01618 
01619 void LLInventoryModel::accountForUpdate(
01620         const LLInventoryModel::update_list_t& update)
01621 {
01622         update_list_t::const_iterator it = update.begin();
01623         update_list_t::const_iterator end = update.end();
01624         for(; it != end; ++it)
01625         {
01626                 accountForUpdate(*it);
01627         }
01628 }
01629 
01630 void LLInventoryModel::accountForUpdate(
01631         const LLInventoryModel::update_map_t& update)
01632 {
01633         LLCategoryUpdate up;
01634         update_map_t::const_iterator it = update.begin();
01635         update_map_t::const_iterator end = update.end();
01636         for(; it != end; ++it)
01637         {
01638                 up.mCategoryID = (*it).first;
01639                 up.mDescendentDelta = (*it).second.mValue;
01640                 accountForUpdate(up);
01641         }
01642 }
01643 
01644 
01645 /*
01646 void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id)
01647 {
01648         LLViewerInventoryCategory* cat = getCategory(category_id);
01649         if(cat)
01650         {
01651                 S32 version = cat->getVersion();
01652                 if(LLViewerInventoryCategory::VERSION_UNKNOWN != version)
01653                 {
01654                         cat->setVersion(version + 1);
01655                         llinfos << "IncrementVersion: " << cat->getName() << " "
01656                                         << cat->getVersion() << llendl;
01657                 }
01658                 else
01659                 {
01660                         llinfos << "Attempt to increment version when unknown: "
01661                                         << category_id << llendl;
01662                 }
01663         }
01664         else
01665         {
01666                 llinfos << "Attempt to increment category: " << category_id << llendl;
01667         }
01668 }
01669 void LLInventoryModel::incrementCategorySetVersion(
01670         const std::set<LLUUID>& categories)
01671 {
01672         if(!categories.empty())
01673         { 
01674                 std::set<LLUUID>::const_iterator it = categories.begin();
01675                 std::set<LLUUID>::const_iterator end = categories.end();
01676                 for(; it != end; ++it)
01677                 {
01678                         incrementCategoryVersion(*it);
01679                 }
01680         }
01681 }
01682 */
01683 
01684 
01685 LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
01686         const LLUUID& cat_id) const
01687 {
01688         LLViewerInventoryCategory* cat = getCategory(cat_id);
01689         if(!cat) return CHILDREN_NO;
01690         if(cat->getDescendentCount() > 0)
01691         {
01692                 return CHILDREN_YES;
01693         }
01694         if(cat->getDescendentCount() == 0)
01695         {
01696                 return CHILDREN_NO;
01697         }
01698         if((cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
01699            || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
01700         {
01701                 return CHILDREN_MAYBE;
01702         }
01703 
01704         // Shouldn't have to run this, but who knows.
01705         parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID());
01706         if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0)
01707         {
01708                 return CHILDREN_YES;
01709         }
01710         parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID());
01711         if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0)
01712         {
01713                 return CHILDREN_YES;
01714         }
01715 
01716         return CHILDREN_NO;
01717 }
01718 
01719 bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
01720 {
01721         LLViewerInventoryCategory* cat = getCategory(cat_id);
01722         if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
01723         {
01724                 S32 descendents_server = cat->getDescendentCount();
01725                 LLInventoryModel::cat_array_t* cats;
01726                 LLInventoryModel::item_array_t* items;
01727                 getDirectDescendentsOf(cat_id, cats, items);
01728                 S32 descendents_actual = 0;
01729                 if(cats && items)
01730                 {
01731                         descendents_actual = cats->count() + items->count();
01732                 }
01733                 if(descendents_server == descendents_actual)
01734                 {
01735                         return true;
01736                 }
01737         }
01738         return false;
01739 }
01740 
01741 bool LLInventoryModel::loadSkeleton(
01742         const LLInventoryModel::options_t& options,
01743         const LLUUID& owner_id)
01744 {
01745         lldebugs << "importing inventory skeleton for " << owner_id << llendl;
01746 
01747         typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
01748         cat_set_t temp_cats;
01749 
01750         update_map_t child_counts;
01751 
01752         LLUUID id;
01753         LLAssetType::EType preferred_type;
01754         bool rv = true;
01755         for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
01756         {
01757                 LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id);
01758                 response_t::const_iterator no_response = (*it).end();
01759                 response_t::const_iterator skel;
01760                 skel = (*it).find("name");
01761                 if(skel == no_response) goto clean_cat;
01762                 cat->rename(LLString((*skel).second.c_str()));
01763                 skel = (*it).find("folder_id");
01764                 if(skel == no_response) goto clean_cat;
01765                 id.set((*skel).second.c_str());
01766                 // if an id is null, it locks the viewer.
01767                 if(id.isNull()) goto clean_cat;
01768                 cat->setUUID(id);
01769                 skel = (*it).find("parent_id");
01770                 if(skel == no_response) goto clean_cat;
01771                 id.set((*skel).second.c_str());
01772                 cat->setParent(id);
01773                 skel = (*it).find("type_default");
01774                 if(skel == no_response)
01775                 {
01776                         preferred_type = LLAssetType::AT_NONE;
01777                 }
01778                 else
01779                 {
01780                         S32 t = atoi((*skel).second.c_str());
01781                         preferred_type = (LLAssetType::EType)t;
01782                 }
01783                 cat->setPreferredType(preferred_type);
01784                 skel = (*it).find("version");
01785                 if(skel == no_response) goto clean_cat;
01786                 cat->setVersion(atoi((*skel).second.c_str()));
01787                 temp_cats.insert(cat);
01788                 continue;
01789         clean_cat:
01790                 llwarns << "Unable to import near " << cat->getName() << llendl;
01791                 rv = false;
01792                 //delete cat; // automatic when cat is reasigned or destroyed
01793         }
01794 
01795         S32 cached_category_count = 0;
01796         S32 cached_item_count = 0;
01797         if(!temp_cats.empty())
01798         {
01799                 cat_array_t categories;
01800                 item_array_t items;
01801                 char owner_id_str[UUID_STR_LENGTH];             /*Flawfinder: ignore*/
01802                 owner_id.toString(owner_id_str);
01803                 std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str));
01804                 char inventory_filename[LL_MAX_PATH];           /*Flawfinder: ignore*/
01805                 snprintf(                       /* Flawfinder: ignore */
01806                         inventory_filename,
01807                         LL_MAX_PATH,
01808                         CACHE_FORMAT_STRING,
01809                         path.c_str());
01810                 const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN;
01811                 std::string gzip_filename(inventory_filename);
01812                 gzip_filename.append(".gz");
01813                 LLFILE* fp = LLFile::fopen(gzip_filename.c_str(), "rb");                /*Flawfinder: ignore*/
01814                 bool remove_inventory_file = false;
01815                 if(fp)
01816                 {
01817                         fclose(fp);
01818                         fp = NULL;
01819                         if(gunzip_file(gzip_filename.c_str(), inventory_filename))
01820                         {
01821                                 // we only want to remove the inventory file if it was
01822                                 // gzipped before we loaded, and we successfully
01823                                 // gunziped it.
01824                                 remove_inventory_file = true;
01825                         }
01826                         else
01827                         {
01828                                 llinfos << "Unable to gunzip " << gzip_filename << llendl;
01829                         }
01830                 }
01831                 if(loadFromFile(inventory_filename, categories, items))
01832                 {
01833                         // We were able to find a cache of files. So, use what we
01834                         // found to generate a set of categories we should add. We
01835                         // will go through each category loaded and if the version
01836                         // does not match, invalidate the version.
01837                         S32 count = categories.count();
01838                         cat_set_t::iterator not_cached = temp_cats.end();
01839                         std::set<LLUUID> cached_ids;
01840                         for(S32 i = 0; i < count; ++i)
01841                         {
01842                                 LLViewerInventoryCategory* cat = categories[i];
01843                                 cat_set_t::iterator cit = temp_cats.find(cat);
01844                                 if (cit == temp_cats.end())
01845                                 {
01846                                         continue; // cache corruption?? not sure why this happens -SJB
01847                                 }
01848                                 LLViewerInventoryCategory* tcat = *cit;
01849                                 
01850                                 // we can safely ignore anything loaded from file, but
01851                                 // not sent down in the skeleton.
01852                                 if(cit == not_cached)
01853                                 {
01854                                         continue;
01855                                 }
01856                                 if(cat->getVersion() != tcat->getVersion())
01857                                 {
01858                                         // if the cached version does not match the server version,
01859                                         // throw away the version we have so we can fetch the
01860                                         // correct contents the next time the viewer opens the folder.
01861                                         tcat->setVersion(NO_VERSION);
01862                                 }
01863                                 else
01864                                 {
01865                                         cached_ids.insert(tcat->getUUID());
01866                                 }
01867                         }
01868 
01869                         // go ahead and add the cats returned during the download
01870                         std::set<LLUUID>::iterator not_cached_id = cached_ids.end();
01871                         cached_category_count = cached_ids.size();
01872                         for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
01873                         {
01874                                 if(cached_ids.find((*it)->getUUID()) == not_cached_id)
01875                                 {
01876                                         // this check is performed so that we do not
01877                                         // mark new folders in the skeleton (and not in cache)
01878                                         // as being cached.
01879                                         LLViewerInventoryCategory *llvic = (*it);
01880                                         llvic->setVersion(NO_VERSION);
01881                                 }
01882                                 addCategory(*it);
01883                                 ++child_counts[(*it)->getParentUUID()];
01884                         }
01885 
01886                         // Add all the items loaded which are parented to a
01887                         // category with a correctly cached parent
01888                         count = items.count();
01889                         cat_map_t::iterator unparented = mCategoryMap.end();
01890                         for(int i = 0; i < count; ++i)
01891                         {
01892                                 cat_map_t::iterator cit = mCategoryMap.find(items[i]->getParentUUID());
01893                                 
01894                                 if(cit != unparented)
01895                                 {
01896                                         LLViewerInventoryCategory* cat = cit->second;
01897                                         if(cat->getVersion() != NO_VERSION)
01898                                         {
01899                                                 addItem(items[i]);
01900                                                 cached_item_count += 1;
01901                                                 ++child_counts[cat->getUUID()];
01902                                         }
01903                                 }
01904                         }
01905                 }
01906                 else
01907                 {
01908                         // go ahead and add everything after stripping the version
01909                         // information.
01910                         for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
01911                         {
01912                                 LLViewerInventoryCategory *llvic = (*it);
01913                                 llvic->setVersion(NO_VERSION);
01914                                 addCategory(*it);
01915                         }
01916                 }
01917 
01918                 // At this point, we need to set the known descendents for each
01919                 // category which successfully cached so that we do not
01920                 // needlessly fetch descendents for categories which we have.
01921                 update_map_t::iterator no_child_counts = child_counts.end();
01922                 update_map_t::iterator the_count;
01923                 for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
01924                 {
01925                         LLViewerInventoryCategory* cat = (*it);
01926                         if(cat->getVersion() != NO_VERSION)
01927                         {
01928                                 the_count = child_counts.find(cat->getUUID());
01929                                 if(the_count != no_child_counts)
01930                                 {
01931                                         cat->setDescendentCount((*the_count).second.mValue);
01932                                 }
01933                                 else
01934                                 {
01935                                         cat->setDescendentCount(0);
01936                                 }
01937                         }
01938                 }
01939 
01940                 if(remove_inventory_file)
01941                 {
01942                         // clean up the gunzipped file.
01943                         LLFile::remove(inventory_filename);
01944                 }
01945                 categories.clear(); // will unref and delete entries
01946         }
01947 
01948         llinfos << "Successfully loaded " << cached_category_count
01949                         << " categories and " << cached_item_count << " items from cache."
01950                         << llendl;
01951 
01952         return rv;
01953 }
01954 
01955 bool LLInventoryModel::loadMeat(
01956         const LLInventoryModel::options_t& options, const LLUUID& owner_id)
01957 {
01958         llinfos << "importing inventory for " << owner_id << llendl;
01959         LLPermissions default_perm;
01960         default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null);
01961         LLPointer<LLViewerInventoryItem> item;
01962         LLUUID id;
01963         LLAssetType::EType type;
01964         LLInventoryType::EType inv_type;
01965         bool rv = true;
01966         for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
01967         {
01968                 item = new LLViewerInventoryItem;
01969                 response_t::const_iterator no_response = (*it).end();
01970                 response_t::const_iterator meat;
01971                 meat = (*it).find("name");
01972                 if(meat == no_response) goto clean_item;
01973                 item->rename(LLString((*meat).second.c_str()));
01974                 meat = (*it).find("item_id");
01975                 if(meat == no_response) goto clean_item;
01976                 id.set((*meat).second.c_str());
01977                 item->setUUID(id);
01978                 meat = (*it).find("parent_id");
01979                 if(meat == no_response) goto clean_item;
01980                 id.set((*meat).second.c_str());
01981                 item->setParent(id);
01982                 meat = (*it).find("type");
01983                 if(meat == no_response) goto clean_item;
01984                 type = (LLAssetType::EType)atoi((*meat).second.c_str());
01985                 item->setType(type);
01986                 meat = (*it).find("inv_type");
01987                 if(meat != no_response)
01988                 {
01989                         inv_type = (LLInventoryType::EType)atoi((*meat).second.c_str());
01990                         item->setInventoryType(inv_type);
01991                 }
01992                 meat = (*it).find("data_id");
01993                 if(meat == no_response) goto clean_item;
01994                 id.set((*meat).second.c_str());
01995                 if(LLAssetType::AT_CALLINGCARD == type)
01996                 {
01997                         LLPermissions perm;
01998                         perm.init(id, owner_id, LLUUID::null, LLUUID::null);
01999                         item->setPermissions(perm);
02000                 }
02001                 else
02002                 {
02003                         meat = (*it).find("perm_mask");
02004                         if(meat != no_response)
02005                         {
02006                                 PermissionMask perm_mask = atoi((*meat).second.c_str());
02007                                 default_perm.initMasks(
02008                                         perm_mask, perm_mask, perm_mask, perm_mask, perm_mask);
02009                         }
02010                         else
02011                         {
02012                                 default_perm.initMasks(
02013                                         PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
02014                         }
02015                         item->setPermissions(default_perm);
02016                         item->setAssetUUID(id);
02017                 }
02018                 meat = (*it).find("flags");
02019                 if(meat != no_response)
02020                 {
02021                         item->setFlags(strtoul((*meat).second.c_str(), NULL, 0));
02022                 }
02023                 meat = (*it).find("time");
02024                 if(meat != no_response)
02025                 {
02026                         item->setCreationDate(atoi((*meat).second.c_str()));
02027                 }
02028                 addItem(item);
02029                 continue;
02030         clean_item:
02031                 llwarns << "Unable to import near " << item->getName() << llendl;
02032                 rv = false;
02033                 //delete item; // automatic when item is reassigned or destroyed
02034         }
02035         return rv;
02036 }
02037 
02038 // This is a brute force method to rebuild the entire parent-child
02039 // relations. The overall operation has O(NlogN) performance, which
02040 // should be sufficient for our needs. 
02041 void LLInventoryModel::buildParentChildMap()
02042 {
02043         llinfos << "LLInventoryModel::buildParentChildMap()" << llendl;
02044 
02045         // *NOTE: I am skipping the logic around folder version
02046         // synchronization here because it seems if a folder is lost, we
02047         // might actually want to invalidate it at that point - not
02048         // attempt to cache. More time & thought is necessary.
02049 
02050         // First the categories. We'll copy all of the categories into a
02051         // temporary container to iterate over (oh for real iterators.)
02052         // While we're at it, we'll allocate the arrays in the trees.
02053         cat_array_t cats;
02054         cat_array_t* catsp;
02055         item_array_t* itemsp;
02056         
02057         for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
02058         {
02059                 LLViewerInventoryCategory* cat = cit->second;
02060                 cats.put(cat);
02061                 if (mParentChildCategoryTree.count(cat->getUUID()) == 0)
02062                 {
02063                         catsp = new cat_array_t;
02064                         mParentChildCategoryTree[cat->getUUID()] = catsp;
02065                 }
02066                 if (mParentChildItemTree.count(cat->getUUID()) == 0)
02067                 {
02068                         itemsp = new item_array_t;
02069                         mParentChildItemTree[cat->getUUID()] = itemsp;
02070                 }
02071         }
02072 
02073         // Insert a special parent for the root - so that lookups on
02074         // LLUUID::null as the parent work correctly. This is kind of a
02075         // blatent wastes of space since we allocate a block of memory for
02076         // the array, but whatever - it's not that much space.
02077         if (mParentChildCategoryTree.count(LLUUID::null) == 0)
02078         {
02079                 catsp = new cat_array_t;
02080                 mParentChildCategoryTree[LLUUID::null] = catsp;
02081         }
02082 
02083         // Now we have a structure with all of the categories that we can
02084         // iterate over and insert into the correct place in the child
02085         // category tree. 
02086         S32 count = cats.count();
02087         S32 i;
02088         S32 lost = 0;
02089         for(i = 0; i < count; ++i)
02090         {
02091                 LLViewerInventoryCategory* cat = cats.get(i);
02092                 catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
02093                 if(catsp)
02094                 {
02095                         catsp->put(cat);
02096                 }
02097                 else
02098                 {
02099                         // *NOTE: This process could be a lot more efficient if we
02100                         // used the new MoveInventoryFolder message, but we would
02101                         // have to continue to do the update & build here. So, to
02102                         // implement it, we would need a set or map of uuid pairs
02103                         // which would be (folder_id, new_parent_id) to be sent up
02104                         // to the server.
02105                         llinfos << "Lost categroy: " << cat->getUUID() << " - "
02106                                         << cat->getName() << llendl;
02107                         ++lost;
02108                         // plop it into the lost & found.
02109                         LLAssetType::EType pref = cat->getPreferredType();
02110                         if(LLAssetType::AT_NONE == pref)
02111                         {
02112                                 cat->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
02113                         }
02114                         else if(LLAssetType::AT_CATEGORY == pref)
02115                         {
02116                                 // it's the root
02117                                 cat->setParent(LLUUID::null);
02118                         }
02119                         else
02120                         {
02121                                 // it's a protected folder.
02122                                 cat->setParent(gAgent.getInventoryRootID());
02123                         }
02124                         cat->updateServer(TRUE);
02125                         catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
02126                         if(catsp)
02127                         {
02128                                 catsp->put(cat);
02129                         }
02130                         else
02131                         {               
02132                                 llwarns << "Lost and found Not there!!" << llendl;
02133                         }
02134                 }
02135         }
02136         if(lost)
02137         {
02138                 llwarns << "Found  " << lost << " lost categories." << llendl;
02139         }
02140 
02141         // Now the items. We allocated in the last step, so now all we
02142         // have to do is iterate over the items and put them in the right
02143         // place.
02144         item_array_t items;
02145         if(!mItemMap.empty())
02146         {
02147                 LLPointer<LLViewerInventoryItem> item;
02148                 for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
02149                 {
02150                         item = (*iit).second;
02151                         items.put(item);
02152                 }
02153         }
02154         count = items.count();
02155         lost = 0;
02156         std::vector<LLUUID> lost_item_ids;
02157         for(i = 0; i < count; ++i)
02158         {
02159                 LLPointer<LLViewerInventoryItem> item;
02160                 item = items.get(i);
02161                 itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
02162                 if(itemsp)
02163                 {
02164                         itemsp->put(item);
02165                 }
02166                 else
02167                 {
02168                         llinfos << "Lost item: " << item->getUUID() << " - "
02169                                         << item->getName() << llendl;
02170                         ++lost;
02171                         // plop it into the lost & found.
02172                         //
02173                         item->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
02174                         // move it later using a special message to move items. If
02175                         // we update server here, the client might crash.
02176                         //item->updateServer();
02177                         lost_item_ids.push_back(item->getUUID());
02178                         itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
02179                         if(itemsp)
02180                         {
02181                                 itemsp->put(item);
02182                         }
02183                         else
02184                         {
02185                                 llwarns << "Lost and found Not there!!" << llendl;
02186                         }
02187                 }
02188         }
02189         if(lost)
02190         {
02191                 llwarns << "Found " << lost << " lost items." << llendl;
02192                 LLMessageSystem* msg = gMessageSystem;
02193                 BOOL start_new_message = TRUE;
02194                 LLUUID lnf = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
02195                 for(std::vector<LLUUID>::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it)
02196                 {
02197                         if(start_new_message)
02198                         {
02199                                 start_new_message = FALSE;
02200                                 msg->newMessageFast(_PREHASH_MoveInventoryItem);
02201                                 msg->nextBlockFast(_PREHASH_AgentData);
02202                                 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
02203                                 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
02204                                 msg->addBOOLFast(_PREHASH_Stamp, FALSE);
02205                         }
02206                         msg->nextBlockFast(_PREHASH_InventoryData);
02207                         msg->addUUIDFast(_PREHASH_ItemID, (*it));
02208                         msg->addUUIDFast(_PREHASH_FolderID, lnf);
02209                         msg->addString("NewName", NULL);
02210                         if(msg->isSendFull(NULL))
02211                         {
02212                                 start_new_message = TRUE;
02213                                 gAgent.sendReliableMessage();
02214                         }
02215                 }
02216                 if(!start_new_message)
02217                 {
02218                         gAgent.sendReliableMessage();
02219                 }
02220         }
02221 
02222         const LLUUID& agent_inv_root_id = gAgent.getInventoryRootID();
02223         if (agent_inv_root_id.notNull())
02224         {
02225                 cat_array_t* catsp = get_ptr_in_map(mParentChildCategoryTree, agent_inv_root_id);
02226                 if(catsp)
02227                 {
02228                         // 'My Inventory',
02229                         // root of the agent's inv found.
02230                         // The inv tree is built.
02231                         mIsAgentInvUsable = true;
02232                 }
02233         }
02234 }
02235 
02236 struct LLUUIDAndName
02237 {
02238         LLUUIDAndName() {}
02239         LLUUIDAndName(const LLUUID& id, const LLString& name);
02240         bool operator==(const LLUUIDAndName& rhs) const;
02241         bool operator<(const LLUUIDAndName& rhs) const;
02242         bool operator>(const LLUUIDAndName& rhs) const;
02243 
02244         LLUUID mID;
02245         LLString mName;
02246 };
02247 
02248 LLUUIDAndName::LLUUIDAndName(const LLUUID& id, const LLString& name) :
02249         mID(id), mName(name)
02250 {
02251 }
02252 
02253 bool LLUUIDAndName::operator==(const LLUUIDAndName& rhs) const
02254 {
02255         return ((mID == rhs.mID) && (mName == rhs.mName));
02256 }
02257 
02258 bool LLUUIDAndName::operator<(const LLUUIDAndName& rhs) const
02259 {
02260         return (mID < rhs.mID);
02261 }
02262 
02263 bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const
02264 {
02265         return (mID > rhs.mID);
02266 }
02267 
02268 // Given the current state of the inventory items, figure out the
02269 // clone information. *FIX: This is sub-optimal, since we can insert
02270 // this information snurgically, but this makes sure the implementation
02271 // works before we worry about optimization.
02272 //void LLInventoryModel::recalculateCloneInformation()
02273 //{
02274 //      //dumpInventory();
02275 //
02276 //      // This implements a 'multi-map' like structure to keep track of
02277 //      // how many clones we find.
02278 //      typedef LLDynamicArray<LLViewerInventoryItem*> viewer_item_array_t;
02279 //      typedef std::map<LLUUIDAndName, viewer_item_array_t*> clone_map_t;
02280 //      clone_map_t clone_map;
02281 //      LLUUIDAndName id_and_name;
02282 //      viewer_item_array_t* clones = NULL;
02283 //      LLViewerInventoryItem* item = NULL;
02284 //      for(item = (LLViewerInventoryItem*)mItemMap.getFirstData();
02285 //              item != NULL;
02286 //              item = (LLViewerInventoryItem*)mItemMap.getNextData())
02287 //      {
02288 //              if(item->getType() == LLAssetType::AT_CALLINGCARD)
02289 //              {
02290 //                      // if it's a calling card, we key off of the creator id, not
02291 //                      // the asset id.
02292 //                      id_and_name.mID = item->getCreatorUUID();
02293 //              }
02294 //              else
02295 //              {
02296 //                      // if it's not a calling card, we key clones from the
02297 //                      // asset id.
02298 //                      id_and_name.mID = item->getAssetUUID();
02299 //              }
02300 //              if(id_and_name.mID == LLUUID::null)
02301 //              {
02302 //                      continue;
02303 //              }
02304 //              id_and_name.mName = item->getName();
02305 //              if(clone_map.checkData(id_and_name))
02306 //              {
02307 //                      clones = clone_map.getData(id_and_name);
02308 //              }
02309 //              else
02310 //              {
02311 //                      clones = new viewer_item_array_t;
02312 //                      clone_map.addData(id_and_name, clones);
02313 //              }
02314 //              clones->put(item);
02315 //      }
02316 //
02317 //      S32 count = 0;
02318 //      for(clones = clone_map.getFirstData();
02319 //              clones != NULL;
02320 //              clones = clone_map.getNextData())
02321 //      {
02322 //              count = clones->count();
02323 //              for(S32 i = 0; i < count; i++)
02324 //              {
02325 //                      item = clones->get(i);
02326 //                      item->setCloneCount(count - 1);
02327 //                      //clones[i] = NULL;
02328 //              }
02329 //              delete clones;
02330 //      }
02331 //      clone_map.removeAllData();
02332 //      //dumpInventory();
02333 //}
02334 
02335 // static
02336 bool LLInventoryModel::loadFromFile(
02337         const char* filename,
02338         LLInventoryModel::cat_array_t& categories,
02339         LLInventoryModel::item_array_t& items)
02340 {
02341         if(!filename)
02342         {
02343                 llerrs << "Filename is Null!" << llendl;
02344                 return false;
02345         }
02346         llinfos << "LLInventoryModel::loadFromFile(" << filename << ")" << llendl;
02347         LLFILE* file = LLFile::fopen(filename, "rb");           /*Flawfinder: ignore*/
02348         if(!file)
02349         {
02350                 llinfos << "unable to load inventory from: " << filename << llendl;
02351                 return false;
02352         }
02353         // *NOTE: This buffer size is hard coded into scanf() below.
02354         char buffer[MAX_STRING];                /*Flawfinder: ignore*/
02355         char keyword[MAX_STRING];               /*Flawfinder: ignore*/
02356         while(!feof(file) && fgets(buffer, MAX_STRING, file)) 
02357         {
02358                 sscanf(buffer, " %254s", keyword);      /* Flawfinder: ignore */
02359                 if(0 == strcmp("inv_category", keyword))
02360                 {
02361                         LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null);
02362                         if(inv_cat->importFileLocal(file))
02363                         {
02364                                 categories.put(inv_cat);
02365                         }
02366                         else
02367                         {
02368                                 llwarns << "loadInventoryFromFile().  Ignoring invalid inventory category: " << inv_cat->getName() << llendl;
02369                                 //delete inv_cat; // automatic when inv_cat is reassigned or destroyed
02370                         }
02371                 }
02372                 else if(0 == strcmp("inv_item", keyword))
02373                 {
02374                         LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem;
02375                         if( inv_item->importFileLocal(file) )
02376                         {
02377                                 // *FIX: Need a better solution, this prevents the
02378                                 // application from freezing, but breaks inventory
02379                                 // caching.
02380                                 if(inv_item->getUUID().isNull())
02381                                 {
02382                                         //delete inv_item; // automatic when inv_cat is reassigned or destroyed
02383                                         llwarns << "Ignoring inventory with null item id: "
02384                                                         << inv_item->getName() << llendl;
02385                                                 
02386                                 }
02387                                 else
02388                                 {
02389                                         items.put(inv_item);
02390                                 }
02391                         }
02392                         else
02393                         {
02394                                 llwarns << "loadInventoryFromFile().  Ignoring invalid inventory item: " << inv_item->getName() << llendl;
02395                                 //delete inv_item; // automatic when inv_cat is reassigned or destroyed
02396                         }
02397                 }
02398                 else
02399                 {
02400                         llwarns << "Unknown token in inventory file '" << keyword << "'"
02401                                         << llendl;
02402                 }
02403         }
02404         fclose(file);
02405         return true;
02406 }
02407 
02408 // static
02409 bool LLInventoryModel::saveToFile(
02410         const char* filename,
02411         const cat_array_t& categories,
02412         const item_array_t& items)
02413 {
02414         if(!filename)
02415         {
02416                 llerrs << "Filename is Null!" << llendl;
02417                 return false;
02418         }
02419         llinfos << "LLInventoryModel::saveToFile(" << filename << ")" << llendl;
02420         LLFILE* file = LLFile::fopen(filename, "wb");           /*Flawfinder: ignore*/
02421         if(!file)
02422         {
02423                 llwarns << "unable to save inventory to: " << filename << llendl;
02424                 return false;
02425         }
02426 
02427         S32 count = categories.count();
02428         S32 i;
02429         for(i = 0; i < count; ++i)
02430         {
02431                 LLViewerInventoryCategory* cat = categories[i];
02432                 if(cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
02433                 {
02434                         cat->exportFileLocal(file);
02435                 }
02436         }
02437 
02438         count = items.count();
02439         for(i = 0; i < count; ++i)
02440         {
02441                 items[i]->exportFile(file);
02442         }
02443 
02444         fclose(file);
02445         return true;
02446 }
02447 
02448 // message handling functionality
02449 // static
02450 void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
02451 {
02452         //msg->setHandlerFuncFast(_PREHASH_InventoryUpdate,
02453         //                                      processInventoryUpdate,
02454         //                                      NULL);
02455         //msg->setHandlerFuncFast(_PREHASH_UseCachedInventory,
02456         //                                      processUseCachedInventory,
02457         //                                      NULL);
02458         msg->setHandlerFuncFast(_PREHASH_UpdateCreateInventoryItem,
02459                                                 processUpdateCreateInventoryItem,
02460                                                 NULL);
02461         msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem,
02462                                                 processRemoveInventoryItem,
02463                                                 NULL);
02464         msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder,
02465                                                 processUpdateInventoryFolder,
02466                                                 NULL);
02467         msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,
02468                                                 processRemoveInventoryFolder,
02469                                                 NULL);
02470         //msg->setHandlerFuncFast(_PREHASH_ExchangeCallingCard,
02471         //                                              processExchangeCallingcard,
02472         //                                              NULL);
02473         //msg->setHandlerFuncFast(_PREHASH_AddCallingCard,
02474         //                                      processAddCallingcard,
02475         //                                      NULL);
02476         //msg->setHandlerFuncFast(_PREHASH_DeclineCallingCard,
02477         //                                      processDeclineCallingcard,
02478         //                                      NULL);
02479         msg->setHandlerFuncFast(_PREHASH_SaveAssetIntoInventory,
02480                                                 processSaveAssetIntoInventory,
02481                                                 NULL);
02482         msg->setHandlerFuncFast(_PREHASH_BulkUpdateInventory,
02483                                                         processBulkUpdateInventory,
02484                                                         NULL);
02485         msg->setHandlerFunc("InventoryDescendents", processInventoryDescendents);
02486         msg->setHandlerFunc("MoveInventoryItem", processMoveInventoryItem);
02487         msg->setHandlerFunc("FetchInventoryReply", processFetchInventoryReply);
02488 }
02489 
02490 
02491 //      static
02492 void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, void**)
02493 {
02494         // do accounting and highlight new items if they arrive
02495         if (gInventory.messageUpdateCore(msg, true))
02496         {
02497                 U32 callback_id;
02498                 LLUUID item_id;
02499                 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
02500                 msg->getU32Fast(_PREHASH_InventoryData, _PREHASH_CallbackID, callback_id);
02501 
02502                 gInventoryCallbacks.fire(callback_id, item_id);
02503         }
02504 
02505 }
02506 
02507 // static
02508 void LLInventoryModel::processFetchInventoryReply(LLMessageSystem* msg, void**)
02509 {
02510         // no accounting
02511         gInventory.messageUpdateCore(msg, false);
02512 }
02513 
02514 
02515 bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account)
02516 {
02517         //make sure our added inventory observer is active -Gigs
02518         start_new_inventory_observer();
02519 
02520         LLUUID agent_id;
02521         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
02522         if(agent_id != gAgent.getID())
02523         {
02524                 llwarns << "Got a inventory update for the wrong agent: " << agent_id
02525                                 << llendl;
02526                 return false;
02527         }
02528         item_array_t items;
02529         update_map_t update;
02530         S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
02531         bool all_one_folder = true;
02532         LLUUID folder_id;
02533         // Does this loop ever execute more than once? -Gigs
02534         for(S32 i = 0; i < count; ++i)
02535         {
02536                 LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
02537                 titem->unpackMessage(msg, _PREHASH_InventoryData, i);
02538                 lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
02539                                  << titem->getUUID() << llendl;
02540                 items.push_back(titem);
02541                 // examine update for changes.
02542                 LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
02543                 if(itemp)
02544                 {
02545                         if(titem->getParentUUID() == itemp->getParentUUID())
02546                         {
02547                                 update[titem->getParentUUID()];
02548                         }
02549                         else
02550                         {
02551                                 ++update[titem->getParentUUID()];
02552                                 --update[itemp->getParentUUID()];
02553                         }
02554                 }
02555                 else
02556                 {
02557                         ++update[titem->getParentUUID()];
02558                 }
02559                 if (folder_id.isNull())
02560                 {
02561                         folder_id = titem->getParentUUID();
02562                 }
02563                 else
02564                 {
02565                         all_one_folder = false;
02566                 }
02567         }
02568         if(account)
02569         {
02570                 gInventory.accountForUpdate(update);
02571         }
02572 
02573         U32 changes = 0x0;
02574         //as above, this loop never seems to loop more than once per call
02575         for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
02576         {
02577                 changes |= gInventory.updateItem(*it);
02578         }
02579         gInventory.notifyObservers();
02580         gViewerWindow->getWindow()->decBusyCount();
02581 
02582         return true;
02583 }
02584 
02585 //      static
02586 void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
02587 {
02588         lldebugs << "LLInventoryModel::processRemoveInventoryItem()" << llendl;
02589         LLUUID agent_id, item_id;
02590         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
02591         if(agent_id != gAgent.getID())
02592         {
02593                 llwarns << "Got a RemoveInventoryItem for the wrong agent."
02594                                 << llendl;
02595                 return;
02596         }
02597         S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
02598         std::vector<LLUUID> item_ids;
02599         update_map_t update;
02600         for(S32 i = 0; i < count; ++i)
02601         {
02602                 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
02603                 LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
02604                 if(itemp)
02605                 {
02606                         // we only bother with the delete and account if we found
02607                         // the item - this is usually a back-up for permissions,
02608                         // so frequently the item will already be gone.
02609                         --update[itemp->getParentUUID()];
02610                         item_ids.push_back(item_id);
02611                 }
02612         }
02613         gInventory.accountForUpdate(update);
02614         for(std::vector<LLUUID>::iterator it = item_ids.begin(); it != item_ids.end(); ++it)
02615         {
02616                 gInventory.deleteObject(*it);
02617         }
02618         gInventory.notifyObservers();
02619 }
02620 
02621 //      static
02622 void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
02623                                                                                                         void**)
02624 {
02625         lldebugs << "LLInventoryModel::processUpdateInventoryFolder()" << llendl;
02626         LLUUID agent_id, folder_id, parent_id;
02627         //char name[DB_INV_ITEM_NAME_BUF_SIZE];
02628         msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
02629         if(agent_id != gAgent.getID())
02630         {
02631                 llwarns << "Got an UpdateInventoryFolder for the wrong agent."
02632                                 << llendl;
02633                 return;
02634         }
02635         LLPointer<LLViewerInventoryCategory> lastfolder; // hack
02636         cat_array_t folders;
02637         update_map_t update;
02638         S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
02639         for(S32 i = 0; i < count; ++i)
02640         {
02641                 LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
02642                 lastfolder = tfolder;
02643                 tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
02644                 // make sure it's not a protected folder
02645                 tfolder->setPreferredType(LLAssetType::AT_NONE);
02646                 folders.push_back(tfolder);
02647                 // examine update for changes.
02648                 LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
02649                 if(folderp)
02650                 {
02651                         if(tfolder->getParentUUID() == folderp->getParentUUID())
02652                         {
02653                                 update[tfolder->getParentUUID()];
02654                         }
02655                         else
02656                         {
02657                                 ++update[tfolder->getParentUUID()];
02658                                 --update[folderp->getParentUUID()];
02659                         }
02660                 }
02661                 else
02662                 {
02663                         ++update[tfolder->getParentUUID()];
02664                 }
02665         }
02666         gInventory.accountForUpdate(update);
02667         for (cat_array_t::iterator it = folders.begin(); it != folders.end(); ++it)
02668         {
02669                 gInventory.updateCategory(*it);
02670         }
02671         gInventory.notifyObservers();
02672 
02673         // *HACK: Do the 'show' logic for a new item in the inventory.
02674         LLInventoryView* view = LLInventoryView::getActiveInventory();
02675         if(view)
02676         {
02677                 view->getPanel()->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO);
02678         }
02679 }
02680 
02681 //      static
02682 void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg,
02683                                                                                                         void**)
02684 {
02685         lldebugs << "LLInventoryModel::processRemoveInventoryFolder()" << llendl;
02686         LLUUID agent_id, folder_id;
02687         msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
02688         if(agent_id != gAgent.getID())
02689         {
02690                 llwarns << "Got a RemoveInventoryFolder for the wrong agent."
02691                                 << llendl;
02692                 return;
02693         }
02694         std::vector<LLUUID> folder_ids;
02695         update_map_t update;
02696         S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
02697         for(S32 i = 0; i < count; ++i)
02698         {
02699                 msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, folder_id, i);
02700                 LLViewerInventoryCategory* folderp = gInventory.getCategory(folder_id);
02701                 if(folderp)
02702                 {
02703                         --update[folderp->getParentUUID()];
02704                         folder_ids.push_back(folder_id);
02705                 }
02706         }
02707         gInventory.accountForUpdate(update);
02708         for(std::vector<LLUUID>::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it)
02709         {
02710                 gInventory.deleteObject(*it);
02711         }
02712         gInventory.notifyObservers();
02713 }
02714 
02715 //      static
02716 void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
02717                                                                                                          void**)
02718 {
02719         LLUUID agent_id;
02720         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
02721         if(agent_id != gAgent.getID())
02722         {
02723                 llwarns << "Got a SaveAssetIntoInventory message for the wrong agent."
02724                                 << llendl;
02725                 return;
02726         }
02727 
02728         LLUUID item_id;
02729         msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
02730 
02731         // The viewer ignores the asset id because this message is only
02732         // used for attachments/objects, so the asset id is not used in
02733         // the viewer anyway.
02734         lldebugs << "LLInventoryModel::processSaveAssetIntoInventory itemID="
02735                 << item_id << llendl;
02736         LLViewerInventoryItem* item = gInventory.getItem( item_id );
02737         if( item )
02738         {
02739                 LLCategoryUpdate up(item->getParentUUID(), 0);
02740                 gInventory.accountForUpdate(up);
02741                 gInventory.addChangedMask( LLInventoryObserver::INTERNAL, item_id);
02742                 gInventory.notifyObservers();
02743         }
02744         else
02745         {
02746                 llinfos << "LLInventoryModel::processSaveAssetIntoInventory item"
02747                         " not found: " << item_id << llendl;
02748         }
02749         if(gViewerWindow)
02750         {
02751                 gViewerWindow->getWindow()->decBusyCount();
02752         }
02753 }
02754 
02755 struct InventoryCallbackInfo
02756 {
02757         InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) :
02758                 mCallback(callback), mInvID(inv_id) {}
02759         U32 mCallback;
02760         LLUUID mInvID;
02761 };
02762 
02763 // static
02764 void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
02765 {
02766         LLUUID agent_id;
02767         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
02768         if(agent_id != gAgent.getID())
02769         {
02770                 llwarns << "Got a BulkUpdateInventory for the wrong agent." << llendl;
02771                 return;
02772         }
02773         LLUUID tid;
02774         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid);
02775         llinfos << "Bulk inventory: " << tid << llendl;
02776 
02777         update_map_t update;
02778         cat_array_t folders;
02779         S32 count;
02780         S32 i;
02781         count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
02782         for(i = 0; i < count; ++i)
02783         {
02784                 LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
02785                 tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
02786                 //llinfos << "unpaked folder '" << tfolder->getName() << "' ("
02787                 //              << tfolder->getUUID() << ") in " << tfolder->getParentUUID()
02788                 //              << llendl;
02789                 if(tfolder->getUUID().notNull())
02790                 {
02791                         folders.push_back(tfolder);
02792                         LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
02793                         if(folderp)
02794                         {
02795                                 if(tfolder->getParentUUID() == folderp->getParentUUID())
02796                                 {
02797                                         update[tfolder->getParentUUID()];
02798                                 }
02799                                 else
02800                                 {
02801                                         ++update[tfolder->getParentUUID()];
02802                                         --update[folderp->getParentUUID()];
02803                                 }
02804                         }
02805                         else
02806                         {
02807                                 // we could not find the folder, so it is probably
02808                                 // new. However, we only want to attempt accounting
02809                                 // for the parent if we can find the parent.
02810                                 folderp = gInventory.getCategory(tfolder->getParentUUID());
02811                                 if(folderp)
02812                                 {
02813                                         ++update[tfolder->getParentUUID()];
02814                                 }
02815                         }
02816                 }
02817         }
02818 
02819 
02820         count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
02821         std::vector<LLUUID> wearable_ids;
02822         item_array_t items;
02823         std::list<InventoryCallbackInfo> cblist;
02824         for(i = 0; i < count; ++i)
02825         {
02826                 LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
02827                 titem->unpackMessage(msg, _PREHASH_ItemData, i);
02828                 //llinfos << "unpaked item '" << titem->getName() << "' in "
02829                 //              << titem->getParentUUID() << llendl;
02830                 U32 callback_id;
02831                 msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id);
02832                 if(titem->getUUID().notNull())
02833                 {
02834                         items.push_back(titem);
02835                         cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID()));
02836                         if (titem->getInventoryType() == LLInventoryType::IT_WEARABLE)
02837                         {
02838                                 wearable_ids.push_back(titem->getUUID());
02839                         }
02840                         // examine update for changes.
02841                         LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
02842                         if(itemp)
02843                         {
02844                                 if(titem->getParentUUID() == itemp->getParentUUID())
02845                                 {
02846                                         update[titem->getParentUUID()];
02847                                 }
02848                                 else
02849                                 {
02850                                         ++update[titem->getParentUUID()];
02851                                         --update[itemp->getParentUUID()];
02852                                 }
02853                         }
02854                         else
02855                         {
02856                                 LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID());
02857                                 if(folderp)
02858                                 {
02859                                         ++update[titem->getParentUUID()];
02860                                 }
02861                         }
02862                 }
02863                 else
02864                 {
02865                         cblist.push_back(InventoryCallbackInfo(callback_id, LLUUID::null));
02866                 }
02867         }
02868         gInventory.accountForUpdate(update);
02869 
02870         for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit)
02871         {
02872                 gInventory.updateCategory(*cit);
02873         }
02874         for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit)
02875         {
02876                 gInventory.updateItem(*iit);
02877         }
02878         gInventory.notifyObservers();
02879 
02880         // The incoming inventory could span more than one BulkInventoryUpdate packet,
02881         // so record the transaction ID for this purchase, then wear all clothing
02882         // that comes in as part of that transaction ID.  JC
02883         if (LLInventoryView::sWearNewClothing)
02884         {
02885                 LLInventoryView::sWearNewClothingTransactionID = tid;
02886                 LLInventoryView::sWearNewClothing = FALSE;
02887         }
02888 
02889         if (tid == LLInventoryView::sWearNewClothingTransactionID)
02890         {
02891                 count = wearable_ids.size();
02892                 for (i = 0; i < count; ++i)
02893                 {
02894                         LLViewerInventoryItem* wearable_item;
02895                         wearable_item = gInventory.getItem(wearable_ids[i]);
02896                         wear_inventory_item_on_avatar(wearable_item);
02897                 }
02898         }
02899 
02900         std::list<InventoryCallbackInfo>::iterator inv_it;
02901         for (inv_it = cblist.begin(); inv_it != cblist.end(); ++inv_it)
02902         {
02903                 InventoryCallbackInfo cbinfo = (*inv_it);
02904                 gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID);
02905         }
02906         // Don't show the inventory.  We used to call showAgentInventory here.
02907         //LLInventoryView* view = LLInventoryView::getActiveInventory();
02908         //if(view)
02909         //{
02910         //      const BOOL take_keyboard_focus = FALSE;
02911         //      view->setSelection(category.getUUID(), take_keyboard_focus );
02912         //      LLView* focus_view = gFocusMgr.getKeyboardFocus();
02913         //      LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
02914         //      // HACK to open inventory offers that are accepted.  This information
02915         //      // really needs to flow through the instant messages and inventory
02916         //      // transfer/update messages.
02917         //      if (LLInventoryView::sOpenNextNewItem)
02918         //      {
02919         //              view->openSelected();
02920         //              LLInventoryView::sOpenNextNewItem = FALSE;
02921         //      }
02922         //
02923         //      // restore keyboard focus
02924         //      gFocusMgr.setKeyboardFocus(focus_view);
02925         //}
02926 }
02927 
02928 // static
02929 void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
02930 {
02931         LLUUID agent_id;
02932         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
02933         if(agent_id != gAgent.getID())
02934         {
02935                 llwarns << "Got a UpdateInventoryItem for the wrong agent."
02936                                 << llendl;
02937                 return;
02938         }
02939         LLUUID parent_id;
02940         msg->getUUID("AgentData", "FolderID", parent_id);
02941         LLUUID owner_id;
02942         msg->getUUID("AgentData", "OwnerID", owner_id);
02943         S32 version;
02944         msg->getS32("AgentData", "Version", version);
02945         S32 descendents;
02946         msg->getS32("AgentData", "Descendents", descendents);
02947         S32 i;
02948         S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
02949         LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
02950         for(i = 0; i < count; ++i)
02951         {
02952                 tcategory->unpackMessage(msg, _PREHASH_FolderData, i);
02953                 gInventory.updateCategory(tcategory);
02954         }
02955 
02956         count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
02957         LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
02958         for(i = 0; i < count; ++i)
02959         {
02960                 titem->unpackMessage(msg, _PREHASH_ItemData, i);
02961                 gInventory.updateItem(titem);
02962         }
02963 
02964         // set version and descendentcount according to message.
02965         LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id);
02966         if(cat)
02967         {
02968                 cat->setVersion(version);
02969                 cat->setDescendentCount(descendents);
02970         }
02971         gInventory.notifyObservers();
02972 }
02973 
02974 // static
02975 void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
02976 {
02977         lldebugs << "LLInventoryModel::processMoveInventoryItem()" << llendl;
02978         LLUUID agent_id;
02979         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
02980         if(agent_id != gAgent.getID())
02981         {
02982                 llwarns << "Got a MoveInventoryItem message for the wrong agent."
02983                                 << llendl;
02984                 return;
02985         }
02986 
02987         LLUUID item_id;
02988         LLUUID folder_id;
02989         char new_name[MAX_STRING];              /*Flawfinder: ignore*/
02990         bool anything_changed = false;
02991         S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
02992         for(S32 i = 0; i < count; ++i)
02993         {
02994                 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
02995                 LLViewerInventoryItem* item = gInventory.getItem(item_id);
02996                 if(item)
02997                 {
02998                         LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
02999                         msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_FolderID, folder_id, i);
03000                         msg->getString("InventoryData", "NewName", MAX_STRING, new_name, i);
03001 
03002                         lldebugs << "moving item " << item_id << " to folder "
03003                                          << folder_id << llendl;
03004                         update_list_t update;
03005                         LLCategoryUpdate old_folder(item->getParentUUID(), -1);
03006                         update.push_back(old_folder);
03007                         LLCategoryUpdate new_folder(folder_id, 1);
03008                         update.push_back(new_folder);
03009                         gInventory.accountForUpdate(update);
03010 
03011                         new_item->setParent(folder_id);
03012                         if(strlen(new_name) > 0)                /*Flawfinder: ignore*/
03013                         {
03014                                 new_item->rename(new_name);
03015                         }
03016                         gInventory.updateItem(new_item);
03017                         anything_changed = true;
03018                 }
03019                 else
03020                 {
03021                         llinfos << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << llendl;
03022                 }
03023         }
03024         if(anything_changed)
03025         {
03026                 gInventory.notifyObservers();
03027         }
03028 }
03029 
03030 // *NOTE: DEBUG functionality
03031 void LLInventoryModel::dumpInventory()
03032 {
03033         llinfos << "\nBegin Inventory Dump\n**********************:" << llendl;
03034         llinfos << "mCategroy[] contains " << mCategoryMap.size() << " items." << llendl;
03035         for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
03036         {
03037                 LLViewerInventoryCategory* cat = cit->second;
03038                 if(cat)
03039                 {
03040                         llinfos << "  " <<  cat->getUUID() << " '" << cat->getName() << "' "
03041                                         << cat->getVersion() << " " << cat->getDescendentCount()
03042                                         << llendl;
03043                 }
03044                 else
03045                 {
03046                         llinfos << "  NULL!" << llendl;
03047                 }
03048         }       
03049         llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl;
03050         for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
03051         {
03052                 LLViewerInventoryItem* item = iit->second;
03053                 if(item)
03054                 {
03055                         llinfos << "  " << item->getUUID() << " "
03056                                         << item->getName() << llendl;
03057                 }
03058                 else
03059                 {
03060                         llinfos << "  NULL!" << llendl;
03061                 }
03062         }
03063         llinfos << "\n**********************\nEnd Inventory Dump" << llendl;
03064 }
03065 
03069 
03070 bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
03071 {
03072         if(mType == LLAssetType::AT_CATEGORY)
03073         {
03074                 if(cat) return TRUE;
03075         }
03076         if(item)
03077         {
03078                 if(item->getType() == mType) return TRUE;
03079         }
03080         return FALSE;
03081 }
03082 
03083 bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
03084 {
03085         if(mType == LLAssetType::AT_CATEGORY)
03086         {
03087                 if(cat) return FALSE;
03088         }
03089         if(item)
03090         {
03091                 if(item->getType() == mType) return FALSE;
03092                 else return TRUE;
03093         }
03094         return TRUE;
03095 }
03096 
03097 bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
03098 {
03099         if(mType == LLAssetType::AT_CATEGORY)
03100         {
03101                 if(cat) 
03102                 {
03103                         return TRUE;
03104                 }
03105         }
03106         if(item)
03107         {
03108                 if(item->getType() == mType)
03109                 {
03110                         LLPermissions perm = item->getPermissions();
03111                         if ((perm.getMaskBase() & mPerm) == mPerm)
03112                         {
03113                                 return TRUE;
03114                         }
03115                 }
03116         }
03117         return FALSE;
03118 }
03119 
03120 
03121 //bool LLIsClone::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
03122 //{
03123 //      if(cat) return FALSE;
03124 //      if(item)
03125 //      {
03126 //              if(mItemMap->getType() == LLAssetType::AT_CALLINGCARD)
03127 //              {
03128 //                      if((item->getType() == LLAssetType::AT_CALLINGCARD)
03129 //                         && !(item->getCreatorUUID().isNull())
03130 //                         && (item->getCreatorUUID() == mItemMap->getCreatorUUID()))
03131 //                      {
03132 //                              return TRUE;
03133 //                      }
03134 //              }
03135 //              else
03136 //              {
03137 //                      if((item->getType() == mItemMap->getType())
03138 //                         && !(item->getAssetUUID().isNull())
03139 //                         && (item->getAssetUUID() == mItemMap->getAssetUUID())
03140 //                         && (item->getName() == mItemMap->getName()))
03141 //                      {
03142 //                              return TRUE;
03143 //                      }
03144 //              }
03145 //      }
03146 //      return FALSE;
03147 //}
03148 
03149 bool LLBuddyCollector::operator()(LLInventoryCategory* cat,
03150                                                                   LLInventoryItem* item)
03151 {
03152         if(item)
03153         {
03154                 if((LLAssetType::AT_CALLINGCARD == item->getType())
03155                    && (!item->getCreatorUUID().isNull())
03156                    && (item->getCreatorUUID() != gAgent.getID()))
03157                 {
03158                         return true;
03159                 }
03160         }
03161         return false;
03162 }
03163 
03164 
03165 bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat,
03166                                                                                 LLInventoryItem* item)
03167 {
03168         if(item)
03169         {
03170                 if((LLAssetType::AT_CALLINGCARD == item->getType())
03171                    && (item->getCreatorUUID().notNull())
03172                    && (item->getCreatorUUID() != gAgent.getID()))
03173                 {
03174                         mSeen.insert(item->getCreatorUUID());
03175                         return true;
03176                 }
03177         }
03178         return false;
03179 }
03180 
03181 
03182 bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat,
03183                                                                                         LLInventoryItem* item)
03184 {
03185         if(item)
03186         {
03187                 if((LLAssetType::AT_CALLINGCARD == item->getType())
03188                    && (item->getCreatorUUID() == mBuddyID))
03189                 {
03190                         return TRUE;
03191                 }
03192         }
03193         return FALSE;
03194 }
03195 
03196 
03197 bool LLNameCategoryCollector::operator()(
03198         LLInventoryCategory* cat, LLInventoryItem* item)
03199 {
03200         if(cat)
03201         {
03202                 if (!LLString::compareInsensitive(mName.c_str(), cat->getName().c_str()))
03203                 {
03204                         return true;
03205                 }
03206         }
03207         return false;
03208 }
03209 
03210 
03211 
03215 
03216 void LLInventoryCompletionObserver::changed(U32 mask)
03217 {
03218         // scan through the incomplete items and move or erase them as
03219         // appropriate.
03220         if(!mIncomplete.empty())
03221         {
03222                 for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
03223                 {
03224                         LLViewerInventoryItem* item = gInventory.getItem(*it);
03225                         if(!item)
03226                         {
03227                                 it = mIncomplete.erase(it);
03228                                 continue;
03229                         }
03230                         if(item->isComplete())
03231                         {
03232                                 mComplete.push_back(*it);
03233                                 it = mIncomplete.erase(it);
03234                                 continue;
03235                         }
03236                         ++it;
03237                 }
03238                 if(mIncomplete.empty())
03239                 {
03240                         done();
03241                 }
03242         }
03243 }
03244 
03245 void LLInventoryCompletionObserver::watchItem(const LLUUID& id)
03246 {
03247         if(id.notNull())
03248         {
03249                 mIncomplete.push_back(id);
03250         }
03251 }
03252 
03253 
03254 void LLInventoryFetchObserver::changed(U32 mask)
03255 {
03256         // scan through the incomplete items and move or erase them as
03257         // appropriate.
03258         if(!mIncomplete.empty())
03259         {
03260                 for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
03261                 {
03262                         LLViewerInventoryItem* item = gInventory.getItem(*it);
03263                         if(!item)
03264                         {
03265                                 // BUG: This can cause done() to get called prematurely below.
03266                                 // This happens with the LLGestureInventoryFetchObserver that
03267                                 // loads gestures at startup. JC
03268                                 it = mIncomplete.erase(it);
03269                                 continue;
03270                         }
03271                         if(item->isComplete())
03272                         {
03273                                 mComplete.push_back(*it);
03274                                 it = mIncomplete.erase(it);
03275                                 continue;
03276                         }
03277                         ++it;
03278                 }
03279                 if(mIncomplete.empty())
03280                 {
03281                         done();
03282                 }
03283         }
03284         //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl;
03285         //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl;
03286 }
03287 
03288 bool LLInventoryFetchObserver::isEverythingComplete() const
03289 {
03290         return mIncomplete.empty();
03291 }
03292 
03293 void LLInventoryFetchObserver::fetchItems(
03294         const LLInventoryFetchObserver::item_ref_t& ids)
03295 {
03296         LLMessageSystem* msg = gMessageSystem;
03297         BOOL start_new_message = TRUE;
03298         LLUUID owner_id;
03299         for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it)
03300         {
03301                 LLViewerInventoryItem* item = gInventory.getItem(*it);
03302                 if(item)
03303                 {
03304                         if(item->isComplete())
03305                         {
03306                                 // It's complete, so put it on the complete container.
03307                                 mComplete.push_back(*it);
03308                                 continue;
03309                         }
03310                         else
03311                         {
03312                                 owner_id = item->getPermissions().getOwner();
03313                         }
03314                 }
03315                 else
03316                 {
03317                         // assume it's agent inventory.
03318                         owner_id = gAgent.getID();
03319                 }
03320 
03321                 // It's incomplete, so put it on the incomplete container, and
03322                 // pack this on the message.
03323                 mIncomplete.push_back(*it);
03324                 if(start_new_message)
03325                 {
03326                         start_new_message = FALSE;
03327                         msg->newMessageFast(_PREHASH_FetchInventory);
03328                         msg->nextBlockFast(_PREHASH_AgentData);
03329                         msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
03330                         msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
03331                 }
03332                 msg->nextBlockFast(_PREHASH_InventoryData);
03333                 msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
03334                 msg->addUUIDFast(_PREHASH_ItemID, (*it));
03335                 if(msg->isSendFull(NULL))
03336                 {
03337                         start_new_message = TRUE;
03338                         gAgent.sendReliableMessage();
03339                 }
03340         }
03341         if(!start_new_message)
03342         {
03343                 gAgent.sendReliableMessage();
03344         }
03345 }
03346 
03347 // virtual
03348 void LLInventoryFetchDescendentsObserver::changed(U32 mask)
03349 {
03350         for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
03351         {
03352                 LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
03353                 if(!cat)
03354                 {
03355                         it = mIncompleteFolders.erase(it);
03356                         continue;
03357                 }
03358                 if(isComplete(cat))
03359                 {
03360                         mCompleteFolders.push_back(*it);
03361                         it = mIncompleteFolders.erase(it);
03362                         continue;
03363                 }
03364                 ++it;
03365         }
03366         if(mIncompleteFolders.empty())
03367         {
03368                 done();
03369         }
03370 }
03371 
03372 void LLInventoryFetchDescendentsObserver::fetchDescendents(
03373         const folder_ref_t& ids)
03374 {
03375         for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
03376         {
03377                 LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
03378                 if(!cat) continue;
03379                 if(!isComplete(cat))
03380                 {
03381                         cat->fetchDescendents();                //blindly fetch it without seeing if anything else is fetching it.
03382                         mIncompleteFolders.push_back(*it);      //Add to list of things being downloaded for this observer.
03383                 }
03384                 else
03385                 {
03386                         mCompleteFolders.push_back(*it);
03387                 }
03388         }
03389 }
03390 
03391 bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const
03392 {
03393         return mIncompleteFolders.empty();
03394 }
03395 
03396 bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat)
03397 {
03398         S32 version = cat->getVersion();
03399         S32 descendents = cat->getDescendentCount();
03400         if((LLViewerInventoryCategory::VERSION_UNKNOWN == version)
03401            || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents))
03402         {
03403                 return false;
03404         }
03405         // it might be complete - check known descendents against
03406         // currently available.
03407         LLInventoryModel::cat_array_t* cats;
03408         LLInventoryModel::item_array_t* items;
03409         gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
03410         if(!cats || !items)
03411         {
03412                 // bit of a hack - pretend we're done if they are gone or
03413                 // incomplete. should never know, but it would suck if this
03414                 // kept tight looping because of a corrupt memory state.
03415                 return true;
03416         }
03417         S32 known = cats->count() + items->count();
03418         if(descendents == known)
03419         {
03420                 // hey - we're done.
03421                 return true;
03422         }
03423         return false;
03424 }
03425 
03426 void LLInventoryFetchComboObserver::changed(U32 mask)
03427 {
03428         if(!mIncompleteItems.empty())
03429         {
03430                 for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); )
03431                 {
03432                         LLViewerInventoryItem* item = gInventory.getItem(*it);
03433                         if(!item)
03434                         {
03435                                 it = mIncompleteItems.erase(it);
03436                                 continue;
03437                         }
03438                         if(item->isComplete())
03439                         {
03440                                 mCompleteItems.push_back(*it);
03441                                 it = mIncompleteItems.erase(it);
03442                                 continue;
03443                         }
03444                         ++it;
03445                 }
03446         }
03447         if(!mIncompleteFolders.empty())
03448         {
03449                 for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
03450                 {
03451                         LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
03452                         if(!cat)
03453                         {
03454                                 it = mIncompleteFolders.erase(it);
03455                                 continue;
03456                         }
03457                         if(gInventory.isCategoryComplete(*it))
03458                         {
03459                                 mCompleteFolders.push_back(*it);
03460                                 it = mIncompleteFolders.erase(it);
03461                                 continue;
03462                         }
03463                         ++it;
03464                 }
03465         }
03466         if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty())
03467         {
03468                 mDone = true;
03469                 done();
03470         }
03471 }
03472 
03473 void LLInventoryFetchComboObserver::fetch(
03474         const folder_ref_t& folder_ids,
03475         const item_ref_t& item_ids)
03476 {
03477         lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl;
03478         for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit)
03479         {
03480                 LLViewerInventoryCategory* cat = gInventory.getCategory(*fit);
03481                 if(!cat) continue;
03482                 if(!gInventory.isCategoryComplete(*fit))
03483                 {
03484                         cat->fetchDescendents();
03485                         lldebugs << "fetching folder " << *fit <<llendl;
03486                         mIncompleteFolders.push_back(*fit);
03487                 }
03488                 else
03489                 {
03490                         mCompleteFolders.push_back(*fit);
03491                         lldebugs << "completing folder " << *fit <<llendl;
03492                 }
03493         }
03494 
03495         // Now for the items - we fetch everything which is not a direct
03496         // descendent of an incomplete folder because the item will show
03497         // up in an inventory descendents message soon enough so we do not
03498         // have to fetch it individually.
03499         LLUUID owner_id;
03500         LLMessageSystem* msg = gMessageSystem;
03501         bool start_new_message = true;
03502         for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit)
03503         {
03504                 LLViewerInventoryItem* item = gInventory.getItem(*iit);
03505                 if(!item)
03506                 {
03507                         lldebugs << "uanble to find item " << *iit << llendl;
03508                         continue;
03509                 }
03510                 if(item->isComplete())
03511                 {
03512                         // It's complete, so put it on the complete container.
03513                         mCompleteItems.push_back(*iit);
03514                         lldebugs << "completing item " << *iit << llendl;
03515                         continue;
03516                 }
03517                 else
03518                 {
03519                         mIncompleteItems.push_back(*iit);
03520                         owner_id = item->getPermissions().getOwner();
03521                 }
03522                 if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end())
03523                 {
03524                         lldebugs << "fetching item " << *iit << llendl;
03525                         if(start_new_message)
03526                         {
03527                                 start_new_message = false;
03528                                 msg->newMessageFast(_PREHASH_FetchInventory);
03529                                 msg->nextBlockFast(_PREHASH_AgentData);
03530                                 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
03531                                 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
03532                         }
03533                         msg->nextBlockFast(_PREHASH_InventoryData);
03534                         msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
03535                         msg->addUUIDFast(_PREHASH_ItemID, (*iit));
03536                         if(msg->isSendFullFast(_PREHASH_InventoryData))
03537                         {
03538                                 start_new_message = true;
03539                                 gAgent.sendReliableMessage();
03540                         }
03541                 }
03542                 else
03543                 {
03544                         lldebugs << "not worrying about " << *iit << llendl;
03545                 }
03546         }
03547         if(!start_new_message)
03548         {
03549                 gAgent.sendReliableMessage();
03550         }
03551 }
03552 
03553 void LLInventoryExistenceObserver::watchItem(const LLUUID& id)
03554 {
03555         if(id.notNull())
03556         {
03557                 mMIA.push_back(id);
03558         }
03559 }
03560 
03561 void LLInventoryExistenceObserver::changed(U32 mask)
03562 {
03563         // scan through the incomplete items and move or erase them as
03564         // appropriate.
03565         if(!mMIA.empty())
03566         {
03567                 for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); )
03568                 {
03569                         LLViewerInventoryItem* item = gInventory.getItem(*it);
03570                         if(!item)
03571                         {
03572                                 ++it;
03573                                 continue;
03574                         }
03575                         mExist.push_back(*it);
03576                         it = mMIA.erase(it);
03577                 }
03578                 if(mMIA.empty())
03579                 {
03580                         done();
03581                 }
03582         }
03583 }
03584 
03585 void LLInventoryAddedObserver::changed(U32 mask)
03586 {
03587         if(!(mask & LLInventoryObserver::ADD))
03588         {
03589                 return;
03590         }
03591 
03592         // *HACK: If this was in response to a packet off
03593         // the network, figure out which item was updated.
03594         // Code from Gigs Taggert, sin allowed by JC.
03595         LLMessageSystem* msg = gMessageSystem;
03596         const char* msg_name = msg->getMessageName();
03597         if (!msg_name) return;
03598 
03599         // We only want newly created inventory items. JC
03600         if ( strcmp(msg_name, "UpdateCreateInventoryItem") )
03601         {
03602                 return;
03603         }
03604 
03605         LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
03606         S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
03607         for(S32 i = 0; i < num_blocks; ++i)
03608         {
03609                 titem->unpackMessage(msg, _PREHASH_InventoryData, i);
03610                 if (!(titem->getUUID().isNull()))
03611                 {
03612                         //we don't do anything with null keys
03613                         mAdded.push_back(titem->getUUID());
03614                 }
03615         }
03616         if (!mAdded.empty())
03617         {
03618                 done();
03619         }
03620 }
03621 
03622 LLInventoryTransactionObserver::LLInventoryTransactionObserver(
03623         const LLTransactionID& transaction_id) :
03624         mTransactionID(transaction_id)
03625 {
03626 }
03627 
03628 void LLInventoryTransactionObserver::changed(U32 mask)
03629 {
03630         if(mask & LLInventoryObserver::ADD)
03631         {
03632                 // This could be it - see if we are processing a bulk update
03633                 LLMessageSystem* msg = gMessageSystem;
03634                 if(msg->getMessageName()
03635                    && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory")))
03636                 {
03637                         // we have a match for the message - now check the
03638                         // transaction id.
03639                         LLUUID id;
03640                         msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id);
03641                         if(id == mTransactionID)
03642                         {
03643                                 // woo hoo, we found it
03644                                 folder_ref_t folders;
03645                                 item_ref_t items;
03646                                 S32 count;
03647                                 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
03648                                 S32 i;
03649                                 for(i = 0; i < count; ++i)
03650                                 {
03651                                         msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i);
03652                                         if(id.notNull())
03653                                         {
03654                                                 folders.push_back(id);
03655                                         }
03656                                 }
03657                                 count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
03658                                 for(i = 0; i < count; ++i)
03659                                 {
03660                                         msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i);
03661                                         if(id.notNull())
03662                                         {
03663                                                 items.push_back(id);
03664                                         }
03665                                 }
03666 
03667                                 // call the derived class the implements this method.
03668                                 done(folders, items);
03669                         }
03670                 }
03671         }
03672 }
03673 
03674 
03678 bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
03679 {
03680         return (item && item->getAssetUUID() == mAssetID);
03681 }
03682 
03683 
03687 
03688 
03689 /*
03690 BOOL decompress_file(const char* src_filename, const char* dst_filename)
03691 {
03692         BOOL rv = FALSE;
03693         gzFile src = NULL;
03694         U8* buffer = NULL;
03695         LLFILE* dst = NULL;
03696         S32 bytes = 0;
03697         const S32 DECOMPRESS_BUFFER_SIZE = 32000;
03698 
03699         // open the files
03700         src = gzopen(src_filename, "rb");
03701         if(!src) goto err_decompress;
03702         dst = LLFile::fopen(dst_filename, "wb");
03703         if(!dst) goto err_decompress;
03704 
03705         // decompress.
03706         buffer = new U8[DECOMPRESS_BUFFER_SIZE + 1];
03707 
03708         do
03709         {
03710                 bytes = gzread(src, buffer, DECOMPRESS_BUFFER_SIZE);
03711                 if (bytes < 0)
03712                 {
03713                         goto err_decompress;
03714                 }
03715 
03716                 fwrite(buffer, bytes, 1, dst);
03717         } while(gzeof(src) == 0);
03718 
03719         // success
03720         rv = TRUE;
03721 
03722  err_decompress:
03723         if(src != NULL) gzclose(src);
03724         if(buffer != NULL) delete[] buffer;
03725         if(dst != NULL) fclose(dst);
03726         return rv;
03727 }
03728 */

Generated on Fri May 16 08:33:43 2008 for SecondLife by  doxygen 1.5.5