aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llassetstorage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/llmessage/llassetstorage.cpp')
-rw-r--r--linden/indra/llmessage/llassetstorage.cpp1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llassetstorage.cpp b/linden/indra/llmessage/llassetstorage.cpp
new file mode 100644
index 0000000..eb26157
--- /dev/null
+++ b/linden/indra/llmessage/llassetstorage.cpp
@@ -0,0 +1,1183 @@
1/**
2 * @file llassetstorage.cpp
3 * @brief Implementation of the base asset storage system.
4 *
5 * Copyright (c) 2001-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 "linden_common.h"
29
30// system library includes
31#include <stdlib.h>
32#include <stdio.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <algorithm>
36
37#include "llassetstorage.h"
38
39// linden library includes
40#include "llmath.h"
41#include "llstring.h"
42#include "lldir.h"
43#include "llsd.h"
44
45// this library includes
46#include "message.h"
47#include "llxfermanager.h"
48#include "llvfile.h"
49#include "llvfs.h"
50#include "lldbstrings.h"
51
52#include "lltransfersourceasset.h"
53#include "lltransfertargetvfile.h" // For debugging
54
55LLAssetStorage *gAssetStorage = NULL;
56
57const LLUUID CATEGORIZE_LOST_AND_FOUND_ID("00000000-0000-0000-0000-000000000010");
58
59const F32 LL_ASSET_STORAGE_TIMEOUT = 300.0f; // anything that takes longer than this will abort
60
61
62
63///----------------------------------------------------------------------------
64/// LLAssetInfo
65///----------------------------------------------------------------------------
66
67LLAssetInfo::LLAssetInfo( void )
68: mDescription(),
69 mName(),
70 mUuid(),
71 mCreatorID(),
72 mType( LLAssetType::AT_NONE )
73{ }
74
75LLAssetInfo::LLAssetInfo( const LLUUID& object_id, const LLUUID& creator_id,
76 LLAssetType::EType type, const char* name,
77 const char* desc )
78: mUuid( object_id ),
79 mCreatorID( creator_id ),
80 mType( type )
81{
82 setName( name );
83 setDescription( desc );
84}
85
86LLAssetInfo::LLAssetInfo( const LLNameValue& nv )
87{
88 setFromNameValue( nv );
89}
90
91// make sure the name is short enough, and strip all pipes since they
92// are reserved characters in our inventory tracking system.
93void LLAssetInfo::setName( const std::string& name )
94{
95 if( !name.empty() )
96 {
97 mName.assign( name, 0, llmin((U32)name.size(), (U32)DB_INV_ITEM_NAME_STR_LEN) );
98 mName.erase( std::remove(mName.begin(), mName.end(), '|'),
99 mName.end() );
100 }
101}
102
103// make sure the name is short enough, and strip all pipes since they
104// are reserved characters in our inventory tracking system.
105void LLAssetInfo::setDescription( const std::string& desc )
106{
107 if( !desc.empty() )
108 {
109 mDescription.assign( desc, 0, llmin((U32)desc.size(),
110 (U32)DB_INV_ITEM_DESC_STR_LEN) );
111 mDescription.erase( std::remove(mDescription.begin(),
112 mDescription.end(), '|'),
113 mDescription.end() );
114 }
115}
116
117// Assets (aka potential inventory items) can be applied to an
118// object in the world. We'll store that as a string name value
119// pair where the name encodes part of asset info, and the value
120// the rest. LLAssetInfo objects will be responsible for parsing
121// the meaning out froman LLNameValue object. See the inventory
122// design docs for details. Briefly:
123// name=<inv_type>|<uuid>
124// value=<creatorid>|<name>|<description>|
125void LLAssetInfo::setFromNameValue( const LLNameValue& nv )
126{
127 std::string str;
128 std::string buf;
129 std::string::size_type pos1;
130 std::string::size_type pos2;
131
132 // convert the name to useful information
133 str.assign( nv.mName );
134 pos1 = str.find('|');
135 buf.assign( str, 0, pos1++ );
136 mType = LLAssetType::lookup( buf.c_str() );
137 buf.assign( str, pos1, std::string::npos );
138 mUuid.set( buf.c_str() );
139
140 // convert the value to useful information
141 str.assign( nv.getAsset() );
142 pos1 = str.find('|');
143 buf.assign( str, 0, pos1++ );
144 mCreatorID.set( buf.c_str() );
145 pos2 = str.find( '|', pos1 );
146 buf.assign( str, pos1, (pos2++) - pos1 );
147 setName( buf.c_str() );
148 buf.assign( str, pos2, std::string::npos );
149 setDescription( buf.c_str() );
150 llinfos << "uuid: " << mUuid << llendl;
151 llinfos << "creator: " << mCreatorID << llendl;
152}
153
154///----------------------------------------------------------------------------
155/// LLAssetRequest
156///----------------------------------------------------------------------------
157
158LLAssetRequest::LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType type)
159: mUUID(uuid),
160 mType(type),
161 mDownCallback( NULL ),
162 mUpCallback( NULL ),
163 mInfoCallback( NULL ),
164 mUserData( NULL ),
165 mHost(),
166 mIsTemp( FALSE ),
167 mIsLocal(FALSE),
168 mIsPriority(FALSE),
169 mDataSentInFirstPacket(FALSE),
170 mDataIsInVFS( FALSE )
171{
172 // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
173 // running a message system loop.
174 mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
175}
176
177LLAssetRequest::~LLAssetRequest()
178{
179}
180
181
182///----------------------------------------------------------------------------
183/// LLInvItemRequest
184///----------------------------------------------------------------------------
185
186LLInvItemRequest::LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType type)
187: mUUID(uuid),
188 mType(type),
189 mDownCallback( NULL ),
190 mUserData( NULL ),
191 mHost(),
192 mIsTemp( FALSE ),
193 mIsPriority(FALSE),
194 mDataSentInFirstPacket(FALSE),
195 mDataIsInVFS( FALSE )
196{
197 // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
198 // running a message system loop.
199 mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
200}
201
202LLInvItemRequest::~LLInvItemRequest()
203{
204}
205
206///----------------------------------------------------------------------------
207/// LLEstateAssetRequest
208///----------------------------------------------------------------------------
209
210LLEstateAssetRequest::LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType atype,
211 EstateAssetType etype)
212: mUUID(uuid),
213 mAType(atype),
214 mEstateAssetType(etype),
215 mDownCallback( NULL ),
216 mUserData( NULL ),
217 mHost(),
218 mIsTemp( FALSE ),
219 mIsPriority(FALSE),
220 mDataSentInFirstPacket(FALSE),
221 mDataIsInVFS( FALSE )
222{
223 // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
224 // running a message system loop.
225 mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
226}
227
228LLEstateAssetRequest::~LLEstateAssetRequest()
229{
230}
231
232
233///----------------------------------------------------------------------------
234/// LLAssetStorage
235///----------------------------------------------------------------------------
236
237// since many of these functions are called by the messaging and xfer systems,
238// they are declared as static and are passed a "this" handle
239// it's a C/C++ mish-mash!
240
241// TODO: permissions on modifications - maybe don't allow at all?
242// TODO: verify that failures get propogated down
243// TODO: rework tempfile handling?
244
245
246LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, const LLHost &upstream_host)
247{
248 _init(msg, xfer, vfs, upstream_host);
249}
250
251
252LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
253 LLVFS *vfs)
254{
255 _init(msg, xfer, vfs, LLHost::invalid);
256}
257
258
259void LLAssetStorage::_init(LLMessageSystem *msg,
260 LLXferManager *xfer,
261 LLVFS *vfs,
262 const LLHost &upstream_host)
263{
264 mShutDown = FALSE;
265 mMessageSys = msg;
266 mXferManager = xfer;
267 mVFS = vfs;
268
269 setUpstream(upstream_host);
270 msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this);
271}
272
273LLAssetStorage::~LLAssetStorage()
274{
275 mShutDown = TRUE;
276
277 _cleanupRequests(TRUE, LL_ERR_CIRCUIT_GONE);
278
279 if (gMessageSystem)
280 {
281 // Warning! This won't work if there's more than one asset storage.
282 // unregister our callbacks with the message system
283 gMessageSystem->setHandlerFuncFast(_PREHASH_AssetUploadComplete, NULL, NULL);
284 }
285}
286
287void LLAssetStorage::setUpstream(const LLHost &upstream_host)
288{
289 llinfos << "AssetStorage: Setting upstream provider to " << upstream_host << llendl;
290
291 mUpstreamHost = upstream_host;
292}
293
294void LLAssetStorage::checkForTimeouts()
295{
296 _cleanupRequests(FALSE, LL_ERR_TCP_TIMEOUT);
297}
298
299void LLAssetStorage::_cleanupRequests(BOOL all, S32 error)
300{
301 const S32 NUM_QUEUES = 3;
302 F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
303
304 std::list<LLAssetRequest*>* requests[NUM_QUEUES];
305 requests[0] = &mPendingDownloads;
306 requests[1] = &mPendingUploads;
307 requests[2] = &mPendingLocalUploads;
308 static const char* REQUEST_TYPE[NUM_QUEUES] = { "download", "upload", "localuploads"};
309
310 std::list<LLAssetRequest*> timed_out;
311
312 for (S32 ii = 0; ii < NUM_QUEUES; ++ii)
313 {
314 for (std::list<LLAssetRequest*>::iterator iter = requests[ii]->begin();
315 iter != requests[ii]->end(); )
316 {
317 std::list<LLAssetRequest*>::iterator curiter = iter++;
318 LLAssetRequest* tmp = *curiter;
319 // if all is true, we want to clean up everything
320 // otherwise just check for timed out requests
321 // EXCEPT for upload timeouts
322 if (all
323 || ((0 == ii)
324 && LL_ASSET_STORAGE_TIMEOUT < (mt_secs - tmp->mTime)))
325 {
326 llwarns << "Asset " << REQUEST_TYPE[ii] << " request "
327 << (all ? "aborted" : "timed out") << " for "
328 << tmp->getUUID() << "."
329 << LLAssetType::lookup(tmp->getType()) << llendl;
330
331 timed_out.push_front(tmp);
332 iter = requests[ii]->erase(curiter);
333 }
334 }
335 }
336
337 LLAssetInfo info;
338 for (std::list<LLAssetRequest*>::iterator iter = timed_out.begin();
339 iter != timed_out.end(); )
340 {
341 std::list<LLAssetRequest*>::iterator curiter = iter++;
342 LLAssetRequest* tmp = *curiter;
343 if (tmp->mUpCallback)
344 {
345 tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error);
346 }
347 if (tmp->mDownCallback)
348 {
349 tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error);
350 }
351 if (tmp->mInfoCallback)
352 {
353 tmp->mInfoCallback(&info, tmp->mUserData, error);
354 }
355 delete tmp;
356 }
357
358}
359
360BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type)
361{
362 return mVFS->getExists(uuid, type);
363}
364
365///////////////////////////////////////////////////////////////////////////
366// GET routines
367///////////////////////////////////////////////////////////////////////////
368
369// IW - uuid is passed by value to avoid side effects, please don't re-add &
370void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *,S32), void *user_data, BOOL is_priority)
371{
372 lldebugs << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << llendl;
373
374 if (mShutDown)
375 {
376 return; // don't get the asset or do any callbacks, we are shutting down
377 }
378
379 if (uuid.isNull())
380 {
381 // Special case early out for NULL uuid
382 if (callback)
383 {
384 callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
385 }
386 return;
387 }
388
389 BOOL exists = mVFS->getExists(uuid, type);
390 LLVFile file(mVFS, uuid, type);
391 U32 size = exists ? file.getSize() : 0;
392
393 if (size < 1)
394 {
395 if (exists)
396 {
397 llwarns << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << llendl;
398 file.remove();
399 }
400
401 BOOL duplicate = FALSE;
402
403 // check to see if there's a pending download of this uuid already
404 for (std::list<LLAssetRequest*>::iterator iter = mPendingDownloads.begin();
405 iter != mPendingDownloads.end(); ++iter )
406 {
407 LLAssetRequest *tmp = *iter;
408 if ((type == tmp->getType()) && (uuid == tmp->getUUID()))
409 {
410 if (callback == tmp->mDownCallback && user_data == tmp->mUserData)
411 {
412 // this is a duplicate from the same subsystem - throw it away
413 llinfos << "Discarding duplicate request for UUID " << uuid << llendl;
414 return;
415 }
416 else
417 {
418 llinfos << "Adding additional non-duplicate request for UUID " << uuid << llendl;
419 }
420
421 // this is a duplicate request
422 // queue the request, but don't actually ask for it again
423 duplicate = TRUE;
424 break;
425 }
426 }
427
428 // This can be overridden by subclasses
429 _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority);
430 }
431 else
432 {
433 // we've already got the file
434 // theoretically, partial files w/o a pending request shouldn't happen
435 // unless there's a weird error
436 if (callback)
437 {
438 callback(mVFS, uuid, type, user_data, LL_ERR_NOERR);
439 }
440 }
441}
442
443void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType atype,
444 LLGetAssetCallback callback,
445 void *user_data, BOOL duplicate,
446 BOOL is_priority)
447{
448 if (mUpstreamHost.isOk())
449 {
450 // stash the callback info so we can find it after we get the response message
451 LLAssetRequest *req = new LLAssetRequest(uuid, atype);
452 req->mDownCallback = callback;
453 req->mUserData = user_data;
454 req->mIsPriority = is_priority;
455
456 mPendingDownloads.push_back(req);
457
458 if (!duplicate)
459 {
460 // send request message to our upstream data provider
461 // Create a new asset transfer.
462 LLTransferSourceParamsAsset spa;
463 spa.setAsset(uuid, atype);
464
465 // Set our destination file, and the completion callback.
466 LLTransferTargetParamsVFile tpvf;
467 tpvf.setAsset(uuid, atype);
468 tpvf.setCallback(downloadCompleteCallback, req);
469
470 llinfos << "Starting transfer for " << uuid << llendl;
471 LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
472 ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
473 }
474 }
475 else
476 {
477 // uh-oh, we shouldn't have gotten here
478 llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
479 if (callback)
480 {
481 callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE);
482 }
483 }
484}
485
486
487void LLAssetStorage::downloadCompleteCallback(
488 S32 result,
489 const LLUUID& file_id,
490 LLAssetType::EType file_type,
491 void* user_data)
492{
493 lldebugs << "LLAssetStorage::downloadCompleteCallback() for " << file_id
494 << "," << LLAssetType::lookup(file_type) << llendl;
495 LLAssetRequest* req = (LLAssetRequest*)user_data;
496 if(!req)
497 {
498 llwarns << "LLAssetStorage::downloadCompleteCallback called without"
499 "a valid request." << llendl;
500 return;
501 }
502 if (!gAssetStorage)
503 {
504 llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
505 return;
506 }
507
508 req->setUUID(file_id);
509 req->setType(file_type);
510 if (LL_ERR_NOERR == result)
511 {
512 // we might have gotten a zero-size file
513 LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
514 if (vfile.getSize() <= 0)
515 {
516 llwarns << "downloadCompleteCallback has non-existent or zero-size asset " << req->getUUID() << llendl;
517
518 result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
519 vfile.remove();
520 }
521 }
522
523 // find and callback ALL pending requests for this UUID
524 // SJB: We process the callbacks in reverse order, I do not know if this is important,
525 // but I didn't want to mess with it.
526 std::list<LLAssetRequest*> requests;
527 for (std::list<LLAssetRequest*>::iterator iter = gAssetStorage->mPendingDownloads.begin();
528 iter != gAssetStorage->mPendingDownloads.end(); )
529 {
530 std::list<LLAssetRequest*>::iterator curiter = iter++;
531 LLAssetRequest* tmp = *curiter;
532 if ((tmp->getUUID() == req->getUUID()) && (tmp->getType()== req->getType()))
533 {
534 requests.push_front(tmp);
535 iter = gAssetStorage->mPendingDownloads.erase(curiter);
536 }
537 }
538 for (std::list<LLAssetRequest*>::iterator iter = requests.begin();
539 iter != requests.end(); )
540 {
541 std::list<LLAssetRequest*>::iterator curiter = iter++;
542 LLAssetRequest* tmp = *curiter;
543 if (tmp->mDownCallback)
544 {
545 tmp->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), tmp->mUserData, result);
546 }
547 delete tmp;
548 }
549}
550
551void LLAssetStorage::getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
552 const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype,
553 LLGetAssetCallback callback, void *user_data, BOOL is_priority)
554{
555 lldebugs << "LLAssetStorage::getEstateAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << ", estatetype " << etype << llendl;
556
557 //
558 // Probably will get rid of this early out?
559 //
560 if (asset_id.isNull())
561 {
562 // Special case early out for NULL uuid
563 if (callback)
564 {
565 callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
566 }
567 return;
568 }
569
570 BOOL exists = mVFS->getExists(asset_id, atype);
571 LLVFile file(mVFS, asset_id, atype);
572 U32 size = exists ? file.getSize() : 0;
573
574 if (size < 1)
575 {
576 if (exists)
577 {
578 llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
579 file.remove();
580 }
581
582 // See whether we should talk to the object's originating sim, or the upstream provider.
583 LLHost source_host;
584 if (object_sim.isOk())
585 {
586 source_host = object_sim;
587 }
588 else
589 {
590 source_host = mUpstreamHost;
591 }
592 if (source_host.isOk())
593 {
594 // stash the callback info so we can find it after we get the response message
595 LLEstateAssetRequest *req = new LLEstateAssetRequest(asset_id, atype, etype);
596 req->mDownCallback = callback;
597 req->mUserData = user_data;
598 req->mIsPriority = is_priority;
599
600 // send request message to our upstream data provider
601 // Create a new asset transfer.
602 LLTransferSourceParamsEstate spe;
603 spe.setAgentSession(agent_id, session_id);
604 spe.setEstateAssetType(etype);
605
606 // Set our destination file, and the completion callback.
607 LLTransferTargetParamsVFile tpvf;
608 tpvf.setAsset(asset_id, atype);
609 tpvf.setCallback(downloadEstateAssetCompleteCallback, req);
610
611 llinfos << "Starting transfer for " << asset_id << llendl;
612 LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
613 ttcp->requestTransfer(spe, tpvf, 100.f + (is_priority ? 1.f : 0.f));
614 }
615 else
616 {
617 // uh-oh, we shouldn't have gotten here
618 llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
619 if (callback)
620 {
621 callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE);
622 }
623 }
624 }
625 else
626 {
627 // we've already got the file
628 // theoretically, partial files w/o a pending request shouldn't happen
629 // unless there's a weird error
630 if (callback)
631 {
632 callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR);
633 }
634 }
635}
636
637void LLAssetStorage::downloadEstateAssetCompleteCallback(
638 S32 result,
639 const LLUUID& file_id,
640 LLAssetType::EType file_type,
641 void* user_data)
642{
643 LLEstateAssetRequest *req = (LLEstateAssetRequest*)user_data;
644 if(!req)
645 {
646 llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
647 " without a valid request." << llendl;
648 return;
649 }
650 if (!gAssetStorage)
651 {
652 llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
653 " without any asset system, aborting!" << llendl;
654 return;
655 }
656
657 req->setUUID(file_id);
658 req->setType(file_type);
659 if (LL_ERR_NOERR == result)
660 {
661 // we might have gotten a zero-size file
662 LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType());
663 if (vfile.getSize() <= 0)
664 {
665 llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
666
667 result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
668 vfile.remove();
669 }
670 }
671
672 req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result);
673}
674
675void LLAssetStorage::getInvItemAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
676 const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id,
677 const LLUUID &asset_id, LLAssetType::EType atype,
678 LLGetAssetCallback callback, void *user_data, BOOL is_priority)
679{
680 lldebugs << "LLAssetStorage::getInvItemAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << llendl;
681
682 //
683 // Probably will get rid of this early out?
684 //
685 //if (asset_id.isNull())
686 //{
687 // // Special case early out for NULL uuid
688 // if (callback)
689 // {
690 // callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
691 // }
692 // return;
693 //}
694
695 bool exists = false;
696 U32 size = 0;
697
698 if(asset_id.notNull())
699 {
700 exists = mVFS->getExists(asset_id, atype);
701 LLVFile file(mVFS, asset_id, atype);
702 size = exists ? file.getSize() : 0;
703 if(exists && size < 1)
704 {
705 llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
706 file.remove();
707 }
708
709 }
710
711 if (size < 1)
712 {
713 // See whether we should talk to the object's originating sim,
714 // or the upstream provider.
715 LLHost source_host;
716 if (object_sim.isOk())
717 {
718 source_host = object_sim;
719 }
720 else
721 {
722 source_host = mUpstreamHost;
723 }
724 if (source_host.isOk())
725 {
726 // stash the callback info so we can find it after we get the response message
727 LLInvItemRequest *req = new LLInvItemRequest(asset_id, atype);
728 req->mDownCallback = callback;
729 req->mUserData = user_data;
730 req->mIsPriority = is_priority;
731
732 // send request message to our upstream data provider
733 // Create a new asset transfer.
734 LLTransferSourceParamsInvItem spi;
735 spi.setAgentSession(agent_id, session_id);
736 spi.setInvItem(owner_id, task_id, item_id);
737 spi.setAsset(asset_id, atype);
738
739 // Set our destination file, and the completion callback.
740 LLTransferTargetParamsVFile tpvf;
741 tpvf.setAsset(asset_id, atype);
742 tpvf.setCallback(downloadInvItemCompleteCallback, req);
743
744 llinfos << "Starting transfer for inventory asset "
745 << item_id << " owned by " << owner_id << "," << task_id
746 << llendl;
747 LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
748 ttcp->requestTransfer(spi, tpvf, 100.f + (is_priority ? 1.f : 0.f));
749 }
750 else
751 {
752 // uh-oh, we shouldn't have gotten here
753 llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
754 if (callback)
755 {
756 callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE);
757 }
758 }
759 }
760 else
761 {
762 // we've already got the file
763 // theoretically, partial files w/o a pending request shouldn't happen
764 // unless there's a weird error
765 if (callback)
766 {
767 callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR);
768 }
769 }
770}
771
772
773void LLAssetStorage::downloadInvItemCompleteCallback(
774 S32 result,
775 const LLUUID& file_id,
776 LLAssetType::EType file_type,
777 void* user_data)
778{
779 LLInvItemRequest *req = (LLInvItemRequest*)user_data;
780 if(!req)
781 {
782 llwarns << "LLAssetStorage::downloadEstateAssetCompleteCallback called"
783 " without a valid request." << llendl;
784 return;
785 }
786 if (!gAssetStorage)
787 {
788 llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
789 return;
790 }
791
792 req->setUUID(file_id);
793 req->setType(file_type);
794 if (LL_ERR_NOERR == result)
795 {
796 // we might have gotten a zero-size file
797 LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
798 if (vfile.getSize() <= 0)
799 {
800 llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
801
802 result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
803 vfile.remove();
804 }
805 }
806
807 req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result);
808}
809
810/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
811// Store routines
812/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
813
814// virtual
815void LLAssetStorage::cancelStoreAsset(
816 const LLUUID& uuid,
817 LLAssetType::EType atype)
818{
819 bool do_callback = true;
820 LLAssetRequest* req = NULL;
821
822 if(mPendingUploads.size() > 0)
823 {
824 req = mPendingUploads.front();
825 if((req->getUUID() == uuid) && (req->getType() == atype))
826 {
827 // This is probably because the request is in progress - do
828 // not attempt to cancel.
829 do_callback = false;
830 }
831 }
832
833 if (mPendingLocalUploads.size() > 0)
834 {
835 req = mPendingLocalUploads.front();
836 if((req->getUUID() == uuid) && (req->getType() == atype))
837 {
838 // This is probably because the request is in progress - do
839 // not attempt to cancel.
840 do_callback = false;
841 }
842 }
843
844 if (do_callback)
845 {
846 // clear it out of the upload queue if it is there.
847 _callUploadCallbacks(uuid, atype, FALSE);
848 }
849}
850
851// static
852void LLAssetStorage::uploadCompleteCallback(const LLUUID& uuid, void *user_data, S32 result) // StoreAssetData callback (fixed)
853{
854 if (!gAssetStorage)
855 {
856 llwarns << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << llendl;
857 return;
858 }
859 LLAssetRequest *req = (LLAssetRequest *)user_data;
860 BOOL success = TRUE;
861
862 if (result)
863 {
864 llwarns << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << llendl;
865 success = FALSE;
866 }
867
868 // we're done grabbing the file, tell the client
869 gAssetStorage->mMessageSys->newMessageFast(_PREHASH_AssetUploadComplete);
870 gAssetStorage->mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
871 gAssetStorage->mMessageSys->addUUIDFast(_PREHASH_UUID, uuid);
872 gAssetStorage->mMessageSys->addS8Fast(_PREHASH_Type, req->getType());
873 gAssetStorage->mMessageSys->addBOOLFast(_PREHASH_Success, success);
874 gAssetStorage->mMessageSys->sendReliable(req->mHost);
875
876 delete req;
877}
878
879void LLAssetStorage::processUploadComplete(LLMessageSystem *msg, void **user_data)
880{
881 LLAssetStorage *this_ptr = (LLAssetStorage *)user_data;
882 LLUUID uuid;
883 S8 asset_type_s8;
884 LLAssetType::EType asset_type;
885 BOOL success = FALSE;
886
887 msg->getUUIDFast(_PREHASH_AssetBlock, _PREHASH_UUID, uuid);
888 msg->getS8Fast(_PREHASH_AssetBlock, _PREHASH_Type, asset_type_s8);
889 msg->getBOOLFast(_PREHASH_AssetBlock, _PREHASH_Success, success);
890
891 asset_type = (LLAssetType::EType)asset_type_s8;
892 this_ptr->_callUploadCallbacks(uuid, asset_type, success);
893}
894
895void LLAssetStorage::_callUploadCallbacks(const LLUUID &uuid, LLAssetType::EType asset_type, BOOL success)
896{
897 // SJB: We process the callbacks in reverse order, I do not know if this is important,
898 // but I didn't want to mess with it.
899 std::list<LLAssetRequest*> requests;
900 for (std::list<LLAssetRequest*>::iterator iter = mPendingUploads.begin();
901 iter != mPendingUploads.end(); )
902 {
903 std::list<LLAssetRequest*>::iterator curiter = iter++;
904 LLAssetRequest* req = *curiter;
905 if ((req->getUUID() == uuid) && (req->getType() == asset_type))
906 {
907 requests.push_front(req);
908 iter = mPendingUploads.erase(curiter);
909 }
910 }
911 for (std::list<LLAssetRequest*>::iterator iter = mPendingLocalUploads.begin();
912 iter != mPendingLocalUploads.end(); )
913 {
914 std::list<LLAssetRequest*>::iterator curiter = iter++;
915 LLAssetRequest* req = *curiter;
916 if ((req->getUUID() == uuid) && (req->getType() == asset_type))
917 {
918 requests.push_front(req);
919 iter = mPendingLocalUploads.erase(curiter);
920 }
921 }
922 for (std::list<LLAssetRequest*>::iterator iter = requests.begin();
923 iter != requests.end(); )
924 {
925 std::list<LLAssetRequest*>::iterator curiter = iter++;
926 LLAssetRequest* req = *curiter;
927 if (req->mUpCallback)
928 {
929 req->mUpCallback(uuid, req->mUserData, (success ? LL_ERR_NOERR : LL_ERR_ASSET_REQUEST_FAILED ));
930 }
931 delete req;
932 }
933}
934
935
936S32 LLAssetStorage::getNumPendingDownloads() const
937{
938 return mPendingDownloads.size();
939}
940
941S32 LLAssetStorage::getNumPendingUploads() const
942{
943 return mPendingUploads.size();
944}
945
946S32 LLAssetStorage::getNumPendingLocalUploads()
947{
948 return mPendingLocalUploads.size();
949}
950
951LLSD LLAssetStorage::getPendingTypes(const std::list<LLAssetRequest*>& requests) const
952{
953 LLSD type_counts;
954 std::list<LLAssetRequest*>::const_iterator it = requests.begin();
955 std::list<LLAssetRequest*>::const_iterator end = requests.end();
956 for ( ; it != end; ++it)
957 {
958 LLAssetRequest* req = *it;
959
960 const char* type_name = LLAssetType::lookupHumanReadable(req->getType());
961 type_counts[type_name] = type_counts[type_name].asInteger() + 1;
962 }
963 return type_counts;
964}
965
966LLSD LLAssetStorage::getPendingDownloadTypes() const
967{
968 return getPendingTypes(mPendingDownloads);
969}
970
971LLSD LLAssetStorage::getPendingUploadTypes() const
972{
973 return getPendingTypes(mPendingUploads);
974}
975
976// static
977const char* LLAssetStorage::getErrorString(S32 status)
978{
979 switch( status )
980 {
981 case LL_ERR_NOERR:
982 return "No error";
983
984 case LL_ERR_ASSET_REQUEST_FAILED:
985 return "Asset request: failed";
986
987 case LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE:
988 return "Asset request: non-existent file";
989
990 case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
991 return "Asset request: asset not found in database";
992
993 case LL_ERR_EOF:
994 return "End of file";
995
996 case LL_ERR_CANNOT_OPEN_FILE:
997 return "Cannot open file";
998
999 case LL_ERR_FILE_NOT_FOUND:
1000 return "File not found";
1001
1002 case LL_ERR_TCP_TIMEOUT:
1003 return "File transfer timeout";
1004
1005 case LL_ERR_CIRCUIT_GONE:
1006 return "Circuit gone";
1007
1008 default:
1009 return "Unknown status";
1010 }
1011}
1012
1013
1014
1015void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32), void *user_data, BOOL is_priority)
1016{
1017 // check for duplicates here, since we're about to fool the normal duplicate checker
1018 for (std::list<LLAssetRequest*>::iterator iter = mPendingDownloads.begin();
1019 iter != mPendingDownloads.end(); )
1020 {
1021 LLAssetRequest* tmp = *iter++;
1022 if (type == tmp->getType() &&
1023 uuid == tmp->getUUID() &&
1024 legacyGetDataCallback == tmp->mDownCallback &&
1025 callback == ((LLLegacyAssetRequest *)tmp->mUserData)->mDownCallback &&
1026 user_data == ((LLLegacyAssetRequest *)tmp->mUserData)->mUserData)
1027 {
1028 // this is a duplicate from the same subsystem - throw it away
1029 llinfos << "Discarding duplicate request for UUID " << uuid << llendl;
1030 return;
1031 }
1032 }
1033
1034
1035 LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
1036
1037 legacy->mDownCallback = callback;
1038 legacy->mUserData = user_data;
1039
1040 getAssetData(uuid, type, legacyGetDataCallback, (void **)legacy,
1041 is_priority);
1042}
1043
1044// static
1045void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 status)
1046{
1047 LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
1048 char filename[LL_MAX_PATH] = ""; /* Flawfinder: ignore */
1049
1050 if (! status)
1051 {
1052 LLVFile file(vfs, uuid, type);
1053
1054 char uuid_str[UUID_STR_LENGTH]; /* Flawfinder: ignore */
1055
1056 uuid.toString(uuid_str);
1057 snprintf(filename,sizeof(filename),"%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type)); /* Flawfinder: ignore */
1058
1059 FILE *fp = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
1060 if (fp)
1061 {
1062 const S32 buf_size = 65536;
1063 U8 copy_buf[buf_size];
1064 while (file.read(copy_buf, buf_size))
1065 {
1066 if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1)
1067 {
1068 // return a bad file error if we can't write the whole thing
1069 status = LL_ERR_CANNOT_OPEN_FILE;
1070 }
1071 }
1072
1073 fclose(fp);
1074 }
1075 else
1076 {
1077 status = LL_ERR_CANNOT_OPEN_FILE;
1078 }
1079 }
1080
1081 legacy->mDownCallback(filename, uuid, legacy->mUserData, status);
1082 delete legacy;
1083}
1084
1085// this is overridden on the viewer and the sim, so it doesn't really do anything
1086// virtual
1087void LLAssetStorage::storeAssetData(
1088 const LLTransactionID& tid,
1089 LLAssetType::EType asset_type,
1090 LLStoreAssetCallback callback,
1091 void* user_data,
1092 bool temp_file,
1093 bool is_priority,
1094 bool store_local)
1095{
1096 llwarns << "storeAssetData: wrong version called" << llendl;
1097}
1098
1099// virtual
1100// this does nothing, viewer and sim both override this.
1101void LLAssetStorage::storeAssetData(
1102 const LLUUID& asset_id,
1103 LLAssetType::EType asset_type,
1104 LLStoreAssetCallback callback,
1105 void* user_data,
1106 bool temp_file ,
1107 bool is_priority,
1108 bool store_local,
1109 const LLUUID& requesting_agent_id)
1110{
1111 llwarns << "storeAssetData: wrong version called" << llendl;
1112}
1113
1114// virtual
1115// this does nothing, viewer and sim both override this.
1116void LLAssetStorage::storeAssetData(
1117 const char* filename,
1118 const LLUUID& asset_id,
1119 LLAssetType::EType asset_type,
1120 LLStoreAssetCallback callback,
1121 void* user_data,
1122 bool temp_file,
1123 bool is_priority)
1124{
1125 llwarns << "storeAssetData: wrong version called" << llendl;
1126}
1127
1128// virtual
1129// this does nothing, viewer and sim both override this.
1130void LLAssetStorage::storeAssetData(
1131 const char* filename,
1132 const LLTransactionID &transactoin_id,
1133 LLAssetType::EType asset_type,
1134 LLStoreAssetCallback callback,
1135 void* user_data,
1136 bool temp_file,
1137 bool is_priority)
1138{
1139 llwarns << "storeAssetData: wrong version called" << llendl;
1140}
1141
1142// static
1143void LLAssetStorage::legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status)
1144{
1145 LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
1146 if (legacy && legacy->mUpCallback)
1147 {
1148 legacy->mUpCallback(uuid, legacy->mUserData, status);
1149 }
1150 delete legacy;
1151}
1152
1153// virtual
1154void LLAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
1155{ }
1156
1157// virtual
1158BOOL LLAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
1159{ return FALSE; }
1160
1161// virtual
1162std::string LLAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
1163{ return std::string(); }
1164
1165// virtual
1166LLUUID LLAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
1167{ return LLUUID::null; }
1168
1169// virtual
1170void LLAssetStorage::removeTempAssetData(const LLUUID& asset_id)
1171{ }
1172
1173// virtual
1174void LLAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
1175{ }
1176
1177// virtual
1178void LLAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
1179{ }
1180
1181// virtual
1182void LLAssetStorage::clearTempAssetData()
1183{ }