//////////////////////////////////////////////////////////////////////////
// Private methods
//////////////////////////////////////////////////////////////////////////
void LLOutfitsList::computeDifference(
	const LLInventoryModel::cat_array_t& vcats, 
	uuid_vec_t& vadded, 
	uuid_vec_t& vremoved)
{
	uuid_vec_t vnew;
	// Creating a vector of newly collected sub-categories UUIDs.
	for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin();
		iter != vcats.end();
		iter++)
	{
		vnew.push_back((*iter)->getUUID());
	}

	uuid_vec_t vcur;
	// Creating a vector of currently displayed sub-categories UUIDs.
	for (outfits_map_t::const_iterator iter = mOutfitsMap.begin();
		iter != mOutfitsMap.end();
		iter++)
	{
		vcur.push_back((*iter).first);
	}

	LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved);
}
void LLFloaterGesture::done()
{
	//this method can be called twice: for GestureFolder and once after loading all sudir of GestureFolder
	if (gInventory.isCategoryComplete(mGestureFolderID))
	{
		LL_DEBUGS("Gesture")<< "mGestureFolderID loaded" << LL_ENDL;
		// we load only gesture folder without childred.
		LLInventoryModel::cat_array_t* categories;
		LLInventoryModel::item_array_t* items;
		uuid_vec_t unloaded_folders;
		LL_DEBUGS("Gesture")<< "Get subdirs of Gesture Folder...." << LL_ENDL;
		gInventory.getDirectDescendentsOf(mGestureFolderID, categories, items);
		if (categories->empty())
		{
			gInventory.removeObserver(this);
			LL_INFOS("Gesture")<< "Gesture dos NOT contains sub-directories."<< LL_ENDL;
			return;
		}
		LL_DEBUGS("Gesture")<< "There are " << categories->size() << " Folders "<< LL_ENDL;
		for (LLInventoryModel::cat_array_t::iterator it = categories->begin(); it != categories->end(); it++)
		{
			if (!gInventory.isCategoryComplete(it->get()->getUUID()))
			{
				unloaded_folders.push_back(it->get()->getUUID());
				LL_DEBUGS("Gesture")<< it->get()->getName()<< " Folder added to fetchlist"<< LL_ENDL;
			}

		}
		if (!unloaded_folders.empty())
		{
			LL_DEBUGS("Gesture")<< "Fetching subdirectories....." << LL_ENDL;
			setFetchIDs(unloaded_folders);
			startFetch();
		}
		else
		{
			LL_DEBUGS("Gesture")<< "All Gesture subdirectories have been loaded."<< LL_ENDL;
			gInventory.removeObserver(this);
			buildGestureList();
		}
	}
	else
	{
		LL_WARNS("Gesture")<< "Gesture list was NOT loaded"<< LL_ENDL;
	}
}
//static
void LLLocalInventory::saveInvCache(std::string filename, LLFolderView* folder)
{
	LLInventoryModel* model = &gInventory;
	std::set<LLUUID> selected_items;
	folder->getSelectionList(selected_items);
	if(selected_items.size() < 1)
	{
		// No items selected?  Wtfboom
		return;
	}
	LLInventoryModel::cat_array_t cats;
	LLInventoryModel::item_array_t items;
	// Make complete lists of child categories and items
	std::set<LLUUID>::iterator sel_iter = selected_items.begin();
	std::set<LLUUID>::iterator sel_end = selected_items.end();
	for( ; sel_iter != sel_end; ++sel_iter)
	{
		LLInventoryCategory* cat = model->getCategory(*sel_iter);
		if(cat)
		{
			climb(cat, cats, items);
		}
	}
	// And what about items inside a folder that wasn't selected?
	// I guess I will just add selected items, so long as they aren't already added
	for(sel_iter = selected_items.begin(); sel_iter != sel_end; ++sel_iter)
	{
		LLInventoryItem* item = model->getItem(*sel_iter);
		if(item)
		{
			if(std::find(items.begin(), items.end(), item) == items.end())
			{
				items.push_back(LLPointer<LLViewerInventoryItem>((LLViewerInventoryItem*)item));
				LLInventoryCategory* parent = model->getCategory(item->getParentUUID());
				if(std::find(cats.begin(), cats.end(), parent) == cats.end())
				{
					cats.push_back(LLPointer<LLViewerInventoryCategory>((LLViewerInventoryCategory*)parent));
				}
			}
		}
	}
	LLInventoryModel::saveToFile(filename, cats, items);
}
Exemple #4
0
void LLFriendCardsManager::collectFriendsLists(folderid_buddies_map_t& folderBuddiesMap) const
{
	folderBuddiesMap.clear();

	LLInventoryModel::cat_array_t* listFolders;
	LLInventoryModel::item_array_t* items;

	// get folders in the Friend folder. Items should be NULL due to Cards should be in lists.
	gInventory.getDirectDescendentsOf(findFriendFolderUUIDImpl(), listFolders, items);

	if (NULL == listFolders)
		return;

	LLInventoryModel::cat_array_t::const_iterator itCats;	// to iterate Friend Lists (categories)
	LLInventoryModel::item_array_t::const_iterator itBuddy;	// to iterate Buddies in each List
	LLInventoryModel::cat_array_t* fakeCatsArg;
	for (itCats = listFolders->begin(); itCats != listFolders->end(); ++itCats)
	{
		if (items)
			items->clear();

		// *HACK: Only Friends/All content will be shown for now
		// *TODO: Remove this hack, implement sorting if it will be needded by spec.
		if ((*itCats)->getUUID() != findFriendAllSubfolderUUIDImpl())
			continue;

		gInventory.getDirectDescendentsOf((*itCats)->getUUID(), fakeCatsArg, items);

		if (NULL == items)
			continue;

		uuid_vec_t buddyUUIDs;
		for (itBuddy = items->begin(); itBuddy != items->end(); ++itBuddy)
		{
			buddyUUIDs.push_back((*itBuddy)->getCreatorUUID());
		}

		folderBuddiesMap.insert(make_pair((*itCats)->getUUID(), buddyUUIDs));
	}
}
	virtual ~LLOrderMyOutfitsOnDestroy()
	{
		if (!LLApp::isRunning())
		{
			llwarns << "called during shutdown, skipping" << llendl;
			return;
		}
		
		const LLUUID& my_outfits_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
		if (my_outfits_id.isNull()) return;

		LLInventoryModel::cat_array_t* cats;
		LLInventoryModel::item_array_t* items;
		gInventory.getDirectDescendentsOf(my_outfits_id, cats, items);
		if (!cats) return;

		//My Outfits should at least contain saved initial outfit and one another outfit
		if (cats->size() < 2)
		{
			llwarning("My Outfits category was not populated properly", 0);
			return;
		}

		llinfos << "Starting updating My Outfits with wearables ordering information" << llendl;

		for (LLInventoryModel::cat_array_t::iterator outfit_iter = cats->begin();
			outfit_iter != cats->end(); ++outfit_iter)
		{
			const LLUUID& cat_id = (*outfit_iter)->getUUID();
			if (cat_id.isNull()) continue;

			// saved initial outfit already contains wearables ordering information
			if (cat_id == LLAppearanceMgr::getInstance()->getBaseOutfitUUID()) continue;

			LLAppearanceMgr::getInstance()->updateClothingOrderingInfo(cat_id);
		}

		llinfos << "Finished updating My Outfits with wearables ordering information" << llendl;
	}
