aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llinventorymodel.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llinventorymodel.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/llinventorymodel.cpp3519
1 files changed, 3519 insertions, 0 deletions
diff --git a/linden/indra/newview/llinventorymodel.cpp b/linden/indra/newview/llinventorymodel.cpp
new file mode 100644
index 0000000..04506c8
--- /dev/null
+++ b/linden/indra/newview/llinventorymodel.cpp
@@ -0,0 +1,3519 @@
1/**
2 * @file llinventorymodel.cpp
3 * @brief Implementation of the inventory model used to track agent inventory.
4 *
5 * Copyright (c) 2002-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "llviewerprecompiledheaders.h"
29#include "llinventorymodel.h"
30
31#include "llassetstorage.h"
32#include "llcrc.h"
33#include "lldir.h"
34#include "llsys.h"
35#include "llxfermanager.h"
36#include "message.h"
37
38#include "llagent.h"
39#include "llfloater.h"
40#include "llfocusmgr.h"
41#include "llinventoryview.h"
42#include "llviewerinventory.h"
43#include "llviewerwindow.h"
44#include "viewer.h"
45#include "lldbstrings.h"
46#include "llviewerstats.h"
47#include "llmutelist.h"
48#include "llnotify.h"
49#include "llcallbacklist.h"
50#include <deque>
51
52//#define DIFF_INVENTORY_FILES
53#ifdef DIFF_INVENTORY_FILES
54#include "process.h"
55#endif
56
57BOOL LLInventoryModel::sBackgroundFetchActive = FALSE;
58BOOL LLInventoryModel::sAllFoldersFetched = FALSE;
59BOOL LLInventoryModel::sFullFetchStarted = FALSE;
60S32 LLInventoryModel::sNumFetchRetries = 0;
61F32 LLInventoryModel::sMinTimeBetweenFetches = 0.3f;
62F32 LLInventoryModel::sMaxTimeBetweenFetches = 10.f;
63BOOL LLInventoryModel::sTimelyFetchPending = FALSE;
64LLFrameTimer LLInventoryModel::sFetchTimer;
65
66// RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue
67static std::deque<LLUUID> sFetchQueue;
68
69///----------------------------------------------------------------------------
70/// Local function declarations, constants, enums, and typedefs
71///----------------------------------------------------------------------------
72
73//BOOL decompress_file(const char* src_filename, const char* dst_filename);
74const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f;
75const S32 MAX_FETCH_RETRIES = 5;
76const char CACHE_FORMAT_STRING[] = "%s.inv";
77const char* NEW_CATEGORY_NAME = "New Folder";
78const char* NEW_CATEGORY_NAMES[LLAssetType::AT_COUNT] =
79{
80 "Textures", // AT_TEXTURE
81 "Sounds", // AT_SOUND
82 "Calling Cards", // AT_CALLINGCARD
83 "Landmarks", // AT_LANDMARK
84 "Scripts", // AT_SCRIPT (deprecated?)
85 "Clothing", // AT_CLOTHING
86 "Objects", // AT_OBJECT
87 "Notecards", // AT_NOTECARD
88 "New Folder", // AT_CATEGORY
89 "Inventory", // AT_ROOT_CATEGORY
90 "Scripts", // AT_LSL_TEXT
91 "Scripts", // AT_LSL_BYTECODE
92 "Uncompressed Images", // AT_TEXTURE_TGA
93 "Body Parts", // AT_BODYPART
94 "Trash", // AT_TRASH
95 "Photo Album", // AT_SNAPSHOT_CATEGORY
96 "Lost And Found", // AT_LOST_AND_FOUND
97 "Uncompressed Sounds", // AT_SOUND_WAV
98 "Uncompressed Images", // AT_IMAGE_TGA
99 "Uncompressed Images", // AT_IMAGE_JPEG
100 "Animations", // AT_ANIMATION
101 "Gestures", // AT_GESTURE
102};
103
104struct InventoryIDPtrLess
105{
106 bool operator()(const LLViewerInventoryCategory* i1, const LLViewerInventoryCategory* i2) const
107 {
108 return (i1->getUUID() < i2->getUUID());
109 }
110};
111
112class LLCanCache : public LLInventoryCollectFunctor
113{
114public:
115 LLCanCache(LLInventoryModel* model) : mModel(model) {}
116 virtual ~LLCanCache() {}
117 virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
118protected:
119 LLInventoryModel* mModel;
120 std::set<LLUUID> mCachedCatIDs;
121};
122
123bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
124{
125 bool rv = false;
126 if(item)
127 {
128 if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end())
129 {
130 rv = true;
131 }
132 }
133 else if(cat)
134 {
135 // HACK: downcast
136 LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat;
137 if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
138 {
139 S32 descendents_server = c->getDescendentCount();
140 LLInventoryModel::cat_array_t* cats;
141 LLInventoryModel::item_array_t* items;
142 mModel->getDirectDescendentsOf(
143 c->getUUID(),
144 cats,
145 items);
146 S32 descendents_actual = 0;
147 if(cats && items)
148 {
149 descendents_actual = cats->count() + items->count();
150 }
151 if(descendents_server == descendents_actual)
152 {
153 mCachedCatIDs.insert(c->getUUID());
154 rv = true;
155 }
156 }
157 }
158 return rv;
159}
160
161///----------------------------------------------------------------------------
162/// Class LLInventoryModel
163///----------------------------------------------------------------------------
164
165// global for the agent inventory.
166LLInventoryModel gInventory;
167
168// Default constructor
169LLInventoryModel::LLInventoryModel() :
170 mModifyMask(LLInventoryObserver::ALL),
171 mLastItem(NULL)
172{
173}
174
175// Destroys the object
176LLInventoryModel::~LLInventoryModel()
177{
178 empty();
179 for (observer_list_t::iterator iter = mObservers.begin();
180 iter != mObservers.end(); ++iter)
181 {
182 delete *iter;
183 }
184}
185
186// This is a convenience function to check if one object has a parent
187// chain up to the category specified by UUID.
188BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
189 const LLUUID& cat_id)
190{
191 LLInventoryObject* obj = getObject(obj_id);
192 while(obj)
193 {
194 const LLUUID& parent_id = obj->getParentUUID();
195 if( parent_id.isNull() )
196 {
197 return FALSE;
198 }
199 if(parent_id == cat_id)
200 {
201 return TRUE;
202 }
203 // Since we're scanning up the parents, we only need to check
204 // in the category list.
205 obj = getCategory(parent_id);
206 }
207 return FALSE;
208}
209
210// Get the object by id. Returns NULL if not found.
211LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const
212{
213 LLViewerInventoryCategory* cat = getCategory(id);
214 if (cat)
215 {
216 return cat;
217 }
218 LLViewerInventoryItem* item = getItem(id);
219 if (item)
220 {
221 return item;
222 }
223 return NULL;
224}
225
226// Get the item by id. Returns NULL if not found.
227LLViewerInventoryItem* LLInventoryModel::getItem(const LLUUID& id) const
228{
229 LLViewerInventoryItem* item = NULL;
230 if(mLastItem.notNull() && mLastItem->getUUID() == id)
231 {
232 item = mLastItem;
233 }
234 else
235 {
236 item_map_t::const_iterator iter = mItemMap.find(id);
237 if (iter != mItemMap.end())
238 {
239 item = iter->second;
240 mLastItem = item;
241 }
242 }
243 return item;
244}
245
246// Get the category by id. Returns NULL if not found
247LLViewerInventoryCategory* LLInventoryModel::getCategory(const LLUUID& id) const
248{
249 LLViewerInventoryCategory* category = NULL;
250 cat_map_t::const_iterator iter = mCategoryMap.find(id);
251 if (iter != mCategoryMap.end())
252 {
253 category = iter->second;
254 }
255 return category;
256}
257
258S32 LLInventoryModel::getItemCount() const
259{
260 return mItemMap.size();
261}
262
263S32 LLInventoryModel::getCategoryCount() const
264{
265 return mCategoryMap.size();
266}
267
268// Return the direct descendents of the id provided. The array
269// provided points straight into the guts of this object, and
270// should only be used for read operations, since modifications
271// may invalidate the internal state of the inventory. Set passed
272// in values to NULL if the call fails.
273void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
274 cat_array_t*& categories,
275 item_array_t*& items) const
276{
277 categories = get_ptr_in_map(mParentChildCategoryTree, cat_id);
278 items = get_ptr_in_map(mParentChildItemTree, cat_id);
279}
280
281// findCategoryUUIDForType() returns the uuid of the category that
282// specifies 'type' as what it defaults to containing. The category is
283// not necessarily only for that type. *NOTE: This will create a new
284// inventory category on the fly if one does not exist.
285LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t)
286{
287 LLUUID rv = findCatUUID(t);
288 if(rv.isNull())
289 {
290 rv = gAgent.getInventoryRootID();
291 if(rv.notNull())
292 {
293 rv = createNewCategory(rv, t, NULL);
294 }
295 }
296 return rv;
297}
298
299// Internal method which looks for a category with the specified
300// preferred type. Returns LLUUID::null if not found.
301LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type)
302{
303 LLUUID root_id = gAgent.getInventoryRootID();
304 if(LLAssetType::AT_CATEGORY == preferred_type)
305 {
306 return root_id;
307 }
308 if(root_id.notNull())
309 {
310 cat_array_t* cats = NULL;
311 cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
312 if(cats)
313 {
314 S32 count = cats->count();
315 for(S32 i = 0; i < count; ++i)
316 {
317 if(cats->get(i)->getPreferredType() == preferred_type)
318 {
319 return cats->get(i)->getUUID();
320 }
321 }
322 }
323 }
324 return LLUUID::null;
325}
326
327// Convenience function to create a new category. You could call
328// updateCatgory() with a newly generated UUID category, but this
329// version will take care of details like what the name should be
330// based on preferred type. Returns the UUID of the new category.
331LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
332 LLAssetType::EType preferred_type,
333 const LLString& pname)
334{
335 LLUUID id;
336 id.generate();
337 LLString name = pname;
338 if(!pname.empty())
339 {
340 name.assign(pname);
341 }
342 else if((preferred_type >= LLAssetType::AT_TEXTURE) &&
343 (preferred_type < LLAssetType::AT_COUNT))
344 {
345 name.assign(NEW_CATEGORY_NAMES[preferred_type]);
346 }
347 else
348 {
349 name.assign(NEW_CATEGORY_NAME);
350 }
351
352 // Add the category to the internal representation
353 LLPointer<LLViewerInventoryCategory> cat =
354 new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
355 cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
356 cat->setDescendentCount(0);
357 LLCategoryUpdate update(cat->getParentUUID(), 1);
358 accountForUpdate(update);
359 updateCategory(cat);
360
361 // Create the category on the server. We do this to prevent people
362 // from munging their protected folders.
363 LLMessageSystem* msg = gMessageSystem;
364 msg->newMessage("CreateInventoryFolder");
365 msg->nextBlock("AgentData");
366 msg->addUUID("AgentID", gAgent.getID());
367 msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
368 msg->nextBlock("FolderData");
369 cat->packMessage(msg);
370 gAgent.sendReliableMessage();
371
372 // return the folder id of the newly created folder
373 return id;
374}
375
376// Starting with the object specified, add it's descendents to the
377// array provided, but do not add the inventory object specified by
378// id. There is no guaranteed order. Neither array will be erased
379// before adding objects to it. Do not store a copy of the pointers
380// collected - use them, and collect them again later if you need to
381// reference the same objects.
382
383class LLAlwaysCollect : public LLInventoryCollectFunctor
384{
385public:
386 virtual ~LLAlwaysCollect() {}
387 virtual bool operator()(LLInventoryCategory* cat,
388 LLInventoryItem* item)
389 {
390 return TRUE;
391 }
392};
393
394void LLInventoryModel::collectDescendents(const LLUUID& id,
395 cat_array_t& cats,
396 item_array_t& items,
397 BOOL include_trash)
398{
399 LLAlwaysCollect always;
400 collectDescendentsIf(id, cats, items, include_trash, always);
401}
402
403void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
404 cat_array_t& cats,
405 item_array_t& items,
406 BOOL include_trash,
407 LLInventoryCollectFunctor& add)
408{
409 // Start with categories
410 if(!include_trash)
411 {
412 LLUUID trash_id(findCatUUID(LLAssetType::AT_TRASH));
413 if(trash_id.notNull() && (trash_id == id))
414 return;
415 }
416 cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
417 if(cat_array)
418 {
419 S32 count = cat_array->count();
420 for(S32 i = 0; i < count; ++i)
421 {
422 LLViewerInventoryCategory* cat = cat_array->get(i);
423 if(add(cat,NULL))
424 {
425 cats.put(cat);
426 }
427 collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add);
428 }
429 }
430
431 // Move onto items
432 LLViewerInventoryItem* item = NULL;
433 item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
434 if(item_array)
435 {
436 S32 count = item_array->count();
437 for(S32 i = 0; i < count; ++i)
438 {
439 item = item_array->get(i);
440 if(add(NULL, item))
441 {
442 items.put(item);
443 }
444 }
445 }
446}
447
448// Generates a string containing the path to the item specified by
449// item_id.
450void LLInventoryModel::appendPath(const LLUUID& id, LLString& path)
451{
452 LLString temp;
453 LLInventoryObject* obj = getObject(id);
454 LLUUID parent_id;
455 if(obj) parent_id = obj->getParentUUID();
456 LLString forward_slash("/");
457 while(obj)
458 {
459 obj = getCategory(parent_id);
460 if(obj)
461 {
462 temp.assign(forward_slash + obj->getName() + temp);
463 parent_id = obj->getParentUUID();
464 }
465 }
466 path.append(temp);
467}
468
469// Calling this method with an inventory item will either change an
470// existing item with a matching item_id, or will add the item to the
471// current inventory.
472U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
473{
474 U32 mask = LLInventoryObserver::NONE;
475 if(item->getUUID().isNull())
476 {
477 return mask;
478 }
479 LLViewerInventoryItem* old_item = getItem(item->getUUID());
480 if(old_item)
481 {
482 // We already have an old item, modify it's values
483 LLUUID old_parent_id = old_item->getParentUUID();
484 LLUUID new_parent_id = item->getParentUUID();
485 if(old_parent_id != new_parent_id)
486 {
487 // need to update the parent-child tree
488 item_array_t* item_array;
489 item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id);
490 if(item_array)
491 {
492 item_array->removeObj(old_item);
493 }
494 item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id);
495 if(item_array)
496 {
497 item_array->put(old_item);
498 }
499 mask |= LLInventoryObserver::STRUCTURE;
500 }
501 if(old_item->getName() != item->getName())
502 {
503 mask |= LLInventoryObserver::LABEL;
504 }
505 old_item->copy(item);
506 mask |= LLInventoryObserver::INTERNAL;
507 }
508 else
509 {
510 // Simply add this item
511 LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
512 addItem(new_item);
513
514 if(item->getParentUUID().isNull())
515 {
516 LLUUID category_id = findCategoryUUIDForType(new_item->getType());
517 new_item->setParent(category_id);
518 item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id);
519 if( item_array )
520 {
521 // *FIX: bit of a hack to call update server from here...
522 new_item->updateServer(TRUE);
523 item_array->put(new_item);
524 }
525 else
526 {
527 llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl;
528 }
529 }
530 else
531 {
532 // *NOTE: The general scheme is that if every byte of the
533 // uuid is 0, except for the last one or two,the use the
534 // last two bytes of the parent id, and match that up
535 // against the type. For now, we're only worried about
536 // lost & found.
537 LLUUID parent_id = item->getParentUUID();
538 if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID)
539 {
540 parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
541 new_item->setParent(parent_id);
542 }
543 item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
544 if(item_array)
545 {
546 item_array->put(new_item);
547 }
548 else
549 {
550 // Whoops! No such parent, make one.
551 llinfos << "Lost item: " << new_item->getUUID() << " - "
552 << new_item->getName() << llendl;
553 parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
554 new_item->setParent(parent_id);
555 item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
556 if(item_array)
557 {
558 // *FIX: bit of a hack to call update server from
559 // here...
560 new_item->updateServer(TRUE);
561 item_array->put(new_item);
562 }
563 else
564 {
565 llwarns << "Lost and found Not there!!" << llendl;
566 }
567 }
568 }
569 mask |= LLInventoryObserver::ADD;
570 }
571 if(item->getType() == LLAssetType::AT_CALLINGCARD)
572 {
573 mask |= LLInventoryObserver::CALLING_CARD;
574 }
575 addChangedMask(mask, item->getUUID());
576 return mask;
577}
578
579// Calling this method with an inventory category will either change
580// an existing item with the matching id, or it will add the category.
581void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
582{
583 if(cat->getUUID().isNull())
584 {
585 return;
586 }
587 LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
588 if(old_cat)
589 {
590 // We already have an old category, modify it's values
591 U32 mask = LLInventoryObserver::NONE;
592 LLUUID old_parent_id = old_cat->getParentUUID();
593 LLUUID new_parent_id = cat->getParentUUID();
594 if(old_parent_id != new_parent_id)
595 {
596 // need to update the parent-child tree
597 cat_array_t* cat_array;
598 cat_array = get_ptr_in_map(mParentChildCategoryTree, old_parent_id);
599 if(cat_array)
600 {
601 cat_array->removeObj(old_cat);
602 }
603 cat_array = get_ptr_in_map(mParentChildCategoryTree, new_parent_id);
604 if(cat_array)
605 {
606 cat_array->put(old_cat);
607 }
608 mask |= LLInventoryObserver::STRUCTURE;
609 }
610 if(old_cat->getName() != cat->getName())
611 {
612 mask |= LLInventoryObserver::LABEL;
613 }
614 old_cat->copy(cat);
615 addChangedMask(mask, cat->getUUID());
616 }
617 else
618 {
619 // add this category
620 LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat->getParentUUID());
621 new_cat->copy(cat);
622 addCategory(new_cat);
623
624 // make sure this category is correctly referenced by it's parent.
625 cat_array_t* cat_array;
626 cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
627 if(cat_array)
628 {
629 cat_array->put(new_cat);
630 }
631
632 // make space in the tree for this category's children.
633 cat_array_t* catsp = new cat_array_t;
634 item_array_t* itemsp = new item_array_t;
635 mParentChildCategoryTree[new_cat->getUUID()] = catsp;
636 mParentChildItemTree[new_cat->getUUID()] = itemsp;
637 addChangedMask(LLInventoryObserver::ADD, cat->getUUID());
638 }
639}
640
641void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
642{
643 lldebugs << "LLInventoryModel::moveObject()" << llendl;
644 if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))
645 {
646 llwarns << "Could not move inventory object " << object_id << " to "
647 << cat_id << llendl;
648 return;
649 }
650 LLViewerInventoryCategory* cat = getCategory(object_id);
651 if(cat && (cat->getParentUUID() != cat_id))
652 {
653 cat_array_t* cat_array;
654 cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
655 if(cat_array) cat_array->removeObj(cat);
656 cat_array = get_ptr_in_map(mParentChildCategoryTree, cat_id);
657 cat->setParent(cat_id);
658 if(cat_array) cat_array->put(cat);
659 addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
660 return;
661 }
662 LLViewerInventoryItem* item = getItem(object_id);
663 if(item && (item->getParentUUID() != cat_id))
664 {
665 item_array_t* item_array;
666 item_array = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
667 if(item_array) item_array->removeObj(item);
668 item_array = get_ptr_in_map(mParentChildItemTree, cat_id);
669 item->setParent(cat_id);
670 if(item_array) item_array->put(item);
671 addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
672 return;
673 }
674}
675
676// Delete a particular inventory object by ID.
677void LLInventoryModel::deleteObject(const LLUUID& id)
678{
679 lldebugs << "LLInventoryModel::deleteObject()" << llendl;
680 LLPointer<LLInventoryObject> obj = getObject(id);
681 if(obj)
682 {
683 lldebugs << "Deleting inventory object " << id << llendl;
684 mLastItem = NULL;
685 LLUUID parent_id = obj->getParentUUID();
686 mCategoryMap.erase(id);
687 mItemMap.erase(id);
688 //mInventory.erase(id);
689 item_array_t* item_list = get_ptr_in_map(mParentChildItemTree, parent_id);
690 if(item_list)
691 {
692 LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
693 item_list->removeObj(item);
694 }
695 cat_array_t* cat_list = get_ptr_in_map(mParentChildCategoryTree, parent_id);
696 if(cat_list)
697 {
698 LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
699 cat_list->removeObj(cat);
700 }
701 item_list = get_ptr_in_map(mParentChildItemTree, id);
702 if(item_list)
703 {
704 delete item_list;
705 mParentChildItemTree.erase(id);
706 }
707 cat_list = get_ptr_in_map(mParentChildCategoryTree, id);
708 if(cat_list)
709 {
710 delete cat_list;
711 mParentChildCategoryTree.erase(id);
712 }
713 addChangedMask(LLInventoryObserver::REMOVE, id);
714 obj = NULL; // delete obj
715 }
716}
717
718// This is a method which collects the descendents of the id
719// provided. If the category is not found, no action is
720// taken. This method goes through the long winded process of
721// cancelling any calling cards, removing server representation of
722// folders, items, etc in a fairly efficient manner.
723void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
724{
725 EHasChildren children = categoryHasChildren(id);
726 if(children == CHILDREN_NO)
727 {
728 llinfos << "Not purging descendents of " << id << llendl;
729 return;
730 }
731 LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
732 if(cat.notNull())
733 {
734 // do the cache accounting
735 llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
736 << llendl;
737 S32 descendents = cat->getDescendentCount();
738 if(descendents > 0)
739 {
740 LLCategoryUpdate up(id, -descendents);
741 accountForUpdate(up);
742 }
743
744 // we know that descendent count is 0, aide since the
745 // accounting may actually not do an update, we should force
746 // it here.
747 cat->setDescendentCount(0);
748
749 // send it upstream
750 LLMessageSystem* msg = gMessageSystem;
751 msg->newMessage("PurgeInventoryDescendents");
752 msg->nextBlock("AgentData");
753 msg->addUUID("AgentID", gAgent.getID());
754 msg->addUUID("SessionID", gAgent.getSessionID());
755 msg->nextBlock("InventoryData");
756 msg->addUUID("FolderID", id);
757 gAgent.sendReliableMessage();
758
759 // unceremoniously remove anything we have locally stored.
760 cat_array_t categories;
761 item_array_t items;
762 collectDescendents(id,
763 categories,
764 items,
765 INCLUDE_TRASH);
766 S32 count = items.count();
767 S32 i;
768 for(i = 0; i < count; ++i)
769 {
770 deleteObject(items.get(i)->getUUID());
771 }
772 count = categories.count();
773 for(i = 0; i < count; ++i)
774 {
775 deleteObject(categories.get(i)->getUUID());
776 }
777 }
778}
779
780void LLInventoryModel::deleteFromServer(LLDynamicArray<LLUUID>& category_ids,
781 LLDynamicArray<LLUUID>& item_ids)
782{
783 // Store off tre UUIDS of parents which are being deleted (thus no
784 // need to increment) and the parents which are being modified. We
785 // have to increment the version of the parent with each message
786 // sent upstream since the dataserver will increment each unique
787 // parent per update message.
788 std::set<LLUUID> ignore_parents;
789 update_map_t inc_parents;
790
791 S32 i;
792 S32 count = category_ids.count();
793 BOOL start_new_message = TRUE;
794 LLMessageSystem* msg = gMessageSystem;
795 LLPointer<LLViewerInventoryCategory> cat;
796 for(i = 0; i < count; i++)
797 {
798 if(start_new_message)
799 {
800 start_new_message = FALSE;
801 msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
802 msg->nextBlockFast(_PREHASH_AgentData);
803 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
804 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
805 }
806 LLUUID cat_id = category_ids.get(i);
807
808 msg->nextBlockFast(_PREHASH_FolderData);
809 msg->addUUIDFast(_PREHASH_FolderID, cat_id);
810 cat = getCategory(cat_id);
811 ignore_parents.insert(cat_id);
812 addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, cat_id);
813 if(cat.notNull() && (ignore_parents.find(cat->getParentUUID())==ignore_parents.end()))
814 {
815 --inc_parents[cat->getParentUUID()];
816 }
817 if(msg->isSendFullFast(_PREHASH_FolderData))
818 {
819 start_new_message = TRUE;
820 msg->nextBlockFast(_PREHASH_ItemData);
821 msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
822 gAgent.sendReliableMessage();
823 accountForUpdate(inc_parents);
824 inc_parents.clear();
825 }
826 }
827
828 count = item_ids.count();
829 std::set<LLUUID>::iterator not_ignored = ignore_parents.end();
830 LLPointer<LLViewerInventoryItem> item;
831 if((0 == count) && (!start_new_message))
832 {
833 msg->nextBlockFast(_PREHASH_ItemData);
834 msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
835 }
836 for(i = 0; i < count; i++)
837 {
838 if(start_new_message)
839 {
840 start_new_message = FALSE;
841 msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
842 msg->nextBlockFast(_PREHASH_AgentData);
843 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
844 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
845 msg->nextBlockFast(_PREHASH_FolderData);
846 msg->addUUIDFast(_PREHASH_FolderID, LLUUID::null);
847 }
848 LLUUID item_id = item_ids.get(i);
849 msg->nextBlockFast(_PREHASH_ItemData);
850 msg->addUUIDFast(_PREHASH_ItemID, item_id);
851 item = getItem(item_id);
852 addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, item_id);
853 if(item.notNull() && (ignore_parents.find(item->getParentUUID()) == not_ignored))
854 {
855 --inc_parents[item->getParentUUID()];
856 }
857 if(msg->isSendFullFast(_PREHASH_ItemData))
858 {
859 start_new_message = TRUE;
860 gAgent.sendReliableMessage();
861 accountForUpdate(inc_parents);
862 inc_parents.clear();
863 }
864 }
865 if(!start_new_message)
866 {
867 gAgent.sendReliableMessage();
868 accountForUpdate(inc_parents);
869 }
870}
871
872// Add/remove an observer. If the observer is destroyed, be sure to
873// remove it.
874void LLInventoryModel::addObserver(LLInventoryObserver* observer)
875{
876 mObservers.insert(observer);
877}
878
879void LLInventoryModel::removeObserver(LLInventoryObserver* observer)
880{
881 mObservers.erase(observer);
882}
883
884// Call this method when it's time to update everyone on a new state,
885// by default, the inventory model will not update observers
886// automatically.
887void LLInventoryModel::notifyObservers()
888{
889 for (observer_list_t::iterator iter = mObservers.begin();
890 iter != mObservers.end(); )
891 {
892 LLInventoryObserver* observer = *iter;
893 observer->changed(mModifyMask);
894 // safe way to incrament since changed may delete entries! (@!##%@!@&*!)
895 iter = mObservers.upper_bound(observer);
896 }
897
898 mModifyMask = LLInventoryObserver::NONE;
899 mChangedItemIDs.clear();
900}
901
902// store flag for change
903// and id of object change applies to
904void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)
905{
906 mModifyMask |= mask;
907 if (referent.notNull())
908 {
909 mChangedItemIDs.insert(referent);
910 }
911}
912
913// This method to prepares a set of mock inventory which provides
914// minimal functionality before the actual arrival of inventory.
915/*
916void LLInventoryModel::mock(const LLUUID& root_id)
917{
918 llinfos << "LLInventoryModel::mock() " << root_id << llendl;
919 if(root_id.isNull())
920 {
921 llwarns << "Not a valid root id" << llendl;
922 return;
923 }
924 LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(
925 root_id,
926 LLUUID::null,
927 LLAssetType::AT_CATEGORY,
928 NEW_CATEGORY_NAMES[LLAssetType::AT_ROOT_CATEGORY],
929 gAgent.getID());
930 addCategory(cat);
931 gInventory.buildParentChildMap();
932}
933*/
934
935void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id)
936{
937 LLViewerInventoryCategory* cat = getCategory(folder_id);
938 if(!cat)
939 {
940 llwarns << "Asked to fetch descendents of non-existent folder: "
941 << folder_id << llendl;
942 return;
943 }
944 //S32 known_descendents = 0;
945 ///cat_array_t* categories = get_ptr_in_map(mParentChildCategoryTree, folder_id);
946 //item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id);
947 //if(categories)
948 //{
949 // known_descendents += categories->count();
950 //}
951 //if(items)
952 //{
953 // known_descendents += items->count();
954 //}
955 if(!cat->fetchDescendents())
956 {
957 //llinfos << "Not fetching descendents" << llendl;
958 }
959}
960
961// static
962bool LLInventoryModel::isEverythingFetched()
963{
964 return (sAllFoldersFetched ? true : false);
965}
966
967//static
968BOOL LLInventoryModel::backgroundFetchActive()
969{
970 return sBackgroundFetchActive;
971}
972
973//static
974void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
975{
976 if (!sAllFoldersFetched)
977 {
978 sBackgroundFetchActive = TRUE;
979 if (cat_id.isNull())
980 {
981 if (!sFullFetchStarted)
982 {
983 sFullFetchStarted = TRUE;
984 sFetchQueue.push_back(gInventoryLibraryRoot);
985 sFetchQueue.push_back(gAgent.getInventoryRootID());
986 gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
987 }
988 }
989 else
990 {
991 // specific folder requests go to front of queue
992 if (sFetchQueue.empty() || sFetchQueue.front() != cat_id)
993 {
994 sFetchQueue.push_front(cat_id);
995 gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
996 }
997 }
998 }
999}
1000
1001//static
1002void LLInventoryModel::stopBackgroundFetch()
1003{
1004 if (sBackgroundFetchActive)
1005 {
1006 sBackgroundFetchActive = FALSE;
1007 gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL);
1008 }
1009}
1010
1011//static
1012void LLInventoryModel::backgroundFetch(void*)
1013{
1014 if (sBackgroundFetchActive)
1015 {
1016 // no more categories to fetch, stop fetch process
1017 if (sFetchQueue.empty())
1018 {
1019 llinfos << "Inventory fetch completed" << llendl;
1020 if (sFullFetchStarted)
1021 {
1022 sAllFoldersFetched = TRUE;
1023 }
1024 stopBackgroundFetch();
1025 return;
1026 }
1027
1028 F32 fast_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.1f);
1029 F32 slow_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.5f);
1030 if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() > slow_fetch_time)
1031 {
1032 // double timeouts on failure
1033 sMinTimeBetweenFetches = llmin(sMinTimeBetweenFetches * 2.f, 10.f);
1034 sMaxTimeBetweenFetches = llmin(sMaxTimeBetweenFetches * 2.f, 120.f);
1035 llinfos << "Inventory fetch times grown to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
1036 // fetch is no longer considered "timely" although we will wait for full time-out
1037 sTimelyFetchPending = FALSE;
1038 }
1039
1040 while(1)
1041 {
1042 if (sFetchQueue.empty())
1043 {
1044 break;
1045 }
1046
1047 if(gDisconnected)
1048 {
1049 // just bail if we are disconnected.
1050 break;
1051 }
1052
1053 LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front());
1054
1055 // category has been deleted, remove from queue.
1056 if (!cat)
1057 {
1058 sFetchQueue.pop_front();
1059 continue;
1060 }
1061
1062 if (sFetchTimer.getElapsedTimeF32() > sMinTimeBetweenFetches &&
1063 LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
1064 {
1065 // category exists but has no children yet, fetch the descendants
1066 // for now, just request every time and rely on retry timer to throttle
1067 if (cat->fetchDescendents())
1068 {
1069 sFetchTimer.reset();
1070 sTimelyFetchPending = TRUE;
1071 }
1072 else
1073 {
1074 // The catagory also tracks if it has expired and here it says it hasn't
1075 // yet. Get out of here because nothing is going to happen until we
1076 // update the timers.
1077 break;
1078 }
1079 }
1080 // do I have all my children?
1081 else if (gInventory.isCategoryComplete(sFetchQueue.front()))
1082 {
1083 // finished with this category, remove from queue
1084 sFetchQueue.pop_front();
1085
1086 // add all children to queue
1087 parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID());
1088 if (cat_it != gInventory.mParentChildCategoryTree.end())
1089 {
1090 cat_array_t* child_categories = cat_it->second;
1091
1092 for (S32 child_num = 0; child_num < child_categories->count(); child_num++)
1093 {
1094 sFetchQueue.push_back(child_categories->get(child_num)->getUUID());
1095 }
1096 }
1097
1098 // we received a response in less than the fast time
1099 if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() < fast_fetch_time)
1100 {
1101 // shrink timeouts based on success
1102 sMinTimeBetweenFetches = llmax(sMinTimeBetweenFetches * 0.8f, 0.3f);
1103 sMaxTimeBetweenFetches = llmax(sMaxTimeBetweenFetches * 0.8f, 10.f);
1104 //llinfos << "Inventory fetch times shrunk to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
1105 }
1106
1107 sTimelyFetchPending = FALSE;
1108 continue;
1109 }
1110 else if (sFetchTimer.getElapsedTimeF32() > sMaxTimeBetweenFetches)
1111 {
1112 // received first packet, but our num descendants does not match db's num descendants
1113 // so try again later
1114 LLUUID fetch_id = sFetchQueue.front();
1115 sFetchQueue.pop_front();
1116
1117 if (sNumFetchRetries++ < MAX_FETCH_RETRIES)
1118 {
1119 // push on back of queue
1120 sFetchQueue.push_back(fetch_id);
1121 }
1122 sTimelyFetchPending = FALSE;
1123 sFetchTimer.reset();
1124 break;
1125 }
1126
1127 // not enough time has elapsed to do a new fetch
1128 break;
1129 }
1130 }
1131}
1132
1133void LLInventoryModel::cache(
1134 const LLUUID& parent_folder_id,
1135 const LLUUID& agent_id)
1136{
1137 lldebugs << "Caching " << parent_folder_id << " for " << agent_id
1138 << llendl;
1139 LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);
1140 if(!root_cat) return;
1141 cat_array_t categories;
1142 categories.put(root_cat);
1143 item_array_t items;
1144
1145 LLCanCache can_cache(this);
1146 can_cache(root_cat, NULL);
1147 collectDescendentsIf(
1148 parent_folder_id,
1149 categories,
1150 items,
1151 INCLUDE_TRASH,
1152 can_cache);
1153 char agent_id_str[UUID_STR_LENGTH];
1154 char inventory_filename[LL_MAX_PATH];
1155 agent_id.toString(agent_id_str);
1156 std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_str));
1157 snprintf(
1158 inventory_filename,
1159 LL_MAX_PATH,
1160 CACHE_FORMAT_STRING,
1161 path.c_str());
1162 saveToFile(inventory_filename, categories, items);
1163 std::string gzip_filename(inventory_filename);
1164 gzip_filename.append(".gz");
1165 if(gzip_file(inventory_filename, gzip_filename.c_str()))
1166 {
1167 lldebugs << "Successfully compressed " << inventory_filename << llendl;
1168 LLFile::remove(inventory_filename);
1169 }
1170 else
1171 {
1172 llwarns << "Unable to compress " << inventory_filename << llendl;
1173 }
1174}
1175
1176
1177void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
1178{
1179 //llinfos << "LLInventoryModel::addCategory()" << llendl;
1180 if(category)
1181 {
1182 // Insert category uniquely into the map
1183 mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
1184 //mInventory[category->getUUID()] = category;
1185 }
1186}
1187
1188void LLInventoryModel::addItem(LLViewerInventoryItem* item)
1189{
1190 //llinfos << "LLInventoryModel::addItem()" << llendl;
1191 if(item)
1192 {
1193 mItemMap[item->getUUID()] = item;
1194 //mInventory[item->getUUID()] = item;
1195 }
1196}
1197
1198// Empty the entire contents
1199void LLInventoryModel::empty()
1200{
1201// llinfos << "LLInventoryModel::empty()" << llendl;
1202 std::for_each(
1203 mParentChildCategoryTree.begin(),
1204 mParentChildCategoryTree.end(),
1205 DeletePairedPointer());
1206 mParentChildCategoryTree.clear();
1207 std::for_each(
1208 mParentChildItemTree.begin(),
1209 mParentChildItemTree.end(),
1210 DeletePairedPointer());
1211 mParentChildItemTree.clear();
1212 mCategoryMap.clear(); // remove all references (should delete entries)
1213 mItemMap.clear(); // remove all references (should delete entries)
1214 mLastItem = NULL;
1215 //mInventory.clear();
1216}
1217
1218void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update)
1219{
1220 LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
1221 if(cat)
1222 {
1223 bool accounted = false;
1224 S32 version = cat->getVersion();
1225 if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
1226 {
1227 S32 descendents_server = cat->getDescendentCount();
1228 LLInventoryModel::cat_array_t* cats;
1229 LLInventoryModel::item_array_t* items;
1230 getDirectDescendentsOf(update.mCategoryID, cats, items);
1231 S32 descendents_actual = 0;
1232 if(cats && items)
1233 {
1234 descendents_actual = cats->count() + items->count();
1235 }
1236 if(descendents_server == descendents_actual)
1237 {
1238 accounted = true;
1239 descendents_actual += update.mDescendentDelta;
1240 cat->setDescendentCount(descendents_actual);
1241 cat->setVersion(++version);
1242 llinfos << "accounted: '" << cat->getName() << "' "
1243 << version << " with " << descendents_actual
1244 << " descendents." << llendl;
1245 }
1246 }
1247 if(!accounted)
1248 {
1249 lldebugs << "No accounting for: '" << cat->getName() << "' "
1250 << version << llendl;
1251 }
1252 }
1253 else
1254 {
1255 llwarns << "No category found for update " << update.mCategoryID
1256 << llendl;
1257 }
1258}
1259
1260void LLInventoryModel::accountForUpdate(
1261 const LLInventoryModel::update_list_t& update)
1262{
1263 update_list_t::const_iterator it = update.begin();
1264 update_list_t::const_iterator end = update.end();
1265 for(; it != end; ++it)
1266 {
1267 accountForUpdate(*it);
1268 }
1269}
1270
1271void LLInventoryModel::accountForUpdate(
1272 const LLInventoryModel::update_map_t& update)
1273{
1274 LLCategoryUpdate up;
1275 update_map_t::const_iterator it = update.begin();
1276 update_map_t::const_iterator end = update.end();
1277 for(; it != end; ++it)
1278 {
1279 up.mCategoryID = (*it).first;
1280 up.mDescendentDelta = (*it).second.mValue;
1281 accountForUpdate(up);
1282 }
1283}
1284
1285
1286/*
1287void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id)
1288{
1289 LLViewerInventoryCategory* cat = getCategory(category_id);
1290 if(cat)
1291 {
1292 S32 version = cat->getVersion();
1293 if(LLViewerInventoryCategory::VERSION_UNKNOWN != version)
1294 {
1295 cat->setVersion(version + 1);
1296 llinfos << "IncrementVersion: " << cat->getName() << " "
1297 << cat->getVersion() << llendl;
1298 }
1299 else
1300 {
1301 llinfos << "Attempt to increment version when unknown: "
1302 << category_id << llendl;
1303 }
1304 }
1305 else
1306 {
1307 llinfos << "Attempt to increment category: " << category_id << llendl;
1308 }
1309}
1310void LLInventoryModel::incrementCategorySetVersion(
1311 const std::set<LLUUID>& categories)
1312{
1313 if(!categories.empty())
1314 {
1315 std::set<LLUUID>::const_iterator it = categories.begin();
1316 std::set<LLUUID>::const_iterator end = categories.end();
1317 for(; it != end; ++it)
1318 {
1319 incrementCategoryVersion(*it);
1320 }
1321 }
1322}
1323*/
1324
1325
1326LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
1327 const LLUUID& cat_id) const
1328{
1329 LLViewerInventoryCategory* cat = getCategory(cat_id);
1330 if(!cat) return CHILDREN_NO;
1331 if(cat->getDescendentCount() > 0)
1332 {
1333 return CHILDREN_YES;
1334 }
1335 if(cat->getDescendentCount() == 0)
1336 {
1337 return CHILDREN_NO;
1338 }
1339 if((cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
1340 || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
1341 {
1342 return CHILDREN_MAYBE;
1343 }
1344
1345 // Shouldn't have to run this, but who knows.
1346 parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID());
1347 if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0)
1348 {
1349 return CHILDREN_YES;
1350 }
1351 parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID());
1352 if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0)
1353 {
1354 return CHILDREN_YES;
1355 }
1356
1357 return CHILDREN_NO;
1358}
1359
1360bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
1361{
1362 LLViewerInventoryCategory* cat = getCategory(cat_id);
1363 if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
1364 {
1365 S32 descendents_server = cat->getDescendentCount();
1366 LLInventoryModel::cat_array_t* cats;
1367 LLInventoryModel::item_array_t* items;
1368 getDirectDescendentsOf(cat_id, cats, items);
1369 S32 descendents_actual = 0;
1370 if(cats && items)
1371 {
1372 descendents_actual = cats->count() + items->count();
1373 }
1374 if(descendents_server == descendents_actual)
1375 {
1376 return true;
1377 }
1378 }
1379 return false;
1380}
1381
1382bool LLInventoryModel::loadSkeleton(
1383 const LLInventoryModel::options_t& options,
1384 const LLUUID& owner_id)
1385{
1386 lldebugs << "importing inventory skeleton for " << owner_id << llendl;
1387
1388 typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
1389 cat_set_t temp_cats;
1390
1391 update_map_t child_counts;
1392
1393 LLUUID id;
1394 LLAssetType::EType preferred_type;
1395 bool rv = true;
1396 for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
1397 {
1398 LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id);
1399 response_t::const_iterator no_response = (*it).end();
1400 response_t::const_iterator skel;
1401 skel = (*it).find("name");
1402 if(skel == no_response) goto clean_cat;
1403 cat->rename(LLString((*skel).second.c_str()));
1404 skel = (*it).find("folder_id");
1405 if(skel == no_response) goto clean_cat;
1406 id.set((*skel).second.c_str());
1407 // if an id is null, it locks the viewer.
1408 if(id.isNull()) goto clean_cat;
1409 cat->setUUID(id);
1410 skel = (*it).find("parent_id");
1411 if(skel == no_response) goto clean_cat;
1412 id.set((*skel).second.c_str());
1413 cat->setParent(id);
1414 skel = (*it).find("type_default");
1415 if(skel == no_response)
1416 {
1417 preferred_type = LLAssetType::AT_NONE;
1418 }
1419 else
1420 {
1421 S32 t = atoi((*skel).second.c_str());
1422 preferred_type = (LLAssetType::EType)t;
1423 }
1424 cat->setPreferredType(preferred_type);
1425 skel = (*it).find("version");
1426 if(skel == no_response) goto clean_cat;
1427 cat->setVersion(atoi((*skel).second.c_str()));
1428 temp_cats.insert(cat);
1429 continue;
1430 clean_cat:
1431 llwarns << "Unable to import near " << cat->getName() << llendl;
1432 rv = false;
1433 //delete cat; // automatic when cat is reasigned or destroyed
1434 }
1435
1436 S32 cached_category_count = 0;
1437 S32 cached_item_count = 0;
1438 if(!temp_cats.empty())
1439 {
1440 cat_array_t categories;
1441 item_array_t items;
1442 char owner_id_str[UUID_STR_LENGTH];
1443 owner_id.toString(owner_id_str);
1444 std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str));
1445 char inventory_filename[LL_MAX_PATH];
1446 snprintf(
1447 inventory_filename,
1448 LL_MAX_PATH,
1449 CACHE_FORMAT_STRING,
1450 path.c_str());
1451 const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN;
1452 std::string gzip_filename(inventory_filename);
1453 gzip_filename.append(".gz");
1454 FILE* fp = LLFile::fopen(gzip_filename.c_str(), "rb");
1455 bool remove_inventory_file = false;
1456 if(fp)
1457 {
1458 fclose(fp);
1459 fp = NULL;
1460 if(gunzip_file(gzip_filename.c_str(), inventory_filename))
1461 {
1462 // we only want to remove the inventory file if it was
1463 // gzipped before we loaded, and we successfully
1464 // gunziped it.
1465 remove_inventory_file = true;
1466 }
1467 else
1468 {
1469 llinfos << "Unable to gunzip " << gzip_filename << llendl;
1470 }
1471 }
1472 if(loadFromFile(inventory_filename, categories, items))
1473 {
1474 // We were able to find a cache of files. So, use what we
1475 // found to generate a set of categories we should add. We
1476 // will go through each category loaded and if the version
1477 // does not match, invalidate the version.
1478 S32 count = categories.count();
1479 cat_set_t::iterator not_cached = temp_cats.end();
1480 std::set<LLUUID> cached_ids;
1481 for(S32 i = 0; i < count; ++i)
1482 {
1483 LLViewerInventoryCategory* cat = categories[i];
1484 cat_set_t::iterator cit = temp_cats.find(cat);
1485 LLViewerInventoryCategory* tcat = *cit;
1486
1487 // we can safely ignore anything loaded from file, but
1488 // not sent down in the skeleton.
1489 if(cit == not_cached) continue;
1490 if(cat->getVersion() != tcat->getVersion())
1491 {
1492 // if the cached version does not match the server version,
1493 // throw away the version we have so we can fetch the
1494 // correct contents the next time the viewer opens the folder.
1495 tcat->setVersion(NO_VERSION);
1496 }
1497 else
1498 {
1499 cached_ids.insert(tcat->getUUID());
1500 }
1501 }
1502
1503 // go ahead and add the cats returned during the download
1504 std::set<LLUUID>::iterator not_cached_id = cached_ids.end();
1505 cached_category_count = cached_ids.size();
1506 for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
1507 {
1508 if(cached_ids.find((*it)->getUUID()) == not_cached_id)
1509 {
1510 // this check is performed so that we do not
1511 // mark new folders in the skeleton (and not in cache)
1512 // as being cached.
1513 LLViewerInventoryCategory *llvic = (*it);
1514 llvic->setVersion(NO_VERSION);
1515 }
1516 addCategory(*it);
1517 ++child_counts[(*it)->getParentUUID()];
1518 }
1519
1520 // Add all the items loaded which are parented to a
1521 // category with a correctly cached parent
1522 count = items.count();
1523 cat_map_t::iterator unparented = mCategoryMap.end();
1524 for(int i = 0; i < count; ++i)
1525 {
1526 cat_map_t::iterator cit = mCategoryMap.find(items[i]->getParentUUID());
1527
1528 if(cit != unparented)
1529 {
1530 LLViewerInventoryCategory* cat = cit->second;
1531 if(cat->getVersion() != NO_VERSION)
1532 {
1533 addItem(items[i]);
1534 cached_item_count += 1;
1535 ++child_counts[cat->getUUID()];
1536 }
1537 }
1538 }
1539 }
1540 else
1541 {
1542 // go ahead and add everything after stripping the version
1543 // information.
1544 for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
1545 {
1546 LLViewerInventoryCategory *llvic = (*it);
1547 llvic->setVersion(NO_VERSION);
1548 addCategory(*it);
1549 }
1550 }
1551
1552 // At this point, we need to set the known descendents for each
1553 // category which successfully cached so that we do not
1554 // needlessly fetch descendents for categories which we have.
1555 update_map_t::iterator no_child_counts = child_counts.end();
1556 update_map_t::iterator the_count;
1557 for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
1558 {
1559 LLViewerInventoryCategory* cat = (*it);
1560 if(cat->getVersion() != NO_VERSION)
1561 {
1562 the_count = child_counts.find(cat->getUUID());
1563 if(the_count != no_child_counts)
1564 {
1565 cat->setDescendentCount((*the_count).second.mValue);
1566 }
1567 else
1568 {
1569 cat->setDescendentCount(0);
1570 }
1571 }
1572 }
1573
1574 if(remove_inventory_file)
1575 {
1576 // clean up the gunzipped file.
1577 LLFile::remove(inventory_filename);
1578 }
1579 categories.clear(); // will unref and delete entries
1580 }
1581
1582 llinfos << "Successfully loaded " << cached_category_count
1583 << " categories and " << cached_item_count << " items from cache."
1584 << llendl;
1585
1586 return rv;
1587}
1588
1589bool LLInventoryModel::loadMeat(
1590 const LLInventoryModel::options_t& options, const LLUUID& owner_id)
1591{
1592 llinfos << "importing inventory for " << owner_id << llendl;
1593 LLPermissions default_perm;
1594 default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null);
1595 LLPointer<LLViewerInventoryItem> item;
1596 LLUUID id;
1597 LLAssetType::EType type;
1598 LLInventoryType::EType inv_type;
1599 bool rv = true;
1600 for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
1601 {
1602 item = new LLViewerInventoryItem;
1603 response_t::const_iterator no_response = (*it).end();
1604 response_t::const_iterator meat;
1605 meat = (*it).find("name");
1606 if(meat == no_response) goto clean_item;
1607 item->rename(LLString((*meat).second.c_str()));
1608 meat = (*it).find("item_id");
1609 if(meat == no_response) goto clean_item;
1610 id.set((*meat).second.c_str());
1611 item->setUUID(id);
1612 meat = (*it).find("parent_id");
1613 if(meat == no_response) goto clean_item;
1614 id.set((*meat).second.c_str());
1615 item->setParent(id);
1616 meat = (*it).find("type");
1617 if(meat == no_response) goto clean_item;
1618 type = (LLAssetType::EType)atoi((*meat).second.c_str());
1619 item->setType(type);
1620 meat = (*it).find("inv_type");
1621 if(meat != no_response)
1622 {
1623 inv_type = (LLInventoryType::EType)atoi((*meat).second.c_str());
1624 item->setInventoryType(inv_type);
1625 }
1626 meat = (*it).find("data_id");
1627 if(meat == no_response) goto clean_item;
1628 id.set((*meat).second.c_str());
1629 if(LLAssetType::AT_CALLINGCARD == type)
1630 {
1631 LLPermissions perm;
1632 perm.init(id, owner_id, LLUUID::null, LLUUID::null);
1633 item->setPermissions(perm);
1634 }
1635 else
1636 {
1637 meat = (*it).find("perm_mask");
1638 if(meat != no_response)
1639 {
1640 PermissionMask perm_mask = atoi((*meat).second.c_str());
1641 default_perm.initMasks(
1642 perm_mask, perm_mask, perm_mask, perm_mask, perm_mask);
1643 }
1644 else
1645 {
1646 default_perm.initMasks(
1647 PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
1648 }
1649 item->setPermissions(default_perm);
1650 item->setAssetUUID(id);
1651 }
1652 meat = (*it).find("flags");
1653 if(meat != no_response)
1654 {
1655 item->setFlags(strtoul((*meat).second.c_str(), NULL, 0));
1656 }
1657 meat = (*it).find("time");
1658 if(meat != no_response)
1659 {
1660 item->setCreationDate(atoi((*meat).second.c_str()));
1661 }
1662 addItem(item);
1663 continue;
1664 clean_item:
1665 llwarns << "Unable to import near " << item->getName() << llendl;
1666 rv = false;
1667 //delete item; // automatic when item is reassigned or destroyed
1668 }
1669 return rv;
1670}
1671
1672// This is a brute force method to rebuild the entire parent-child
1673// relations. The overall operation has O(NlogN) performance, which
1674// should be sufficient for our needs.
1675void LLInventoryModel::buildParentChildMap()
1676{
1677 llinfos << "LLInventoryModel::buildParentChildMap()" << llendl;
1678
1679 // *NOTE: I am skipping the logic around folder version
1680 // synchronization here because it seems if a folder is lost, we
1681 // might actually want to invalidate it at that point - not
1682 // attempt to cache. More time & thought is necessary.
1683
1684 // First the categories. We'll copy all of the categories into a
1685 // temporary container to iterate over (oh for real iterators.)
1686 // While we're at it, we'll allocate the arrays in the trees.
1687 cat_array_t cats;
1688 cat_array_t* catsp;
1689 item_array_t* itemsp;
1690
1691 for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
1692 {
1693 LLViewerInventoryCategory* cat = cit->second;
1694 cats.put(cat);
1695 if (mParentChildCategoryTree.count(cat->getUUID()) == 0)
1696 {
1697 catsp = new cat_array_t;
1698 mParentChildCategoryTree[cat->getUUID()] = catsp;
1699 }
1700 if (mParentChildItemTree.count(cat->getUUID()) == 0)
1701 {
1702 itemsp = new item_array_t;
1703 mParentChildItemTree[cat->getUUID()] = itemsp;
1704 }
1705 }
1706
1707 // Insert a special parent for the root - so that lookups on
1708 // LLUUID::null as the parent work correctly. This is kind of a
1709 // blatent wastes of space since we allocate a block of memory for
1710 // the array, but whatever - it's not that much space.
1711 if (mParentChildCategoryTree.count(LLUUID::null) == 0)
1712 {
1713 catsp = new cat_array_t;
1714 mParentChildCategoryTree[LLUUID::null] = catsp;
1715 }
1716
1717 // Now we have a structure with all of the categories that we can
1718 // iterate over and insert into the correct place in the child
1719 // category tree.
1720 S32 count = cats.count();
1721 S32 i;
1722 S32 lost = 0;
1723 for(i = 0; i < count; ++i)
1724 {
1725 LLViewerInventoryCategory* cat = cats.get(i);
1726 catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
1727 if(catsp)
1728 {
1729 catsp->put(cat);
1730 }
1731 else
1732 {
1733 // *NOTE: This process could be a lot more efficient if we
1734 // used the new MoveInventoryFolder message, but we would
1735 // have to continue to do the update & build here. So, to
1736 // implement it, we would need a set or map of uuid pairs
1737 // which would be (folder_id, new_parent_id) to be sent up
1738 // to the server.
1739 llinfos << "Lost categroy: " << cat->getUUID() << " - "
1740 << cat->getName() << llendl;
1741 ++lost;
1742 // plop it into the lost & found.
1743 LLAssetType::EType pref = cat->getPreferredType();
1744 if(LLAssetType::AT_NONE == pref)
1745 {
1746 cat->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
1747 }
1748 else if(LLAssetType::AT_CATEGORY == pref)
1749 {
1750 // it's the root
1751 cat->setParent(LLUUID::null);
1752 }
1753 else
1754 {
1755 // it's a protected folder.
1756 cat->setParent(gAgent.getInventoryRootID());
1757 }
1758 cat->updateServer(TRUE);
1759 catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
1760 if(catsp)
1761 {
1762 catsp->put(cat);
1763 }
1764 else
1765 {
1766 llwarns << "Lost and found Not there!!" << llendl;
1767 }
1768 }
1769 }
1770 if(lost)
1771 {
1772 llwarns << "Found " << lost << " lost categories." << llendl;
1773 }
1774
1775 // Now the items. We allocated in the last step, so now all we
1776 // have to do is iterate over the items and put them in the right
1777 // place.
1778 item_array_t items;
1779 if(!mItemMap.empty())
1780 {
1781 LLPointer<LLViewerInventoryItem> item;
1782 for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
1783 {
1784 item = (*iit).second;
1785 items.put(item);
1786 }
1787 }
1788 count = items.count();
1789 lost = 0;
1790 std::vector<LLUUID> lost_item_ids;
1791 for(i = 0; i < count; ++i)
1792 {
1793 LLPointer<LLViewerInventoryItem> item;
1794 item = items.get(i);
1795 itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
1796 if(itemsp)
1797 {
1798 itemsp->put(item);
1799 }
1800 else
1801 {
1802 llinfos << "Lost item: " << item->getUUID() << " - "
1803 << item->getName() << llendl;
1804 ++lost;
1805 // plop it into the lost & found.
1806 //
1807 item->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
1808 // move it later using a special message to move items. If
1809 // we update server here, the client might crash.
1810 //item->updateServer();
1811 lost_item_ids.push_back(item->getUUID());
1812 itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
1813 if(itemsp)
1814 {
1815 itemsp->put(item);
1816 }
1817 else
1818 {
1819 llwarns << "Lost and found Not there!!" << llendl;
1820 }
1821 }
1822 }
1823 if(lost)
1824 {
1825 llwarns << "Found " << lost << " lost items." << llendl;
1826 LLMessageSystem* msg = gMessageSystem;
1827 BOOL start_new_message = TRUE;
1828 LLUUID lnf = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
1829 for(std::vector<LLUUID>::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it)
1830 {
1831 if(start_new_message)
1832 {
1833 start_new_message = FALSE;
1834 msg->newMessageFast(_PREHASH_MoveInventoryItem);
1835 msg->nextBlockFast(_PREHASH_AgentData);
1836 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
1837 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
1838 msg->addBOOLFast(_PREHASH_Stamp, FALSE);
1839 }
1840 msg->nextBlockFast(_PREHASH_InventoryData);
1841 msg->addUUIDFast(_PREHASH_ItemID, (*it));
1842 msg->addUUIDFast(_PREHASH_FolderID, lnf);
1843 msg->addString("NewName", NULL);
1844 if(msg->mCurrentSendTotal >= MTUBYTES)
1845 {
1846 start_new_message = TRUE;
1847 gAgent.sendReliableMessage();
1848 }
1849 }
1850 if(!start_new_message)
1851 {
1852 gAgent.sendReliableMessage();
1853 }
1854 }
1855}
1856
1857struct LLUUIDAndName
1858{
1859 LLUUIDAndName() {}
1860 LLUUIDAndName(const LLUUID& id, const LLString& name);
1861 bool operator==(const LLUUIDAndName& rhs) const;
1862 bool operator<(const LLUUIDAndName& rhs) const;
1863 bool operator>(const LLUUIDAndName& rhs) const;
1864
1865 LLUUID mID;
1866 LLString mName;
1867};
1868
1869LLUUIDAndName::LLUUIDAndName(const LLUUID& id, const LLString& name) :
1870 mID(id), mName(name)
1871{
1872}
1873
1874bool LLUUIDAndName::operator==(const LLUUIDAndName& rhs) const
1875{
1876 return ((mID == rhs.mID) && (mName == rhs.mName));
1877}
1878
1879bool LLUUIDAndName::operator<(const LLUUIDAndName& rhs) const
1880{
1881 return (mID < rhs.mID);
1882}
1883
1884bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const
1885{
1886 return (mID > rhs.mID);
1887}
1888
1889// Given the current state of the inventory items, figure out the
1890// clone information. *FIX: This is sub-optimal, since we can insert
1891// this information snurgically, but this makes sure the implementation
1892// works before we worry about optimization.
1893//void LLInventoryModel::recalculateCloneInformation()
1894//{
1895// //dumpInventory();
1896//
1897// // This implements a 'multi-map' like structure to keep track of
1898// // how many clones we find.
1899// typedef LLDynamicArray<LLViewerInventoryItem*> viewer_item_array_t;
1900// typedef std::map<LLUUIDAndName, viewer_item_array_t*> clone_map_t;
1901// clone_map_t clone_map;
1902// LLUUIDAndName id_and_name;
1903// viewer_item_array_t* clones = NULL;
1904// LLViewerInventoryItem* item = NULL;
1905// for(item = (LLViewerInventoryItem*)mItemMap.getFirstData();
1906// item != NULL;
1907// item = (LLViewerInventoryItem*)mItemMap.getNextData())
1908// {
1909// if(item->getType() == LLAssetType::AT_CALLINGCARD)
1910// {
1911// // if it's a calling card, we key off of the creator id, not
1912// // the asset id.
1913// id_and_name.mID = item->getCreatorUUID();
1914// }
1915// else
1916// {
1917// // if it's not a calling card, we key clones from the
1918// // asset id.
1919// id_and_name.mID = item->getAssetUUID();
1920// }
1921// if(id_and_name.mID == LLUUID::null)
1922// {
1923// continue;
1924// }
1925// id_and_name.mName = item->getName();
1926// if(clone_map.checkData(id_and_name))
1927// {
1928// clones = clone_map.getData(id_and_name);
1929// }
1930// else
1931// {
1932// clones = new viewer_item_array_t;
1933// clone_map.addData(id_and_name, clones);
1934// }
1935// clones->put(item);
1936// }
1937//
1938// S32 count = 0;
1939// for(clones = clone_map.getFirstData();
1940// clones != NULL;
1941// clones = clone_map.getNextData())
1942// {
1943// count = clones->count();
1944// for(S32 i = 0; i < count; i++)
1945// {
1946// item = clones->get(i);
1947// item->setCloneCount(count - 1);
1948// //clones[i] = NULL;
1949// }
1950// delete clones;
1951// }
1952// clone_map.removeAllData();
1953// //dumpInventory();
1954//}
1955
1956// static
1957bool LLInventoryModel::loadFromFile(
1958 const char* filename,
1959 LLInventoryModel::cat_array_t& categories,
1960 LLInventoryModel::item_array_t& items)
1961{
1962 llinfos << "LLInventoryModel::loadFromFile(" << filename << ")" << llendl;
1963 FILE* file = LLFile::fopen(filename, "rb");
1964 if(!file)
1965 {
1966 llinfos << "unable to load inventory from: " << filename << llendl;
1967 return false;
1968 }
1969 char buffer[MAX_STRING];
1970 char keyword[MAX_STRING];
1971 while(!feof(file) && fgets(buffer, MAX_STRING, file))
1972 {
1973 sscanf(buffer, " %s", keyword);
1974 if(0 == strcmp("inv_category", keyword))
1975 {
1976 LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null);
1977 if(inv_cat->importFileLocal(file))
1978 {
1979 categories.put(inv_cat);
1980 }
1981 else
1982 {
1983 llwarns << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << llendl;
1984 //delete inv_cat; // automatic when inv_cat is reassigned or destroyed
1985 }
1986 }
1987 else if(0 == strcmp("inv_item", keyword))
1988 {
1989 LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem;
1990 if( inv_item->importFileLocal(file) )
1991 {
1992 // *FIX: Need a better solution, this prevents the
1993 // application from freezing, but breaks inventory
1994 // caching.
1995 if(inv_item->getUUID().isNull())
1996 {
1997 //delete inv_item; // automatic when inv_cat is reassigned or destroyed
1998 llwarns << "Ignoring inventory with null item id: "
1999 << inv_item->getName() << llendl;
2000
2001 }
2002 else
2003 {
2004 items.put(inv_item);
2005 }
2006 }
2007 else
2008 {
2009 llwarns << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << llendl;
2010 //delete inv_item; // automatic when inv_cat is reassigned or destroyed
2011 }
2012 }
2013 else
2014 {
2015 llwarns << "Unknown token in inventory file '" << keyword << "'"
2016 << llendl;
2017 }
2018 }
2019 fclose(file);
2020 return true;
2021}
2022
2023// static
2024bool LLInventoryModel::saveToFile(
2025 const char* filename,
2026 const cat_array_t& categories,
2027 const item_array_t& items)
2028{
2029 llinfos << "LLInventoryModel::saveToFile(" << filename << ")" << llendl;
2030 FILE* file = LLFile::fopen(filename, "wb");
2031 if(!file)
2032 {
2033 llwarns << "unable to save inventory to: " << filename << llendl;
2034 return false;
2035 }
2036
2037 S32 count = categories.count();
2038 S32 i;
2039 for(i = 0; i < count; ++i)
2040 {
2041 LLViewerInventoryCategory* cat = categories[i];
2042 if(cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
2043 {
2044 cat->exportFileLocal(file);
2045 }
2046 }
2047
2048 count = items.count();
2049 for(i = 0; i < count; ++i)
2050 {
2051 items[i]->exportFile(file);
2052 }
2053
2054 fclose(file);
2055 return true;
2056}
2057
2058// message handling functionality
2059// static
2060void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
2061{
2062 //msg->setHandlerFuncFast(_PREHASH_InventoryUpdate,
2063 // processInventoryUpdate,
2064 // NULL);
2065 //msg->setHandlerFuncFast(_PREHASH_UseCachedInventory,
2066 // processUseCachedInventory,
2067 // NULL);
2068 msg->setHandlerFuncFast(_PREHASH_UpdateCreateInventoryItem,
2069 processUpdateCreateInventoryItem,
2070 NULL);
2071 msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem,
2072 processRemoveInventoryItem,
2073 NULL);
2074 msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder,
2075 processUpdateInventoryFolder,
2076 NULL);
2077 msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,
2078 processRemoveInventoryFolder,
2079 NULL);
2080 //msg->setHandlerFuncFast(_PREHASH_ExchangeCallingCard,
2081 // processExchangeCallingcard,
2082 // NULL);
2083 //msg->setHandlerFuncFast(_PREHASH_AddCallingCard,
2084 // processAddCallingcard,
2085 // NULL);
2086 //msg->setHandlerFuncFast(_PREHASH_DeclineCallingCard,
2087 // processDeclineCallingcard,
2088 // NULL);
2089 msg->setHandlerFuncFast(_PREHASH_SaveAssetIntoInventory,
2090 processSaveAssetIntoInventory,
2091 NULL);
2092 msg->setHandlerFuncFast(_PREHASH_BulkUpdateInventory,
2093 processBulkUpdateInventory,
2094 NULL);
2095 msg->setHandlerFunc("InventoryDescendents", processInventoryDescendents);
2096 msg->setHandlerFunc("MoveInventoryItem", processMoveInventoryItem);
2097 msg->setHandlerFunc("FetchInventoryReply", processFetchInventoryReply);
2098}
2099
2100/*
2101//struct LLUpdateInventoryInfo
2102{
2103 LLString mFilenameAndPath;
2104 BOOL mIsComplete;
2105};
2106
2107// static
2108void LLInventoryModel::processInventoryUpdate(LLMessageSystem* msg, void**)
2109{
2110 lldebugs << "LLInventoryModel::processInventoryUpdate()" << llendl;
2111 LLUUID agent_id;
2112 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_AgentID, agent_id);
2113 if(agent_id != gAgent.getID())
2114 {
2115 llwarns << "Got an inventory update for the wrong agent." << llendl;
2116 return;
2117 }
2118
2119 BOOL is_complete;
2120 msg->getBOOLFast(_PREHASH_InventoryData, _PREHASH_IsComplete, is_complete);
2121 char filename[MAX_STRING];
2122 msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, MAX_STRING, filename);
2123
2124 LLUpdateInventoryInfo* info = new LLUpdateInventoryInfo;
2125 info->mFilenameAndPath = gDirUtilp->getExpandedFilename( LL_PATH_TEMP, filename );
2126 info->mIsComplete = is_complete;
2127
2128 gXferManager->requestFile(info->mFilenameAndPath,
2129 filename,
2130 LL_PATH_CACHE, // The remote file is in the data directory
2131 gUserServer,
2132 TRUE, // Delete the remote file after the transfer
2133 processInventoryFile,
2134 (void*)info,
2135 LLXferManager::HIGH_PRIORITY);
2136}
2137
2138// static
2139void LLInventoryModel::processInventoryFile(void** user_data, S32 error_code)
2140{
2141 llinfos << "LLInventoryModel::processInventoryFile()" << llendl;
2142 //gInventory.dumpInventory();
2143 LLUpdateInventoryInfo* info = (LLUpdateInventoryInfo*)user_data;
2144 if(info && (0 == error_code))
2145 {
2146 if(info->mIsComplete)
2147 {
2148 if(gInventory.isLoaded())
2149 {
2150 // it's a complete update, and we've already loaded
2151 // everything. Therefore, we need to blow away
2152 // everything.
2153 gInventory.empty();
2154 }
2155 else
2156 {
2157 // We want to merge with anything that's happened
2158 // before the load. Therefore, we only want to get rid
2159 // of the root category.
2160 gInventory.deleteObject(gAgent.getInventoryRootID());
2161 }
2162 }
2163
2164 // decompress if necessary
2165 const char* filename = info->mFilenameAndPath.c_str();
2166 const char* ext = filename + strlen(filename) - 6;
2167 char dst_filename[LL_MAX_PATH];
2168 if(0 == strnicmp(ext, "gz", 2))
2169 {
2170 // it's a gz file. decmpress it.
2171 dst_filename[0] = '\0';
2172 strncat(dst_filename, filename, (ext - filename));
2173 strcat(dst_filename, "tmp");
2174 BOOL success = gunzip_file(filename, dst_filename);
2175 LLFile::remove(filename);
2176 if(!success)
2177 {
2178 llwarns << "Error loading inventory file. Return code: " << error_code << llendl;
2179 LLNotifyBox::showXml("InventoryNetworkCorruption");
2180 goto exit;
2181 }
2182 filename = dst_filename;
2183 }
2184
2185#ifdef DIFF_INVENTORY_FILES
2186 char agent_id_string[UUID_STR_LENGTH];
2187 char inventory_filename[LL_MAX_PATH];
2188 gAgent.getID().toString(agent_id_string);
2189 sprintf(inventory_filename, CACHE_FORMAT_STRING, gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
2190 char buffer[MAX_STRING];
2191 sprintf(buffer,
2192 "c:/cygwin/bin/diff %s %s",
2193 inventory_filename,
2194 filename);
2195 llinfos << buffer << llendl;
2196 system(buffer);
2197
2198#endif
2199
2200 // load it all up.
2201 gInventory.loadFromFile(filename);
2202 gInventory.buildParentChildMap();
2203 //gInventory.recalculateCloneInformation();
2204 gInventory.addChangedMask(LLInventoryObserver::ALL);
2205 if(info->mIsComplete)
2206 {
2207 // Set loaded to true if it's a complete set because it's
2208 // poosible for someone to accept an inventory category
2209 // before they have their complete inventory. If we marked
2210 // it loaded at that point, when the real inventory
2211 // arrived, it would erase (on the client anyway) the
2212 // existence of that inventory.
2213 gInventory.mIsLoaded = TRUE;
2214
2215 //retrieveIM(gMessageSystem);
2216 llinfos << "complete inventory update" << llendl;
2217
2218 // If we promised you a message on load, here it is
2219 if (gViewerStats->getStat(LLViewerStats::ST_INVENTORY_TOO_LONG) > 0.0)
2220 {
2221 LLNotifyBox::showXml("InventoryLoaded");
2222 }
2223 }
2224 else
2225 {
2226 llinfos << "incomplete inventory update" << llendl;
2227 }
2228 gInventory.notifyObservers();
2229 LLFile::remove(filename);
2230 }
2231 else
2232 {
2233 llwarns << "Eror loading inventory file. Return code: " << error_code
2234 << llendl;
2235 if(error_code == LL_ERR_TCP_TIMEOUT)
2236 {
2237 //llwarns << "Re-requesting inventory" << llendl;
2238 //gInventory.requestFromServer(gAgent.getID());
2239 }
2240 }
2241
2242exit:
2243 delete info;
2244 //gInventory.dumpInventory();
2245}
2246
2247// static
2248void LLInventoryModel::processUseCachedInventory(LLMessageSystem* msg, void**)
2249{
2250 llinfos << "LLInventoryModel::processUseCachedInventory()" << llendl;
2251 char agent_id_string[UUID_STR_LENGTH];
2252 gAgent.getID().toString(agent_id_string);
2253 char filename[LL_MAX_PATH];
2254 sprintf(filename, CACHE_FORMAT_STRING, gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
2255 // We want to merge with anything that's happened before the
2256 // load. Therefore, we only want to get rid of the root category.
2257 gInventory.deleteObject(gAgent.getInventoryRootID());
2258 gInventory.loadFromFile(filename);
2259 gInventory.buildParentChildMap();
2260 //gInventory.recalculateCloneInformation();
2261 gInventory.addChangedMask(LLInventoryObserver::ALL);
2262 gInventory.mIsLoaded = TRUE;
2263 gInventory.notifyObservers();
2264 //retrieveIM();
2265}
2266*/
2267
2268// static
2269void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, void**)
2270{
2271 // do accounting and highlight new items if they arrive
2272 if (gInventory.messageUpdateCore(msg, true, true))
2273 {
2274 U32 callback_id;
2275 LLUUID item_id;
2276 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
2277 msg->getU32Fast(_PREHASH_InventoryData, _PREHASH_CallbackID, callback_id);
2278
2279 gInventoryCallbacks.fire(callback_id, item_id);
2280 }
2281
2282}
2283
2284// static
2285void LLInventoryModel::processFetchInventoryReply(LLMessageSystem* msg, void**)
2286{
2287 // no accounting
2288 gInventory.messageUpdateCore(msg, false, false);
2289}
2290
2291
2292bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, bool highlight_new)
2293{
2294 LLUUID agent_id;
2295 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
2296 if(agent_id != gAgent.getID())
2297 {
2298 llwarns << "Got a inventory update for the wrong agent: " << agent_id
2299 << llendl;
2300 return false;
2301 }
2302 LLPointer<LLViewerInventoryItem> lastitem; // hack
2303 item_array_t items;
2304 update_map_t update;
2305 S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
2306 for(S32 i = 0; i < count; ++i)
2307 {
2308 LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
2309 lastitem = titem;
2310 titem->unpackMessage(msg, _PREHASH_InventoryData, i);
2311 lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
2312 << titem->getUUID() << llendl;
2313 items.push_back(titem);
2314 // examine update for changes.
2315 LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
2316 if(itemp)
2317 {
2318 if(titem->getParentUUID() == itemp->getParentUUID())
2319 {
2320 update[titem->getParentUUID()];
2321 }
2322 else
2323 {
2324 ++update[titem->getParentUUID()];
2325 --update[itemp->getParentUUID()];
2326 }
2327 }
2328 else
2329 {
2330 ++update[titem->getParentUUID()];
2331 }
2332 }
2333 if(account)
2334 {
2335 gInventory.accountForUpdate(update);
2336 }
2337
2338 U32 changes = 0x0;
2339 for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
2340 {
2341 changes |= gInventory.updateItem(*it);
2342 }
2343 gInventory.notifyObservers();
2344 gViewerWindow->getWindow()->decBusyCount();
2345
2346 // *HACK: Do the 'show' logic for a new item in the inventory if
2347 // it is a newly created item.
2348 if (highlight_new
2349 && (changes & LLInventoryObserver::ADD) == LLInventoryObserver::ADD)
2350 {
2351 LLUUID trash_id;
2352 trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
2353 if(!gInventory.isObjectDescendentOf(lastitem->getUUID(), trash_id))
2354 {
2355 bool show_keep_discard = lastitem->getPermissions().getCreator() != gAgent.getID();
2356 switch(lastitem->getType())
2357 {
2358 case LLAssetType::AT_NOTECARD:
2359 open_notecard(
2360 lastitem->getUUID(),
2361 LLString("Note: ") + lastitem->getName(),
2362 show_keep_discard,
2363 LLUUID::null,
2364 FALSE);
2365 break;
2366 case LLAssetType::AT_LANDMARK:
2367 open_landmark(
2368 lastitem->getUUID(),
2369 LLString(" ") + lastitem->getName(),
2370 show_keep_discard,
2371 LLUUID::null,
2372 FALSE);
2373 break;
2374 case LLAssetType::AT_TEXTURE:
2375 open_texture(
2376 lastitem->getUUID(),
2377 LLString("Texture: ") + lastitem->getName(),
2378 show_keep_discard,
2379 LLUUID::null,
2380 FALSE);
2381 break;
2382 default:
2383 break;
2384 }
2385 LLInventoryView* view = LLInventoryView::getActiveInventory();
2386 if(view)
2387 {
2388 LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
2389 BOOL inventory_has_focus = gFocusMgr.childHasKeyboardFocus(view);
2390 BOOL user_is_away = gAwayTimer.getStarted();
2391
2392 // don't select lost and found items if an active user is working in the inventory
2393 if (!gInventory.isObjectDescendentOf(lastitem->getUUID(), lost_and_found_id) ||
2394 !inventory_has_focus ||
2395 user_is_away)
2396 {
2397 LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
2398 LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
2399 view->getPanel()->setSelection(lastitem->getUUID(), TAKE_FOCUS_NO);
2400 // HACK to open inventory offers that are
2401 // accepted. This information really needs to
2402 // flow through the instant messages and
2403 // inventory restore keyboard focus
2404 gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
2405 }
2406 }
2407 }
2408 }
2409 return true;
2410}
2411
2412// static
2413void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
2414{
2415 lldebugs << "LLInventoryModel::processRemoveInventoryItem()" << llendl;
2416 LLUUID agent_id, item_id;
2417 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
2418 if(agent_id != gAgent.getID())
2419 {
2420 llwarns << "Got a RemoveInventoryItem for the wrong agent."
2421 << llendl;
2422 return;
2423 }
2424 S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
2425 std::vector<LLUUID> item_ids;
2426 update_map_t update;
2427 for(S32 i = 0; i < count; ++i)
2428 {
2429 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
2430 LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
2431 if(itemp)
2432 {
2433 // we only bother with the delete and account if we found
2434 // the item - this is usually a back-up for permissions,
2435 // so frequently the item will already be gone.
2436 --update[itemp->getParentUUID()];
2437 item_ids.push_back(item_id);
2438 }
2439 }
2440 gInventory.accountForUpdate(update);
2441 for(std::vector<LLUUID>::iterator it = item_ids.begin(); it != item_ids.end(); ++it)
2442 {
2443 gInventory.deleteObject(*it);
2444 }
2445 gInventory.notifyObservers();
2446}
2447
2448// static
2449void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
2450 void**)
2451{
2452 lldebugs << "LLInventoryModel::processUpdateInventoryFolder()" << llendl;
2453 LLUUID agent_id, folder_id, parent_id;
2454 //char name[DB_INV_ITEM_NAME_BUF_SIZE];
2455 msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
2456 if(agent_id != gAgent.getID())
2457 {
2458 llwarns << "Got an UpdateInventoryFolder for the wrong agent."
2459 << llendl;
2460 return;
2461 }
2462 LLPointer<LLViewerInventoryCategory> lastfolder; // hack
2463 cat_array_t folders;
2464 update_map_t update;
2465 S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
2466 for(S32 i = 0; i < count; ++i)
2467 {
2468 LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
2469 lastfolder = tfolder;
2470 tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
2471 // make sure it's not a protected folder
2472 tfolder->setPreferredType(LLAssetType::AT_NONE);
2473 folders.push_back(tfolder);
2474 // examine update for changes.
2475 LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
2476 if(folderp)
2477 {
2478 if(tfolder->getParentUUID() == folderp->getParentUUID())
2479 {
2480 update[tfolder->getParentUUID()];
2481 }
2482 else
2483 {
2484 ++update[tfolder->getParentUUID()];
2485 --update[folderp->getParentUUID()];
2486 }
2487 }
2488 else
2489 {
2490 ++update[tfolder->getParentUUID()];
2491 }
2492 }
2493 gInventory.accountForUpdate(update);
2494 for (cat_array_t::iterator it = folders.begin(); it != folders.end(); ++it)
2495 {
2496 gInventory.updateCategory(*it);
2497 }
2498 gInventory.notifyObservers();
2499
2500 // *HACK: Do the 'show' logic for a new item in the inventory.
2501 LLInventoryView* view = LLInventoryView::getActiveInventory();
2502 if(view)
2503 {
2504 view->getPanel()->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO);
2505 }
2506}
2507
2508// static
2509void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg,
2510 void**)
2511{
2512 lldebugs << "LLInventoryModel::processRemoveInventoryFolder()" << llendl;
2513 LLUUID agent_id, folder_id;
2514 msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
2515 if(agent_id != gAgent.getID())
2516 {
2517 llwarns << "Got a RemoveInventoryFolder for the wrong agent."
2518 << llendl;
2519 return;
2520 }
2521 std::vector<LLUUID> folder_ids;
2522 update_map_t update;
2523 S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
2524 for(S32 i = 0; i < count; ++i)
2525 {
2526 msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, folder_id, i);
2527 LLViewerInventoryCategory* folderp = gInventory.getCategory(folder_id);
2528 if(folderp)
2529 {
2530 --update[folderp->getParentUUID()];
2531 folder_ids.push_back(folder_id);
2532 }
2533 }
2534 gInventory.accountForUpdate(update);
2535 for(std::vector<LLUUID>::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it)
2536 {
2537 gInventory.deleteObject(*it);
2538 }
2539 gInventory.notifyObservers();
2540}
2541
2542// static
2543void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
2544 void**)
2545{
2546 LLUUID agent_id;
2547 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
2548 if(agent_id != gAgent.getID())
2549 {
2550 llwarns << "Got a SaveAssetIntoInventory message for the wrong agent."
2551 << llendl;
2552 return;
2553 }
2554
2555 LLUUID item_id;
2556 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
2557
2558 // The viewer ignores the asset id because this message is only
2559 // used for attachments/objects, so the asset id is not used in
2560 // the viewer anyway.
2561 lldebugs << "LLInventoryModel::processSaveAssetIntoInventory itemID="
2562 << item_id << llendl;
2563 LLViewerInventoryItem* item = gInventory.getItem( item_id );
2564 if( item )
2565 {
2566 LLCategoryUpdate up(item->getParentUUID(), 0);
2567 gInventory.accountForUpdate(up);
2568 gInventory.addChangedMask( LLInventoryObserver::INTERNAL, item_id);
2569 gInventory.notifyObservers();
2570 }
2571 else
2572 {
2573 llinfos << "LLInventoryModel::processSaveAssetIntoInventory item"
2574 " not found: " << item_id << llendl;
2575 }
2576 if(gViewerWindow)
2577 {
2578 gViewerWindow->getWindow()->decBusyCount();
2579 }
2580}
2581
2582struct InventoryCallbackInfo
2583{
2584 InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) :
2585 mCallback(callback), mInvID(inv_id) {}
2586 U32 mCallback;
2587 LLUUID mInvID;
2588};
2589
2590// static
2591void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
2592{
2593 LLUUID agent_id;
2594 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
2595 if(agent_id != gAgent.getID())
2596 {
2597 llwarns << "Got a BulkUpdateInventory for the wrong agent." << llendl;
2598 return;
2599 }
2600 LLUUID tid;
2601 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid);
2602 llinfos << "Bulk inventory: " << tid << llendl;
2603
2604 update_map_t update;
2605 cat_array_t folders;
2606 S32 count;
2607 S32 i;
2608 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
2609 for(i = 0; i < count; ++i)
2610 {
2611 LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
2612 tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
2613 //llinfos << "unpaked folder '" << tfolder->getName() << "' ("
2614 // << tfolder->getUUID() << ") in " << tfolder->getParentUUID()
2615 // << llendl;
2616 if(tfolder->getUUID().notNull())
2617 {
2618 folders.push_back(tfolder);
2619 LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
2620 if(folderp)
2621 {
2622 if(tfolder->getParentUUID() == folderp->getParentUUID())
2623 {
2624 update[tfolder->getParentUUID()];
2625 }
2626 else
2627 {
2628 ++update[tfolder->getParentUUID()];
2629 --update[folderp->getParentUUID()];
2630 }
2631 }
2632 else
2633 {
2634 // we could not find the folder, so it is probably
2635 // new. However, we only want to attempt accounting
2636 // for the parent if we can find the parent.
2637 folderp = gInventory.getCategory(tfolder->getParentUUID());
2638 if(folderp)
2639 {
2640 ++update[tfolder->getParentUUID()];
2641 }
2642 }
2643 }
2644 }
2645
2646
2647 count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
2648 std::vector<LLUUID> wearable_ids;
2649 item_array_t items;
2650 std::list<InventoryCallbackInfo> cblist;
2651 for(i = 0; i < count; ++i)
2652 {
2653 LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
2654 titem->unpackMessage(msg, _PREHASH_ItemData, i);
2655 //llinfos << "unpaked item '" << titem->getName() << "' in "
2656 // << titem->getParentUUID() << llendl;
2657 U32 callback_id;
2658 msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id);
2659 if(titem->getUUID().notNull())
2660 {
2661 items.push_back(titem);
2662 cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID()));
2663 if (titem->getInventoryType() == LLInventoryType::IT_WEARABLE)
2664 {
2665 wearable_ids.push_back(titem->getUUID());
2666 }
2667 // examine update for changes.
2668 LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
2669 if(itemp)
2670 {
2671 if(titem->getParentUUID() == itemp->getParentUUID())
2672 {
2673 update[titem->getParentUUID()];
2674 }
2675 else
2676 {
2677 ++update[titem->getParentUUID()];
2678 --update[itemp->getParentUUID()];
2679 }
2680 }
2681 else
2682 {
2683 LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID());
2684 if(folderp)
2685 {
2686 ++update[titem->getParentUUID()];
2687 }
2688 }
2689 }
2690 else
2691 {
2692 cblist.push_back(InventoryCallbackInfo(callback_id, LLUUID::null));
2693 }
2694 }
2695 gInventory.accountForUpdate(update);
2696
2697 for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit)
2698 {
2699 gInventory.updateCategory(*cit);
2700 }
2701 for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit)
2702 {
2703 gInventory.updateItem(*iit);
2704 }
2705 gInventory.notifyObservers();
2706
2707 // The incoming inventory could span more than one BulkInventoryUpdate packet,
2708 // so record the transaction ID for this purchase, then wear all clothing
2709 // that comes in as part of that transaction ID. JC
2710 if (LLInventoryView::sWearNewClothing)
2711 {
2712 LLInventoryView::sWearNewClothingTransactionID = tid;
2713 LLInventoryView::sWearNewClothing = FALSE;
2714 }
2715
2716 if (tid == LLInventoryView::sWearNewClothingTransactionID)
2717 {
2718 count = wearable_ids.size();
2719 for (i = 0; i < count; ++i)
2720 {
2721 LLViewerInventoryItem* wearable_item;
2722 wearable_item = gInventory.getItem(wearable_ids[i]);
2723 wear_inventory_item_on_avatar(wearable_item);
2724 }
2725 }
2726
2727 std::list<InventoryCallbackInfo>::iterator inv_it;
2728 for (inv_it = cblist.begin(); inv_it != cblist.end(); ++inv_it)
2729 {
2730 InventoryCallbackInfo cbinfo = (*inv_it);
2731 gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID);
2732 }
2733 // Don't show the inventory. We used to call showAgentInventory here.
2734 //LLInventoryView* view = LLInventoryView::getActiveInventory();
2735 //if(view)
2736 //{
2737 // const BOOL take_keyboard_focus = FALSE;
2738 // view->setSelection(category.getUUID(), take_keyboard_focus );
2739 // LLView* focus_view = gFocusMgr.getKeyboardFocus();
2740 // LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
2741 // // HACK to open inventory offers that are accepted. This information
2742 // // really needs to flow through the instant messages and inventory
2743 // // transfer/update messages.
2744 // if (LLInventoryView::sOpenNextNewItem)
2745 // {
2746 // view->openSelected();
2747 // LLInventoryView::sOpenNextNewItem = FALSE;
2748 // }
2749 //
2750 // // restore keyboard focus
2751 // gFocusMgr.setKeyboardFocus(focus_view, callback);
2752 //}
2753}
2754
2755// static
2756void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
2757{
2758 LLUUID agent_id;
2759 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
2760 if(agent_id != gAgent.getID())
2761 {
2762 llwarns << "Got a UpdateInventoryItem for the wrong agent."
2763 << llendl;
2764 return;
2765 }
2766 LLUUID parent_id;
2767 msg->getUUID("AgentData", "FolderID", parent_id);
2768 LLUUID owner_id;
2769 msg->getUUID("AgentData", "OwnerID", owner_id);
2770 S32 version;
2771 msg->getS32("AgentData", "Version", version);
2772 S32 descendents;
2773 msg->getS32("AgentData", "Descendents", descendents);
2774 S32 i;
2775 S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
2776 LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
2777 for(i = 0; i < count; ++i)
2778 {
2779 tcategory->unpackMessage(msg, _PREHASH_FolderData, i);
2780 gInventory.updateCategory(tcategory);
2781 }
2782
2783 count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
2784 LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
2785 for(i = 0; i < count; ++i)
2786 {
2787 titem->unpackMessage(msg, _PREHASH_ItemData, i);
2788 gInventory.updateItem(titem);
2789 }
2790
2791 // set version and descendentcount according to message.
2792 LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id);
2793 if(cat)
2794 {
2795 cat->setVersion(version);
2796 cat->setDescendentCount(descendents);
2797 }
2798 gInventory.notifyObservers();
2799}
2800
2801// static
2802void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
2803{
2804 lldebugs << "LLInventoryModel::processMoveInventoryItem()" << llendl;
2805 LLUUID agent_id;
2806 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
2807 if(agent_id != gAgent.getID())
2808 {
2809 llwarns << "Got a MoveInventoryItem message for the wrong agent."
2810 << llendl;
2811 return;
2812 }
2813
2814 LLUUID item_id;
2815 LLUUID folder_id;
2816 char new_name[MAX_STRING];
2817 bool anything_changed = false;
2818 S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
2819 for(S32 i = 0; i < count; ++i)
2820 {
2821 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
2822 LLViewerInventoryItem* item = gInventory.getItem(item_id);
2823 if(item)
2824 {
2825 LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
2826 msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_FolderID, folder_id, i);
2827 msg->getString("InventoryData", "NewName", MAX_STRING, new_name, i);
2828
2829 lldebugs << "moving item " << item_id << " to folder "
2830 << folder_id << llendl;
2831 update_list_t update;
2832 LLCategoryUpdate old_folder(item->getParentUUID(), -1);
2833 update.push_back(old_folder);
2834 LLCategoryUpdate new_folder(folder_id, 1);
2835 update.push_back(new_folder);
2836 gInventory.accountForUpdate(update);
2837
2838 new_item->setParent(folder_id);
2839 if(strlen(new_name) > 0)
2840 {
2841 new_item->rename(new_name);
2842 }
2843 gInventory.updateItem(new_item);
2844 anything_changed = true;
2845 }
2846 else
2847 {
2848 llinfos << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << llendl;
2849 }
2850 }
2851 if(anything_changed)
2852 {
2853 gInventory.notifyObservers();
2854 }
2855}
2856
2857// *NOTE: DEBUG functionality
2858void LLInventoryModel::dumpInventory()
2859{
2860 llinfos << "\nBegin Inventory Dump\n**********************:" << llendl;
2861 llinfos << "mCategroy[] contains " << mCategoryMap.size() << " items." << llendl;
2862 for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
2863 {
2864 LLViewerInventoryCategory* cat = cit->second;
2865 if(cat)
2866 {
2867 llinfos << " " << cat->getUUID() << " '" << cat->getName() << "' "
2868 << cat->getVersion() << " " << cat->getDescendentCount()
2869 << llendl;
2870 }
2871 else
2872 {
2873 llinfos << " NULL!" << llendl;
2874 }
2875 }
2876 llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl;
2877 for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
2878 {
2879 LLViewerInventoryItem* item = iit->second;
2880 if(item)
2881 {
2882 llinfos << " " << item->getUUID() << " "
2883 << item->getName() << llendl;
2884 }
2885 else
2886 {
2887 llinfos << " NULL!" << llendl;
2888 }
2889 }
2890 llinfos << "\n**********************\nEnd Inventory Dump" << llendl;
2891}
2892
2893
2894///----------------------------------------------------------------------------
2895/// LLInventoryCollectFunctor implementations
2896///----------------------------------------------------------------------------
2897
2898bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
2899{
2900 if(mType == LLAssetType::AT_CATEGORY)
2901 {
2902 if(cat) return TRUE;
2903 }
2904 if(item)
2905 {
2906 if(item->getType() == mType) return TRUE;
2907 }
2908 return FALSE;
2909}
2910
2911bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
2912{
2913 if(mType == LLAssetType::AT_CATEGORY)
2914 {
2915 if(cat) return FALSE;
2916 }
2917 if(item)
2918 {
2919 if(item->getType() == mType) return FALSE;
2920 else return TRUE;
2921 }
2922 return TRUE;
2923}
2924
2925bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
2926{
2927 if(mType == LLAssetType::AT_CATEGORY)
2928 {
2929 if(cat)
2930 {
2931 return TRUE;
2932 }
2933 }
2934 if(item)
2935 {
2936 if(item->getType() == mType)
2937 {
2938 LLPermissions perm = item->getPermissions();
2939 if ((perm.getMaskBase() & mPerm) == mPerm)
2940 {
2941 return TRUE;
2942 }
2943 }
2944 }
2945 return FALSE;
2946}
2947
2948
2949//bool LLIsClone::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
2950//{
2951// if(cat) return FALSE;
2952// if(item)
2953// {
2954// if(mItemMap->getType() == LLAssetType::AT_CALLINGCARD)
2955// {
2956// if((item->getType() == LLAssetType::AT_CALLINGCARD)
2957// && !(item->getCreatorUUID().isNull())
2958// && (item->getCreatorUUID() == mItemMap->getCreatorUUID()))
2959// {
2960// return TRUE;
2961// }
2962// }
2963// else
2964// {
2965// if((item->getType() == mItemMap->getType())
2966// && !(item->getAssetUUID().isNull())
2967// && (item->getAssetUUID() == mItemMap->getAssetUUID())
2968// && (item->getName() == mItemMap->getName()))
2969// {
2970// return TRUE;
2971// }
2972// }
2973// }
2974// return FALSE;
2975//}
2976
2977bool LLBuddyCollector::operator()(LLInventoryCategory* cat,
2978 LLInventoryItem* item)
2979{
2980 if(item)
2981 {
2982 if((LLAssetType::AT_CALLINGCARD == item->getType())
2983 && (!item->getCreatorUUID().isNull())
2984 && (item->getCreatorUUID() != gAgent.getID()))
2985 {
2986 return true;
2987 }
2988 }
2989 return false;
2990}
2991
2992
2993bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat,
2994 LLInventoryItem* item)
2995{
2996 if(item)
2997 {
2998 if((LLAssetType::AT_CALLINGCARD == item->getType())
2999 && (item->getCreatorUUID().notNull())
3000 && (item->getCreatorUUID() != gAgent.getID()))
3001 {
3002 mSeen.insert(item->getCreatorUUID());
3003 return true;
3004 }
3005 }
3006 return false;
3007}
3008
3009
3010bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat,
3011 LLInventoryItem* item)
3012{
3013 if(item)
3014 {
3015 if((LLAssetType::AT_CALLINGCARD == item->getType())
3016 && (item->getCreatorUUID() == mBuddyID))
3017 {
3018 return TRUE;
3019 }
3020 }
3021 return FALSE;
3022}
3023
3024
3025bool LLNameCategoryCollector::operator()(
3026 LLInventoryCategory* cat, LLInventoryItem* item)
3027{
3028 if(cat)
3029 {
3030 if (!LLString::compareInsensitive(mName.c_str(), cat->getName().c_str()))
3031 {
3032 return true;
3033 }
3034 }
3035 return false;
3036}
3037
3038
3039
3040///----------------------------------------------------------------------------
3041/// Observers
3042///----------------------------------------------------------------------------
3043
3044void LLInventoryCompletionObserver::changed(U32 mask)
3045{
3046 // scan through the incomplete items and move or erase them as
3047 // appropriate.
3048 if(!mIncomplete.empty())
3049 {
3050 for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
3051 {
3052 LLViewerInventoryItem* item = gInventory.getItem(*it);
3053 if(!item)
3054 {
3055 it = mIncomplete.erase(it);
3056 continue;
3057 }
3058 if(item->isComplete())
3059 {
3060 mComplete.push_back(*it);
3061 it = mIncomplete.erase(it);
3062 continue;
3063 }
3064 ++it;
3065 }
3066 if(mIncomplete.empty())
3067 {
3068 done();
3069 }
3070 }
3071}
3072
3073void LLInventoryCompletionObserver::watchItem(const LLUUID& id)
3074{
3075 if(id.notNull())
3076 {
3077 mIncomplete.push_back(id);
3078 }
3079}
3080
3081
3082void LLInventoryFetchObserver::changed(U32 mask)
3083{
3084 // scan through the incomplete items and move or erase them as
3085 // appropriate.
3086 if(!mIncomplete.empty())
3087 {
3088 for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
3089 {
3090 LLViewerInventoryItem* item = gInventory.getItem(*it);
3091 if(!item)
3092 {
3093 // BUG: This can cause done() to get called prematurely below.
3094 // This happens with the LLGestureInventoryFetchObserver that
3095 // loads gestures at startup. JC
3096 it = mIncomplete.erase(it);
3097 continue;
3098 }
3099 if(item->isComplete())
3100 {
3101 mComplete.push_back(*it);
3102 it = mIncomplete.erase(it);
3103 continue;
3104 }
3105 ++it;
3106 }
3107 if(mIncomplete.empty())
3108 {
3109 done();
3110 }
3111 }
3112 //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl;
3113 //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl;
3114}
3115
3116bool LLInventoryFetchObserver::isEverythingComplete() const
3117{
3118 return mIncomplete.empty();
3119}
3120
3121void LLInventoryFetchObserver::fetchItems(
3122 const LLInventoryFetchObserver::item_ref_t& ids)
3123{
3124 LLMessageSystem* msg = gMessageSystem;
3125 BOOL start_new_message = TRUE;
3126 LLUUID owner_id;
3127 for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it)
3128 {
3129 LLViewerInventoryItem* item = gInventory.getItem(*it);
3130 if(item)
3131 {
3132 if(item->isComplete())
3133 {
3134 // It's complete, so put it on the complete container.
3135 mComplete.push_back(*it);
3136 continue;
3137 }
3138 else
3139 {
3140 owner_id = item->getPermissions().getOwner();
3141 }
3142 }
3143 else
3144 {
3145 // assume it's agent inventory.
3146 owner_id = gAgent.getID();
3147 }
3148
3149 // It's incomplete, so put it on the incomplete container, and
3150 // pack this on the message.
3151 mIncomplete.push_back(*it);
3152 if(start_new_message)
3153 {
3154 start_new_message = FALSE;
3155 msg->newMessageFast(_PREHASH_FetchInventory);
3156 msg->nextBlockFast(_PREHASH_AgentData);
3157 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
3158 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
3159 }
3160 msg->nextBlockFast(_PREHASH_InventoryData);
3161 msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
3162 msg->addUUIDFast(_PREHASH_ItemID, (*it));
3163 if(msg->getCurrentSendTotal() >= MTUBYTES)
3164 {
3165 start_new_message = TRUE;
3166 gAgent.sendReliableMessage();
3167 }
3168 }
3169 if(!start_new_message)
3170 {
3171 gAgent.sendReliableMessage();
3172 }
3173}
3174
3175// virtual
3176void LLInventoryFetchDescendentsObserver::changed(U32 mask)
3177{
3178 for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
3179 {
3180 LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
3181 if(!cat)
3182 {
3183 it = mIncompleteFolders.erase(it);
3184 continue;
3185 }
3186 if(isComplete(cat))
3187 {
3188 mCompleteFolders.push_back(*it);
3189 it = mIncompleteFolders.erase(it);
3190 continue;
3191 }
3192 ++it;
3193 }
3194 if(mIncompleteFolders.empty())
3195 {
3196 done();
3197 }
3198}
3199
3200void LLInventoryFetchDescendentsObserver::fetchDescendents(
3201 const folder_ref_t& ids)
3202{
3203 for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
3204 {
3205 LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
3206 if(!cat) continue;
3207 if(!isComplete(cat))
3208 {
3209 cat->fetchDescendents();
3210 mIncompleteFolders.push_back(*it);
3211 }
3212 else
3213 {
3214 mCompleteFolders.push_back(*it);
3215 }
3216 }
3217}
3218
3219bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const
3220{
3221 return mIncompleteFolders.empty();
3222}
3223
3224bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat)
3225{
3226 S32 version = cat->getVersion();
3227 S32 descendents = cat->getDescendentCount();
3228 if((LLViewerInventoryCategory::VERSION_UNKNOWN == version)
3229 || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents))
3230 {
3231 return false;
3232 }
3233 // it might be complete - check known descendents against
3234 // currently available.
3235 LLInventoryModel::cat_array_t* cats;
3236 LLInventoryModel::item_array_t* items;
3237 gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
3238 if(!cats || !items)
3239 {
3240 // bit of a hack - pretend we're done if they are gone or
3241 // incomplete. should never know, but it would suck if this
3242 // kept tight looping because of a corrupt memory state.
3243 return true;
3244 }
3245 S32 known = cats->count() + items->count();
3246 if(descendents == known)
3247 {
3248 // hey - we're done.
3249 return true;
3250 }
3251 return false;
3252}
3253
3254void LLInventoryFetchComboObserver::changed(U32 mask)
3255{
3256 if(!mIncompleteItems.empty())
3257 {
3258 for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); )
3259 {
3260 LLViewerInventoryItem* item = gInventory.getItem(*it);
3261 if(!item)
3262 {
3263 it = mIncompleteItems.erase(it);
3264 continue;
3265 }
3266 if(item->isComplete())
3267 {
3268 mCompleteItems.push_back(*it);
3269 it = mIncompleteItems.erase(it);
3270 continue;
3271 }
3272 ++it;
3273 }
3274 }
3275 if(!mIncompleteFolders.empty())
3276 {
3277 for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
3278 {
3279 LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
3280 if(!cat)
3281 {
3282 it = mIncompleteFolders.erase(it);
3283 continue;
3284 }
3285 if(gInventory.isCategoryComplete(*it))
3286 {
3287 mCompleteFolders.push_back(*it);
3288 it = mIncompleteFolders.erase(it);
3289 continue;
3290 }
3291 ++it;
3292 }
3293 }
3294 if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty())
3295 {
3296 mDone = true;
3297 done();
3298 }
3299}
3300
3301void LLInventoryFetchComboObserver::fetch(
3302 const folder_ref_t& folder_ids,
3303 const item_ref_t& item_ids)
3304{
3305 lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl;
3306 for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit)
3307 {
3308 LLViewerInventoryCategory* cat = gInventory.getCategory(*fit);
3309 if(!cat) continue;
3310 if(!gInventory.isCategoryComplete(*fit))
3311 {
3312 cat->fetchDescendents();
3313 lldebugs << "fetching folder " << *fit <<llendl;
3314 mIncompleteFolders.push_back(*fit);
3315 }
3316 else
3317 {
3318 mCompleteFolders.push_back(*fit);
3319 lldebugs << "completing folder " << *fit <<llendl;
3320 }
3321 }
3322
3323 // Now for the items - we fetch everything which is not a direct
3324 // descendent of an incomplete folder because the item will show
3325 // up in an inventory descendents message soon enough so we do not
3326 // have to fetch it individually.
3327 LLUUID owner_id;
3328 LLMessageSystem* msg = gMessageSystem;
3329 bool start_new_message = true;
3330 for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit)
3331 {
3332 LLViewerInventoryItem* item = gInventory.getItem(*iit);
3333 if(!item)
3334 {
3335 lldebugs << "uanble to find item " << *iit << llendl;
3336 continue;
3337 }
3338 if(item->isComplete())
3339 {
3340 // It's complete, so put it on the complete container.
3341 mCompleteItems.push_back(*iit);
3342 lldebugs << "completing item " << *iit << llendl;
3343 continue;
3344 }
3345 else
3346 {
3347 mIncompleteItems.push_back(*iit);
3348 owner_id = item->getPermissions().getOwner();
3349 }
3350 if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end())
3351 {
3352 lldebugs << "fetching item " << *iit << llendl;
3353 if(start_new_message)
3354 {
3355 start_new_message = false;
3356 msg->newMessageFast(_PREHASH_FetchInventory);
3357 msg->nextBlockFast(_PREHASH_AgentData);
3358 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
3359 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
3360 }
3361 msg->nextBlockFast(_PREHASH_InventoryData);
3362 msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
3363 msg->addUUIDFast(_PREHASH_ItemID, (*iit));
3364 if(msg->isSendFullFast(_PREHASH_InventoryData))
3365 {
3366 start_new_message = true;
3367 gAgent.sendReliableMessage();
3368 }
3369 }
3370 else
3371 {
3372 lldebugs << "not worrying about " << *iit << llendl;
3373 }
3374 }
3375 if(!start_new_message)
3376 {
3377 gAgent.sendReliableMessage();
3378 }
3379}
3380
3381void LLInventoryExistenceObserver::watchItem(const LLUUID& id)
3382{
3383 if(id.notNull())
3384 {
3385 mMIA.push_back(id);
3386 }
3387}
3388
3389void LLInventoryExistenceObserver::changed(U32 mask)
3390{
3391 // scan through the incomplete items and move or erase them as
3392 // appropriate.
3393 if(!mMIA.empty())
3394 {
3395 for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); )
3396 {
3397 LLViewerInventoryItem* item = gInventory.getItem(*it);
3398 if(!item)
3399 {
3400 ++it;
3401 continue;
3402 }
3403 mExist.push_back(*it);
3404 it = mMIA.erase(it);
3405 }
3406 if(mMIA.empty())
3407 {
3408 done();
3409 }
3410 }
3411}
3412
3413LLInventoryTransactionObserver::LLInventoryTransactionObserver(
3414 const LLTransactionID& transaction_id) :
3415 mTransactionID(transaction_id)
3416{
3417}
3418
3419void LLInventoryTransactionObserver::changed(U32 mask)
3420{
3421 if(mask & LLInventoryObserver::ADD)
3422 {
3423 // This could be it - see if we are processing a bulk update
3424 LLMessageSystem* msg = gMessageSystem;
3425 if(msg->getMessageName()
3426 && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory")))
3427 {
3428 // we have a match for the message - now check the
3429 // transaction id.
3430 LLUUID id;
3431 msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id);
3432 if(id == mTransactionID)
3433 {
3434 // woo hoo, we found it
3435 folder_ref_t folders;
3436 item_ref_t items;
3437 S32 count;
3438 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
3439 S32 i;
3440 for(i = 0; i < count; ++i)
3441 {
3442 msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i);
3443 if(id.notNull())
3444 {
3445 folders.push_back(id);
3446 }
3447 }
3448 count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
3449 for(i = 0; i < count; ++i)
3450 {
3451 msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i);
3452 if(id.notNull())
3453 {
3454 items.push_back(id);
3455 }
3456 }
3457
3458 // call the derived class the implements this method.
3459 done(folders, items);
3460 }
3461 }
3462 }
3463}
3464
3465
3466///----------------------------------------------------------------------------
3467/// LLAssetIDMatches
3468///----------------------------------------------------------------------------
3469bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
3470{
3471 return (item && item->getAssetUUID() == mAssetID);
3472}
3473
3474
3475///----------------------------------------------------------------------------
3476/// Local function definitions
3477///----------------------------------------------------------------------------
3478
3479
3480/*
3481BOOL decompress_file(const char* src_filename, const char* dst_filename)
3482{
3483 BOOL rv = FALSE;
3484 gzFile src = NULL;
3485 U8* buffer = NULL;
3486 FILE* dst = NULL;
3487 S32 bytes = 0;
3488 const S32 DECOMPRESS_BUFFER_SIZE = 32000;
3489
3490 // open the files
3491 src = gzopen(src_filename, "rb");
3492 if(!src) goto err_decompress;
3493 dst = LLFile::fopen(dst_filename, "wb");
3494 if(!dst) goto err_decompress;
3495
3496 // decompress.
3497 buffer = new U8[DECOMPRESS_BUFFER_SIZE + 1];
3498
3499 do
3500 {
3501 bytes = gzread(src, buffer, DECOMPRESS_BUFFER_SIZE);
3502 if (bytes < 0)
3503 {
3504 goto err_decompress;
3505 }
3506
3507 fwrite(buffer, bytes, 1, dst);
3508 } while(gzeof(src) == 0);
3509
3510 // success
3511 rv = TRUE;
3512
3513 err_decompress:
3514 if(src != NULL) gzclose(src);
3515 if(buffer != NULL) delete[] buffer;
3516 if(dst != NULL) fclose(dst);
3517 return rv;
3518}
3519*/