PageRenderTime 152ms CodeModel.GetById 14ms app.highlight 126ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llmessage/llassetstorage.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1580 lines | 1174 code | 199 blank | 207 comment | 163 complexity | 4b5f16ab6590d03f7a19aec54381f04f MD5 | raw file
   1/** 
   2 * @file llassetstorage.cpp
   3 * @brief Implementation of the base asset storage system.
   4 *
   5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "linden_common.h"
  28
  29// system library includes
  30#include <sys/types.h>
  31#include <sys/stat.h>
  32
  33#include "llassetstorage.h"
  34
  35// linden library includes
  36#include "llmath.h"
  37#include "llstring.h"
  38#include "lldir.h"
  39#include "llsd.h"
  40#include "llframetimer.h"
  41
  42// this library includes
  43#include "message.h"
  44#include "llxfermanager.h"
  45#include "llvfile.h"
  46#include "llvfs.h"
  47#include "lldbstrings.h"
  48
  49#include "lltransfersourceasset.h"
  50#include "lltransfertargetvfile.h" // For debugging
  51
  52#include "llmetrics.h"
  53
  54LLAssetStorage *gAssetStorage = NULL;
  55LLMetrics *LLAssetStorage::metric_recipient = NULL;
  56
  57const LLUUID CATEGORIZE_LOST_AND_FOUND_ID(std::string("00000000-0000-0000-0000-000000000010"));
  58
  59const U64 TOXIC_ASSET_LIFETIME = (120 * 1000000);		// microseconds
  60
  61LLTempAssetStorage::~LLTempAssetStorage()
  62{
  63}
  64
  65///----------------------------------------------------------------------------
  66/// LLAssetInfo
  67///----------------------------------------------------------------------------
  68
  69LLAssetInfo::LLAssetInfo( void )
  70:	mDescription(),
  71	mName(),
  72	mUuid(),
  73	mCreatorID(),
  74	mType( LLAssetType::AT_NONE )
  75{ }
  76
  77LLAssetInfo::LLAssetInfo( const LLUUID& object_id, const LLUUID& creator_id,
  78						  LLAssetType::EType type, const char* name,
  79						  const char* desc )
  80:	mUuid( object_id ), 
  81	mCreatorID( creator_id ), 
  82	mType( type )
  83{
  84	setName( name );
  85	setDescription( desc );
  86}
  87
  88LLAssetInfo::LLAssetInfo( const LLNameValue& nv )
  89{
  90	setFromNameValue( nv );
  91}
  92
  93// make sure the name is short enough, and strip all pipes since they
  94// are reserved characters in our inventory tracking system.
  95void LLAssetInfo::setName( const std::string& name )
  96{
  97	if( !name.empty() )
  98	{
  99		mName.assign( name, 0, llmin((U32)name.size(), (U32)DB_INV_ITEM_NAME_STR_LEN) );
 100		mName.erase( std::remove(mName.begin(), mName.end(), '|'),
 101					 mName.end() );
 102	}
 103}
 104
 105// make sure the name is short enough, and strip all pipes since they
 106// are reserved characters in our inventory tracking system.
 107void LLAssetInfo::setDescription( const std::string& desc )
 108{
 109	if( !desc.empty() )
 110	{
 111		mDescription.assign( desc, 0, llmin((U32)desc.size(),
 112										 (U32)DB_INV_ITEM_DESC_STR_LEN) );
 113		mDescription.erase( std::remove(mDescription.begin(),
 114										mDescription.end(), '|'),
 115							mDescription.end() );
 116	}
 117}
 118
 119// Assets (aka potential inventory items) can be applied to an
 120// object in the world. We'll store that as a string name value
 121// pair where the name encodes part of asset info, and the value
 122// the rest.  LLAssetInfo objects will be responsible for parsing
 123// the meaning out froman LLNameValue object. See the inventory
 124// design docs for details. Briefly:
 125//   name=<inv_type>|<uuid>
 126//   value=<creatorid>|<name>|<description>|
 127void LLAssetInfo::setFromNameValue( const LLNameValue& nv )
 128{
 129	std::string str;
 130	std::string buf;
 131	std::string::size_type pos1;
 132	std::string::size_type pos2;
 133
 134	// convert the name to useful information
 135	str.assign( nv.mName );
 136	pos1 = str.find('|');
 137	buf.assign( str, 0, pos1++ );
 138	mType = LLAssetType::lookup( buf );
 139	buf.assign( str, pos1, std::string::npos );
 140	mUuid.set( buf );
 141
 142	// convert the value to useful information
 143	str.assign( nv.getAsset() );
 144	pos1 = str.find('|');
 145	buf.assign( str, 0, pos1++ );
 146	mCreatorID.set( buf );
 147	pos2 = str.find( '|', pos1 );
 148	buf.assign( str, pos1, (pos2++) - pos1 );
 149	setName( buf );
 150	buf.assign( str, pos2, std::string::npos );
 151	setDescription( buf );
 152	LL_DEBUGS("AssetStorage") << "uuid: " << mUuid << llendl;
 153	LL_DEBUGS("AssetStorage") << "creator: " << mCreatorID << llendl;
 154}
 155
 156///----------------------------------------------------------------------------
 157/// LLAssetRequest
 158///----------------------------------------------------------------------------
 159
 160LLAssetRequest::LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType type)
 161:	mUUID(uuid),
 162	mType(type),
 163	mDownCallback( NULL ),
 164	mUpCallback( NULL ),
 165	mInfoCallback( NULL ),
 166	mUserData( NULL ),
 167	mHost(),
 168	mIsTemp( FALSE ),
 169	mIsLocal(FALSE),
 170	mIsUserWaiting(FALSE),
 171	mTimeout(LL_ASSET_STORAGE_TIMEOUT),
 172	mIsPriority(FALSE),
 173	mDataSentInFirstPacket(FALSE),
 174	mDataIsInVFS( FALSE )
 175{
 176	// Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
 177	//  running a message system loop.
 178	mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
 179}
 180
 181// virtual
 182LLAssetRequest::~LLAssetRequest()
 183{
 184}
 185
 186// virtual
 187LLSD LLAssetRequest::getTerseDetails() const
 188{
 189	LLSD sd;
 190	sd["asset_id"] = getUUID();
 191	sd["type_long"] = LLAssetType::lookupHumanReadable(getType());
 192	sd["type"] = LLAssetType::lookup(getType());
 193	sd["time"] = mTime;
 194	time_t timestamp = (time_t) mTime;
 195	std::ostringstream time_string;
 196	time_string << ctime(&timestamp);
 197	sd["time_string"] = time_string.str();
 198	return sd;
 199}
 200
 201// virtual
 202LLSD LLAssetRequest::getFullDetails() const
 203{
 204	LLSD sd = getTerseDetails();
 205	sd["host"] = mHost.getIPandPort();
 206	sd["requesting_agent"] = mRequestingAgentID;
 207	sd["is_temp"] = mIsTemp;
 208	sd["is_local"] = mIsLocal;
 209	sd["is_priority"] = mIsPriority;
 210	sd["data_send_in_first_packet"] = mDataSentInFirstPacket;
 211	sd["data_is_in_vfs"] = mDataIsInVFS;
 212
 213	return sd;
 214}
 215
 216///----------------------------------------------------------------------------
 217/// LLInvItemRequest
 218///----------------------------------------------------------------------------
 219
 220LLInvItemRequest::LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType type)
 221:	mUUID(uuid),
 222	mType(type),
 223	mDownCallback( NULL ),
 224	mUserData( NULL ),
 225	mHost(),
 226	mIsTemp( FALSE ),
 227	mIsPriority(FALSE),
 228	mDataSentInFirstPacket(FALSE),
 229	mDataIsInVFS( FALSE )
 230{
 231	// Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
 232	//  running a message system loop.
 233	mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
 234}
 235
 236LLInvItemRequest::~LLInvItemRequest()
 237{
 238}
 239
 240///----------------------------------------------------------------------------
 241/// LLEstateAssetRequest
 242///----------------------------------------------------------------------------
 243
 244LLEstateAssetRequest::LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType atype,
 245										   EstateAssetType etype)
 246:	mUUID(uuid),
 247	mAType(atype),
 248	mEstateAssetType(etype),
 249	mDownCallback( NULL ),
 250	mUserData( NULL ),
 251	mHost(),
 252	mIsTemp( FALSE ),
 253	mIsPriority(FALSE),
 254	mDataSentInFirstPacket(FALSE),
 255	mDataIsInVFS( FALSE )
 256{
 257	// Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
 258	//  running a message system loop.
 259	mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
 260}
 261
 262LLEstateAssetRequest::~LLEstateAssetRequest()
 263{
 264}
 265
 266
 267///----------------------------------------------------------------------------
 268/// LLAssetStorage
 269///----------------------------------------------------------------------------
 270
 271// since many of these functions are called by the messaging and xfer systems,
 272// they are declared as static and are passed a "this" handle
 273// it's a C/C++ mish-mash!
 274
 275// TODO: permissions on modifications - maybe don't allow at all?
 276// TODO: verify that failures get propogated down
 277// TODO: rework tempfile handling?
 278
 279
 280LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host)
 281{
 282	_init(msg, xfer, vfs, static_vfs, upstream_host);
 283}
 284
 285
 286LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
 287							   LLVFS *vfs, LLVFS *static_vfs)
 288{
 289	_init(msg, xfer, vfs, static_vfs, LLHost::invalid);
 290}
 291
 292
 293void LLAssetStorage::_init(LLMessageSystem *msg,
 294						   LLXferManager *xfer,
 295						   LLVFS *vfs,
 296						   LLVFS *static_vfs,
 297						   const LLHost &upstream_host)
 298{
 299	mShutDown = FALSE;
 300	mMessageSys = msg;
 301	mXferManager = xfer;
 302	mVFS = vfs;
 303	mStaticVFS = static_vfs;
 304
 305	setUpstream(upstream_host);
 306	msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this);
 307}
 308
 309LLAssetStorage::~LLAssetStorage()
 310{
 311	mShutDown = TRUE;
 312	
 313	_cleanupRequests(TRUE, LL_ERR_CIRCUIT_GONE);
 314
 315	if (gMessageSystem)
 316	{
 317		// Warning!  This won't work if there's more than one asset storage.
 318		// unregister our callbacks with the message system
 319		gMessageSystem->setHandlerFuncFast(_PREHASH_AssetUploadComplete, NULL, NULL);
 320	}
 321
 322	// Clear the toxic asset map
 323	mToxicAssetMap.clear();
 324}
 325
 326void LLAssetStorage::setUpstream(const LLHost &upstream_host)
 327{
 328	LL_DEBUGS("AppInit") << "AssetStorage: Setting upstream provider to " << upstream_host << LL_ENDL;
 329	
 330	mUpstreamHost = upstream_host;
 331}
 332
 333void LLAssetStorage::checkForTimeouts()
 334{
 335	_cleanupRequests(FALSE, LL_ERR_TCP_TIMEOUT);
 336}
 337
 338void LLAssetStorage::_cleanupRequests(BOOL all, S32 error)
 339{
 340	F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
 341
 342	request_list_t timed_out;
 343	S32 rt;
 344	for (rt = 0; rt < RT_COUNT; rt++)
 345	{
 346		request_list_t* requests = getRequestList((ERequestType)rt);
 347		for (request_list_t::iterator iter = requests->begin();
 348			 iter != requests->end(); )
 349		{
 350			request_list_t::iterator curiter = iter++;
 351			LLAssetRequest* tmp = *curiter;
 352			// if all is true, we want to clean up everything
 353			// otherwise just check for timed out requests
 354			// EXCEPT for upload timeouts
 355			if (all 
 356				|| ((RT_DOWNLOAD == rt)
 357					&& LL_ASSET_STORAGE_TIMEOUT < (mt_secs - tmp->mTime)))
 358			{
 359				llwarns << "Asset " << getRequestName((ERequestType)rt) << " request "
 360						<< (all ? "aborted" : "timed out") << " for "
 361						<< tmp->getUUID() << "."
 362						<< LLAssetType::lookup(tmp->getType()) << llendl;
 363
 364				timed_out.push_front(tmp);
 365				iter = requests->erase(curiter);
 366			}
 367		}
 368	}
 369
 370	LLAssetInfo	info;
 371	for (request_list_t::iterator iter = timed_out.begin();
 372		 iter != timed_out.end();  )
 373	{
 374		request_list_t::iterator curiter = iter++;
 375		LLAssetRequest* tmp = *curiter;
 376		if (tmp->mUpCallback)
 377		{
 378			tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error, LL_EXSTAT_NONE);
 379		}
 380		if (tmp->mDownCallback)
 381		{
 382			tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LL_EXSTAT_NONE);
 383		}
 384		if (tmp->mInfoCallback)
 385		{
 386			tmp->mInfoCallback(&info, tmp->mUserData, error);
 387		}
 388		delete tmp;
 389	}
 390
 391}
 392
 393BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type)
 394{
 395	return mStaticVFS->getExists(uuid, type) || mVFS->getExists(uuid, type);
 396}
 397
 398bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type,
 399													  LLGetAssetCallback callback, void *user_data)
 400{
 401	if (user_data)
 402	{
 403		// The *user_data should not be passed without a callback to clean it up.
 404		llassert(callback != NULL)
 405	}
 406
 407	BOOL exists = mStaticVFS->getExists(uuid, type);
 408	if (exists)
 409	{
 410		LLVFile file(mStaticVFS, uuid, type);
 411		U32 size = file.getSize();
 412		if (size > 0)
 413		{
 414			// we've already got the file
 415			if (callback)
 416			{
 417				callback(mStaticVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED);
 418			}
 419			return true;
 420		}
 421		else
 422		{
 423			llwarns << "Asset vfile " << uuid << ":" << type
 424					<< " found in static cache with bad size " << file.getSize() << ", ignoring" << llendl;
 425		}
 426	}
 427	return false;
 428}
 429
 430///////////////////////////////////////////////////////////////////////////
 431// GET routines
 432///////////////////////////////////////////////////////////////////////////
 433
 434// IW - uuid is passed by value to avoid side effects, please don't re-add &    
 435void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data, BOOL is_priority)
 436{
 437	LL_DEBUGS("AssetStorage") << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << llendl;
 438
 439	LL_DEBUGS("AssetStorage") << "ASSET_TRACE requesting " << uuid << " type " << LLAssetType::lookup(type) << llendl;
 440
 441	if (user_data)
 442	{
 443		// The *user_data should not be passed without a callback to clean it up.
 444		llassert(callback != NULL)
 445	}
 446
 447	if (mShutDown)
 448	{
 449		LL_DEBUGS("AssetStorage") << "ASSET_TRACE cancelled " << uuid << " type " << LLAssetType::lookup(type) << " shutting down" << llendl;
 450
 451		if (callback)
 452		{
 453			callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_NONE);
 454		}
 455		return;
 456	}
 457
 458	if (uuid.isNull())
 459	{
 460		// Special case early out for NULL uuid and for shutting down
 461		if (callback)
 462		{
 463			callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID);
 464		}
 465		return;
 466	}
 467
 468	// Try static VFS first.
 469	if (findInStaticVFSAndInvokeCallback(uuid,type,callback,user_data))
 470	{
 471		LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in static VFS" << llendl;
 472		return;
 473	}
 474
 475	BOOL exists = mVFS->getExists(uuid, type);
 476	LLVFile file(mVFS, uuid, type);
 477	U32 size = exists ? file.getSize() : 0;
 478	
 479	if (size > 0)
 480	{
 481		// we've already got the file
 482		// theoretically, partial files w/o a pending request shouldn't happen
 483		// unless there's a weird error
 484		if (callback)
 485		{
 486			callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED);
 487		}
 488
 489		LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in VFS" << llendl;
 490	}
 491	else
 492	{
 493		if (exists)
 494		{
 495			llwarns << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << llendl;
 496			file.remove();
 497		}
 498		
 499		BOOL duplicate = FALSE;
 500		
 501		// check to see if there's a pending download of this uuid already
 502		for (request_list_t::iterator iter = mPendingDownloads.begin();
 503			 iter != mPendingDownloads.end(); ++iter )
 504		{
 505			LLAssetRequest  *tmp = *iter;
 506			if ((type == tmp->getType()) && (uuid == tmp->getUUID()))
 507			{
 508				if (callback == tmp->mDownCallback && user_data == tmp->mUserData)
 509				{
 510					// this is a duplicate from the same subsystem - throw it away
 511					llwarns << "Discarding duplicate request for asset " << uuid
 512							<< "." << LLAssetType::lookup(type) << llendl;
 513					return;
 514				}
 515				
 516				// this is a duplicate request
 517				// queue the request, but don't actually ask for it again
 518				duplicate = TRUE;
 519			}
 520		}
 521		if (duplicate)
 522		{
 523			LL_DEBUGS("AssetStorage") << "Adding additional non-duplicate request for asset " << uuid 
 524					<< "." << LLAssetType::lookup(type) << llendl;
 525		}
 526		
 527		// This can be overridden by subclasses
 528		_queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority);	
 529	}
 530
 531}
 532
 533//
 534// *NOTE:  Logic here is replicated in LLViewerAssetStorage::_queueDataRequest.
 535// Changes here may need to be replicated in the viewer's derived class.
 536//
 537void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType atype,
 538									   LLGetAssetCallback callback,
 539									   void *user_data, BOOL duplicate,
 540									   BOOL is_priority)
 541{
 542	if (mUpstreamHost.isOk())
 543	{
 544		// stash the callback info so we can find it after we get the response message
 545		LLAssetRequest *req = new LLAssetRequest(uuid, atype);
 546		req->mDownCallback = callback;
 547		req->mUserData = user_data;
 548		req->mIsPriority = is_priority;
 549	
 550		mPendingDownloads.push_back(req);
 551	
 552		if (!duplicate)
 553		{
 554			// send request message to our upstream data provider
 555			// Create a new asset transfer.
 556			LLTransferSourceParamsAsset spa;
 557			spa.setAsset(uuid, atype);
 558
 559			// Set our destination file, and the completion callback.
 560			LLTransferTargetParamsVFile tpvf;
 561			tpvf.setAsset(uuid, atype);
 562			tpvf.setCallback(downloadCompleteCallback, req);
 563
 564			//llinfos << "Starting transfer for " << uuid << llendl;
 565			LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
 566			ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
 567		}
 568	}
 569	else
 570	{
 571		// uh-oh, we shouldn't have gotten here
 572		llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
 573		if (callback)
 574		{
 575			callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM);
 576		}
 577	}
 578}
 579
 580
 581void LLAssetStorage::downloadCompleteCallback(
 582	S32 result,
 583	const LLUUID& file_id,
 584	LLAssetType::EType file_type,
 585	void* user_data, LLExtStat ext_status)
 586{
 587	LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << file_id << " downloadCompleteCallback" << llendl;
 588
 589	LL_DEBUGS("AssetStorage") << "LLAssetStorage::downloadCompleteCallback() for " << file_id
 590		 << "," << LLAssetType::lookup(file_type) << llendl;
 591	LLAssetRequest* req = (LLAssetRequest*)user_data;
 592	if(!req)
 593	{
 594		llwarns << "LLAssetStorage::downloadCompleteCallback called without"
 595			"a valid request." << llendl;
 596		return;
 597	}
 598	if (!gAssetStorage)
 599	{
 600		llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
 601		return;
 602	}
 603
 604	// Inefficient since we're doing a find through a list that may have thousands of elements.
 605	// This is due for refactoring; we will probably change mPendingDownloads into a set.
 606	request_list_t::iterator download_iter = std::find(gAssetStorage->mPendingDownloads.begin(), 
 607													   gAssetStorage->mPendingDownloads.end(),
 608													   req);
 609	// If the LLAssetRequest doesn't exist in the downloads queue, then it either has already been deleted
 610	// by _cleanupRequests, or it's a transfer.
 611	if (download_iter != gAssetStorage->mPendingDownloads.end())
 612	{
 613		req->setUUID(file_id);
 614		req->setType(file_type);
 615	}
 616
 617	if (LL_ERR_NOERR == result)
 618	{
 619		// we might have gotten a zero-size file
 620		LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
 621		if (vfile.getSize() <= 0)
 622		{
 623			llwarns << "downloadCompleteCallback has non-existent or zero-size asset " << req->getUUID() << llendl;
 624			
 625			result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
 626			vfile.remove();
 627		}
 628	}
 629	
 630	// find and callback ALL pending requests for this UUID
 631	// SJB: We process the callbacks in reverse order, I do not know if this is important,
 632	//      but I didn't want to mess with it.
 633	request_list_t requests;
 634	for (request_list_t::iterator iter = gAssetStorage->mPendingDownloads.begin();
 635		 iter != gAssetStorage->mPendingDownloads.end();  )
 636	{
 637		request_list_t::iterator curiter = iter++;
 638		LLAssetRequest* tmp = *curiter;
 639		if ((tmp->getUUID() == file_id) && (tmp->getType()== file_type))
 640		{
 641			requests.push_front(tmp);
 642			iter = gAssetStorage->mPendingDownloads.erase(curiter);
 643		}
 644	}
 645	for (request_list_t::iterator iter = requests.begin();
 646		 iter != requests.end();  )
 647	{
 648		request_list_t::iterator curiter = iter++;
 649		LLAssetRequest* tmp = *curiter;
 650		if (tmp->mDownCallback)
 651		{
 652			tmp->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), tmp->mUserData, result, ext_status);
 653		}
 654		delete tmp;
 655	}
 656}
 657
 658void LLAssetStorage::getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
 659									const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype,
 660									 LLGetAssetCallback callback, void *user_data, BOOL is_priority)
 661{
 662	lldebugs << "LLAssetStorage::getEstateAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << ", estatetype " << etype << llendl;
 663
 664	//
 665	// Probably will get rid of this early out?
 666	//
 667	if (asset_id.isNull())
 668	{
 669		// Special case early out for NULL uuid
 670		if (callback)
 671		{
 672			callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID);
 673		}
 674		return;
 675	}
 676
 677	// Try static VFS first.
 678	if (findInStaticVFSAndInvokeCallback(asset_id,atype,callback,user_data))
 679	{
 680		return;
 681	}
 682	
 683	BOOL exists = mVFS->getExists(asset_id, atype);
 684	LLVFile file(mVFS, asset_id, atype);
 685	U32 size = exists ? file.getSize() : 0;
 686
 687	if (size > 0)
 688	{
 689		// we've already got the file
 690		// theoretically, partial files w/o a pending request shouldn't happen
 691		// unless there's a weird error
 692		if (callback)
 693		{
 694			callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED);
 695		}
 696	}
 697	else
 698	{
 699		if (exists)
 700		{
 701			llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
 702			file.remove();
 703		}
 704
 705		// See whether we should talk to the object's originating sim, or the upstream provider.
 706		LLHost source_host;
 707		if (object_sim.isOk())
 708		{
 709			source_host = object_sim;
 710		}
 711		else
 712		{
 713			source_host = mUpstreamHost;
 714		}
 715		if (source_host.isOk())
 716		{
 717			// stash the callback info so we can find it after we get the response message
 718			LLEstateAssetRequest *req = new LLEstateAssetRequest(asset_id, atype, etype);
 719			req->mDownCallback = callback;
 720			req->mUserData = user_data;
 721			req->mIsPriority = is_priority;
 722
 723			// send request message to our upstream data provider
 724			// Create a new asset transfer.
 725			LLTransferSourceParamsEstate spe;
 726			spe.setAgentSession(agent_id, session_id);
 727			spe.setEstateAssetType(etype);
 728
 729			// Set our destination file, and the completion callback.
 730			LLTransferTargetParamsVFile tpvf;
 731			tpvf.setAsset(asset_id, atype);
 732			tpvf.setCallback(downloadEstateAssetCompleteCallback, req);
 733
 734			LL_DEBUGS("AssetStorage") << "Starting transfer for " << asset_id << llendl;
 735			LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
 736			ttcp->requestTransfer(spe, tpvf, 100.f + (is_priority ? 1.f : 0.f));
 737		}
 738		else
 739		{
 740			// uh-oh, we shouldn't have gotten here
 741			llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
 742			if (callback)
 743			{
 744				callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM);
 745			}
 746		}
 747	}
 748}
 749
 750void LLAssetStorage::downloadEstateAssetCompleteCallback(
 751	S32 result,
 752	const LLUUID& file_id,
 753	LLAssetType::EType file_type,
 754	void* user_data,
 755	LLExtStat ext_status)
 756{
 757	LLEstateAssetRequest *req = (LLEstateAssetRequest*)user_data;
 758	if(!req)
 759	{
 760		llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
 761			" without a valid request." << llendl;
 762		return;
 763	}
 764	if (!gAssetStorage)
 765	{
 766		llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
 767			" without any asset system, aborting!" << llendl;
 768		return;
 769	}
 770
 771	req->setUUID(file_id);
 772	req->setType(file_type);
 773	if (LL_ERR_NOERR == result)
 774	{
 775		// we might have gotten a zero-size file
 776		LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType());
 777		if (vfile.getSize() <= 0)
 778		{
 779			llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
 780
 781			result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
 782			vfile.remove();
 783		}
 784	}
 785
 786	req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result, ext_status);
 787}
 788
 789void LLAssetStorage::getInvItemAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
 790									 const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id,
 791									 const LLUUID &asset_id, LLAssetType::EType atype,
 792									 LLGetAssetCallback callback, void *user_data, BOOL is_priority)
 793{
 794	lldebugs << "LLAssetStorage::getInvItemAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << llendl;
 795
 796	//
 797	// Probably will get rid of this early out?
 798	//
 799	//if (asset_id.isNull())
 800	//{
 801	//	// Special case early out for NULL uuid
 802	//	if (callback)
 803	//	{
 804	//		callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
 805	//	}
 806	//	return;
 807	//}
 808
 809	bool exists = false; 
 810	U32 size = 0;
 811
 812	if(asset_id.notNull())
 813	{
 814		// Try static VFS first.
 815		if (findInStaticVFSAndInvokeCallback( asset_id, atype, callback, user_data))
 816		{
 817			return;
 818		}
 819
 820		exists = mVFS->getExists(asset_id, atype);
 821		LLVFile file(mVFS, asset_id, atype);
 822		size = exists ? file.getSize() : 0;
 823		if(exists && size < 1)
 824		{
 825			llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
 826			file.remove();
 827		}
 828
 829	}
 830
 831	if (size > 0)
 832	{
 833		// we've already got the file
 834		// theoretically, partial files w/o a pending request shouldn't happen
 835		// unless there's a weird error
 836		if (callback)
 837		{
 838			callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED);
 839		}
 840	}
 841	else
 842	{
 843		// See whether we should talk to the object's originating sim,
 844		// or the upstream provider.
 845		LLHost source_host;
 846		if (object_sim.isOk())
 847		{
 848			source_host = object_sim;
 849		}
 850		else
 851		{
 852			source_host = mUpstreamHost;
 853		}
 854		if (source_host.isOk())
 855		{
 856			// stash the callback info so we can find it after we get the response message
 857			LLInvItemRequest *req = new LLInvItemRequest(asset_id, atype);
 858			req->mDownCallback = callback;
 859			req->mUserData = user_data;
 860			req->mIsPriority = is_priority;
 861
 862			// send request message to our upstream data provider
 863			// Create a new asset transfer.
 864			LLTransferSourceParamsInvItem spi;
 865			spi.setAgentSession(agent_id, session_id);
 866			spi.setInvItem(owner_id, task_id, item_id);
 867			spi.setAsset(asset_id, atype);
 868
 869			// Set our destination file, and the completion callback.
 870			LLTransferTargetParamsVFile tpvf;
 871			tpvf.setAsset(asset_id, atype);
 872			tpvf.setCallback(downloadInvItemCompleteCallback, req);
 873
 874			LL_DEBUGS("AssetStorage") << "Starting transfer for inventory asset "
 875				<< item_id << " owned by " << owner_id << "," << task_id
 876				<< llendl;
 877			LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
 878			ttcp->requestTransfer(spi, tpvf, 100.f + (is_priority ? 1.f : 0.f));
 879		}
 880		else
 881		{
 882			// uh-oh, we shouldn't have gotten here
 883			llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
 884			if (callback)
 885			{
 886				callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM);
 887			}
 888		}
 889	}
 890}
 891
 892
 893void LLAssetStorage::downloadInvItemCompleteCallback(
 894	S32 result,
 895	const LLUUID& file_id,
 896	LLAssetType::EType file_type,
 897	void* user_data,
 898	LLExtStat ext_status)
 899{
 900	LLInvItemRequest *req = (LLInvItemRequest*)user_data;
 901	if(!req)
 902	{
 903		llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
 904			" without a valid request." << llendl;
 905		return;
 906	}
 907	if (!gAssetStorage)
 908	{
 909		llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
 910		return;
 911	}
 912
 913	req->setUUID(file_id);
 914	req->setType(file_type);
 915	if (LL_ERR_NOERR == result)
 916	{
 917		// we might have gotten a zero-size file
 918		LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
 919		if (vfile.getSize() <= 0)
 920		{
 921			llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
 922
 923			result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
 924			vfile.remove();
 925		}
 926	}
 927
 928	req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result, ext_status);
 929}
 930
 931/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 932// Store routines
 933/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 934
 935// static
 936void LLAssetStorage::uploadCompleteCallback(const LLUUID& uuid, void *user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed)
 937{
 938	if (!gAssetStorage)
 939	{
 940		llwarns << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << llendl;
 941		return;
 942	}
 943	LLAssetRequest	*req	 = (LLAssetRequest *)user_data;
 944	BOOL			success  = TRUE;
 945
 946	if (result)
 947	{
 948		llwarns << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << llendl;
 949		success = FALSE;
 950	}
 951
 952	// we're done grabbing the file, tell the client
 953	gAssetStorage->mMessageSys->newMessageFast(_PREHASH_AssetUploadComplete);
 954	gAssetStorage->mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
 955	gAssetStorage->mMessageSys->addUUIDFast(_PREHASH_UUID, uuid);
 956	gAssetStorage->mMessageSys->addS8Fast(_PREHASH_Type, req->getType());
 957	gAssetStorage->mMessageSys->addBOOLFast(_PREHASH_Success, success);
 958	gAssetStorage->mMessageSys->sendReliable(req->mHost);
 959
 960	delete req;
 961}
 962
 963void LLAssetStorage::processUploadComplete(LLMessageSystem *msg, void **user_data)
 964{
 965	LLAssetStorage	*this_ptr = (LLAssetStorage *)user_data;
 966	LLUUID			uuid;
 967	S8				asset_type_s8;
 968	LLAssetType::EType asset_type;
 969	BOOL			success = FALSE;
 970
 971	msg->getUUIDFast(_PREHASH_AssetBlock, _PREHASH_UUID, uuid);
 972	msg->getS8Fast(_PREHASH_AssetBlock, _PREHASH_Type, asset_type_s8);
 973	msg->getBOOLFast(_PREHASH_AssetBlock, _PREHASH_Success, success);
 974
 975	asset_type = (LLAssetType::EType)asset_type_s8;
 976	this_ptr->_callUploadCallbacks(uuid, asset_type, success, LL_EXSTAT_NONE);
 977}
 978
 979void LLAssetStorage::_callUploadCallbacks(const LLUUID &uuid, LLAssetType::EType asset_type, BOOL success, LLExtStat ext_status )
 980{
 981	// SJB: We process the callbacks in reverse order, I do not know if this is important,
 982	//      but I didn't want to mess with it.
 983	request_list_t requests;
 984	for (request_list_t::iterator iter = mPendingUploads.begin();
 985		 iter != mPendingUploads.end();  )
 986	{
 987		request_list_t::iterator curiter = iter++;
 988		LLAssetRequest* req = *curiter;
 989		if ((req->getUUID() == uuid) && (req->getType() == asset_type))
 990		{
 991			requests.push_front(req);
 992			iter = mPendingUploads.erase(curiter);
 993		}
 994	}
 995	for (request_list_t::iterator iter = mPendingLocalUploads.begin();
 996		 iter != mPendingLocalUploads.end();  )
 997	{
 998		request_list_t::iterator curiter = iter++;
 999		LLAssetRequest* req = *curiter;
1000		if ((req->getUUID() == uuid) && (req->getType() == asset_type))
1001		{
1002			requests.push_front(req);
1003			iter = mPendingLocalUploads.erase(curiter);
1004		}
1005	}
1006	for (request_list_t::iterator iter = requests.begin();
1007		 iter != requests.end();  )
1008	{
1009		request_list_t::iterator curiter = iter++;
1010		LLAssetRequest* req = *curiter;
1011		if (req->mUpCallback)
1012		{
1013			req->mUpCallback(uuid, req->mUserData, (success ?  LL_ERR_NOERR :  LL_ERR_ASSET_REQUEST_FAILED ), ext_status );
1014		}
1015		delete req;
1016	}
1017}
1018
1019LLAssetStorage::request_list_t* LLAssetStorage::getRequestList(LLAssetStorage::ERequestType rt)
1020{
1021	switch (rt)
1022	{
1023	case RT_DOWNLOAD:
1024		return &mPendingDownloads;
1025	case RT_UPLOAD:
1026		return &mPendingUploads;
1027	case RT_LOCALUPLOAD:
1028		return &mPendingLocalUploads;
1029	default:
1030		llwarns << "Unable to find request list for request type '" << rt << "'" << llendl;
1031		return NULL;
1032	}
1033}
1034
1035const LLAssetStorage::request_list_t* LLAssetStorage::getRequestList(LLAssetStorage::ERequestType rt) const
1036{
1037	switch (rt)
1038	{
1039	case RT_DOWNLOAD:
1040		return &mPendingDownloads;
1041	case RT_UPLOAD:
1042		return &mPendingUploads;
1043	case RT_LOCALUPLOAD:
1044		return &mPendingLocalUploads;
1045	default:
1046		llwarns << "Unable to find request list for request type '" << rt << "'" << llendl;
1047		return NULL;
1048	}
1049}
1050
1051// static
1052std::string LLAssetStorage::getRequestName(LLAssetStorage::ERequestType rt)
1053{
1054	switch (rt)
1055	{
1056	case RT_DOWNLOAD:
1057		return "download";
1058	case RT_UPLOAD:
1059		return "upload";
1060	case RT_LOCALUPLOAD:
1061		return "localupload";
1062	default:
1063		llwarns << "Unable to find request name for request type '" << rt << "'" << llendl;
1064		return "";
1065	}
1066}
1067
1068S32 LLAssetStorage::getNumPending(LLAssetStorage::ERequestType rt) const
1069{
1070	const request_list_t* requests = getRequestList(rt);
1071	S32 num_pending = -1;
1072	if (requests)
1073	{
1074		num_pending = requests->size();
1075	}
1076	return num_pending;
1077}
1078
1079S32 LLAssetStorage::getNumPendingDownloads() const
1080{
1081	return getNumPending(RT_DOWNLOAD);
1082}
1083
1084S32 LLAssetStorage::getNumPendingUploads() const
1085{
1086	return getNumPending(RT_UPLOAD);
1087}
1088
1089S32 LLAssetStorage::getNumPendingLocalUploads()
1090{
1091	return getNumPending(RT_LOCALUPLOAD);
1092}
1093
1094// virtual
1095LLSD LLAssetStorage::getPendingDetails(LLAssetStorage::ERequestType rt,
1096										LLAssetType::EType asset_type,
1097										const std::string& detail_prefix) const
1098{
1099	const request_list_t* requests = getRequestList(rt);
1100	LLSD sd;
1101	sd["requests"] = getPendingDetailsImpl(requests, asset_type, detail_prefix);
1102	return sd;
1103}
1104
1105// virtual
1106LLSD LLAssetStorage::getPendingDetailsImpl(const LLAssetStorage::request_list_t* requests,
1107										LLAssetType::EType asset_type,
1108										const std::string& detail_prefix) const
1109{
1110	LLSD details;
1111	if (requests)
1112	{
1113		request_list_t::const_iterator it = requests->begin();
1114		request_list_t::const_iterator end = requests->end();
1115		for ( ; it != end; ++it)
1116		{
1117			LLAssetRequest* req = *it;
1118			if (   (LLAssetType::AT_NONE == asset_type)
1119				|| (req->getType() == asset_type) )
1120			{
1121				LLSD row = req->getTerseDetails();
1122
1123				std::ostringstream detail;
1124				detail	<< detail_prefix << "/" << LLAssetType::lookup(req->getType())
1125						<< "/" << req->getUUID();
1126				row["detail"] = LLURI(detail.str());
1127
1128				details.append(row);
1129			}
1130		}
1131	}
1132	return details;
1133}
1134
1135
1136// static
1137const LLAssetRequest* LLAssetStorage::findRequest(const LLAssetStorage::request_list_t* requests,
1138										LLAssetType::EType asset_type,
1139										const LLUUID& asset_id)
1140{
1141	if (requests) 
1142	{
1143		// Search the requests list for the asset.
1144		request_list_t::const_iterator iter = requests->begin();
1145		request_list_t::const_iterator end  = requests->end();
1146		for (; iter != end; ++iter)
1147		{
1148			const LLAssetRequest* req = *iter;
1149			if (asset_type == req->getType() &&
1150				asset_id == req->getUUID() )
1151			{
1152				return req;
1153			}
1154		}
1155	}
1156	return NULL;
1157}
1158
1159// static
1160LLAssetRequest* LLAssetStorage::findRequest(LLAssetStorage::request_list_t* requests,
1161										LLAssetType::EType asset_type,
1162										const LLUUID& asset_id)
1163{
1164	if (requests) 
1165	{
1166		// Search the requests list for the asset.
1167		request_list_t::iterator iter = requests->begin();
1168		request_list_t::iterator end  = requests->end();
1169		for (; iter != end; ++iter)
1170		{
1171			LLAssetRequest* req = *iter;
1172			if (asset_type == req->getType() &&
1173				asset_id == req->getUUID() )
1174			{
1175				return req;
1176			}
1177		}
1178	}
1179	return NULL;
1180}
1181
1182
1183// virtual
1184LLSD LLAssetStorage::getPendingRequest(LLAssetStorage::ERequestType rt,
1185										LLAssetType::EType asset_type,
1186										const LLUUID& asset_id) const
1187{
1188	const request_list_t* requests = getRequestList(rt);
1189	return getPendingRequestImpl(requests, asset_type, asset_id);
1190}
1191
1192// virtual
1193LLSD LLAssetStorage::getPendingRequestImpl(const LLAssetStorage::request_list_t* requests,
1194										LLAssetType::EType asset_type,
1195										const LLUUID& asset_id) const
1196{
1197	LLSD sd;
1198	const LLAssetRequest* req = findRequest(requests, asset_type, asset_id);
1199	if (req)
1200	{
1201		sd = req->getFullDetails();
1202	}
1203	return sd;
1204}
1205
1206// virtual
1207bool LLAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt,
1208											LLAssetType::EType asset_type,
1209											const LLUUID& asset_id)
1210{
1211	request_list_t* requests = getRequestList(rt);
1212	if (deletePendingRequestImpl(requests, asset_type, asset_id))
1213	{
1214		LL_DEBUGS("AssetStorage") << "Asset " << getRequestName(rt) << " request for "
1215				<< asset_id << "." << LLAssetType::lookup(asset_type)
1216				<< " removed from pending queue." << llendl;
1217		return true;
1218	}
1219	return false;
1220}
1221
1222// virtual
1223bool LLAssetStorage::deletePendingRequestImpl(LLAssetStorage::request_list_t* requests,
1224											LLAssetType::EType asset_type,
1225											const LLUUID& asset_id)
1226{
1227	LLAssetRequest* req = findRequest(requests, asset_type, asset_id);
1228	if (req)
1229	{
1230		// Remove the request from this list.
1231		requests->remove(req);
1232		S32 error = LL_ERR_TCP_TIMEOUT;
1233		// Run callbacks.
1234		if (req->mUpCallback)
1235		{
1236			req->mUpCallback(req->getUUID(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED);
1237		}
1238		if (req->mDownCallback)
1239		{
1240			req->mDownCallback(mVFS, req->getUUID(), req->getType(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED);
1241		}
1242		if (req->mInfoCallback)
1243		{
1244			LLAssetInfo info;
1245			req->mInfoCallback(&info, req->mUserData, error);
1246		}
1247		delete req;
1248		return true;
1249	}
1250	
1251	return false;
1252}
1253
1254// static
1255const char* LLAssetStorage::getErrorString(S32 status)
1256{
1257	switch( status )
1258	{
1259	case LL_ERR_NOERR:
1260		return "No error";
1261
1262	case LL_ERR_ASSET_REQUEST_FAILED:
1263		return "Asset request: failed";
1264
1265	case LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE:
1266		return "Asset request: non-existent file";
1267
1268	case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
1269		return "Asset request: asset not found in database";
1270
1271	case LL_ERR_EOF:
1272		return "End of file";
1273
1274	case LL_ERR_CANNOT_OPEN_FILE:
1275		return "Cannot open file";
1276
1277	case LL_ERR_FILE_NOT_FOUND:
1278		return "File not found";
1279
1280	case LL_ERR_TCP_TIMEOUT:
1281		return "File transfer timeout";
1282
1283	case LL_ERR_CIRCUIT_GONE:
1284		return "Circuit gone";
1285
1286	case LL_ERR_PRICE_MISMATCH:
1287		return "Viewer and server do not agree on price";
1288
1289	default:
1290		return "Unknown status";
1291	}
1292}
1293
1294
1295
1296void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32, LLExtStat), void *user_data, BOOL is_priority)
1297{
1298	// check for duplicates here, since we're about to fool the normal duplicate checker
1299	for (request_list_t::iterator iter = mPendingDownloads.begin();
1300		 iter != mPendingDownloads.end();  )
1301	{
1302		LLAssetRequest* tmp = *iter++;
1303		if (type == tmp->getType() && 
1304			uuid == tmp->getUUID() &&
1305			legacyGetDataCallback == tmp->mDownCallback &&
1306			callback == ((LLLegacyAssetRequest *)tmp->mUserData)->mDownCallback &&
1307			user_data == ((LLLegacyAssetRequest *)tmp->mUserData)->mUserData)
1308		{
1309			// this is a duplicate from the same subsystem - throw it away
1310			LL_DEBUGS("AssetStorage") << "Discarding duplicate request for UUID " << uuid << llendl;
1311			return;
1312		}
1313	}
1314	
1315	
1316	LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
1317
1318	legacy->mDownCallback = callback;
1319	legacy->mUserData = user_data;
1320
1321	getAssetData(uuid, type, legacyGetDataCallback, (void **)legacy,
1322				 is_priority);
1323}
1324
1325// static
1326void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 status, LLExtStat ext_status)
1327{
1328	LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
1329	std::string filename;
1330
1331	// Check if the asset is marked toxic, and don't load bad stuff
1332	BOOL toxic = gAssetStorage->isAssetToxic( uuid );
1333
1334	if ( !status
1335		&& !toxic )
1336	{
1337		LLVFile file(vfs, uuid, type);
1338
1339		std::string uuid_str;
1340
1341		uuid.toString(uuid_str);
1342		filename = llformat("%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type));
1343
1344		LLFILE* fp = LLFile::fopen(filename, "wb");	/* Flawfinder: ignore */ 
1345		if (fp)
1346		{
1347			const S32 buf_size = 65536;
1348			U8 copy_buf[buf_size];
1349			while (file.read(copy_buf, buf_size))	/* Flawfinder: ignore */
1350			{
1351				if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1)
1352				{
1353					// return a bad file error if we can't write the whole thing
1354					status = LL_ERR_CANNOT_OPEN_FILE;
1355				}
1356			}
1357
1358			fclose(fp);
1359		}
1360		else
1361		{
1362			status = LL_ERR_CANNOT_OPEN_FILE;
1363		}
1364	}
1365
1366	legacy->mDownCallback(filename.c_str(), uuid, legacy->mUserData, status, ext_status);
1367	delete legacy;
1368}
1369
1370// this is overridden on the viewer and the sim, so it doesn't really do anything
1371// virtual 
1372void LLAssetStorage::storeAssetData(
1373	const LLTransactionID& tid,
1374	LLAssetType::EType asset_type,
1375	LLStoreAssetCallback callback,
1376	void* user_data,
1377	bool temp_file,
1378	bool is_priority,
1379	bool store_local,
1380	bool user_waiting,
1381	F64 timeout)
1382{
1383	llwarns << "storeAssetData: wrong version called" << llendl;
1384	// LLAssetStorage metric: Virtual base call
1385	reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 1" );
1386}
1387
1388// virtual
1389// this does nothing, viewer and sim both override this.
1390void LLAssetStorage::storeAssetData(
1391	const LLUUID& asset_id,
1392	LLAssetType::EType asset_type,
1393	LLStoreAssetCallback callback,
1394	void* user_data,
1395	bool temp_file ,
1396	bool is_priority,
1397	bool store_local,
1398	const LLUUID& requesting_agent_id,
1399	bool user_waiting,
1400	F64 timeout)
1401{
1402	llwarns << "storeAssetData: wrong version called" << llendl;
1403	// LLAssetStorage metric: Virtual base call
1404	reportMetric( asset_id, asset_type, LLStringUtil::null, requesting_agent_id, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 2" );
1405}
1406
1407// virtual
1408// this does nothing, viewer and sim both override this.
1409void LLAssetStorage::storeAssetData(
1410	const std::string& filename,
1411	const LLUUID& asset_id,
1412	LLAssetType::EType asset_type,
1413	LLStoreAssetCallback callback,
1414	void* user_data,
1415	bool temp_file,
1416	bool is_priority,
1417	bool user_waiting,
1418	F64 timeout)
1419{
1420	llwarns << "storeAssetData: wrong version called" << llendl;
1421	// LLAssetStorage metric: Virtual base call
1422	reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 3" );
1423}
1424
1425// virtual
1426// this does nothing, viewer and sim both override this.
1427void LLAssetStorage::storeAssetData(
1428	const std::string& filename,
1429	const LLTransactionID &transactoin_id,
1430	LLAssetType::EType asset_type,
1431	LLStoreAssetCallback callback,
1432	void* user_data,
1433	bool temp_file,
1434	bool is_priority,
1435	bool user_waiting,
1436	F64 timeout)
1437{
1438	llwarns << "storeAssetData: wrong version called" << llendl;
1439	// LLAssetStorage metric: Virtual base call
1440	reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 4" );
1441}
1442
1443// static
1444void LLAssetStorage::legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status, LLExtStat ext_status)
1445{
1446	LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
1447	if (legacy && legacy->mUpCallback)
1448	{
1449		legacy->mUpCallback(uuid, legacy->mUserData, status, ext_status);
1450	}
1451	delete legacy;
1452}
1453
1454// virtual
1455void LLAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name) 
1456{ }
1457
1458// virtual
1459BOOL LLAssetStorage::hasTempAssetData(const LLUUID& texture_id) const 
1460{ return FALSE; }
1461
1462// virtual
1463std::string LLAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const 
1464{ return std::string(); }
1465
1466// virtual
1467LLUUID LLAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const 
1468{ return LLUUID::null; }
1469
1470// virtual
1471void LLAssetStorage::removeTempAssetData(const LLUUID& asset_id) 
1472{ }
1473
1474// virtual
1475void LLAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id) 
1476{ }
1477
1478// virtual
1479void LLAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const 
1480{ }
1481
1482// virtual
1483void LLAssetStorage::clearTempAssetData() 
1484{ }
1485
1486// static
1487void LLAssetStorage::reportMetric( const LLUUID& asset_id, const LLAssetType::EType asset_type, const std::string& in_filename,
1488								   const LLUUID& agent_id, S32 asset_size, EMetricResult result,
1489								   const char *file, const S32 line, const std::string& in_message )
1490{
1491	if( !metric_recipient )
1492	{
1493		LL_DEBUGS("AssetStorage") << "Couldn't store LLAssetStoreage::reportMetric - no metrics_recipient" << llendl;
1494		return;
1495	}
1496
1497	std::string filename(in_filename);
1498	if (filename.empty())
1499		filename = ll_safe_string(file);
1500	
1501	// Create revised message - new_message = "in_message :: file:line"
1502	std::stringstream new_message;
1503	new_message << in_message << " :: " << filename << ":" << line;
1504
1505	// Change always_report to true if debugging... do not check it in this way
1506	static bool always_report = false;
1507	const char *metric_name = "LLAssetStorage::Metrics";
1508
1509	bool success = result == MR_OKAY;
1510
1511	if( (!success) || always_report )
1512	{
1513		LLSD stats;
1514		stats["asset_id"] = asset_id;
1515		stats["asset_type"] = asset_type;
1516		stats["filename"] = filename;
1517		stats["agent_id"] = agent_id;
1518		stats["asset_size"] = (S32)asset_size;
1519		stats["result"] = (S32)result;
1520
1521		metric_recipient->recordEventDetails( metric_name, new_message.str(), success, stats);
1522	}
1523	else
1524	{
1525		metric_recipient->recordEvent(metric_name, new_message.str(), success);
1526	}
1527}
1528
1529
1530// Check if an asset is in the toxic map.  If it is, the entry is updated
1531BOOL	LLAssetStorage::isAssetToxic( const LLUUID& uuid )
1532{
1533	BOOL is_toxic = FALSE;
1534
1535	if ( !uuid.isNull() )
1536	{
1537		toxic_asset_map_t::iterator iter = mToxicAssetMap.find( uuid );
1538		if ( iter != mToxicAssetMap.end() )
1539		{	// Found toxic asset
1540			(*iter).second = LLFrameTimer::getTotalTime() + TOXIC_ASSET_LIFETIME;
1541			is_toxic = TRUE;
1542		} 
1543	}
1544	return is_toxic;
1545}
1546
1547
1548
1549
1550// Clean the toxic asset list, remove old entries
1551void	LLAssetStorage::flushOldToxicAssets( BOOL force_it )
1552{
1553	// Scan and look for old entries
1554	U64 now = LLFrameTimer::getTotalTime();
1555	toxic_asset_map_t::iterator iter = mToxicAssetMap.begin();
1556	while ( iter != mToxicAssetMap.end() )
1557	{
1558		if ( force_it
1559			|| (*iter).second < now )
1560		{	// Too old - remove it
1561			mToxicAssetMap.erase( iter++ );
1562		}
1563		else
1564		{
1565			iter++;
1566		}
1567	}
1568}
1569
1570
1571// Add an item to the toxic asset map
1572void	LLAssetStorage::markAssetToxic( const LLUUID& uuid )
1573{	
1574	if ( !uuid.isNull() )
1575	{
1576		// Set the value to the current time.  Creates a new entry if needed
1577		mToxicAssetMap[ uuid ] = LLFrameTimer::getTotalTime() + TOXIC_ASSET_LIFETIME;
1578	}
1579}
1580