1080 lines
31 KiB
C++
1080 lines
31 KiB
C++
/**
|
|
* @file llviewerinventory.cpp
|
|
* @brief Implementation of the viewer side inventory objects.
|
|
*
|
|
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
|
*
|
|
* Copyright (c) 2002-2009, Linden Research, Inc.
|
|
*
|
|
* Second Life Viewer Source Code
|
|
* The source code in this file ("Source Code") is provided by Linden Lab
|
|
* to you under the terms of the GNU General Public License, version 2.0
|
|
* ("GPL"), unless you have obtained a separate licensing agreement
|
|
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
|
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
|
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
|
*
|
|
* There are special exceptions to the terms and conditions of the GPL as
|
|
* it is applied to this Source Code. View the full text of the exception
|
|
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
|
* online at
|
|
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
|
*
|
|
* By copying, modifying or distributing this software, you acknowledge
|
|
* that you have read and understood your obligations described above,
|
|
* and agree to abide by those obligations.
|
|
*
|
|
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
|
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
|
* COMPLETENESS OR PERFORMANCE.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
#include "llviewerinventory.h"
|
|
|
|
#include "message.h"
|
|
#include "indra_constants.h"
|
|
|
|
#include "llagent.h"
|
|
#include "llagentcamera.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llconsole.h"
|
|
#include "llinventorymodel.h"
|
|
#include "llnotify.h"
|
|
#include "llimview.h"
|
|
#include "llgesturemgr.h"
|
|
|
|
#include "llinventorybridge.h"
|
|
#include "llinventorydefines.h"
|
|
#include "llinventoryview.h"
|
|
|
|
#include "llviewerregion.h"
|
|
#include "llviewerobjectlist.h"
|
|
#include "llpreviewgesture.h"
|
|
#include "llviewerwindow.h"
|
|
|
|
// <edit>
|
|
#include "llappviewer.h" // System Folders
|
|
// </edit>
|
|
|
|
///----------------------------------------------------------------------------
|
|
/// Local function declarations, constants, enums, and typedefs
|
|
///----------------------------------------------------------------------------
|
|
|
|
///----------------------------------------------------------------------------
|
|
/// Class LLViewerInventoryItem
|
|
///----------------------------------------------------------------------------
|
|
|
|
LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& uuid,
|
|
const LLUUID& parent_uuid,
|
|
const LLPermissions& perm,
|
|
const LLUUID& asset_uuid,
|
|
LLAssetType::EType type,
|
|
LLInventoryType::EType inv_type,
|
|
const std::string& name,
|
|
const std::string& desc,
|
|
const LLSaleInfo& sale_info,
|
|
U32 flags,
|
|
time_t creation_date_utc) :
|
|
LLInventoryItem(uuid, parent_uuid, perm, asset_uuid, type, inv_type,
|
|
name, desc, sale_info, flags, creation_date_utc),
|
|
mIsComplete(TRUE)
|
|
{
|
|
}
|
|
|
|
LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id,
|
|
const LLUUID& parent_id,
|
|
const std::string& name,
|
|
LLInventoryType::EType inv_type) :
|
|
LLInventoryItem(),
|
|
mIsComplete(FALSE)
|
|
{
|
|
mUUID = item_id;
|
|
mParentUUID = parent_id;
|
|
mInventoryType = inv_type;
|
|
mName = name;
|
|
}
|
|
|
|
LLViewerInventoryItem::LLViewerInventoryItem() :
|
|
LLInventoryItem(),
|
|
mIsComplete(FALSE)
|
|
{
|
|
}
|
|
|
|
LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other) :
|
|
LLInventoryItem()
|
|
{
|
|
copyViewerItem(other);
|
|
// <edit>
|
|
//if (!mIsComplete)
|
|
//{
|
|
// llwarns << "LLViewerInventoryItem copy constructor for incomplete item"
|
|
// << mUUID << llendl;
|
|
//}
|
|
}
|
|
|
|
LLViewerInventoryItem::LLViewerInventoryItem(const LLInventoryItem *other) :
|
|
LLInventoryItem(other),
|
|
mIsComplete(TRUE)
|
|
{
|
|
}
|
|
|
|
|
|
LLViewerInventoryItem::~LLViewerInventoryItem()
|
|
{
|
|
}
|
|
|
|
void LLViewerInventoryItem::copyViewerItem(const LLViewerInventoryItem* other)
|
|
{
|
|
LLInventoryItem::copyItem(other);
|
|
mIsComplete = other->mIsComplete;
|
|
mTransactionID = other->mTransactionID;
|
|
}
|
|
|
|
// virtual
|
|
void LLViewerInventoryItem::copyItem(const LLInventoryItem *other)
|
|
{
|
|
LLInventoryItem::copyItem(other);
|
|
mIsComplete = true;
|
|
mTransactionID.setNull();
|
|
}
|
|
|
|
void LLViewerInventoryItem::cloneViewerItem(LLPointer<LLViewerInventoryItem>& newitem) const
|
|
{
|
|
newitem = new LLViewerInventoryItem(this);
|
|
if(newitem.notNull())
|
|
{
|
|
LLUUID item_id;
|
|
item_id.generate();
|
|
newitem->setUUID(item_id);
|
|
}
|
|
}
|
|
|
|
void LLViewerInventoryItem::removeFromServer()
|
|
{
|
|
// <edit> this check is ghetto
|
|
if((mParentUUID == gSystemFolderRoot) || (gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)))
|
|
{
|
|
return;
|
|
}
|
|
// </edit>
|
|
llinfos << "Removing inventory item " << mUUID << " from server."
|
|
<< llendl;
|
|
|
|
LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
|
|
gInventory.accountForUpdate(up);
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_RemoveInventoryItem);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
msg->addUUIDFast(_PREHASH_ItemID, mUUID);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void LLViewerInventoryItem::updateServer(BOOL is_new) const
|
|
{
|
|
// <edit>
|
|
//if(gAgent.getID() != mPermissions.getOwner())
|
|
//{
|
|
// // *FIX: deal with this better.
|
|
// llwarns << "LLViewerInventoryItem::updateServer() - for unowned item"
|
|
// << llendl;
|
|
if((mParentUUID == gSystemFolderRoot) || (gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)))
|
|
{
|
|
// </edit>
|
|
return;
|
|
}
|
|
if(!mIsComplete)
|
|
{
|
|
// *FIX: deal with this better.
|
|
llwarns << "LLViewerInventoryItem::updateServer() - for incomplete item"
|
|
<< llendl;
|
|
LLNotifications::instance().add("IncompleteInventoryItem");
|
|
return;
|
|
}
|
|
LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0);
|
|
gInventory.accountForUpdate(up);
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_UpdateInventoryItem);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->addUUIDFast(_PREHASH_TransactionID, mTransactionID);
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
msg->addU32Fast(_PREHASH_CallbackID, 0);
|
|
packMessage(msg);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void LLViewerInventoryItem::fetchFromServer(void) const
|
|
{
|
|
if(!mIsComplete)
|
|
{
|
|
std::string url;
|
|
|
|
if( ALEXANDRIA_LINDEN_ID.getString() == mPermissions.getOwner().getString())
|
|
url = gAgent.getRegion()->getCapability("FetchLib");
|
|
else
|
|
url = gAgent.getRegion()->getCapability("FetchInventory");
|
|
|
|
if (!url.empty())
|
|
{
|
|
LLSD body;
|
|
body["agent_id"] = gAgent.getID();
|
|
body["items"][0]["owner_id"] = mPermissions.getOwner();
|
|
body["items"][0]["item_id"] = mUUID;
|
|
|
|
LLHTTPClient::post(url, body, new LLInventoryModel::fetchInventoryResponder(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();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// *FIX: this can be removed after a bit.
|
|
llwarns << "request to fetch complete item" << llendl;
|
|
}
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLViewerInventoryItem::unpackMessage(LLSD item)
|
|
{
|
|
BOOL rv = LLInventoryItem::fromLLSD(item);
|
|
mIsComplete = TRUE;
|
|
return rv;
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLViewerInventoryItem::unpackMessage(
|
|
LLMessageSystem* msg, const char* block, S32 block_num)
|
|
{
|
|
BOOL rv = LLInventoryItem::unpackMessage(msg, block, block_num);
|
|
mIsComplete = TRUE;
|
|
return rv;
|
|
}
|
|
|
|
void LLViewerInventoryItem::setTransactionID(const LLTransactionID& transaction_id)
|
|
{
|
|
mTransactionID = transaction_id;
|
|
}
|
|
// virtual
|
|
void LLViewerInventoryItem::packMessage(LLMessageSystem* msg) const
|
|
{
|
|
msg->addUUIDFast(_PREHASH_ItemID, mUUID);
|
|
msg->addUUIDFast(_PREHASH_FolderID, mParentUUID);
|
|
mPermissions.packMessage(msg);
|
|
msg->addUUIDFast(_PREHASH_TransactionID, mTransactionID);
|
|
S8 type = static_cast<S8>(mType);
|
|
msg->addS8Fast(_PREHASH_Type, type);
|
|
type = static_cast<S8>(mInventoryType);
|
|
msg->addS8Fast(_PREHASH_InvType, type);
|
|
msg->addU32Fast(_PREHASH_Flags, mFlags);
|
|
mSaleInfo.packMessage(msg);
|
|
msg->addStringFast(_PREHASH_Name, mName);
|
|
msg->addStringFast(_PREHASH_Description, mDescription);
|
|
msg->addS32Fast(_PREHASH_CreationDate, mCreationDate);
|
|
U32 crc = getCRC32();
|
|
msg->addU32Fast(_PREHASH_CRC, crc);
|
|
}
|
|
// virtual
|
|
BOOL LLViewerInventoryItem::importFile(LLFILE* fp)
|
|
{
|
|
BOOL rv = LLInventoryItem::importFile(fp);
|
|
mIsComplete = TRUE;
|
|
return rv;
|
|
}
|
|
|
|
// virtual
|
|
BOOL LLViewerInventoryItem::importLegacyStream(std::istream& input_stream)
|
|
{
|
|
BOOL rv = LLInventoryItem::importLegacyStream(input_stream);
|
|
mIsComplete = TRUE;
|
|
return rv;
|
|
}
|
|
|
|
bool LLViewerInventoryItem::importFileLocal(LLFILE* fp)
|
|
{
|
|
// TODO: convert all functions that return BOOL to return bool
|
|
bool rv = (LLInventoryItem::importFile(fp) ? true : false);
|
|
mIsComplete = false;
|
|
return rv;
|
|
}
|
|
|
|
bool LLViewerInventoryItem::exportFileLocal(LLFILE* fp) const
|
|
{
|
|
std::string uuid_str;
|
|
fprintf(fp, "\tinv_item\t0\n\t{\n");
|
|
mUUID.toString(uuid_str);
|
|
fprintf(fp, "\t\titem_id\t%s\n", uuid_str.c_str());
|
|
mParentUUID.toString(uuid_str);
|
|
fprintf(fp, "\t\tparent_id\t%s\n", uuid_str.c_str());
|
|
mPermissions.exportFile(fp);
|
|
fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
|
|
const std::string& inv_type_str = LLInventoryType::lookup(mInventoryType);
|
|
if(!inv_type_str.empty()) fprintf(fp, "\t\tinv_type\t%s\n", inv_type_str.c_str());
|
|
fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
|
|
fprintf(fp, "\t\tcreation_date\t%d\n", (S32) mCreationDate);
|
|
fprintf(fp,"\t}\n");
|
|
return true;
|
|
}
|
|
|
|
void LLViewerInventoryItem::updateParentOnServer(BOOL restamp) const
|
|
{
|
|
// <edit>
|
|
if(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)) return;
|
|
// </edit>
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_MoveInventoryItem);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->addBOOLFast(_PREHASH_Stamp, restamp);
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
msg->addUUIDFast(_PREHASH_ItemID, mUUID);
|
|
msg->addUUIDFast(_PREHASH_FolderID, mParentUUID);
|
|
msg->addString("NewName", NULL);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
//void LLViewerInventoryItem::setCloneCount(S32 clones)
|
|
//{
|
|
// mClones = clones;
|
|
//}
|
|
|
|
//S32 LLViewerInventoryItem::getCloneCount() const
|
|
//{
|
|
// return mClones;
|
|
//}
|
|
|
|
// [RLVa:KB] - Checked: 2010-09-27 (RLVa-1.1.3a) | Added: RLVa-1.1.3a
|
|
bool LLViewerInventoryItem::isWearableType() const
|
|
{
|
|
return (getInventoryType() == LLInventoryType::IT_WEARABLE);
|
|
}
|
|
|
|
EWearableType LLViewerInventoryItem::getWearableType() const
|
|
{
|
|
if (!isWearableType())
|
|
{
|
|
return WT_INVALID;
|
|
}
|
|
return EWearableType(getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK);
|
|
}
|
|
// [/RLVa:KB]
|
|
|
|
///----------------------------------------------------------------------------
|
|
/// Class LLViewerInventoryCategory
|
|
///----------------------------------------------------------------------------
|
|
|
|
LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid,
|
|
const LLUUID& parent_uuid,
|
|
LLFolderType::EType pref,
|
|
const std::string& name,
|
|
const LLUUID& owner_id) :
|
|
LLInventoryCategory(uuid, parent_uuid, pref, name),
|
|
mOwnerID(owner_id),
|
|
mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
|
|
mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
|
|
{
|
|
mDescendentsRequested.reset();
|
|
}
|
|
|
|
LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) :
|
|
mOwnerID(owner_id),
|
|
mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
|
|
mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
|
|
{
|
|
mDescendentsRequested.reset();
|
|
}
|
|
|
|
LLViewerInventoryCategory::LLViewerInventoryCategory(const LLViewerInventoryCategory* other)
|
|
{
|
|
copyViewerCategory(other);
|
|
}
|
|
|
|
LLViewerInventoryCategory::~LLViewerInventoryCategory()
|
|
{
|
|
}
|
|
|
|
void LLViewerInventoryCategory::copyViewerCategory(const LLViewerInventoryCategory* other)
|
|
{
|
|
copyCategory(other);
|
|
mOwnerID = other->mOwnerID;
|
|
mVersion = other->mVersion;
|
|
mDescendentCount = other->mDescendentCount;
|
|
mDescendentsRequested = other->mDescendentsRequested;
|
|
}
|
|
|
|
|
|
void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const
|
|
{
|
|
// <edit>
|
|
if(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)) return;
|
|
// </edit>
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_MoveInventoryFolder);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
|
|
msg->addBOOL("Stamp", restamp);
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
msg->addUUIDFast(_PREHASH_FolderID, mUUID);
|
|
msg->addUUIDFast(_PREHASH_ParentID, mParentUUID);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void LLViewerInventoryCategory::updateServer(BOOL is_new) const
|
|
{
|
|
// communicate that change with the server.
|
|
if ( (LLFolderType::FT_NONE != mPreferredType) && (LLFolderType::FT_OUTFIT != mPreferredType) )
|
|
{
|
|
LLNotifications::instance().add("CannotModifyProtectedCategories");
|
|
return;
|
|
}
|
|
|
|
LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0);
|
|
gInventory.accountForUpdate(up);
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_UpdateInventoryFolder);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_FolderData);
|
|
packMessage(msg);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void LLViewerInventoryCategory::removeFromServer( void )
|
|
{
|
|
llinfos << "Removing inventory category " << mUUID << " from server."
|
|
<< llendl;
|
|
// communicate that change with the server.
|
|
if ( (LLFolderType::FT_NONE != mPreferredType) && (LLFolderType::FT_OUTFIT != mPreferredType) )
|
|
{
|
|
LLNotifications::instance().add("CannotRemoveProtectedCategories");
|
|
return;
|
|
}
|
|
|
|
LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
|
|
gInventory.accountForUpdate(up);
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_RemoveInventoryFolder);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_FolderData);
|
|
msg->addUUIDFast(_PREHASH_FolderID, mUUID);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
bool LLViewerInventoryCategory::fetchDescendents()
|
|
{
|
|
// <edit>
|
|
if((mUUID == gSystemFolderRoot) || (gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot))) return false;
|
|
// </edit>
|
|
if((VERSION_UNKNOWN == mVersion)
|
|
&& mDescendentsRequested.hasExpired()) //Expired check prevents multiple downloads.
|
|
{
|
|
const F32 FETCH_TIMER_EXPIRY = 10.0f;
|
|
mDescendentsRequested.reset();
|
|
mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
|
|
|
|
// bitfield
|
|
// 1 = by date
|
|
// 2 = folders by date
|
|
// Need to mask off anything but the first bit.
|
|
// This comes from LLInventoryFilter from llfolderview.h
|
|
U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1;
|
|
|
|
// *NOTE: For bug EXT-2879, originally commented out
|
|
// gAgent.getRegion()->getCapability in order to use the old
|
|
// message-based system. This has been uncommented now that
|
|
// AIS folks are aware of the issue and have a fix in process.
|
|
// see ticket for details.
|
|
|
|
std::string url;
|
|
if (gAgent.getRegion())
|
|
{
|
|
url = gAgent.getRegion()->getCapability("FetchInventoryDescendents");
|
|
}
|
|
else
|
|
{
|
|
llwarns << "agent region is null" << llendl;
|
|
}
|
|
if (!url.empty()) //Capability found. Build up LLSD and use it.
|
|
{
|
|
LLInventoryModel::startBackgroundFetch(mUUID);
|
|
}
|
|
else
|
|
{ //Deprecated, but if we don't have a capability, use the old system.
|
|
//llinfos << "FetchInventoryDescendents capability not found. Using deprecated UDP message." << llendl;
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessage("FetchInventoryDescendents");
|
|
msg->nextBlock("AgentData");
|
|
msg->addUUID("AgentID", gAgent.getID());
|
|
msg->addUUID("SessionID", gAgent.getSessionID());
|
|
msg->nextBlock("InventoryData");
|
|
msg->addUUID("FolderID", mUUID);
|
|
msg->addUUID("OwnerID", mOwnerID);
|
|
|
|
msg->addS32("SortOrder", sort_order);
|
|
msg->addBOOL("FetchFolders", FALSE);
|
|
msg->addBOOL("FetchItems", TRUE);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LLViewerInventoryCategory::importFileLocal(LLFILE* fp)
|
|
{
|
|
// *NOTE: This buffer size is hard coded into scanf() below.
|
|
char buffer[MAX_STRING]; /* Flawfinder: ignore */
|
|
char keyword[MAX_STRING]; /* Flawfinder: ignore */
|
|
char valuestr[MAX_STRING]; /* Flawfinder: ignore */
|
|
|
|
keyword[0] = '\0';
|
|
valuestr[0] = '\0';
|
|
while(!feof(fp))
|
|
{
|
|
if (fgets(buffer, MAX_STRING, fp) == NULL)
|
|
{
|
|
buffer[0] = '\0';
|
|
}
|
|
|
|
sscanf( /* Flawfinder: ignore */
|
|
buffer, " %254s %254s", keyword, valuestr);
|
|
if(0 == strcmp("{",keyword))
|
|
{
|
|
continue;
|
|
}
|
|
if(0 == strcmp("}", keyword))
|
|
{
|
|
break;
|
|
}
|
|
else if(0 == strcmp("cat_id", keyword))
|
|
{
|
|
mUUID.set(valuestr);
|
|
}
|
|
else if(0 == strcmp("parent_id", keyword))
|
|
{
|
|
mParentUUID.set(valuestr);
|
|
}
|
|
else if(0 == strcmp("type", keyword))
|
|
{
|
|
mType = LLAssetType::lookup(valuestr);
|
|
}
|
|
else if(0 == strcmp("pref_type", keyword))
|
|
{
|
|
mPreferredType = LLFolderType::assetTypeToFolderType(LLAssetType::lookup(valuestr));
|
|
}
|
|
else if(0 == strcmp("name", keyword))
|
|
{
|
|
//strcpy(valuestr, buffer + strlen(keyword) + 3);
|
|
// *NOTE: Not ANSI C, but widely supported.
|
|
sscanf( /* Flawfinder: ignore */
|
|
buffer, " %254s %254[^|]", keyword, valuestr);
|
|
mName.assign(valuestr);
|
|
LLStringUtil::replaceNonstandardASCII(mName, ' ');
|
|
LLStringUtil::replaceChar(mName, '|', ' ');
|
|
}
|
|
else if(0 == strcmp("owner_id", keyword))
|
|
{
|
|
mOwnerID.set(valuestr);
|
|
}
|
|
else if(0 == strcmp("version", keyword))
|
|
{
|
|
sscanf(valuestr, "%d", &mVersion);
|
|
}
|
|
else
|
|
{
|
|
llwarns << "unknown keyword '" << keyword
|
|
<< "' in inventory import category " << mUUID << llendl;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool LLViewerInventoryCategory::exportFileLocal(LLFILE* fp) const
|
|
{
|
|
std::string uuid_str;
|
|
fprintf(fp, "\tinv_category\t0\n\t{\n");
|
|
mUUID.toString(uuid_str);
|
|
fprintf(fp, "\t\tcat_id\t%s\n", uuid_str.c_str());
|
|
mParentUUID.toString(uuid_str);
|
|
fprintf(fp, "\t\tparent_id\t%s\n", uuid_str.c_str());
|
|
fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
|
|
fprintf(fp, "\t\tpref_type\t%s\n", LLFolderType::lookup(mPreferredType).c_str());
|
|
fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
|
|
mOwnerID.toString(uuid_str);
|
|
fprintf(fp, "\t\towner_id\t%s\n", uuid_str.c_str());
|
|
fprintf(fp, "\t\tversion\t%d\n", mVersion);
|
|
fprintf(fp,"\t}\n");
|
|
return true;
|
|
}
|
|
|
|
///----------------------------------------------------------------------------
|
|
/// Local function definitions
|
|
///----------------------------------------------------------------------------
|
|
|
|
LLInventoryCallbackManager *LLInventoryCallbackManager::sInstance = NULL;
|
|
|
|
LLInventoryCallbackManager::LLInventoryCallbackManager() :
|
|
mLastCallback(0)
|
|
{
|
|
if( sInstance != NULL )
|
|
{
|
|
llwarns << "LLInventoryCallbackManager::LLInventoryCallbackManager: unexpected multiple instances" << llendl;
|
|
return;
|
|
}
|
|
sInstance = this;
|
|
}
|
|
|
|
LLInventoryCallbackManager::~LLInventoryCallbackManager()
|
|
{
|
|
if( sInstance != this )
|
|
{
|
|
llwarns << "LLInventoryCallbackManager::~LLInventoryCallbackManager: unexpected multiple instances" << llendl;
|
|
return;
|
|
}
|
|
sInstance = NULL;
|
|
}
|
|
|
|
U32 LLInventoryCallbackManager::registerCB(LLPointer<LLInventoryCallback> cb)
|
|
{
|
|
if (cb.isNull())
|
|
return 0;
|
|
|
|
mLastCallback++;
|
|
if (!mLastCallback)
|
|
mLastCallback++;
|
|
|
|
mMap[mLastCallback] = cb;
|
|
return mLastCallback;
|
|
}
|
|
|
|
void LLInventoryCallbackManager::fire(U32 callback_id, const LLUUID& item_id)
|
|
{
|
|
if (!callback_id || item_id.isNull())
|
|
return;
|
|
|
|
std::map<U32, LLPointer<LLInventoryCallback> >::iterator i;
|
|
|
|
i = mMap.find(callback_id);
|
|
if (i != mMap.end())
|
|
{
|
|
(*i).second->fire(item_id);
|
|
mMap.erase(i);
|
|
}
|
|
}
|
|
|
|
void WearOnAvatarCallback::fire(const LLUUID& inv_item)
|
|
{
|
|
if (inv_item.isNull())
|
|
return;
|
|
|
|
LLViewerInventoryItem *item = gInventory.getItem(inv_item);
|
|
if (item)
|
|
{
|
|
wear_inventory_item_on_avatar(item);
|
|
}
|
|
}
|
|
|
|
RezAttachmentCallback::RezAttachmentCallback(LLViewerJointAttachment *attachmentp, bool replace)
|
|
{
|
|
mAttach = attachmentp;
|
|
mReplace = replace;
|
|
}
|
|
RezAttachmentCallback::~RezAttachmentCallback()
|
|
{
|
|
}
|
|
|
|
void RezAttachmentCallback::fire(const LLUUID& inv_item)
|
|
{
|
|
if (inv_item.isNull())
|
|
return;
|
|
|
|
LLViewerInventoryItem *item = gInventory.getItem(inv_item);
|
|
if (item)
|
|
{
|
|
rez_attachment(item, mAttach, mReplace);
|
|
}
|
|
}
|
|
|
|
extern LLGestureManager gGestureManager;
|
|
void ActivateGestureCallback::fire(const LLUUID& inv_item)
|
|
{
|
|
if (inv_item.isNull())
|
|
return;
|
|
|
|
gGestureManager.activateGesture(inv_item);
|
|
}
|
|
|
|
void CreateGestureCallback::fire(const LLUUID& inv_item)
|
|
{
|
|
if (inv_item.isNull())
|
|
return;
|
|
|
|
gGestureManager.activateGesture(inv_item);
|
|
|
|
LLViewerInventoryItem* item = gInventory.getItem(inv_item);
|
|
if (!item) return;
|
|
gInventory.updateItem(item);
|
|
gInventory.notifyObservers();
|
|
|
|
if(!LLPreview::show(inv_item,FALSE))
|
|
{
|
|
LLPreviewGesture* preview = LLPreviewGesture::show(std::string("Gesture: ") + item->getName(), inv_item, LLUUID::null);
|
|
// Force to be entirely onscreen.
|
|
gFloaterView->adjustToFitScreen(preview, FALSE);
|
|
}
|
|
}
|
|
|
|
LLInventoryCallbackManager gInventoryCallbacks;
|
|
|
|
void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
|
|
const LLUUID& parent, const LLTransactionID& transaction_id,
|
|
const std::string& name,
|
|
const std::string& desc, LLAssetType::EType asset_type,
|
|
LLInventoryType::EType inv_type, EWearableType wtype,
|
|
U32 next_owner_perm,
|
|
LLPointer<LLInventoryCallback> cb)
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_CreateInventoryItem);
|
|
msg->nextBlock(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, agent_id);
|
|
msg->addUUIDFast(_PREHASH_SessionID, session_id);
|
|
msg->nextBlock(_PREHASH_InventoryBlock);
|
|
msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
|
|
msg->addUUIDFast(_PREHASH_FolderID, parent);
|
|
msg->addUUIDFast(_PREHASH_TransactionID, transaction_id);
|
|
msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_perm);
|
|
msg->addS8Fast(_PREHASH_Type, (S8)asset_type);
|
|
msg->addS8Fast(_PREHASH_InvType, (S8)inv_type);
|
|
msg->addU8Fast(_PREHASH_WearableType, (U8)wtype);
|
|
msg->addStringFast(_PREHASH_Name, name);
|
|
msg->addStringFast(_PREHASH_Description, desc);
|
|
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void copy_inventory_item(
|
|
const LLUUID& agent_id,
|
|
const LLUUID& current_owner,
|
|
const LLUUID& item_id,
|
|
const LLUUID& parent_id,
|
|
const std::string& new_name,
|
|
LLPointer<LLInventoryCallback> cb)
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_CopyInventoryItem);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, agent_id);
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
|
|
msg->addUUIDFast(_PREHASH_OldAgentID, current_owner);
|
|
msg->addUUIDFast(_PREHASH_OldItemID, item_id);
|
|
msg->addUUIDFast(_PREHASH_NewFolderID, parent_id);
|
|
msg->addStringFast(_PREHASH_NewName, new_name);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void link_inventory_item(
|
|
const LLUUID& agent_id,
|
|
const LLUUID& item_id,
|
|
const LLUUID& parent_id,
|
|
const std::string& new_name,
|
|
const std::string& new_description,
|
|
const LLAssetType::EType asset_type,
|
|
LLPointer<LLInventoryCallback> cb)
|
|
{
|
|
const LLInventoryObject *baseobj = gInventory.getObject(item_id);
|
|
if (!baseobj)
|
|
{
|
|
llwarns << "attempt to link to unknown item, linked-to-item's itemID " << item_id << llendl;
|
|
return;
|
|
}
|
|
if (baseobj && baseobj->getIsLinkType())
|
|
{
|
|
llwarns << "attempt to create a link to a link, linked-to-item's itemID " << item_id << llendl;
|
|
return;
|
|
}
|
|
|
|
if (baseobj && !LLAssetType::lookupCanLink(baseobj->getType()))
|
|
{
|
|
// Fail if item can be found but is of a type that can't be linked.
|
|
// Arguably should fail if the item can't be found too, but that could
|
|
// be a larger behavioral change.
|
|
llwarns << "attempt to link an unlinkable item, type = " << baseobj->getActualType() << llendl;
|
|
return;
|
|
}
|
|
|
|
LLUUID transaction_id;
|
|
LLInventoryType::EType inv_type = LLInventoryType::IT_NONE;
|
|
if (dynamic_cast<const LLInventoryCategory *>(baseobj))
|
|
{
|
|
inv_type = LLInventoryType::IT_CATEGORY;
|
|
}
|
|
else
|
|
{
|
|
const LLViewerInventoryItem *baseitem = dynamic_cast<const LLViewerInventoryItem *>(baseobj);
|
|
if (baseitem)
|
|
{
|
|
inv_type = baseitem->getInventoryType();
|
|
}
|
|
}
|
|
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_LinkInventoryItem);
|
|
msg->nextBlock(_PREHASH_AgentData);
|
|
{
|
|
msg->addUUIDFast(_PREHASH_AgentID, agent_id);
|
|
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
|
|
}
|
|
msg->nextBlock(_PREHASH_InventoryBlock);
|
|
{
|
|
msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
|
|
msg->addUUIDFast(_PREHASH_FolderID, parent_id);
|
|
msg->addUUIDFast(_PREHASH_TransactionID, transaction_id);
|
|
msg->addUUIDFast(_PREHASH_OldItemID, item_id);
|
|
msg->addS8Fast(_PREHASH_Type, (S8)asset_type);
|
|
msg->addS8Fast(_PREHASH_InvType, (S8)inv_type);
|
|
msg->addStringFast(_PREHASH_Name, new_name);
|
|
msg->addStringFast(_PREHASH_Description, new_description);
|
|
}
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
void move_inventory_item(
|
|
const LLUUID& agent_id,
|
|
const LLUUID& session_id,
|
|
const LLUUID& item_id,
|
|
const LLUUID& parent_id,
|
|
const std::string& new_name,
|
|
LLPointer<LLInventoryCallback> cb)
|
|
{
|
|
LLMessageSystem* msg = gMessageSystem;
|
|
msg->newMessageFast(_PREHASH_MoveInventoryItem);
|
|
msg->nextBlockFast(_PREHASH_AgentData);
|
|
msg->addUUIDFast(_PREHASH_AgentID, agent_id);
|
|
msg->addUUIDFast(_PREHASH_SessionID, session_id);
|
|
msg->addBOOLFast(_PREHASH_Stamp, FALSE);
|
|
msg->nextBlockFast(_PREHASH_InventoryData);
|
|
msg->addUUIDFast(_PREHASH_ItemID, item_id);
|
|
msg->addUUIDFast(_PREHASH_FolderID, parent_id);
|
|
msg->addStringFast(_PREHASH_NewName, new_name);
|
|
gAgent.sendReliableMessage();
|
|
}
|
|
|
|
class LLCopyInventoryFromNotecardResponder : public LLHTTPClient::Responder
|
|
{
|
|
public:
|
|
//If we get back a normal response, handle it here
|
|
virtual void result(const LLSD& content)
|
|
{
|
|
// What do we do here?
|
|
llinfos << "CopyInventoryFromNotecard request successful." << llendl;
|
|
}
|
|
|
|
//If we get back an error (not found, etc...), handle it here
|
|
virtual void error(U32 status, const std::string& reason)
|
|
{
|
|
llinfos << "LLCopyInventoryFromNotecardResponder::error "
|
|
<< status << ": " << reason << llendl;
|
|
}
|
|
};
|
|
|
|
void copy_inventory_from_notecard(const LLUUID& object_id, const LLUUID& notecard_inv_id, const LLInventoryItem *src, U32 callback_id)
|
|
{
|
|
LLSD body;
|
|
LLViewerRegion* viewer_region = NULL;
|
|
if(object_id.notNull())
|
|
{
|
|
LLViewerObject* vo = gObjectList.findObject(object_id);
|
|
if(vo)
|
|
{
|
|
viewer_region = vo->getRegion();
|
|
}
|
|
}
|
|
|
|
// Fallback to the agents region if for some reason the
|
|
// object isn't found in the viewer.
|
|
if(!viewer_region)
|
|
{
|
|
viewer_region = gAgent.getRegion();
|
|
}
|
|
|
|
if(viewer_region)
|
|
{
|
|
std::string url = viewer_region->getCapability("CopyInventoryFromNotecard");
|
|
if (!url.empty())
|
|
{
|
|
body["notecard-id"] = notecard_inv_id;
|
|
body["object-id"] = object_id;
|
|
body["item-id"] = src->getUUID();
|
|
body["folder-id"] = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(src->getType()));
|
|
body["callback-id"] = (LLSD::Integer)callback_id;
|
|
|
|
LLHTTPClient::post(url, body, new LLCopyInventoryFromNotecardResponder());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
LLAssetType::EType LLViewerInventoryItem::getType() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getType();
|
|
}
|
|
if (const LLViewerInventoryCategory *linked_category = getLinkedCategory())
|
|
{
|
|
return linked_category->getType();
|
|
}
|
|
return LLInventoryItem::getType();
|
|
}
|
|
|
|
const LLUUID& LLViewerInventoryItem::getAssetUUID() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getAssetUUID();
|
|
}
|
|
|
|
return LLInventoryItem::getAssetUUID();
|
|
}
|
|
|
|
const std::string& LLViewerInventoryItem::getName() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getName();
|
|
}
|
|
if (const LLViewerInventoryCategory *linked_category = getLinkedCategory())
|
|
{
|
|
return linked_category->getName();
|
|
}
|
|
|
|
return LLInventoryItem::getName();
|
|
}
|
|
|
|
const LLPermissions& LLViewerInventoryItem::getPermissions() const
|
|
{
|
|
// Use the actual permissions of the symlink, not its parent.
|
|
return LLInventoryItem::getPermissions();
|
|
}
|
|
|
|
const LLUUID& LLViewerInventoryItem::getCreatorUUID() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getCreatorUUID();
|
|
}
|
|
|
|
return LLInventoryItem::getCreatorUUID();
|
|
}
|
|
|
|
const std::string& LLViewerInventoryItem::getDescription() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getDescription();
|
|
}
|
|
|
|
return LLInventoryItem::getDescription();
|
|
}
|
|
|
|
const LLSaleInfo& LLViewerInventoryItem::getSaleInfo() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getSaleInfo();
|
|
}
|
|
|
|
return LLInventoryItem::getSaleInfo();
|
|
}
|
|
|
|
LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getInventoryType();
|
|
}
|
|
|
|
// Categories don't have types. If this item is an AT_FOLDER_LINK,
|
|
// treat it as a category.
|
|
if (getLinkedCategory())
|
|
{
|
|
return LLInventoryType::IT_CATEGORY;
|
|
}
|
|
|
|
return LLInventoryItem::getInventoryType();
|
|
}
|
|
|
|
U32 LLViewerInventoryItem::getFlags() const
|
|
{
|
|
if (const LLViewerInventoryItem *linked_item = getLinkedItem())
|
|
{
|
|
return linked_item->getFlags();
|
|
}
|
|
return LLInventoryItem::getFlags();
|
|
}
|
|
|
|
|
|
// This returns true if the item that this item points to
|
|
// doesn't exist in memory (i.e. LLInventoryModel). The baseitem
|
|
// might still be in the database but just not loaded yet.
|
|
bool LLViewerInventoryItem::getIsBrokenLink() const
|
|
{
|
|
// If the item's type resolves to be a link, that means either:
|
|
// A. It wasn't able to perform indirection, i.e. the baseobj doesn't exist in memory.
|
|
// B. It's pointing to another link, which is illegal.
|
|
return LLAssetType::lookupIsLinkType(getType());
|
|
}
|
|
|
|
LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const
|
|
{
|
|
if (mType == LLAssetType::AT_LINK)
|
|
{
|
|
LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID);
|
|
if (linked_item && linked_item->getIsLinkType())
|
|
{
|
|
llwarns << "Warning: Accessing link to link" << llendl;
|
|
return NULL;
|
|
}
|
|
return linked_item;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
LLViewerInventoryCategory *LLViewerInventoryItem::getLinkedCategory() const
|
|
{
|
|
if (mType == LLAssetType::AT_LINK_FOLDER)
|
|
{
|
|
LLViewerInventoryCategory *linked_category = gInventory.getCategory(mAssetUUID);
|
|
return linked_category;
|
|
}
|
|
return NULL;
|
|
}
|