Exemple #6
0
// LLUUID catID=TraverseCategories("/My Inventory/#Luna/Durp")
LLUUID TraverseCategories(const std::string& target_cat, LLViewerInventoryCategory *ccat, int i)
{
    // Split the path into an array.
    boost::char_separator<char> sep("/");
    boost::tokenizer<boost::char_separator<char> > tok(target_cat,sep);
    std::vector<std::string> vec;
    vec.assign(tok.begin(),tok.end());

    std::string cat = vec[i];
    LLUUID id;
    if(i==0)
    {
        id=gAgent.getInventoryRootID();
    } else {
        id = ccat->getUUID();
    }
    LLInventoryModel::cat_array_t *cats;
    LLInventoryModel::item_array_t *items;

    gInventory.getDirectDescendentsOf(id,cats,items);

    LLInventoryModel::cat_array_t::iterator cat_iter = cats->begin();
    LLInventoryModel::cat_array_t::iterator cat_end = cats->end();
    for( ; cat_iter != cat_end; ++cat_iter)
    {
        LLViewerInventoryCategory *c_cat=(*cat_iter);
        if(c_cat->getName() == cat)
        {
            if(vec.size() == i+1)
            {
                return c_cat->getUUID();
            } else {
                ++i;
                return TraverseCategories(target_cat,*cat_iter,i);
            }
        }
    }
    return LLUUID::null;
}
//static
void LLLocalInventory::loadInvCache(std::string filename)
{
    std::string extension = gDirUtilp->getExtension(filename);
    std::string inv_filename = filename;
    if(extension == "gz")
    {
        LLUUID random;
        random.generate();
        inv_filename = filename.substr(0, filename.length() - 3) + "." + random.asString();

        if(!gunzip_file(filename, inv_filename))
        {
            // failure... message?
            return;
        }
    }

    LLInventoryModel::cat_array_t cats;
    LLInventoryModel::item_array_t items;
    bool is_cache_obsolete = false;
    if(LLInventoryModel::loadFromFile(inv_filename, cats, items, is_cache_obsolete))
    {
        // create a container category for everything
        LLViewerInventoryCategory* container = new LLViewerInventoryCategory(gAgent.getID());
        container->rename(gDirUtilp->getBaseFileName(filename, false));
        LLUUID container_id;
        container_id.generate();
        container->setUUID(container_id);
        container->setParent(gSystemFolderRoot);
        container->setPreferredType(LLFolderType::FT_NONE);
        LLInventoryModel::update_map_t container_update;
        ++container_update[container->getParentUUID()];
        gInventory.accountForUpdate(container_update);
        gInventory.updateCategory(container);
        gInventory.notifyObservers();

        LLViewerInventoryCategory* orphaned_items = new LLViewerInventoryCategory(gAgent.getID());
        orphaned_items->rename("Orphaned Items");
        LLUUID orphaned_items_id;

        orphaned_items_id.generate();
        orphaned_items->setUUID(orphaned_items_id);
        orphaned_items->setParent(container_id);
        orphaned_items->setPreferredType(LLFolderType::FT_NONE);

        LLInventoryModel::update_map_t orphaned_items_update;
        ++orphaned_items_update[orphaned_items->getParentUUID()];
        gInventory.accountForUpdate(orphaned_items_update);
        gInventory.updateCategory(orphaned_items);
        gInventory.notifyObservers();

        //conflict handling
        std::map<LLUUID,LLUUID> conflicting_cats;
        int dropped_cats = 0;
        int dropped_items = 0;

        // Add all categories
        LLInventoryModel::cat_array_t::iterator cat_iter = cats.begin();
        LLInventoryModel::cat_array_t::iterator cat_end = cats.end();
        for(; cat_iter != cat_end; ++cat_iter)
        {
            // Conditionally change its parent
            // Note: Should I search for missing parent id's?

            //if the parent is null, it goes in the very root of the tree!
            if((*cat_iter)->getParentUUID().isNull())
            {
                (*cat_iter)->setParent(container_id);
            }
            // If the parent exists and outside of pretend inventory, generate a new uuid
            else if(gInventory.getCategory((*cat_iter)->getParentUUID()))
            {
                if(!gInventory.isObjectDescendentOf((*cat_iter)->getParentUUID(), gSystemFolderRoot, TRUE))
                {
                    std::map<LLUUID,LLUUID>::iterator itr = conflicting_cats.find((*cat_iter)->getParentUUID());
                    if(itr == conflicting_cats.end())
                    {
                        dropped_cats++;
                        continue;
                    }
                    (*cat_iter)->setParent(itr->second);
                }
            } else {
                //well balls, this is orphaned.
                (*cat_iter)->setParent(orphaned_items_id);
            }
            // If this category already exists, generate a new uuid
            if(gInventory.getCategory((*cat_iter)->getUUID()))
            {
                LLUUID cat_random;
                cat_random.generate();
                conflicting_cats[(*cat_iter)->getUUID()] = cat_random;
                (*cat_iter)->setUUID(cat_random);
            }

            LLInventoryModel::update_map_t update;
            ++update[(*cat_iter)->getParentUUID()];
            gInventory.accountForUpdate(update);
            gInventory.updateCategory(*cat_iter);
            gInventory.notifyObservers();
        }

        // Add all items
        LLInventoryModel::item_array_t::iterator item_iter = items.begin();
        LLInventoryModel::item_array_t::iterator item_end = items.end();
        for(; item_iter != item_end; ++item_iter)
        {
            // Conditionally change its parent
            // Note: Should I search for missing parent id's?

            //if the parent is null, it goes in the very root of the tree!
            if((*item_iter)->getParentUUID().isNull())
            {
                (*item_iter)->setParent(container_id);
            }

            // If the parent exists and outside of pretend inventory, generate a new uuid
            if(gInventory.getCategory((*item_iter)->getParentUUID()))
            {
                if(!gInventory.isObjectDescendentOf((*item_iter)->getParentUUID(), gSystemFolderRoot, TRUE))
                {
                    std::map<LLUUID,LLUUID>::iterator itr = conflicting_cats.find((*item_iter)->getParentUUID());
                    if(itr == conflicting_cats.end())
                    {
                        dropped_items++;
                        continue;
                    }
                    (*item_iter)->setParent(itr->second);
                }
            } else {
                //well balls, this is orphaned.
                (*item_iter)->setParent(orphaned_items_id);
            }
            // Avoid conflicts with real inventory...
            // If this item id already exists, generate a new uuid
            if(gInventory.getItem((*item_iter)->getUUID()))
            {
                LLUUID item_random;
                item_random.generate();
                (*item_iter)->setUUID(item_random);
            }

            LLInventoryModel::update_map_t update;
            ++update[(*item_iter)->getParentUUID()];
            gInventory.accountForUpdate(update);
            gInventory.updateItem(*item_iter);
            gInventory.notifyObservers();
        }

        // Quality time
        if(dropped_items || dropped_cats)
        {
            std::ostringstream message;
            message << "Some items were ignored due to conflicts:\n\n";
            if(dropped_cats) message << dropped_cats << " folders\n";
            if(dropped_items) message << dropped_items << " items\n";

            LLSD args;
            args["ERROR_MESSAGE"] = message.str();
            LLNotificationsUtil::add("ErrorMessage", args);
        }
        conflicting_cats.clear();// srsly dont think this is need but w/e :D
    }

    // remove temporary unzipped file
    if(extension == "gz")
    {
        LLFile::remove(inv_filename);
    }

}
// Bundle up a bunch of requests to send all at once.
// static   
void LLInventoryModelBackgroundFetch::bulkFetch(std::string url)
{
	//Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped.
	//If there are items in mFetchQueue, we want to check the time since the last bulkFetch was 
	//sent.  If it exceeds our retry time, go ahead and fire off another batch.  
	//Stopbackgroundfetch will be run from the Responder instead of here.  

	S16 max_concurrent_fetches=8;
	F32 new_min_time = 0.5f;			//HACK!  Clean this up when old code goes away entirely.
	if (mMinTimeBetweenFetches < new_min_time) 
	{
		mMinTimeBetweenFetches=new_min_time;  //HACK!  See above.
	}
	
	if (gDisconnected ||
		(mBulkFetchCount > max_concurrent_fetches) ||
		(mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches))
	{
		return; // just bail if we are disconnected
	}	

	U32 folder_count=0;
	U32 max_batch_size=5;

	U32 sort_order = gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1;

	uuid_vec_t recursive_cats;

	LLSD body;
	LLSD body_lib;

	while (!(mFetchQueue.empty()) && (folder_count < max_batch_size))
	{
		const FetchQueueInfo& fetch_info = mFetchQueue.front();
		const LLUUID &cat_id = fetch_info.mCatUUID;
        if (cat_id.isNull()) //DEV-17797
        {
			LLSD folder_sd;
			folder_sd["folder_id"]		= LLUUID::null.asString();
			folder_sd["owner_id"]		= gAgent.getID();
			folder_sd["sort_order"]		= (LLSD::Integer)sort_order;
			folder_sd["fetch_folders"]	= (LLSD::Boolean)FALSE;
			folder_sd["fetch_items"]	= (LLSD::Boolean)TRUE;
			body["folders"].append(folder_sd);
            folder_count++;
        }
        else
        {
		    const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
		
		    if (cat)
		    {
			    if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
			    {
				    LLSD folder_sd;
				    folder_sd["folder_id"]		= cat->getUUID();
				    folder_sd["owner_id"]		= cat->getOwnerID();
				    folder_sd["sort_order"]		= (LLSD::Integer)sort_order;
				    folder_sd["fetch_folders"]	= TRUE; //(LLSD::Boolean)sFullFetchStarted;
				    folder_sd["fetch_items"]	= (LLSD::Boolean)TRUE;
				    
				    if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
					    body_lib["folders"].append(folder_sd);
				    else
					    body["folders"].append(folder_sd);
				    folder_count++;
			    }
				// May already have this folder, but append child folders to list.
			    if (fetch_info.mRecursive)
			    {	
					LLInventoryModel::cat_array_t* categories;
					LLInventoryModel::item_array_t* items;
					gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items);
					for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
						 it != categories->end();
						 ++it)
					{
						mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive));
				    }
			    }
		    }
        }
		if (fetch_info.mRecursive)
			recursive_cats.push_back(cat_id);

		mFetchQueue.pop_front();
	}
		
	if (folder_count > 0)
	{
		mBulkFetchCount++;
		if (body["folders"].size())
		{
			LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(body, recursive_cats);
			LLHTTPClient::post(url, body, fetcher, 300.0);
		}
		if (body_lib["folders"].size())
		{
			std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents2");
			
			LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(body_lib, recursive_cats);
			LLHTTPClient::post(url_lib, body_lib, fetcher, 300.0);
		}
		mFetchTimer.reset();
	}
	else if (isBulkFetchProcessingComplete())
	{
		setAllFoldersFetched();
	}
}
void LLInventoryModelBackgroundFetch::backgroundFetch()
{
	if (mBackgroundFetchActive && gAgent.getRegion())
	{
		// If we'll be using the capability, we'll be sending batches and the background thing isn't as important.
		std::string url = gAgent.getRegion()->getCapability("FetchInventoryDescendents2");   
		if (gSavedSettings.getBOOL("UseHTTPInventory") && !url.empty()) 
		{
			bulkFetch(url);
			return;
		}
		
#if 1
		//--------------------------------------------------------------------------------
		// DEPRECATED OLD CODE
		//

		// No more categories to fetch, stop fetch process.
		if (mFetchQueue.empty())
		{
			llinfos << "Inventory fetch completed" << llendl;

			setAllFoldersFetched();
			return;
		}

		F32 fast_fetch_time = lerp(mMinTimeBetweenFetches, mMaxTimeBetweenFetches, 0.1f);
		F32 slow_fetch_time = lerp(mMinTimeBetweenFetches, mMaxTimeBetweenFetches, 0.5f);
		if (mTimelyFetchPending && mFetchTimer.getElapsedTimeF32() > slow_fetch_time)
		{
			// Double timeouts on failure.
			mMinTimeBetweenFetches = llmin(mMinTimeBetweenFetches * 2.f, 10.f);
			mMaxTimeBetweenFetches = llmin(mMaxTimeBetweenFetches * 2.f, 120.f);
			llinfos << "Inventory fetch times grown to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << llendl;
			// fetch is no longer considered "timely" although we will wait for full time-out.
			mTimelyFetchPending = FALSE;
		}

		while(1)
		{
			if (mFetchQueue.empty())
			{
				break;
			}

			if(gDisconnected)
			{
				// Just bail if we are disconnected.
				break;
			}

			const FetchQueueInfo info = mFetchQueue.front();
			LLViewerInventoryCategory* cat = gInventory.getCategory(info.mCatUUID);

			// Category has been deleted, remove from queue.
			if (!cat)
			{
				mFetchQueue.pop_front();
				continue;
			}
			
			if (mFetchTimer.getElapsedTimeF32() > mMinTimeBetweenFetches && 
				LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
			{
				// Category exists but has no children yet, fetch the descendants
				// for now, just request every time and rely on retry timer to throttle.
				if (cat->fetch())
				{
					mFetchTimer.reset();
					mTimelyFetchPending = TRUE;
				}
				else
				{
					//  The catagory also tracks if it has expired and here it says it hasn't
					//  yet.  Get out of here because nothing is going to happen until we
					//  update the timers.
					break;
				}
			}
			// Do I have all my children?
			else if (gInventory.isCategoryComplete(info.mCatUUID))
			{
				// Finished with this category, remove from queue.
				mFetchQueue.pop_front();

				// Add all children to queue.
				LLInventoryModel::cat_array_t* categories;
				LLInventoryModel::item_array_t* items;
				gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items);
				for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
					 it != categories->end();
					 ++it)
				{
					mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(),info.mRecursive));
				}

				// We received a response in less than the fast time.
				if (mTimelyFetchPending && mFetchTimer.getElapsedTimeF32() < fast_fetch_time)
				{
					// Shrink timeouts based on success.
					mMinTimeBetweenFetches = llmax(mMinTimeBetweenFetches * 0.8f, 0.3f);
					mMaxTimeBetweenFetches = llmax(mMaxTimeBetweenFetches * 0.8f, 10.f);
					//llinfos << "Inventory fetch times shrunk to (" << mMinTimeBetweenFetches << ", " << mMaxTimeBetweenFetches << ")" << llendl;
				}

				mTimelyFetchPending = FALSE;
				continue;
			}
			else if (mFetchTimer.getElapsedTimeF32() > mMaxTimeBetweenFetches)
			{
				// Received first packet, but our num descendants does not match db's num descendants
				// so try again later.
				mFetchQueue.pop_front();

				if (mNumFetchRetries++ < MAX_FETCH_RETRIES)
				{
					// push on back of queue
					mFetchQueue.push_back(info);
				}
				mTimelyFetchPending = FALSE;
				mFetchTimer.reset();
				break;
			}

			// Not enough time has elapsed to do a new fetch
			break;
		}

		//
		// DEPRECATED OLD CODE
		//--------------------------------------------------------------------------------
