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

Generated on Thu Jul 1 06:08:46 2010 for Second Life Viewer by  doxygen 1.4.7