#endif
	}
}
// Bundle up a bunch of requests to send all at once.
// static
void LLInventoryModelBackgroundFetch::bulkFetch()
{
    //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped.
    //If there are items in mFetchQueue, we want to check the time since the last bulkFetch was
    //sent.  If it exceeds our retry time, go ahead and fire off another batch.
    LLViewerRegion* region = gAgent.getRegion();
    if (!region) return;

    S16 max_concurrent_fetches=8;
    F32 new_min_time = 0.5f;			//HACK!  Clean this up when old code goes away entirely.
    if (mMinTimeBetweenFetches < new_min_time)
    {
        mMinTimeBetweenFetches=new_min_time;  //HACK!  See above.
    }

    if (gDisconnected ||
            (mFetchCount > max_concurrent_fetches) ||
            (mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches))
    {
        return; // just bail if we are disconnected
    }

    U32 item_count=0;
    U32 folder_count=0;
    U32 max_batch_size=5;

    U32 sort_order = gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1;

    uuid_vec_t recursive_cats;

    LLSD folder_request_body;
    LLSD folder_request_body_lib;
    LLSD item_request_body;
    LLSD item_request_body_lib;

    while (!mFetchQueue.empty()
            && (item_count + folder_count) < max_batch_size)
    {
        const FetchQueueInfo& fetch_info = mFetchQueue.front();
        if (fetch_info.mIsCategory)
        {
            const LLUUID &cat_id = fetch_info.mUUID;
            if (cat_id.isNull()) //DEV-17797
            {
                LLSD folder_sd;
                folder_sd["folder_id"]		= LLUUID::null.asString();
                folder_sd["owner_id"]		= gAgent.getID();
                folder_sd["sort_order"]		= (LLSD::Integer)sort_order;
                folder_sd["fetch_folders"]	= (LLSD::Boolean)FALSE;
                folder_sd["fetch_items"]	= (LLSD::Boolean)TRUE;
                folder_request_body["folders"].append(folder_sd);
                folder_count++;
            }
            else
            {
                const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);

                if (cat)
                {
                    if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
                    {
                        LLSD folder_sd;
                        folder_sd["folder_id"]		= cat->getUUID();
                        folder_sd["owner_id"]		= cat->getOwnerID();
                        folder_sd["sort_order"]		= (LLSD::Integer)sort_order;
                        folder_sd["fetch_folders"]	= TRUE; //(LLSD::Boolean)sFullFetchStarted;
                        folder_sd["fetch_items"]	= (LLSD::Boolean)TRUE;

                        if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
                            folder_request_body_lib["folders"].append(folder_sd);
                        else
                            folder_request_body["folders"].append(folder_sd);
                        folder_count++;
                    }
                    // May already have this folder, but append child folders to list.
                    if (fetch_info.mRecursive)
                    {
                        LLInventoryModel::cat_array_t* categories;
                        LLInventoryModel::item_array_t* items;
                        gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items);
                        for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
                                it != categories->end();
                                ++it)
                        {
                            mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive));
                        }
                    }
                }
            }
            if (fetch_info.mRecursive)
                recursive_cats.push_back(cat_id);
        }
        else
        {
            LLViewerInventoryItem* itemp = gInventory.getItem(fetch_info.mUUID);
            if (itemp)
            {
                LLSD item_sd;
                item_sd["owner_id"] = itemp->getPermissions().getOwner();
                item_sd["item_id"] = itemp->getUUID();
                if (itemp->getPermissions().getOwner() == gAgent.getID())
                {
                    item_request_body.append(item_sd);
                }
                else
                {
                    item_request_body_lib.append(item_sd);
                }
                //itemp->fetchFromServer();
                item_count++;
            }
        }

        mFetchQueue.pop_front();
    }

    if (item_count + folder_count > 0)
    {
        if (folder_count)
        {
            std::string url = region->getCapability("FetchInventoryDescendents2");
            mFetchCount++;
            if (folder_request_body["folders"].size())
            {
                LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body, recursive_cats);
                LLHTTPClient::post(url, folder_request_body, fetcher, 300.0);
            }
            if (folder_request_body_lib["folders"].size())
            {
                std::string url_lib = gAgent.getRegion()->getCapability("FetchLibDescendents2");

                LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body_lib, recursive_cats);
                LLHTTPClient::post(url_lib, folder_request_body_lib, fetcher, 300.0);
            }
        }
        if (item_count)
        {
            std::string url;

            if (item_request_body.size())
            {
                mFetchCount++;
                url = region->getCapability("FetchInventory2");
                if (!url.empty())
                {
                    LLSD body;
                    body["agent_id"]	= gAgent.getID();
                    body["items"] = item_request_body;

                    LLHTTPClient::post(url, body, new LLInventoryModelFetchItemResponder(body));
                }
                //else
                //{
                //	LLMessageSystem* msg = gMessageSystem;
                //	msg->newMessage("FetchInventory");
                //	msg->nextBlock("AgentData");
                //	msg->addUUID("AgentID", gAgent.getID());
                //	msg->addUUID("SessionID", gAgent.getSessionID());
                //	msg->nextBlock("InventoryData");
                //	msg->addUUID("OwnerID", mPermissions.getOwner());
                //	msg->addUUID("ItemID", mUUID);
                //	gAgent.sendReliableMessage();
                //}
            }

            if (item_request_body_lib.size())
            {
                mFetchCount++;

                url = region->getCapability("FetchLib2");
                if (!url.empty())
                {
                    LLSD body;
                    body["agent_id"]	= gAgent.getID();
                    body["items"] = item_request_body_lib;

                    LLHTTPClient::post(url, body, new LLInventoryModelFetchItemResponder(body));
                }
            }
        }
        mFetchTimer.reset();
    }

    else if (isBulkFetchProcessingComplete())
    {
        setAllFoldersFetched();
    }
}
// Bundle up a bunch of requests to send all at once.
void LLInventoryModelBackgroundFetch::bulkFetch()
{
	//Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped.
	//If there are items in mFetchQueue, we want to check the time since the last bulkFetch was 
	//sent.  If it exceeds our retry time, go ahead and fire off another batch.  
	LLViewerRegion* region = gAgent.getRegion();
	if (gDisconnected || !region) return;

	U32 const max_batch_size = 5;

	U32 sort_order = gSavedSettings.getU32(LLInventoryPanel::DEFAULT_SORT_ORDER) & 0x1;

	uuid_vec_t recursive_cats;

	U32 folder_count=0;
	U32 folder_lib_count=0;
	U32 item_count=0;
	U32 item_lib_count=0;

	// This function can do up to four requests at once.
	LLPointer<AIPerService::Approvement> approved_folder;
	LLPointer<AIPerService::Approvement> approved_folder_lib;
	LLPointer<AIPerService::Approvement> approved_item;
	LLPointer<AIPerService::Approvement> approved_item_lib;

	LLSD folder_request_body;
	LLSD folder_request_body_lib;
	LLSD item_request_body;
	LLSD item_request_body_lib;

	while (!mFetchQueue.empty())
	{
		const FetchQueueInfo& fetch_info = mFetchQueue.front();
		if (fetch_info.mIsCategory)
		{
			const LLUUID &cat_id = fetch_info.mUUID;
			if (!cat_id.isNull())
			{
				const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id);
		
				if (cat)
				{
					if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
					{
						LLSD folder_sd;
						folder_sd["folder_id"]		= cat->getUUID();
						folder_sd["owner_id"]		= cat->getOwnerID();
						folder_sd["sort_order"]		= (LLSD::Integer)sort_order;
						folder_sd["fetch_folders"]	= TRUE; //(LLSD::Boolean)sFullFetchStarted;
						folder_sd["fetch_items"]	= (LLSD::Boolean)TRUE;
				    
						if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID())
						{
							if (folder_lib_count == max_batch_size ||
								(folder_lib_count == 0 &&
								 !(approved_folder_lib = AIPerService::approveHTTPRequestFor(mPerServicePtr, cap_inventory))))
							{
								break;
							}
							folder_request_body_lib["folders"].append(folder_sd);
							++folder_lib_count;
						}
						else
						{
							if (folder_count == max_batch_size ||
								(folder_count == 0 &&
								 !(approved_folder = AIPerService::approveHTTPRequestFor(mPerServicePtr, cap_inventory))))
							{
								break;
							}
							folder_request_body["folders"].append(folder_sd);
							++folder_count;
						}
					}
					// May already have this folder, but append child folders to list.
					if (fetch_info.mRecursive)
					{	
						LLInventoryModel::cat_array_t* categories;
						LLInventoryModel::item_array_t* items;
						gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items);
						for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin();
							 it != categories->end();
							 ++it)
						{
							mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive));
						}
					}
				}
				if (fetch_info.mRecursive)
					recursive_cats.push_back(cat_id);
			}
		}
		else
		{
			LLViewerInventoryItem* itemp = gInventory.getItem(fetch_info.mUUID);
			if (itemp)
			{
				LLSD item_sd;
				item_sd["owner_id"] = itemp->getPermissions().getOwner();
				item_sd["item_id"] = itemp->getUUID();
				if (itemp->getPermissions().getOwner() == gAgent.getID())
				{
					if (item_count == max_batch_size ||
						(item_count == 0 &&
						 !(approved_item = AIPerService::approveHTTPRequestFor(mPerServicePtr, cap_inventory))))
					{
						break;
					}
					item_request_body.append(item_sd);
					++item_count;
				}
				else
				{
					if (item_lib_count == max_batch_size ||
						(item_lib_count == 0 &&
						 !(approved_item_lib = AIPerService::approveHTTPRequestFor(mPerServicePtr, cap_inventory))))
					{
						break;
					}
					item_request_body_lib.append(item_sd);
					++item_lib_count;
				}
			}
		}

		mFetchQueue.pop_front();
	}
		
	if (item_count + folder_count + item_lib_count + folder_lib_count > 0)
	{
		if (folder_count)
		{
			std::string url = region->getCapability("FetchInventoryDescendents2");   
			llassert(!url.empty());
			++mFetchCount;
			LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body, recursive_cats);
			LLHTTPClient::post_approved(url, folder_request_body, fetcher, approved_folder);
		}
		if (folder_lib_count)
		{
			std::string url = gAgent.getRegion()->getCapability("FetchLibDescendents2");
			llassert(!url.empty());
			++mFetchCount;
			LLInventoryModelFetchDescendentsResponder *fetcher = new LLInventoryModelFetchDescendentsResponder(folder_request_body_lib, recursive_cats);
			LLHTTPClient::post_approved(url, folder_request_body_lib, fetcher, approved_folder_lib);
		}
		if (item_count)
		{
			std::string url = region->getCapability("FetchInventory2");
			llassert(!url.empty());
			++mFetchCount;
			LLSD body;
			body["agent_id"] = gAgent.getID();
			body["items"] = item_request_body;
			LLHTTPClient::post_approved(url, body, new LLInventoryModelFetchItemResponder(body), approved_item);
		}
		if (item_lib_count)
		{
			std::string url = region->getCapability("FetchLib2");
			llassert(!url.empty());
			++mFetchCount;
			LLSD body;
			body["agent_id"] = gAgent.getID();
			body["items"] = item_request_body_lib;
			LLHTTPClient::post_approved(url, body, new LLInventoryModelFetchItemResponder(body), approved_item_lib);
		}
		mFetchTimer.reset();
	}
	else if (isBulkFetchProcessingComplete())
	{
		llinfos << "Inventory fetch completed" << llendl;
		setAllFoldersFetched();
	}
}